[Theme Editor] Major CORS issues for theme developers using Vite

Short description of issue

With recent updates to Chrome Local Network Access, there are now major CORS issues for any developers using vite to bundle their assets when developing themes.

Reproduction steps

A basic vite config should work like this, if using the popular vite-plugin-shopify:

import { defineConfig } from "vite"
import shopify from "vite-plugin-shopify"

export default defineConfig({
  plugins: [
    shopify({
      themeRoot: "app",
      sourceCodeDir: "src",
      entrypointsDir: "src/entrypoints",
      snippetFile: "vite-assets.liquid",
    }),
  ],
})

After recent updates to Chrome the only way to correctly bundle assets is with a tunnel approach:

import { defineConfig } from "vite"
import shopify from "vite-plugin-shopify"

export default defineConfig({
  plugins: [
    shopify({
      themeRoot: "app",
      sourceCodeDir: "src",
      entrypointsDir: "src/entrypoints",
      snippetFile: "vite-assets.liquid",
      tunnel: true,
    }),
  ],
  server: {
    allowedHosts: true,
    cors: {
      origin: [
        /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/,
        /^https?:\/\/[^/]+\.myshopify\.com$/,
      ],
    },

Additional info

There has been a long discussion on the GitHub issue for here: Trouble with CORS only in Shopify Theme Editor · Issue #204 · barrel/shopify-vite · GitHub. Proposed solutions are sticking with a tunnel vite.config (works), disabling Chrome’s “Local Network Access” flag (works), and another dev says that if the editor’s iframe can have allow="local-network-access" added, the issue may be fixed.

I am wondering what the Shopify dev ream recommends here, or if they even are aware of this issue. Vite is an extremely popular tool for creating themes and the current DX is not good for theme development with these recent issues.

What type of topic is this

Troubleshooting

1 Like

We’re actually hitting this ourselves in our own development flows and are talking about it. We may look at adding local-network-access to the allow list on our iframes but need to have a broader talk about security implications based on Chrome’s direction here.

Hey Gray, thanks for getting back to me. Glad to hear the Shopify team is aware of the issue!

Yes, the sooner this gets resolved, the better for devs working with Vite. In the meantime, what would you recommend for the vite.config setup? Should we rely on the tunnel approach for now?

Also, once a fix is released, where would that be documented in the Shopify developer channel or changelog ecosystem?

Thanks

I’ll try to make sure to update this thread.

2 Likes

Same here.
This is currently blocking my team’s workflow completely.
Following this thread for any updates or fixes :confused:

Try adding the following under plugins:

{
  name: 'add-private-network-header',
  configureServer(server) {
    server.middlewares.use((req, res, next) => {
      res.setHeader('Access-Control-Allow-Private-Network', 'true');
      next();
    });
  }
}

Can’t guarantee it will work.

Thank you for trying :slight_smile:

But it’s not working :frowning:

I managed to hack something together using cloudflared that works on both local and editor.

current relevant npm script:

   "dev": "concurrently --names \"VITE,SHOPIFY\" -c \"bgBlue.bold,bgMagenta.bold\" \"node vite-tunnel.js\" \"npm:shopify:dev\"",

the vite-tunnel.js content:

import { spawn } from 'child_process';

const tunnel = spawn('cloudflared', ['tunnel', '--url', 'http://localhost:5173']);

tunnel.stderr.on('data', (data) => {
  const output = data.toString();
  const match = output.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/);

  if (match) {
    const tunnelUrl = match[0];
    const tunnelHost = tunnelUrl.replace('https://', '');

    console.log(`\x1b[32m%s\x1b[0m`, `[Tunnel] Linked: ${tunnelUrl}`);
    console.log(`\x1b[33m%s\x1b[0m`, `[Tunnel] Waiting 3s for DNS propagation...`);

    setTimeout(() => {
      console.log(`\x1b[32m%s\x1b[0m`, `[Vite] Launching...`);
      
      const vite = spawn('vite', [], {
        stdio: 'inherit',
        shell: true,
        env: { ...process.env, VITE_TUNNEL_URL: tunnelHost }
      });

      // 3000 ms is a minimum by experience to let the DNS propagate
    }, 3000);
  }
});

and the vite.config.js:

import { defineConfig } from 'vite';
import shopify from 'vite-plugin-shopify';

const tunnelUrl = process.env.VITE_TUNNEL_URL ? `https://${process.env.VITE_TUNNEL_URL}` : 'http://localhost:5173';

export default defineConfig({
  plugins: [
    shopify({
      themeRoot: './',
    }),
  ],
  css: {
    preprocessorOptions: {
      scss: {
        api: 'modern-compiler',
        silenceDeprecations: ['import'],
      },
    },
  },
  server: {
    host: 'localhost',
    port: 5173,
    origin: tunnelUrl,
    cors: true,
    hmr: {
      protocol: 'wss',
      host: process.env.VITE_TUNNEL_URL || 'localhost',
      clientPort: 443,
      port: 5173,
    },
  },
  build: {
    emptyOutDir: false,
  },
});

hope this will work for you too till something more elegant comes up!

Hey all, the vite-plugin-shopify was recently updated. Add the tunnel: true option with allowedHosts: [".trycloudflare.com"]. Here is a basic config:

import { defineConfig } from "vite"
import tailwindcss from "@tailwindcss/vite"
import shopify from "vite-plugin-shopify"

export default defineConfig({
  plugins: [
    tailwindcss(),
    shopify({ tunnel: true }),
  ],
  server: {
    allowedHosts: [".trycloudflare.com"],
    cors: {
      origin: [
        /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/,
        /^https?:\/\/[^/]+\.myshopify\.com$/,
      ],
    },
  },
  build: {
    rollupOptions: {
      output: {
        entryFileNames: "[name].[hash].min.js",
        chunkFileNames: "[name].[hash].min.js",
        assetFileNames: "[name].[hash].min[extname]",
      },
    },
  },
})

Hope this helps!