@seyuna/postcss 1.0.0-canary.15 → 1.0.0-canary.16

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.
@@ -1,61 +1,26 @@
1
1
  import { PluginContext } from "../config";
2
2
 
3
- /**
4
- * Utility to parse an oklch string and return its parts
5
- */
6
- function parseOklch(color: string) {
7
- const match = color.match(/oklch\(([^ ]+) ([^ ]+) ([^ /]+)(?: \/ ([^)]+))?\)/);
8
- if (!match) return null;
9
- return {
10
- l: match[1],
11
- c: match[2],
12
- h: match[3],
13
- a: match[4] || "1",
14
- };
15
- }
16
-
17
- export function sc(
18
- context: PluginContext,
19
- name: string,
20
- alpha?: string,
21
- lightness?: string,
22
- chroma?: string
23
- ) {
24
- let a: string = alpha && alpha !== "null" ? alpha : "1";
25
- let l: string = lightness && lightness !== "null" ? lightness : "var(--lightness)";
26
- let c: string = chroma && chroma !== "null" ? chroma : "var(--chroma)";
27
- let h: string = `var(--${name}-hue)`;
28
-
29
- return `oklch(${l} ${c} ${h} / ${a})`;
30
- }
31
-
32
- export function fc(
33
- context: PluginContext,
34
- name: string,
35
- alpha?: string,
36
- lightness?: string,
37
- chroma?: string
38
- ) {
39
- let a: string = alpha && alpha !== "null" ? alpha : "1";
40
- let l: string = lightness && lightness !== "null" ? lightness : `var(--${name}-lightness)`;
41
- let c: string = chroma && chroma !== "null" ? chroma : `var(--${name}-chroma)`;
42
- let h: string = `var(--${name}-hue)`;
43
-
44
- return `oklch(${l} ${c} ${h} / ${a})`;
45
- }
46
-
47
3
  /**
48
4
  * Resolves a color name to its CSS variables based on its type (standard or fixed)
49
5
  */
50
- function getColorVariables(context: PluginContext, color: string) {
6
+ function getColorVariables(context: PluginContext, color: string, type?: 'sc' | 'fc') {
51
7
  const { config } = context;
52
8
  const hues = config?.ui?.theme?.hues || {};
53
9
  const colors = config?.ui?.theme?.colors || {};
54
10
  const lightColors = config?.ui?.theme?.light?.colors || {};
55
11
  const darkColors = config?.ui?.theme?.dark?.colors || {};
56
12
 
57
- // Check if it's a standard color (sc)
58
- if (color in hues) {
13
+ const isStandard = color in hues;
14
+ const isFixed = color in colors || color in lightColors || color in darkColors;
15
+
16
+ if (type === 'sc' && !isStandard) {
17
+ throw new Error(`Standard color '${color}' not found in seyuna.json hues`);
18
+ }
19
+ if (type === 'fc' && !isFixed) {
20
+ throw new Error(`Fixed color '${color}' not found in seyuna.json colors`);
21
+ }
22
+
23
+ if (isStandard) {
59
24
  return {
60
25
  l: "var(--lightness)",
61
26
  c: "var(--chroma)",
@@ -63,8 +28,7 @@ function getColorVariables(context: PluginContext, color: string) {
63
28
  };
64
29
  }
65
30
 
66
- // Check if it's a fixed color (fc)
67
- if (color in colors || color in lightColors || color in darkColors) {
31
+ if (isFixed) {
68
32
  return {
69
33
  l: `var(--${color}-lightness)`,
70
34
  c: `var(--${color}-chroma)`,
@@ -72,72 +36,55 @@ function getColorVariables(context: PluginContext, color: string) {
72
36
  };
73
37
  }
74
38
 
75
- // Fallback to assume it's a fixed color if unknown but used as a name
76
- return {
77
- l: `var(--${color}-lightness)`,
78
- c: `var(--${color}-chroma)`,
79
- h: `var(--${color}-hue)`,
80
- };
39
+ throw new Error(`Color '${color}' not found in seyuna.json`);
81
40
  }
82
41
 
83
- export function alpha(context: PluginContext, color: string, value: string) {
84
- const parsed = parseOklch(color);
85
- if (parsed) {
86
- return `oklch(${parsed.l} ${parsed.c} ${parsed.h} / ${value})`;
87
- }
42
+ export function sc(
43
+ context: PluginContext,
44
+ name: string,
45
+ alpha?: string,
46
+ lightness?: string,
47
+ chroma?: string
48
+ ) {
49
+ const vars = getColorVariables(context, name, 'sc');
50
+ const a = alpha && alpha !== "null" ? alpha : "1";
51
+ const l = lightness && lightness !== "null" ? lightness : vars.l;
52
+ const c = chroma && chroma !== "null" ? chroma : vars.c;
88
53
 
54
+ return `oklch(${l} ${c} ${vars.h} / ${a})`;
55
+ }
56
+
57
+ export function fc(
58
+ context: PluginContext,
59
+ name: string,
60
+ alpha?: string,
61
+ lightness?: string,
62
+ chroma?: string
63
+ ) {
64
+ const vars = getColorVariables(context, name, 'fc');
65
+ const a = alpha && alpha !== "null" ? alpha : "1";
66
+ const l = lightness && lightness !== "null" ? lightness : vars.l;
67
+ const c = chroma && chroma !== "null" ? chroma : vars.c;
68
+
69
+ return `oklch(${l} ${c} ${vars.h} / ${a})`;
70
+ }
71
+
72
+ export function alpha(context: PluginContext, color: string, value: string) {
89
73
  const { l, c, h } = getColorVariables(context, color);
90
74
  return `oklch(${l} ${c} ${h} / ${value})`;
91
75
  }
92
76
 
93
77
  export function lighten(context: PluginContext, color: string, amount: string) {
94
- const parsed = parseOklch(color);
95
-
96
- if (parsed) {
97
- if (parsed.l.startsWith('var(')) {
98
- return `oklch(calc(${parsed.l} + ${amount}) ${parsed.c} ${parsed.h} / ${parsed.a})`;
99
- }
100
-
101
- const lValue = parseFloat(parsed.l);
102
- const amtValue = parseFloat(amount);
103
- return `oklch(${Math.min(1, lValue + amtValue)} ${parsed.c} ${parsed.h} / ${parsed.a})`;
104
- }
105
-
106
78
  const { l, c, h } = getColorVariables(context, color);
107
79
  return `oklch(calc(${l} + ${amount}) ${c} ${h} / 1)`;
108
80
  }
109
81
 
110
82
  export function darken(context: PluginContext, color: string, amount: string) {
111
- const parsed = parseOklch(color);
112
-
113
- if (parsed) {
114
- if (parsed.l.startsWith('var(')) {
115
- return `oklch(calc(${parsed.l} - ${amount}) ${parsed.c} ${parsed.h} / ${parsed.a})`;
116
- }
117
-
118
- const lValue = parseFloat(parsed.l);
119
- const amtValue = parseFloat(amount);
120
- return `oklch(${Math.max(0, lValue - amtValue)} ${parsed.c} ${parsed.h} / ${parsed.a})`;
121
- }
122
-
123
83
  const { l, c, h } = getColorVariables(context, color);
124
84
  return `oklch(calc(${l} - ${amount}) ${c} ${h} / 1)`;
125
85
  }
126
86
 
127
87
  export function contrast(context: PluginContext, color: string) {
128
- const parsed = parseOklch(color);
129
- let l: string;
130
-
131
- if (parsed) {
132
- l = parsed.l;
133
- } else {
134
- const vars = getColorVariables(context, color);
135
- l = vars.l;
136
- }
137
-
138
- // Dynamic CSS contrast logic:
139
- // (L - 0.6) * -1000 will be:
140
- // - very negative if L > 0.6 (clamped to 0 / black)
141
- // - very positive if L < 0.6 (clamped to 1 / white)
88
+ const { l } = getColorVariables(context, color);
142
89
  return `oklch(calc((${l} - 0.6) * -1000) 0 0)`;
143
90
  }
package/src/plugin.ts CHANGED
@@ -13,7 +13,6 @@ export const dynamicFunctionsPlugin: PluginCreator<ConfigOptions> = (
13
13
  config,
14
14
  options,
15
15
  functions: fnMap,
16
- mixins: {},
17
16
  };
18
17
 
19
18
  return {
@@ -9,6 +9,10 @@ const mockConfig = {
9
9
  primary: "200",
10
10
  secondary: "100"
11
11
  },
12
+ colors: {
13
+ white: { lightness: 1, chroma: 0, hue: 0 },
14
+ black: { lightness: 0, chroma: 0, hue: 0 }
15
+ },
12
16
  light: {
13
17
  lightness: 0.66,
14
18
  colors: {
@@ -28,8 +32,9 @@ const mockConfig = {
28
32
  }
29
33
  };
30
34
 
31
- async function run(input: string, opts = {}) {
32
- return postcss([plugin(opts)]).process(input, { from: undefined });
35
+ async function run(input: string, opts: any = {}) {
36
+ const mergedOpts = { config: mockConfig, ...opts };
37
+ return postcss([plugin(mergedOpts)]).process(input, { from: undefined });
33
38
  }
34
39
 
35
40
  describe('Seyuna PostCSS Plugin', () => {
@@ -47,23 +52,23 @@ describe('Seyuna PostCSS Plugin', () => {
47
52
  expect(result.css).toContain(output);
48
53
  });
49
54
 
50
- it('processes alpha() function', async () => {
51
- const input = '.test { color: alpha(oklch(0.5 0.1 200 / 1), 0.5); }';
52
- const output = 'color: oklch(0.5 0.1 200 / 0.5)';
55
+ it('processes alpha() function with color name', async () => {
56
+ const input = '.test { color: alpha(primary, 0.5); }';
57
+ const output = 'color: oklch(var(--lightness) var(--chroma) var(--primary-hue) / 0.5)';
53
58
  const result = await run(input);
54
59
  expect(result.css).toContain(output);
55
60
  });
56
61
 
57
- it('processes lighten() function', async () => {
58
- const input = '.test { color: lighten(oklch(0.5 0.1 200 / 1), 0.1); }';
59
- const output = 'color: oklch(0.6 0.1 200 / 1)';
62
+ it('processes lighten() function with color name', async () => {
63
+ const input = '.test { color: lighten(primary, 0.1); }';
64
+ const output = 'color: oklch(calc(var(--lightness) + 0.1) var(--chroma) var(--primary-hue) / 1)';
60
65
  const result = await run(input);
61
66
  expect(result.css).toContain(output);
62
67
  });
63
68
 
64
- it('processes lighten() with CSS variables', async () => {
65
- const input = '.test { color: lighten(oklch(var(--l) 0.1 200 / 1), 0.1); }';
66
- const output = 'color: oklch(calc(var(--l) + 0.1) 0.1 200 / 1)';
69
+ it('processes darken() function with color name', async () => {
70
+ const input = '.test { color: darken(primary, 0.1); }';
71
+ const output = 'color: oklch(calc(var(--lightness) - 0.1) var(--chroma) var(--primary-hue) / 1)';
67
72
  const result = await run(input);
68
73
  expect(result.css).toContain(output);
69
74
  });
@@ -90,108 +95,21 @@ describe('Seyuna PostCSS Plugin', () => {
90
95
  expect(result.css).toContain('[data-mode="light"] &');
91
96
  });
92
97
 
93
- it('processes mixins using @define-mixin and @apply', async () => {
94
- const input = `
95
- @define-mixin btn {
96
- padding: 1rem;
97
- border-radius: 4px;
98
- }
99
- .primary-btn {
100
- @apply btn;
101
- color: white;
102
- }
103
- `;
104
- const result = await run(input);
105
- expect(result.css).toContain('.primary-btn {');
106
- expect(result.css).toContain('padding: 1rem');
107
- expect(result.css).toContain('border-radius: 4px');
108
- expect(result.css).toContain('color: white');
109
- expect(result.css).not.toContain('@define-mixin');
110
- });
111
-
112
- it('processes @if directive with literal', async () => {
113
- const input = `
114
- .test {
115
- @if (true) {
116
- color: red;
117
- }
118
- @if (false) {
119
- color: blue;
120
- }
121
- }
122
- `;
123
- const result = await run(input);
124
- expect(result.css).toContain('color: red');
125
- expect(result.css).not.toContain('color: blue');
126
- });
127
-
128
- it('processes @if directive with theme()', async () => {
129
- const input = `
130
- .glass {
131
- @if (theme(ui.features.glass)) {
132
- backdrop-filter: blur(10px);
133
- }
134
- }
135
- `;
136
- const config = {
137
- ui: {
138
- features: {
139
- glass: "true"
140
- }
141
- }
142
- };
143
- const result = await run(input, { config });
144
- expect(result.css).toContain('backdrop-filter: blur(10px)');
145
- });
146
-
147
- it('resolves custom media tokens', async () => {
148
- const input = `
149
- @media (--tablet) {
150
- .sidebar { display: block; }
151
- }
152
- `;
153
- const config = {
154
- media: {
155
- tablet: "(min-width: 768px)"
156
- }
157
- };
158
- const result = await run(input, { config });
159
- expect(result.css).toContain('@media (min-width: 768px)');
160
- });
161
-
162
- it('handles @custom-media directive', async () => {
163
- const input = `
164
- @custom-media --mobile (max-width: 480px);
165
- @media (--mobile) {
166
- .full-width { width: 100%; }
167
- }
168
- `;
169
- const result = await run(input);
170
- expect(result.css).toContain('@media (max-width: 480px)');
171
- });
172
-
173
- it('processes alpha() with color name', async () => {
98
+ it('processes alpha() with standard color', async () => {
174
99
  const input = '.test { color: alpha(primary, 0.5); }';
175
- const output = 'color: oklch(var(--primary-lightness) var(--primary-chroma) var(--primary-hue) / 0.5)';
176
- const result = await run(input);
177
- expect(result.css).toContain(output);
178
- });
179
-
180
- it('processes lighten() with color name', async () => {
181
- const input = '.test { color: lighten(primary, 0.1); }';
182
- const output = 'color: oklch(calc(var(--primary-lightness) + 0.1) var(--primary-chroma) var(--primary-hue) / 1)';
183
- const result = await run(input);
100
+ const output = 'color: oklch(var(--lightness) var(--chroma) var(--primary-hue) / 0.5)';
101
+ const result = await run(input, { config: mockConfig });
184
102
  expect(result.css).toContain(output);
185
103
  });
186
104
 
187
- it('processes darken() with color name', async () => {
188
- const input = '.test { color: darken(primary, 0.1); }';
189
- const output = 'color: oklch(calc(var(--primary-lightness) - 0.1) var(--primary-chroma) var(--primary-hue) / 1)';
190
- const result = await run(input);
105
+ it('processes alpha() with fixed color', async () => {
106
+ const input = '.test { color: alpha(surface, 0.5); }';
107
+ const output = 'color: oklch(var(--surface-lightness) var(--surface-chroma) var(--surface-hue) / 0.5)';
108
+ const result = await run(input, { config: mockConfig });
191
109
  expect(result.css).toContain(output);
192
110
  });
193
111
 
194
- it('processes contrast() with color name', async () => {
112
+ it('processes contrast() with fixed color', async () => {
195
113
  const input = '.test { color: contrast(surface); }';
196
114
  const output = 'color: oklch(calc((var(--surface-lightness) - 0.6) * -1000) 0 0)';
197
115
  const result = await run(input, { config: mockConfig });
@@ -205,47 +123,21 @@ describe('Seyuna PostCSS Plugin', () => {
205
123
  expect(result.css).toContain(output);
206
124
  });
207
125
 
208
- it('processes alpha() with standard color (sc)', async () => {
209
- const input = '.test { color: alpha(primary, 0.5); }';
210
- // 'primary' is in hues in mockConfig, so it should use var(--lightness) and var(--chroma)
211
- const output = 'color: oklch(var(--lightness) var(--chroma) var(--primary-hue) / 0.5)';
212
- const result = await run(input, { config: mockConfig });
213
- expect(result.css).toContain(output);
126
+ it('throws error for unknown standard color in strict mode', async () => {
127
+ const input = '.test { color: sc(unknown); }';
128
+ await expect(run(input, { config: mockConfig, strict: true }))
129
+ .rejects.toThrow(/Standard color 'unknown' not found/);
214
130
  });
215
131
 
216
- it('processes alpha() with fixed color (fc)', async () => {
217
- const input = '.test { color: alpha(surface, 0.5); }';
218
- // 'surface' is in light.colors/dark.colors in mockConfig, so it should use its own lightness/chroma
219
- const output = 'color: oklch(var(--surface-lightness) var(--surface-chroma) var(--surface-hue) / 0.5)';
220
- const result = await run(input, { config: mockConfig });
221
- expect(result.css).toContain(output);
222
- });
223
-
224
- it('processes nested functions: contrast(fc(white))', async () => {
225
- const input = '.test { color: contrast(fc(white)); }';
226
- // fc(white) -> oklch(var(--white-lightness) ...)
227
- // contrast extracts lightness -> var(--white-lightness)
228
- const output = 'color: oklch(calc((var(--white-lightness) - 0.6) * -1000) 0 0)';
229
- const result = await run(input, { config: mockConfig });
230
- expect(result.css).toContain(output);
132
+ it('throws error for unknown fixed color in strict mode', async () => {
133
+ const input = '.test { color: fc(unknown); }';
134
+ await expect(run(input, { config: mockConfig, strict: true }))
135
+ .rejects.toThrow(/Fixed color 'unknown' not found/);
231
136
  });
232
137
 
233
- it('processes nested functions: alpha(sc(primary), 0.5)', async () => {
234
- const input = '.test { color: alpha(sc(primary), 0.5); }';
235
- // sc(primary) -> oklch(var(--lightness) var(--chroma) var(--primary-hue) / 1)
236
- // alpha(..., 0.5) extracts components and reapplies alpha
237
- const output = 'color: oklch(var(--lightness) var(--chroma) var(--primary-hue) / 0.5)';
238
- const result = await run(input, { config: mockConfig });
239
- expect(result.css).toContain(output);
240
- });
241
-
242
- it('processes multiple nested functions: contrast(alpha(fc(surface), 1))', async () => {
243
- const input = '.test { color: contrast(alpha(fc(surface), 1)); }';
244
- // inner-most: fc(surface) -> oklch(var(--surface-lightness) ...)
245
- // next: alpha(..., 1) -> oklch(var(--surface-lightness) ...)
246
- // outer: contrast extracts lightness -> var(--surface-lightness)
247
- const output = 'color: oklch(calc((var(--surface-lightness) - 0.6) * -1000) 0 0)';
248
- const result = await run(input, { config: mockConfig });
249
- expect(result.css).toContain(output);
138
+ it('throws error for unknown color in alpha() in strict mode', async () => {
139
+ const input = '.test { color: alpha(unknown, 0.5); }';
140
+ await expect(run(input, { config: mockConfig, strict: true }))
141
+ .rejects.toThrow(/Color 'unknown' not found in seyuna.json/);
250
142
  });
251
143
  });
@@ -1,6 +0,0 @@
1
- import { AtRule } from "postcss";
2
- import { PluginContext } from "../config";
3
- /**
4
- * Handler for @if (condition) { ... }
5
- */
6
- export declare function conditional(atRule: AtRule, context: PluginContext): void;
@@ -1,29 +0,0 @@
1
- import { processFunctions } from "../parser";
2
- /**
3
- * Handler for @if (condition) { ... }
4
- */
5
- export function conditional(atRule, context) {
6
- const { functions: fnMap } = context;
7
- // Clean up params (remove parentheses if present)
8
- let condition = atRule.params.trim().replace(/^\(|\)$/g, '').trim();
9
- // Process functions in the condition (e.g., theme access)
10
- const evaluated = processFunctions(condition, fnMap, atRule, context).trim();
11
- // Simple truthy evaluation
12
- const isFalsy = !evaluated ||
13
- evaluated === 'false' ||
14
- evaluated === '0' ||
15
- evaluated === 'null' ||
16
- evaluated === 'undefined';
17
- if (!isFalsy) {
18
- // Ungroup the nodes
19
- if (atRule.nodes && atRule.nodes.length > 0) {
20
- atRule.replaceWith(...atRule.nodes.map(n => n.clone()));
21
- }
22
- else {
23
- atRule.remove();
24
- }
25
- }
26
- else {
27
- atRule.remove();
28
- }
29
- }
@@ -1,15 +0,0 @@
1
- import { AtRule } from "postcss";
2
- import { PluginContext } from "../config";
3
- /**
4
- * Global store for CSS-defined custom media (if needed)
5
- * However, we primarily use the config.
6
- */
7
- /**
8
- * Handler for @media at-rules to resolve custom media tokens
9
- */
10
- export declare function resolveCustomMedia(atRule: AtRule, context: PluginContext): void;
11
- /**
12
- * [Future-proofing] Handler for @custom-media --name query
13
- * Adds to the config-like store for the current run
14
- */
15
- export declare function defineCustomMedia(atRule: AtRule, context: PluginContext): void;
@@ -1,40 +0,0 @@
1
- /**
2
- * Global store for CSS-defined custom media (if needed)
3
- * However, we primarily use the config.
4
- */
5
- /**
6
- * Handler for @media at-rules to resolve custom media tokens
7
- */
8
- export function resolveCustomMedia(atRule, context) {
9
- const { config } = context;
10
- const mediaTokens = config.media || {};
11
- // Regex to find --tokens anywhere
12
- let params = atRule.params;
13
- const tokenRegex = /--[a-zA-Z0-9\-_]+/g;
14
- params = params.replace(tokenRegex, (match) => {
15
- const tokenName = match.slice(2);
16
- if (mediaTokens[tokenName]) {
17
- return mediaTokens[tokenName];
18
- }
19
- return match;
20
- });
21
- // Clean up double parentheses like ((...)) if any
22
- params = params.replace(/\(\((.+)\)\)/g, '($1)');
23
- atRule.params = params;
24
- }
25
- /**
26
- * [Future-proofing] Handler for @custom-media --name query
27
- * Adds to the config-like store for the current run
28
- */
29
- export function defineCustomMedia(atRule, context) {
30
- const match = atRule.params.match(/^(--[a-zA-Z0-9\-_]+)\s+(.+)$/);
31
- if (match) {
32
- const name = match[1].slice(2);
33
- const query = match[2];
34
- if (!context.config.media) {
35
- context.config.media = {};
36
- }
37
- context.config.media[name] = query;
38
- }
39
- atRule.remove();
40
- }
@@ -1,10 +0,0 @@
1
- import { AtRule } from "postcss";
2
- import { PluginContext } from "../config";
3
- /**
4
- * Handler for @define-mixin [name] { ... }
5
- */
6
- export declare function defineMixin(atRule: AtRule, context: PluginContext): void;
7
- /**
8
- * Handler for @apply [name]
9
- */
10
- export declare function applyMixin(atRule: AtRule, context: PluginContext): void;
@@ -1,37 +0,0 @@
1
- import { reportError } from "../errors";
2
- /**
3
- * Handler for @define-mixin [name] { ... }
4
- */
5
- export function defineMixin(atRule, context) {
6
- const name = atRule.params.trim();
7
- if (!name) {
8
- reportError("Mixin name is required", atRule, context);
9
- return;
10
- }
11
- // Store the nodes (cloned)
12
- context.mixins[name] = atRule.nodes?.map(n => n.clone()) || [];
13
- // Remove the at-rule from the output
14
- atRule.remove();
15
- }
16
- /**
17
- * Handler for @apply [name]
18
- */
19
- export function applyMixin(atRule, context) {
20
- const name = atRule.params.trim();
21
- if (!name) {
22
- reportError("Mixin name is required for @apply", atRule, context);
23
- return;
24
- }
25
- const mixinNodes = context.mixins[name];
26
- if (!mixinNodes) {
27
- reportError(`Mixin "${name}" not found`, atRule, context);
28
- return;
29
- }
30
- // Inject the nodes, ensuring they have the correct source for mapping
31
- const nodesToInject = mixinNodes.map(n => {
32
- const cloned = n.clone();
33
- cloned.source = atRule.source;
34
- return cloned;
35
- });
36
- atRule.replaceWith(...nodesToInject);
37
- }
@@ -1,34 +0,0 @@
1
- import { AtRule } from "postcss";
2
- import { PluginContext } from "../config";
3
- import { processFunctions } from "../parser";
4
-
5
- /**
6
- * Handler for @if (condition) { ... }
7
- */
8
- export function conditional(atRule: AtRule, context: PluginContext) {
9
- const { functions: fnMap } = context;
10
-
11
- // Clean up params (remove parentheses if present)
12
- let condition = atRule.params.trim().replace(/^\(|\)$/g, '').trim();
13
-
14
- // Process functions in the condition (e.g., theme access)
15
- const evaluated = processFunctions(condition, fnMap, atRule, context).trim();
16
-
17
- // Simple truthy evaluation
18
- const isFalsy = !evaluated ||
19
- evaluated === 'false' ||
20
- evaluated === '0' ||
21
- evaluated === 'null' ||
22
- evaluated === 'undefined';
23
-
24
- if (!isFalsy) {
25
- // Ungroup the nodes
26
- if (atRule.nodes && atRule.nodes.length > 0) {
27
- atRule.replaceWith(...atRule.nodes.map(n => n.clone()));
28
- } else {
29
- atRule.remove();
30
- }
31
- } else {
32
- atRule.remove();
33
- }
34
- }
@@ -1,50 +0,0 @@
1
- import { AtRule } from "postcss";
2
- import { PluginContext } from "../config";
3
-
4
- /**
5
- * Global store for CSS-defined custom media (if needed)
6
- * However, we primarily use the config.
7
- */
8
-
9
- /**
10
- * Handler for @media at-rules to resolve custom media tokens
11
- */
12
- export function resolveCustomMedia(atRule: AtRule, context: PluginContext) {
13
- const { config } = context;
14
- const mediaTokens = (config as any).media || {};
15
-
16
- // Regex to find --tokens anywhere
17
- let params = atRule.params;
18
- const tokenRegex = /--[a-zA-Z0-9\-_]+/g;
19
-
20
- params = params.replace(tokenRegex, (match) => {
21
- const tokenName = match.slice(2);
22
- if (mediaTokens[tokenName]) {
23
- return mediaTokens[tokenName];
24
- }
25
- return match;
26
- });
27
-
28
- // Clean up double parentheses like ((...)) if any
29
- params = params.replace(/\(\((.+)\)\)/g, '($1)');
30
-
31
- atRule.params = params;
32
- }
33
-
34
- /**
35
- * [Future-proofing] Handler for @custom-media --name query
36
- * Adds to the config-like store for the current run
37
- */
38
- export function defineCustomMedia(atRule: AtRule, context: PluginContext) {
39
- const match = atRule.params.match(/^(--[a-zA-Z0-9\-_]+)\s+(.+)$/);
40
- if (match) {
41
- const name = match[1].slice(2);
42
- const query = match[2];
43
-
44
- if (!(context.config as any).media) {
45
- (context.config as any).media = {};
46
- }
47
- (context.config as any).media[name] = query;
48
- }
49
- atRule.remove();
50
- }