From bbc5989c3686f213ec82457bd623f0db0205794c Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Thu, 17 Sep 2020 18:44:11 +0800 Subject: [PATCH 01/20] fix(dev): preserve named exports when optimizing cjs deps --- .vscode/launch.json | 17 +++++ package.json | 3 + playground/App.vue | 7 +- .../TestCjsDepNamedExport.vue | 30 ++++++++ playground/package.json | 2 + playground/yarn.lock | 47 ++++++++++++ src/node/optimizer/index.ts | 2 + src/node/optimizer/pluginCjsEntry.ts | 73 +++++++++++++++++++ yarn.lock | 54 +++++++++++++- 9 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 playground/cjs-dep-named-export/TestCjsDepNamedExport.vue create mode 100644 src/node/optimizer/pluginCjsEntry.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000000000..31201ed103ee89 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "port": 9229, + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "pwa-node" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 0129a24a99743e..f182b529ecb199 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "brotli-size": "^4.0.0", "chalk": "^4.1.0", "chokidar": "^3.4.2", + "cjs-module-lexer": "^0.2.12", "clean-css": "^4.2.3", "debug": "^4.1.1", "dotenv": "^8.2.0", @@ -130,6 +131,8 @@ "prettier": "^2.0.4", "pug": "^2.0.4", "puppeteer": "^3.0.0", + "react": "^16.13.1", + "react-dom": "^16.13.1", "rimraf": "^3.0.2", "sass": "^1.26.5", "source-map-support": "^0.5.19", diff --git a/playground/App.vue b/playground/App.vue index aec75469f06796..17a6126d235a70 100644 --- a/playground/App.vue +++ b/playground/App.vue @@ -30,7 +30,8 @@ - + + diff --git a/playground/package.json b/playground/package.json index c2877e74c8715c..3f369bfec1c3c6 100644 --- a/playground/package.json +++ b/playground/package.json @@ -10,6 +10,8 @@ "moment": "link:../node_modules/moment", "@babel/runtime": "link:../node_modules/@babel/runtime", "normalize.css": "link:../node_modules/normalize.css", + "react": "link:../node_modules/react", + "react-dom": "link:../node_modules/react-dom", "optimize-linked": "link:./optimize-linked", "resolve-browser-field-test-package": "link:./resolve/browser-field", "rewrite-optimized-test-package": "link:./resolve/rewrite-optimized/test-package", diff --git a/playground/yarn.lock b/playground/yarn.lock index 38b416d65ca9d0..ba714980ee5efb 100644 --- a/playground/yarn.lock +++ b/playground/yarn.lock @@ -10,6 +10,11 @@ version "0.0.0" uid "" +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + "linked-dep@link:./optimize-linked/linked-dep": version "0.0.0" uid "" @@ -18,6 +23,13 @@ version "0.0.0" uid "" +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + "moment@link:../node_modules/moment": version "0.0.0" uid "" @@ -26,10 +38,37 @@ version "0.0.0" uid "" +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + "optimize-linked@link:./optimize-linked": version "0.0.0" uid "" +prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +"react-dom@link:../node_modules/react-dom": + version "0.0.0" + uid "" + +react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +"react@link:../node_modules/react": + version "0.0.0" + uid "" + regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" @@ -46,3 +85,11 @@ regenerator-runtime@^0.13.4: "rewrite-unoptimized-test-package@link:./resolve/rewrite-unoptimized/test-package": version "0.0.0" uid "" + +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" diff --git a/src/node/optimizer/index.ts b/src/node/optimizer/index.ts index c8b14f64fc9bbd..be0660cbd34633 100644 --- a/src/node/optimizer/index.ts +++ b/src/node/optimizer/index.ts @@ -16,6 +16,7 @@ import { init, parse } from 'es-module-lexer' import chalk from 'chalk' import { Ora } from 'ora' import { createDepAssetPlugin, depAssetExternalPlugin } from './pluginAssets' +import { createCjsEntryNamedExportPlugin } from './pluginCjsEntry' const debug = require('debug')('vite:optimize') @@ -187,6 +188,7 @@ export async function optimizeDeps( ...config.rollupInputOptions, plugins: [ depAssetExternalPlugin, + createCjsEntryNamedExportPlugin(), ...(await createBaseRollupPlugins(root, resolver, config)), createDepAssetPlugin(resolver, root) ] diff --git a/src/node/optimizer/pluginCjsEntry.ts b/src/node/optimizer/pluginCjsEntry.ts new file mode 100644 index 00000000000000..cc6f03fd193dc0 --- /dev/null +++ b/src/node/optimizer/pluginCjsEntry.ts @@ -0,0 +1,73 @@ +import fs from 'fs-extra' +import * as path from 'path' +import { Plugin } from 'rollup' +// @ts-ignore +import cjsLexer from 'cjs-module-lexer' +const { init: initCjsLexer, parse: parseCjs } = cjsLexer + +export function createCjsEntryNamedExportPlugin(): Plugin { + const proxyEntries: { [proxyModuleId: string]: string } = {} + return { + name: 'vite:cjs-entry-named-export', + // @ts-ignore + async options(opts) { + const input = opts.input + await initCjsLexer() + if (typeof input !== 'object' || Array.isArray(input)) { + throw new Error('expect rollup input to be object') + } + Object.entries(input).map(([id, entryFilePath]) => { + const cjsExports = Object.keys(getCjsExports(entryFilePath, {})) + if (cjsExports.length > 0) { + debugger + const exportCode: string[] = [ + `import * as __proxied_cjs_module__ from '${entryFilePath}'`, + // @rollup/plugin-commonjs creates a default export for each cjs module + `export { default } from '${entryFilePath}';` + ] + cjsExports + .filter((exportName) => exportName !== 'default') + .forEach((exportName) => { + exportCode.push( + `export const ${exportName} = __proxied_cjs_module__['${exportName}'];` + ) + }) + const proxyModuleId = `\0@proxyEntries/${id}` + proxyEntries[proxyModuleId] = exportCode.join('\n') + input[id] = proxyModuleId + } + }) + return opts + }, + resolveId(importee, importer) { + if (proxyEntries[importee]) return importee + }, + load(id) { + if (proxyEntries[id]) return proxyEntries[id] + } + } +} + +function getCjsExports( + filePath: string, + result: { [exportName: string]: true } +) { + const content = fs.readFileSync(filePath, 'utf-8') + let parseResult: { exports: string[]; reexports: string[] } + try { + parseResult = parseCjs(content) + } catch (error) { + // not a commonjs module + return result + } + const { exports: cjsExports, reexports: cjsReExports } = parseResult + cjsExports.forEach((exportName: string) => { + result[exportName] = true + }) + cjsReExports.forEach((cjsReExport: string) => { + // Example : /node_modules/react/index.js re-export from "./cjs/react.production.min.js" + const reExportsFiles = path.resolve(path.dirname(filePath), cjsReExport) + getCjsExports(reExportsFiles, result) + }) + return result +} diff --git a/yarn.lock b/yarn.lock index 5c7f5a85516ee3..674707eea45408 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1627,6 +1627,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +cjs-module-lexer@^0.2.12: + version "0.2.12" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.2.12.tgz#f7e8fe5ceed35e92d33e8b28bbf2ed390725f3ab" + integrity sha512-dCHkd6cc45OFYhZS4Oj598SdIpnLfbyR/wesguT0xzwRPKsW10FScy+f+UW4UEFj0kL8VlWSuQwpXezRqwMOnw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3978,7 +3983,7 @@ js-stringify@^1.0.1: resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -4411,6 +4416,13 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -4843,7 +4855,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -5372,6 +5384,15 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.4" +prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + proxy-from-env@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -5553,11 +5574,30 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -react-is@^16.12.0: +react-dom@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + +react-is@^16.12.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" @@ -5965,6 +6005,14 @@ saxes@^5.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + selfsigned@^1.10.7: version "1.10.7" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" From 2bf3a1ebede3e64df482a474adab6d90c6606339 Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Thu, 17 Sep 2020 18:46:07 +0800 Subject: [PATCH 02/20] chore: delete my vscode config --- .vscode/launch.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 31201ed103ee89..00000000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Attach", - "port": 9229, - "request": "attach", - "skipFiles": [ - "/**" - ], - "type": "pwa-node" - } - ] -} \ No newline at end of file From ec1857bf583dd03e0a73cd5c6df987202f0edd0f Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Thu, 17 Sep 2020 20:55:44 +0800 Subject: [PATCH 03/20] feat(dev): allow users to manually specify cjs named exports --- package.json | 1 + .../TestCjsDepNamedExport.vue | 5 +++- playground/package.json | 1 + playground/vite.config.ts | 5 +++- playground/yarn.lock | 4 ++++ src/node/optimizer/index.ts | 9 +++++++- src/node/optimizer/pluginCjsEntry.ts | 23 +++++++++++++++---- yarn.lock | 2 +- 8 files changed, 42 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f182b529ecb199..910db088131bbc 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "postcss-nesting": "^7.0.1", "preact": "^10.4.1", "prettier": "^2.0.4", + "prop-types": "^15.7.2", "pug": "^2.0.4", "puppeteer": "^3.0.0", "react": "^16.13.1", diff --git a/playground/cjs-dep-named-export/TestCjsDepNamedExport.vue b/playground/cjs-dep-named-export/TestCjsDepNamedExport.vue index c7610d0c9573f4..ec96e372d448bd 100644 --- a/playground/cjs-dep-named-export/TestCjsDepNamedExport.vue +++ b/playground/cjs-dep-named-export/TestCjsDepNamedExport.vue @@ -8,6 +8,7 @@ diff --git a/src/node/optimizer/entryAnalysisPlugin.ts b/src/node/optimizer/entryAnalysisPlugin.ts new file mode 100644 index 00000000000000..b15dc02642e4e6 --- /dev/null +++ b/src/node/optimizer/entryAnalysisPlugin.ts @@ -0,0 +1,57 @@ +import { Plugin } from 'rollup' +import { init, parse } from 'es-module-lexer' +import * as fs from 'fs-extra' + +export function entryAnalysisPlugin(): Plugin { + const analysis: { mayBeCjs: { [name: string]: true } } = { mayBeCjs: {} } + return { + name: 'vite:cjs-entry-named-export', + async generateBundle(options, bundles) { + await init + Object.values(bundles).forEach((bundle) => { + if (bundle.type === 'chunk' && bundle.isEntry) { + if (bundle.facadeModuleId) { + const [, exports] = parse( + fs.readFileSync(bundle.facadeModuleId, 'utf-8') + ) + if (exports.length === 0) { + // likely commonjs + analysis.mayBeCjs[bundle.name] = true + } + } + } + }) + this.emitFile({ + type: 'asset', + fileName: '_analysis.json', + source: JSON.stringify(analysis) + }) + } + } +} + +async function isCjs(filePath: string) {} + +// function getCjsExports( +// filePath: string, +// result: { [exportName: string]: true } +// ) { +// const content = fs.readFileSync(filePath, 'utf-8') +// let parseResult: { exports: string[]; reexports: string[] } +// try { +// parseResult = parseCjs(content) +// } catch (error) { +// // not a commonjs module +// return result +// } +// const { exports: cjsExports, reexports: cjsReExports } = parseResult +// cjsExports.forEach((exportName: string) => { +// result[exportName] = true +// }) +// cjsReExports.forEach((cjsReExport: string) => { +// // Example : react/index.js re-export from "./cjs/react.production.min.js" +// const reExportsFiles = resolveFrom(path.dirname(filePath), cjsReExport) +// getCjsExports(reExportsFiles, result) +// }) +// return result +// } diff --git a/src/node/optimizer/index.ts b/src/node/optimizer/index.ts index 4e298913ad3d00..89d1aeed53ad67 100644 --- a/src/node/optimizer/index.ts +++ b/src/node/optimizer/index.ts @@ -16,7 +16,7 @@ import { init, parse } from 'es-module-lexer' import chalk from 'chalk' import { Ora } from 'ora' import { createDepAssetPlugin, depAssetExternalPlugin } from './pluginAssets' -import { createCjsEntryNamedExportPlugin } from './pluginCjsEntry' +import { entryAnalysisPlugin } from './entryAnalysisPlugin' const debug = require('debug')('vite:optimize') @@ -195,7 +195,7 @@ export async function optimizeDeps( ...config.rollupInputOptions, plugins: [ depAssetExternalPlugin, - createCjsEntryNamedExportPlugin(options.cjsExports), + entryAnalysisPlugin(), ...(await createBaseRollupPlugins(root, resolver, config)), createDepAssetPlugin(resolver, root) ] @@ -218,6 +218,10 @@ export async function optimizeDeps( await fs.ensureDir(path.dirname(filePath)) await fs.writeFile(filePath, chunk.code) } + if (chunk.type === 'asset' && chunk.fileName === '_analysis.json') { + const filePath = path.join(cacheDir, chunk.fileName) + await fs.writeFile(filePath, chunk.source) + } } await fs.writeFile(hashPath, depHash) diff --git a/src/node/optimizer/pluginCjsEntry.ts b/src/node/optimizer/pluginCjsEntry.ts deleted file mode 100644 index 97bddd76c885b0..00000000000000 --- a/src/node/optimizer/pluginCjsEntry.ts +++ /dev/null @@ -1,88 +0,0 @@ -import fs from 'fs-extra' -import * as path from 'path' -import { Plugin } from 'rollup' -// @ts-ignore -import cjsLexer from 'cjs-module-lexer' -import { resolveFrom } from '../utils' -const { init: initCjsLexer, parse: parseCjs } = cjsLexer - -export function createCjsEntryNamedExportPlugin( - manualNamedExports: { - [depName: string]: string[] - } = {} -): Plugin { - const proxyEntries: { [proxyModuleId: string]: string } = {} - return { - name: 'vite:cjs-entry-named-export', - // @ts-ignore - async options(opts) { - const input = opts.input - await initCjsLexer() - if (typeof input !== 'object' || Array.isArray(input)) { - throw new Error('expect rollup input to be object') - } - Object.entries(input).map(([id, entryFilePath]) => { - const cjsExportsObj: { - [exportName: string]: true - } = {} - if (manualNamedExports[id]) { - manualNamedExports[id].forEach((exportName) => { - cjsExportsObj[exportName] = true - }) - } - getCjsExports(entryFilePath, cjsExportsObj) - - const cjsExports = Object.keys(cjsExportsObj) - if (cjsExports.length > 0) { - debugger - const exportCode: string[] = [ - `import * as __proxied_cjs_module__ from '${entryFilePath}'`, - // @rollup/plugin-commonjs creates a default export for each cjs module - `export { default } from '${entryFilePath}';` - ] - cjsExports - .filter((exportName) => exportName !== 'default') - .forEach((exportName) => { - exportCode.push( - `export const ${exportName} = __proxied_cjs_module__['${exportName}'];` - ) - }) - const proxyModuleId = `\0@proxyEntries/${id}` - proxyEntries[proxyModuleId] = exportCode.join('\n') - input[id] = proxyModuleId - } - }) - return opts - }, - resolveId(importee, importer) { - if (proxyEntries[importee]) return importee - }, - load(id) { - if (proxyEntries[id]) return proxyEntries[id] - } - } -} - -function getCjsExports( - filePath: string, - result: { [exportName: string]: true } -) { - const content = fs.readFileSync(filePath, 'utf-8') - let parseResult: { exports: string[]; reexports: string[] } - try { - parseResult = parseCjs(content) - } catch (error) { - // not a commonjs module - return result - } - const { exports: cjsExports, reexports: cjsReExports } = parseResult - cjsExports.forEach((exportName: string) => { - result[exportName] = true - }) - cjsReExports.forEach((cjsReExport: string) => { - // Example : react/index.js re-export from "./cjs/react.production.min.js" - const reExportsFiles = resolveFrom(path.dirname(filePath), cjsReExport) - getCjsExports(reExportsFiles, result) - }) - return result -} diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index 374a10816e0848..152a45c35a8f00 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -1,5 +1,6 @@ import { ServerPlugin } from '.' import path from 'path' +import * as fs from 'fs-extra' import LRUCache from 'lru-cache' import MagicString from 'magic-string' import { @@ -7,6 +8,9 @@ import { parse as parseImports, ImportSpecifier } from 'es-module-lexer' +import { ImportDeclaration } from '@babel/types' +import { makeLegalIdentifier } from '@rollup/pluginutils' + import { InternalResolver, resolveBareModuleRequest, @@ -33,6 +37,8 @@ import { import chalk from 'chalk' import { isCSSRequest } from '../utils/cssUtils' import { envPublicPath } from './serverPluginEnv' +import { resolveOptimizedCacheDir } from '../optimizer' +import { parse } from '../utils/babelParse' const debug = require('debug')('vite:rewrite') @@ -150,8 +156,15 @@ export function rewriteImports( importeeMap.set(importer, currentImportees) for (let i = 0; i < imports.length; i++) { - const { s: start, e: end, d: dynamicIndex } = imports[i] + const { + s: start, + e: end, + d: dynamicIndex, + ss: expStart, + se: expEnd + } = imports[i] let id = source.substring(start, end) + let hasLiteralDynamicId = false if (dynamicIndex >= 0) { const literalIdMatch = id.match(/^(?:'([^']+)'|"([^"]+)")$/) @@ -176,11 +189,31 @@ export function rewriteImports( if (resolved !== id) { debug(` "${id}" --> "${resolved}"`) - s.overwrite( - start, - end, - hasLiteralDynamicId ? `'${resolved}'` : resolved - ) + + if (isOptimizedCjs(root, id)) { + if (dynamicIndex === -1) { + const exp = source.substring(expStart, expEnd) + const replacement = transformCjsImport(exp, id, resolved, i) + s.overwrite(expStart, expEnd, replacement) + } else if (hasLiteralDynamicId) { + // es-module-lexer give us wrong expEnd for dynamic import: + // https://github.com/guybedford/es-module-lexer/issues/53 + // So we can only use `start` and `end` for now + // For example, for import('path') + // replace the 'path' with + // '${resolved}').then(m=>m.default + // will give us + // import('${resolved}').then(m=>m.default) + s.overwrite(start, end, `'${resolved}').then(m=>m.default`) + } + } else { + s.overwrite( + start, + end, + hasLiteralDynamicId ? `'${resolved}'` : resolved + ) + } + hasReplaced = true } @@ -294,3 +327,69 @@ export const resolveImport = ( } return id } + +const analysisCache = new Map() + +function getAnalysis(root: string): { mayBeCjs: { [name: string]: true } } { + if (analysisCache.has(root)) return analysisCache.get(root)! + const cacheDir = resolveOptimizedCacheDir(root) + if (!cacheDir) throw new Error('cacheDir not found') + const analysis = fs.readJsonSync(path.join(cacheDir, '_analysis.json')) + analysisCache.set(root, analysis) + return analysis +} + +function isOptimizedCjs(root: string, id: string) { + const analysis = getAnalysis(root) + return !!analysis.mayBeCjs[id] +} + +function transformCjsImport( + exp: string, + id: string, + resolvedPath: string, + importIndex: number +) { + const ast = parse(exp)[0] as ImportDeclaration + const importNames: { importedName: string; localName: string }[] = [] + + ast.specifiers.forEach((obj) => { + if (obj.type === 'ImportSpecifier') { + const importedName = obj.imported.name + const localName = obj.local.name + importNames.push({ importedName, localName }) + } else if (obj.type === 'ImportDefaultSpecifier') { + importNames.push({ importedName: 'default', localName: obj.local.name }) + } else if (obj.type === 'ImportNamespaceSpecifier') { + importNames.push({ importedName: '*', localName: obj.local.name }) + } + }) + + return generateCjsImport(importNames, id, resolvedPath, importIndex) +} + +function generateCjsImport( + importNames: { importedName: string; localName: string }[], + id: string, + resolvedPath: string, + importIndex: number +) { + debugger + // If there is multiple import for same id in one file, + // importIndex will prevent the cjsModuleName to be duplicate + const cjsModuleName = makeLegalIdentifier( + `$viteCjsImport${importIndex}_${id}` + ) + const lines: string[] = [ + `import ${cjsModuleName} from "${resolvedPath}";`, + `console.log("${cjsModuleName}", ${cjsModuleName});` + ] + importNames.forEach(({ importedName, localName }) => { + if (importedName === '*' || importedName === 'default') { + lines.push(`const ${localName} = ${cjsModuleName};`) + } else { + lines.push(`const ${localName} = ${cjsModuleName}["${importedName}"];`) + } + }) + return lines.join('\n') +} From 463079e478ec0519ae1f9d532a7588cbadeeb490 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:21:56 +0800 Subject: [PATCH 08/20] fix: delete unused code --- src/node/optimizer/entryAnalysisPlugin.ts | 26 ----------------------- 1 file changed, 26 deletions(-) diff --git a/src/node/optimizer/entryAnalysisPlugin.ts b/src/node/optimizer/entryAnalysisPlugin.ts index b15dc02642e4e6..c4ee97594b4521 100644 --- a/src/node/optimizer/entryAnalysisPlugin.ts +++ b/src/node/optimizer/entryAnalysisPlugin.ts @@ -29,29 +29,3 @@ export function entryAnalysisPlugin(): Plugin { } } } - -async function isCjs(filePath: string) {} - -// function getCjsExports( -// filePath: string, -// result: { [exportName: string]: true } -// ) { -// const content = fs.readFileSync(filePath, 'utf-8') -// let parseResult: { exports: string[]; reexports: string[] } -// try { -// parseResult = parseCjs(content) -// } catch (error) { -// // not a commonjs module -// return result -// } -// const { exports: cjsExports, reexports: cjsReExports } = parseResult -// cjsExports.forEach((exportName: string) => { -// result[exportName] = true -// }) -// cjsReExports.forEach((cjsReExport: string) => { -// // Example : react/index.js re-export from "./cjs/react.production.min.js" -// const reExportsFiles = resolveFrom(path.dirname(filePath), cjsReExport) -// getCjsExports(reExportsFiles, result) -// }) -// return result -// } From 61ebbabe9a63daf5ec5cc606bfb5708eaaa9c8a0 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:24:54 +0800 Subject: [PATCH 09/20] test: fix test --- test/test.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/test.js b/test/test.js index 41f8596a112d42..e1abc1b9893c84 100644 --- a/test/test.js +++ b/test/test.js @@ -735,8 +735,13 @@ describe('vite', () => { }) test('optimizing commonjs dependencies and do named-import from it', async () => { - expect((await getText('.cjs-dep-named-export')).trim()).toBe( - 'result: success' + expect((await getText('.cjs-dep-named-export-static')).trim()).toBe( + 'static import result: success' + ) + await click('.cjs-dep-named-export-dynamic-load') + await expectByPolling( + () => getText('.cjs-dep-named-export-dynamic').trim(), + 'dynamic import result: success' ) }) } From 9884e0e8f94d573e2646815d5c11c1905aee8f04 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:33:23 +0800 Subject: [PATCH 10/20] chore: remove unused dep --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 910db088131bbc..89d637e751920a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "brotli-size": "^4.0.0", "chalk": "^4.1.0", "chokidar": "^3.4.2", - "cjs-module-lexer": "^0.2.12", "clean-css": "^4.2.3", "debug": "^4.1.1", "dotenv": "^8.2.0", diff --git a/yarn.lock b/yarn.lock index cdbf8a68441405..b4d93efc03c035 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1627,11 +1627,6 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cjs-module-lexer@^0.2.12: - version "0.2.12" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.2.12.tgz#f7e8fe5ceed35e92d33e8b28bbf2ed390725f3ab" - integrity sha512-dCHkd6cc45OFYhZS4Oj598SdIpnLfbyR/wesguT0xzwRPKsW10FScy+f+UW4UEFj0kL8VlWSuQwpXezRqwMOnw== - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" From 742064428311ef1b21ae98ab336361ab30b458e8 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:34:49 +0800 Subject: [PATCH 11/20] fix: remove unused config --- playground/vite.config.ts | 5 +---- src/node/optimizer/index.ts | 7 ------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/playground/vite.config.ts b/playground/vite.config.ts index a16dd4f21ed602..7e68d4a4e62d88 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -15,10 +15,7 @@ const config: UserConfig = { vueCustomBlockTransforms: { i18n: i18nTransform }, optimizeDeps: { exclude: ['bootstrap', 'rewrite-unoptimized-test-package'], - link: ['optimize-linked'], - cjsExports: { - 'prop-types': ['oneOfType'] - } + link: ['optimize-linked'] }, cssPreprocessOptions: { less: { diff --git a/src/node/optimizer/index.ts b/src/node/optimizer/index.ts index 89d1aeed53ad67..384fb7211f71b3 100644 --- a/src/node/optimizer/index.ts +++ b/src/node/optimizer/index.ts @@ -54,13 +54,6 @@ export interface DepOptimizationOptions { * @default true */ auto?: boolean - /** - * Manually specify the named exports of commonjs dependencies. - * Note that vite already do some static analysis, so most cjs dependencies should work out of box without this config (.e.g react, react-dom). - */ - cjsExports?: { - [depName: string]: string[] - } } export const OPTIMIZE_CACHE_DIR = `node_modules/.vite_opt_cache` From c7c26554327d7b88e1c983f57870ddd522a7c870 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:42:35 +0800 Subject: [PATCH 12/20] test: fix test --- test/test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index e1abc1b9893c84..ab8d9ffda22e15 100644 --- a/test/test.js +++ b/test/test.js @@ -740,7 +740,8 @@ describe('vite', () => { ) await click('.cjs-dep-named-export-dynamic-load') await expectByPolling( - () => getText('.cjs-dep-named-export-dynamic').trim(), + async () => + ((await getText('.cjs-dep-named-export-dynamic')) || '').trim(), 'dynamic import result: success' ) }) From 4f905dd84e1658a13fa15f58aec515b5ee011d5e Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:51:02 +0800 Subject: [PATCH 13/20] fix: fix code style --- src/node/server/serverPluginModuleRewrite.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index 152a45c35a8f00..2ae6bef718c6b4 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -164,7 +164,6 @@ export function rewriteImports( se: expEnd } = imports[i] let id = source.substring(start, end) - let hasLiteralDynamicId = false if (dynamicIndex >= 0) { const literalIdMatch = id.match(/^(?:'([^']+)'|"([^"]+)")$/) @@ -189,7 +188,6 @@ export function rewriteImports( if (resolved !== id) { debug(` "${id}" --> "${resolved}"`) - if (isOptimizedCjs(root, id)) { if (dynamicIndex === -1) { const exp = source.substring(expStart, expEnd) @@ -198,12 +196,10 @@ export function rewriteImports( } else if (hasLiteralDynamicId) { // es-module-lexer give us wrong expEnd for dynamic import: // https://github.com/guybedford/es-module-lexer/issues/53 - // So we can only use `start` and `end` for now + // So we can't use expEnd for now. // For example, for import('path') - // replace the 'path' with - // '${resolved}').then(m=>m.default - // will give us - // import('${resolved}').then(m=>m.default) + // replace the 'path' with '${resolved}').then(m=>m.default + // will give us import('${resolved}').then(m=>m.default) s.overwrite(start, end, `'${resolved}').then(m=>m.default`) } } else { @@ -213,7 +209,6 @@ export function rewriteImports( hasLiteralDynamicId ? `'${resolved}'` : resolved ) } - hasReplaced = true } @@ -330,6 +325,10 @@ export const resolveImport = ( const analysisCache = new Map() +/** + * get analysis result from optimize step: + * which optimized dependencies may be commonjs + */ function getAnalysis(root: string): { mayBeCjs: { [name: string]: true } } { if (analysisCache.has(root)) return analysisCache.get(root)! const cacheDir = resolveOptimizedCacheDir(root) @@ -374,7 +373,6 @@ function generateCjsImport( resolvedPath: string, importIndex: number ) { - debugger // If there is multiple import for same id in one file, // importIndex will prevent the cjsModuleName to be duplicate const cjsModuleName = makeLegalIdentifier( From adc01db68c093c98604a8ccba7daed7a30def311 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sun, 20 Sep 2020 22:55:15 +0800 Subject: [PATCH 14/20] fix: remove console.log --- src/node/server/serverPluginModuleRewrite.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index f84ebec0395695..5307021b061c3a 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -353,7 +353,7 @@ function transformCjsImport( id: string, resolvedPath: string, importIndex: number -) { +): string { const ast = parse(exp)[0] as ImportDeclaration const importNames: { importedName: string; localName: string }[] = [] @@ -377,16 +377,13 @@ function generateCjsImport( id: string, resolvedPath: string, importIndex: number -) { +): string { // If there is multiple import for same id in one file, // importIndex will prevent the cjsModuleName to be duplicate const cjsModuleName = makeLegalIdentifier( `$viteCjsImport${importIndex}_${id}` ) - const lines: string[] = [ - `import ${cjsModuleName} from "${resolvedPath}";`, - `console.log("${cjsModuleName}", ${cjsModuleName});` - ] + const lines: string[] = [`import ${cjsModuleName} from "${resolvedPath}";`] importNames.forEach(({ importedName, localName }) => { if (importedName === '*' || importedName === 'default') { lines.push(`const ${localName} = ${cjsModuleName};`) From d8247e68631972694b73b39c91029ec1fd48166e Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Wed, 30 Sep 2020 23:59:26 +0800 Subject: [PATCH 15/20] fix(dev): simplify dynamic import rewriting logic --- src/node/server/serverPluginModuleRewrite.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index 5307021b061c3a..ff4d7025942e04 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -199,13 +199,12 @@ export function rewriteImports( const replacement = transformCjsImport(exp, id, resolved, i) s.overwrite(expStart, expEnd, replacement) } else if (hasLiteralDynamicId) { - // es-module-lexer give us wrong expEnd for dynamic import: - // https://github.com/guybedford/es-module-lexer/issues/53 - // So we can't use expEnd for now. - // For example, for import('path') - // replace the 'path' with '${resolved}').then(m=>m.default - // will give us import('${resolved}').then(m=>m.default) - s.overwrite(start, end, `'${resolved}').then(m=>m.default`) + // rewrite `import('package')` + s.overwrite( + dynamicIndex, + end + 1, + `import('${resolved}').then(m=>m.default)` + ) } } else { s.overwrite( From 87d42115f442e31dd0794cb7cc521ed62437473e Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Thu, 15 Oct 2020 20:34:51 +0800 Subject: [PATCH 16/20] feat(dev): use cjs detection from @rollup/plugin-commonjs --- package.json | 3 ++- src/node/build/index.ts | 2 +- src/node/optimizer/entryAnalysisPlugin.ts | 14 ++++--------- src/node/server/serverPluginModuleRewrite.ts | 9 ++++++--- src/node/tsconfig.json | 3 +++ yarn.lock | 21 ++++++++++++++++---- 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 54c07b99f92861..83dfb80e033d69 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ }, "dependencies": { "@babel/parser": "^7.11.5", + "@csr632/rollup-plugin-commonjs": "^15.1.0", "@rollup/plugin-commonjs": "^15.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^9.0.0", @@ -91,7 +92,7 @@ "postcss-import": "^12.0.1", "postcss-load-config": "^2.1.0", "resolve": "^1.17.0", - "rollup": "^2.26.11", + "rollup": "^2.30.0", "rollup-plugin-dynamic-import-variables": "^1.1.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-vue": "^6.0.0-beta.10", diff --git a/src/node/build/index.ts b/src/node/build/index.ts index 7f4849a5cdc1a2..ef624beabc2b2c 100644 --- a/src/node/build/index.ts +++ b/src/node/build/index.ts @@ -170,7 +170,7 @@ export async function createBaseRollupPlugins( dedupe: options.rollupDedupe || [], mainFields }), - require('@rollup/plugin-commonjs')({ + require('@csr632/rollup-plugin-commonjs')({ extensions: ['.js', '.cjs'] }), dynamicImport({ diff --git a/src/node/optimizer/entryAnalysisPlugin.ts b/src/node/optimizer/entryAnalysisPlugin.ts index c4ee97594b4521..39f48cd164db5d 100644 --- a/src/node/optimizer/entryAnalysisPlugin.ts +++ b/src/node/optimizer/entryAnalysisPlugin.ts @@ -1,22 +1,16 @@ import { Plugin } from 'rollup' -import { init, parse } from 'es-module-lexer' -import * as fs from 'fs-extra' export function entryAnalysisPlugin(): Plugin { - const analysis: { mayBeCjs: { [name: string]: true } } = { mayBeCjs: {} } + const analysis: { isCommonjs: { [name: string]: true } } = { isCommonjs: {} } return { name: 'vite:cjs-entry-named-export', async generateBundle(options, bundles) { - await init Object.values(bundles).forEach((bundle) => { if (bundle.type === 'chunk' && bundle.isEntry) { if (bundle.facadeModuleId) { - const [, exports] = parse( - fs.readFileSync(bundle.facadeModuleId, 'utf-8') - ) - if (exports.length === 0) { - // likely commonjs - analysis.mayBeCjs[bundle.name] = true + const facadeInfo = this.getModuleInfo(bundle.facadeModuleId) + if (facadeInfo?.meta?.commonjs?.isCommonJS) { + analysis.isCommonjs[bundle.name] = true } } } diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index ff4d7025942e04..a7d0c9d3952742 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -327,13 +327,16 @@ export const resolveImport = ( return id } -const analysisCache = new Map() +const analysisCache = new Map< + string, + { isCommonjs: { [name: string]: true } } +>() /** * get analysis result from optimize step: * which optimized dependencies may be commonjs */ -function getAnalysis(root: string): { mayBeCjs: { [name: string]: true } } { +function getAnalysis(root: string): { isCommonjs: { [name: string]: true } } { if (analysisCache.has(root)) return analysisCache.get(root)! const cacheDir = resolveOptimizedCacheDir(root) if (!cacheDir) throw new Error('cacheDir not found') @@ -344,7 +347,7 @@ function getAnalysis(root: string): { mayBeCjs: { [name: string]: true } } { function isOptimizedCjs(root: string, id: string) { const analysis = getAnalysis(root) - return !!analysis.mayBeCjs[id] + return !!analysis.isCommonjs[id] } function transformCjsImport( diff --git a/src/node/tsconfig.json b/src/node/tsconfig.json index 9ff62a09480474..77b0b8cbf2a7d6 100644 --- a/src/node/tsconfig.json +++ b/src/node/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + // compile away optional-chaining-operator + // node support table: https://node.green/#ES2020-features-optional-chaining-operator----- + "target": "ES2019", "outDir": "../../dist", "module": "commonjs", "lib": ["ESNext"], diff --git a/yarn.lock b/yarn.lock index b4d93efc03c035..26c2d507aa831d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -278,6 +278,19 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@csr632/rollup-plugin-commonjs@^15.1.0": + version "15.1.0" + resolved "https://registry.yarnpkg.com/@csr632/rollup-plugin-commonjs/-/rollup-plugin-commonjs-15.1.0.tgz#a076b57c7310be81ee92e36479f739f5834bfd1a" + integrity sha512-GFd19sgtA1b48mLXtcPL6caAyclSDFJFd3VV8rxXPSH1R5q8vMcMJD22K3hcRRkmkJ83BFcYusTquteOht1Nsg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -5925,10 +5938,10 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.26.11: - version "2.26.11" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.11.tgz#4fc31de9c7b83d50916fc8395f8c3d24730cdaae" - integrity sha512-xyfxxhsE6hW57xhfL1I+ixH8l2bdoIMaAecdQiWF3N7IgJEMu99JG+daBiSZQjnBpzFxa0/xZm+3pbCdAQehHw== +rollup@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.30.0.tgz#316a1eb0389dbda4082ef2d191b31488995e4c41" + integrity sha512-j4K1hUZfgFM03DUpayd3c7kZW+2wDbI6rj7ssQxpCpL1vsGpaM0vSorxBuePFwQDFq9O2DI6AOQbm174Awsq4w== optionalDependencies: fsevents "~2.1.2" From 9225c77009b651f625c7ae046c8bd7090b5d5c03 Mon Sep 17 00:00:00 2001 From: Sirui Chen <632882184@qq.com> Date: Sat, 24 Oct 2020 13:17:15 +0800 Subject: [PATCH 17/20] fix(dev): add exception handling --- src/node/optimizer/entryAnalysisPlugin.ts | 7 +++++- src/node/server/serverPluginModuleRewrite.ts | 23 ++++++++++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/node/optimizer/entryAnalysisPlugin.ts b/src/node/optimizer/entryAnalysisPlugin.ts index 39f48cd164db5d..a547d096e95b3d 100644 --- a/src/node/optimizer/entryAnalysisPlugin.ts +++ b/src/node/optimizer/entryAnalysisPlugin.ts @@ -1,7 +1,11 @@ import { Plugin } from 'rollup' +export interface OptimizeAnalysisResult { + isCommonjs: { [name: string]: true } +} + export function entryAnalysisPlugin(): Plugin { - const analysis: { isCommonjs: { [name: string]: true } } = { isCommonjs: {} } + const analysis: OptimizeAnalysisResult = { isCommonjs: {} } return { name: 'vite:cjs-entry-named-export', async generateBundle(options, bundles) { @@ -9,6 +13,7 @@ export function entryAnalysisPlugin(): Plugin { if (bundle.type === 'chunk' && bundle.isEntry) { if (bundle.facadeModuleId) { const facadeInfo = this.getModuleInfo(bundle.facadeModuleId) + // this info is exposed by rollup commonjs plugin if (facadeInfo?.meta?.commonjs?.isCommonJS) { analysis.isCommonjs[bundle.name] = true } diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index a7d0c9d3952742..d57164bf88848d 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -38,6 +38,7 @@ import { isCSSRequest } from '../utils/cssUtils' import { envPublicPath } from './serverPluginEnv' import { resolveOptimizedCacheDir } from '../optimizer' import { parse } from '../utils/babelParse' +import { OptimizeAnalysisResult } from '../optimizer/entryAnalysisPlugin' const debug = require('debug')('vite:rewrite') @@ -327,20 +328,24 @@ export const resolveImport = ( return id } -const analysisCache = new Map< - string, - { isCommonjs: { [name: string]: true } } ->() +const analysisCache = new Map() /** - * get analysis result from optimize step: - * which optimized dependencies may be commonjs + * read analysis result from optimize step */ -function getAnalysis(root: string): { isCommonjs: { [name: string]: true } } { +function getAnalysis(root: string): OptimizeAnalysisResult { if (analysisCache.has(root)) return analysisCache.get(root)! const cacheDir = resolveOptimizedCacheDir(root) - if (!cacheDir) throw new Error('cacheDir not found') - const analysis = fs.readJsonSync(path.join(cacheDir, '_analysis.json')) + if (!cacheDir) throw new Error('[vite] cacheDir not found') + let analysis: OptimizeAnalysisResult + try { + analysis = fs.readJsonSync(path.join(cacheDir, '_analysis.json')) + } catch (error) { + throw new Error(`[vite] fail to read _analysis.json from ${cacheDir}`) + } + if (typeof analysis.isCommonjs !== 'object' || analysis.isCommonjs === null) { + throw new Error(`[vite] invalid _analysis.json`) + } analysisCache.set(root, analysis) return analysis } From ba3ccab085151d32a7443ca24d3ed73bc46be2d8 Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Tue, 27 Oct 2020 14:29:47 +0800 Subject: [PATCH 18/20] fix: use official @rollup/plugin-commonjs --- package.json | 9 ++++---- src/node/build/index.ts | 2 +- yarn.lock | 48 +++++++++++++++-------------------------- 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 83dfb80e033d69..ad3cb209cca709 100644 --- a/package.json +++ b/package.json @@ -51,11 +51,10 @@ }, "dependencies": { "@babel/parser": "^7.11.5", - "@csr632/rollup-plugin-commonjs": "^15.1.0", - "@rollup/plugin-commonjs": "^15.0.0", + "@rollup/plugin-commonjs": "^16.0.0", "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^9.0.0", - "@rollup/pluginutils": "^4.0.0", + "@rollup/plugin-node-resolve": "^10.0.0", + "@rollup/pluginutils": "^4.1.0", "@types/koa": "^2.11.4", "@types/lru-cache": "^5.1.0", "@vue/compiler-dom": "^3.0.0-rc.10", @@ -92,7 +91,7 @@ "postcss-import": "^12.0.1", "postcss-load-config": "^2.1.0", "resolve": "^1.17.0", - "rollup": "^2.30.0", + "rollup": "^2.32.1", "rollup-plugin-dynamic-import-variables": "^1.1.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-vue": "^6.0.0-beta.10", diff --git a/src/node/build/index.ts b/src/node/build/index.ts index ef624beabc2b2c..7f4849a5cdc1a2 100644 --- a/src/node/build/index.ts +++ b/src/node/build/index.ts @@ -170,7 +170,7 @@ export async function createBaseRollupPlugins( dedupe: options.rollupDedupe || [], mainFields }), - require('@csr632/rollup-plugin-commonjs')({ + require('@rollup/plugin-commonjs')({ extensions: ['.js', '.cjs'] }), dynamicImport({ diff --git a/yarn.lock b/yarn.lock index 26c2d507aa831d..a7533fac3dec0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -278,19 +278,6 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@csr632/rollup-plugin-commonjs@^15.1.0": - version "15.1.0" - resolved "https://registry.yarnpkg.com/@csr632/rollup-plugin-commonjs/-/rollup-plugin-commonjs-15.1.0.tgz#a076b57c7310be81ee92e36479f739f5834bfd1a" - integrity sha512-GFd19sgtA1b48mLXtcPL6caAyclSDFJFd3VV8rxXPSH1R5q8vMcMJD22K3hcRRkmkJ83BFcYusTquteOht1Nsg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -519,10 +506,10 @@ resolved "https://registry.yarnpkg.com/@pika/react/-/react-16.13.1.tgz#20e47997d2a2f1e5da39a8e28b75db2ec77d99c6" integrity sha512-v33Ub2QxntNpDFRnkj3tCbT6jMb7Etu7LOMQO/YAulLRIDtDvJdMwuOVJDdPYUmDtWjfWOB5xSP7nl7k0BApbQ== -"@rollup/plugin-commonjs@^15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-15.0.0.tgz#690d15a9d54ba829db93555bff9b98ff34e08574" - integrity sha512-8uAdikHqVyrT32w1zB9VhW6uGwGjhKgnDNP4pQJsjdnyF4FgCj6/bmv24c7v2CuKhq32CcyCwRzMPEElaKkn0w== +"@rollup/plugin-commonjs@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" + integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== dependencies: "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" @@ -539,10 +526,10 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-node-resolve@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz#39bd0034ce9126b39c1699695f440b4b7d2b62e6" - integrity sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg== +"@rollup/plugin-node-resolve@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz#44064a2b98df7530e66acf8941ff262fc9b4ead8" + integrity sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" @@ -560,12 +547,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@rollup/pluginutils@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.0.0.tgz#e18e9f5a3925779fc15209dd316c1bd260d195ef" - integrity sha512-b5QiJRye4JlSg29bKNEECoKbLuPXZkPEHSgEjjP1CJV1CPdDBybfYHfm6kyq8yK51h/Zsyl8OvWUrp0FUBukEQ== +"@rollup/pluginutils@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" + integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ== dependencies: - "@types/estree" "0.0.45" estree-walker "^2.0.1" picomatch "^2.2.2" @@ -675,7 +661,7 @@ resolved "https://registry.yarnpkg.com/@types/es-module-lexer/-/es-module-lexer-0.3.0.tgz#9fee3f19f64e6b3f999eeb3a70bd177a4d57a6cb" integrity sha512-XI3MGSejUQIJ3wzY0i5IHy5J3eb36M/ytgG8jIOssP08ovtRPcjpjXQqrx51AHBNBOisTS/NQNWJitI17+EwzQ== -"@types/estree@*", "@types/estree@0.0.45": +"@types/estree@*": version "0.0.45" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== @@ -5938,10 +5924,10 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.30.0: - version "2.30.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.30.0.tgz#316a1eb0389dbda4082ef2d191b31488995e4c41" - integrity sha512-j4K1hUZfgFM03DUpayd3c7kZW+2wDbI6rj7ssQxpCpL1vsGpaM0vSorxBuePFwQDFq9O2DI6AOQbm174Awsq4w== +rollup@^2.32.1: + version "2.32.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.32.1.tgz#625a92c54f5b4d28ada12d618641491d4dbb548c" + integrity sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw== optionalDependencies: fsevents "~2.1.2" From d19d14fbe81229af2f04aac44a0cf8294ee04f23 Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Tue, 27 Oct 2020 14:56:02 +0800 Subject: [PATCH 19/20] fix: fix build error --- src/node/server/serverPluginModuleRewrite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index d57164bf88848d..2b058074c4b2cd 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -365,7 +365,7 @@ function transformCjsImport( const importNames: { importedName: string; localName: string }[] = [] ast.specifiers.forEach((obj) => { - if (obj.type === 'ImportSpecifier') { + if (obj.type === 'ImportSpecifier' && obj.imported.type === 'Identifier') { const importedName = obj.imported.name const localName = obj.local.name importNames.push({ importedName, localName }) From 18435160263180c34066b2fe54c70df198d91e11 Mon Sep 17 00:00:00 2001 From: csr632 <632882184@qq.com> Date: Tue, 27 Oct 2020 17:27:27 +0800 Subject: [PATCH 20/20] fix: better error handling --- src/node/server/serverPluginModuleRewrite.ts | 21 +++++++++++--------- src/node/utils/index.ts | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/node/server/serverPluginModuleRewrite.ts b/src/node/server/serverPluginModuleRewrite.ts index 2b058074c4b2cd..f8012669c36105 100644 --- a/src/node/server/serverPluginModuleRewrite.ts +++ b/src/node/server/serverPluginModuleRewrite.ts @@ -31,7 +31,8 @@ import { cleanUrl, isExternalUrl, bareImportRE, - removeUnRelatedHmrQuery + removeUnRelatedHmrQuery, + isPlainObject } from '../utils' import chalk from 'chalk' import { isCSSRequest } from '../utils/cssUtils' @@ -328,22 +329,23 @@ export const resolveImport = ( return id } -const analysisCache = new Map() +const analysisCache = new Map() /** * read analysis result from optimize step + * If we can't find analysis result, return null + * (maybe because user set optimizeDeps.auto to false) */ -function getAnalysis(root: string): OptimizeAnalysisResult { +function getAnalysis(root: string): OptimizeAnalysisResult | null { if (analysisCache.has(root)) return analysisCache.get(root)! - const cacheDir = resolveOptimizedCacheDir(root) - if (!cacheDir) throw new Error('[vite] cacheDir not found') - let analysis: OptimizeAnalysisResult + let analysis: OptimizeAnalysisResult | null try { - analysis = fs.readJsonSync(path.join(cacheDir, '_analysis.json')) + const cacheDir = resolveOptimizedCacheDir(root) + analysis = fs.readJsonSync(path.join(cacheDir!, '_analysis.json')) } catch (error) { - throw new Error(`[vite] fail to read _analysis.json from ${cacheDir}`) + analysis = null } - if (typeof analysis.isCommonjs !== 'object' || analysis.isCommonjs === null) { + if (analysis && !isPlainObject(analysis.isCommonjs)) { throw new Error(`[vite] invalid _analysis.json`) } analysisCache.set(root, analysis) @@ -352,6 +354,7 @@ function getAnalysis(root: string): OptimizeAnalysisResult { function isOptimizedCjs(root: string, id: string) { const analysis = getAnalysis(root) + if (!analysis) return false return !!analysis.isCommonjs[id] } diff --git a/src/node/utils/index.ts b/src/node/utils/index.ts index e0fd77c4aec48a..386864994af16f 100644 --- a/src/node/utils/index.ts +++ b/src/node/utils/index.ts @@ -6,3 +6,6 @@ export * from './resolveVue' export function toArray(arg: T | T[] | undefined) { return arg === void 0 ? [] : Array.isArray(arg) ? arg : [arg] } + +export const isPlainObject = (val: unknown): val is object => + Object.prototype.toString.call(val) === '[object Object]'