From 844fc55b36d90c054ad90e9c95ced9d33e4a1342 Mon Sep 17 00:00:00 2001 From: Sean Larkin Date: Fri, 31 Mar 2023 09:26:38 -0700 Subject: [PATCH 1/2] refactor(moduletypes): introduce module type constants, reduce memory footprint define/json plugins --- lib/DefinePlugin.js | 80 +++++++++++++++++++---------------- lib/ModuleTypeConstants.js | 25 +++++++++++ lib/json/JsonModulesPlugin.js | 18 +++++--- 3 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 lib/ModuleTypeConstants.js diff --git a/lib/DefinePlugin.js b/lib/DefinePlugin.js index b9e728082e4..9fba0f06e5f 100644 --- a/lib/DefinePlugin.js +++ b/lib/DefinePlugin.js @@ -9,6 +9,12 @@ const RuntimeGlobals = require("./RuntimeGlobals"); const WebpackError = require("./WebpackError"); const ConstDependency = require("./dependencies/ConstDependency"); const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression"); +const { + JAVASCRIPT_MODULE_TYPE_AUTO, + JAVASCRIPT_MODULE_TYPE_ESM, + JAVASCRIPT_MODULE_TYPE_DYNAMIC +} = require("./ModuleTypeConstants"); + const { evaluateToString, toConstantDependency @@ -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__/; class DefinePlugin { /** @@ -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, @@ -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(); @@ -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; }); @@ -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 @@ -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, @@ -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); @@ -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 @@ -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, @@ -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, @@ -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); @@ -533,7 +541,7 @@ class DefinePlugin { parser.hooks.typeof .for(key) .tap( - "DefinePlugin", + PLUGIN_NAME, withValueDependency( key, toConstantDependency(parser, JSON.stringify("object")) @@ -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 @@ -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; diff --git a/lib/ModuleTypeConstants.js b/lib/ModuleTypeConstants.js new file mode 100644 index 00000000000..30940d8c4db --- /dev/null +++ b/lib/ModuleTypeConstants.js @@ -0,0 +1,25 @@ +/** + * @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; diff --git a/lib/json/JsonModulesPlugin.js b/lib/json/JsonModulesPlugin.js index 3743eec8d61..f745c0be082 100644 --- a/lib/json/JsonModulesPlugin.js +++ b/lib/json/JsonModulesPlugin.js @@ -8,6 +8,7 @@ const createSchemaValidation = require("../util/create-schema-validation"); const JsonGenerator = require("./JsonGenerator"); const JsonParser = require("./JsonParser"); +const { JSON_MODULE_TYPE } = require("../ModuleTypeConstants"); /** @typedef {import("../Compiler")} Compiler */ @@ -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(); }); } From 4bcc0f0ed56a7a97cfd0a188d8ba182ff0ef7e0e Mon Sep 17 00:00:00 2001 From: Sean Larkin Date: Fri, 31 Mar 2023 09:45:36 -0700 Subject: [PATCH 2/2] yarn lint fix --- lib/DefinePlugin.js | 8 ++++---- lib/ModuleTypeConstants.js | 7 +++++++ lib/json/JsonModulesPlugin.js | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/DefinePlugin.js b/lib/DefinePlugin.js index 9fba0f06e5f..c7fb1d85c47 100644 --- a/lib/DefinePlugin.js +++ b/lib/DefinePlugin.js @@ -5,15 +5,15 @@ "use strict"; -const RuntimeGlobals = require("./RuntimeGlobals"); -const WebpackError = require("./WebpackError"); -const ConstDependency = require("./dependencies/ConstDependency"); -const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression"); 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, diff --git a/lib/ModuleTypeConstants.js b/lib/ModuleTypeConstants.js index 30940d8c4db..9c77c9c0b48 100644 --- a/lib/ModuleTypeConstants.js +++ b/lib/ModuleTypeConstants.js @@ -1,3 +1,10 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Sean Larkin @TheLarkInn +*/ + +"use strict"; + /** * @type {Readonly<"javascript/auto">} */ diff --git a/lib/json/JsonModulesPlugin.js b/lib/json/JsonModulesPlugin.js index f745c0be082..fc82bb540cf 100644 --- a/lib/json/JsonModulesPlugin.js +++ b/lib/json/JsonModulesPlugin.js @@ -5,10 +5,10 @@ "use strict"; +const { JSON_MODULE_TYPE } = require("../ModuleTypeConstants"); const createSchemaValidation = require("../util/create-schema-validation"); const JsonGenerator = require("./JsonGenerator"); const JsonParser = require("./JsonParser"); -const { JSON_MODULE_TYPE } = require("../ModuleTypeConstants"); /** @typedef {import("../Compiler")} Compiler */