Files
discourse/frontend/asset-processor/postcss-variable-renamer.test.mjs
T
David Taylor 439efc7419 DEV: Introduce system for renaming CSS variables (#42733)
From time-to-time, variables may need to be renamed in core. To avoid
breaking existing themes and plugins, this commit introduces a system to
automatically rewrite old variable names to new ones. A list of renames
is maintained in `stylesheets/variable-renames.json`, and is applied
when CSS is compiled. This list also powers a stylelint rule which will
automatically rewrite old names to new names in source code.

Transformations apply to all core/theme/plugin code. For now, the
stylelint rule/autofix applies to core only, but this will be extracted
to `@discourse/lint-configs` in the near future.

When a transformation is applied, it adds a trailing `/* automatically
renamed --old to --new */` comment so that the behavior is
understandable from the browser developer tools.
2026-09-01 11:34:46 +01:00

78 lines
2.5 KiB
JavaScript

/* eslint-disable qunit/require-expect */
import postcss from "postcss";
import { expect, test } from "vitest";
import renames from "../../app/assets/stylesheets/variable-renames.json";
import postcssVariableRenamer from "./postcss-variable-renamer.js";
const testMap = { "--old-name": "--new-name" };
function process(css) {
return postcss([postcssVariableRenamer(testMap)]).process(css, {
from: undefined,
}).css;
}
test("rewrites declarations of a renamed variable", () => {
expect(process(":root { --old-name: red; }")).toBe(
":root { --new-name: red; /* automatically renamed --old-name to --new-name */ }"
);
});
test("rewrites var() references", () => {
expect(process("a { color: var(--old-name); }")).toBe(
"a { color: var(--new-name); /* automatically renamed --old-name to --new-name */ }"
);
});
test("rewrites references inside fallbacks", () => {
expect(process("a { color: var(--brand, var(--old-name)); }")).toBe(
"a { color: var(--brand, var(--new-name)); /* automatically renamed --old-name to --new-name */ }"
);
});
test("preserves fallback of a renamed reference", () => {
expect(process("a { color: var(--old-name, blue); }")).toBe(
"a { color: var(--new-name, blue); /* automatically renamed --old-name to --new-name */ }"
);
});
test("leaves longer names alone", () => {
const css = "a { color: var(--old-name-hover); }";
expect(process(css)).toBe(css);
});
test("leaves names with a prefix alone", () => {
const css = "a { color: var(--theme--old-name); }";
expect(process(css)).toBe(css);
});
test("adds one comment when a declaration sets and reads the same old name", () => {
expect(process(":root { --old-name: var(--old-name, red); }")).toBe(
":root { --new-name: var(--new-name, red); /* automatically renamed --old-name to --new-name */ }"
);
});
test("leaves untouched declarations alone", () => {
const css = "a { color: var(--primary); }";
expect(process(css)).toBe(css);
});
test("leaves string contents alone", () => {
const css = 'a::before { content: "--old-name"; }';
expect(process(css)).toBe(css);
});
test("map has no chains", () => {
const oldNames = new Set(Object.keys(renames));
for (const newName of Object.values(renames)) {
expect(oldNames).not.toContain(newName);
}
});
test("map only contains custom property names", () => {
for (const [oldName, newName] of Object.entries(renames)) {
expect(oldName).toMatch(/^--[\w-]+$/);
expect(newName).toMatch(/^--[\w-]+$/);
}
});