@seyuna/postcss 1.0.0-canary.27 → 1.0.0-canary.29

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/dist/config.js CHANGED
@@ -1,87 +1,42 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
1
  const DEFAULT_OPTIONS = {
4
- configPath: 'seyuna.json',
5
- modeAttribute: 'data-mode',
2
+ configPath: "seyuna.json",
3
+ modeAttribute: "data-mode",
6
4
  strict: false,
7
5
  config: undefined,
8
6
  functions: undefined,
9
7
  };
10
- let cachedConfig = null;
11
- let cachedConfigPath = null;
12
8
  export function loadConfig(options = {}) {
13
9
  const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
14
10
  if (mergedOptions.config) {
15
11
  return { config: mergedOptions.config, options: mergedOptions };
16
12
  }
17
- const configPath = path.resolve(process.cwd(), mergedOptions.configPath);
18
- // Cache config if it's the same path
19
- if (cachedConfig && cachedConfigPath === configPath) {
20
- return { config: cachedConfig, options: mergedOptions };
21
- }
22
- try {
23
- if (!fs.existsSync(configPath)) {
24
- if (mergedOptions.strict) {
25
- throw new Error(`Seyuna config not found at ${configPath}`);
26
- }
27
- return {
28
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } },
29
- options: mergedOptions,
30
- };
31
- }
32
- const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
33
- cachedConfig = data;
34
- cachedConfigPath = configPath;
35
- return { config: data, options: mergedOptions };
36
- }
37
- catch (error) {
38
- if (mergedOptions.strict) {
39
- throw error;
40
- }
41
- console.warn(`[Seyuna PostCSS] Warning: Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
42
- return {
43
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } },
44
- options: mergedOptions,
45
- };
46
- }
13
+ // Return base empty config - we'll populate it from CSS
14
+ return {
15
+ config: {
16
+ ui: {
17
+ theme: {
18
+ hues: {},
19
+ colors: {},
20
+ light: {
21
+ chroma: 0,
22
+ lightness: 0,
23
+ background: { lightness: 0, chroma: 0, hue: 0 },
24
+ text: { lightness: 0, chroma: 0, hue: 0 },
25
+ colors: {},
26
+ },
27
+ dark: {
28
+ chroma: 0,
29
+ lightness: 0,
30
+ background: { lightness: 0, chroma: 0, hue: 0 },
31
+ text: { lightness: 0, chroma: 0, hue: 0 },
32
+ colors: {},
33
+ },
34
+ },
35
+ },
36
+ },
37
+ options: mergedOptions,
38
+ };
47
39
  }
48
40
  export async function loadConfigAsync(options = {}) {
49
- const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
50
- if (mergedOptions.config) {
51
- return { config: mergedOptions.config, options: mergedOptions };
52
- }
53
- const configPath = path.resolve(process.cwd(), mergedOptions.configPath);
54
- // Cache config if it's the same path
55
- if (cachedConfig && cachedConfigPath === configPath) {
56
- return { config: cachedConfig, options: mergedOptions };
57
- }
58
- try {
59
- try {
60
- await fs.promises.access(configPath);
61
- }
62
- catch {
63
- if (mergedOptions.strict) {
64
- throw new Error(`Seyuna config not found at ${configPath}`);
65
- }
66
- return {
67
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } },
68
- options: mergedOptions,
69
- };
70
- }
71
- const content = await fs.promises.readFile(configPath, 'utf-8');
72
- const data = JSON.parse(content);
73
- cachedConfig = data;
74
- cachedConfigPath = configPath;
75
- return { config: data, options: mergedOptions };
76
- }
77
- catch (error) {
78
- if (mergedOptions.strict) {
79
- throw error;
80
- }
81
- console.warn(`[Seyuna PostCSS] Warning: Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
82
- return {
83
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } },
84
- options: mergedOptions,
85
- };
86
- }
41
+ return loadConfig(options);
87
42
  }
package/dist/types.d.ts CHANGED
@@ -34,14 +34,12 @@ export type Mode = "system" | "light" | "dark";
34
34
  */
35
35
  export interface UI {
36
36
  theme: Theme;
37
- mode: Mode;
38
37
  output_dir?: string;
39
38
  }
40
39
  /**
41
40
  * Root configuration structure for a Seyuna project.
42
41
  */
43
42
  export interface SeyunaConfig {
44
- license?: string;
45
43
  ui?: UI;
46
44
  }
47
45
  export type FunctionMap = Record<string, (context: PluginContext, ...args: string[]) => string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seyuna/postcss",
3
- "version": "1.0.0-canary.27",
3
+ "version": "1.0.0-canary.29",
4
4
  "description": "Seyuna UI's postcss plugin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,78 @@
1
+ import { PluginContext } from "../types.js";
2
+
3
+ /**
4
+ * Handler for @config "seyuna"
5
+ * Parses the configuration defined directly in CSS.
6
+ */
7
+ export function handleConfig(atRule: any, context: PluginContext) {
8
+ const params = atRule.params.replace(/['"]/g, "");
9
+
10
+ if (params === "seyuna") {
11
+ const { config } = context;
12
+ if (!config.ui) {
13
+ config.ui = {
14
+ theme: {
15
+ hues: {},
16
+ colors: {},
17
+ light: {
18
+ chroma: 0,
19
+ lightness: 0,
20
+ background: { lightness: 0, chroma: 0, hue: 0 },
21
+ text: { lightness: 0, chroma: 0, hue: 0 },
22
+ colors: {},
23
+ },
24
+ dark: {
25
+ chroma: 0,
26
+ lightness: 0,
27
+ background: { lightness: 0, chroma: 0, hue: 0 },
28
+ text: { lightness: 0, chroma: 0, hue: 0 },
29
+ colors: {},
30
+ },
31
+ },
32
+ };
33
+ }
34
+
35
+ const theme = config.ui.theme;
36
+
37
+ atRule.walkDecls((decl: any) => {
38
+ const prop = decl.prop;
39
+ const value = decl.value;
40
+
41
+ // Hues: --hue-alpha: 0;
42
+ if (prop.startsWith("--hue-")) {
43
+ const name = prop.replace("--hue-", "");
44
+ theme.hues[name] = parseFloat(value);
45
+ }
46
+
47
+ // Fixed Colors: --color-primary: 0.66 0.26 240;
48
+ else if (prop.startsWith("--color-")) {
49
+ const name = prop.replace("--color-", "");
50
+ const [l, c, h] = value.split(/\s+/).map(parseFloat);
51
+ theme.colors[name] = { lightness: l, chroma: c, hue: h };
52
+ }
53
+
54
+ // Theme Palettes: --light-lightness: 0.66;
55
+ else if (prop.startsWith("--light-") || prop.startsWith("--dark-")) {
56
+ const isLight = prop.startsWith("--light-");
57
+ const palette = isLight ? theme.light : theme.dark;
58
+ const key = prop.replace(isLight ? "--light-" : "--dark-", "");
59
+
60
+ if (key === "lightness") palette.lightness = parseFloat(value);
61
+ else if (key === "chroma") palette.chroma = parseFloat(value);
62
+ else if (key === "background" || key === "text") {
63
+ const [l, c, h] = value.split(/\s+/).map(parseFloat);
64
+ (palette as any)[key] = { lightness: l, chroma: c, hue: h };
65
+ } else {
66
+ // Custom colors in palette: --light-surface: 1 0 0;
67
+ // Also supports overrides like --light-color-primary: 0.8 0.1 240;
68
+ const cleanKey = key.replace(/^color-/, "");
69
+ const [l, c, h] = value.split(/\s+/).map(parseFloat);
70
+ palette.colors[cleanKey] = { lightness: l, chroma: c, hue: h };
71
+ }
72
+ }
73
+ });
74
+
75
+ // Remove the at-rule from CSS output
76
+ atRule.remove();
77
+ }
78
+ }
@@ -10,10 +10,6 @@ import postcss from "postcss";
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = path.dirname(__filename);
12
12
 
13
- const STANDARD_HUES = [
14
- 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega'
15
- ];
16
-
17
13
  /**
18
14
  * Handler for @import "seyuna"
19
15
  * Injects the core Seyuna Design System variables and base styles
@@ -56,14 +52,12 @@ export function handleImport(atRule: any, context: PluginContext) {
56
52
  // 2. Global Hues and Base Colors (:root)
57
53
  const rootRule = new Rule({ selector: ":root", source: atRule.source });
58
54
 
59
- // Process all hues from config plus defaults
60
- const allHues = new Set([...STANDARD_HUES, ...Object.keys(theme.hues || {})]);
61
- allHues.forEach((name) => {
62
- const i = STANDARD_HUES.indexOf(name);
63
- const defaultValue = i !== -1 ? i * 15 : 0;
64
- const value = (theme.hues && theme.hues[name] !== undefined) ? theme.hues[name] : defaultValue;
65
- rootRule.append(new Declaration({ prop: `--${name}-hue`, value: String(value) }));
66
- });
55
+ // Process all hues from config
56
+ if (theme.hues) {
57
+ for (const [name, value] of Object.entries(theme.hues)) {
58
+ rootRule.append(new Declaration({ prop: `--${name}-hue`, value: String(value) }));
59
+ }
60
+ }
67
61
 
68
62
  // Add shared colors from theme.colors
69
63
  if (theme.colors) {
@@ -3,6 +3,7 @@ import { eachStandardColor, eachFixedColor } from "./color.js";
3
3
  import container from "./container.js";
4
4
  import { light, dark } from "./color-scheme.js";
5
5
  import { handleImport } from "./import.js";
6
+ import { handleConfig } from "./config.js";
6
7
  import { PluginContext } from "../types.js";
7
8
 
8
9
  // Each handler has a name (matches the at-rule) and the function
@@ -13,6 +14,7 @@ export interface AtRuleHandler {
13
14
 
14
15
  // Ordered array ensures execution order
15
16
  export const atRuleHandlers: AtRuleHandler[] = [
17
+ { name: "config", handler: handleConfig },
16
18
  { name: "import", handler: handleImport },
17
19
  { name: "each-standard-color", handler: eachStandardColor },
18
20
  { name: "each-fixed-color", handler: eachFixedColor },
package/src/config.ts CHANGED
@@ -1,104 +1,63 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { SeyunaConfig, PluginOptions, PluginContext, FunctionMap } from './types.js';
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import {
4
+ SeyunaConfig,
5
+ PluginOptions,
6
+ PluginContext,
7
+ FunctionMap,
8
+ } from "./types.js";
4
9
 
5
10
  // Re-export types for backwards compatibility
6
- export type { PluginOptions, PluginContext, FunctionMap } from './types.js';
11
+ export type { PluginOptions, PluginContext, FunctionMap } from "./types.js";
7
12
 
8
13
  const DEFAULT_OPTIONS: Required<PluginOptions> = {
9
- configPath: 'seyuna.json',
10
- modeAttribute: 'data-mode',
14
+ configPath: "seyuna.json",
15
+ modeAttribute: "data-mode",
11
16
  strict: false,
12
17
  config: undefined as any,
13
18
  functions: undefined as any,
14
19
  };
15
20
 
16
- let cachedConfig: SeyunaConfig | null = null;
17
- let cachedConfigPath: string | null = null;
18
-
19
- export function loadConfig(options: PluginOptions = {}): { config: SeyunaConfig, options: Required<PluginOptions> } {
21
+ export function loadConfig(options: PluginOptions = {}): {
22
+ config: SeyunaConfig;
23
+ options: Required<PluginOptions>;
24
+ } {
20
25
  const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
21
26
 
22
27
  if (mergedOptions.config) {
23
28
  return { config: mergedOptions.config, options: mergedOptions };
24
29
  }
25
30
 
26
- const configPath = path.resolve(process.cwd(), mergedOptions.configPath);
27
-
28
- // Cache config if it's the same path
29
- if (cachedConfig && cachedConfigPath === configPath) {
30
- return { config: cachedConfig, options: mergedOptions };
31
- }
32
-
33
- try {
34
- if (!fs.existsSync(configPath)) {
35
- if (mergedOptions.strict) {
36
- throw new Error(`Seyuna config not found at ${configPath}`);
37
- }
38
- return {
39
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } } as any,
40
- options: mergedOptions,
41
- };
42
- }
43
-
44
- const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
45
- cachedConfig = data;
46
- cachedConfigPath = configPath;
47
-
48
- return { config: data, options: mergedOptions };
49
- } catch (error) {
50
- if (mergedOptions.strict) {
51
- throw error;
52
- }
53
- console.warn(`[Seyuna PostCSS] Warning: Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
54
- return {
55
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } } as any,
56
- options: mergedOptions,
57
- };
58
- }
31
+ // Return base empty config - we'll populate it from CSS
32
+ return {
33
+ config: {
34
+ ui: {
35
+ theme: {
36
+ hues: {},
37
+ colors: {},
38
+ light: {
39
+ chroma: 0,
40
+ lightness: 0,
41
+ background: { lightness: 0, chroma: 0, hue: 0 },
42
+ text: { lightness: 0, chroma: 0, hue: 0 },
43
+ colors: {},
44
+ },
45
+ dark: {
46
+ chroma: 0,
47
+ lightness: 0,
48
+ background: { lightness: 0, chroma: 0, hue: 0 },
49
+ text: { lightness: 0, chroma: 0, hue: 0 },
50
+ colors: {},
51
+ },
52
+ },
53
+ },
54
+ } as any,
55
+ options: mergedOptions,
56
+ };
59
57
  }
60
58
 
61
- export async function loadConfigAsync(options: PluginOptions = {}): Promise<{ config: SeyunaConfig, options: Required<PluginOptions> }> {
62
- const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
63
-
64
- if (mergedOptions.config) {
65
- return { config: mergedOptions.config, options: mergedOptions };
66
- }
67
-
68
- const configPath = path.resolve(process.cwd(), mergedOptions.configPath);
69
-
70
- // Cache config if it's the same path
71
- if (cachedConfig && cachedConfigPath === configPath) {
72
- return { config: cachedConfig, options: mergedOptions };
73
- }
74
-
75
- try {
76
- try {
77
- await fs.promises.access(configPath);
78
- } catch {
79
- if (mergedOptions.strict) {
80
- throw new Error(`Seyuna config not found at ${configPath}`);
81
- }
82
- return {
83
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } } as any,
84
- options: mergedOptions,
85
- };
86
- }
87
-
88
- const content = await fs.promises.readFile(configPath, 'utf-8');
89
- const data = JSON.parse(content);
90
- cachedConfig = data;
91
- cachedConfigPath = configPath;
92
-
93
- return { config: data, options: mergedOptions };
94
- } catch (error) {
95
- if (mergedOptions.strict) {
96
- throw error;
97
- }
98
- console.warn(`[Seyuna PostCSS] Warning: Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
99
- return {
100
- config: { ui: { theme: { hues: {}, light: { colors: {} }, dark: { colors: {} } } } } as any,
101
- options: mergedOptions,
102
- };
103
- }
59
+ export async function loadConfigAsync(
60
+ options: PluginOptions = {},
61
+ ): Promise<{ config: SeyunaConfig; options: Required<PluginOptions> }> {
62
+ return loadConfig(options);
104
63
  }
package/src/types.ts CHANGED
@@ -41,7 +41,6 @@ export type Mode = "system" | "light" | "dark";
41
41
  */
42
42
  export interface UI {
43
43
  theme: Theme;
44
- mode: Mode;
45
44
  output_dir?: string;
46
45
  }
47
46
 
@@ -49,7 +48,6 @@ export interface UI {
49
48
  * Root configuration structure for a Seyuna project.
50
49
  */
51
50
  export interface SeyunaConfig {
52
- license?: string;
53
51
  ui?: UI;
54
52
  }
55
53