Skip to content

Commit 8ab85e2

Browse files
committed
feat: implement module.generator.json.JSONParse
For large `.json` modules, webpack will generate `JSON.parse` by default for better performance. But there are some circumstances that `JSON.parse` is not a good choice (e.g.: when doing AOT compilation). Thus, a new generator option `module.generator.json.JSONParse` is added to disable generating `JSON.parse` for `.json` module. The default value is kept as `true` and can be opt-out by custom rules. fix: #19319
1 parent cb25853 commit 8ab85e2

22 files changed

Lines changed: 236 additions & 13 deletions

declarations/WebpackOptions.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3346,6 +3346,15 @@ export interface JavascriptParserOptions {
33463346
wrappedContextRegExp?: RegExp;
33473347
[k: string]: any;
33483348
}
3349+
/**
3350+
* Generator options for json modules.
3351+
*/
3352+
export interface JsonGeneratorOptions {
3353+
/**
3354+
* Use `JSON.parse` when the JSON string is longer than 20 characters.
3355+
*/
3356+
JSONParse?: boolean;
3357+
}
33493358
/**
33503359
* Options for the default backend.
33513360
*/
@@ -3882,6 +3891,10 @@ export interface GeneratorOptionsByModuleTypeKnown {
38823891
* No generator options are supported for this module type.
38833892
*/
38843893
"javascript/esm"?: EmptyGeneratorOptions;
3894+
/**
3895+
* Generator options for json modules.
3896+
*/
3897+
json?: JsonGeneratorOptions;
38853898
}
38863899
/**
38873900
* Specify options for each generator.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* This file was automatically generated.
3+
* DO NOT MODIFY BY HAND.
4+
* Run `yarn special-lint-fix` to update
5+
*/
6+
7+
export interface JsonModulesPluginGeneratorOptions {
8+
/**
9+
* Use `JSON.parse` when the JSON string is longer than 20 characters.
10+
*/
11+
JSONParse?: boolean;
12+
}

lib/config/defaults.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const {
4747
/** @typedef {import("../../declarations/WebpackOptions").GeneratorOptionsByModuleTypeKnown} GeneratorOptionsByModuleTypeKnown */
4848
/** @typedef {import("../../declarations/WebpackOptions").InfrastructureLogging} InfrastructureLogging */
4949
/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
50+
/** @typedef {import("../../declarations/WebpackOptions").JsonGeneratorOptions} JsonGeneratorOptions */
5051
/** @typedef {import("../../declarations/WebpackOptions").Library} Library */
5152
/** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */
5253
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
@@ -581,6 +582,14 @@ const applyJavascriptParserOptionsDefaults = (
581582
if (futureDefaults) D(parserOptions, "exportsPresence", "error");
582583
};
583584

585+
/**
586+
* @param {JsonGeneratorOptions} generatorOptions generator options
587+
* @returns {void}
588+
*/
589+
const applyJsonGeneratorOptionsDefaults = generatorOptions => {
590+
D(generatorOptions, "JSONParse", true);
591+
};
592+
584593
/**
585594
* @param {CssGeneratorOptions} generatorOptions generator options
586595
* @param {object} options options
@@ -682,6 +691,12 @@ const applyModuleDefaults = (
682691
}
683692
);
684693

694+
F(module.generator, "json", () => ({}));
695+
applyJsonGeneratorOptionsDefaults(
696+
/** @type {NonNullable<GeneratorOptionsByModuleTypeKnown["json"]>} */
697+
(module.generator.json)
698+
);
699+
685700
if (css) {
686701
F(module.parser, CSS_MODULE_TYPE, () => ({}));
687702

lib/json/JsonGenerator.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const { JS_TYPES } = require("../ModuleSourceTypesConstants");
1313
const RuntimeGlobals = require("../RuntimeGlobals");
1414

1515
/** @typedef {import("webpack-sources").Source} Source */
16+
/** @typedef {import("../../declarations/WebpackOptions").JsonGeneratorOptions} JsonGeneratorOptions */
1617
/** @typedef {import("../ExportsInfo")} ExportsInfo */
1718
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
1819
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
@@ -106,6 +107,14 @@ const createObjectForExportsInfo = (data, exportsInfo, runtime) => {
106107
};
107108

108109
class JsonGenerator extends Generator {
110+
/**
111+
* @param {JsonGeneratorOptions} options options
112+
*/
113+
constructor(options) {
114+
super();
115+
this.options = options;
116+
}
117+
109118
/**
110119
* @param {NormalModule} module fresh module
111120
* @returns {SourceTypes} available types (do not mutate)
@@ -176,7 +185,9 @@ class JsonGenerator extends Generator {
176185
// Use JSON because JSON.parse() is much faster than JavaScript evaluation
177186
const jsonStr = /** @type {string} */ (stringifySafe(finalJson));
178187
const jsonExpr =
179-
jsonStr.length > 20 && typeof finalJson === "object"
188+
this.options.JSONParse &&
189+
jsonStr.length > 20 &&
190+
typeof finalJson === "object"
180191
? `/*#__PURE__*/JSON.parse('${jsonStr.replace(/[\\']/g, "\\$&")}')`
181192
: jsonStr.replace(/"__proto__":/g, '["__proto__"]:');
182193
/** @type {string} */

lib/json/JsonModulesPlugin.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ const validate = createSchemaValidation(
2222
}
2323
);
2424

25+
const validateGenerator = createSchemaValidation(
26+
require("../../schemas/plugins/JsonModulesPluginGenerator.check.js"),
27+
() => require("../../schemas/plugins/JsonModulesPluginGenerator.json"),
28+
{
29+
name: "Json Modules Plugin",
30+
baseDataPath: "generator"
31+
}
32+
);
33+
2534
const PLUGIN_NAME = "JsonModulesPlugin";
2635

2736
/**
@@ -46,7 +55,10 @@ class JsonModulesPlugin {
4655
});
4756
normalModuleFactory.hooks.createGenerator
4857
.for(JSON_MODULE_TYPE)
49-
.tap(PLUGIN_NAME, () => new JsonGenerator());
58+
.tap(PLUGIN_NAME, generatorOptions => {
59+
validateGenerator(generatorOptions);
60+
return new JsonGenerator(generatorOptions);
61+
});
5062
}
5163
);
5264
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,6 @@
187187
"node node_modules/prettier/bin/prettier.cjs --cache --write --ignore-unknown",
188188
"cspell --cache --no-must-find-files"
189189
]
190-
}
190+
},
191+
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
191192
}

schemas/WebpackOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemas/WebpackOptions.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,9 @@
15711571
},
15721572
"javascript/esm": {
15731573
"$ref": "#/definitions/EmptyGeneratorOptions"
1574+
},
1575+
"json": {
1576+
"$ref": "#/definitions/JsonGeneratorOptions"
15741577
}
15751578
}
15761579
},
@@ -2009,6 +2012,17 @@
20092012
}
20102013
}
20112014
},
2015+
"JsonGeneratorOptions": {
2016+
"description": "Generator options for json modules.",
2017+
"type": "object",
2018+
"additionalProperties": false,
2019+
"properties": {
2020+
"JSONParse": {
2021+
"description": "Use `JSON.parse` when the JSON string is longer than 20 characters.",
2022+
"type": "boolean"
2023+
}
2024+
}
2025+
},
20122026
"Layer": {
20132027
"description": "Specifies the layer in which modules of this entrypoint are placed.",
20142028
"anyOf": [
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* This file was automatically generated.
3+
* DO NOT MODIFY BY HAND.
4+
* Run `yarn special-lint-fix` to update
5+
*/
6+
declare const check: (options: import("../../declarations/plugins/JsonModulesPluginGenerator").JsonModulesPluginGeneratorOptions) => boolean;
7+
export = check;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* This file was automatically generated.
3+
* DO NOT MODIFY BY HAND.
4+
* Run `yarn special-lint-fix` to update
5+
*/
6+
"use strict";function r(e,{instancePath:t="",parentData:a,parentDataProperty:o,rootData:s=e}={}){if(!e||"object"!=typeof e||Array.isArray(e))return r.errors=[{params:{type:"object"}}],!1;{const t=0;for(const t in e)if("JSONParse"!==t)return r.errors=[{params:{additionalProperty:t}}],!1;if(0===t&&void 0!==e.JSONParse&&"boolean"!=typeof e.JSONParse)return r.errors=[{params:{type:"boolean"}}],!1}return r.errors=null,!0}module.exports=r,module.exports.default=r;

0 commit comments

Comments
 (0)