1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Tobias Koppers @sokra
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const { RawSource } = require("webpack-sources");
|
---|
9 | const ConcatenationScope = require("../ConcatenationScope");
|
---|
10 | const { UsageState } = require("../ExportsInfo");
|
---|
11 | const Generator = require("../Generator");
|
---|
12 | const { JS_TYPES } = require("../ModuleSourceTypesConstants");
|
---|
13 | const RuntimeGlobals = require("../RuntimeGlobals");
|
---|
14 |
|
---|
15 | /** @typedef {import("webpack-sources").Source} Source */
|
---|
16 | /** @typedef {import("../ExportsInfo")} ExportsInfo */
|
---|
17 | /** @typedef {import("../Generator").GenerateContext} GenerateContext */
|
---|
18 | /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
|
---|
19 | /** @typedef {import("../Module").SourceTypes} SourceTypes */
|
---|
20 | /** @typedef {import("../NormalModule")} NormalModule */
|
---|
21 | /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
---|
22 | /** @typedef {import("./JsonData")} JsonData */
|
---|
23 | /** @typedef {import("./JsonModulesPlugin").RawJsonData} RawJsonData */
|
---|
24 |
|
---|
25 | /**
|
---|
26 | * @param {RawJsonData} data Raw JSON data
|
---|
27 | * @returns {undefined|string} stringified data
|
---|
28 | */
|
---|
29 | const stringifySafe = data => {
|
---|
30 | const stringified = JSON.stringify(data);
|
---|
31 | if (!stringified) {
|
---|
32 | return; // Invalid JSON
|
---|
33 | }
|
---|
34 |
|
---|
35 | return stringified.replace(/\u2028|\u2029/g, str =>
|
---|
36 | str === "\u2029" ? "\\u2029" : "\\u2028"
|
---|
37 | ); // invalid in JavaScript but valid JSON
|
---|
38 | };
|
---|
39 |
|
---|
40 | /**
|
---|
41 | * @param {RawJsonData} data Raw JSON data (always an object or array)
|
---|
42 | * @param {ExportsInfo} exportsInfo exports info
|
---|
43 | * @param {RuntimeSpec} runtime the runtime
|
---|
44 | * @returns {RawJsonData} reduced data
|
---|
45 | */
|
---|
46 | const createObjectForExportsInfo = (data, exportsInfo, runtime) => {
|
---|
47 | if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused)
|
---|
48 | return data;
|
---|
49 | const isArray = Array.isArray(data);
|
---|
50 | /** @type {RawJsonData} */
|
---|
51 | const reducedData = isArray ? [] : {};
|
---|
52 | for (const key of Object.keys(data)) {
|
---|
53 | const exportInfo = exportsInfo.getReadOnlyExportInfo(key);
|
---|
54 | const used = exportInfo.getUsed(runtime);
|
---|
55 | if (used === UsageState.Unused) continue;
|
---|
56 |
|
---|
57 | /** @type {RawJsonData} */
|
---|
58 | const value =
|
---|
59 | used === UsageState.OnlyPropertiesUsed && exportInfo.exportsInfo
|
---|
60 | ? createObjectForExportsInfo(data[key], exportInfo.exportsInfo, runtime)
|
---|
61 | : data[key];
|
---|
62 |
|
---|
63 | const name = /** @type {string} */ (exportInfo.getUsedName(key, runtime));
|
---|
64 | /** @type {Record<string, RawJsonData>} */ (reducedData)[name] = value;
|
---|
65 | }
|
---|
66 | if (isArray) {
|
---|
67 | const arrayLengthWhenUsed =
|
---|
68 | exportsInfo.getReadOnlyExportInfo("length").getUsed(runtime) !==
|
---|
69 | UsageState.Unused
|
---|
70 | ? data.length
|
---|
71 | : undefined;
|
---|
72 |
|
---|
73 | let sizeObjectMinusArray = 0;
|
---|
74 | for (let i = 0; i < reducedData.length; i++) {
|
---|
75 | if (reducedData[i] === undefined) {
|
---|
76 | sizeObjectMinusArray -= 2;
|
---|
77 | } else {
|
---|
78 | sizeObjectMinusArray += `${i}`.length + 3;
|
---|
79 | }
|
---|
80 | }
|
---|
81 | if (arrayLengthWhenUsed !== undefined) {
|
---|
82 | sizeObjectMinusArray +=
|
---|
83 | `${arrayLengthWhenUsed}`.length +
|
---|
84 | 8 -
|
---|
85 | (arrayLengthWhenUsed - reducedData.length) * 2;
|
---|
86 | }
|
---|
87 | if (sizeObjectMinusArray < 0)
|
---|
88 | return Object.assign(
|
---|
89 | arrayLengthWhenUsed === undefined
|
---|
90 | ? {}
|
---|
91 | : { length: arrayLengthWhenUsed },
|
---|
92 | reducedData
|
---|
93 | );
|
---|
94 | /** @type {number} */
|
---|
95 | const generatedLength =
|
---|
96 | arrayLengthWhenUsed !== undefined
|
---|
97 | ? Math.max(arrayLengthWhenUsed, reducedData.length)
|
---|
98 | : reducedData.length;
|
---|
99 | for (let i = 0; i < generatedLength; i++) {
|
---|
100 | if (reducedData[i] === undefined) {
|
---|
101 | reducedData[i] = 0;
|
---|
102 | }
|
---|
103 | }
|
---|
104 | }
|
---|
105 | return reducedData;
|
---|
106 | };
|
---|
107 |
|
---|
108 | class JsonGenerator extends Generator {
|
---|
109 | /**
|
---|
110 | * @param {NormalModule} module fresh module
|
---|
111 | * @returns {SourceTypes} available types (do not mutate)
|
---|
112 | */
|
---|
113 | getTypes(module) {
|
---|
114 | return JS_TYPES;
|
---|
115 | }
|
---|
116 |
|
---|
117 | /**
|
---|
118 | * @param {NormalModule} module the module
|
---|
119 | * @param {string=} type source type
|
---|
120 | * @returns {number} estimate size of the module
|
---|
121 | */
|
---|
122 | getSize(module, type) {
|
---|
123 | /** @type {RawJsonData | undefined} */
|
---|
124 | const data =
|
---|
125 | module.buildInfo &&
|
---|
126 | module.buildInfo.jsonData &&
|
---|
127 | module.buildInfo.jsonData.get();
|
---|
128 | if (!data) return 0;
|
---|
129 | return /** @type {string} */ (stringifySafe(data)).length + 10;
|
---|
130 | }
|
---|
131 |
|
---|
132 | /**
|
---|
133 | * @param {NormalModule} module module for which the bailout reason should be determined
|
---|
134 | * @param {ConcatenationBailoutReasonContext} context context
|
---|
135 | * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
|
---|
136 | */
|
---|
137 | getConcatenationBailoutReason(module, context) {
|
---|
138 | return undefined;
|
---|
139 | }
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * @param {NormalModule} module module for which the code should be generated
|
---|
143 | * @param {GenerateContext} generateContext context for generate
|
---|
144 | * @returns {Source | null} generated code
|
---|
145 | */
|
---|
146 | generate(
|
---|
147 | module,
|
---|
148 | {
|
---|
149 | moduleGraph,
|
---|
150 | runtimeTemplate,
|
---|
151 | runtimeRequirements,
|
---|
152 | runtime,
|
---|
153 | concatenationScope
|
---|
154 | }
|
---|
155 | ) {
|
---|
156 | /** @type {RawJsonData | undefined} */
|
---|
157 | const data =
|
---|
158 | module.buildInfo &&
|
---|
159 | module.buildInfo.jsonData &&
|
---|
160 | module.buildInfo.jsonData.get();
|
---|
161 | if (data === undefined) {
|
---|
162 | return new RawSource(
|
---|
163 | runtimeTemplate.missingModuleStatement({
|
---|
164 | request: module.rawRequest
|
---|
165 | })
|
---|
166 | );
|
---|
167 | }
|
---|
168 | const exportsInfo = moduleGraph.getExportsInfo(module);
|
---|
169 | /** @type {RawJsonData} */
|
---|
170 | const finalJson =
|
---|
171 | typeof data === "object" &&
|
---|
172 | data &&
|
---|
173 | exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused
|
---|
174 | ? createObjectForExportsInfo(data, exportsInfo, runtime)
|
---|
175 | : data;
|
---|
176 | // Use JSON because JSON.parse() is much faster than JavaScript evaluation
|
---|
177 | const jsonStr = /** @type {string} */ (stringifySafe(finalJson));
|
---|
178 | const jsonExpr =
|
---|
179 | jsonStr.length > 20 && typeof finalJson === "object"
|
---|
180 | ? `/*#__PURE__*/JSON.parse('${jsonStr.replace(/[\\']/g, "\\$&")}')`
|
---|
181 | : jsonStr;
|
---|
182 | /** @type {string} */
|
---|
183 | let content;
|
---|
184 | if (concatenationScope) {
|
---|
185 | content = `${runtimeTemplate.supportsConst() ? "const" : "var"} ${
|
---|
186 | ConcatenationScope.NAMESPACE_OBJECT_EXPORT
|
---|
187 | } = ${jsonExpr};`;
|
---|
188 | concatenationScope.registerNamespaceExport(
|
---|
189 | ConcatenationScope.NAMESPACE_OBJECT_EXPORT
|
---|
190 | );
|
---|
191 | } else {
|
---|
192 | runtimeRequirements.add(RuntimeGlobals.module);
|
---|
193 | content = `${module.moduleArgument}.exports = ${jsonExpr};`;
|
---|
194 | }
|
---|
195 | return new RawSource(content);
|
---|
196 | }
|
---|
197 | }
|
---|
198 |
|
---|
199 | module.exports = JsonGenerator;
|
---|