source: frontend/node_modules/webpack/lib/dependencies/CssIcssExportDependency.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 28.6 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Ivan Kopeykin @vankop
4*/
5
6"use strict";
7
8const { CSS_TYPE, JAVASCRIPT_TYPE } = require("../ModuleSourceTypeConstants");
9const { interpolate } = require("../TemplatedPathPlugin");
10const WebpackError = require("../errors/WebpackError");
11const { cssExportConvention } = require("../util/conventions");
12const createHash = require("../util/createHash");
13const { makePathsRelative } = require("../util/identifier");
14const makeSerializable = require("../util/makeSerializable");
15const memoize = require("../util/memoize");
16const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
17const { updateHashFromSource } = require("../util/source");
18const CssIcssImportDependency = require("./CssIcssImportDependency");
19const NullDependency = require("./NullDependency");
20
21const getCssParser = memoize(() => require("../css/CssParser"));
22
23/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
24/** @typedef {import("../../declarations/WebpackOptions").HashFunction} HashFunction */
25/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorExportsConvention} CssGeneratorExportsConvention */
26/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorLocalIdentName} CssGeneratorLocalIdentName */
27/** @typedef {import("../css/CssModule")} CssModule */
28/** @typedef {import("../Module").BuildInfo} BuildInfo */
29/** @typedef {import("../Dependency")} Dependency */
30/** @typedef {import("../Dependency").ReferencedExports} ReferencedExports */
31/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
32/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
33/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
34/** @typedef {import("../ModuleGraph")} ModuleGraph */
35/** @typedef {import("../css/CssGenerator")} CssGenerator */
36/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
37/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
38/** @typedef {import("../util/Hash")} Hash */
39/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
40/** @typedef {import("../ChunkGraph")} ChunkGraph */
41/** @typedef {import("../Compilation").ModulePathData} ModulePathData */
42/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
43/** @typedef {import("../css/CssParser").Range} Range */
44
45/** @typedef {(name: string) => string | string[]} ExportsConventionFn */
46
47/**
48 * Returns local ident.
49 * @param {string} local css local
50 * @param {CssModule} module module
51 * @param {ChunkGraph} chunkGraph chunk graph
52 * @param {RuntimeTemplate} runtimeTemplate runtime template
53 * @returns {string} local ident
54 */
55const getLocalIdent = (local, module, chunkGraph, runtimeTemplate) => {
56 const generator = /** @type {CssGenerator} */ (module.generator);
57 const localIdentName =
58 /** @type {CssGeneratorLocalIdentName} */
59 (generator.options.localIdentName);
60 const relativeResourcePath = makePathsRelative(
61 /** @type {string} */
62 (runtimeTemplate.compilation.compiler.context),
63 /** @type {string} */
64 (module.getResource()),
65 runtimeTemplate.compilation.compiler.root
66 );
67 const { uniqueName } = runtimeTemplate.outputOptions;
68
69 let localIdentHash = "";
70
71 if (
72 typeof localIdentName === "function" ||
73 /\[(?:fullhash|hash)\]/.test(localIdentName)
74 ) {
75 const hashSalt = generator.options.localIdentHashSalt;
76 const hashDigest =
77 /** @type {string} */
78 (generator.options.localIdentHashDigest);
79 const hashDigestLength = generator.options.localIdentHashDigestLength;
80 const hashFunction =
81 /** @type {HashFunction} */
82 (generator.options.localIdentHashFunction);
83
84 const hash = createHash(hashFunction);
85
86 if (hashSalt) {
87 hash.update(hashSalt);
88 }
89
90 if (uniqueName) {
91 hash.update(uniqueName);
92 }
93
94 hash.update(relativeResourcePath);
95 hash.update(local);
96
97 localIdentHash = hash.digest(hashDigest).slice(0, hashDigestLength);
98 }
99
100 let contentHash = "";
101
102 if (
103 typeof localIdentName === "function" ||
104 /\[contenthash\]/.test(localIdentName)
105 ) {
106 const hash = createHash(runtimeTemplate.outputOptions.hashFunction);
107 const source = module.originalSource();
108
109 if (source) {
110 updateHashFromSource(hash, source);
111 }
112
113 if (module.error) {
114 hash.update(module.error.toString());
115 }
116
117 const fullContentHash = hash.digest(
118 runtimeTemplate.outputOptions.hashDigest
119 );
120
121 contentHash = nonNumericOnlyHash(
122 fullContentHash,
123 runtimeTemplate.outputOptions.hashDigestLength
124 );
125 }
126
127 let localIdent = interpolate(localIdentName, {
128 prepareId: (id) => {
129 if (typeof id !== "string") return id;
130
131 return (
132 id
133 .replace(/^([.-]|[^a-z0-9_-])+/i, "")
134 // We keep the `@` symbol because it can be used in the package name (e.g. `@company/package`), and if we replace it with `_`, a class conflict may occur.
135 // For example - `@import "@foo/package/style.module.css"` and `@import "foo/package/style.module.css"` (`foo` is a package, `package` is just a directory) will create a class conflict.
136 .replace(/[^a-z0-9@_-]+/gi, "_")
137 );
138 },
139 filename: relativeResourcePath,
140 hash: localIdentHash,
141 local,
142 contentHash,
143 chunkGraph,
144 module
145 });
146
147 // TODO move these things into interpolate
148 if (/\[local\]/.test(localIdent)) {
149 localIdent = localIdent.replace(/\[local\]/g, local);
150 }
151
152 if (/\[uniqueName\]/.test(localIdent)) {
153 localIdent = localIdent.replace(
154 /\[uniqueName\]/g,
155 /** @type {string} */ (uniqueName)
156 );
157 }
158
159 // Protect the first character from unsupported values
160 return localIdent.replace(/^((-?\d)|--)/, "_$1");
161};
162
163/** @typedef {string | [string, string] | [string, string, string]} Value */
164
165// 0 - replace, 1 - replace, 2 - append, 2 - once
166/** @typedef {0 | 1 | 2 | 3 | 4} ExportMode */
167// 0 - normal, 1 - custom css variable, 2 - grid custom ident, 3 - composes
168/** @typedef {0 | 1 | 2 | 3} ExportType */
169
170/**
171 * Computes the interpolated identifier for `(module, value, exportType)`.
172 * Module-level reference so `moduleGraph.cached` can use it as a stable
173 * computer key — repeated lookups during a build skip
174 * `cssExportConvention`, `getLocalIdent` (with its content / path hashing)
175 * and `escapeIdentifier`.
176 * @param {ModuleGraph} _moduleGraph module graph (unused, kept for `cached` signature)
177 * @param {CssModule} module css module the value resolves in
178 * @param {string} value raw value to interpolate
179 * @param {ExportType} exportType export type discriminator
180 * @param {ChunkGraph} chunkGraph chunk graph
181 * @param {RuntimeTemplate} runtimeTemplate runtime template
182 * @returns {string} interpolated identifier
183 */
184const computeInterpolatedIdentifier = (
185 _moduleGraph,
186 module,
187 value,
188 exportType,
189 chunkGraph,
190 runtimeTemplate
191) => {
192 const generator = /** @type {CssGenerator} */ (module.generator);
193 const local = cssExportConvention(
194 value,
195 /** @type {CssGeneratorExportsConvention} */
196 (generator.options.exportsConvention)
197 )[0];
198 const prefix =
199 exportType === CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
200 ? "--"
201 : "";
202 return (
203 prefix +
204 getCssParser().escapeIdentifier(
205 getLocalIdent(local, module, chunkGraph, runtimeTemplate),
206 runtimeTemplate.compilation.compiler.root
207 )
208 );
209};
210
211/**
212 * Top-level computer for `resolve`. Allocates a fresh `seen` set; recursive
213 * calls go through the un-cached inner path so cycle detection still works.
214 * @param {ModuleGraph} moduleGraph module graph
215 * @param {CssModule} module module to search
216 * @param {string} localName local name
217 * @param {string} importName imported export name
218 * @param {string | undefined} request request of the active `@value` import
219 * @param {ChunkGraph} chunkGraph chunk graph
220 * @param {RuntimeTemplate} runtimeTemplate runtime template
221 * @returns {string | undefined} resolved value or undefined
222 */
223const computeResolve = (
224 moduleGraph,
225 module,
226 localName,
227 importName,
228 request,
229 chunkGraph,
230 runtimeTemplate
231) =>
232 CssIcssExportDependency.Template._doResolve(
233 localName,
234 importName,
235 /** @type {DependencyTemplateContext} */
236 (
237 /** @type {unknown} */
238 ({ moduleGraph, module, chunkGraph, runtimeTemplate })
239 ),
240 request,
241 new Set()
242 );
243
244/**
245 * Top-level computer for `resolveReferences`. See `computeResolve`.
246 * @param {ModuleGraph} moduleGraph module graph
247 * @param {CssIcssExportDependency} dep export dependency
248 * @param {CssModule} module module that hosts `dep`
249 * @param {ChunkGraph} chunkGraph chunk graph
250 * @param {RuntimeTemplate} runtimeTemplate runtime template
251 * @returns {string[]} final references, deduplicated
252 */
253const computeResolveReferences = (
254 moduleGraph,
255 dep,
256 module,
257 chunkGraph,
258 runtimeTemplate
259) =>
260 CssIcssExportDependency.Template._doResolveReferences(
261 dep,
262 /** @type {DependencyTemplateContext} */
263 (
264 /** @type {unknown} */
265 ({ moduleGraph, module, chunkGraph, runtimeTemplate })
266 ),
267 new Set()
268 );
269
270class CssIcssExportDependency extends NullDependency {
271 /**
272 * Example of dependency:
273 *
274 * :export { LOCAL_NAME: EXPORT_NAME }
275 * @param {string} name export name
276 * @param {Value} value value or local name and import name
277 * @param {Range=} range range
278 * @param {boolean=} interpolate true when value need to be interpolated, otherwise false
279 * @param {ExportMode=} exportMode export mode
280 * @param {ExportType=} exportType export type
281 */
282 constructor(
283 name,
284 value,
285 range,
286 interpolate = false,
287 exportMode = CssIcssExportDependency.EXPORT_MODE.REPLACE,
288 exportType = CssIcssExportDependency.EXPORT_TYPE.NORMAL
289 ) {
290 super();
291 this.name = name;
292 this.value = value;
293 this.range = range;
294 this.interpolate = interpolate;
295 this.exportMode = exportMode;
296 this.exportType = exportType;
297 /** @type {undefined | string} */
298 this._hashUpdate = undefined;
299 /** @type {undefined | string[]} */
300 this._conventionNames = undefined;
301 /** @type {undefined | string[]} */
302 this._valueConventionNames = undefined;
303 }
304
305 get type() {
306 return "css :export";
307 }
308
309 /**
310 * Gets exports convention names.
311 * @param {string} name export name
312 * @param {CssGeneratorExportsConvention} convention convention of the export name
313 * @returns {string[]} convention results
314 */
315 getExportsConventionNames(name, convention) {
316 if (this._conventionNames) {
317 return this._conventionNames;
318 }
319 this._conventionNames = cssExportConvention(name, convention);
320 return this._conventionNames;
321 }
322
323 /**
324 * Memoized `cssExportConvention(this.value, convention)`. Used by every
325 * code path that needs the convention-derived aliases of the composed /
326 * referenced class: `getReferencedExports`, `getWarnings`, and the template's
327 * `getIdentifier`. Caller guarantees `typeof this.value === "string"` —
328 * the array form (cross-module references) is resolved separately.
329 * @param {CssGeneratorExportsConvention} convention convention of the export name
330 * @returns {string[]} convention results
331 */
332 getValueConventionNames(convention) {
333 if (this._valueConventionNames) {
334 return this._valueConventionNames;
335 }
336 this._valueConventionNames = cssExportConvention(
337 /** @type {string} */ (this.value),
338 convention
339 );
340 return this._valueConventionNames;
341 }
342
343 /**
344 * Returns list of exports referenced by this dependency
345 * @param {ModuleGraph} moduleGraph module graph
346 * @param {RuntimeSpec} runtime the runtime for which the module is analysed
347 * @returns {ReferencedExports} referenced exports
348 */
349 getReferencedExports(moduleGraph, runtime) {
350 if (
351 this.exportMode === CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE &&
352 typeof this.value === "string"
353 ) {
354 // `composes: foo;` — the composed class (`this.value`) is the one
355 // referenced, not the class doing the composing (`this.name`). Apply
356 // the generator's `exportsConvention` so the export names produced
357 // by the convention are what the optimizer sees.
358 const module =
359 /** @type {CssModule | undefined} */
360 (moduleGraph.getParentModule(this));
361 if (!module) return super.getReferencedExports(moduleGraph, runtime);
362 const generator = /** @type {CssGenerator} */ (module.generator);
363 const names = this.getValueConventionNames(
364 /** @type {CssGeneratorExportsConvention} */
365 (generator.options.exportsConvention)
366 );
367 return names.map((name) => ({
368 name: [name],
369 canMangle: true
370 }));
371 }
372
373 return super.getReferencedExports(moduleGraph, runtime);
374 }
375
376 /**
377 * Returns the exported names
378 * @param {ModuleGraph} moduleGraph module graph
379 * @returns {ExportsSpec | undefined} export names
380 */
381 getExports(moduleGraph) {
382 if (
383 this.exportMode === CssIcssExportDependency.EXPORT_MODE.NONE ||
384 this.exportMode === CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE
385 ) {
386 return;
387 }
388
389 const module = /** @type {CssModule} */ (moduleGraph.getParentModule(this));
390 const generator = /** @type {CssGenerator} */ (module.generator);
391 const names = this.getExportsConventionNames(
392 this.name,
393 /** @type {CssGeneratorExportsConvention} */
394 (generator.options.exportsConvention)
395 );
396
397 return {
398 exports: [...names].map((name) => ({
399 name,
400 canMangle: true
401 })),
402 dependencies: undefined
403 };
404 }
405
406 /**
407 * Returns warnings.
408 * @param {ModuleGraph} moduleGraph module graph
409 * @returns {WebpackError[] | null | undefined} warnings
410 */
411 getWarnings(moduleGraph) {
412 if (
413 this.exportMode === CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE &&
414 typeof this.value === "string"
415 ) {
416 const module =
417 /** @type {CssModule | undefined} */
418 (moduleGraph.getParentModule(this));
419
420 if (!module) return null;
421
422 // `ExportsInfo` only stores names produced by `exportsConvention`,
423 // so a raw `isExportProvided(this.value)` check is a false-positive
424 // for `camel-case-only` / `dashes-only` (and any custom function
425 // that drops the original spelling). Treat the composed class as
426 // provided if *any* of its convention-produced aliases is provided.
427 const generator = /** @type {CssGenerator} */ (module.generator);
428 const exportsInfo = moduleGraph.getExportsInfo(module);
429 const names = this.getValueConventionNames(
430 /** @type {CssGeneratorExportsConvention} */
431 (generator.options.exportsConvention)
432 );
433 const isProvided = names.some((name) =>
434 exportsInfo.isExportProvided(name)
435 );
436
437 if (!isProvided) {
438 const error = new WebpackError(
439 `Self-referencing name "${this.value}" not found`
440 );
441 error.module = module;
442
443 return [error];
444 }
445 }
446
447 return null;
448 }
449
450 /**
451 * Updates the hash with the data contributed by this instance.
452 * @param {Hash} hash hash to be updated
453 * @param {UpdateHashContext} context context
454 * @returns {void}
455 */
456 updateHash(hash, { chunkGraph }) {
457 if (this._hashUpdate === undefined) {
458 const module =
459 /** @type {CssModule} */
460 (chunkGraph.moduleGraph.getParentModule(this));
461 const generator = /** @type {CssGenerator} */ (module.generator);
462 const names = this.getExportsConventionNames(
463 this.name,
464 /** @type {CssGeneratorExportsConvention} */
465 (generator.options.exportsConvention)
466 );
467 // Include all instance state that affects the emitted output —
468 // `name`, `value`, `range`, `interpolate`, `exportMode`,
469 // `exportType` — so changes like switching `composes: foo` →
470 // `composes: bar` or `ONCE` → `APPEND` invalidate caches.
471 this._hashUpdate = `exportsConvention|${JSON.stringify(names)}|localIdentName|${JSON.stringify(generator.options.localIdentName)}|value|${JSON.stringify(this.value)}|range|${JSON.stringify(this.range)}|interpolate|${this.interpolate}|exportMode|${this.exportMode}|exportType|${this.exportType}`;
472 }
473 hash.update(this._hashUpdate);
474 }
475
476 /**
477 * Serializes this instance into the provided serializer context.
478 * @param {ObjectSerializerContext} context context
479 */
480 serialize(context) {
481 const { write } = context;
482 write(this.name);
483 write(this.value);
484 write(this.range);
485 write(this.interpolate);
486 write(this.exportMode);
487 write(this.exportType);
488 super.serialize(context);
489 }
490
491 /**
492 * Restores this instance from the provided deserializer context.
493 * @param {ObjectDeserializerContext} context context
494 */
495 deserialize(context) {
496 const { read } = context;
497 this.name = read();
498 this.value = read();
499 this.range = read();
500 this.interpolate = read();
501 this.exportMode = read();
502 this.exportType = read();
503 super.deserialize(context);
504 }
505}
506
507CssIcssExportDependency.Template = class CssIcssExportDependencyTemplate extends (
508 NullDependency.Template
509) {
510 /**
511 * Returns found reference. The top-level call (no `seen` argument) is
512 * memoized via `moduleGraph.cached` keyed by `(module, localName,
513 * importName, request, chunkGraph, runtimeTemplate)`. Recursive callers
514 * pass `seen` and skip the cache so the cycle guard is preserved — only
515 * completed top-level resolutions land in the cache.
516 * @param {string} localName local name
517 * @param {string} importName import name
518 * @param {DependencyTemplateContext} templateContext the context object
519 * @param {string | undefined} request user request of the `@value` import that was active when the reference was parsed — used to disambiguate when the same local name is imported from multiple modules
520 * @param {Set<CssIcssExportDependency>=} seen seen to prevent cyclical problems
521 * @returns {string | undefined} found reference
522 */
523 static resolve(localName, importName, templateContext, request, seen) {
524 if (seen !== undefined) {
525 return CssIcssExportDependencyTemplate._doResolve(
526 localName,
527 importName,
528 templateContext,
529 request,
530 seen
531 );
532 }
533 const { moduleGraph, module, chunkGraph, runtimeTemplate } =
534 templateContext;
535 return moduleGraph.cached(
536 computeResolve,
537 /** @type {CssModule} */ (module),
538 localName,
539 importName,
540 request,
541 chunkGraph,
542 runtimeTemplate
543 );
544 }
545
546 /**
547 * Inner recursive worker for `resolve`. Not memoized — see `resolve`.
548 * @param {string} localName local name
549 * @param {string} importName import name
550 * @param {DependencyTemplateContext} templateContext the context object
551 * @param {string | undefined} request user request
552 * @param {Set<CssIcssExportDependency>} seen seen to prevent cyclical problems
553 * @returns {string | undefined} found reference
554 */
555 static _doResolve(localName, importName, templateContext, request, seen) {
556 const { moduleGraph } = templateContext;
557 const importDep =
558 /** @type {CssIcssImportDependency | undefined} */
559 (
560 CssIcssExportDependencyTemplate.findImportDep(
561 templateContext.module.dependencies,
562 localName,
563 request
564 )
565 );
566 if (!importDep) return undefined;
567
568 const module = /** @type {CssModule} */ (moduleGraph.getModule(importDep));
569 if (!module) return undefined;
570
571 const exportDep =
572 /** @type {CssIcssExportDependency} */
573 (
574 module.dependencies.find(
575 (d) => d instanceof CssIcssExportDependency && d.name === importName
576 )
577 );
578
579 if (!exportDep) return undefined;
580
581 if (seen.has(exportDep)) return undefined;
582 seen.add(exportDep);
583
584 const { value, interpolate } = exportDep;
585
586 if (Array.isArray(value)) {
587 return CssIcssExportDependencyTemplate._doResolve(
588 value[0],
589 value[1],
590 {
591 ...templateContext,
592 module
593 },
594 value[2],
595 seen
596 );
597 }
598
599 if (interpolate) {
600 return CssIcssExportDependency.Template.getIdentifier(value, exportDep, {
601 ...templateContext,
602 module
603 });
604 }
605
606 return value;
607 }
608
609 /**
610 * Finds the active `CssIcssImportDependency` for a given local name. When a
611 * `request` is provided the lookup also requires the import dependency's
612 * `request` to match — this lets references that appear between two
613 * `@value foo from "..."` declarations resolve through the import that was
614 * in scope at the reference site, rather than always picking the first.
615 * @param {Iterable<Dependency>} dependencies module dependencies to search
616 * @param {string} localName local name
617 * @param {string=} request user request of the `@value` import to match
618 * @returns {CssIcssImportDependency | undefined} matching import dep, if any
619 */
620 static findImportDep(dependencies, localName, request) {
621 /** @type {CssIcssImportDependency | undefined} */
622 let firstMatch;
623 for (const d of dependencies) {
624 if (d instanceof CssIcssImportDependency && d.localName === localName) {
625 if (request === undefined) return d;
626 if (d.request === request) return d;
627 if (firstMatch === undefined) firstMatch = d;
628 }
629 }
630 return firstMatch;
631 }
632
633 /**
634 * Resolves references. The top-level call (no `seen` argument) is
635 * memoized via `moduleGraph.cached`; recursive callers pass `seen` and
636 * bypass the cache to keep the cycle guard intact.
637 * @param {CssIcssExportDependency} dep value
638 * @param {DependencyTemplateContext} templateContext template context
639 * @param {Set<CssIcssExportDependency>=} seen to prevent cyclical problems
640 * @returns {string[]} final names
641 */
642 static resolveReferences(dep, templateContext, seen) {
643 if (seen !== undefined) {
644 return CssIcssExportDependencyTemplate._doResolveReferences(
645 dep,
646 templateContext,
647 seen
648 );
649 }
650 const { moduleGraph, module, chunkGraph, runtimeTemplate } =
651 templateContext;
652 return moduleGraph.cached(
653 computeResolveReferences,
654 dep,
655 /** @type {CssModule} */ (module),
656 chunkGraph,
657 runtimeTemplate
658 );
659 }
660
661 /**
662 * Inner recursive worker for `resolveReferences`. Not memoized.
663 * @param {CssIcssExportDependency} dep value
664 * @param {DependencyTemplateContext} templateContext template context
665 * @param {Set<CssIcssExportDependency>} seen to prevent cyclical problems
666 * @returns {string[]} final names
667 */
668 static _doResolveReferences(dep, templateContext, seen) {
669 /** @type {string[]} */
670 const references = [];
671
672 if (seen.has(dep)) return references;
673 seen.add(dep);
674
675 if (Array.isArray(dep.value)) {
676 const importDep =
677 /** @type {CssIcssImportDependency | undefined} */
678 (
679 CssIcssExportDependencyTemplate.findImportDep(
680 templateContext.module.dependencies,
681 dep.value[0],
682 dep.value[2]
683 )
684 );
685 if (!importDep) return references;
686
687 const module =
688 /** @type {CssModule} */
689 (templateContext.moduleGraph.getModule(importDep));
690 if (!module) return references;
691
692 for (const d of module.dependencies) {
693 if (d instanceof CssIcssExportDependency && d.name === dep.value[1]) {
694 if (Array.isArray(d.value)) {
695 const deepReferences =
696 CssIcssExportDependencyTemplate._doResolveReferences(
697 d,
698 {
699 ...templateContext,
700 module
701 },
702 seen
703 );
704
705 references.push(...deepReferences);
706 } else {
707 references.push(
708 CssIcssExportDependencyTemplate.getIdentifier(d.value, d, {
709 ...templateContext,
710 module
711 })
712 );
713 }
714 }
715 }
716 } else {
717 // Adding basic class
718 references.push(
719 CssIcssExportDependencyTemplate.getIdentifier(
720 dep.value,
721 dep,
722 templateContext
723 )
724 );
725
726 for (const d of templateContext.module.dependencies) {
727 if (
728 d instanceof CssIcssExportDependency &&
729 d.exportType === CssIcssExportDependency.EXPORT_TYPE.COMPOSES &&
730 d.name === dep.value
731 ) {
732 if (Array.isArray(d.value)) {
733 const deepReferences =
734 CssIcssExportDependencyTemplate._doResolveReferences(
735 d,
736 templateContext,
737 seen
738 );
739
740 references.push(...deepReferences);
741 } else {
742 references.push(
743 CssIcssExportDependencyTemplate.getIdentifier(
744 d.value,
745 d,
746 templateContext
747 )
748 );
749 }
750 }
751 }
752 }
753
754 return [...new Set(references)];
755 }
756
757 /**
758 * Returns identifier. When the dep opts into interpolation the full
759 * computation is memoized via `moduleGraph.cached` keyed by
760 * `(module, value, exportType, chunkGraph, runtimeTemplate)` so
761 * `cssExportConvention` / `getLocalIdent` / `escapeIdentifier` are not
762 * re-run for the same identifier during code generation.
763 * @param {string} value value to identifier
764 * @param {Dependency} dependency the dependency for which the template should be applied
765 * @param {DependencyTemplateContext} templateContext the context object
766 * @returns {string} identifier
767 */
768 static getIdentifier(value, dependency, templateContext) {
769 const dep = /** @type {CssIcssExportDependency} */ (dependency);
770 if (!dep.interpolate) return value;
771 const { moduleGraph, module, chunkGraph, runtimeTemplate } =
772 templateContext;
773 return moduleGraph.cached(
774 computeInterpolatedIdentifier,
775 /** @type {CssModule} */ (module),
776 value,
777 dep.exportType,
778 chunkGraph,
779 runtimeTemplate
780 );
781 }
782
783 /**
784 * Applies the plugin by registering its hooks on the compiler.
785 * @param {Dependency} dependency the dependency for which the template should be applied
786 * @param {ReplaceSource} source the current replace source which can be modified
787 * @param {DependencyTemplateContext} templateContext the context object
788 * @returns {void}
789 */
790 apply(dependency, source, templateContext) {
791 const dep = /** @type {CssIcssExportDependency} */ (dependency);
792 if (!dep.range && templateContext.type !== JAVASCRIPT_TYPE) return;
793 const { module: m, moduleGraph, runtime, cssData } = templateContext;
794 const module = /** @type {CssModule} */ (m);
795 const generator = /** @type {CssGenerator} */ (module.generator);
796 const isReference = Array.isArray(dep.value);
797
798 /** @type {string} */
799 let value;
800
801 // The `composes` has more complex logic for collecting all the classes
802 if (
803 dep.exportType === CssIcssExportDependency.EXPORT_TYPE.COMPOSES &&
804 templateContext.type === JAVASCRIPT_TYPE
805 ) {
806 value = CssIcssExportDependencyTemplate.resolveReferences(
807 dep,
808 templateContext
809 ).join(" ");
810 } else if (isReference) {
811 const resolved = CssIcssExportDependencyTemplate.resolve(
812 dep.value[0],
813 dep.value[1],
814 templateContext,
815 dep.value[2]
816 );
817
818 // Fallback to the local name if not resolved
819 value = resolved || dep.value[0];
820 } else {
821 value = CssIcssExportDependencyTemplate.getIdentifier(
822 /** @type {string} */ (dep.value),
823 dep,
824 templateContext
825 );
826 }
827
828 if (
829 dep.exportType ===
830 CssIcssExportDependency.EXPORT_TYPE.GRID_CUSTOM_IDENTIFIER
831 ) {
832 value += `-${dep.name}`;
833 }
834
835 if (
836 templateContext.type === JAVASCRIPT_TYPE &&
837 dep.exportMode !== CssIcssExportDependency.EXPORT_MODE.NONE
838 ) {
839 const names = dep.getExportsConventionNames(
840 dep.name,
841 /** @type {CssGeneratorExportsConvention} */
842 (generator.options.exportsConvention)
843 );
844 const usedNames =
845 /** @type {string[]} */
846 (
847 names
848 .map((name) =>
849 moduleGraph.getExportInfo(module, name).getUsedName(name, runtime)
850 )
851 .filter(Boolean)
852 );
853 const allNames = new Set([...usedNames, ...names]);
854 const unescaped = getCssParser().unescapeIdentifier(
855 value,
856 templateContext.runtimeTemplate.compilation.compiler.root
857 );
858
859 const depLocStart =
860 dep.loc &&
861 /** @type {{ start?: { line: number, column: number } }} */ (dep.loc)
862 .start;
863 for (const used of allNames) {
864 if (dep.exportMode === CssIcssExportDependency.EXPORT_MODE.ONCE) {
865 if (cssData.exports.has(used)) return;
866 cssData.exports.set(used, unescaped);
867 if (
868 depLocStart &&
869 cssData.exportLocs &&
870 !cssData.exportLocs.has(used)
871 ) {
872 cssData.exportLocs.set(used, {
873 line: depLocStart.line,
874 column: depLocStart.column
875 });
876 }
877 } else {
878 const originalValue =
879 dep.exportMode === CssIcssExportDependency.EXPORT_MODE.REPLACE
880 ? undefined
881 : cssData.exports.get(used);
882
883 cssData.exports.set(
884 used,
885 `${originalValue ? `${originalValue}${unescaped ? " " : ""}` : ""}${unescaped}`
886 );
887 // Record the source location once per export (use the first
888 // occurrence — for APPEND/REPLACE this corresponds to the
889 // first selector seen, which is a reasonable anchor).
890 if (
891 depLocStart &&
892 cssData.exportLocs &&
893 !cssData.exportLocs.has(used)
894 ) {
895 cssData.exportLocs.set(used, {
896 line: depLocStart.line,
897 column: depLocStart.column
898 });
899 }
900 }
901 }
902 } else if (
903 dep.range &&
904 templateContext.type === CSS_TYPE &&
905 dep.exportMode !== CssIcssExportDependency.EXPORT_MODE.APPEND &&
906 dep.exportMode !== CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE
907 ) {
908 source.replace(dep.range[0], dep.range[1] - 1, value);
909 }
910 }
911};
912
913/** @type {Record<"NONE" | "REPLACE" | "APPEND" | "ONCE" | "SELF_REFERENCE", ExportMode>} */
914CssIcssExportDependency.EXPORT_MODE = {
915 NONE: 0,
916 REPLACE: 1,
917 APPEND: 2,
918 ONCE: 3,
919 SELF_REFERENCE: 4
920};
921
922/** @type {Record<"NORMAL" | "CUSTOM_VARIABLE" | "GRID_CUSTOM_IDENTIFIER" | "COMPOSES", ExportType>} */
923CssIcssExportDependency.EXPORT_TYPE = {
924 NORMAL: 0,
925 CUSTOM_VARIABLE: 1,
926 GRID_CUSTOM_IDENTIFIER: 2,
927 COMPOSES: 3
928};
929
930makeSerializable(
931 CssIcssExportDependency,
932 "webpack/lib/dependencies/CssIcssExportDependency"
933);
934
935module.exports = CssIcssExportDependency;
Note: See TracBrowser for help on using the repository browser.