webpack/lib/css/CssModulesPlugin.js

925 lines
27 KiB
JavaScript
Raw Normal View History

2021-11-30 19:55:51 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
2024-08-12 22:12:05 +08:00
const { SyncWaterfallHook, SyncHook } = require("tapable");
2023-05-10 19:05:00 +08:00
const {
ConcatSource,
PrefixSource,
2024-03-16 19:39:46 +08:00
ReplaceSource,
CachedSource
2023-05-10 19:05:00 +08:00
} = require("webpack-sources");
2024-08-12 22:12:05 +08:00
const Compilation = require("../Compilation");
2023-04-25 21:59:17 +08:00
const CssModule = require("../CssModule");
2024-08-12 22:12:05 +08:00
const { tryRunOrWebpackError } = require("../HookWebpackError");
2021-11-30 19:55:51 +08:00
const HotUpdateChunk = require("../HotUpdateChunk");
2023-04-26 06:19:06 +08:00
const {
CSS_MODULE_TYPE,
CSS_MODULE_TYPE_GLOBAL,
2022-12-16 20:56:14 +08:00
CSS_MODULE_TYPE_MODULE,
CSS_MODULE_TYPE_AUTO
2023-04-26 06:19:06 +08:00
} = require("../ModuleTypeConstants");
2021-11-30 19:55:51 +08:00
const RuntimeGlobals = require("../RuntimeGlobals");
const SelfModuleFactory = require("../SelfModuleFactory");
2023-04-29 03:26:27 +08:00
const WebpackError = require("../WebpackError");
2021-12-14 23:02:26 +08:00
const CssExportDependency = require("../dependencies/CssExportDependency");
2021-12-01 21:15:19 +08:00
const CssImportDependency = require("../dependencies/CssImportDependency");
2021-12-15 15:34:31 +08:00
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
2021-11-30 20:46:42 +08:00
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
2021-12-18 01:10:00 +08:00
const { compareModulesByIdentifier } = require("../util/comparators");
2021-11-30 19:55:51 +08:00
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
2023-05-10 19:05:00 +08:00
const { getUndoPath } = require("../util/identifier");
2021-12-03 23:23:09 +08:00
const memoize = require("../util/memoize");
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
const CssExportsGenerator = require("./CssExportsGenerator");
2021-11-30 19:55:51 +08:00
const CssGenerator = require("./CssGenerator");
const CssParser = require("./CssParser");
/** @typedef {import("webpack-sources").Source} Source */
2024-08-08 02:59:26 +08:00
/** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputOptions */
2021-11-30 19:55:51 +08:00
/** @typedef {import("../Chunk")} Chunk */
2023-04-29 02:50:41 +08:00
/** @typedef {import("../ChunkGraph")} ChunkGraph */
2023-04-29 03:26:27 +08:00
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
2024-08-12 22:12:05 +08:00
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
2021-11-30 19:55:51 +08:00
/** @typedef {import("../Compiler")} Compiler */
2024-03-16 19:39:46 +08:00
/** @typedef {import("../CssModule").Inheritance} Inheritance */
2024-06-10 22:38:22 +08:00
/** @typedef {import("../DependencyTemplate").CssExportsData} CssExportsData */
2021-11-30 19:55:51 +08:00
/** @typedef {import("../Module")} Module */
2024-08-12 22:12:05 +08:00
/** @typedef {import("../Template").RuntimeTemplate} RuntimeTemplate */
2024-08-09 01:03:17 +08:00
/** @typedef {import("../TemplatedPathPlugin").TemplatePath} TemplatePath */
2024-08-12 22:12:05 +08:00
/** @typedef {import("../util/Hash")} Hash */
2023-04-29 03:26:27 +08:00
/** @typedef {import("../util/memoize")} Memoize */
2021-11-30 19:55:51 +08:00
2024-08-12 22:12:05 +08:00
/**
* @typedef {object} ChunkRenderContext
* @property {RuntimeTemplate} runtimeTemplate runtime template
*/
/**
* @typedef {object} CompilationHooks
* @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
* @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
*/
2021-12-03 23:23:09 +08:00
const getCssLoadingRuntimeModule = memoize(() =>
require("./CssLoadingRuntimeModule")
);
2023-06-04 01:52:25 +08:00
/**
* @param {string} name name
* @returns {{oneOf: [{$ref: string}], definitions: *}} schema
*/
2021-11-30 19:55:51 +08:00
const getSchema = name => {
const { definitions } = require("../../schemas/WebpackOptions.json");
return {
definitions,
oneOf: [{ $ref: `#/definitions/${name}` }]
};
};
2024-01-12 15:47:31 +08:00
const generatorValidationOptions = {
name: "Css Modules Plugin",
baseDataPath: "generator"
};
const validateGeneratorOptions = {
css: createSchemaValidation(
require("../../schemas/plugins/css/CssGeneratorOptions.check.js"),
() => getSchema("CssGeneratorOptions"),
generatorValidationOptions
),
"css/auto": createSchemaValidation(
require("../../schemas/plugins/css/CssAutoGeneratorOptions.check.js"),
() => getSchema("CssAutoGeneratorOptions"),
generatorValidationOptions
),
"css/module": createSchemaValidation(
require("../../schemas/plugins/css/CssModuleGeneratorOptions.check.js"),
() => getSchema("CssModuleGeneratorOptions"),
generatorValidationOptions
),
"css/global": createSchemaValidation(
require("../../schemas/plugins/css/CssGlobalGeneratorOptions.check.js"),
() => getSchema("CssGlobalGeneratorOptions"),
generatorValidationOptions
)
};
2024-01-13 18:23:30 +08:00
const parserValidationOptions = {
name: "Css Modules Plugin",
baseDataPath: "parser"
};
const validateParserOptions = {
css: createSchemaValidation(
require("../../schemas/plugins/css/CssParserOptions.check.js"),
() => getSchema("CssParserOptions"),
parserValidationOptions
),
"css/auto": createSchemaValidation(
require("../../schemas/plugins/css/CssAutoParserOptions.check.js"),
() => getSchema("CssAutoParserOptions"),
parserValidationOptions
),
"css/module": createSchemaValidation(
require("../../schemas/plugins/css/CssModuleParserOptions.check.js"),
() => getSchema("CssModuleParserOptions"),
parserValidationOptions
),
"css/global": createSchemaValidation(
require("../../schemas/plugins/css/CssGlobalParserOptions.check.js"),
() => getSchema("CssGlobalParserOptions"),
parserValidationOptions
)
};
2021-11-30 19:55:51 +08:00
2024-08-12 22:12:05 +08:00
/** @type {WeakMap<Compilation, CompilationHooks>} */
const compilationHooksMap = new WeakMap();
2023-04-29 03:26:27 +08:00
/**
* @param {string} str string
* @param {boolean=} omitOptionalUnderscore if true, optional underscore is not added
* @returns {string} escaped string
*/
const escapeCss = (str, omitOptionalUnderscore) => {
const escaped = `${str}`.replace(
// cspell:word uffff
2024-08-02 02:36:27 +08:00
/[^a-zA-Z0-9_\u0081-\uFFFF-]/g,
s => `\\${s}`
);
return !omitOptionalUnderscore && /^(?!--)[0-9_-]/.test(escaped)
? `_${escaped}`
: escaped;
2021-11-30 19:55:51 +08:00
};
2024-03-01 17:15:42 +08:00
/**
* @param {string} str string
* @returns {string} encoded string
*/
2024-07-31 11:50:02 +08:00
const lzwEncode = str => {
2024-03-01 17:15:42 +08:00
/** @type {Map<string, string>} */
const map = new Map();
let encoded = "";
let phrase = str[0];
let code = 256;
2024-08-02 02:36:27 +08:00
const maxCode = "\uFFFF".charCodeAt(0);
2024-03-01 17:15:42 +08:00
for (let i = 1; i < str.length; i++) {
const c = str[i];
if (map.has(phrase + c)) {
phrase += c;
} else {
encoded += phrase.length > 1 ? map.get(phrase) : phrase;
map.set(phrase + c, String.fromCharCode(code));
phrase = c;
if (++code > maxCode) {
code = 256;
map.clear();
}
}
}
encoded += phrase.length > 1 ? map.get(phrase) : phrase;
return encoded;
};
2024-08-12 22:12:05 +08:00
const PLUGIN_NAME = "CssModulesPlugin";
2021-11-30 19:55:51 +08:00
class CssModulesPlugin {
2024-08-12 22:12:05 +08:00
/**
* @param {Compilation} compilation the compilation
* @returns {CompilationHooks} the attached hooks
*/
static getCompilationHooks(compilation) {
if (!(compilation instanceof Compilation)) {
throw new TypeError(
"The 'compilation' argument must be an instance of Compilation"
);
}
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
hooks = {
renderModulePackage: new SyncWaterfallHook([
"source",
"module",
"renderContext"
]),
chunkHash: new SyncHook(["chunk", "hash", "context"])
};
compilationHooksMap.set(compilation, hooks);
}
return hooks;
}
2024-03-16 19:39:46 +08:00
constructor() {
/** @type {WeakMap<Source, { undoPath: string, inheritance: Inheritance, source: CachedSource }>} */
this._moduleCache = new WeakMap();
}
2021-11-30 19:55:51 +08:00
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
2024-08-12 22:12:05 +08:00
PLUGIN_NAME,
2021-11-30 19:55:51 +08:00
(compilation, { normalModuleFactory }) => {
2024-08-12 22:12:05 +08:00
const hooks = CssModulesPlugin.getCompilationHooks(compilation);
const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
2021-11-30 20:46:42 +08:00
compilation.dependencyFactories.set(
CssUrlDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssUrlDependency,
new CssUrlDependency.Template()
);
2021-12-15 15:34:31 +08:00
compilation.dependencyTemplates.set(
CssLocalIdentifierDependency,
new CssLocalIdentifierDependency.Template()
);
compilation.dependencyFactories.set(
CssSelfLocalIdentifierDependency,
selfFactory
);
compilation.dependencyTemplates.set(
CssSelfLocalIdentifierDependency,
new CssSelfLocalIdentifierDependency.Template()
);
2021-12-14 23:02:26 +08:00
compilation.dependencyTemplates.set(
CssExportDependency,
new CssExportDependency.Template()
);
2021-12-01 20:27:00 +08:00
compilation.dependencyFactories.set(
CssImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssImportDependency,
new CssImportDependency.Template()
);
2021-11-30 20:46:42 +08:00
compilation.dependencyTemplates.set(
StaticExportsDependency,
new StaticExportsDependency.Template()
);
2023-04-26 06:19:06 +08:00
for (const type of [
CSS_MODULE_TYPE,
CSS_MODULE_TYPE_GLOBAL,
2022-12-16 20:56:14 +08:00
CSS_MODULE_TYPE_MODULE,
CSS_MODULE_TYPE_AUTO
2023-04-26 06:19:06 +08:00
]) {
normalModuleFactory.hooks.createParser
.for(type)
2024-08-12 22:12:05 +08:00
.tap(PLUGIN_NAME, parserOptions => {
2024-01-13 18:23:30 +08:00
validateParserOptions[type](parserOptions);
const { namedExports } = parserOptions;
switch (type) {
case CSS_MODULE_TYPE:
2024-01-13 18:23:30 +08:00
return new CssParser({
namedExports
});
case CSS_MODULE_TYPE_GLOBAL:
return new CssParser({
defaultMode: "global",
2024-01-13 18:23:30 +08:00
namedExports
});
2023-04-26 06:19:06 +08:00
case CSS_MODULE_TYPE_MODULE:
return new CssParser({
2024-01-13 18:23:30 +08:00
defaultMode: "local",
namedExports
});
case CSS_MODULE_TYPE_AUTO:
return new CssParser({
defaultMode: "auto",
namedExports
});
}
2021-12-15 15:34:31 +08:00
});
normalModuleFactory.hooks.createGenerator
.for(type)
2024-08-12 22:12:05 +08:00
.tap(PLUGIN_NAME, generatorOptions => {
2024-01-12 15:47:31 +08:00
validateGeneratorOptions[type](generatorOptions);
return generatorOptions.exportsOnly
2024-02-22 19:58:59 +08:00
? new CssExportsGenerator(
generatorOptions.exportsConvention,
generatorOptions.localIdentName,
generatorOptions.esModule
2024-07-31 05:43:19 +08:00
)
2024-02-22 19:58:59 +08:00
: new CssGenerator(
generatorOptions.exportsConvention,
generatorOptions.localIdentName,
generatorOptions.esModule
2024-07-31 05:43:19 +08:00
);
2021-12-15 15:34:31 +08:00
});
2023-04-25 21:59:17 +08:00
normalModuleFactory.hooks.createModuleClass
.for(type)
2024-08-12 22:12:05 +08:00
.tap(PLUGIN_NAME, (createData, resolveData) => {
2023-04-25 21:59:17 +08:00
if (resolveData.dependencies.length > 0) {
2023-04-26 06:19:06 +08:00
// When CSS is imported from CSS there is only one dependency
2023-04-25 21:59:17 +08:00
const dependency = resolveData.dependencies[0];
2023-05-01 22:46:47 +08:00
if (dependency instanceof CssImportDependency) {
const parent =
/** @type {CssModule} */
(compilation.moduleGraph.getParentModule(dependency));
if (parent instanceof CssModule) {
2023-05-02 05:33:09 +08:00
/** @type {import("../CssModule").Inheritance | undefined} */
let inheritance;
if (
(parent.cssLayer !== null &&
parent.cssLayer !== undefined) ||
parent.supports ||
parent.media
) {
if (!inheritance) {
inheritance = [];
}
inheritance.push([
parent.cssLayer,
parent.supports,
parent.media
]);
}
2023-05-01 22:46:47 +08:00
if (parent.inheritance) {
2023-05-02 05:33:09 +08:00
if (!inheritance) {
inheritance = [];
}
2023-05-01 22:46:47 +08:00
inheritance.push(...parent.inheritance);
}
return new CssModule({
...createData,
cssLayer: dependency.layer,
supports: dependency.supports,
media: dependency.media,
inheritance
});
}
return new CssModule({
...createData,
cssLayer: dependency.layer,
supports: dependency.supports,
media: dependency.media
});
}
2023-04-25 21:59:17 +08:00
}
return new CssModule(createData);
});
}
const orderedCssModulesPerChunk = new WeakMap();
compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
const { chunkGraph } = compilation;
for (const chunk of compilation.chunks) {
if (CssModulesPlugin.chunkHasCss(chunk, chunkGraph)) {
orderedCssModulesPerChunk.set(
chunk,
this.getOrderedChunkCssModules(chunk, chunkGraph, compilation)
);
}
}
});
2024-08-12 22:12:05 +08:00
compilation.hooks.chunkHash.tap(
"CssModulesPlugin",
(chunk, hash, context) => {
hooks.chunkHash.call(chunk, hash, context);
}
);
compilation.hooks.contentHash.tap("CssModulesPlugin", chunk => {
2021-11-30 19:55:51 +08:00
const {
chunkGraph,
2024-08-12 22:12:05 +08:00
codeGenerationResults,
moduleGraph,
runtimeTemplate,
2021-11-30 19:55:51 +08:00
outputOptions: {
hashSalt,
hashDigest,
hashDigestLength,
hashFunction
}
} = compilation;
const hash = createHash(hashFunction);
if (hashSalt) hash.update(hashSalt);
2024-08-12 22:12:05 +08:00
hooks.chunkHash.call(chunk, hash, {
chunkGraph,
codeGenerationResults,
moduleGraph,
runtimeTemplate
});
const modules = orderedCssModulesPerChunk.get(chunk);
if (modules) {
for (const module of modules) {
hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
}
2021-11-30 19:55:51 +08:00
}
const digest = /** @type {string} */ (hash.digest(hashDigest));
chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength);
2021-11-30 19:55:51 +08:00
});
2024-08-12 22:12:05 +08:00
compilation.hooks.renderManifest.tap(PLUGIN_NAME, (result, options) => {
2021-11-30 19:55:51 +08:00
const { chunkGraph } = compilation;
2024-08-12 22:12:05 +08:00
const { hash, chunk, codeGenerationResults, runtimeTemplate } =
options;
2021-11-30 19:55:51 +08:00
if (chunk instanceof HotUpdateChunk) return result;
2023-04-29 03:26:27 +08:00
/** @type {CssModule[] | undefined} */
const modules = orderedCssModulesPerChunk.get(chunk);
if (modules !== undefined) {
const { path: filename, info } = compilation.getPathWithInfo(
CssModulesPlugin.getChunkFilenameTemplate(
chunk,
compilation.outputOptions
),
{
hash,
runtime: chunk.runtime,
chunk,
contentHashType: "css"
}
);
const undoPath = getUndoPath(
filename,
compilation.outputOptions.path,
false
);
2021-11-30 19:55:51 +08:00
result.push({
render: () =>
this.renderChunk({
chunk,
chunkGraph,
2021-12-15 23:46:13 +08:00
codeGenerationResults,
2021-12-18 01:10:00 +08:00
uniqueName: compilation.outputOptions.uniqueName,
2024-03-06 14:29:57 +08:00
cssHeadDataCompression:
compilation.outputOptions.cssHeadDataCompression,
2024-03-16 18:38:58 +08:00
undoPath,
2024-08-12 22:12:05 +08:00
modules,
runtimeTemplate,
hooks
2021-11-30 19:55:51 +08:00
}),
2023-05-10 19:05:00 +08:00
filename,
info,
2021-11-30 19:55:51 +08:00
identifier: `css${chunk.id}`,
hash: chunk.contentHash.css
});
}
return result;
});
const globalChunkLoading = compilation.outputOptions.chunkLoading;
2023-06-04 01:52:25 +08:00
/**
* @param {Chunk} chunk the chunk
* @returns {boolean} true, when enabled
*/
const isEnabledForChunk = chunk => {
const options = chunk.getEntryOptions();
const chunkLoading =
options && options.chunkLoading !== undefined
? options.chunkLoading
: globalChunkLoading;
return chunkLoading === "jsonp" || chunkLoading === "import";
};
const onceForChunkSet = new WeakSet();
2023-06-04 01:52:25 +08:00
/**
* @param {Chunk} chunk chunk to check
* @param {Set<string>} set runtime requirements
*/
2021-11-30 19:55:51 +08:00
const handler = (chunk, set) => {
if (onceForChunkSet.has(chunk)) return;
onceForChunkSet.add(chunk);
if (!isEnabledForChunk(chunk)) return;
2021-11-30 19:55:51 +08:00
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
2021-12-01 16:50:13 +08:00
set.add(RuntimeGlobals.makeNamespaceObject);
2021-11-30 19:55:51 +08:00
2021-12-03 23:23:09 +08:00
const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
2021-11-30 19:55:51 +08:00
compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
};
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hasCssModules)
2024-08-12 22:12:05 +08:00
.tap(PLUGIN_NAME, handler);
2021-11-30 19:55:51 +08:00
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
2024-10-03 00:05:39 +08:00
if (!isEnabledForChunk(chunk)) return;
if (
!chunkGraph.hasModuleInGraph(
chunk,
m =>
m.type === CSS_MODULE_TYPE ||
m.type === CSS_MODULE_TYPE_GLOBAL ||
m.type === CSS_MODULE_TYPE_MODULE ||
m.type === CSS_MODULE_TYPE_AUTO
)
) {
return;
}
2024-10-03 00:05:39 +08:00
set.add(RuntimeGlobals.hasOwnProperty);
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkCssFilename);
});
2021-11-30 19:55:51 +08:00
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
2024-10-03 00:05:39 +08:00
if (!isEnabledForChunk(chunk)) return;
if (
!chunkGraph.hasModuleInGraph(
chunk,
m =>
m.type === CSS_MODULE_TYPE ||
m.type === CSS_MODULE_TYPE_GLOBAL ||
m.type === CSS_MODULE_TYPE_MODULE ||
m.type === CSS_MODULE_TYPE_AUTO
)
) {
return;
}
2024-10-03 00:05:39 +08:00
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkCssFilename);
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
});
2021-11-30 19:55:51 +08:00
}
);
}
2023-04-29 02:50:41 +08:00
/**
* @param {Chunk} chunk chunk
* @param {Iterable<Module>} modules unordered modules
* @param {Compilation} compilation compilation
* @returns {Module[]} ordered modules
*/
2021-12-18 01:10:00 +08:00
getModulesInOrder(chunk, modules, compilation) {
if (!modules) return [];
2023-04-29 03:26:27 +08:00
/** @type {Module[]} */
2021-12-18 01:10:00 +08:00
const modulesList = [...modules];
// Get ordered list of modules per chunk group
// Lists are in reverse order to allow to use Array.pop()
const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
const sortedModules = modulesList
2024-07-31 11:31:11 +08:00
.map(module => ({
module,
index: chunkGroup.getModulePostOrderIndex(module)
}))
2021-12-18 01:10:00 +08:00
.filter(item => item.index !== undefined)
2023-06-04 01:52:25 +08:00
.sort(
(a, b) =>
/** @type {number} */ (b.index) - /** @type {number} */ (a.index)
)
2021-12-18 01:10:00 +08:00
.map(item => item.module);
return { list: sortedModules, set: new Set(sortedModules) };
});
if (modulesByChunkGroup.length === 1)
return modulesByChunkGroup[0].list.reverse();
const compareModuleLists = ({ list: a }, { list: b }) => {
if (a.length === 0) {
return b.length === 0 ? 0 : 1;
}
2024-07-31 04:21:27 +08:00
if (b.length === 0) return -1;
return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
2021-12-18 01:10:00 +08:00
};
modulesByChunkGroup.sort(compareModuleLists);
2023-04-29 03:26:27 +08:00
/** @type {Module[]} */
2021-12-18 01:10:00 +08:00
const finalModules = [];
for (;;) {
const failedModules = new Set();
const list = modulesByChunkGroup[0].list;
if (list.length === 0) {
// done, everything empty
break;
}
2023-04-29 02:50:41 +08:00
/** @type {Module} */
2021-12-18 01:10:00 +08:00
let selectedModule = list[list.length - 1];
2024-07-31 06:15:03 +08:00
let hasFailed;
2021-12-18 01:10:00 +08:00
outer: for (;;) {
for (const { list, set } of modulesByChunkGroup) {
if (list.length === 0) continue;
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) continue;
if (!set.has(selectedModule)) continue;
failedModules.add(selectedModule);
if (failedModules.has(lastModule)) {
// There is a conflict, try other alternatives
hasFailed = lastModule;
continue;
}
selectedModule = lastModule;
hasFailed = false;
continue outer; // restart
}
break;
}
if (hasFailed) {
// There is a not resolve-able conflict with the selectedModule
// TODO print better warning
compilation.warnings.push(
new WebpackError(
`chunk ${chunk.name || chunk.id}\nConflicting order between ${
/** @type {Module} */
(hasFailed).readableIdentifier(compilation.requestShortener)
} and ${selectedModule.readableIdentifier(
compilation.requestShortener
)}`
)
);
2023-04-29 02:50:41 +08:00
selectedModule = /** @type {Module} */ (hasFailed);
2021-12-18 01:10:00 +08:00
}
// Insert the selected module into the final modules list
finalModules.push(selectedModule);
// Remove the selected module from all lists
for (const { list, set } of modulesByChunkGroup) {
const lastModule = list[list.length - 1];
if (lastModule === selectedModule) list.pop();
else if (hasFailed && set.has(selectedModule)) {
const idx = list.indexOf(selectedModule);
if (idx >= 0) list.splice(idx, 1);
}
}
modulesByChunkGroup.sort(compareModuleLists);
}
return finalModules;
}
2023-04-29 02:50:41 +08:00
/**
* @param {Chunk} chunk chunk
* @param {ChunkGraph} chunkGraph chunk graph
* @param {Compilation} compilation compilation
* @returns {Module[]} ordered css modules
*/
2021-12-18 01:10:00 +08:00
getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
return [
...this.getModulesInOrder(
chunk,
2023-06-04 01:52:25 +08:00
/** @type {Iterable<Module>} */
(
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css-import",
compareModulesByIdentifier
)
2021-12-18 01:10:00 +08:00
),
compilation
),
...this.getModulesInOrder(
chunk,
2023-06-04 01:52:25 +08:00
/** @type {Iterable<Module>} */
(
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css",
compareModulesByIdentifier
)
2021-12-18 01:10:00 +08:00
),
compilation
)
];
2021-11-30 19:55:51 +08:00
}
2024-03-16 19:39:46 +08:00
/**
2024-06-11 21:09:50 +08:00
* @param {object} options options
2024-03-16 19:39:46 +08:00
* @param {string[]} options.metaData meta data
* @param {string} options.undoPath undo path for public path auto
* @param {Chunk} options.chunk chunk
* @param {ChunkGraph} options.chunkGraph chunk graph
* @param {CodeGenerationResults} options.codeGenerationResults code generation results
* @param {CssModule} options.module css module
2024-08-12 22:12:05 +08:00
* @param {RuntimeTemplate} options.runtimeTemplate runtime template
* @param {CompilationHooks} options.hooks hooks
2024-03-16 19:39:46 +08:00
* @returns {Source} css module source
*/
renderModule({
metaData,
undoPath,
chunk,
chunkGraph,
codeGenerationResults,
2024-08-12 22:12:05 +08:00
module,
hooks,
runtimeTemplate
2024-03-16 19:39:46 +08:00
}) {
const codeGenResult = codeGenerationResults.get(module, chunk.runtime);
const moduleSourceContent =
/** @type {Source} */
(
codeGenResult.sources.get("css") ||
codeGenResult.sources.get("css-import")
);
const cacheEntry = this._moduleCache.get(moduleSourceContent);
/** @type {Inheritance} */
2024-07-31 04:09:42 +08:00
const inheritance = [[module.cssLayer, module.supports, module.media]];
2024-03-16 19:39:46 +08:00
if (module.inheritance) {
inheritance.push(...module.inheritance);
}
let source;
if (
cacheEntry &&
cacheEntry.undoPath === undoPath &&
cacheEntry.inheritance.every(([layer, supports, media], i) => {
const item = inheritance[i];
if (Array.isArray(item)) {
return layer === item[0] && supports === item[1] && media === item[2];
}
return false;
})
) {
source = cacheEntry.source;
} else {
2024-03-18 18:56:28 +08:00
const moduleSourceCode = /** @type {string} */ (
moduleSourceContent.source()
);
2024-03-16 19:39:46 +08:00
const publicPathAutoRegex = new RegExp(
CssUrlDependency.PUBLIC_PATH_AUTO,
"g"
);
/** @type {Source} */
let moduleSource = new ReplaceSource(moduleSourceContent);
let match;
while ((match = publicPathAutoRegex.exec(moduleSourceCode))) {
/** @type {ReplaceSource} */ (moduleSource).replace(
match.index,
(match.index += match[0].length - 1),
undoPath
);
}
for (let i = 0; i < inheritance.length; i++) {
const layer = inheritance[i][0];
const supports = inheritance[i][1];
const media = inheritance[i][2];
if (media) {
moduleSource = new ConcatSource(
`@media ${media} {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
if (supports) {
moduleSource = new ConcatSource(
`@supports (${supports}) {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
// Layer can be anonymous
if (layer !== undefined && layer !== null) {
moduleSource = new ConcatSource(
`@layer${layer ? ` ${layer}` : ""} {\n`,
new PrefixSource("\t", moduleSource),
"}\n"
);
}
}
if (moduleSource) {
moduleSource = new ConcatSource(moduleSource, "\n");
}
source = new CachedSource(moduleSource);
this._moduleCache.set(moduleSourceContent, {
inheritance,
undoPath,
source
});
}
/** @type {CssExportsData | undefined} */
const cssExportsData =
codeGenResult.data && codeGenResult.data.get("css-exports");
const exports = cssExportsData && cssExportsData.exports;
const esModule = cssExportsData && cssExportsData.esModule;
2024-07-31 10:39:30 +08:00
let moduleId = String(chunkGraph.getModuleId(module));
2024-03-16 19:39:46 +08:00
// When `optimization.moduleIds` is `named` the module id is a path, so we need to normalize it between platforms
if (typeof moduleId === "string") {
moduleId = moduleId.replace(/\\/g, "/");
}
metaData.push(
`${
exports
? Array.from(
exports,
([n, v]) => `${escapeCss(n)}:${escapeCss(v)}/`
2024-07-31 05:43:19 +08:00
).join("")
2024-03-16 19:39:46 +08:00
: ""
}${esModule ? "&" : ""}${escapeCss(moduleId)}`
);
2024-08-12 22:12:05 +08:00
return tryRunOrWebpackError(
() =>
hooks.renderModulePackage.call(source, module, {
runtimeTemplate
}),
"CssModulesPlugin.getCompilationHooks().renderModulePackage"
);
2024-03-16 19:39:46 +08:00
}
2023-04-29 03:26:27 +08:00
/**
2024-06-11 21:09:50 +08:00
* @param {object} options options
2023-04-29 03:26:27 +08:00
* @param {string | undefined} options.uniqueName unique name
2024-03-06 14:29:57 +08:00
* @param {boolean | undefined} options.cssHeadDataCompression compress css head data
2024-03-16 18:38:58 +08:00
* @param {string} options.undoPath undo path for public path auto
2023-04-29 03:26:27 +08:00
* @param {Chunk} options.chunk chunk
* @param {ChunkGraph} options.chunkGraph chunk graph
* @param {CodeGenerationResults} options.codeGenerationResults code generation results
* @param {CssModule[]} options.modules ordered css modules
2024-08-12 22:12:05 +08:00
* @param {RuntimeTemplate} options.runtimeTemplate runtime template
* @param {CompilationHooks} options.hooks hooks
2023-04-29 03:26:27 +08:00
* @returns {Source} generated source
*/
2021-12-18 01:10:00 +08:00
renderChunk({
uniqueName,
2024-03-06 14:29:57 +08:00
cssHeadDataCompression,
2024-03-16 18:38:58 +08:00
undoPath,
2021-12-18 01:10:00 +08:00
chunk,
chunkGraph,
codeGenerationResults,
2024-08-12 22:12:05 +08:00
modules,
runtimeTemplate,
hooks
2021-12-18 01:10:00 +08:00
}) {
2021-11-30 19:55:51 +08:00
const source = new ConcatSource();
2023-04-29 03:26:27 +08:00
/** @type {string[]} */
2021-12-14 23:02:26 +08:00
const metaData = [];
2021-11-30 19:55:51 +08:00
for (const module of modules) {
try {
2024-03-16 19:39:46 +08:00
const moduleSource = this.renderModule({
metaData,
undoPath,
chunk,
chunkGraph,
codeGenerationResults,
2024-08-12 22:12:05 +08:00
module,
runtimeTemplate,
hooks
2024-03-16 19:39:46 +08:00
});
source.add(moduleSource);
2024-07-31 15:37:05 +08:00
} catch (err) {
2023-06-04 01:52:25 +08:00
/** @type {Error} */
2024-07-31 15:37:05 +08:00
(err).message += `\nduring rendering of css ${module.identifier()}`;
throw err;
2021-11-30 19:55:51 +08:00
}
}
2024-03-06 14:29:57 +08:00
const metaDataStr = metaData.join(",");
2021-11-30 19:55:51 +08:00
source.add(
2021-12-15 23:46:13 +08:00
`head{--webpack-${escapeCss(
2024-07-31 10:39:30 +08:00
(uniqueName ? `${uniqueName}-` : "") + chunk.id,
2021-12-15 23:46:13 +08:00
true
2024-07-31 11:50:02 +08:00
)}:${cssHeadDataCompression ? lzwEncode(metaDataStr) : metaDataStr};}`
2021-11-30 19:55:51 +08:00
);
2024-08-12 22:12:05 +08:00
chunk.rendered = true;
2021-11-30 19:55:51 +08:00
return source;
}
2023-04-29 03:26:27 +08:00
/**
* @param {Chunk} chunk chunk
* @param {OutputOptions} outputOptions output options
2024-08-09 01:03:17 +08:00
* @returns {TemplatePath} used filename template
2023-04-29 03:26:27 +08:00
*/
2021-11-30 19:55:51 +08:00
static getChunkFilenameTemplate(chunk, outputOptions) {
if (chunk.cssFilenameTemplate) {
return chunk.cssFilenameTemplate;
2021-12-17 19:18:01 +08:00
} else if (chunk.canBeInitial()) {
2024-08-09 01:03:17 +08:00
return /** @type {TemplatePath} */ (outputOptions.cssFilename);
2021-11-30 19:55:51 +08:00
}
2024-08-09 01:03:17 +08:00
return /** @type {TemplatePath} */ (outputOptions.cssChunkFilename);
2021-11-30 19:55:51 +08:00
}
2023-04-29 02:50:41 +08:00
/**
* @param {Chunk} chunk chunk
* @param {ChunkGraph} chunkGraph chunk graph
* @returns {boolean} true, when the chunk has css
*/
2021-11-30 19:55:51 +08:00
static chunkHasCss(chunk, chunkGraph) {
2021-12-03 02:29:55 +08:00
return (
2024-07-31 11:11:11 +08:00
Boolean(chunkGraph.getChunkModulesIterableBySourceType(chunk, "css")) ||
Boolean(
chunkGraph.getChunkModulesIterableBySourceType(chunk, "css-import")
)
2021-12-03 02:29:55 +08:00
);
2021-11-30 19:55:51 +08:00
}
}
module.exports = CssModulesPlugin;