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

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,12 @@
1
+ # [1.0.0-canary.14](https://github.com/seyuna-corp/seyuna-postcss/compare/v1.0.0-canary.13...v1.0.0-canary.14) (2025-10-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * bug in generateRules ([d5b66d3](https://github.com/seyuna-corp/seyuna-postcss/commit/d5b66d32786f554c0e4f7ea98ae526e7f7ac82c5))
7
+ * removed pnpm lock file that ([6d87d61](https://github.com/seyuna-corp/seyuna-postcss/commit/6d87d616a79a2a539b09dea26bc907dee1d437e2))
8
+ * updated lock file ([d52dcfb](https://github.com/seyuna-corp/seyuna-postcss/commit/d52dcfb2bccbd502d744ceb09aa83394df4ac057))
9
+
1
10
  # [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
11
 
3
12
 
@@ -1,9 +1,9 @@
1
1
  import { AtRule } from "postcss";
2
2
  /**
3
- * Custom PostCSS handler for @each-standard-color
3
+ * Handler for @each-standard-color
4
4
  */
5
5
  export declare function eachStandardColor(atRule: AtRule): void;
6
6
  /**
7
- * Custom PostCSS handler for @each-fixed-color
7
+ * Handler for @each-fixed-color
8
8
  */
9
9
  export declare function eachFixedColor(atRule: AtRule): void;
@@ -5,91 +5,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.eachStandardColor = eachStandardColor;
7
7
  exports.eachFixedColor = eachFixedColor;
8
- const postcss_1 = require("postcss");
9
8
  const fs_1 = __importDefault(require("fs"));
10
9
  const path_1 = __importDefault(require("path"));
11
- const color_1 = require("../functions/color");
10
+ const helpers_1 = require("../helpers");
12
11
  /**
13
- * Helper: safely clone nodes and replace {name} placeholders,
14
- * evaluate fc() and sc() calls.
15
- */
16
- function cloneNodesWithName(nodeList, name) {
17
- return nodeList.flatMap((node) => {
18
- const cloned = node.clone();
19
- if (cloned.type === "decl") {
20
- const decl = cloned;
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;
40
- }
41
- if (cloned.type === "rule") {
42
- const rule = cloned;
43
- if (!rule.selector)
44
- return [];
45
- rule.selector = rule.selector.replace(/\{name\}/g, name);
46
- rule.nodes = cloneNodesWithName(rule.nodes || [], name);
47
- return rule;
48
- }
49
- if (cloned.type === "atrule") {
50
- cloned.params = cloned.params.replace(/\{name\}/g, name);
51
- cloned.nodes = cloneNodesWithName(cloned.nodes || [], name);
52
- return cloned.nodes.length ? cloned : [];
53
- }
54
- return [];
55
- });
56
- }
57
- /**
58
- * Custom PostCSS handler for @each-standard-color
12
+ * Handler for @each-standard-color
59
13
  */
60
14
  function eachStandardColor(atRule) {
61
15
  const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
62
16
  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) {
68
- const rule = new postcss_1.Rule({ selector: `.${hueName}` });
69
- cloneNodesWithName(nodes, hueName).forEach((n) => rule.append(n));
70
- if (rule.nodes.length)
71
- generatedRules.push(rule);
72
- }
73
- atRule.replaceWith(...generatedRules);
17
+ const hueNames = Object.keys(data.ui.theme.hues);
18
+ const rules = (0, helpers_1.generateRules)(hueNames, atRule);
19
+ if (rules.length)
20
+ atRule.replaceWith(...rules);
21
+ else
22
+ atRule.remove();
74
23
  }
75
24
  /**
76
- * Custom PostCSS handler for @each-fixed-color
25
+ * Handler for @each-fixed-color
77
26
  */
78
27
  function eachFixedColor(atRule) {
79
28
  const jsonPath = path_1.default.resolve(process.cwd(), "seyuna.json");
80
29
  const data = JSON.parse(fs_1.default.readFileSync(jsonPath, "utf-8"));
81
- const light_colors = data.ui.theme.light.colors;
82
- const dark_colors = data.ui.theme.dark.colors;
83
30
  const mergedNames = [
84
- ...new Set([...Object.keys(light_colors), ...Object.keys(dark_colors)]),
31
+ ...new Set([
32
+ ...Object.keys(data.ui.theme.light.colors),
33
+ ...Object.keys(data.ui.theme.dark.colors),
34
+ ]),
85
35
  ];
86
- const nodes = atRule.nodes ?? [];
87
- const generatedRules = [];
88
- for (const colorName of mergedNames) {
89
- const rule = new postcss_1.Rule({ selector: `.${colorName}` });
90
- cloneNodesWithName(nodes, colorName).forEach((n) => rule.append(n));
91
- if (rule.nodes.length)
92
- generatedRules.push(rule);
93
- }
94
- atRule.replaceWith(...generatedRules);
36
+ const rules = (0, helpers_1.generateRules)(mergedNames, atRule);
37
+ if (rules.length)
38
+ atRule.replaceWith(...rules);
39
+ else
40
+ atRule.remove();
95
41
  }
@@ -0,0 +1,10 @@
1
+ import { Rule, ChildNode, AtRule } from "postcss";
2
+ /**
3
+ * Helper: clone nodes and replace {name} placeholders safely
4
+ * Returns only valid Rules or Declarations (never raw AtRules)
5
+ */
6
+ export declare function cloneNodes(nodes: ChildNode[], name: string): ChildNode[];
7
+ /**
8
+ * Generate CSS rules from a list of names
9
+ */
10
+ export declare function generateRules(names: string[], atRule: AtRule): Rule[];
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cloneNodes = cloneNodes;
4
+ exports.generateRules = generateRules;
5
+ const postcss_1 = require("postcss");
6
+ const color_1 = require("./functions/color");
7
+ /**
8
+ * Helper: clone nodes and replace {name} placeholders safely
9
+ * Returns only valid Rules or Declarations (never raw AtRules)
10
+ */
11
+ function cloneNodes(nodes, name) {
12
+ return nodes.flatMap((node) => {
13
+ const cloned = node.clone();
14
+ if (cloned.type === "decl") {
15
+ const decl = cloned;
16
+ let value = decl.value.replace(/\{name\}/g, name);
17
+ if (/sc\(/.test(value)) {
18
+ const args = value
19
+ .match(/sc\(([^)]*)\)/)?.[1]
20
+ .split(",")
21
+ .map((s) => s.trim());
22
+ if (args)
23
+ value = (0, color_1.sc)(...args);
24
+ }
25
+ if (/fc\(/.test(value)) {
26
+ const args = value
27
+ .match(/fc\(([^)]*)\)/)?.[1]
28
+ .split(",")
29
+ .map((s) => s.trim());
30
+ if (args)
31
+ value = (0, color_1.fc)(...args);
32
+ }
33
+ decl.value = value;
34
+ return decl;
35
+ }
36
+ if (cloned.type === "rule") {
37
+ const rule = cloned;
38
+ if (!rule.selector)
39
+ return [];
40
+ rule.selector = rule.selector.replace(/\{name\}/g, name);
41
+ // Recursively clone child nodes and only keep valid rules/decls
42
+ rule.nodes = cloneNodes(rule.nodes || [], name).filter((n) => n.type === "rule" || n.type === "decl");
43
+ if (!rule.nodes.length)
44
+ return [];
45
+ return rule;
46
+ }
47
+ // Ignore AtRules inside rules — they must be processed first
48
+ return [];
49
+ });
50
+ }
51
+ /**
52
+ * Generate CSS rules from a list of names
53
+ */
54
+ function generateRules(names, atRule) {
55
+ const nodes = atRule.nodes ?? [];
56
+ const generatedRules = [];
57
+ for (const name of names) {
58
+ const rule = new postcss_1.Rule({ selector: `&.${name}` });
59
+ cloneNodes(nodes, name).forEach((n) => {
60
+ if (n.type === "rule" && n.selector && n.nodes?.length)
61
+ rule.append(n);
62
+ if (n.type === "decl")
63
+ rule.append(n);
64
+ });
65
+ if (rule.nodes.length)
66
+ generatedRules.push(rule);
67
+ }
68
+ return generatedRules;
69
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@seyuna/postcss",
3
- "version": "1.0.0-canary.13",
3
+ "version": "1.0.0-canary.14",
4
4
  "description": "Seyuna UI's postcss plugin",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "build": "tsc"
8
+ "build": "tsc",
9
+ "dev": "tsc -w"
9
10
  },
10
11
  "keywords": [
11
12
  "postcss",
@@ -30,6 +31,7 @@
30
31
  "@semantic-release/release-notes-generator": "^14.0.3",
31
32
  "@types/node": "^20.0.0",
32
33
  "postcss": "^8.5.6",
34
+ "postcss-selector-parser": "^7.1.0",
33
35
  "semantic-release": "^24.2.7",
34
36
  "typescript": "^5.0.0"
35
37
  }
@@ -2,99 +2,36 @@ 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
+ import { generateRules } from "../helpers";
6
6
 
7
7
  /**
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
8
+ * Handler for @each-standard-color
59
9
  */
60
10
  export function eachStandardColor(atRule: AtRule) {
61
11
  const jsonPath = path.resolve(process.cwd(), "seyuna.json");
62
12
  const data: SeyunaConfig = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
63
- const hues = data.ui.theme.hues;
64
- const hueNames = Object.keys(hues);
65
-
66
- const nodes = atRule.nodes ?? [];
67
- const generatedRules: Rule[] = [];
13
+ const hueNames = Object.keys(data.ui.theme.hues);
68
14
 
69
- for (const hueName of hueNames) {
70
- const rule = new Rule({ selector: `.${hueName}` });
71
- cloneNodesWithName(nodes, hueName).forEach((n) => rule.append(n));
72
- if (rule.nodes.length) generatedRules.push(rule);
73
- }
74
-
75
- atRule.replaceWith(...generatedRules);
15
+ const rules = generateRules(hueNames, atRule);
16
+ if (rules.length) atRule.replaceWith(...rules);
17
+ else atRule.remove();
76
18
  }
77
19
 
78
20
  /**
79
- * Custom PostCSS handler for @each-fixed-color
21
+ * Handler for @each-fixed-color
80
22
  */
81
23
  export function eachFixedColor(atRule: AtRule) {
82
24
  const jsonPath = path.resolve(process.cwd(), "seyuna.json");
83
25
  const data: SeyunaConfig = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
84
- const light_colors = data.ui.theme.light.colors;
85
- const dark_colors = data.ui.theme.dark.colors;
26
+
86
27
  const mergedNames = [
87
- ...new Set([...Object.keys(light_colors), ...Object.keys(dark_colors)]),
28
+ ...new Set([
29
+ ...Object.keys(data.ui.theme.light.colors),
30
+ ...Object.keys(data.ui.theme.dark.colors),
31
+ ]),
88
32
  ];
89
33
 
90
- const nodes = atRule.nodes ?? [];
91
- const generatedRules: Rule[] = [];
92
-
93
- for (const colorName of mergedNames) {
94
- const rule = new Rule({ selector: `.${colorName}` });
95
- cloneNodesWithName(nodes, colorName).forEach((n) => rule.append(n));
96
- if (rule.nodes.length) generatedRules.push(rule);
97
- }
98
-
99
- atRule.replaceWith(...generatedRules);
34
+ const rules = generateRules(mergedNames, atRule);
35
+ if (rules.length) atRule.replaceWith(...rules);
36
+ else atRule.remove();
100
37
  }
package/src/helpers.ts ADDED
@@ -0,0 +1,73 @@
1
+ import { Rule, ChildNode, Declaration, AtRule } from "postcss";
2
+ import { fc, sc } from "./functions/color";
3
+
4
+ /**
5
+ * Helper: clone nodes and replace {name} placeholders safely
6
+ * Returns only valid Rules or Declarations (never raw AtRules)
7
+ */
8
+ export function cloneNodes(nodes: ChildNode[], name: string): ChildNode[] {
9
+ return nodes.flatMap((node) => {
10
+ const cloned = node.clone();
11
+
12
+ if (cloned.type === "decl") {
13
+ const decl = cloned as Declaration;
14
+ let value = decl.value.replace(/\{name\}/g, name);
15
+
16
+ if (/sc\(/.test(value)) {
17
+ const args = value
18
+ .match(/sc\(([^)]*)\)/)?.[1]
19
+ .split(",")
20
+ .map((s) => s.trim());
21
+ if (args) value = sc(...(args as [string, string?, string?, string?]));
22
+ }
23
+ if (/fc\(/.test(value)) {
24
+ const args = value
25
+ .match(/fc\(([^)]*)\)/)?.[1]
26
+ .split(",")
27
+ .map((s) => s.trim());
28
+ if (args) value = fc(...(args as [string, string?, string?, string?]));
29
+ }
30
+
31
+ decl.value = value;
32
+ return decl;
33
+ }
34
+
35
+ if (cloned.type === "rule") {
36
+ const rule = cloned as Rule;
37
+ if (!rule.selector) return [];
38
+
39
+ rule.selector = rule.selector.replace(/\{name\}/g, name);
40
+
41
+ // Recursively clone child nodes and only keep valid rules/decls
42
+ rule.nodes = cloneNodes(rule.nodes || [], name).filter(
43
+ (n) => n.type === "rule" || n.type === "decl"
44
+ );
45
+
46
+ if (!rule.nodes.length) return [];
47
+ return rule;
48
+ }
49
+
50
+ // Ignore AtRules inside rules — they must be processed first
51
+ return [];
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Generate CSS rules from a list of names
57
+ */
58
+ export function generateRules(names: string[], atRule: AtRule): Rule[] {
59
+ const nodes = atRule.nodes ?? [];
60
+ const generatedRules: Rule[] = [];
61
+
62
+ for (const name of names) {
63
+ const rule = new Rule({ selector: `&.${name}` });
64
+ cloneNodes(nodes, name).forEach((n) => {
65
+ if (n.type === "rule" && n.selector && n.nodes?.length) rule.append(n);
66
+ if (n.type === "decl") rule.append(n);
67
+ });
68
+
69
+ if (rule.nodes.length) generatedRules.push(rule);
70
+ }
71
+
72
+ return generatedRules;
73
+ }