From 6a5be8c193d1e7b519ed068232fc6d1dc555bcc7 Mon Sep 17 00:00:00 2001 From: swandir Date: Thu, 13 Jan 2022 19:14:01 +0700 Subject: [PATCH 1/6] fix(optimizer): browser mapping for yarn pnp --- packages/vite/src/node/optimizer/esbuildDepPlugin.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 6c778e2c8bf8d3..9a3ec744a6ebcf 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -209,9 +209,14 @@ export function esbuildDepPlugin( if (isRunningWithYarnPnp) { build.onResolve( { filter: /.*/ }, - async ({ path, importer, kind, resolveDir }) => ({ + async ({ path, importer, kind, resolveDir, namespace }) => ({ // pass along resolveDir for entries - path: await resolve(path, importer, kind, resolveDir) + path: await resolve( + path, + importer, + kind, + namespace === 'dep' ? resolveDir : undefined + ) }) ) build.onLoad({ filter: /.*/ }, async (args) => ({ From 1dce5eb361187e17d99fb9c7cb40773bfd8f75ed Mon Sep 17 00:00:00 2001 From: swandir Date: Tue, 18 Jan 2022 10:07:45 +0300 Subject: [PATCH 2/6] fix(optimizer): handle externals in pnp compat --- .../src/node/optimizer/esbuildDepPlugin.ts | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 9a3ec744a6ebcf..1f91847e77138b 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -68,6 +68,24 @@ export function esbuildDepPlugin( return resolver(id, _importer, undefined, ssr) } + const resolveResult = (id: string, resolved: string) => { + if (resolved.startsWith(browserExternalId)) { + return { + path: id, + namespace: 'browser-external' + } + } + if (isExternalUrl(resolved)) { + return { + path: resolved, + external: true + } + } + return { + path: path.resolve(resolved) + } + } + return { name: 'vite:dep-pre-bundle', setup(build) { @@ -122,21 +140,7 @@ export function esbuildDepPlugin( // use vite's own resolver const resolved = await resolve(id, importer, kind) if (resolved) { - if (resolved.startsWith(browserExternalId)) { - return { - path: id, - namespace: 'browser-external' - } - } - if (isExternalUrl(resolved)) { - return { - path: resolved, - external: true - } - } - return { - path: path.resolve(resolved) - } + return resolveResult(id, resolved) } } ) @@ -209,15 +213,18 @@ export function esbuildDepPlugin( if (isRunningWithYarnPnp) { build.onResolve( { filter: /.*/ }, - async ({ path, importer, kind, resolveDir, namespace }) => ({ - // pass along resolveDir for entries - path: await resolve( - path, + async ({ path: id, importer, kind, resolveDir, namespace }) => { + const resolved = await resolve( + id, importer, kind, + // pass along resolveDir for entries namespace === 'dep' ? resolveDir : undefined ) - }) + if (resolved) { + return resolveResult(id, resolved) + } + } ) build.onLoad({ filter: /.*/ }, async (args) => ({ contents: await require('fs').promises.readFile(args.path), From 76a49daa91e0fe44b2662f61c378c5e22afda752 Mon Sep 17 00:00:00 2001 From: swandir Date: Thu, 27 Jan 2022 21:14:11 +0300 Subject: [PATCH 3/6] fix(optimizer): browser-external proxy --- .../src/node/optimizer/esbuildDepPlugin.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 1f91847e77138b..4b78944e89d2c4 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -34,6 +34,21 @@ const externalTypes = [ ...KNOWN_ASSET_TYPES ] +const browserExternalContents = (id: string) => `\ +module.exports = new Proxy({}, { + get() { + return new Proxy({}, { + get() { + throw new Error( + 'Module "${id}" has been externalized for ' + + 'browser compatibility and cannot be accessed in client code.' + ) + } + }) + } +}) +` + export function esbuildDepPlugin( qualified: Record, exportsData: Record, @@ -197,15 +212,7 @@ export function esbuildDepPlugin( build.onLoad( { filter: /.*/, namespace: 'browser-external' }, ({ path: id }) => { - return { - contents: - `export default new Proxy({}, { - get() { - throw new Error('Module "${id}" has been externalized for ` + - `browser compatibility and cannot be accessed in client code.') - } -})` - } + return { contents: browserExternalContents(id) } } ) From 19f7d9a6c86ce30105d5fb1e9fae4753fe4c1c5c Mon Sep 17 00:00:00 2001 From: swandir Date: Thu, 10 Feb 2022 12:00:45 +0300 Subject: [PATCH 4/6] fix(optimizer): return empty contents for browser-external in pnp mode only --- .../src/node/optimizer/esbuildDepPlugin.ts | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 4b78944e89d2c4..e9187ca782b0c7 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -34,21 +34,6 @@ const externalTypes = [ ...KNOWN_ASSET_TYPES ] -const browserExternalContents = (id: string) => `\ -module.exports = new Proxy({}, { - get() { - return new Proxy({}, { - get() { - throw new Error( - 'Module "${id}" has been externalized for ' + - 'browser compatibility and cannot be accessed in client code.' - ) - } - }) - } -}) -` - export function esbuildDepPlugin( qualified: Record, exportsData: Record, @@ -209,13 +194,6 @@ export function esbuildDepPlugin( } }) - build.onLoad( - { filter: /.*/, namespace: 'browser-external' }, - ({ path: id }) => { - return { contents: browserExternalContents(id) } - } - ) - // yarn 2 pnp compat if (isRunningWithYarnPnp) { build.onResolve( @@ -233,6 +211,14 @@ export function esbuildDepPlugin( } } ) + + // Without pnp esbuild is transforming modules disabled for the browser + // to empty CommonJS modules. In pnp mode we achieve the same by passing + // empty contents causing esbuild to produce an empty CommonJS module. + build.onLoad({ filter: /.*/, namespace: 'browser-external' }, () => { + return { contents: '' } + }) + build.onLoad({ filter: /.*/ }, async (args) => ({ contents: await require('fs').promises.readFile(args.path), loader: 'default' From 84205e220c6daa44b3c415b8cc6efa500fb08a1b Mon Sep 17 00:00:00 2001 From: swandir Date: Thu, 10 Feb 2022 16:53:31 +0300 Subject: [PATCH 5/6] fix(optimizer): empty contents onLoad for all --- .../vite/src/node/optimizer/esbuildDepPlugin.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index e9187ca782b0c7..a6262db0c65913 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -194,6 +194,14 @@ export function esbuildDepPlugin( } }) + // Returning empty contents that will be turned by ESBuild + // into a CommonJS module exporting an empty object. + // This is what ESBuild does when bundling a dependency + // starting from a non-browser-external entrypoint. + build.onLoad({ filter: /.*/, namespace: 'browser-external' }, () => { + return { contents: '' } + }) + // yarn 2 pnp compat if (isRunningWithYarnPnp) { build.onResolve( @@ -212,13 +220,6 @@ export function esbuildDepPlugin( } ) - // Without pnp esbuild is transforming modules disabled for the browser - // to empty CommonJS modules. In pnp mode we achieve the same by passing - // empty contents causing esbuild to produce an empty CommonJS module. - build.onLoad({ filter: /.*/, namespace: 'browser-external' }, () => { - return { contents: '' } - }) - build.onLoad({ filter: /.*/ }, async (args) => ({ contents: await require('fs').promises.readFile(args.path), loader: 'default' From fc7d47dddc449a3d38b309a7e6edb1f4eade2fb6 Mon Sep 17 00:00:00 2001 From: swandir Date: Sun, 20 Feb 2022 18:40:25 +0300 Subject: [PATCH 6/6] fix(optimizer): revert to commonjs proxy --- .../src/node/optimizer/esbuildDepPlugin.ts | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index a6262db0c65913..d6a0eccea0c126 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -194,13 +194,37 @@ export function esbuildDepPlugin( } }) - // Returning empty contents that will be turned by ESBuild - // into a CommonJS module exporting an empty object. - // This is what ESBuild does when bundling a dependency - // starting from a non-browser-external entrypoint. - build.onLoad({ filter: /.*/, namespace: 'browser-external' }, () => { - return { contents: '' } - }) + // Contents of this module will be processed by ESBuild during + // dependency bundling. If it's an ES module with a single + // default export, ESBuild will fail with an error if a named import is used: + // [ERROR] No matching export in "browser-external:*" for import "*" + // This only happens in Yarn PnP mode because otherwise ESBuild + // handles browser mapping internally. + // Aside from PnP, these cases go through this proxy module: + // - deep package imports that are directly disabled via browser field; + // - direct default imports of node builtins; + // - node builtins re-exported via a package that does not remap for the browser. + build.onLoad( + { filter: /.*/, namespace: 'browser-external' }, + ({ path }) => { + return { + contents: `\ +module.exports = new Proxy({}, { + get() { + return new Proxy({}, { + get() { + throw new Error( + 'Module "${path}" has been externalized for ' + + 'browser compatibility and cannot be accessed in client code.' + ) + } + }) + } +}) +` + } + } + ) // yarn 2 pnp compat if (isRunningWithYarnPnp) {