Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
package.lib.css.CssModulesPlugin.js Maven / Gradle / Ivy
Go to download
Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const {
ConcatSource,
PrefixSource,
ReplaceSource,
CachedSource
} = require("webpack-sources");
const CssModule = require("../CssModule");
const HotUpdateChunk = require("../HotUpdateChunk");
const {
CSS_MODULE_TYPE,
CSS_MODULE_TYPE_GLOBAL,
CSS_MODULE_TYPE_MODULE,
CSS_MODULE_TYPE_AUTO
} = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
const SelfModuleFactory = require("../SelfModuleFactory");
const WebpackError = require("../WebpackError");
const CssExportDependency = require("../dependencies/CssExportDependency");
const CssImportDependency = require("../dependencies/CssImportDependency");
const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
const { compareModulesByIdentifier } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
const { getUndoPath } = require("../util/identifier");
const memoize = require("../util/memoize");
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
const CssExportsGenerator = require("./CssExportsGenerator");
const CssGenerator = require("./CssGenerator");
const CssParser = require("./CssParser");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").Output} OutputOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../CssModule").Inheritance} Inheritance */
/** @typedef {import("../DependencyTemplate").CssExportsData} CssExportsData */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../util/memoize")} Memoize */
const getCssLoadingRuntimeModule = memoize(() =>
require("./CssLoadingRuntimeModule")
);
/**
* @param {string} name name
* @returns {{oneOf: [{$ref: string}], definitions: *}} schema
*/
const getSchema = name => {
const { definitions } = require("../../schemas/WebpackOptions.json");
return {
definitions,
oneOf: [{ $ref: `#/definitions/${name}` }]
};
};
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
)
};
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
)
};
/**
* @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
/[^a-zA-Z0-9_\u0081-\uffff-]/g,
s => `\\${s}`
);
return !omitOptionalUnderscore && /^(?!--)[0-9_-]/.test(escaped)
? `_${escaped}`
: escaped;
};
/**
* @param {string} str string
* @returns {string} encoded string
*/
const LZWEncode = str => {
/** @type {Map} */
const map = new Map();
let encoded = "";
let phrase = str[0];
let code = 256;
let maxCode = "\uffff".charCodeAt(0);
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;
};
const plugin = "CssModulesPlugin";
class CssModulesPlugin {
constructor() {
/** @type {WeakMap} */
this._moduleCache = new WeakMap();
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
plugin,
(compilation, { normalModuleFactory }) => {
const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
compilation.dependencyFactories.set(
CssUrlDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssUrlDependency,
new CssUrlDependency.Template()
);
compilation.dependencyTemplates.set(
CssLocalIdentifierDependency,
new CssLocalIdentifierDependency.Template()
);
compilation.dependencyFactories.set(
CssSelfLocalIdentifierDependency,
selfFactory
);
compilation.dependencyTemplates.set(
CssSelfLocalIdentifierDependency,
new CssSelfLocalIdentifierDependency.Template()
);
compilation.dependencyTemplates.set(
CssExportDependency,
new CssExportDependency.Template()
);
compilation.dependencyFactories.set(
CssImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CssImportDependency,
new CssImportDependency.Template()
);
compilation.dependencyTemplates.set(
StaticExportsDependency,
new StaticExportsDependency.Template()
);
for (const type of [
CSS_MODULE_TYPE,
CSS_MODULE_TYPE_GLOBAL,
CSS_MODULE_TYPE_MODULE,
CSS_MODULE_TYPE_AUTO
]) {
normalModuleFactory.hooks.createParser
.for(type)
.tap(plugin, parserOptions => {
validateParserOptions[type](parserOptions);
const { namedExports } = parserOptions;
switch (type) {
case CSS_MODULE_TYPE_GLOBAL:
case CSS_MODULE_TYPE_AUTO:
return new CssParser({
namedExports
});
case CSS_MODULE_TYPE:
return new CssParser({
allowModeSwitch: false,
namedExports
});
case CSS_MODULE_TYPE_MODULE:
return new CssParser({
defaultMode: "local",
namedExports
});
}
});
normalModuleFactory.hooks.createGenerator
.for(type)
.tap(plugin, generatorOptions => {
validateGeneratorOptions[type](generatorOptions);
return generatorOptions.exportsOnly
? new CssExportsGenerator(
generatorOptions.exportsConvention,
generatorOptions.localIdentName,
generatorOptions.esModule
)
: new CssGenerator(
generatorOptions.exportsConvention,
generatorOptions.localIdentName,
generatorOptions.esModule
);
});
normalModuleFactory.hooks.createModuleClass
.for(type)
.tap(plugin, (createData, resolveData) => {
if (resolveData.dependencies.length > 0) {
// When CSS is imported from CSS there is only one dependency
const dependency = resolveData.dependencies[0];
if (dependency instanceof CssImportDependency) {
const parent =
/** @type {CssModule} */
(compilation.moduleGraph.getParentModule(dependency));
if (parent instanceof CssModule) {
/** @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
]);
}
if (parent.inheritance) {
if (!inheritance) {
inheritance = [];
}
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
});
}
}
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)
);
}
}
});
compilation.hooks.contentHash.tap("CssModulesPlugin", chunk => {
const {
chunkGraph,
outputOptions: {
hashSalt,
hashDigest,
hashDigestLength,
hashFunction
}
} = compilation;
const modules = orderedCssModulesPerChunk.get(chunk);
if (modules === undefined) return;
const hash = createHash(hashFunction);
if (hashSalt) hash.update(hashSalt);
for (const module of modules) {
hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
}
const digest = /** @type {string} */ (hash.digest(hashDigest));
chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength);
});
compilation.hooks.renderManifest.tap(plugin, (result, options) => {
const { chunkGraph } = compilation;
const { hash, chunk, codeGenerationResults } = options;
if (chunk instanceof HotUpdateChunk) return result;
/** @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
);
result.push({
render: () =>
this.renderChunk({
chunk,
chunkGraph,
codeGenerationResults,
uniqueName: compilation.outputOptions.uniqueName,
cssHeadDataCompression:
compilation.outputOptions.cssHeadDataCompression,
undoPath,
modules
}),
filename,
info,
identifier: `css${chunk.id}`,
hash: chunk.contentHash.css
});
}
return result;
});
const globalChunkLoading = compilation.outputOptions.chunkLoading;
/**
* @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();
/**
* @param {Chunk} chunk chunk to check
* @param {Set} set runtime requirements
*/
const handler = (chunk, set) => {
if (onceForChunkSet.has(chunk)) return;
onceForChunkSet.add(chunk);
if (!isEnabledForChunk(chunk)) return;
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkCssFilename);
set.add(RuntimeGlobals.hasOwnProperty);
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
set.add(RuntimeGlobals.makeNamespaceObject);
const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
};
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hasCssModules)
.tap(plugin, handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap(plugin, handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap(plugin, handler);
}
);
}
/**
* @param {Chunk} chunk chunk
* @param {Iterable} modules unordered modules
* @param {Compilation} compilation compilation
* @returns {Module[]} ordered modules
*/
getModulesInOrder(chunk, modules, compilation) {
if (!modules) return [];
/** @type {Module[]} */
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
.map(module => {
return {
module,
index: chunkGroup.getModulePostOrderIndex(module)
};
})
.filter(item => item.index !== undefined)
.sort(
(a, b) =>
/** @type {number} */ (b.index) - /** @type {number} */ (a.index)
)
.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;
} else {
if (b.length === 0) return -1;
return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
}
};
modulesByChunkGroup.sort(compareModuleLists);
/** @type {Module[]} */
const finalModules = [];
for (;;) {
const failedModules = new Set();
const list = modulesByChunkGroup[0].list;
if (list.length === 0) {
// done, everything empty
break;
}
/** @type {Module} */
let selectedModule = list[list.length - 1];
let hasFailed = undefined;
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
)}`
)
);
selectedModule = /** @type {Module} */ (hasFailed);
}
// 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;
}
/**
* @param {Chunk} chunk chunk
* @param {ChunkGraph} chunkGraph chunk graph
* @param {Compilation} compilation compilation
* @returns {Module[]} ordered css modules
*/
getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
return [
...this.getModulesInOrder(
chunk,
/** @type {Iterable} */
(
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css-import",
compareModulesByIdentifier
)
),
compilation
),
...this.getModulesInOrder(
chunk,
/** @type {Iterable} */
(
chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
"css",
compareModulesByIdentifier
)
),
compilation
)
];
}
/**
* @param {object} options options
* @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
* @returns {Source} css module source
*/
renderModule({
metaData,
undoPath,
chunk,
chunkGraph,
codeGenerationResults,
module
}) {
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} */
let inheritance = [[module.cssLayer, module.supports, module.media]];
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 {
const moduleSourceCode = /** @type {string} */ (
moduleSourceContent.source()
);
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;
let moduleId = chunkGraph.getModuleId(module) + "";
// 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)}/`
).join("")
: ""
}${esModule ? "&" : ""}${escapeCss(moduleId)}`
);
return source;
}
/**
* @param {object} options options
* @param {string | undefined} options.uniqueName unique name
* @param {boolean | undefined} options.cssHeadDataCompression compress css head 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.modules ordered css modules
* @returns {Source} generated source
*/
renderChunk({
uniqueName,
cssHeadDataCompression,
undoPath,
chunk,
chunkGraph,
codeGenerationResults,
modules
}) {
const source = new ConcatSource();
/** @type {string[]} */
const metaData = [];
for (const module of modules) {
try {
const moduleSource = this.renderModule({
metaData,
undoPath,
chunk,
chunkGraph,
codeGenerationResults,
module
});
source.add(moduleSource);
} catch (e) {
/** @type {Error} */
(e).message += `\nduring rendering of css ${module.identifier()}`;
throw e;
}
}
const metaDataStr = metaData.join(",");
source.add(
`head{--webpack-${escapeCss(
(uniqueName ? uniqueName + "-" : "") + chunk.id,
true
)}:${cssHeadDataCompression ? LZWEncode(metaDataStr) : metaDataStr};}`
);
return source;
}
/**
* @param {Chunk} chunk chunk
* @param {OutputOptions} outputOptions output options
* @returns {Chunk["cssFilenameTemplate"] | OutputOptions["cssFilename"] | OutputOptions["cssChunkFilename"]} used filename template
*/
static getChunkFilenameTemplate(chunk, outputOptions) {
if (chunk.cssFilenameTemplate) {
return chunk.cssFilenameTemplate;
} else if (chunk.canBeInitial()) {
return outputOptions.cssFilename;
} else {
return outputOptions.cssChunkFilename;
}
}
/**
* @param {Chunk} chunk chunk
* @param {ChunkGraph} chunkGraph chunk graph
* @returns {boolean} true, when the chunk has css
*/
static chunkHasCss(chunk, chunkGraph) {
return (
!!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css") ||
!!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css-import")
);
}
}
module.exports = CssModulesPlugin;