diff --git a/examples/module/README.md b/examples/module/README.md
index f277a5f4be6..1070ace1342 100644
--- a/examples/module/README.md
+++ b/examples/module/README.md
@@ -10,6 +10,8 @@ inc();
print(value);
resetCounter();
print(value);
+
+export { inc, print };
```
# methods.js
@@ -39,11 +41,52 @@ export function reset() {
```javascript
/******/ "use strict";
+/******/ // The require scope
+/******/ var __webpack_require__ = {};
+/******/
+```
+
+/* webpack runtime code */
+
+``` js
+/************************************************************************/
+/******/ /* webpack/runtime/define property getters */
+/******/ (() => {
+/******/ // define getter functions for harmony exports
+/******/ __webpack_require__.d = (exports, definition) => {
+/******/ for(var key in definition) {
+/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
+/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
+/******/ }
+/******/ }
+/******/ };
+/******/ })();
+/******/
+/******/ /* webpack/runtime/hasOwnProperty shorthand */
+/******/ (() => {
+/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
+/******/ })();
+/******/
+/************************************************************************/
+```
+
+
+
+``` js
+var __webpack_exports__ = {};
/*!********************************!*\
!*** ./example.js + 2 modules ***!
\********************************/
/*! namespace exports */
-/*! runtime requirements: */
+/*! export inc [provided] [used in main] [could be renamed] -> ./counter.js .increment */
+/*! export print [provided] [used in main] [could be renamed] -> ./methods.js .print */
+/*! runtime requirements: __webpack_exports__, __webpack_require__.d, __webpack_require__.* */
+
+// EXPORTS
+__webpack_require__.d(__webpack_exports__, {
+ "inc": () => (/* reexport */ increment),
+ "print": () => (/* reexport */ print)
+});
;// CONCATENATED MODULE: ./counter.js
let value = 0;
@@ -72,12 +115,18 @@ increment();
print(value);
counter_reset();
print(value);
+
+
+
+var __webpack_exports__inc = __webpack_exports__.inc;
+var __webpack_exports__print = __webpack_exports__.print;
+export { __webpack_exports__inc as inc, __webpack_exports__print as print };
```
# dist/output.js (production)
```javascript
-let o=0;function n(){o++}const c=o=>console.log(o);c(o),n(),n(),n(),c(o),o=0,c(o);
+var e={d:(o,r)=>{for(var t in r)e.o(r,t)&&!e.o(o,t)&&Object.defineProperty(o,t,{enumerable:!0,get:r[t]})},o:(e,o)=>Object.prototype.hasOwnProperty.call(e,o)},o={};e.d(o,{a:()=>t,S:()=>a});let r=0;function t(){r++}const a=e=>console.log(e);a(r),t(),t(),t(),a(r),r=0,a(r);var n=o.a,c=o.S;export{n as inc,c as print};
```
# Info
@@ -85,25 +134,29 @@ let o=0;function n(){o++}const c=o=>console.log(o);c(o),n(),n(),n(),c(o),o=0,c(o
## Unoptimized
```
-asset output.js 580 bytes [emitted] [javascript module] (name: main)
-chunk (runtime: main) output.js (main) 429 bytes [entry] [rendered]
+asset output.js 2.05 KiB [emitted] [javascript module] (name: main)
+chunk (runtime: main) output.js (main) 453 bytes (javascript) 396 bytes (runtime) [entry] [rendered]
> ./example.js main
- ./example.js + 2 modules 429 bytes [built] [code generated]
- [no exports]
- [no exports used]
+ runtime modules 396 bytes 2 modules
+ ./example.js + 2 modules 453 bytes [built] [code generated]
+ [exports: inc, print]
+ [all exports used]
entry ./example.js main
-webpack 5.11.1 compiled successfully
+ used as library export
+webpack 5.20.2 compiled successfully
```
## Production mode
```
-asset output.js 82 bytes [emitted] [javascript module] [minimized] (name: main)
-chunk (runtime: main) output.js (main) 429 bytes [entry] [rendered]
+asset output.js 314 bytes [emitted] [javascript module] [minimized] (name: main)
+chunk (runtime: main) output.js (main) 453 bytes (javascript) 396 bytes (runtime) [entry] [rendered]
> ./example.js main
- ./example.js + 2 modules 429 bytes [built] [code generated]
- [no exports]
- [no exports used]
+ runtime modules 396 bytes 2 modules
+ ./example.js + 2 modules 453 bytes [built] [code generated]
+ [exports: inc, print]
+ [all exports used]
entry ./example.js main
-webpack 5.11.1 compiled successfully
+ used as library export
+webpack 5.20.2 compiled successfully
```
diff --git a/examples/module/example.js b/examples/module/example.js
index ec782d96733..29e215a8009 100644
--- a/examples/module/example.js
+++ b/examples/module/example.js
@@ -7,3 +7,5 @@ inc();
print(value);
resetCounter();
print(value);
+
+export { inc, print };
diff --git a/examples/module/webpack.config.js b/examples/module/webpack.config.js
index f354df368ff..0f10a6d3d39 100644
--- a/examples/module/webpack.config.js
+++ b/examples/module/webpack.config.js
@@ -1,6 +1,9 @@
module.exports = {
output: {
- module: true
+ module: true,
+ library: {
+ type: "module"
+ }
},
optimization: {
usedExports: true,
diff --git a/lib/ExportsInfo.js b/lib/ExportsInfo.js
index 7d8b1cdf644..6b85eb5ec4e 100644
--- a/lib/ExportsInfo.js
+++ b/lib/ExportsInfo.js
@@ -395,6 +395,7 @@ class ExportsInfo {
setAllKnownExportsUsed(runtime) {
let changed = false;
for (const exportInfo of this._exports.values()) {
+ if (!exportInfo.provided) continue;
if (exportInfo.setUsed(UsageState.Used, runtime)) {
changed = true;
}
diff --git a/lib/library/AbstractLibraryPlugin.js b/lib/library/AbstractLibraryPlugin.js
index f1a7a694821..c126eb9609b 100644
--- a/lib/library/AbstractLibraryPlugin.js
+++ b/lib/library/AbstractLibraryPlugin.js
@@ -17,6 +17,7 @@ const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin")
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
+/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
/** @typedef {import("../util/Hash")} Hash */
const COMMON_LIBRARY_NAME_MESSAGE =
@@ -52,28 +53,36 @@ class AbstractLibraryPlugin {
apply(compiler) {
const { _pluginName } = this;
compiler.hooks.thisCompilation.tap(_pluginName, compilation => {
- compilation.hooks.finishModules.tap(_pluginName, () => {
- for (const [
- name,
- {
- dependencies: deps,
- options: { library }
- }
- ] of compilation.entries) {
- const options = this._parseOptionsCached(
- library !== undefined ? library : compilation.outputOptions.library
- );
- if (options !== false) {
- const dep = deps[deps.length - 1];
- if (dep) {
- const module = compilation.moduleGraph.getModule(dep);
- if (module) {
- this.finishEntryModule(module, name, { options, compilation });
+ compilation.hooks.finishModules.tap(
+ { name: _pluginName, stage: 10 },
+ () => {
+ for (const [
+ name,
+ {
+ dependencies: deps,
+ options: { library }
+ }
+ ] of compilation.entries) {
+ const options = this._parseOptionsCached(
+ library !== undefined
+ ? library
+ : compilation.outputOptions.library
+ );
+ if (options !== false) {
+ const dep = deps[deps.length - 1];
+ if (dep) {
+ const module = compilation.moduleGraph.getModule(dep);
+ if (module) {
+ this.finishEntryModule(module, name, {
+ options,
+ compilation
+ });
+ }
}
}
}
}
- });
+ );
const getOptionsForChunk = chunk => {
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0)
@@ -85,23 +94,47 @@ class AbstractLibraryPlugin {
);
};
- compilation.hooks.additionalChunkRuntimeRequirements.tap(
- _pluginName,
- (chunk, set) => {
- const options = getOptionsForChunk(chunk);
- if (options !== false) {
- this.runtimeRequirements(chunk, set, { options, compilation });
+ if (
+ this.render !== AbstractLibraryPlugin.prototype.render ||
+ this.runtimeRequirements !==
+ AbstractLibraryPlugin.prototype.runtimeRequirements
+ ) {
+ compilation.hooks.additionalChunkRuntimeRequirements.tap(
+ _pluginName,
+ (chunk, set) => {
+ const options = getOptionsForChunk(chunk);
+ if (options !== false) {
+ this.runtimeRequirements(chunk, set, { options, compilation });
+ }
}
- }
- );
+ );
+ }
const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
- hooks.render.tap(_pluginName, (source, renderContext) => {
- const options = getOptionsForChunk(renderContext.chunk);
- if (options === false) return source;
- return this.render(source, renderContext, { options, compilation });
- });
+ if (this.render !== AbstractLibraryPlugin.prototype.render) {
+ hooks.render.tap(_pluginName, (source, renderContext) => {
+ const options = getOptionsForChunk(renderContext.chunk);
+ if (options === false) return source;
+ return this.render(source, renderContext, { options, compilation });
+ });
+ }
+
+ if (
+ this.renderStartup !== AbstractLibraryPlugin.prototype.renderStartup
+ ) {
+ hooks.renderStartup.tap(
+ _pluginName,
+ (source, module, renderContext) => {
+ const options = getOptionsForChunk(renderContext.chunk);
+ if (options === false) return source;
+ return this.renderStartup(source, module, renderContext, {
+ options,
+ compilation
+ });
+ }
+ );
+ }
hooks.chunkHash.tap(_pluginName, (chunk, hash, context) => {
const options = getOptionsForChunk(chunk);
@@ -151,7 +184,8 @@ class AbstractLibraryPlugin {
* @returns {void}
*/
runtimeRequirements(chunk, set, libraryContext) {
- set.add(RuntimeGlobals.returnExportsFromRuntime);
+ if (this.render !== AbstractLibraryPlugin.prototype.render)
+ set.add(RuntimeGlobals.returnExportsFromRuntime);
}
/**
@@ -164,6 +198,17 @@ class AbstractLibraryPlugin {
return source;
}
+ /**
+ * @param {Source} source source
+ * @param {Module} module module
+ * @param {StartupRenderContext} renderContext render context
+ * @param {LibraryContext} libraryContext context
+ * @returns {Source} source with library export
+ */
+ renderStartup(source, module, renderContext, libraryContext) {
+ return source;
+ }
+
/**
* @param {Chunk} chunk the chunk
* @param {Hash} hash hash
diff --git a/lib/library/EnableLibraryPlugin.js b/lib/library/EnableLibraryPlugin.js
index 5fed0806db6..0e00750d09b 100644
--- a/lib/library/EnableLibraryPlugin.js
+++ b/lib/library/EnableLibraryPlugin.js
@@ -209,9 +209,13 @@ class EnableLibraryPlugin {
}).apply(compiler);
break;
}
- case "module":
- // TODO implement module library
+ case "module": {
+ const ModuleLibraryPlugin = require("./ModuleLibraryPlugin");
+ new ModuleLibraryPlugin({
+ type
+ }).apply(compiler);
break;
+ }
default:
throw new Error(`Unsupported library type ${type}.
Plugins which provide custom library types must call EnableLibraryPlugin.setEnabled(compiler, type) to disable this error.`);
diff --git a/lib/library/ExportPropertyLibraryPlugin.js b/lib/library/ExportPropertyLibraryPlugin.js
index 46112ee7d12..3e7b61cfc63 100644
--- a/lib/library/ExportPropertyLibraryPlugin.js
+++ b/lib/library/ExportPropertyLibraryPlugin.js
@@ -14,6 +14,7 @@ const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
+/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
@@ -84,6 +85,15 @@ class ExportPropertyLibraryPlugin extends AbstractLibraryPlugin {
}
moduleGraph.addExtraReason(module, "used as library export");
}
+
+ /**
+ * @param {Chunk} chunk the chunk
+ * @param {Set} set runtime requirements
+ * @param {LibraryContext} libraryContext context
+ * @returns {void}
+ */
+ runtimeRequirements(chunk, set, libraryContext) {}
+
/**
* @param {Source} source source
* @param {RenderContext} renderContext render context
diff --git a/lib/library/ModuleLibraryPlugin.js b/lib/library/ModuleLibraryPlugin.js
new file mode 100644
index 00000000000..2690d9c7aca
--- /dev/null
+++ b/lib/library/ModuleLibraryPlugin.js
@@ -0,0 +1,100 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+
+"use strict";
+
+const { ConcatSource } = require("webpack-sources");
+const Template = require("../Template");
+const propertyAccess = require("../util/propertyAccess");
+const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
+
+/** @typedef {import("webpack-sources").Source} Source */
+/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
+/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
+/** @typedef {import("../Chunk")} Chunk */
+/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
+/** @typedef {import("../Compiler")} Compiler */
+/** @typedef {import("../Module")} Module */
+/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
+/** @typedef {import("../util/Hash")} Hash */
+/** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext} LibraryContext */
+
+/**
+ * @typedef {Object} ModuleLibraryPluginOptions
+ * @property {LibraryType} type
+ */
+
+/**
+ * @typedef {Object} ModuleLibraryPluginParsed
+ * @property {string} name
+ */
+
+/**
+ * @typedef {ModuleLibraryPluginParsed} T
+ * @extends {AbstractLibraryPlugin}
+ */
+class ModuleLibraryPlugin extends AbstractLibraryPlugin {
+ /**
+ * @param {ModuleLibraryPluginOptions} options the plugin options
+ */
+ constructor(options) {
+ super({
+ pluginName: "ModuleLibraryPlugin",
+ type: options.type
+ });
+ }
+
+ /**
+ * @param {LibraryOptions} library normalized library option
+ * @returns {T | false} preprocess as needed by overriding
+ */
+ parseOptions(library) {
+ const { name } = library;
+ if (name) {
+ throw new Error(
+ `Library name must unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}`
+ );
+ }
+ return {
+ name: /** @type {string} */ (name)
+ };
+ }
+
+ /**
+ * @param {Source} source source
+ * @param {Module} module module
+ * @param {StartupRenderContext} renderContext render context
+ * @param {LibraryContext} libraryContext context
+ * @returns {Source} source with library export
+ */
+ renderStartup(
+ source,
+ module,
+ { moduleGraph, chunk },
+ { options, compilation }
+ ) {
+ const result = new ConcatSource(source);
+ const exportsInfo = moduleGraph.getExportsInfo(module);
+ const exports = [];
+ for (const exportInfo of exportsInfo.orderedExports) {
+ if (!exportInfo.provided) continue;
+ const varName = `__webpack_exports__${Template.toIdentifier(
+ exportInfo.name
+ )}`;
+ result.add(
+ `var ${varName} = __webpack_exports__${propertyAccess([
+ 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;
+ }
+}
+
+module.exports = ModuleLibraryPlugin;
diff --git a/types.d.ts b/types.d.ts
index 8eee76af46a..7dae801accd 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -128,6 +128,12 @@ declare class AbstractLibraryPlugin {
renderContext: RenderContextObject,
libraryContext: LibraryContext
): Source;
+ renderStartup(
+ source: Source,
+ module: Module,
+ renderContext: StartupRenderContext,
+ libraryContext: LibraryContext
+ ): Source;
chunkHash(
chunk: Chunk,
hash: Hash,