Skip to content
10 changes: 8 additions & 2 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1636,11 +1636,17 @@ export interface NodeOptions {
/**
* Include a polyfill for the '__dirname' variable.
*/
__dirname?: false | true | "warn-mock" | "mock" | "eval-only";
__dirname?: false | true | "warn-mock" | "mock" | "node-module" | "eval-only";
/**
* Include a polyfill for the '__filename' variable.
*/
__filename?: false | true | "warn-mock" | "mock" | "eval-only";
__filename?:
| false
| true
| "warn-mock"
| "mock"
| "node-module"
| "eval-only";
/**
* Include a polyfill for the 'global' variable.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/DependencyTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* @property {InitFragment<GenerateContext>[]} initFragments mutable array of init fragments for the current module
* @property {ConcatenationScope=} concatenationScope when in a concatenated module, information about other concatenated modules
* @property {CodeGenerationResults} codeGenerationResults the code generation results
* @property {InitFragment<GenerateContext>[]} chunkInitFragments chunkInitFragments
*/

/**
Expand Down
48 changes: 48 additions & 0 deletions lib/NodeStuffPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const NodeStuffInWebError = require("./NodeStuffInWebError");
const RuntimeGlobals = require("./RuntimeGlobals");
const CachedConstDependency = require("./dependencies/CachedConstDependency");
const ConstDependency = require("./dependencies/ConstDependency");
const ExternalModuleDependency = require("./dependencies/ExternalModuleDependency");
const {
evaluateToString,
expressionIsUnsupported
Expand Down Expand Up @@ -52,6 +53,11 @@ class NodeStuffPlugin {
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
compilation.dependencyTemplates.set(
ExternalModuleDependency,
new ExternalModuleDependency.Template()
);

/**
* @param {JavascriptParser} parser the parser
* @param {JavascriptParserOptions} parserOptions options
Expand Down Expand Up @@ -128,6 +134,35 @@ class NodeStuffPlugin {
});
};

/**
* @param {string} expressionName expression name
* @param {(value: string) => void} fn function
* @returns {void}
*/
const setUrlModuleConstant = (expressionName, fn) => {
parser.hooks.expression
.for(expressionName)
.tap(PLUGIN_NAME, expr => {
const dep = new ExternalModuleDependency(
"url",
[
{
name: "fileURLToPath",
value: "__webpack_fileURLToPath__"
}
],
undefined,
fn("__webpack_fileURLToPath__"),
expr.range,
expressionName
);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);

return true;
});
};

/**
* @param {string} expressionName expression name
* @param {string} value value
Expand All @@ -150,6 +185,12 @@ class NodeStuffPlugin {
"__filename is a Node.js feature and isn't available in browsers."
);
break;
case "node-module":
setUrlModuleConstant(
"__filename",
functionName => `${functionName}(import.meta.url)`
);
break;
case true:
setModuleConstant("__filename", module =>
relative(compiler.inputFileSystem, context, module.resource)
Expand Down Expand Up @@ -177,6 +218,13 @@ class NodeStuffPlugin {
"__dirname is a Node.js feature and isn't available in browsers."
);
break;
case "node-module":
setUrlModuleConstant(
"__dirname",
functionName =>
`${functionName}(import.meta.url + "/..").slice(0, -1)`
);
break;
case true:
setModuleConstant("__dirname", module =>
relative(compiler.inputFileSystem, context, module.context)
Expand Down
23 changes: 14 additions & 9 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ const applyWebpackOptionsDefaults = options => {
futureDefaults:
/** @type {NonNullable<WebpackOptions["experiments"]["futureDefaults"]>} */
(options.experiments.futureDefaults),
outputModule: options.output.module,
targetProperties
});

Expand Down Expand Up @@ -1246,26 +1247,30 @@ const applyLoaderDefaults = (loader, { targetProperties, environment }) => {
* @param {Object} options options
* @param {TargetProperties | false} options.targetProperties target properties
* @param {boolean} options.futureDefaults is future defaults enabled
* @param {boolean} options.outputModule is output type is module
* @returns {void}
*/
const applyNodeDefaults = (node, { futureDefaults, targetProperties }) => {
const applyNodeDefaults = (
node,
{ futureDefaults, outputModule, targetProperties }
) => {
if (node === false) return;

F(node, "global", () => {
if (targetProperties && targetProperties.global) return false;
// TODO webpack 6 should always default to false
return futureDefaults ? "warn" : true;
});
F(node, "__filename", () => {
if (targetProperties && targetProperties.node) return "eval-only";
// TODO webpack 6 should always default to false
return futureDefaults ? "warn-mock" : "mock";
});
F(node, "__dirname", () => {
if (targetProperties && targetProperties.node) return "eval-only";

const handlerForNames = () => {
if (targetProperties && targetProperties.node)
return outputModule ? "node-module" : "eval-only";
// TODO webpack 6 should always default to false
return futureDefaults ? "warn-mock" : "mock";
});
};

F(node, "__filename", handlerForNames);
F(node, "__dirname", handlerForNames);
};

/**
Expand Down
15 changes: 14 additions & 1 deletion lib/css/CssExportsGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CssExportsGenerator extends Generator {

generateContext.runtimeRequirements.add(RuntimeGlobals.module);

let chunkInitFragments;
const runtimeRequirements = new Set();

const templateContext = {
Expand All @@ -60,7 +61,19 @@ class CssExportsGenerator extends Generator {
concatenationScope: generateContext.concatenationScope,
codeGenerationResults: generateContext.codeGenerationResults,
initFragments,
cssExports
cssExports,
get chunkInitFragments() {
if (!chunkInitFragments) {
const data = generateContext.getData();
chunkInitFragments = data.get("chunkInitFragments");
if (!chunkInitFragments) {
chunkInitFragments = [];
data.set("chunkInitFragments", chunkInitFragments);
}
}

return chunkInitFragments;
}
};

/**
Expand Down
15 changes: 14 additions & 1 deletion lib/css/CssGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class CssGenerator extends Generator {

generateContext.runtimeRequirements.add(RuntimeGlobals.hasCssModules);

let chunkInitFragments;
const templateContext = {
runtimeTemplate: generateContext.runtimeTemplate,
dependencyTemplates: generateContext.dependencyTemplates,
Expand All @@ -49,7 +50,19 @@ class CssGenerator extends Generator {
concatenationScope: generateContext.concatenationScope,
codeGenerationResults: generateContext.codeGenerationResults,
initFragments,
cssExports
cssExports,
get chunkInitFragments() {
if (!chunkInitFragments) {
const data = generateContext.getData();
chunkInitFragments = data.get("chunkInitFragments");
if (!chunkInitFragments) {
chunkInitFragments = [];
data.set("chunkInitFragments", chunkInitFragments);
}
}

return chunkInitFragments;
}
};

/**
Expand Down
9 changes: 8 additions & 1 deletion lib/dependencies/CachedConstDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class CachedConstDependency extends NullDependency {
this._hashUpdate = undefined;
}

/**
* @returns {string} hash update
*/
_createHashUpdate() {
return `${this.identifier}${this.range}${this.expression}`;
}

/**
* Update the hash
* @param {Hash} hash hash to be updated
Expand All @@ -46,7 +53,7 @@ class CachedConstDependency extends NullDependency {
*/
updateHash(hash, context) {
if (this._hashUpdate === undefined)
this._hashUpdate = "" + this.identifier + this.range + this.expression;
this._hashUpdate = this._createHashUpdate();
hash.update(this._hashUpdate);
}

Expand Down
98 changes: 98 additions & 0 deletions lib/dependencies/ExternalModuleDependency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/

"use strict";

const makeSerializable = require("../util/makeSerializable");
const CachedConstDependency = require("./CachedConstDependency");
const ExternalModuleInitFragment = require("./ExternalModuleInitFragment");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {import("../util/Hash")} Hash */

class ExternalModuleDependency extends CachedConstDependency {
constructor(
module,
importSpecifiers,
defaultImport,
expression,
range,
identifier
) {
super(expression, range, identifier);

this.importedModule = module;
this.specifiers = importSpecifiers;
this.default = defaultImport;
}

/**
* @returns {string} hash update
*/
_createHashUpdate() {
return `${this.importedModule}${JSON.stringify(this.specifiers)}${
this.default || "null"
}${super._createHashUpdate()}`;
}

/**
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
super.serialize(context);
const { write } = context;
write(this.importedModule);
write(this.specifiers);
write(this.default);
}

/**
* @param {ObjectDeserializerContext} context context
*/
deserialize(context) {
super.deserialize(context);
const { read } = context;
this.importedModule = read();
this.specifiers = read();
this.default = read();
}
}

makeSerializable(
ExternalModuleDependency,
"webpack/lib/dependencies/ExternalModuleDependency"
);

ExternalModuleDependency.Template = class ExternalModuleDependencyTemplate extends (
CachedConstDependency.Template
) {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, templateContext) {
super.apply(dependency, source, templateContext);
const dep = /** @type {ExternalModuleDependency} */ (dependency);
const { chunkInitFragments } = templateContext;

chunkInitFragments.push(
new ExternalModuleInitFragment(
dep.importedModule,
dep.specifiers,
dep.default
)
);
}
};

module.exports = ExternalModuleDependency;
Loading