From 08cd56741419f782fa51829e85ef3ee70a5cd35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ertu=C4=9Frul=20Keremo=C4=9Flu?= Date: Tue, 31 Mar 2026 05:59:49 +0300 Subject: [PATCH 1/5] Support at-rule property definitions in variable renaming --- src/variable.ts | 28 ++++++- test/variable.test.ts | 172 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 4 deletions(-) diff --git a/src/variable.ts b/src/variable.ts index 46189ec..d7cb8ea 100644 --- a/src/variable.ts +++ b/src/variable.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import postcss, {Declaration} from 'postcss'; +import postcss, {AtRule, Declaration} from 'postcss'; import valueParser from 'postcss-value-parser'; import {type RenamingMap, type VariableRenamingOptions} from './options'; import {type SkipPredicate, createSkipPredicate} from './skip'; @@ -109,19 +109,39 @@ function plugin({ return parsed.toString(); } - const alreadyProcessedNodes = new Set(); + const alreadyProcessedDeclarationNodes = new Set(); + function renameDeclaration(declarationNode: Declaration): void { - if (alreadyProcessedNodes.has(declarationNode)) { + if (alreadyProcessedDeclarationNodes.has(declarationNode)) { return; } - alreadyProcessedNodes.add(declarationNode); + alreadyProcessedDeclarationNodes.add(declarationNode); declarationNode.prop = renameVariable(declarationNode.prop); declarationNode.value = renameValue(declarationNode.value); } + const alreadyProcessedAtRulePropertyNodes = new Set(); + + function renameAtRuleProperty(atRuleNode: AtRule): void { + if (alreadyProcessedAtRulePropertyNodes.has(atRuleNode)) { + return; + } + + alreadyProcessedAtRulePropertyNodes.add(atRuleNode); + + atRuleNode.params = renameVariable(atRuleNode.params); + + atRuleNode.walkDecls(declarationNode => { + renameDeclaration(declarationNode); + }); + } + return { + AtRule: { + property: renameAtRuleProperty, + }, Declaration: renameDeclaration, OnceExit() { if (outputMapCallback) { diff --git a/test/variable.test.ts b/test/variable.test.ts index 7229ba6..eed7015 100644 --- a/test/variable.test.ts +++ b/test/variable.test.ts @@ -469,6 +469,19 @@ describe('with strategy "none"', () => { ); }); }); + + describe('with @property (contains initial-value)', () => { + const input = + '@property --test-property { syntax: ""; initial-value: var(--initial); }'; + + it('does nothing with no options', () => { + assertPostcss(run(input), input); + }); + + it('does nothing with an explicit strategy', () => { + assertPostcss(run(input, {strategy: 'none'}), input); + }); + }); }); describe('with strategy "debug"', () => { @@ -946,4 +959,163 @@ describe('with strategy "debug"', () => { ); }); }); + + describe('with @property (contains initial-value)', () => { + const input = + '@property --test-property { syntax: ""; initial-value: var(--initial); }'; + + it('adds an underscore after every name', () => { + assertPostcss( + run(input, {strategy: 'debug'}), + '@property --test-property_ { syntax: ""; initial-value: var(--initial_); }', + ); + }); + + it('emits an output map', () => { + assertMapEquals( + input, + { + initial: 'initial_', + 'test-property': 'test-property_', + }, + { + strategy: 'debug', + }, + ); + }); + + it('includes the prefix in the output map', () => { + assertMapEquals( + input, + { + initial: 'pf-initial_', + 'test-property': 'pf-test-property_', + }, + { + strategy: 'debug', + prefix: 'pf', + }, + ); + }); + + it('omits excluded names from the output map', () => { + assertMapEquals( + input, + { + 'test-property': 'test-property_', + }, + { + strategy: 'debug', + except: ['initial'], + }, + ); + }); + }); + + describe('with declaration + @property (contains initial-value) + declaration', () => { + it('adds an underscore after every name', () => { + const input = ` + --initial: 45; + + @property --test-property { + syntax: ""; + initial-value: var(--initial); + } + + --test-property: 45; + `; + + const expected = ` + --initial_: 45; + + @property --test-property_ { + syntax: ""; + initial-value: var(--initial_); + } + + --test-property_: 45; + `; + + assertPostcss(run(input, {strategy: 'debug'}), expected); + }); + }); +}); + +describe('with strategy "minimal"', () => { + describe('with declaration + @property (contains initial-value) + declaration', () => { + const input1 = ` + --test-property: 45; + + @property --test-property { + syntax: ""; + initial-value: var(--initial); + } + + --initial: 45; + `; + + const expected1 = ` + --a: 45; + + @property --a { + syntax: ""; + initial-value: var(--b); + } + + --b: 45; + `; + + const input2 = ` + --initial: 45; + + @property --test-property { + syntax: ""; + initial-value: var(--initial); + } + + --test-property: 45; + `; + + const expected2 = ` + --a: 45; + + @property --b { + syntax: ""; + initial-value: var(--a); + } + + --b: 45; + `; + + it('respects declaration order', () => { + assertPostcss(run(input1, {strategy: 'minimal'}), expected1); + assertPostcss(run(input2, {strategy: 'minimal'}), expected2); + }); + + it('includes the prefix in the output map', () => { + assertMapEquals( + input1, + { + initial: 'pf-b', + 'test-property': 'pf-a', + }, + { + strategy: 'minimal', + prefix: 'pf', + }, + ); + + assertMapEquals( + input2, + { + initial: 'pf-a', + 'test-property': 'pf-b', + }, + { + strategy: 'minimal', + prefix: 'pf', + }, + ); + }); + }); }); From fa01eed0921c1fd16c9c1e2436499e6e93766341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ertu=C4=9Frul=20Keremo=C4=9Flu?= Date: Wed, 8 Apr 2026 07:18:46 +0300 Subject: [PATCH 2/5] Remove handling of declarations in at-rule property definitions --- src/variable.ts | 4 -- test/variable.test.ts | 149 +++++++++++++++++------------------------- 2 files changed, 61 insertions(+), 92 deletions(-) diff --git a/src/variable.ts b/src/variable.ts index d7cb8ea..702985f 100644 --- a/src/variable.ts +++ b/src/variable.ts @@ -132,10 +132,6 @@ function plugin({ alreadyProcessedAtRulePropertyNodes.add(atRuleNode); atRuleNode.params = renameVariable(atRuleNode.params); - - atRuleNode.walkDecls(declarationNode => { - renameDeclaration(declarationNode); - }); } return { diff --git a/test/variable.test.ts b/test/variable.test.ts index eed7015..a61e04b 100644 --- a/test/variable.test.ts +++ b/test/variable.test.ts @@ -470,9 +470,13 @@ describe('with strategy "none"', () => { }); }); - describe('with @property (contains initial-value)', () => { - const input = - '@property --test-property { syntax: ""; initial-value: var(--initial); }'; + describe('with @property', () => { + const input = ` + @property --test-property { + syntax: ""; + inherits: false; + initial-value: 45deg; + }`; it('does nothing with no options', () => { assertPostcss(run(input), input); @@ -960,22 +964,29 @@ describe('with strategy "debug"', () => { }); }); - describe('with @property (contains initial-value)', () => { - const input = - '@property --test-property { syntax: ""; initial-value: var(--initial); }'; + describe('with @property', () => { + const input = ` + @property --test-property { + syntax: ""; + inherits: false; + initial-value: 45deg; + }`; + + const expected = ` + @property --test-property_ { + syntax: ""; + inherits: false; + initial-value: 45deg; + }`; it('adds an underscore after every name', () => { - assertPostcss( - run(input, {strategy: 'debug'}), - '@property --test-property_ { syntax: ""; initial-value: var(--initial_); }', - ); + assertPostcss(run(input, {strategy: 'debug'}), expected); }); it('emits an output map', () => { assertMapEquals( input, { - initial: 'initial_', 'test-property': 'test-property_', }, { @@ -988,7 +999,6 @@ describe('with strategy "debug"', () => { assertMapEquals( input, { - initial: 'pf-initial_', 'test-property': 'pf-test-property_', }, { @@ -1001,102 +1011,77 @@ describe('with strategy "debug"', () => { it('omits excluded names from the output map', () => { assertMapEquals( input, - { - 'test-property': 'test-property_', - }, + {}, { strategy: 'debug', - except: ['initial'], + except: ['test-property'], }, ); }); }); - - describe('with declaration + @property (contains initial-value) + declaration', () => { - it('adds an underscore after every name', () => { - const input = ` - --initial: 45; - - @property --test-property { - syntax: ""; - initial-value: var(--initial); - } - - --test-property: 45; - `; - - const expected = ` - --initial_: 45; - - @property --test-property_ { - syntax: ""; - initial-value: var(--initial_); - } - - --test-property_: 45; - `; - - assertPostcss(run(input, {strategy: 'debug'}), expected); - }); - }); }); describe('with strategy "minimal"', () => { - describe('with declaration + @property (contains initial-value) + declaration', () => { - const input1 = ` - --test-property: 45; + describe('with declaration + @property', () => { + const input = ` + --test-property: 45deg; @property --test-property { syntax: ""; - initial-value: var(--initial); - } - - --initial: 45; - `; + inherits: false; + }`; - const expected1 = ` - --a: 45; + const expected = ` + --a: 45deg; @property --a { syntax: ""; - initial-value: var(--b); - } + inherits: false; + }`; - --b: 45; - `; + it('maps names to the shortest possible strings', () => { + assertPostcss(run(input, {strategy: 'minimal'}), expected); + }); - const input2 = ` - --initial: 45; - + it('includes the prefix in the output map', () => { + assertMapEquals( + input, + { + 'test-property': 'pf-a', + }, + { + strategy: 'minimal', + prefix: 'pf', + }, + ); + }); + }); + + describe('with @property + declaration', () => { + const input = ` @property --test-property { syntax: ""; - initial-value: var(--initial); + inherits: false; } - --test-property: 45; - `; + --test-property: 45deg;`; - const expected2 = ` - --a: 45; - - @property --b { + const expected = ` + @property --a { syntax: ""; - initial-value: var(--a); + inherits: false; } - --b: 45; - `; + --a: 45deg;`; - it('respects declaration order', () => { - assertPostcss(run(input1, {strategy: 'minimal'}), expected1); - assertPostcss(run(input2, {strategy: 'minimal'}), expected2); + it('maps names to the shortest possible strings', () => { + assertPostcss(run(input, {strategy: 'minimal'}), expected); }); it('includes the prefix in the output map', () => { assertMapEquals( - input1, + input, { - initial: 'pf-b', 'test-property': 'pf-a', }, { @@ -1104,18 +1089,6 @@ describe('with strategy "minimal"', () => { prefix: 'pf', }, ); - - assertMapEquals( - input2, - { - initial: 'pf-a', - 'test-property': 'pf-b', - }, - { - strategy: 'minimal', - prefix: 'pf', - }, - ); }); }); }); From f201270b2597f0935fff1e07b23b6040ec538235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ertu=C4=9Frul=20Keremo=C4=9Flu?= Date: Thu, 9 Apr 2026 09:44:49 +0300 Subject: [PATCH 3/5] Fix at-rule property definitions in tests to match the specification --- test/variable.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/variable.test.ts b/test/variable.test.ts index a61e04b..6f3b094 100644 --- a/test/variable.test.ts +++ b/test/variable.test.ts @@ -1027,7 +1027,7 @@ describe('with strategy "minimal"', () => { --test-property: 45deg; @property --test-property { - syntax: ""; + syntax: "*"; inherits: false; }`; @@ -1035,7 +1035,7 @@ describe('with strategy "minimal"', () => { --a: 45deg; @property --a { - syntax: ""; + syntax: "*"; inherits: false; }`; @@ -1060,7 +1060,7 @@ describe('with strategy "minimal"', () => { describe('with @property + declaration', () => { const input = ` @property --test-property { - syntax: ""; + syntax: "*"; inherits: false; } @@ -1068,7 +1068,7 @@ describe('with strategy "minimal"', () => { const expected = ` @property --a { - syntax: ""; + syntax: "*"; inherits: false; } From e36fd380b9fd292dff8dcdb5aec150ce1c1caafb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 9 Apr 2026 16:22:04 -0700 Subject: [PATCH 4/5] Style tweaks --- src/variable.ts | 18 +++++++----------- test/variable.test.ts | 12 ++++++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/variable.ts b/src/variable.ts index 702985f..844e299 100644 --- a/src/variable.ts +++ b/src/variable.ts @@ -109,35 +109,31 @@ function plugin({ return parsed.toString(); } - const alreadyProcessedDeclarationNodes = new Set(); - + const alreadyProcessedDeclarations = new Set(); function renameDeclaration(declarationNode: Declaration): void { - if (alreadyProcessedDeclarationNodes.has(declarationNode)) { + if (alreadyProcessedDeclarations.has(declarationNode)) { return; } - alreadyProcessedDeclarationNodes.add(declarationNode); + alreadyProcessedDeclarations.add(declarationNode); declarationNode.prop = renameVariable(declarationNode.prop); declarationNode.value = renameValue(declarationNode.value); } - const alreadyProcessedAtRulePropertyNodes = new Set(); - + const alreadyProcessedAtRules = new Set(); function renameAtRuleProperty(atRuleNode: AtRule): void { - if (alreadyProcessedAtRulePropertyNodes.has(atRuleNode)) { + if (alreadyProcessedAtRules.has(atRuleNode)) { return; } - alreadyProcessedAtRulePropertyNodes.add(atRuleNode); + alreadyProcessedAtRules.add(atRuleNode); atRuleNode.params = renameVariable(atRuleNode.params); } return { - AtRule: { - property: renameAtRuleProperty, - }, + AtRule: {property: renameAtRuleProperty}, Declaration: renameDeclaration, OnceExit() { if (outputMapCallback) { diff --git a/test/variable.test.ts b/test/variable.test.ts index 6f3b094..9a70311 100644 --- a/test/variable.test.ts +++ b/test/variable.test.ts @@ -476,7 +476,8 @@ describe('with strategy "none"', () => { syntax: ""; inherits: false; initial-value: 45deg; - }`; + } + `; it('does nothing with no options', () => { assertPostcss(run(input), input); @@ -493,7 +494,8 @@ describe('with strategy "debug"', () => { const input = ` .no-variables-here { absolutely: "nothing"; - }`; + } + `; const options: plugin.Options = { strategy: 'debug', @@ -970,14 +972,16 @@ describe('with strategy "debug"', () => { syntax: ""; inherits: false; initial-value: 45deg; - }`; + } + `; const expected = ` @property --test-property_ { syntax: ""; inherits: false; initial-value: 45deg; - }`; + } + `; it('adds an underscore after every name', () => { assertPostcss(run(input, {strategy: 'debug'}), expected); From ec0b65f195f2b055aee8d163c728c30c36f3a4a5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 9 Apr 2026 16:26:53 -0700 Subject: [PATCH 5/5] Bump version --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645bd10..c752867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.1 + +* Variable renaming now also renames variables defined by the `@property` rule. + ## 0.8.0 * Added variable renaming capabilities. Users who are interested in using the diff --git a/package.json b/package.json index 8b228dd..f50f067 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcss-rename", - "version": "0.8.0", + "version": "0.8.1", "description": "A PostCSS plugin to replace CSS names based on a customizable renaming scheme.", "keywords": [ "postcss",