Skip to content

Commit

Permalink
fix: contenthash for css generator options
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jul 10, 2024
2 parents e716d44 + 97335e1 commit 1a27b9e
Show file tree
Hide file tree
Showing 15 changed files with 455 additions and 99 deletions.
39 changes: 18 additions & 21 deletions lib/css/CssExportsGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const { UsageState } = require("../ExportsInfo");
const Generator = require("../Generator");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const { cssExportConvention } = require("../util/conventions");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorExportsConvention} CssGeneratorExportsConvention */
Expand Down Expand Up @@ -135,20 +134,18 @@ class CssExportsGenerator extends Generator {
const source = new ConcatSource();
const usedIdentifiers = new Set();
for (const [name, v] of cssExportsData.exports) {
for (let k of cssExportConvention(name, this.convention)) {
let identifier = Template.toIdentifier(k);
let i = 0;
while (usedIdentifiers.has(identifier)) {
identifier = Template.toIdentifier(k + i);
}
usedIdentifiers.add(identifier);
generateContext.concatenationScope.registerExport(k, identifier);
source.add(
`${
generateContext.runtimeTemplate.supportsConst() ? "const" : "var"
} ${identifier} = ${JSON.stringify(v)};\n`
);
let identifier = Template.toIdentifier(name);
let i = 0;
while (usedIdentifiers.has(identifier)) {
identifier = Template.toIdentifier(name + i);
}
usedIdentifiers.add(identifier);
generateContext.concatenationScope.registerExport(name, identifier);
source.add(
`${
generateContext.runtimeTemplate.supportsConst() ? "const" : "var"
} ${identifier} = ${JSON.stringify(v)};\n`
);
}
return source;
} else {
Expand All @@ -163,16 +160,14 @@ class CssExportsGenerator extends Generator {
RuntimeGlobals.makeNamespaceObject
);
}
const newExports = [];
for (let [k, v] of cssExportsData.exports) {
for (let name of cssExportConvention(k, this.convention)) {
newExports.push(`\t${JSON.stringify(name)}: ${JSON.stringify(v)}`);
}
const exports = [];
for (let [name, v] of cssExportsData.exports) {
exports.push(`\t${JSON.stringify(name)}: ${JSON.stringify(v)}`);
}
return new RawSource(
`${needNsObj ? `${RuntimeGlobals.makeNamespaceObject}(` : ""}${
module.moduleArgument
}.exports = {\n${newExports.join(",\n")}\n}${needNsObj ? ")" : ""};`
}.exports = {\n${exports.join(",\n")}\n}${needNsObj ? ")" : ""};`
);
}
}
Expand All @@ -198,7 +193,9 @@ class CssExportsGenerator extends Generator {
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {}
updateHash(hash, { module }) {
hash.update(this.esModule.toString());
}
}

module.exports = CssExportsGenerator;
14 changes: 3 additions & 11 deletions lib/css/CssGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const { ReplaceSource } = require("webpack-sources");
const Generator = require("../Generator");
const InitFragment = require("../InitFragment");
const RuntimeGlobals = require("../RuntimeGlobals");
const { cssExportConvention } = require("../util/conventions");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorExportsConvention} CssGeneratorExportsConvention */
Expand Down Expand Up @@ -106,15 +105,6 @@ class CssGenerator extends Generator {
if (module.presentationalDependencies !== undefined)
module.presentationalDependencies.forEach(handleDependency);

if (cssExportsData.exports.size > 0) {
const newExports = new Map();
for (let [name, v] of cssExportsData.exports) {
for (let newName of cssExportConvention(name, this.convention)) {
newExports.set(newName, v);
}
}
cssExportsData.exports = newExports;
}
const data = generateContext.getData();
data.set("css-exports", cssExportsData);

Expand Down Expand Up @@ -148,7 +138,9 @@ class CssGenerator extends Generator {
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
updateHash(hash, { module }) {}
updateHash(hash, { module }) {
hash.update(this.esModule.toString());
}
}

module.exports = CssGenerator;
81 changes: 72 additions & 9 deletions lib/dependencies/CssExportDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@

"use strict";

const { cssExportConvention } = require("../util/conventions");
const makeSerializable = require("../util/makeSerializable");
const NullDependency = require("./NullDependency");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorExportsConvention} CssGeneratorExportsConvention */
/** @typedef {import("../CssModule")} CssModule */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../css/CssExportsGenerator")} CssExportsGenerator */
/** @typedef {import("../css/CssGenerator")} CssGenerator */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {import("../util/Hash")} Hash */

class CssExportDependency extends NullDependency {
/**
Expand All @@ -31,24 +38,60 @@ class CssExportDependency extends NullDependency {
return "css :export";
}

/**
* @param {string} name export name
* @param {CssGeneratorExportsConvention} convention convention of the export name
* @returns {string[]} convention results
*/
getExportsConventionNames(name, convention) {
if (this._conventionNames) {
return this._conventionNames;
}
this._conventionNames = cssExportConvention(name, convention);
return this._conventionNames;
}

/**
* Returns the exported names
* @param {ModuleGraph} moduleGraph module graph
* @returns {ExportsSpec | undefined} export names
*/
getExports(moduleGraph) {
const name = this.name;
const module = /** @type {CssModule} */ (moduleGraph.getParentModule(this));
const convention = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
).convention;
const names = this.getExportsConventionNames(this.name, convention);
return {
exports: [
{
name,
canMangle: true
}
],
exports: names.map(name => ({
name,
canMangle: true
})),
dependencies: undefined
};
}

/**
* Update the hash
* @param {Hash} hash hash to be updated
* @param {UpdateHashContext} context context
* @returns {void}
*/
updateHash(hash, { chunkGraph }) {
const module = /** @type {CssModule} */ (
chunkGraph.moduleGraph.getParentModule(this)
);
const generator = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
);
const names = this.getExportsConventionNames(
this.name,
generator.convention
);
hash.update(`exportsConvention`);
hash.update(JSON.stringify(names));
}

/**
* @param {ObjectSerializerContext} context context
*/
Expand Down Expand Up @@ -79,9 +122,29 @@ CssExportDependency.Template = class CssExportDependencyTemplate extends (
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, { cssExportsData }) {
apply(
dependency,
source,
{ cssExportsData, module: m, runtime, moduleGraph }
) {
const dep = /** @type {CssExportDependency} */ (dependency);
cssExportsData.exports.set(dep.name, dep.value);
const module = /** @type {CssModule} */ (m);
const convention = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
).convention;
const names = dep.getExportsConventionNames(dep.name, convention);
const usedNames = /** @type {string[]} */ (
names
.map(name =>
moduleGraph.getExportInfo(module, name).getUsedName(name, runtime)
)
.filter(Boolean)
);
if (usedNames.length === 0) return;

for (const used of usedNames) {
cssExportsData.exports.set(used, dep.value);
}
}
};

Expand Down
87 changes: 67 additions & 20 deletions lib/dependencies/CssLocalIdentifierDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

"use strict";

const { cssExportConvention } = require("../util/conventions");
const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
const makeSerializable = require("../util/makeSerializable");
Expand All @@ -17,6 +18,7 @@ const NullDependency = require("./NullDependency");
/** @typedef {import("../CssModule")} CssModule */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
Expand All @@ -25,6 +27,7 @@ const NullDependency = require("./NullDependency");
/** @typedef {import("../css/CssParser").Range} Range */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
/** @typedef {import("../util/Hash")} Hash */

/**
* @param {string} local css local
Expand Down Expand Up @@ -88,24 +91,62 @@ class CssLocalIdentifierDependency extends NullDependency {
return "css local identifier";
}

/**
* @param {string} name export name
* @param {CssGeneratorExportsConvention} convention convention of the export name
* @returns {string[]} convention results
*/
getExportsConventionNames(name, convention) {
if (this._conventionNames) {
return this._conventionNames;
}
this._conventionNames = cssExportConvention(this.name, convention);
return this._conventionNames;
}

/**
* Returns the exported names
* @param {ModuleGraph} moduleGraph module graph
* @returns {ExportsSpec | undefined} export names
*/
getExports(moduleGraph) {
const name = this.name;
const module = /** @type {CssModule} */ (moduleGraph.getParentModule(this));
const convention = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
).convention;
const names = this.getExportsConventionNames(this.name, convention);
return {
exports: [
{
name,
canMangle: true
}
],
exports: names.map(name => ({
name,
canMangle: true
})),
dependencies: undefined
};
}

/**
* Update the hash
* @param {Hash} hash hash to be updated
* @param {UpdateHashContext} context context
* @returns {void}
*/
updateHash(hash, { chunkGraph }) {
const module = /** @type {CssModule} */ (
chunkGraph.moduleGraph.getParentModule(this)
);
const generator = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
);
const names = this.getExportsConventionNames(
this.name,
generator.convention
);
hash.update(`exportsConvention`);
hash.update(JSON.stringify(names));
hash.update(`localIdentName`);
hash.update(generator.localIdentName);
}

/**
* @param {ObjectSerializerContext} context context
*/
Expand Down Expand Up @@ -158,7 +199,7 @@ CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTempla
dependency,
source,
{
module,
module: m,
moduleGraph,
chunkGraph,
runtime,
Expand All @@ -167,26 +208,32 @@ CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTempla
}
) {
const dep = /** @type {CssLocalIdentifierDependency} */ (dependency);
const used = moduleGraph
.getExportInfo(module, dep.name)
.getUsedName(dep.name, runtime);

if (!used) return;
const module = /** @type {CssModule} */ (m);
const convention = /** @type {CssGenerator | CssExportsGenerator} */ (
module.generator
).convention;
const names = dep.getExportsConventionNames(dep.name, convention);
const usedNames = /** @type {string[]} */ (
names
.map(name =>
moduleGraph.getExportInfo(module, name).getUsedName(name, runtime)
)
.filter(Boolean)
);
if (usedNames.length === 0) return;

// use the first usedName to generate localIdent, it's shorter when mangle exports enabled
const localIdent =
dep.prefix +
getLocalIdent(
used,
/** @type {CssModule} */ (module),
chunkGraph,
runtimeTemplate
);
getLocalIdent(usedNames[0], module, chunkGraph, runtimeTemplate);
source.replace(
dep.range[0],
dep.range[1] - 1,
escapeCssIdentifier(localIdent, dep.prefix)
);
if (used) cssExportsData.exports.set(used, localIdent);
for (const used of usedNames) {
cssExportsData.exports.set(used, localIdent);
}
}
};

Expand Down
4 changes: 2 additions & 2 deletions lib/util/conventions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* @param {string} input input
* @param {CssGeneratorExportsConvention | undefined} convention convention
* @returns {Set<string>} results
* @returns {string[]} results
*/
exports.cssExportConvention = (input, convention) => {
const set = new Set();
Expand Down Expand Up @@ -42,7 +42,7 @@ exports.cssExportConvention = (input, convention) => {
}
}
}
return set;
return Array.from(set);
};

// Copy from css-loader
Expand Down
Loading

0 comments on commit 1a27b9e

Please sign in to comment.