diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 66b0bcdbe050a9..f9a59fbeac77df 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -17,6 +17,9 @@ const externalWithConversionNamespace = 'vite:dep-pre-bundle:external-conversion' const convertedExternalPrefix = 'vite-dep-pre-bundle-external:' +const cjsExternalFacadeNamespace = 'vite:cjs-external-facade' +const nonFacadePrefix = 'vite-cjs-external-facade:' + const externalTypes = [ 'css', // supported pre-processor types @@ -312,19 +315,38 @@ export function esbuildCjsExternalPlugin(externals: string[]): Plugin { `^${text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}$` const filter = new RegExp(externals.map(escape).join('|')) - build.onResolve({ filter: /.*/, namespace: 'external' }, (args) => ({ - path: args.path, - external: true - })) + build.onResolve({ filter: new RegExp(`^${nonFacadePrefix}`) }, (args) => { + return { + path: args.path.slice(nonFacadePrefix.length), + external: true + } + }) + + build.onResolve({ filter }, (args) => { + if (args.kind === 'require-call') { + return { + path: args.path, + namespace: cjsExternalFacadeNamespace + } + } - build.onResolve({ filter }, (args) => ({ - path: args.path, - namespace: 'external' - })) + return { + path: args.path, + external: true + } + }) - build.onLoad({ filter: /.*/, namespace: 'external' }, (args) => ({ - contents: `export * from ${JSON.stringify(args.path)}` - })) + build.onLoad( + { filter: /.*/, namespace: cjsExternalFacadeNamespace }, + (args) => ({ + contents: + `import * as m from ${JSON.stringify( + nonFacadePrefix + args.path + )};` + + `export default m.default;` + + `export * from ${JSON.stringify(nonFacadePrefix + args.path)};` + }) + ) } } } diff --git a/playground/external/__tests__/external.spec.ts b/playground/external/__tests__/external.spec.ts index 029ffc8422a3ba..100eceaebc3418 100644 --- a/playground/external/__tests__/external.spec.ts +++ b/playground/external/__tests__/external.spec.ts @@ -7,6 +7,11 @@ test('importmap', () => { ) }) +test('should have default exports', async () => { + expect(await page.textContent('#imported-slash-exists')).toBe('true') + expect(await page.textContent('#required-slash-exists')).toBe('true') +}) + describe.runIf(isBuild)('build', () => { test('should externalize imported packages', async () => { // If `vue` is successfully externalized, the page should use the version from the import map diff --git a/playground/external/dep-that-imports-vue/index.js b/playground/external/dep-that-imports/index.js similarity index 51% rename from playground/external/dep-that-imports-vue/index.js rename to playground/external/dep-that-imports/index.js index aa79957e9b3b49..a7888d2af338c6 100644 --- a/playground/external/dep-that-imports-vue/index.js +++ b/playground/external/dep-that-imports/index.js @@ -1,3 +1,5 @@ import { version } from 'vue' +import slash from 'slash' document.querySelector('#imported-vue-version').textContent = version +document.querySelector('#imported-slash-exists').textContent = !!slash diff --git a/playground/external/dep-that-imports-vue/package.json b/playground/external/dep-that-imports/package.json similarity index 59% rename from playground/external/dep-that-imports-vue/package.json rename to playground/external/dep-that-imports/package.json index 7b77b5f5c398cf..ee293530a20505 100644 --- a/playground/external/dep-that-imports-vue/package.json +++ b/playground/external/dep-that-imports/package.json @@ -1,8 +1,9 @@ { - "name": "@vitejs/dep-that-imports-vue", + "name": "@vitejs/dep-that-imports", "private": true, "version": "0.0.0", "dependencies": { + "slash": "^5.0.0", "vue": "^3.2.40" } } diff --git a/playground/external/dep-that-requires-vue/index.js b/playground/external/dep-that-requires-vue/index.js deleted file mode 100644 index 0b573021a00b57..00000000000000 --- a/playground/external/dep-that-requires-vue/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const { version } = require('vue') - -document.querySelector('#required-vue-version').textContent = version diff --git a/playground/external/dep-that-requires/index.js b/playground/external/dep-that-requires/index.js new file mode 100644 index 00000000000000..aef4e3fa7359a5 --- /dev/null +++ b/playground/external/dep-that-requires/index.js @@ -0,0 +1,5 @@ +const { version } = require('vue') +const { default: slash } = require('slash') + +document.querySelector('#required-vue-version').textContent = version +document.querySelector('#required-slash-exists').textContent = !!slash diff --git a/playground/external/dep-that-requires-vue/package.json b/playground/external/dep-that-requires/package.json similarity index 59% rename from playground/external/dep-that-requires-vue/package.json rename to playground/external/dep-that-requires/package.json index fadb1a48240671..4aab49019d6039 100644 --- a/playground/external/dep-that-requires-vue/package.json +++ b/playground/external/dep-that-requires/package.json @@ -1,8 +1,9 @@ { - "name": "@vitejs/dep-that-requires-vue", + "name": "@vitejs/dep-that-requires", "private": true, "version": "0.0.0", "dependencies": { + "slash": "^5.0.0", "vue": "^3.2.40" } } diff --git a/playground/external/index.html b/playground/external/index.html index 66a16c329c1fc5..1fcedbf2d64ae3 100644 --- a/playground/external/index.html +++ b/playground/external/index.html @@ -7,7 +7,8 @@ @@ -15,6 +16,8 @@

Imported Vue version:

Required Vue version:

+

Imported slash exists:

+

Required slash exists:

diff --git a/playground/external/package.json b/playground/external/package.json index 53e2dd628302b4..4b3da2d2986c1d 100644 --- a/playground/external/package.json +++ b/playground/external/package.json @@ -9,10 +9,11 @@ "preview": "vite preview" }, "dependencies": { - "@vitejs/dep-that-imports-vue": "file:./dep-that-imports-vue", - "@vitejs/dep-that-requires-vue": "file:./dep-that-requires-vue" + "@vitejs/dep-that-imports": "file:./dep-that-imports", + "@vitejs/dep-that-requires": "file:./dep-that-requires" }, "devDependencies": { + "slash": "^5.0.0", "vite": "workspace:*", "vue": "^3.2.40" } diff --git a/playground/external/src/main.js b/playground/external/src/main.js index 0e1d3bece640e9..f16b94b75706e5 100644 --- a/playground/external/src/main.js +++ b/playground/external/src/main.js @@ -1,2 +1,2 @@ -import '@vitejs/dep-that-imports-vue' -import '@vitejs/dep-that-requires-vue' +import '@vitejs/dep-that-imports' +import '@vitejs/dep-that-requires' diff --git a/playground/external/vite.config.js b/playground/external/vite.config.js index fde8e763f810e0..9de99bb2025bfc 100644 --- a/playground/external/vite.config.js +++ b/playground/external/vite.config.js @@ -1,13 +1,17 @@ import { defineConfig } from 'vite' export default defineConfig({ + optimizeDeps: { + include: ['dep-that-imports', 'dep-that-requires'], + exclude: ['vue', 'slash'] + }, build: { minify: false, rollupOptions: { - external: ['vue'] + external: ['vue', 'slash'] }, commonjsOptions: { - esmExternals: ['vue'] + esmExternals: ['vue', 'slash'] } } }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 000d30c1040610..fea66dbe79eb54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -489,27 +489,33 @@ importers: playground/external: specifiers: - '@vitejs/dep-that-imports-vue': file:./dep-that-imports-vue - '@vitejs/dep-that-requires-vue': file:./dep-that-requires-vue + '@vitejs/dep-that-imports': file:./dep-that-imports + '@vitejs/dep-that-requires': file:./dep-that-requires + slash: ^5.0.0 vite: workspace:* vue: ^3.2.40 dependencies: - '@vitejs/dep-that-imports-vue': file:playground/external/dep-that-imports-vue - '@vitejs/dep-that-requires-vue': file:playground/external/dep-that-requires-vue + '@vitejs/dep-that-imports': file:playground/external/dep-that-imports + '@vitejs/dep-that-requires': file:playground/external/dep-that-requires devDependencies: + slash: 5.0.0 vite: link:../../packages/vite vue: 3.2.40 - playground/external/dep-that-imports-vue: + playground/external/dep-that-imports: specifiers: + slash: ^5.0.0 vue: ^3.2.40 dependencies: + slash: 5.0.0 vue: 3.2.40 - playground/external/dep-that-requires-vue: + playground/external/dep-that-requires: specifiers: + slash: ^5.0.0 vue: ^3.2.40 dependencies: + slash: 5.0.0 vue: 3.2.40 playground/file-delete-restore: @@ -8161,7 +8167,6 @@ packages: /slash/5.0.0: resolution: {integrity: sha512-n6KkmvKS0623igEVj3FF0OZs1gYYJ0o0Hj939yc1fyxl2xt+xYpLnzJB6xBSqOfV9ZFLEWodBBN/heZJahuIJQ==} engines: {node: '>=14.16'} - dev: true /slice-ansi/3.0.0: resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} @@ -9293,19 +9298,21 @@ packages: version: 1.0.0 dev: false - file:playground/external/dep-that-imports-vue: - resolution: {directory: playground/external/dep-that-imports-vue, type: directory} - name: '@vitejs/dep-that-imports-vue' + file:playground/external/dep-that-imports: + resolution: {directory: playground/external/dep-that-imports, type: directory} + name: '@vitejs/dep-that-imports' version: 0.0.0 dependencies: + slash: 5.0.0 vue: 3.2.40 dev: false - file:playground/external/dep-that-requires-vue: - resolution: {directory: playground/external/dep-that-requires-vue, type: directory} - name: '@vitejs/dep-that-requires-vue' + file:playground/external/dep-that-requires: + resolution: {directory: playground/external/dep-that-requires, type: directory} + name: '@vitejs/dep-that-requires' version: 0.0.0 dependencies: + slash: 5.0.0 vue: 3.2.40 dev: false