vue.jsviteshopify-app

How to set Vite config for dynamic imports (code-splitting) when entry file and chunks are on a different domain?


I'm looking for help to resolve some code-splitting issues in a unique deployment setup (Vue + Vite + Shopify app).

Background and setup

blockTypes.forEach((blockType) => {
  app.component(
    blockType,
    defineAsyncComponent(() => import(`./blocks/${blockType}.vue`))
  )
})

So far, this is all fairly straightforward when I have complete control over the deployment.

Enter Shopify development

This is where it gets tricky.

The Vue application is deployed as a Shopify theme extension block for a Shopify app. The main thing that’s relevant here is that Shopify handles the deployment and sets some constraints.

  1. All assets must be in a flat /assets/ folder (I have this part working with rollupOptions in the vite config)
rollupOptions: {
    input: './src/main.ts',
    output: {
      entryFileNames: 'assets/genesis-block.js',
      chunkFileNames: 'assets/[name]-[hash].js',
      assetFileNames: 'assets/block.[ext]'
  }
}

  1. The domain for the HTML is [some-store].myshopify.com; but the assets are put on a CDN cdn.shopify.com/extensions/[theme_extension_uuid]/assets/* (and the theme_extension_uuid is not known at build time).

  2. Also, the asset URL includes a cache buster param when injected in the html, like this… cdn.shopify.com/extensions/[theme_extension_uuid]/assets/index.js?v=1726188069

Now when loading the page... things get weird:

Worse, for CSS chunks

I get a mess of console errors for each 404, and a lot of red in the network tab.

network tab (entry file is 'gen-block.js')

How to configure for this case?

Is this a vite or @vitejs/plugin-vue concern, or something I should solve for with rollup options? I’m not sure how to move forward.

I also put this question in the vite repo discussion


It works fine when running the app locally. The main issue is when deployed on a different domain (with a path not known at build time) as the window location.

I've tried different ways of splitting code with defineAsyncComponent, and different rollupOptions in vite config.


Solution

  • The solution was to use Vite's renderBuiltUrl feature. It took some trial and error with the code outlined here, but looks like I got it working for both JS and CSS chunks.

    // vite.config
    experimental: {
      renderBuiltUrl(filename, { hostType }) {
        if (hostType === 'js') {
          return {
            runtime: `window.__toCdnUrl(${JSON.stringify(filename)})`
          }
        } else {
          return { relative: true }
        }
      }
    },
    

    Then to resolve the cache busting issue with the entry file, I created a simple proxy module to get the CDN URL at runtime and import the entry file. So this proxy is loaded from the script tag instead of the entry file.

    const loaderPathFile = 'assets/gen-lp-loader.js'
    const loaderSrc = document.querySelector('#MainContent script').src
    const [shopifyCdnUrl] = loaderSrc.split(loaderPathFile)
    window.__toCdnUrl = (filename) => {
      return `${shopifyCdnUrl}${filename}`
    }
    
    // entry file
    import './gen-block.js'
    

    Additional context here: https://github.com/vitejs/vite/discussions/18097