@seyuna/postcss 1.0.0-canary.11 → 1.0.0-canary.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.0.0-canary.13](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.12...v1.0.0-canary.13) (2025-10-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * safely clone nodes and replace {name} placeholders ([8a68ee6](https://github.com/seyuna-corp/seyuna-postcss/commit/8a68ee6f1ade947639151bdbf365c102aa439d37))
7
+
8
+ # [1.0.0-canary.12](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.11...v1.0.0-canary.12) (2025-09-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * color functions not running when used inside at-each rules ([995fd98](https://github.com/seyuna-corp/seyuna-postcss/commit/995fd9816f2c2d51caa67add4d3172cee41d0c65))
14
+
1
15
  # [1.0.0-canary.11](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.10...v1.0.0-canary.11) (2025-09-15)
2
16
 
3
17
 
@@ -1,34 +1,9 @@
1
1
  import { AtRule } from "postcss";
2
2
  /**
3
- * Custom PostCSS plugin handler for `@each-standard-color` at-rules.
4
- *
5
- * Example usage:
6
- *
7
- * @each-standard-color {
8
- * color: white;
9
- * }
10
- *
11
- * Will generate:
12
- *
13
- * .alpha { color: white; }
14
- * .beta { color: white; }
15
- * .gamma { color: white; }
16
- * ...
3
+ * Custom PostCSS handler for @each-standard-color
17
4
  */
18
5
  export declare function eachStandardColor(atRule: AtRule): void;
19
6
  /**
20
- * Custom PostCSS plugin handler for `@each-fixed-color` at-rules.
21
- *
22
- * Example usage:
23
- *
24
- * @each-fixed-color {
25
- * color: white;
26
- * }
27
- *
28
- * Will generate:
29
- *
30
- * .primary { color: white; }
31
- * .secondary { color: white; }
32
- * ...
7
+ * Custom PostCSS handler for @each-fixed-color
33
8
  */
34
9
  export declare function eachFixedColor(atRule: AtRule): void;
@@ -8,116 +8,88 @@ exports.eachFixedColor = eachFixedColor;
8
8
  const postcss_1 = require("postcss");
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
+ const color_1 = require("../functions/color");
11
12
  /**
12
- * Custom PostCSS plugin handler for `@each-standard-color` at-rules.
13
- *
14
- * Example usage:
15
- *
16
- * @each-standard-color {
17
- * color: white;
18
- * }
19
- *
20
- * Will generate:
21
- *
22
- * .alpha { color: white; }
23
- * .beta { color: white; }
24
- * .gamma { color: white; }
25
- * ...
13
+ * Helper: safely clone nodes and replace {name} placeholders,
14
+ * evaluate fc() and sc() calls.
26
15
  */
27
- function eachStandardColor(atRule) {
28
- // Read seyuna.json from project root
29
- const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
30
- const fileContents = fs_1.default.readFileSync(jsonPath, "utf-8");
31
- const data = JSON.parse(fileContents);
32
- const hues = data.ui.theme.hues;
33
- const hueNamesSet = new Set(Object.keys(hues));
34
- // Guard against atRule.nodes being undefined
35
- const nodes = atRule.nodes ?? [];
36
- const generatedRules = [];
37
- // Helper to clone nodes and replace {name} placeholder
38
- const cloneNodesWithName = (name, nodeList = nodes) => nodeList.map((node) => {
16
+ function cloneNodesWithName(nodeList, name) {
17
+ return nodeList.flatMap((node) => {
39
18
  const cloned = node.clone();
40
19
  if (cloned.type === "decl") {
41
20
  const decl = cloned;
42
- decl.value = decl.value.replace(/\{name\}/g, name);
21
+ let value = decl.value.replace(/\{name\}/g, name);
22
+ if (/sc\(/.test(value)) {
23
+ const args = value
24
+ .match(/sc\(([^)]*)\)/)?.[1]
25
+ .split(",")
26
+ .map((s) => s.trim());
27
+ if (args)
28
+ value = (0, color_1.sc)(...args);
29
+ }
30
+ if (/fc\(/.test(value)) {
31
+ const args = value
32
+ .match(/fc\(([^)]*)\)/)?.[1]
33
+ .split(",")
34
+ .map((s) => s.trim());
35
+ if (args)
36
+ value = (0, color_1.fc)(...args);
37
+ }
38
+ decl.value = value;
39
+ return decl;
43
40
  }
44
- else if (cloned.type === "rule") {
41
+ if (cloned.type === "rule") {
45
42
  const rule = cloned;
43
+ if (!rule.selector)
44
+ return [];
46
45
  rule.selector = rule.selector.replace(/\{name\}/g, name);
47
- rule.nodes = cloneNodesWithName(name, rule.nodes || []);
46
+ rule.nodes = cloneNodesWithName(rule.nodes || [], name);
47
+ return rule;
48
48
  }
49
- else if (cloned.type === "atrule") {
50
- // If you want {name} in selectors inside at-rules too
49
+ if (cloned.type === "atrule") {
51
50
  cloned.params = cloned.params.replace(/\{name\}/g, name);
52
- cloned.nodes = cloneNodesWithName(name, cloned.nodes || []);
51
+ cloned.nodes = cloneNodesWithName(cloned.nodes || [], name);
52
+ return cloned.nodes.length ? cloned : [];
53
53
  }
54
- return cloned;
54
+ return [];
55
55
  });
56
- // Generate rules for each hue
57
- for (const hueName of hueNamesSet) {
56
+ }
57
+ /**
58
+ * Custom PostCSS handler for @each-standard-color
59
+ */
60
+ function eachStandardColor(atRule) {
61
+ const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
62
+ const data = JSON.parse(fs_1.default.readFileSync(jsonPath, "utf-8"));
63
+ const hues = data.ui.theme.hues;
64
+ const hueNames = Object.keys(hues);
65
+ const nodes = atRule.nodes ?? [];
66
+ const generatedRules = [];
67
+ for (const hueName of hueNames) {
58
68
  const rule = new postcss_1.Rule({ selector: `.${hueName}` });
59
- cloneNodesWithName(hueName).forEach((n) => rule.append(n));
60
- generatedRules.push(rule);
69
+ cloneNodesWithName(nodes, hueName).forEach((n) => rule.append(n));
70
+ if (rule.nodes.length)
71
+ generatedRules.push(rule);
61
72
  }
62
- // Replace the original @each-seyuna-color at-rule with all the generated rules
63
73
  atRule.replaceWith(...generatedRules);
64
74
  }
65
75
  /**
66
- * Custom PostCSS plugin handler for `@each-fixed-color` at-rules.
67
- *
68
- * Example usage:
69
- *
70
- * @each-fixed-color {
71
- * color: white;
72
- * }
73
- *
74
- * Will generate:
75
- *
76
- * .primary { color: white; }
77
- * .secondary { color: white; }
78
- * ...
76
+ * Custom PostCSS handler for @each-fixed-color
79
77
  */
80
78
  function eachFixedColor(atRule) {
81
- // Read seyuna.json from project root
82
79
  const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
83
- const fileContents = fs_1.default.readFileSync(jsonPath, "utf-8");
84
- const data = JSON.parse(fileContents);
80
+ const data = JSON.parse(fs_1.default.readFileSync(jsonPath, "utf-8"));
85
81
  const light_colors = data.ui.theme.light.colors;
86
82
  const dark_colors = data.ui.theme.dark.colors;
87
- const lightColorNamesSet = new Set(Object.keys(light_colors));
88
- const darkColorNamesSet = new Set(Object.keys(dark_colors));
89
- const mergedColorNamesSet = new Set([
90
- ...lightColorNamesSet,
91
- ...darkColorNamesSet,
92
- ]);
93
- // Guard against atRule.nodes being undefined
83
+ const mergedNames = [
84
+ ...new Set([...Object.keys(light_colors), ...Object.keys(dark_colors)]),
85
+ ];
94
86
  const nodes = atRule.nodes ?? [];
95
87
  const generatedRules = [];
96
- // Helper to clone nodes and replace {name} placeholder
97
- const cloneNodesWithName = (name, nodeList = nodes) => nodeList.map((node) => {
98
- const cloned = node.clone();
99
- if (cloned.type === "decl") {
100
- const decl = cloned;
101
- decl.value = decl.value.replace(/\{name\}/g, name);
102
- }
103
- else if (cloned.type === "rule") {
104
- const rule = cloned;
105
- rule.selector = rule.selector.replace(/\{name\}/g, name);
106
- rule.nodes = cloneNodesWithName(name, rule.nodes || []);
107
- }
108
- else if (cloned.type === "atrule") {
109
- // If you want {name} in selectors inside at-rules too
110
- cloned.params = cloned.params.replace(/\{name\}/g, name);
111
- cloned.nodes = cloneNodesWithName(name, cloned.nodes || []);
112
- }
113
- return cloned;
114
- });
115
- // Generate rules for mergedColorNamesSet
116
- for (const colorName of mergedColorNamesSet) {
88
+ for (const colorName of mergedNames) {
117
89
  const rule = new postcss_1.Rule({ selector: `.${colorName}` });
118
- cloneNodesWithName(colorName).forEach((n) => rule.append(n));
119
- generatedRules.push(rule);
90
+ cloneNodesWithName(nodes, colorName).forEach((n) => rule.append(n));
91
+ if (rule.nodes.length)
92
+ generatedRules.push(rule);
120
93
  }
121
- // Replace the original @each-seyuna-color at-rule with all the generated rules
122
94
  atRule.replaceWith(...generatedRules);
123
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seyuna/postcss",
3
- "version": "1.0.0-canary.11",
3
+ "version": "1.0.0-canary.13",
4
4
  "description": "Seyuna UI's postcss plugin",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,137 +2,99 @@ import { AtRule, Rule, ChildNode, Declaration } from "postcss";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { SeyunaConfig } from "../types";
5
+ import { fc, sc } from "../functions/color";
5
6
 
6
7
  /**
7
- * Custom PostCSS plugin handler for `@each-standard-color` at-rules.
8
- *
9
- * Example usage:
10
- *
11
- * @each-standard-color {
12
- * color: white;
13
- * }
14
- *
15
- * Will generate:
16
- *
17
- * .alpha { color: white; }
18
- * .beta { color: white; }
19
- * .gamma { color: white; }
20
- * ...
8
+ * Helper: safely clone nodes and replace {name} placeholders,
9
+ * evaluate fc() and sc() calls.
10
+ */
11
+ function cloneNodesWithName(nodeList: ChildNode[], name: string): ChildNode[] {
12
+ return nodeList.flatMap((node) => {
13
+ const cloned = node.clone();
14
+
15
+ if (cloned.type === "decl") {
16
+ const decl = cloned as Declaration;
17
+ let value = decl.value.replace(/\{name\}/g, name);
18
+
19
+ if (/sc\(/.test(value)) {
20
+ const args = value
21
+ .match(/sc\(([^)]*)\)/)?.[1]
22
+ .split(",")
23
+ .map((s) => s.trim());
24
+ if (args) value = sc(...(args as [string, string?, string?, string?]));
25
+ }
26
+
27
+ if (/fc\(/.test(value)) {
28
+ const args = value
29
+ .match(/fc\(([^)]*)\)/)?.[1]
30
+ .split(",")
31
+ .map((s) => s.trim());
32
+ if (args) value = fc(...(args as [string, string?, string?, string?]));
33
+ }
34
+
35
+ decl.value = value;
36
+ return decl;
37
+ }
38
+
39
+ if (cloned.type === "rule") {
40
+ const rule = cloned as Rule;
41
+ if (!rule.selector) return [];
42
+ rule.selector = rule.selector.replace(/\{name\}/g, name);
43
+ rule.nodes = cloneNodesWithName(rule.nodes || [], name);
44
+ return rule;
45
+ }
46
+
47
+ if (cloned.type === "atrule") {
48
+ cloned.params = cloned.params.replace(/\{name\}/g, name);
49
+ cloned.nodes = cloneNodesWithName(cloned.nodes || [], name);
50
+ return cloned.nodes.length ? cloned : [];
51
+ }
52
+
53
+ return [];
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Custom PostCSS handler for @each-standard-color
21
59
  */
22
60
  export function eachStandardColor(atRule: AtRule) {
23
- // Read seyuna.json from project root
24
61
  const jsonPath = path.resolve(process.cwd(), "seyuna.json");
25
- const fileContents = fs.readFileSync(jsonPath, "utf-8");
26
- const data: SeyunaConfig = JSON.parse(fileContents);
62
+ const data: SeyunaConfig = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
27
63
  const hues = data.ui.theme.hues;
28
- const hueNamesSet = new Set(Object.keys(hues));
64
+ const hueNames = Object.keys(hues);
29
65
 
30
- // Guard against atRule.nodes being undefined
31
66
  const nodes = atRule.nodes ?? [];
32
-
33
67
  const generatedRules: Rule[] = [];
34
68
 
35
- // Helper to clone nodes and replace {name} placeholder
36
- const cloneNodesWithName = (
37
- name: string,
38
- nodeList: ChildNode[] = nodes
39
- ): ChildNode[] =>
40
- nodeList.map((node) => {
41
- const cloned = node.clone();
42
-
43
- if (cloned.type === "decl") {
44
- const decl = cloned as Declaration;
45
- decl.value = decl.value.replace(/\{name\}/g, name);
46
- } else if (cloned.type === "rule") {
47
- const rule = cloned as Rule;
48
- rule.selector = rule.selector.replace(/\{name\}/g, name);
49
- rule.nodes = cloneNodesWithName(name, rule.nodes || []);
50
- } else if (cloned.type === "atrule") {
51
- // If you want {name} in selectors inside at-rules too
52
- cloned.params = cloned.params.replace(/\{name\}/g, name);
53
- cloned.nodes = cloneNodesWithName(name, cloned.nodes || []);
54
- }
55
-
56
- return cloned;
57
- });
58
-
59
- // Generate rules for each hue
60
- for (const hueName of hueNamesSet) {
69
+ for (const hueName of hueNames) {
61
70
  const rule = new Rule({ selector: `.${hueName}` });
62
- cloneNodesWithName(hueName).forEach((n) => rule.append(n));
63
- generatedRules.push(rule);
71
+ cloneNodesWithName(nodes, hueName).forEach((n) => rule.append(n));
72
+ if (rule.nodes.length) generatedRules.push(rule);
64
73
  }
65
74
 
66
- // Replace the original @each-seyuna-color at-rule with all the generated rules
67
75
  atRule.replaceWith(...generatedRules);
68
76
  }
69
77
 
70
78
  /**
71
- * Custom PostCSS plugin handler for `@each-fixed-color` at-rules.
72
- *
73
- * Example usage:
74
- *
75
- * @each-fixed-color {
76
- * color: white;
77
- * }
78
- *
79
- * Will generate:
80
- *
81
- * .primary { color: white; }
82
- * .secondary { color: white; }
83
- * ...
79
+ * Custom PostCSS handler for @each-fixed-color
84
80
  */
85
81
  export function eachFixedColor(atRule: AtRule) {
86
- // Read seyuna.json from project root
87
82
  const jsonPath = path.resolve(process.cwd(), "seyuna.json");
88
- const fileContents = fs.readFileSync(jsonPath, "utf-8");
89
- const data: SeyunaConfig = JSON.parse(fileContents);
83
+ const data: SeyunaConfig = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
90
84
  const light_colors = data.ui.theme.light.colors;
91
85
  const dark_colors = data.ui.theme.dark.colors;
92
- const lightColorNamesSet = new Set(Object.keys(light_colors));
93
- const darkColorNamesSet = new Set(Object.keys(dark_colors));
94
-
95
- const mergedColorNamesSet = new Set([
96
- ...lightColorNamesSet,
97
- ...darkColorNamesSet,
98
- ]);
86
+ const mergedNames = [
87
+ ...new Set([...Object.keys(light_colors), ...Object.keys(dark_colors)]),
88
+ ];
99
89
 
100
- // Guard against atRule.nodes being undefined
101
90
  const nodes = atRule.nodes ?? [];
102
-
103
91
  const generatedRules: Rule[] = [];
104
92
 
105
- // Helper to clone nodes and replace {name} placeholder
106
- const cloneNodesWithName = (
107
- name: string,
108
- nodeList: ChildNode[] = nodes
109
- ): ChildNode[] =>
110
- nodeList.map((node) => {
111
- const cloned = node.clone();
112
-
113
- if (cloned.type === "decl") {
114
- const decl = cloned as Declaration;
115
- decl.value = decl.value.replace(/\{name\}/g, name);
116
- } else if (cloned.type === "rule") {
117
- const rule = cloned as Rule;
118
- rule.selector = rule.selector.replace(/\{name\}/g, name);
119
- rule.nodes = cloneNodesWithName(name, rule.nodes || []);
120
- } else if (cloned.type === "atrule") {
121
- // If you want {name} in selectors inside at-rules too
122
- cloned.params = cloned.params.replace(/\{name\}/g, name);
123
- cloned.nodes = cloneNodesWithName(name, cloned.nodes || []);
124
- }
125
-
126
- return cloned;
127
- });
128
-
129
- // Generate rules for mergedColorNamesSet
130
- for (const colorName of mergedColorNamesSet) {
93
+ for (const colorName of mergedNames) {
131
94
  const rule = new Rule({ selector: `.${colorName}` });
132
- cloneNodesWithName(colorName).forEach((n) => rule.append(n));
133
- generatedRules.push(rule);
95
+ cloneNodesWithName(nodes, colorName).forEach((n) => rule.append(n));
96
+ if (rule.nodes.length) generatedRules.push(rule);
134
97
  }
135
98
 
136
- // Replace the original @each-seyuna-color at-rule with all the generated rules
137
99
  atRule.replaceWith(...generatedRules);
138
100
  }