Skip to content

Commit

Permalink
feat: tree shakable output for module library
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Apr 2, 2024
1 parent 23c3943 commit 416c9b4
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 80 deletions.
28 changes: 17 additions & 11 deletions lib/javascript/JavascriptModulesPlugin.js
Expand Up @@ -834,17 +834,23 @@ class JavascriptModulesPlugin {
const exports = runtimeRequirements.has(RuntimeGlobals.exports);
const webpackExports =
exports && m.exportsArgument === RuntimeGlobals.exports;
let iife = innerStrict
? "it need to be in strict mode."
: inlinedModules.size > 1
? // TODO check globals and top-level declarations of other entries and chunk modules
// to make a better decision
"it need to be isolated against other entry modules."
: chunkModules
? "it need to be isolated against other modules in the chunk."
: exports && !webpackExports
? `it uses a non-standard name for the exports (${m.exportsArgument}).`
: hooks.embedInRuntimeBailout.call(m, renderContext);
const isModuleLibrary =
compilation.compiler.options.output.library &&
compilation.compiler.options.output.library.type === "module";

let iife = isModuleLibrary
? undefined
: innerStrict
? "it need to be in strict mode."
: inlinedModules.size > 1
? // TODO check globals and top-level declarations of other entries and chunk modules
// to make a better decision
"it need to be isolated against other entry modules."
: chunkModules
? "it need to be isolated against other modules in the chunk."
: exports && !webpackExports
? `it uses a non-standard name for the exports (${m.exportsArgument}).`
: hooks.embedInRuntimeBailout.call(m, renderContext);
let footer;
if (iife !== undefined) {
startupSource.add(
Expand Down
88 changes: 70 additions & 18 deletions lib/library/ModuleLibraryPlugin.js
Expand Up @@ -6,8 +6,10 @@
"use strict";

const { ConcatSource } = require("webpack-sources");
const Entrypoint = require("../Entrypoint");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const ConcatenatedModule = require("../optimize/ConcatenatedModule");
const propertyAccess = require("../util/propertyAccess");
const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");

Expand Down Expand Up @@ -37,6 +39,31 @@ const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
* @extends {AbstractLibraryPlugin<ModuleLibraryPluginParsed>}
*/
class ModuleLibraryPlugin extends AbstractLibraryPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
super.apply(compiler);

compiler.hooks.compilation.tap("ModuleLibraryPlugin", compilation => {
const { exportsDefinitions } =
ConcatenatedModule.getCompilationHooks(compilation);
exportsDefinitions.tap("ModuleLibraryPlugin", () => {
if (
compilation.chunkGroups
.filter(chunkGroup => chunkGroup instanceof Entrypoint)
.some(entryPoint => entryPoint.chunks.length > 1)
) {
return;
}

return true;
});
});
}

/**
* @param {ModuleLibraryPluginOptions} options the plugin options
*/
Expand Down Expand Up @@ -76,31 +103,56 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
{ moduleGraph, chunk },
{ options, compilation }
) {
const shouldExportFinalName = module.buildMeta.exportsFinalName;
const result = new ConcatSource(source);
const exportsInfo = moduleGraph.getExportsInfo(module);
const exports = [];
const isAsync = moduleGraph.isAsync(module);
if (isAsync) {
result.add(
`${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
);
}
for (const exportInfo of exportsInfo.orderedExports) {
if (!exportInfo.provided) continue;
const varName = `${RuntimeGlobals.exports}${Template.toIdentifier(
exportInfo.name
)}`;
result.add(
`var ${varName} = ${RuntimeGlobals.exports}${propertyAccess([
/** @type {string} */
(exportInfo.getUsedName(exportInfo.name, chunk.runtime))
])};\n`
);
exports.push(`${varName} as ${exportInfo.name}`);

if (shouldExportFinalName) {
const definitions = module.buildMeta.exportsFinalName;
for (const exportInfo of exportsInfo.orderedExports) {
if (!exportInfo.provided) continue;
const webpackExportsProperty = exportInfo.getUsedName(
exportInfo.name,
chunk.runtime
);
const finalName =
definitions[
/** @type {string} */
(webpackExportsProperty)
];
exports.push(
finalName === exportInfo.name
? finalName
: `${finalName} as ${exportInfo.name}`
);
}
} else {
const isAsync = moduleGraph.isAsync(module);
if (isAsync) {
result.add(
`${RuntimeGlobals.exports} = await ${RuntimeGlobals.exports};\n`
);
}
for (const exportInfo of exportsInfo.orderedExports) {
if (!exportInfo.provided) continue;
const varName = `${RuntimeGlobals.exports}${Template.toIdentifier(
exportInfo.name
)}`;
result.add(
`var ${varName} = ${RuntimeGlobals.exports}${propertyAccess([
/** @type {string} */
(exportInfo.getUsedName(exportInfo.name, chunk.runtime))
])};\n`
);
exports.push(`${varName} as ${exportInfo.name}`);
}
}

if (exports.length > 0) {
result.add(`export { ${exports.join(", ")} };\n`);
}

return result;
}
}
Expand Down
82 changes: 66 additions & 16 deletions lib/optimize/ConcatenatedModule.js
Expand Up @@ -7,6 +7,7 @@

const eslintScope = require("eslint-scope");
const Referencer = require("eslint-scope/lib/referencer");
const { SyncBailHook } = require("tapable");
const {
CachedSource,
ConcatSource,
Expand Down Expand Up @@ -693,11 +694,20 @@ const getPathInAst = (ast, node) => {

const TYPES = new Set(["javascript"]);

/**
* @typedef {Object} ConcatenateModuleHooks
* @property {SyncBailHook<[Record<string, string>]>} exportsDefinitions
*/

/** @type {WeakMap<Compilation, ConcatenateModuleHooks>} */
const compilationHooksMap = new WeakMap();

class ConcatenatedModule extends Module {
/**
* @param {Module} rootModule the root module of the concatenation
* @param {Set<Module>} modules all modules in the concatenation (including the root module)
* @param {RuntimeSpec} runtime the runtime
* @param {Compilation} compilation the compilation
* @param {Object=} associatedObjectForCache object for caching
* @param {string | HashConstructor=} hashFunction hash function to use
* @returns {ConcatenatedModule} the module
Expand All @@ -706,6 +716,7 @@ class ConcatenatedModule extends Module {
rootModule,
modules,
runtime,
compilation,
associatedObjectForCache,
hashFunction = "md4"
) {
Expand All @@ -719,18 +730,35 @@ class ConcatenatedModule extends Module {
identifier,
rootModule,
modules,
runtime
runtime,
compilation
});
}

/**
* @param {Compilation} compilation the compilation
* @returns {ConcatenateModuleHooks} the attached hooks
*/
static getCompilationHooks(compilation) {
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
hooks = {
exportsDefinitions: new SyncBailHook(["definitions"])
};
compilationHooksMap.set(compilation, hooks);
}
return hooks;
}

/**
* @param {Object} options options
* @param {string} options.identifier the identifier of the module
* @param {Module=} options.rootModule the root module of the concatenation
* @param {RuntimeSpec} options.runtime the selected runtime
* @param {Set<Module>=} options.modules all concatenated modules
* @param {Compilation} options.compilation the compilation
*/
constructor({ identifier, rootModule, modules, runtime }) {
constructor({ identifier, rootModule, modules, runtime, compilation }) {
super(JAVASCRIPT_MODULE_TYPE_ESM, null, rootModule && rootModule.layer);

// Info from Factory
Expand All @@ -742,6 +770,8 @@ class ConcatenatedModule extends Module {
this._modules = modules;
this._runtime = runtime;
this.factoryMeta = rootModule && rootModule.factoryMeta;
/** @type {Compilation | undefined} */
this.compilation = compilation;
}

/**
Expand Down Expand Up @@ -1492,6 +1522,8 @@ class ConcatenatedModule extends Module {
/** @type {BuildMeta} */
(rootInfo.module.buildMeta).strictHarmonyModule;
const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
/** @type {Record<string, string>} */
const definitionsForHook = {};
for (const exportInfo of exportsInfo.orderedExports) {
const name = exportInfo.name;
if (exportInfo.provided === false) continue;
Expand All @@ -1516,6 +1548,7 @@ class ConcatenatedModule extends Module {
strictHarmonyModule,
true
);
definitionsForHook[used] = finalName;
return `/* ${
exportInfo.isReexport() ? "reexport" : "binding"
} */ ${finalName}`;
Expand All @@ -1531,21 +1564,20 @@ class ConcatenatedModule extends Module {
const result = new ConcatSource();

// add harmony compatibility flag (must be first because of possible circular dependencies)
let shouldAddHarmonyFlag = false;
if (
moduleGraph.getExportsInfo(this).otherExportsInfo.getUsed(runtime) !==
UsageState.Unused
) {
result.add(`// ESM COMPAT FLAG\n`);
result.add(
runtimeTemplate.defineEsModuleFlagStatement({
exportsArgument: this.exportsArgument,
runtimeRequirements
})
);
shouldAddHarmonyFlag = true;
}

// define exports
if (exportsMap.size > 0) {
const { exportsDefinitions } = ConcatenatedModule.getCompilationHooks(
this.compilation
);

runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
const definitions = [];
Expand All @@ -1556,12 +1588,29 @@ class ConcatenatedModule extends Module {
)}`
);
}
result.add(`\n// EXPORTS\n`);
result.add(
`${RuntimeGlobals.definePropertyGetters}(${
this.exportsArgument
}, {${definitions.join(",")}\n});\n`
);
const shouldSkipRenderDefinitions =
exportsDefinitions.call(definitionsForHook);

if (!shouldSkipRenderDefinitions) {
if (shouldAddHarmonyFlag) {
result.add(`// ESM COMPAT FLAG\n`);
result.add(
runtimeTemplate.defineEsModuleFlagStatement({
exportsArgument: this.exportsArgument,
runtimeRequirements
})
);
}

result.add(`\n// EXPORTS\n`);
result.add(
`${RuntimeGlobals.definePropertyGetters}(${
this.exportsArgument
}, {${definitions.join(",")}\n});\n`
);
} else {
this.buildMeta.exportsFinalName = definitionsForHook;
}
}

// list unused exports
Expand Down Expand Up @@ -1978,7 +2027,8 @@ ${defineGetters}`
identifier: undefined,
rootModule: undefined,
modules: undefined,
runtime: undefined
runtime: undefined,
compilation: undefined
});
obj.deserialize(context);
return obj;
Expand Down
2 changes: 2 additions & 0 deletions lib/optimize/ModuleConcatenationPlugin.js
Expand Up @@ -398,10 +398,12 @@ class ModuleConcatenationPlugin {
}

// Create a new ConcatenatedModule
ConcatenatedModule.getCompilationHooks(compilation);
let newModule = ConcatenatedModule.create(
rootModule,
modules,
concatConfiguration.runtime,
compilation,
compiler.root,
compilation.outputOptions.hashFunction
);
Expand Down

0 comments on commit 416c9b4

Please sign in to comment.