/* MIT License http://www.opensource.org/licenses/mit-license.php Author Sergey Melyukov @smelukov */ "use strict"; const { ConcatSource, OriginalSource, RawSource, ReplaceSource, SourceMapSource } = require("webpack-sources"); const { UsageState } = require("../ExportsInfo"); const Generator = require("../Generator"); const InitFragment = require("../InitFragment"); const { CSS_TEXT_TYPE, CSS_TEXT_TYPES, CSS_TYPE, CSS_TYPES, JAVASCRIPT_AND_CSS_TEXT_TYPES, JAVASCRIPT_AND_CSS_TYPES, JAVASCRIPT_TYPE, JAVASCRIPT_TYPES } = require("../ModuleSourceTypeConstants"); const RuntimeGlobals = require("../RuntimeGlobals"); const Template = require("../Template"); const CssImportDependency = require("../dependencies/CssImportDependency"); const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency"); const { encodeMappings } = require("../util/createMappings"); const memoize = require("../util/memoize"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../../declarations/WebpackOptions").CssModuleGeneratorOptions} CssModuleGeneratorOptions */ /** @typedef {import("../Compilation").DependencyConstructor} DependencyConstructor */ /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */ /** @typedef {import("../Dependency")} Dependency */ /** @typedef {import("../DependencyTemplate").CssData} CssData */ /** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */ /** @typedef {import("../Generator").GenerateContext} GenerateContext */ /** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */ /** @typedef {import("../Module").BuildInfo} BuildInfo */ /** @typedef {import("../Module").BuildMeta} BuildMeta */ /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */ /** @typedef {import("../Module").SourceType} SourceType */ /** @typedef {import("../Module").SourceTypes} SourceTypes */ /** @typedef {import("../ModuleGraph")} ModuleGraph */ /** @typedef {import("../NormalModule")} NormalModule */ /** @typedef {import("../util/Hash")} Hash */ /** @typedef {import("./CssModulesPlugin").ModuleFactoryCacheEntry} ModuleFactoryCacheEntry */ /** @typedef {import("./CssModule")} CssModule */ /** @typedef {import("../Compilation")} Compilation */ /** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */ /** @typedef {import("../../declarations/WebpackOptions").CssParserExportType} CssParserExportType */ /** @typedef {{ line: number, column: number }} SourcePosition */ /** @typedef {Map} ExportLocsMap */ const getPropertyName = memoize(() => require("../util/property")); const getCssModulesPlugin = memoize(() => require("./CssModulesPlugin")); /** @typedef {import("webpack-sources").RawSourceMap} RawSourceMap */ /** * Build a v3 source map that maps each line in `generatedJs` containing a * known CSS-class export entry back to the corresponding selector position * in the original CSS. Lines without an associated export are left * unmapped — devtools simply shows them as part of the bundled JS. * @param {string} generatedJs the generated JS string * @param {ExportLocsMap} exportLocs map of export names to CSS source location * @param {string} cssContent original CSS source content * @param {string} sourceName source identifier to use in the map * @returns {RawSourceMap} a v3 RawSourceMap */ const buildExportsSourceMap = ( generatedJs, exportLocs, cssContent, sourceName ) => { const lines = generatedJs.split("\n"); const lineByExport = new Map(); for (const [exportName] of exportLocs) { const needle = `${JSON.stringify(exportName)}:`; for (let i = 0; i < lines.length; i++) { if (lines[i].includes(needle)) { lineByExport.set(exportName, i); break; } } } /** @type {(import("../util/createMappings").LineMappings)[]} */ const perLine = lines.map(() => null); for (const [exportName, genLine] of lineByExport) { const pos = /** @type {SourcePosition} */ (exportLocs.get(exportName)); // Source-map V3 uses 0-based lines and 0-based columns. webpack's // dependency `loc` uses 1-based lines and 0-based columns, so subtract // one from the line. perLine[genLine] = { generatedColumn: 0, sourceIndex: 0, originalLine: pos.line - 1, originalColumn: pos.column }; } return { version: 3, file: "", sources: [sourceName], sourcesContent: [cssContent], names: [], mappings: encodeMappings(perLine) }; }; class CssGenerator extends Generator { /** * Creates an instance of CssGenerator. * @param {CssModuleGeneratorOptions} options options * @param {ModuleGraph} moduleGraph the module graph */ constructor(options, moduleGraph) { super(); this.options = options; this._exportsOnly = options.exportsOnly; this._esModule = options.esModule; this._moduleGraph = moduleGraph; /** @type {WeakMap} */ this._moduleFactoryCache = new WeakMap(); } /** * Returns the reason this module cannot be concatenated, when one exists. * @param {NormalModule} module module for which the bailout reason should be determined * @param {ConcatenationBailoutReasonContext} context context * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated */ getConcatenationBailoutReason(module, context) { if (!this._esModule) { return "Module is not an ECMAScript module"; } return undefined; } /** * Returns the `@charset` that will appear at the start of this module's * default export, walking through text imports when the module has no * local `@charset` of its own. * @param {NormalModule} module the module * @param {ModuleGraph} moduleGraph module graph * @param {WeakSet=} visited cycle guard * @returns {string | undefined} the effective charset */ _getEffectiveCharset(module, moduleGraph, visited = new WeakSet()) { if (!module || visited.has(module)) return undefined; const exportType = /** @type {CssModule} */ (module).exportType; if (exportType !== "text" && exportType !== "css-style-sheet") { return undefined; } visited.add(module); const own = module.buildInfo && /** @type {BuildInfo} */ (module.buildInfo).charset; if (own !== undefined) return own; if (exportType !== "text") return undefined; for (const dep of module.dependencies) { if (dep instanceof CssImportDependency) { const depModule = /** @type {NormalModule} */ ( moduleGraph.getModule(dep) ); const inherited = this._getEffectiveCharset( depModule, moduleGraph, visited ); if (inherited !== undefined) return inherited; } } return undefined; } /** * Generate JavaScript expressions that evaluate each `@import`'d module * for side effects. Only used by `style` exportType, where each imported * style module injects its own `