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 { SyncWaterfallHook, SyncHook } = require("tapable");
|
---|
9 | const {
|
---|
10 | ConcatSource,
|
---|
11 | PrefixSource,
|
---|
12 | ReplaceSource,
|
---|
13 | CachedSource,
|
---|
14 | RawSource
|
---|
15 | } = require("webpack-sources");
|
---|
16 | const Compilation = require("../Compilation");
|
---|
17 | const CssModule = require("../CssModule");
|
---|
18 | const { tryRunOrWebpackError } = require("../HookWebpackError");
|
---|
19 | const HotUpdateChunk = require("../HotUpdateChunk");
|
---|
20 | const {
|
---|
21 | CSS_MODULE_TYPE,
|
---|
22 | CSS_MODULE_TYPE_GLOBAL,
|
---|
23 | CSS_MODULE_TYPE_MODULE,
|
---|
24 | CSS_MODULE_TYPE_AUTO
|
---|
25 | } = require("../ModuleTypeConstants");
|
---|
26 | const RuntimeGlobals = require("../RuntimeGlobals");
|
---|
27 | const SelfModuleFactory = require("../SelfModuleFactory");
|
---|
28 | const Template = require("../Template");
|
---|
29 | const WebpackError = require("../WebpackError");
|
---|
30 | const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency");
|
---|
31 | const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
|
---|
32 | const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency");
|
---|
33 | const CssImportDependency = require("../dependencies/CssImportDependency");
|
---|
34 | const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
|
---|
35 | const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
|
---|
36 | const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
---|
37 | const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
---|
38 | const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
|
---|
39 | const { compareModulesByIdentifier } = require("../util/comparators");
|
---|
40 | const createSchemaValidation = require("../util/create-schema-validation");
|
---|
41 | const createHash = require("../util/createHash");
|
---|
42 | const { getUndoPath } = require("../util/identifier");
|
---|
43 | const memoize = require("../util/memoize");
|
---|
44 | const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
|
---|
45 | const CssGenerator = require("./CssGenerator");
|
---|
46 | const CssParser = require("./CssParser");
|
---|
47 |
|
---|
48 | /** @typedef {import("webpack-sources").Source} Source */
|
---|
49 | /** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputOptions */
|
---|
50 | /** @typedef {import("../Chunk")} Chunk */
|
---|
51 | /** @typedef {import("../ChunkGraph")} ChunkGraph */
|
---|
52 | /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
|
---|
53 | /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
|
---|
54 | /** @typedef {import("../Compiler")} Compiler */
|
---|
55 | /** @typedef {import("../CssModule").Inheritance} Inheritance */
|
---|
56 | /** @typedef {import("../Module")} Module */
|
---|
57 | /** @typedef {import("../Template").RuntimeTemplate} RuntimeTemplate */
|
---|
58 | /** @typedef {import("../TemplatedPathPlugin").TemplatePath} TemplatePath */
|
---|
59 | /** @typedef {import("../util/Hash")} Hash */
|
---|
60 | /** @typedef {import("../util/createHash").Algorithm} Algorithm */
|
---|
61 | /** @typedef {import("../util/memoize")} Memoize */
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * @typedef {object} RenderContext
|
---|
65 | * @property {Chunk} chunk the chunk
|
---|
66 | * @property {ChunkGraph} chunkGraph the chunk graph
|
---|
67 | * @property {CodeGenerationResults} codeGenerationResults results of code generation
|
---|
68 | * @property {RuntimeTemplate} runtimeTemplate the runtime template
|
---|
69 | * @property {string} uniqueName the unique name
|
---|
70 | * @property {string} undoPath undo path to css file
|
---|
71 | * @property {CssModule[]} modules modules
|
---|
72 | */
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * @typedef {object} ChunkRenderContext
|
---|
76 | * @property {Chunk} chunk the chunk
|
---|
77 | * @property {ChunkGraph} chunkGraph the chunk graph
|
---|
78 | * @property {CodeGenerationResults} codeGenerationResults results of code generation
|
---|
79 | * @property {RuntimeTemplate} runtimeTemplate the runtime template
|
---|
80 | * @property {string} undoPath undo path to css file
|
---|
81 | */
|
---|
82 |
|
---|
83 | /**
|
---|
84 | * @typedef {object} CompilationHooks
|
---|
85 | * @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
|
---|
86 | * @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
|
---|
87 | */
|
---|
88 |
|
---|
89 | const getCssLoadingRuntimeModule = memoize(() =>
|
---|
90 | require("./CssLoadingRuntimeModule")
|
---|
91 | );
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * @param {string} name name
|
---|
95 | * @returns {{oneOf: [{$ref: string}], definitions: *}} schema
|
---|
96 | */
|
---|
97 | const getSchema = name => {
|
---|
98 | const { definitions } = require("../../schemas/WebpackOptions.json");
|
---|
99 | return {
|
---|
100 | definitions,
|
---|
101 | oneOf: [{ $ref: `#/definitions/${name}` }]
|
---|
102 | };
|
---|
103 | };
|
---|
104 |
|
---|
105 | const generatorValidationOptions = {
|
---|
106 | name: "Css Modules Plugin",
|
---|
107 | baseDataPath: "generator"
|
---|
108 | };
|
---|
109 | const validateGeneratorOptions = {
|
---|
110 | css: createSchemaValidation(
|
---|
111 | require("../../schemas/plugins/css/CssGeneratorOptions.check.js"),
|
---|
112 | () => getSchema("CssGeneratorOptions"),
|
---|
113 | generatorValidationOptions
|
---|
114 | ),
|
---|
115 | "css/auto": createSchemaValidation(
|
---|
116 | require("../../schemas/plugins/css/CssAutoGeneratorOptions.check.js"),
|
---|
117 | () => getSchema("CssAutoGeneratorOptions"),
|
---|
118 | generatorValidationOptions
|
---|
119 | ),
|
---|
120 | "css/module": createSchemaValidation(
|
---|
121 | require("../../schemas/plugins/css/CssModuleGeneratorOptions.check.js"),
|
---|
122 | () => getSchema("CssModuleGeneratorOptions"),
|
---|
123 | generatorValidationOptions
|
---|
124 | ),
|
---|
125 | "css/global": createSchemaValidation(
|
---|
126 | require("../../schemas/plugins/css/CssGlobalGeneratorOptions.check.js"),
|
---|
127 | () => getSchema("CssGlobalGeneratorOptions"),
|
---|
128 | generatorValidationOptions
|
---|
129 | )
|
---|
130 | };
|
---|
131 |
|
---|
132 | const parserValidationOptions = {
|
---|
133 | name: "Css Modules Plugin",
|
---|
134 | baseDataPath: "parser"
|
---|
135 | };
|
---|
136 | const validateParserOptions = {
|
---|
137 | css: createSchemaValidation(
|
---|
138 | require("../../schemas/plugins/css/CssParserOptions.check.js"),
|
---|
139 | () => getSchema("CssParserOptions"),
|
---|
140 | parserValidationOptions
|
---|
141 | ),
|
---|
142 | "css/auto": createSchemaValidation(
|
---|
143 | require("../../schemas/plugins/css/CssAutoParserOptions.check.js"),
|
---|
144 | () => getSchema("CssAutoParserOptions"),
|
---|
145 | parserValidationOptions
|
---|
146 | ),
|
---|
147 | "css/module": createSchemaValidation(
|
---|
148 | require("../../schemas/plugins/css/CssModuleParserOptions.check.js"),
|
---|
149 | () => getSchema("CssModuleParserOptions"),
|
---|
150 | parserValidationOptions
|
---|
151 | ),
|
---|
152 | "css/global": createSchemaValidation(
|
---|
153 | require("../../schemas/plugins/css/CssGlobalParserOptions.check.js"),
|
---|
154 | () => getSchema("CssGlobalParserOptions"),
|
---|
155 | parserValidationOptions
|
---|
156 | )
|
---|
157 | };
|
---|
158 |
|
---|
159 | /** @type {WeakMap<Compilation, CompilationHooks>} */
|
---|
160 | const compilationHooksMap = new WeakMap();
|
---|
161 |
|
---|
162 | const PLUGIN_NAME = "CssModulesPlugin";
|
---|
163 |
|
---|
164 | class CssModulesPlugin {
|
---|
165 | /**
|
---|
166 | * @param {Compilation} compilation the compilation
|
---|
167 | * @returns {CompilationHooks} the attached hooks
|
---|
168 | */
|
---|
169 | static getCompilationHooks(compilation) {
|
---|
170 | if (!(compilation instanceof Compilation)) {
|
---|
171 | throw new TypeError(
|
---|
172 | "The 'compilation' argument must be an instance of Compilation"
|
---|
173 | );
|
---|
174 | }
|
---|
175 | let hooks = compilationHooksMap.get(compilation);
|
---|
176 | if (hooks === undefined) {
|
---|
177 | hooks = {
|
---|
178 | renderModulePackage: new SyncWaterfallHook([
|
---|
179 | "source",
|
---|
180 | "module",
|
---|
181 | "renderContext"
|
---|
182 | ]),
|
---|
183 | chunkHash: new SyncHook(["chunk", "hash", "context"])
|
---|
184 | };
|
---|
185 | compilationHooksMap.set(compilation, hooks);
|
---|
186 | }
|
---|
187 | return hooks;
|
---|
188 | }
|
---|
189 |
|
---|
190 | constructor() {
|
---|
191 | /** @type {WeakMap<Source, { undoPath: string, inheritance: Inheritance, source: CachedSource }>} */
|
---|
192 | this._moduleFactoryCache = new WeakMap();
|
---|
193 | }
|
---|
194 |
|
---|
195 | /**
|
---|
196 | * Apply the plugin
|
---|
197 | * @param {Compiler} compiler the compiler instance
|
---|
198 | * @returns {void}
|
---|
199 | */
|
---|
200 | apply(compiler) {
|
---|
201 | compiler.hooks.compilation.tap(
|
---|
202 | PLUGIN_NAME,
|
---|
203 | (compilation, { normalModuleFactory }) => {
|
---|
204 | const hooks = CssModulesPlugin.getCompilationHooks(compilation);
|
---|
205 | const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
|
---|
206 | compilation.dependencyFactories.set(
|
---|
207 | CssImportDependency,
|
---|
208 | normalModuleFactory
|
---|
209 | );
|
---|
210 | compilation.dependencyTemplates.set(
|
---|
211 | CssImportDependency,
|
---|
212 | new CssImportDependency.Template()
|
---|
213 | );
|
---|
214 | compilation.dependencyFactories.set(
|
---|
215 | CssUrlDependency,
|
---|
216 | normalModuleFactory
|
---|
217 | );
|
---|
218 | compilation.dependencyTemplates.set(
|
---|
219 | CssUrlDependency,
|
---|
220 | new CssUrlDependency.Template()
|
---|
221 | );
|
---|
222 | compilation.dependencyTemplates.set(
|
---|
223 | CssLocalIdentifierDependency,
|
---|
224 | new CssLocalIdentifierDependency.Template()
|
---|
225 | );
|
---|
226 | compilation.dependencyFactories.set(
|
---|
227 | CssSelfLocalIdentifierDependency,
|
---|
228 | selfFactory
|
---|
229 | );
|
---|
230 | compilation.dependencyTemplates.set(
|
---|
231 | CssSelfLocalIdentifierDependency,
|
---|
232 | new CssSelfLocalIdentifierDependency.Template()
|
---|
233 | );
|
---|
234 | compilation.dependencyFactories.set(
|
---|
235 | CssIcssImportDependency,
|
---|
236 | normalModuleFactory
|
---|
237 | );
|
---|
238 | compilation.dependencyTemplates.set(
|
---|
239 | CssIcssImportDependency,
|
---|
240 | new CssIcssImportDependency.Template()
|
---|
241 | );
|
---|
242 | compilation.dependencyTemplates.set(
|
---|
243 | CssIcssExportDependency,
|
---|
244 | new CssIcssExportDependency.Template()
|
---|
245 | );
|
---|
246 | compilation.dependencyTemplates.set(
|
---|
247 | CssIcssSymbolDependency,
|
---|
248 | new CssIcssSymbolDependency.Template()
|
---|
249 | );
|
---|
250 | compilation.dependencyTemplates.set(
|
---|
251 | StaticExportsDependency,
|
---|
252 | new StaticExportsDependency.Template()
|
---|
253 | );
|
---|
254 | for (const type of [
|
---|
255 | CSS_MODULE_TYPE,
|
---|
256 | CSS_MODULE_TYPE_GLOBAL,
|
---|
257 | CSS_MODULE_TYPE_MODULE,
|
---|
258 | CSS_MODULE_TYPE_AUTO
|
---|
259 | ]) {
|
---|
260 | normalModuleFactory.hooks.createParser
|
---|
261 | .for(type)
|
---|
262 | .tap(PLUGIN_NAME, parserOptions => {
|
---|
263 | validateParserOptions[type](parserOptions);
|
---|
264 | const { url, import: importOption, namedExports } = parserOptions;
|
---|
265 |
|
---|
266 | switch (type) {
|
---|
267 | case CSS_MODULE_TYPE:
|
---|
268 | return new CssParser({
|
---|
269 | importOption,
|
---|
270 | url,
|
---|
271 | namedExports
|
---|
272 | });
|
---|
273 | case CSS_MODULE_TYPE_GLOBAL:
|
---|
274 | return new CssParser({
|
---|
275 | defaultMode: "global",
|
---|
276 | importOption,
|
---|
277 | url,
|
---|
278 | namedExports
|
---|
279 | });
|
---|
280 | case CSS_MODULE_TYPE_MODULE:
|
---|
281 | return new CssParser({
|
---|
282 | defaultMode: "local",
|
---|
283 | importOption,
|
---|
284 | url,
|
---|
285 | namedExports
|
---|
286 | });
|
---|
287 | case CSS_MODULE_TYPE_AUTO:
|
---|
288 | return new CssParser({
|
---|
289 | defaultMode: "auto",
|
---|
290 | importOption,
|
---|
291 | url,
|
---|
292 | namedExports
|
---|
293 | });
|
---|
294 | }
|
---|
295 | });
|
---|
296 | normalModuleFactory.hooks.createGenerator
|
---|
297 | .for(type)
|
---|
298 | .tap(PLUGIN_NAME, generatorOptions => {
|
---|
299 | validateGeneratorOptions[type](generatorOptions);
|
---|
300 |
|
---|
301 | return new CssGenerator(generatorOptions);
|
---|
302 | });
|
---|
303 | normalModuleFactory.hooks.createModuleClass
|
---|
304 | .for(type)
|
---|
305 | .tap(PLUGIN_NAME, (createData, resolveData) => {
|
---|
306 | if (resolveData.dependencies.length > 0) {
|
---|
307 | // When CSS is imported from CSS there is only one dependency
|
---|
308 | const dependency = resolveData.dependencies[0];
|
---|
309 |
|
---|
310 | if (dependency instanceof CssImportDependency) {
|
---|
311 | const parent =
|
---|
312 | /** @type {CssModule} */
|
---|
313 | (compilation.moduleGraph.getParentModule(dependency));
|
---|
314 |
|
---|
315 | if (parent instanceof CssModule) {
|
---|
316 | /** @type {import("../CssModule").Inheritance | undefined} */
|
---|
317 | let inheritance;
|
---|
318 |
|
---|
319 | if (
|
---|
320 | parent.cssLayer !== undefined ||
|
---|
321 | parent.supports ||
|
---|
322 | parent.media
|
---|
323 | ) {
|
---|
324 | if (!inheritance) {
|
---|
325 | inheritance = [];
|
---|
326 | }
|
---|
327 |
|
---|
328 | inheritance.push([
|
---|
329 | parent.cssLayer,
|
---|
330 | parent.supports,
|
---|
331 | parent.media
|
---|
332 | ]);
|
---|
333 | }
|
---|
334 |
|
---|
335 | if (parent.inheritance) {
|
---|
336 | if (!inheritance) {
|
---|
337 | inheritance = [];
|
---|
338 | }
|
---|
339 |
|
---|
340 | inheritance.push(...parent.inheritance);
|
---|
341 | }
|
---|
342 |
|
---|
343 | return new CssModule({
|
---|
344 | ...createData,
|
---|
345 | cssLayer: dependency.layer,
|
---|
346 | supports: dependency.supports,
|
---|
347 | media: dependency.media,
|
---|
348 | inheritance
|
---|
349 | });
|
---|
350 | }
|
---|
351 |
|
---|
352 | return new CssModule({
|
---|
353 | ...createData,
|
---|
354 | cssLayer: dependency.layer,
|
---|
355 | supports: dependency.supports,
|
---|
356 | media: dependency.media
|
---|
357 | });
|
---|
358 | }
|
---|
359 | }
|
---|
360 |
|
---|
361 | return new CssModule(createData);
|
---|
362 | });
|
---|
363 | }
|
---|
364 |
|
---|
365 | JavascriptModulesPlugin.getCompilationHooks(
|
---|
366 | compilation
|
---|
367 | ).renderModuleContent.tap(PLUGIN_NAME, (source, module) => {
|
---|
368 | if (module instanceof CssModule && module.hot) {
|
---|
369 | const exports = module.buildInfo.cssData.exports;
|
---|
370 | const stringifiedExports = JSON.stringify(
|
---|
371 | JSON.stringify(
|
---|
372 | Array.from(exports).reduce((obj, [key, value]) => {
|
---|
373 | obj[key] = value;
|
---|
374 | return obj;
|
---|
375 | }, {})
|
---|
376 | )
|
---|
377 | );
|
---|
378 |
|
---|
379 | const hmrCode = Template.asString([
|
---|
380 | "",
|
---|
381 | `var __webpack_css_exports__ = ${stringifiedExports};`,
|
---|
382 | "// only invalidate when locals change",
|
---|
383 | "if (module.hot.data && module.hot.data.__webpack_css_exports__ && module.hot.data.__webpack_css_exports__ != __webpack_css_exports__) {",
|
---|
384 | Template.indent("module.hot.invalidate();"),
|
---|
385 | "} else {",
|
---|
386 | Template.indent("module.hot.accept();"),
|
---|
387 | "}",
|
---|
388 | "module.hot.dispose(function(data) { data.__webpack_css_exports__ = __webpack_css_exports__; });"
|
---|
389 | ]);
|
---|
390 |
|
---|
391 | return new ConcatSource(source, "\n", new RawSource(hmrCode));
|
---|
392 | }
|
---|
393 | });
|
---|
394 | const orderedCssModulesPerChunk = new WeakMap();
|
---|
395 | compilation.hooks.afterCodeGeneration.tap(PLUGIN_NAME, () => {
|
---|
396 | const { chunkGraph } = compilation;
|
---|
397 | for (const chunk of compilation.chunks) {
|
---|
398 | if (CssModulesPlugin.chunkHasCss(chunk, chunkGraph)) {
|
---|
399 | orderedCssModulesPerChunk.set(
|
---|
400 | chunk,
|
---|
401 | this.getOrderedChunkCssModules(chunk, chunkGraph, compilation)
|
---|
402 | );
|
---|
403 | }
|
---|
404 | }
|
---|
405 | });
|
---|
406 | compilation.hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash, context) => {
|
---|
407 | hooks.chunkHash.call(chunk, hash, context);
|
---|
408 | });
|
---|
409 | compilation.hooks.contentHash.tap(PLUGIN_NAME, chunk => {
|
---|
410 | const {
|
---|
411 | chunkGraph,
|
---|
412 | codeGenerationResults,
|
---|
413 | moduleGraph,
|
---|
414 | runtimeTemplate,
|
---|
415 | outputOptions: {
|
---|
416 | hashSalt,
|
---|
417 | hashDigest,
|
---|
418 | hashDigestLength,
|
---|
419 | hashFunction
|
---|
420 | }
|
---|
421 | } = compilation;
|
---|
422 | const hash = createHash(/** @type {Algorithm} */ (hashFunction));
|
---|
423 | if (hashSalt) hash.update(hashSalt);
|
---|
424 | hooks.chunkHash.call(chunk, hash, {
|
---|
425 | chunkGraph,
|
---|
426 | codeGenerationResults,
|
---|
427 | moduleGraph,
|
---|
428 | runtimeTemplate
|
---|
429 | });
|
---|
430 | const modules = orderedCssModulesPerChunk.get(chunk);
|
---|
431 | if (modules) {
|
---|
432 | for (const module of modules) {
|
---|
433 | hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
|
---|
434 | }
|
---|
435 | }
|
---|
436 | const digest = /** @type {string} */ (hash.digest(hashDigest));
|
---|
437 | chunk.contentHash.css = nonNumericOnlyHash(
|
---|
438 | digest,
|
---|
439 | /** @type {number} */
|
---|
440 | (hashDigestLength)
|
---|
441 | );
|
---|
442 | });
|
---|
443 | compilation.hooks.renderManifest.tap(PLUGIN_NAME, (result, options) => {
|
---|
444 | const { chunkGraph } = compilation;
|
---|
445 | const { hash, chunk, codeGenerationResults, runtimeTemplate } =
|
---|
446 | options;
|
---|
447 |
|
---|
448 | if (chunk instanceof HotUpdateChunk) return result;
|
---|
449 |
|
---|
450 | /** @type {CssModule[] | undefined} */
|
---|
451 | const modules = orderedCssModulesPerChunk.get(chunk);
|
---|
452 | if (modules !== undefined) {
|
---|
453 | const { path: filename, info } = compilation.getPathWithInfo(
|
---|
454 | CssModulesPlugin.getChunkFilenameTemplate(
|
---|
455 | chunk,
|
---|
456 | compilation.outputOptions
|
---|
457 | ),
|
---|
458 | {
|
---|
459 | hash,
|
---|
460 | runtime: chunk.runtime,
|
---|
461 | chunk,
|
---|
462 | contentHashType: "css"
|
---|
463 | }
|
---|
464 | );
|
---|
465 | const undoPath = getUndoPath(
|
---|
466 | filename,
|
---|
467 | /** @type {string} */
|
---|
468 | (compilation.outputOptions.path),
|
---|
469 | false
|
---|
470 | );
|
---|
471 | result.push({
|
---|
472 | render: () =>
|
---|
473 | this.renderChunk(
|
---|
474 | {
|
---|
475 | chunk,
|
---|
476 | chunkGraph,
|
---|
477 | codeGenerationResults,
|
---|
478 | uniqueName: compilation.outputOptions.uniqueName,
|
---|
479 | undoPath,
|
---|
480 | modules,
|
---|
481 | runtimeTemplate
|
---|
482 | },
|
---|
483 | hooks
|
---|
484 | ),
|
---|
485 | filename,
|
---|
486 | info,
|
---|
487 | identifier: `css${chunk.id}`,
|
---|
488 | hash: chunk.contentHash.css
|
---|
489 | });
|
---|
490 | }
|
---|
491 | return result;
|
---|
492 | });
|
---|
493 | const globalChunkLoading = compilation.outputOptions.chunkLoading;
|
---|
494 | /**
|
---|
495 | * @param {Chunk} chunk the chunk
|
---|
496 | * @returns {boolean} true, when enabled
|
---|
497 | */
|
---|
498 | const isEnabledForChunk = chunk => {
|
---|
499 | const options = chunk.getEntryOptions();
|
---|
500 | const chunkLoading =
|
---|
501 | options && options.chunkLoading !== undefined
|
---|
502 | ? options.chunkLoading
|
---|
503 | : globalChunkLoading;
|
---|
504 | return chunkLoading === "jsonp" || chunkLoading === "import";
|
---|
505 | };
|
---|
506 | const onceForChunkSet = new WeakSet();
|
---|
507 | /**
|
---|
508 | * @param {Chunk} chunk chunk to check
|
---|
509 | * @param {Set<string>} set runtime requirements
|
---|
510 | */
|
---|
511 | const handler = (chunk, set) => {
|
---|
512 | if (onceForChunkSet.has(chunk)) return;
|
---|
513 | onceForChunkSet.add(chunk);
|
---|
514 | if (!isEnabledForChunk(chunk)) return;
|
---|
515 |
|
---|
516 | set.add(RuntimeGlobals.makeNamespaceObject);
|
---|
517 |
|
---|
518 | const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
|
---|
519 | compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
|
---|
520 | };
|
---|
521 | compilation.hooks.runtimeRequirementInTree
|
---|
522 | .for(RuntimeGlobals.hasCssModules)
|
---|
523 | .tap(PLUGIN_NAME, handler);
|
---|
524 | compilation.hooks.runtimeRequirementInTree
|
---|
525 | .for(RuntimeGlobals.ensureChunkHandlers)
|
---|
526 | .tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
|
---|
527 | if (!isEnabledForChunk(chunk)) return;
|
---|
528 | if (
|
---|
529 | !chunkGraph.hasModuleInGraph(
|
---|
530 | chunk,
|
---|
531 | m =>
|
---|
532 | m.type === CSS_MODULE_TYPE ||
|
---|
533 | m.type === CSS_MODULE_TYPE_GLOBAL ||
|
---|
534 | m.type === CSS_MODULE_TYPE_MODULE ||
|
---|
535 | m.type === CSS_MODULE_TYPE_AUTO
|
---|
536 | )
|
---|
537 | ) {
|
---|
538 | return;
|
---|
539 | }
|
---|
540 |
|
---|
541 | set.add(RuntimeGlobals.hasOwnProperty);
|
---|
542 | set.add(RuntimeGlobals.publicPath);
|
---|
543 | set.add(RuntimeGlobals.getChunkCssFilename);
|
---|
544 | });
|
---|
545 | compilation.hooks.runtimeRequirementInTree
|
---|
546 | .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
|
---|
547 | .tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
|
---|
548 | if (!isEnabledForChunk(chunk)) return;
|
---|
549 | if (
|
---|
550 | !chunkGraph.hasModuleInGraph(
|
---|
551 | chunk,
|
---|
552 | m =>
|
---|
553 | m.type === CSS_MODULE_TYPE ||
|
---|
554 | m.type === CSS_MODULE_TYPE_GLOBAL ||
|
---|
555 | m.type === CSS_MODULE_TYPE_MODULE ||
|
---|
556 | m.type === CSS_MODULE_TYPE_AUTO
|
---|
557 | )
|
---|
558 | ) {
|
---|
559 | return;
|
---|
560 | }
|
---|
561 | set.add(RuntimeGlobals.publicPath);
|
---|
562 | set.add(RuntimeGlobals.getChunkCssFilename);
|
---|
563 | });
|
---|
564 | }
|
---|
565 | );
|
---|
566 | }
|
---|
567 |
|
---|
568 | /**
|
---|
569 | * @param {Chunk} chunk chunk
|
---|
570 | * @param {Iterable<Module>} modules unordered modules
|
---|
571 | * @param {Compilation} compilation compilation
|
---|
572 | * @returns {Module[]} ordered modules
|
---|
573 | */
|
---|
574 | getModulesInOrder(chunk, modules, compilation) {
|
---|
575 | if (!modules) return [];
|
---|
576 |
|
---|
577 | /** @type {Module[]} */
|
---|
578 | const modulesList = [...modules];
|
---|
579 |
|
---|
580 | // Get ordered list of modules per chunk group
|
---|
581 | // Lists are in reverse order to allow to use Array.pop()
|
---|
582 | const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
|
---|
583 | const sortedModules = modulesList
|
---|
584 | .map(module => ({
|
---|
585 | module,
|
---|
586 | index: chunkGroup.getModulePostOrderIndex(module)
|
---|
587 | }))
|
---|
588 | .filter(item => item.index !== undefined)
|
---|
589 | .sort(
|
---|
590 | (a, b) =>
|
---|
591 | /** @type {number} */ (b.index) - /** @type {number} */ (a.index)
|
---|
592 | )
|
---|
593 | .map(item => item.module);
|
---|
594 |
|
---|
595 | return { list: sortedModules, set: new Set(sortedModules) };
|
---|
596 | });
|
---|
597 |
|
---|
598 | if (modulesByChunkGroup.length === 1)
|
---|
599 | return modulesByChunkGroup[0].list.reverse();
|
---|
600 |
|
---|
601 | /**
|
---|
602 | * @param {{ list: Module[] }} a a
|
---|
603 | * @param {{ list: Module[] }} b b
|
---|
604 | * @returns {-1 | 0 | 1} result
|
---|
605 | */
|
---|
606 | const compareModuleLists = ({ list: a }, { list: b }) => {
|
---|
607 | if (a.length === 0) {
|
---|
608 | return b.length === 0 ? 0 : 1;
|
---|
609 | }
|
---|
610 | if (b.length === 0) return -1;
|
---|
611 | return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
|
---|
612 | };
|
---|
613 |
|
---|
614 | modulesByChunkGroup.sort(compareModuleLists);
|
---|
615 |
|
---|
616 | /** @type {Module[]} */
|
---|
617 | const finalModules = [];
|
---|
618 |
|
---|
619 | for (;;) {
|
---|
620 | const failedModules = new Set();
|
---|
621 | const list = modulesByChunkGroup[0].list;
|
---|
622 | if (list.length === 0) {
|
---|
623 | // done, everything empty
|
---|
624 | break;
|
---|
625 | }
|
---|
626 | /** @type {Module} */
|
---|
627 | let selectedModule = list[list.length - 1];
|
---|
628 | let hasFailed;
|
---|
629 | outer: for (;;) {
|
---|
630 | for (const { list, set } of modulesByChunkGroup) {
|
---|
631 | if (list.length === 0) continue;
|
---|
632 | const lastModule = list[list.length - 1];
|
---|
633 | if (lastModule === selectedModule) continue;
|
---|
634 | if (!set.has(selectedModule)) continue;
|
---|
635 | failedModules.add(selectedModule);
|
---|
636 | if (failedModules.has(lastModule)) {
|
---|
637 | // There is a conflict, try other alternatives
|
---|
638 | hasFailed = lastModule;
|
---|
639 | continue;
|
---|
640 | }
|
---|
641 | selectedModule = lastModule;
|
---|
642 | hasFailed = false;
|
---|
643 | continue outer; // restart
|
---|
644 | }
|
---|
645 | break;
|
---|
646 | }
|
---|
647 | if (hasFailed) {
|
---|
648 | // There is a not resolve-able conflict with the selectedModule
|
---|
649 | // TODO print better warning
|
---|
650 | compilation.warnings.push(
|
---|
651 | new WebpackError(
|
---|
652 | `chunk ${chunk.name || chunk.id}\nConflicting order between ${
|
---|
653 | /** @type {Module} */
|
---|
654 | (hasFailed).readableIdentifier(compilation.requestShortener)
|
---|
655 | } and ${selectedModule.readableIdentifier(
|
---|
656 | compilation.requestShortener
|
---|
657 | )}`
|
---|
658 | )
|
---|
659 | );
|
---|
660 | selectedModule = /** @type {Module} */ (hasFailed);
|
---|
661 | }
|
---|
662 | // Insert the selected module into the final modules list
|
---|
663 | finalModules.push(selectedModule);
|
---|
664 | // Remove the selected module from all lists
|
---|
665 | for (const { list, set } of modulesByChunkGroup) {
|
---|
666 | const lastModule = list[list.length - 1];
|
---|
667 | if (lastModule === selectedModule) list.pop();
|
---|
668 | else if (hasFailed && set.has(selectedModule)) {
|
---|
669 | const idx = list.indexOf(selectedModule);
|
---|
670 | if (idx >= 0) list.splice(idx, 1);
|
---|
671 | }
|
---|
672 | }
|
---|
673 | modulesByChunkGroup.sort(compareModuleLists);
|
---|
674 | }
|
---|
675 | return finalModules;
|
---|
676 | }
|
---|
677 |
|
---|
678 | /**
|
---|
679 | * @param {Chunk} chunk chunk
|
---|
680 | * @param {ChunkGraph} chunkGraph chunk graph
|
---|
681 | * @param {Compilation} compilation compilation
|
---|
682 | * @returns {Module[]} ordered css modules
|
---|
683 | */
|
---|
684 | getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
|
---|
685 | return [
|
---|
686 | ...this.getModulesInOrder(
|
---|
687 | chunk,
|
---|
688 | /** @type {Iterable<Module>} */
|
---|
689 | (
|
---|
690 | chunkGraph.getOrderedChunkModulesIterableBySourceType(
|
---|
691 | chunk,
|
---|
692 | "css-import",
|
---|
693 | compareModulesByIdentifier
|
---|
694 | )
|
---|
695 | ),
|
---|
696 | compilation
|
---|
697 | ),
|
---|
698 | ...this.getModulesInOrder(
|
---|
699 | chunk,
|
---|
700 | /** @type {Iterable<Module>} */
|
---|
701 | (
|
---|
702 | chunkGraph.getOrderedChunkModulesIterableBySourceType(
|
---|
703 | chunk,
|
---|
704 | "css",
|
---|
705 | compareModulesByIdentifier
|
---|
706 | )
|
---|
707 | ),
|
---|
708 | compilation
|
---|
709 | )
|
---|
710 | ];
|
---|
711 | }
|
---|
712 |
|
---|
713 | /**
|
---|
714 | * @param {CssModule} module css module
|
---|
715 | * @param {ChunkRenderContext} renderContext options object
|
---|
716 | * @param {CompilationHooks} hooks hooks
|
---|
717 | * @returns {Source} css module source
|
---|
718 | */
|
---|
719 | renderModule(module, renderContext, hooks) {
|
---|
720 | const { codeGenerationResults, chunk, undoPath } = renderContext;
|
---|
721 | const codeGenResult = codeGenerationResults.get(module, chunk.runtime);
|
---|
722 | const moduleSourceContent =
|
---|
723 | /** @type {Source} */
|
---|
724 | (
|
---|
725 | codeGenResult.sources.get("css") ||
|
---|
726 | codeGenResult.sources.get("css-import")
|
---|
727 | );
|
---|
728 | const cacheEntry = this._moduleFactoryCache.get(moduleSourceContent);
|
---|
729 |
|
---|
730 | /** @type {Inheritance} */
|
---|
731 | const inheritance = [[module.cssLayer, module.supports, module.media]];
|
---|
732 | if (module.inheritance) {
|
---|
733 | inheritance.push(...module.inheritance);
|
---|
734 | }
|
---|
735 |
|
---|
736 | let source;
|
---|
737 | if (
|
---|
738 | cacheEntry &&
|
---|
739 | cacheEntry.undoPath === undoPath &&
|
---|
740 | cacheEntry.inheritance.every(([layer, supports, media], i) => {
|
---|
741 | const item = inheritance[i];
|
---|
742 | if (Array.isArray(item)) {
|
---|
743 | return layer === item[0] && supports === item[1] && media === item[2];
|
---|
744 | }
|
---|
745 | return false;
|
---|
746 | })
|
---|
747 | ) {
|
---|
748 | source = cacheEntry.source;
|
---|
749 | } else {
|
---|
750 | const moduleSourceCode =
|
---|
751 | /** @type {string} */
|
---|
752 | (moduleSourceContent.source());
|
---|
753 | const publicPathAutoRegex = new RegExp(
|
---|
754 | CssUrlDependency.PUBLIC_PATH_AUTO,
|
---|
755 | "g"
|
---|
756 | );
|
---|
757 | /** @type {Source} */
|
---|
758 | let moduleSource = new ReplaceSource(moduleSourceContent);
|
---|
759 | let match;
|
---|
760 | while ((match = publicPathAutoRegex.exec(moduleSourceCode))) {
|
---|
761 | /** @type {ReplaceSource} */ (moduleSource).replace(
|
---|
762 | match.index,
|
---|
763 | (match.index += match[0].length - 1),
|
---|
764 | undoPath
|
---|
765 | );
|
---|
766 | }
|
---|
767 |
|
---|
768 | for (let i = 0; i < inheritance.length; i++) {
|
---|
769 | const layer = inheritance[i][0];
|
---|
770 | const supports = inheritance[i][1];
|
---|
771 | const media = inheritance[i][2];
|
---|
772 |
|
---|
773 | if (media) {
|
---|
774 | moduleSource = new ConcatSource(
|
---|
775 | `@media ${media} {\n`,
|
---|
776 | new PrefixSource("\t", moduleSource),
|
---|
777 | "}\n"
|
---|
778 | );
|
---|
779 | }
|
---|
780 |
|
---|
781 | if (supports) {
|
---|
782 | moduleSource = new ConcatSource(
|
---|
783 | `@supports (${supports}) {\n`,
|
---|
784 | new PrefixSource("\t", moduleSource),
|
---|
785 | "}\n"
|
---|
786 | );
|
---|
787 | }
|
---|
788 |
|
---|
789 | // Layer can be anonymous
|
---|
790 | if (layer !== undefined && layer !== null) {
|
---|
791 | moduleSource = new ConcatSource(
|
---|
792 | `@layer${layer ? ` ${layer}` : ""} {\n`,
|
---|
793 | new PrefixSource("\t", moduleSource),
|
---|
794 | "}\n"
|
---|
795 | );
|
---|
796 | }
|
---|
797 | }
|
---|
798 |
|
---|
799 | if (moduleSource) {
|
---|
800 | moduleSource = new ConcatSource(moduleSource, "\n");
|
---|
801 | }
|
---|
802 |
|
---|
803 | source = new CachedSource(moduleSource);
|
---|
804 | this._moduleFactoryCache.set(moduleSourceContent, {
|
---|
805 | inheritance,
|
---|
806 | undoPath,
|
---|
807 | source
|
---|
808 | });
|
---|
809 | }
|
---|
810 |
|
---|
811 | return tryRunOrWebpackError(
|
---|
812 | () => hooks.renderModulePackage.call(source, module, renderContext),
|
---|
813 | "CssModulesPlugin.getCompilationHooks().renderModulePackage"
|
---|
814 | );
|
---|
815 | }
|
---|
816 |
|
---|
817 | /**
|
---|
818 | * @param {RenderContext} renderContext the render context
|
---|
819 | * @param {CompilationHooks} hooks hooks
|
---|
820 | * @returns {Source} generated source
|
---|
821 | */
|
---|
822 | renderChunk(
|
---|
823 | {
|
---|
824 | undoPath,
|
---|
825 | chunk,
|
---|
826 | chunkGraph,
|
---|
827 | codeGenerationResults,
|
---|
828 | modules,
|
---|
829 | runtimeTemplate
|
---|
830 | },
|
---|
831 | hooks
|
---|
832 | ) {
|
---|
833 | const source = new ConcatSource();
|
---|
834 | for (const module of modules) {
|
---|
835 | try {
|
---|
836 | const moduleSource = this.renderModule(
|
---|
837 | module,
|
---|
838 | {
|
---|
839 | undoPath,
|
---|
840 | chunk,
|
---|
841 | chunkGraph,
|
---|
842 | codeGenerationResults,
|
---|
843 | runtimeTemplate
|
---|
844 | },
|
---|
845 | hooks
|
---|
846 | );
|
---|
847 | source.add(moduleSource);
|
---|
848 | } catch (err) {
|
---|
849 | /** @type {Error} */
|
---|
850 | (err).message += `\nduring rendering of css ${module.identifier()}`;
|
---|
851 | throw err;
|
---|
852 | }
|
---|
853 | }
|
---|
854 | chunk.rendered = true;
|
---|
855 | return source;
|
---|
856 | }
|
---|
857 |
|
---|
858 | /**
|
---|
859 | * @param {Chunk} chunk chunk
|
---|
860 | * @param {OutputOptions} outputOptions output options
|
---|
861 | * @returns {TemplatePath} used filename template
|
---|
862 | */
|
---|
863 | static getChunkFilenameTemplate(chunk, outputOptions) {
|
---|
864 | if (chunk.cssFilenameTemplate) {
|
---|
865 | return chunk.cssFilenameTemplate;
|
---|
866 | } else if (chunk.canBeInitial()) {
|
---|
867 | return /** @type {TemplatePath} */ (outputOptions.cssFilename);
|
---|
868 | }
|
---|
869 | return /** @type {TemplatePath} */ (outputOptions.cssChunkFilename);
|
---|
870 | }
|
---|
871 |
|
---|
872 | /**
|
---|
873 | * @param {Chunk} chunk chunk
|
---|
874 | * @param {ChunkGraph} chunkGraph chunk graph
|
---|
875 | * @returns {boolean} true, when the chunk has css
|
---|
876 | */
|
---|
877 | static chunkHasCss(chunk, chunkGraph) {
|
---|
878 | return (
|
---|
879 | Boolean(chunkGraph.getChunkModulesIterableBySourceType(chunk, "css")) ||
|
---|
880 | Boolean(
|
---|
881 | chunkGraph.getChunkModulesIterableBySourceType(chunk, "css-import")
|
---|
882 | )
|
---|
883 | );
|
---|
884 | }
|
---|
885 | }
|
---|
886 |
|
---|
887 | module.exports = CssModulesPlugin;
|
---|