Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(moduletypes): introduce module type constants for plugins #16896

Merged
merged 2 commits into from Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 44 additions & 36 deletions lib/DefinePlugin.js
Expand Up @@ -5,10 +5,16 @@

"use strict";

const {
JAVASCRIPT_MODULE_TYPE_AUTO,
JAVASCRIPT_MODULE_TYPE_ESM,
JAVASCRIPT_MODULE_TYPE_DYNAMIC
} = require("./ModuleTypeConstants");
const RuntimeGlobals = require("./RuntimeGlobals");
const WebpackError = require("./WebpackError");
const ConstDependency = require("./dependencies/ConstDependency");
const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");

const {
evaluateToString,
toConstantDependency
Expand Down Expand Up @@ -249,8 +255,12 @@ const toCacheVersion = code => {
return code + "";
};

const VALUE_DEP_PREFIX = "webpack/DefinePlugin ";
const VALUE_DEP_MAIN = "webpack/DefinePlugin_hash";
const PLUGIN_NAME = "DefinePlugin";
const VALUE_DEP_PREFIX = `webpack/${PLUGIN_NAME} `;
const VALUE_DEP_MAIN = `webpack/${PLUGIN_NAME}_hash`;
const TYPEOF_OPERATOR_REGEXP = /^typeof\s+/;
const WEBPACK_REQUIRE_FUNCTION_REGEXP = /__webpack_require__\s*(!?\.)/;
const WEBPACK_REQUIRE_IDENTIFIER_REGEXP = /__webpack_require__/;
TheLarkInn marked this conversation as resolved.
Show resolved Hide resolved

class DefinePlugin {
/**
Expand Down Expand Up @@ -278,7 +288,7 @@ class DefinePlugin {
apply(compiler) {
const definitions = this.definitions;
compiler.hooks.compilation.tap(
"DefinePlugin",
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
compilation.dependencyTemplates.set(
ConstDependency,
Expand All @@ -300,7 +310,7 @@ class DefinePlugin {
*/
const handler = parser => {
const mainValue = compilation.valueCacheVersions.get(VALUE_DEP_MAIN);
parser.hooks.program.tap("DefinePlugin", () => {
parser.hooks.program.tap(PLUGIN_NAME, () => {
const { buildInfo } = parser.state.module;
if (!buildInfo.valueDependencies)
buildInfo.valueDependencies = new Map();
Expand Down Expand Up @@ -356,7 +366,7 @@ class DefinePlugin {
const splittedKey = key.split(".");
splittedKey.slice(1).forEach((_, i) => {
const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
parser.hooks.canRename.for(fullKey).tap("DefinePlugin", () => {
parser.hooks.canRename.for(fullKey).tap(PLUGIN_NAME, () => {
addValueDependency(key);
return true;
});
Expand All @@ -371,18 +381,18 @@ class DefinePlugin {
*/
const applyDefine = (key, code) => {
const originalKey = key;
const isTypeof = /^typeof\s+/.test(key);
if (isTypeof) key = key.replace(/^typeof\s+/, "");
const isTypeof = TYPEOF_OPERATOR_REGEXP.test(key);
if (isTypeof) key = key.replace(TYPEOF_OPERATOR_REGEXP, "");
let recurse = false;
let recurseTypeof = false;
if (!isTypeof) {
parser.hooks.canRename.for(key).tap("DefinePlugin", () => {
parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
addValueDependency(originalKey);
return true;
});
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", expr => {
.tap(PLUGIN_NAME, expr => {
/**
* this is needed in case there is a recursion in the DefinePlugin
* to prevent an endless recursion
Expand All @@ -408,7 +418,7 @@ class DefinePlugin {
res.setRange(expr.range);
return res;
});
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
addValueDependency(originalKey);
const strCode = toCode(
code,
Expand All @@ -418,11 +428,11 @@ class DefinePlugin {
runtimeTemplate,
!parser.isAsiPosition(expr.range[0])
);
if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.require
])(expr);
} else if (/__webpack_require__/.test(strCode)) {
} else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.requireScope
])(expr);
Expand All @@ -431,7 +441,7 @@ class DefinePlugin {
}
});
}
parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
parser.hooks.evaluateTypeof.for(key).tap(PLUGIN_NAME, expr => {
/**
* this is needed in case there is a recursion in the DefinePlugin
* to prevent an endless recursion
Expand Down Expand Up @@ -459,7 +469,7 @@ class DefinePlugin {
res.setRange(expr.range);
return res;
});
parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
parser.hooks.typeof.for(key).tap(PLUGIN_NAME, expr => {
addValueDependency(originalKey);
const codeCode = toCode(
code,
Expand Down Expand Up @@ -488,26 +498,24 @@ class DefinePlugin {
* @returns {void}
*/
const applyObjectDefine = (key, obj) => {
parser.hooks.canRename.for(key).tap("DefinePlugin", () => {
parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
addValueDependency(key);
return true;
});
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", expr => {
addValueDependency(key);
return new BasicEvaluatedExpression()
.setTruthy()
.setSideEffects(false)
.setRange(expr.range);
});
parser.hooks.evaluateIdentifier.for(key).tap(PLUGIN_NAME, expr => {
addValueDependency(key);
return new BasicEvaluatedExpression()
.setTruthy()
.setSideEffects(false)
.setRange(expr.range);
});
parser.hooks.evaluateTypeof
.for(key)
.tap(
"DefinePlugin",
PLUGIN_NAME,
withValueDependency(key, evaluateToString("object"))
);
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
addValueDependency(key);
const strCode = stringifyObj(
obj,
Expand All @@ -518,11 +526,11 @@ class DefinePlugin {
!parser.isAsiPosition(expr.range[0])
);

if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.require
])(expr);
} else if (/__webpack_require__/.test(strCode)) {
} else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
return toConstantDependency(parser, strCode, [
RuntimeGlobals.requireScope
])(expr);
Expand All @@ -533,7 +541,7 @@ class DefinePlugin {
parser.hooks.typeof
.for(key)
.tap(
"DefinePlugin",
PLUGIN_NAME,
withValueDependency(
key,
toConstantDependency(parser, JSON.stringify("object"))
Expand All @@ -545,14 +553,14 @@ class DefinePlugin {
};

normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("DefinePlugin", handler);
.for(JAVASCRIPT_MODULE_TYPE_AUTO)
.tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("DefinePlugin", handler);
.for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
.tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("DefinePlugin", handler);
.for(JAVASCRIPT_MODULE_TYPE_ESM)
.tap(PLUGIN_NAME, handler);

/**
* Walk definitions
Expand All @@ -571,7 +579,7 @@ class DefinePlugin {
compilation.valueCacheVersions.set(name, version);
} else if (oldVersion !== version) {
const warning = new WebpackError(
`DefinePlugin\nConflicting values for '${prefix + key}'`
`${PLUGIN_NAME}\nConflicting values for '${prefix + key}'`
);
warning.details = `'${oldVersion}' !== '${version}'`;
warning.hideStack = true;
Expand Down
32 changes: 32 additions & 0 deletions lib/ModuleTypeConstants.js
@@ -0,0 +1,32 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sean Larkin @TheLarkInn
*/

"use strict";

/**
* @type {Readonly<"javascript/auto">}
*/
const JAVASCRIPT_MODULE_TYPE_AUTO = "javascript/auto";
/**
* @type {Readonly<"javascript/dynamic">}
*/
const JAVASCRIPT_MODULE_TYPE_DYNAMIC = "javascript/dynamic";
/**
* @type {Readonly<"javascript/esm">}
* This is the module type used for _strict_ ES Module syntax. This means that all legacy formats
* that webpack supports (CommonJS, AMD, SystemJS) are not supported.
*/
const JAVASCRIPT_MODULE_TYPE_ESM = "javascript/esm";

/**
* @type {Readonly<"json">}
* This is the module type used for JSON files. JSON files are always parsed as ES Module.
*/
const JSON_MODULE_TYPE = "json";

exports.JAVASCRIPT_MODULE_TYPE_AUTO = JAVASCRIPT_MODULE_TYPE_AUTO;
exports.JAVASCRIPT_MODULE_TYPE_DYNAMIC = JAVASCRIPT_MODULE_TYPE_DYNAMIC;
exports.JAVASCRIPT_MODULE_TYPE_ESM = JAVASCRIPT_MODULE_TYPE_ESM;
exports.JSON_MODULE_TYPE = JSON_MODULE_TYPE;
18 changes: 13 additions & 5 deletions lib/json/JsonModulesPlugin.js
Expand Up @@ -5,6 +5,7 @@

"use strict";

const { JSON_MODULE_TYPE } = require("../ModuleTypeConstants");
const createSchemaValidation = require("../util/create-schema-validation");
const JsonGenerator = require("./JsonGenerator");
const JsonParser = require("./JsonParser");
Expand All @@ -20,26 +21,33 @@ const validate = createSchemaValidation(
}
);

const PLUGIN_NAME = "JsonModulesPlugin";

/**
* The JsonModulesPlugin is the entrypoint plugin for the json modules feature.
* It adds the json module type to the compiler and registers the json parser and generator.
*/
class JsonModulesPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"JsonModulesPlugin",
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.createParser
.for("json")
.tap("JsonModulesPlugin", parserOptions => {
.for(JSON_MODULE_TYPE)
.tap(PLUGIN_NAME, parserOptions => {
validate(parserOptions);

return new JsonParser(parserOptions);
});
normalModuleFactory.hooks.createGenerator
.for("json")
.tap("JsonModulesPlugin", () => {
.for(JSON_MODULE_TYPE)
.tap(PLUGIN_NAME, () => {
return new JsonGenerator();
});
}
Expand Down