ElMassimo/iles

Issue resolving esm export in vite plugin during build

Closed this issue ยท 4 comments

Description ๐Ÿ“–

pnpm build does not work with vite-plugin-svg-sprite.

Reproduction ๐Ÿž

Repro repo

I just did a simple pnpm create iles@next and added the vite-plugin-svg-sprite dep.

Note that I needed to add shamefully-hoist to the .npmrc in order for iles to find "vue/server-renderer", otherwise I get this error:

vite]: Rollup failed to resolve import "vue/server-renderer" from "src/pages/about.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
โœ– building client + server bundles
build error:
 Error: [vite]: Rollup failed to resolve import "vue/server-renderer" from "src/pages/about.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
    at onRollupWarning (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/vite@3.2.5/node_modules/vite/dist/node/chunks/dep-5605cfa4.js:45832:19)
    at onwarn (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/vite@3.2.5/node_modules/vite/dist/node/chunks/dep-5605cfa4.js:45603:13)
    at Object.onwarn (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/rollup@2.79.1/node_modules/rollup/dist/es/shared/rollup.js:23263:13)
    at ModuleLoader.handleResolveId (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/rollup@2.79.1/node_modules/rollup/dist/es/shared/rollup.js:22158:26)

You can reproduce by cloning the repo and running pnpm build

Dependencies Info
ฮป pnpm list
Legend: production dependency, optional only, dev only

iles-app@0.0.0 /home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro

devDependencies:
iles 0.8.7
typescript 4.9.5
vite-plugin-svg-sprite 0.3.1
vue-tsc 0.38.9

ฮป pnpm iles:info

> iles-app@0.0.0 iles:info /home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro
> iles info

iles v0.8.7 vite v3.2.5

Logs ๐Ÿ“œ

If not providing a reproduction:

Output
> iles-app@0.0.0 build /home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro
> iles build

iles v0.8.7 vite v3.2.5
  iles:config loaded config at /home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts +0ms
  iles:config {
  iles:config   root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config   modules: [
  iles:config     {
  iles:config       name: 'iles:base-config',
  iles:config       debug: true,
  iles:config       drafts: false,
  iles:config       turbo: false,
  iles:config       jsx: undefined,
  iles:config       root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config       base: '/',
  iles:config       siteUrl: '',
  iles:config       prettyUrls: true,
  iles:config       ssg: [Object],
  iles:config       configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts',
  iles:config       assetsDir: 'assets',
  iles:config       pagesDir: 'pages',
  iles:config       srcDir: 'src',
  iles:config       outDir: 'dist',
  iles:config       layoutsDir: 'layouts',
  iles:config       tempDir: '.iles-ssg-temp',
  iles:config       modules: [],
  iles:config       namedPlugins: [Object],
  iles:config       resolvePath: undefined,
  iles:config       vitePlugins: [Array],
  iles:config       vite: [Object],
  iles:config       vue: [Object],
  iles:config       extendFrontmatter: [AsyncFunction: extendFrontmatter],
  iles:config       extendRoute: [Function: extendRoute],
  iles:config       extendRoutes: [Function: extendRoutes],
  iles:config       markdown: [Object],
  iles:config       components: [Object]
  iles:config     },
  iles:config     {
  iles:config       name: '@islands/mdx',
  iles:config       markdown: [Object],
  iles:config       configResolved: [Function: configResolved]
  iles:config     },
  iles:config     {
  iles:config       name: 'user-config',
  iles:config       vite: [Object],
  iles:config       configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts'
  iles:config     },
  iles:config     {
  iles:config       name: '@islands/pages',
  iles:config       configResolved: [Function: configResolved]
  iles:config     }
  iles:config   ],
  iles:config   debug: true,
  iles:config   drafts: false,
  iles:config   turbo: false,
  iles:config   base: '/',
  iles:config   siteUrl: '',
  iles:config   prettyUrls: true,
  iles:config   ssg: { sitemap: true },
  iles:config   configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts',
  iles:config   assetsDir: 'assets',
  iles:config   pagesDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src/pages',
  iles:config   srcDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src',
  iles:config   outDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/dist',
  iles:config   layoutsDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src/layouts',
  iles:config   tempDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/.iles-ssg-temp',
  iles:config   namedPlugins: {
  iles:config     components: {
  iles:config       name: 'unplugin-vue-components',
  iles:config       enforce: 'post',
  iles:config       api: [Object],
  iles:config       transformInclude: [Function: transformInclude],
  iles:config       transform: [Function (anonymous)],
  iles:config       vite: [Object],
  iles:config       webpack: [Function: webpack],
  iles:config       configResolved: [Function: configResolved],
  iles:config       configureServer: [Function: configureServer]
  iles:config     },
  iles:config     vue: {
  iles:config       name: 'vite:vue',
  iles:config       handleHotUpdate: [Function: handleHotUpdate],
  iles:config       config: [Function: config],
  iles:config       configResolved: [Function: configResolved],
  iles:config       configureServer: [Function: configureServer],
  iles:config       buildStart: [Function: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [Function: load],
  iles:config       transform: [Function: transform]
  iles:config     },
  iles:config     pages: {
  iles:config       name: 'iles:pages',
  iles:config       enforce: 'pre',
  iles:config       api: [Getter],
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       configureServer: [AsyncFunction: configureServer],
  iles:config       buildStart: [AsyncFunction: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [AsyncFunction: load],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     }
  iles:config   },
  iles:config   vitePlugins: [
  iles:config     {
  iles:config       name: 'iles:mdx:compile',
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     },
  iles:config     { name: 'iles:mdx:sfc', transform: [AsyncFunction: transform] },
  iles:config     {
  iles:config       name: 'iles:mdx:hmr',
  iles:config       apply: 'serve',
  iles:config       transform: [Function: transform]
  iles:config     },
  iles:config     {
  iles:config       name: 'iles:pages',
  iles:config       enforce: 'pre',
  iles:config       api: [Getter],
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       configureServer: [AsyncFunction: configureServer],
  iles:config       buildStart: [AsyncFunction: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [AsyncFunction: load],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     }
  iles:config   ],
  iles:config   vite: {
  iles:config     root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config     resolve: { alias: [Array], dedupe: [Array] },
  iles:config     server: { fs: [Object] },
  iles:config     build: { cssCodeSplit: false, assetsDir: 'assets' },
  iles:config     define: { 'import.meta.env.DISPOSE_ISLANDS': false },
  iles:config     optimizeDeps: { include: [Array], exclude: [Array] },
  iles:config     plugins: [ [Object] ],
  iles:config     base: '/'
  iles:config   },
  iles:config   vue: {
  iles:config     reactivityTransform: true,
  iles:config     template: { compilerOptions: [Object] }
  iles:config   },
  iles:config   extendFrontmatter: [AsyncFunction (anonymous)],
  iles:config   extendRoute: [AsyncFunction (anonymous)],
  iles:config   extendRoutes: [AsyncFunction (anonymous)],
  iles:config   markdown: {
  iles:config     jsxRuntime: 'automatic',
  iles:config     jsxImportSource: 'iles',
  iles:config     providerImportSource: 'iles',
  iles:config     rehypePlugins: [ [Array] ],
  iles:config     remarkPlugins: [ [Array], [Array], [Array], [Array] ],
  iles:config     recmaPlugins: [ [Function: recmaVueResolveComponents], [Function (anonymous)] ]
  iles:config   },
  iles:config   components: {
  iles:config     dts: true,
  iles:config     extensions: [ 'vue', 'jsx', 'tsx', 'js', 'ts', 'mdx', 'svelte' ],
  iles:config     include: [ /\.vue$/, /\.vue\?vue/, /\.mdx?/ ],
  iles:config     dirs: 'src/components',
  iles:config     resolvers: [ [Function: IlesComponentResolver], [Function (anonymous)] ],
  iles:config     transformer: 'vue3'
  iles:config   }
  iles:config } +0ms
  iles:config {
  iles:config   root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config   modules: [
  iles:config     {
  iles:config       name: 'iles:base-config',
  iles:config       debug: true,
  iles:config       drafts: false,
  iles:config       turbo: false,
  iles:config       jsx: undefined,
  iles:config       root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config       base: '/',
  iles:config       siteUrl: '',
  iles:config       prettyUrls: true,
  iles:config       ssg: [Object],
  iles:config       configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts',
  iles:config       assetsDir: 'assets',
  iles:config       pagesDir: 'pages',
  iles:config       srcDir: 'src',
  iles:config       outDir: 'dist',
  iles:config       layoutsDir: 'layouts',
  iles:config       tempDir: '.iles-ssg-temp',
  iles:config       modules: [],
  iles:config       namedPlugins: [Object],
  iles:config       resolvePath: undefined,
  iles:config       vitePlugins: [Array],
  iles:config       vite: [Object],
  iles:config       vue: [Object],
  iles:config       extendFrontmatter: [AsyncFunction: extendFrontmatter],
  iles:config       extendRoute: [Function: extendRoute],
  iles:config       extendRoutes: [Function: extendRoutes],
  iles:config       markdown: [Object],
  iles:config       components: [Object]
  iles:config     },
  iles:config     {
  iles:config       name: '@islands/mdx',
  iles:config       markdown: [Object],
  iles:config       configResolved: [Function: configResolved]
  iles:config     },
  iles:config     {
  iles:config       name: 'user-config',
  iles:config       vite: [Object],
  iles:config       configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts'
  iles:config     },
  iles:config     {
  iles:config       name: '@islands/pages',
  iles:config       configResolved: [Function: configResolved]
  iles:config     }
  iles:config   ],
  iles:config   debug: true,
  iles:config   drafts: false,
  iles:config   turbo: false,
  iles:config   base: '/',
  iles:config   siteUrl: '',
  iles:config   prettyUrls: true,
  iles:config   ssg: { sitemap: true },
  iles:config   configPath: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/iles.config.ts',
  iles:config   assetsDir: 'assets',
  iles:config   pagesDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src/pages',
  iles:config   srcDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src',
  iles:config   outDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/dist',
  iles:config   layoutsDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/src/layouts',
  iles:config   tempDir: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/.iles-ssg-temp',
  iles:config   namedPlugins: {
  iles:config     components: {
  iles:config       name: 'unplugin-vue-components',
  iles:config       enforce: 'post',
  iles:config       api: [Object],
  iles:config       transformInclude: [Function: transformInclude],
  iles:config       transform: [Function (anonymous)],
  iles:config       vite: [Object],
  iles:config       webpack: [Function: webpack],
  iles:config       configResolved: [Function: configResolved],
  iles:config       configureServer: [Function: configureServer]
  iles:config     },
  iles:config     vue: {
  iles:config       name: 'vite:vue',
  iles:config       handleHotUpdate: [Function: handleHotUpdate],
  iles:config       config: [Function: config],
  iles:config       configResolved: [Function: configResolved],
  iles:config       configureServer: [Function: configureServer],
  iles:config       buildStart: [Function: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [Function: load],
  iles:config       transform: [Function: transform]
  iles:config     },
  iles:config     pages: {
  iles:config       name: 'iles:pages',
  iles:config       enforce: 'pre',
  iles:config       api: [Getter],
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       configureServer: [AsyncFunction: configureServer],
  iles:config       buildStart: [AsyncFunction: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [AsyncFunction: load],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     }
  iles:config   },
  iles:config   vitePlugins: [
  iles:config     {
  iles:config       name: 'iles:mdx:compile',
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     },
  iles:config     { name: 'iles:mdx:sfc', transform: [AsyncFunction: transform] },
  iles:config     {
  iles:config       name: 'iles:mdx:hmr',
  iles:config       apply: 'serve',
  iles:config       transform: [Function: transform]
  iles:config     },
  iles:config     {
  iles:config       name: 'iles:pages',
  iles:config       enforce: 'pre',
  iles:config       api: [Getter],
  iles:config       configResolved: [AsyncFunction: configResolved],
  iles:config       configureServer: [AsyncFunction: configureServer],
  iles:config       buildStart: [AsyncFunction: buildStart],
  iles:config       resolveId: [AsyncFunction: resolveId],
  iles:config       load: [AsyncFunction: load],
  iles:config       transform: [AsyncFunction: transform]
  iles:config     }
  iles:config   ],
  iles:config   vite: {
  iles:config     root: '/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro',
  iles:config     resolve: { alias: [Array], dedupe: [Array] },
  iles:config     server: { fs: [Object] },
  iles:config     build: { cssCodeSplit: false, assetsDir: 'assets' },
  iles:config     define: { 'import.meta.env.DISPOSE_ISLANDS': false },
  iles:config     optimizeDeps: { include: [Array], exclude: [Array] },
  iles:config     plugins: [ [Object] ],
  iles:config     base: '/'
  iles:config   },
  iles:config   vue: {
  iles:config     reactivityTransform: true,
  iles:config     template: { compilerOptions: [Object] }
  iles:config   },
  iles:config   extendFrontmatter: [AsyncFunction (anonymous)],
  iles:config   extendRoute: [AsyncFunction (anonymous)],
  iles:config   extendRoutes: [AsyncFunction (anonymous)],
  iles:config   markdown: {
  iles:config     jsxRuntime: 'automatic',
  iles:config     jsxImportSource: 'iles',
  iles:config     providerImportSource: 'iles',
  iles:config     rehypePlugins: [ [Array] ],
  iles:config     remarkPlugins: [ [Array], [Array], [Array], [Array] ],
  iles:config     recmaPlugins: [ [Function: recmaVueResolveComponents], [Function (anonymous)] ]
  iles:config   },
  iles:config   components: {
  iles:config     dts: true,
  iles:config     extensions: [ 'vue', 'jsx', 'tsx', 'js', 'ts', 'mdx', 'svelte' ],
  iles:config     include: [ /\.vue$/, /\.vue\?vue/, /\.mdx?/ ],
  iles:config     dirs: 'src/components',
  iles:config     resolvers: [ [Function: IlesComponentResolver], [Function (anonymous)] ],
  iles:config     transformer: 'vue3'
  iles:config   }
  iles:config } +6ms
  iles:detect <FrameworkLink client:none> +0ms
  iles:detect <FrameworkLink client:none> +11ms
โœ” building client + server bundles
  done in 1.5s

(node:635346) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
Error while fetching 'default' layout.
/home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/vite-plugin-svg-sprite@0.3.1/node_modules/vite-plugin-svg-sprite/es/runtime.js:15
export default function addSymbol(symbol, id) {
^^^^^^

SyntaxError: Unexpected token 'export'
    at Object.compileFunction (node:vm:360:18)
    at wrapSafe (node:internal/modules/cjs/loader:1088:15)
    at Module._compile (node:internal/modules/cjs/loader:1123:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:169:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
    at async resolveLayout (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/.iles-ssg-temp/app.mjs:4677:54)
โœ– resolving static paths
build error:
 /home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/node_modules/.pnpm/vite-plugin-svg-sprite@0.3.1/node_modules/vite-plugin-svg-sprite/es/runtime.js:15
export default function addSymbol(symbol, id) {
^^^^^^

SyntaxError: Unexpected token 'export'
    at Object.compileFunction (node:vm:360:18)
    at wrapSafe (node:internal/modules/cjs/loader:1088:15)
    at Module._compile (node:internal/modules/cjs/loader:1123:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:169:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
    at async resolveLayout (file:///home/jonathanl/Code/iles-vite-plugin-svg-sprite-repro/.iles-ssg-temp/app.mjs:4677:54)
โ€‰ELIFECYCLEโ€‰ Command failed with exit code 1.

Upon further research I think it has something to do with the plugin dynamically injecting an esm import.

https://github.com/meowtec/vite-plugin-svg-sprite/blob/main/src/index.ts#L63-L67

Hi Jonathan!

Based on the error message:

vite-plugin-svg-sprite@0.3.1/node_modules/vite-plugin-svg-sprite/es/runtime.js:15
export default function addSymbol(symbol, id) {
^^^^^^

SyntaxError: Unexpected token 'export'

it seems to be failing on this line with a SyntaxError.

Since vite-plugin-svg-sprite is not "type": "module", vite-plugin-svg-sprite/es/runtime.js should have the mjs extension instead, otherwise export is not a valid keyword.

To workaround this, you could force Vite not to externalize this package in SSR with:

ssr: { noExternal: ['vite-plugin-svg-sprite'] },

Then, the problem becomes that the runtime of this library assumes window is defined, which is not true for SSR.

After reading how the library works, I don't think vite-plugin-svg-sprite is compatible with SSR, and you might need to look for alternatives.

Since vite-plugin-svg-sprite is not "type": "module", vite-plugin-svg-sprite/es/runtime.js should have the mjs extension instead, otherwise export is not a valid keyword.

To workaround this, you could force Vite not to externalize this package in SSR with:

ssr: { noExternal: ['vite-plugin-svg-sprite'] },

Then, the problem becomes that the runtime of this library assumes window is defined, which is not true for SSR.

After reading how the library works, I don't think vite-plugin-svg-sprite is compatible with SSR, and you might need to look for alternatives.

Thanks for digging in. I hadn't even considered that it wouldn't work with SSR!