From f76a986d629171421ff8993cbe6bead1297b4936 Mon Sep 17 00:00:00 2001 From: Paul Sweeney Date: Tue, 1 Sep 2020 21:27:05 +0100 Subject: [PATCH] Dynamic Import Bundle Info --- lib/impl/ImportExportResolver.js | 4 +- lib/impl/NollupCompiler.js | 15 +++++- lib/impl/PluginContext.js | 13 ++++- lib/impl/utils.js | 13 ++++- package.json | 2 +- test/cases/api/context.js | 85 ++++++++++++++++---------------- test/cases/api/generate.js | 21 ++++++++ 7 files changed, 103 insertions(+), 50 deletions(-) diff --git a/lib/impl/ImportExportResolver.js b/lib/impl/ImportExportResolver.js index 1091a4a..4b57cd1 100644 --- a/lib/impl/ImportExportResolver.js +++ b/lib/impl/ImportExportResolver.js @@ -167,8 +167,8 @@ async function resolveDynamicImport (context, input, output, node, currentpath) let resolved = await PluginLifecycle.hooks.resolveDynamicImport(context, value, currentpath); - if (resolved === false || (typeof resolved === 'object' && resolved.external)) { - output.externalDynamicImports.push(resolved.id || value); + if (isExternal(context, value) || resolved === false || (typeof resolved === 'object' && resolved.external)) { + output.externalDynamicImports.push(value); return; } diff --git a/lib/impl/NollupCompiler.js b/lib/impl/NollupCompiler.js index c0fdb5f..29986f6 100644 --- a/lib/impl/NollupCompiler.js +++ b/lib/impl/NollupCompiler.js @@ -2,7 +2,7 @@ let PluginLifecycle = require('./PluginLifecycle'); let ImportExportResolver = require('./ImportExportResolver'); let CodeGenerator = require('./CodeGenerator'); let ParseError = require('./ParseError'); -let { applyOutputFileNames, getNameFromFileName, emitAssetToBundle } = require('./utils'); +let { applyOutputFileNames, getNameFromFileName, emitAssetToBundle, applyBundleMetadata } = require('./utils'); let path = require('path'); let ErrorHandling = require('./ErrorHandling'); @@ -76,6 +76,12 @@ async function compileModule (context, filePath, parentFilePath, depth, isEntry, } }); + file.externalDynamicImports.forEach(di => { + if (emitted.externalDynamicImports.indexOf(di) === -1) { + emitted.externalDynamicImports.push(di); + } + }); + file.externalImports.forEach(ei => { let foundSource = emitted.externalImports.find(other => other.source === ei.source); if (!foundSource) { @@ -128,6 +134,7 @@ async function compileInputTarget (context, filePath, isEntry, bundleModuleIds) modules: {}, // modules id this input contains dynamicImports: [], // emitted dynamic ids externalImports: [], + externalDynamicImports: [], metaProperties: [], assets: [], chunks: [] @@ -217,10 +224,13 @@ module.exports = { type: 'chunk', map: null, modules: emitted.modules, + dynamicImports: [], imports: [], exports: context.files[file].exports, + __entryModule: file, __entryModuleId: context.files[file].moduleId, __dynamicImports: emitted.dynamicImports, + __externalDynamicImports: emitted.externalDynamicImports, __externalImports: emitted.externalImports, __metaProperties: emitted.metaProperties, }); @@ -241,6 +251,7 @@ module.exports = { type: 'chunk', map: null, modules: emitted.modules, + dynamicImports: [], imports: [], exports: context.files[chunk.id].exports, referenceId: chunk.referenceId, @@ -248,6 +259,7 @@ module.exports = { __entryModule: chunk.id, __entryModuleId: context.files[chunk.id].moduleId, __dynamicImports: emitted.dynamicImports, + __externalDynamicImports: emitted.externalDynamicImports, __externalImports: emitted.externalImports, __metaProperties: emitted.metaProperties, }); @@ -264,6 +276,7 @@ module.exports = { } applyOutputFileNames(context, bundle); + applyBundleMetadata(context, bundle); PluginLifecycle.setCurrentPhase(context, 'generate'); diff --git a/lib/impl/PluginContext.js b/lib/impl/PluginContext.js index 216341d..c4dfd11 100644 --- a/lib/impl/PluginContext.js +++ b/lib/impl/PluginContext.js @@ -93,7 +93,13 @@ module.exports = { id: id, isEntry: file.isEntry, isExternal: false, - importedIds: file.externalImports.map(i => i.source).concat(file.imports.map(i => i.source)) + importedIds: file.externalImports.map(i => i.source).concat(file.imports.map(i => i.source)), + dynamicallyImportedIds: file.externalDynamicImports.concat(file.dynamicImports), + // For importers, need to implement something + // along the lines of "emitted.moduleImporters = {}" + // that's refreshed on each compilation. + importers: [], + dynamicImporters: [] }; } else { // Probably external @@ -101,7 +107,10 @@ module.exports = { id: id, isEntry: false, isExternal: true, - importedIds: [] + importedIds: [], + dynamicallyImportedIds: [], + importers: [], + dynamicImporters: [] } } }, diff --git a/lib/impl/utils.js b/lib/impl/utils.js index 1d0085a..f4e1d50 100644 --- a/lib/impl/utils.js +++ b/lib/impl/utils.js @@ -137,6 +137,16 @@ function applyOutputFileNames (context, bundle) { }); } +function applyBundleMetadata (context, bundle) { + bundle.forEach(entry => { + if (entry.type === 'chunk') { + entry.dynamicImports = entry.__externalDynamicImports.concat(entry.__dynamicImports.map(di => { + return bundle.find(b => b.__entryModule === di).fileName; + })); + } + }); +} + function emitAssetToBundle (context, bundle, asset) { let extensionlessName = getNameFromFileName(asset.name); let extension = path.extname(asset.name); @@ -162,5 +172,6 @@ module.exports = { combineSourceMapChain, combineSourceMapChainFast, applyOutputFileNames, - emitAssetToBundle + emitAssetToBundle, + applyBundleMetadata }; \ No newline at end of file diff --git a/package.json b/package.json index 77adf54..916b140 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,6 @@ "chai": "^4.1.2", "mocha-istanbul-ui": "^0.4.0", "proxyquire": "^2.0.1", - "rollup": "^1.28.0" + "rollup": "^2.26.8" } } diff --git a/test/cases/api/context.js b/test/cases/api/context.js index d85ca7f..434660d 100644 --- a/test/cases/api/context.js +++ b/test/cases/api/context.js @@ -1674,59 +1674,58 @@ describe ('API: Plugin Context', () => { // local module let local_info = fn(path.resolve(process.cwd(), './src/main.js')); - expect(local_info).to.deep.equal({ - id: path.resolve(process.cwd(), './src/main.js'), - isEntry: true, - isExternal: false, - importedIds: ['jquery', 'underscore', path.resolve(process.cwd(), './src/lol.js')], - // hasModuleSideEffects: true - }); + expect(local_info.id).to.equal(path.resolve(process.cwd(), './src/main.js')); + expect(local_info.isEntry).to.equal(true); + expect(local_info.isExternal).to.equal(false); + expect(local_info.importedIds).to.deep.equal(['jquery', 'underscore', path.resolve(process.cwd(), './src/lol.js')]); + // expect(local_info.importers).to.deep.equal([]); + expect(local_info.dynamicallyImportedIds).to.deep.equal(['backbone', path.resolve(process.cwd(), './src/rofl.js')]); + // expect(local_info.dynamicImporters).to.deep.equal([]); let local_info_dep = fn(path.resolve(process.cwd(), './src/lol.js')); - expect(local_info_dep).to.deep.equal({ - id: path.resolve(process.cwd(), './src/lol.js'), - isEntry: false, - isExternal: false, - importedIds: [], - // hasModuleSideEffects: true - }); + expect(local_info_dep.id).to.equal(path.resolve(process.cwd(), './src/lol.js')); + expect(local_info_dep.isEntry).to.equal(false); + expect(local_info_dep.isExternal).to.equal(false); + expect(local_info_dep.importedIds).to.deep.equal([]); + // expect(local_info_dep.importers).to.deep.equal([path.resolve(process.cwd(), './src/main.js')]); + expect(local_info_dep.dynamicallyImportedIds).to.deep.equal([]); + // expect(local_info_dep.dynamicImporters).to.deep.equal([]); let external_info = fn('jquery'); - expect(external_info).to.deep.equal({ - id: 'jquery', - isEntry: false, - isExternal: true, - importedIds: [], - // hasModuleSideEffects: true - }); + expect(external_info.id).to.equal('jquery'); + expect(external_info.isEntry).to.equal(false); + expect(external_info.isExternal).to.equal(true); + expect(external_info.importedIds).to.deep.equal([]); + // expect(external_info.importers).to.deep.equal([path.resolve(process.cwd(), './src/main.js')]); + expect(external_info.dynamicallyImportedIds).to.deep.equal([]); + // expect(external_info.dynamicImporters).to.deep.equal([]); let external_resolve_info = fn('underscore'); - expect(external_resolve_info).to.deep.equal({ - id: 'underscore', - isEntry: false, - isExternal: true, - importedIds: [], - // hasModuleSideEffects: true - }); + expect(external_resolve_info.id).to.equal('underscore'); + expect(external_resolve_info.isEntry).to.equal(false); + expect(external_resolve_info.isExternal).to.equal(true); + expect(external_resolve_info.importedIds).to.deep.equal([]); + // expect(external_resolve_info.importers).to.deep.equal([path.resolve(process.cwd(), './src/main.js')]) + expect(external_resolve_info.dynamicallyImportedIds).to.deep.equal([]); + // expect(external_resolve_info.dynamicImporters).to.deep.equal([]); let dynamic_external_resolve_info = fn('backbone'); - expect(dynamic_external_resolve_info).to.deep.equal({ - id: 'backbone', - isEntry: false, - isExternal: true, - importedIds: [], - // hasModuleSideEffects: true - }); - + expect(dynamic_external_resolve_info.id).to.equal('backbone'); + expect(dynamic_external_resolve_info.isEntry).to.equal(false); + expect(dynamic_external_resolve_info.isExternal).to.equal(true); + expect(dynamic_external_resolve_info.importedIds).to.deep.equal([]); + // expect(dynamic_external_resolve_info.importers).to.deep.equal([]); + expect(dynamic_external_resolve_info.dynamicallyImportedIds).to.deep.equal([]); + // expect(dynamic_external_resolve_info.dynamicImporters).to.deep.equal([path.resolve(process.cwd(), './src/main.js')]); let dynamic_import_info = fn(path.resolve(process.cwd(), './src/rofl.js')); - expect(dynamic_import_info).to.deep.equal({ - id: path.resolve(process.cwd(), './src/rofl.js'), - isEntry: false, - isExternal: false, - importedIds: [], - // hasModuleSideEffects: true - }); + expect(dynamic_import_info.id).to.equal(path.resolve(process.cwd(), './src/rofl.js')); + expect(dynamic_import_info.isEntry).to.equal(false); + expect(dynamic_import_info.isExternal).to.equal(false); + expect(dynamic_import_info.importedIds).to.deep.equal([]); + // expect(dynamic_import_info.importers).to.deep.equal([]); + expect(dynamic_import_info.dynamicallyImportedIds).to.deep.equal([]); + // expect(dynamic_import_info.dynamicImporters).to.deep.equal([path.resolve(process.cwd(), './src/main.js')]); fs.reset(); }); diff --git a/test/cases/api/generate.js b/test/cases/api/generate.js index 58ba2d7..604d5cb 100644 --- a/test/cases/api/generate.js +++ b/test/cases/api/generate.js @@ -390,4 +390,25 @@ describe ('API: generate', () => { fs.reset(); }); + it ('should include deconflicted external dynamic imports in outputted chunkinfos', async () => { + fs.stub('./src/a/dep.js', () => 'export default 123; import("jquery");'); + fs.stub('./src/b/dep.js', () => 'export default 456; import("../c/dep");'); + fs.stub('./src/c/dep.js', () => 'export default 789;'); + fs.stub('./src/main.js', () => 'import("./a/dep"); import("./b/dep"); '); + + let bundle = await nollup({ + input: './src/main.js', + external: ['jquery'] + }); + + let { output } = await bundle.generate({ format: 'esm', chunkFileNames: '[name].js' }); + + expect(output.length).to.equal(4); + expect(output[0].dynamicImports).to.deep.equal(['dep.js', 'dep2.js']); + expect(output[1].dynamicImports).to.deep.equal(['jquery']); + expect(output[2].dynamicImports).to.deep.equal(['dep3.js']); + expect(output[3].dynamicImports).to.deep.equal([]); + fs.reset(); + }); + }); \ No newline at end of file