@airframeui/build 0.2.0

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.
@@ -0,0 +1,274 @@
1
+ /**
2
+ * PostCSS plugin for @airframeui/build
3
+ * Processes @af bp() / @af-apply and replaces inlined @layer af.responsive when
4
+ * breakpoints are customized. Writes generated CSS only when outputDir is set.
5
+ */
6
+ import { buildResponsiveCss, getDefaultBreakpoints } from '@airframeui/core/generate-responsive';
7
+ import { emitConfigTokenCss } from '@airframeui/theme';
8
+ import postcss from 'postcss';
9
+ import postcssImport from 'postcss-import';
10
+ import { createRequire } from 'node:module';
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { loadConfig } from './load-config.js';
14
+ export function isOutputDirEnabled(outputDir) {
15
+ return typeof outputDir === 'string' && outputDir.length > 0;
16
+ }
17
+ function resolveOutputDir(outputDir, cwd) {
18
+ return path.isAbsolute(outputDir) ? outputDir : path.resolve(cwd, outputDir);
19
+ }
20
+ export async function resolveBuildOptions(options = {}) {
21
+ const cwd = options.cwd ?? process.cwd();
22
+ const fileConfig = options.ignoreConfig ? {} : (await loadConfig(cwd)).config;
23
+ const breakpoints = options.breakpoints ?? fileConfig.breakpoints;
24
+ const useDefaultBreakpoints = options.useDefaultBreakpoints ?? fileConfig.useDefaultBreakpoints ?? true;
25
+ const outputDir = options.outputDir !== undefined ? options.outputDir : fileConfig.outputDir;
26
+ const hasCustomBreakpoints = Boolean(breakpoints && Object.keys(breakpoints).length > 0);
27
+ const defaults = getDefaultBreakpoints();
28
+ const finalBreakpoints = useDefaultBreakpoints
29
+ ? { ...defaults, ...(breakpoints ?? {}) }
30
+ : hasCustomBreakpoints && breakpoints
31
+ ? { ...breakpoints }
32
+ : { ...defaults };
33
+ const tokenConfig = {};
34
+ if (hasCustomBreakpoints && breakpoints)
35
+ tokenConfig.breakpoints = breakpoints;
36
+ return {
37
+ ...(breakpoints ? { breakpoints } : {}),
38
+ useDefaultBreakpoints,
39
+ outputDir,
40
+ cwd,
41
+ finalBreakpoints,
42
+ hasCustomBreakpoints,
43
+ tokenConfig,
44
+ };
45
+ }
46
+ function writeGeneratedFiles(opts) {
47
+ const tokenCss = emitConfigTokenCss(opts.tokenConfig);
48
+ if (!opts.hasCustomBreakpoints && !tokenCss) {
49
+ return [];
50
+ }
51
+ const written = [];
52
+ const outputDirPath = resolveOutputDir(opts.outputDir, opts.cwd);
53
+ fs.mkdirSync(outputDirPath, { recursive: true });
54
+ if (opts.hasCustomBreakpoints) {
55
+ const responsivePath = path.join(outputDirPath, 'responsive.css');
56
+ fs.writeFileSync(responsivePath, buildResponsiveCss(opts.finalBreakpoints));
57
+ written.push(responsivePath);
58
+ }
59
+ if (tokenCss) {
60
+ const tokensPath = path.join(outputDirPath, 'tokens.css');
61
+ fs.writeFileSync(tokensPath, tokenCss);
62
+ written.push(tokensPath);
63
+ }
64
+ return written;
65
+ }
66
+ function generatedResponsiveLayer(breakpoints) {
67
+ const parsed = postcss.parse(buildResponsiveCss(breakpoints));
68
+ const layer = parsed.nodes.find((node) => node.type === 'atrule' && node.name === 'layer' && node.params.trim() === 'af.responsive');
69
+ return layer;
70
+ }
71
+ /** Replace inlined `@layer af.responsive` (from core.css) so default 768px `@md` is not left behind. */
72
+ function replaceInlinedResponsiveLayer(root, breakpoints) {
73
+ const layer = generatedResponsiveLayer(breakpoints);
74
+ if (!layer)
75
+ return false;
76
+ let replaced = false;
77
+ root.walkAtRules('layer', (atRule) => {
78
+ if (atRule.params.trim() !== 'af.responsive' || !atRule.nodes)
79
+ return;
80
+ atRule.replaceWith(layer.clone());
81
+ replaced = true;
82
+ });
83
+ return replaced;
84
+ }
85
+ function patchInlinedBreakpointTokens(root, breakpoints) {
86
+ root.walkAtRules('layer', (atRule) => {
87
+ if (atRule.params.trim() !== 'af.tokens')
88
+ return;
89
+ atRule.walkDecls((decl) => {
90
+ for (const [name, value] of Object.entries(breakpoints)) {
91
+ if (decl.prop === `--af-bp-${name}` || decl.prop === `--af-${name}`) {
92
+ decl.value = value;
93
+ }
94
+ }
95
+ });
96
+ });
97
+ }
98
+ async function inlineAtImports(root, result, importOptions, cwd) {
99
+ if (importOptions === false)
100
+ return;
101
+ let hasImport = false;
102
+ root.walkAtRules('import', () => {
103
+ hasImport = true;
104
+ return false;
105
+ });
106
+ if (!hasImport)
107
+ return;
108
+ const userOptions = { ...(importOptions ?? {}) };
109
+ const processOpts = {
110
+ ...(result.opts.from ? { from: result.opts.from } : {}),
111
+ ...(result.opts.to ? { to: result.opts.to } : {}),
112
+ };
113
+ const resolveImport = typeof userOptions['resolve'] === 'function'
114
+ ? userOptions['resolve']
115
+ : (id, basedir) => {
116
+ if (id.startsWith('.') || path.isAbsolute(id)) {
117
+ return path.resolve(basedir, id);
118
+ }
119
+ const require = createRequire(path.join(basedir, 'noop.js'));
120
+ return require.resolve(id, { paths: [basedir, cwd] });
121
+ };
122
+ await postcss([
123
+ postcssImport({
124
+ ...userOptions,
125
+ resolve: resolveImport,
126
+ }),
127
+ ]).process(root, processOpts);
128
+ }
129
+ export function postcssAirframe(options = {}) {
130
+ return {
131
+ postcssPlugin: 'postcss-airframe',
132
+ async Once(root, { result }) {
133
+ await inlineAtImports(root, result, options.import, options.cwd ?? process.cwd());
134
+ const resolved = await resolveBuildOptions(options);
135
+ const { finalBreakpoints } = resolved;
136
+ const utilityClassMap = new Map();
137
+ root.walkRules((rule) => {
138
+ const selector = rule.selector;
139
+ const utilityMatch = selector.match(/^\.(af-[a-z0-9-]+)$/);
140
+ if (utilityMatch && utilityMatch[1]) {
141
+ const className = utilityMatch[1];
142
+ const existing = utilityClassMap.get(className) || [];
143
+ existing.push({
144
+ nodes: rule.nodes ? [...rule.nodes] : [],
145
+ selector,
146
+ });
147
+ utilityClassMap.set(className, existing);
148
+ }
149
+ const pseudoMatch = selector.match(/^\.(af-[a-z0-9-]+)((?:::?[a-z-]+(?:\([^)]*\))?)+)$/);
150
+ if (pseudoMatch && pseudoMatch[1]) {
151
+ const className = pseudoMatch[1];
152
+ const existing = utilityClassMap.get(className) || [];
153
+ existing.push({
154
+ nodes: rule.nodes ? [...rule.nodes] : [],
155
+ selector,
156
+ });
157
+ utilityClassMap.set(className, existing);
158
+ }
159
+ });
160
+ const processApplyDirective = (atRule, utilityClass) => {
161
+ const parentRule = atRule.parent;
162
+ if (!parentRule || parentRule.type !== 'rule') {
163
+ result.warn(`@af-apply can only be used inside a CSS rule`, {
164
+ node: atRule,
165
+ });
166
+ atRule.remove();
167
+ return;
168
+ }
169
+ const utilityRules = utilityClassMap.get(utilityClass);
170
+ if (!utilityRules || utilityRules.length === 0) {
171
+ result.warn(`Utility class "${utilityClass}" not found. Import "@airframeui/core/core.css" before @af-apply so the class is in the CSS tree.`, {
172
+ node: atRule,
173
+ });
174
+ atRule.remove();
175
+ return;
176
+ }
177
+ utilityRules.forEach((utilityRule) => {
178
+ const hasPseudo = utilityRule.selector.includes('::') || utilityRule.selector.includes(':');
179
+ if (hasPseudo) {
180
+ const pseudoMatch = utilityRule.selector.match(/(::?[a-z-]+(?:\([^)]*\))?)/);
181
+ if (pseudoMatch) {
182
+ const nestedRule = postcss.rule({
183
+ selector: `&${pseudoMatch[1]}`,
184
+ });
185
+ utilityRule.nodes.forEach((node) => {
186
+ nestedRule.append(node.clone());
187
+ });
188
+ parentRule.insertBefore(atRule, nestedRule);
189
+ }
190
+ else {
191
+ utilityRule.nodes.forEach((node) => {
192
+ parentRule.insertBefore(atRule, node.clone());
193
+ });
194
+ }
195
+ }
196
+ else {
197
+ utilityRule.nodes.forEach((node) => {
198
+ parentRule.insertBefore(atRule, node.clone());
199
+ });
200
+ }
201
+ });
202
+ atRule.remove();
203
+ };
204
+ root.walkAtRules('af', (atRule) => {
205
+ const params = atRule.params.trim();
206
+ const bpMatch = params.match(/^bp\(([^)]+)\)$/);
207
+ if (bpMatch && bpMatch[1]) {
208
+ const bpName = bpMatch[1].trim();
209
+ const bpValue = finalBreakpoints[bpName];
210
+ if (!bpValue) {
211
+ result.warn(`Unknown breakpoint: ${bpName}. Available: ${Object.keys(finalBreakpoints).join(', ')}`, { node: atRule });
212
+ }
213
+ else {
214
+ const mediaRule = postcss.atRule({
215
+ name: 'media',
216
+ params: `(min-width: ${bpValue})`,
217
+ });
218
+ atRule.nodes?.forEach((node) => {
219
+ mediaRule.append(node.clone());
220
+ });
221
+ atRule.replaceWith(mediaRule);
222
+ }
223
+ }
224
+ else if (params.startsWith('apply')) {
225
+ const applyMatch = params.match(/^apply\(([^)]+)\)$/);
226
+ if (applyMatch && applyMatch[1]) {
227
+ processApplyDirective(atRule, applyMatch[1].trim());
228
+ }
229
+ else {
230
+ result.warn(`Invalid @af apply directive: ${params}. Expected format: @af apply(utility-class-name)`, {
231
+ node: atRule,
232
+ });
233
+ atRule.remove();
234
+ }
235
+ }
236
+ else {
237
+ result.warn(`Invalid @af directive: ${params}. Expected format: @af bp(breakpoint-name) or @af apply(utility-class-name)`, {
238
+ node: atRule,
239
+ });
240
+ }
241
+ });
242
+ root.walkAtRules('af-apply', (atRule) => {
243
+ const utilityClass = atRule.params.trim();
244
+ if (utilityClass) {
245
+ processApplyDirective(atRule, utilityClass);
246
+ }
247
+ else {
248
+ result.warn(`@af-apply requires a utility class name`, {
249
+ node: atRule,
250
+ });
251
+ atRule.remove();
252
+ }
253
+ });
254
+ if (resolved.hasCustomBreakpoints) {
255
+ replaceInlinedResponsiveLayer(root, resolved.finalBreakpoints);
256
+ if (resolved.breakpoints) {
257
+ patchInlinedBreakpointTokens(root, resolved.breakpoints);
258
+ }
259
+ }
260
+ if (isOutputDirEnabled(resolved.outputDir)) {
261
+ writeGeneratedFiles({
262
+ cwd: resolved.cwd,
263
+ outputDir: resolved.outputDir,
264
+ finalBreakpoints: resolved.finalBreakpoints,
265
+ hasCustomBreakpoints: resolved.hasCustomBreakpoints,
266
+ tokenConfig: resolved.tokenConfig,
267
+ });
268
+ }
269
+ },
270
+ };
271
+ }
272
+ postcssAirframe.postcss = true;
273
+ export default postcssAirframe;
274
+ //# sourceMappingURL=postcss-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postcss-plugin.js","sourceRoot":"","sources":["../src/postcss-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * PostCSS plugin export (`@airframeui/build/postcss`).
3
+ * Default export is required for `plugins: { '@airframeui/build/postcss': opts }`.
4
+ */
5
+ export { postcssAirframe, type AirframeBuildOptions } from './postcss-plugin.js';
6
+ export { default } from './postcss-plugin.js';
7
+ //# sourceMappingURL=postcss.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postcss.d.ts","sourceRoot":"","sources":["../src/postcss.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * PostCSS plugin export (`@airframeui/build/postcss`).
3
+ * Default export is required for `plugins: { '@airframeui/build/postcss': opts }`.
4
+ */
5
+ export { postcssAirframe } from './postcss-plugin.js';
6
+ export { default } from './postcss-plugin.js';
7
+ //# sourceMappingURL=postcss.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postcss.js","sourceRoot":"","sources":["../src/postcss.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAA6B,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface RunBuildOptions {
2
+ cwd?: string;
3
+ config?: string;
4
+ input?: string;
5
+ output?: string;
6
+ }
7
+ export interface RunBuildResult {
8
+ exitCode: number;
9
+ messages: string[];
10
+ warnings: string[];
11
+ outputPath?: string;
12
+ }
13
+ /**
14
+ * Optionally write generated CSS when `outputDir` is set, and/or transform
15
+ * `@af bp()` / `@af-apply` when `--input` is passed. Prefer the PostCSS plugin
16
+ * with `@import "@airframeui/core/core.css"` — no `outputDir` needed.
17
+ */
18
+ export declare function runBuild(options?: RunBuildOptions): Promise<RunBuildResult>;
19
+ export declare const DEFAULT_CONFIG = "/** @type {import('@airframeui/build').AirframeBuildOptions} */\nmodule.exports = {\n // Only set values that differ from @airframeui/tokens.\n breakpoints: {\n md: '900px',\n },\n useDefaultBreakpoints: true,\n // Optional. PostCSS keeps @import \"@airframeui/core/core.css\" without this.\n // outputDir: '.airframeui',\n};\n";
20
+ export declare function writeDefaultConfig(cwd?: string): {
21
+ created: boolean;
22
+ path: string;
23
+ };
24
+ //# sourceMappingURL=run-build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-build.d.ts","sourceRoot":"","sources":["../src/run-build.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAmErF;AAED,eAAO,MAAM,cAAc,qVAU1B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,GAAG,SAAgB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAO1F"}
@@ -0,0 +1,87 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import postcss from 'postcss';
4
+ import { loadConfig } from './load-config.js';
5
+ import { isOutputDirEnabled, postcssAirframe, } from './postcss-plugin.js';
6
+ /**
7
+ * Optionally write generated CSS when `outputDir` is set, and/or transform
8
+ * `@af bp()` / `@af-apply` when `--input` is passed. Prefer the PostCSS plugin
9
+ * with `@import "@airframeui/core/core.css"` — no `outputDir` needed.
10
+ */
11
+ export async function runBuild(options = {}) {
12
+ const cwd = options.cwd ?? process.cwd();
13
+ const { config } = await loadConfig(cwd, options.config);
14
+ const messages = [];
15
+ const warnings = [];
16
+ const pluginOptions = {
17
+ ignoreConfig: true,
18
+ cwd,
19
+ useDefaultBreakpoints: config.useDefaultBreakpoints ?? true,
20
+ ...(config.outputDir !== undefined ? { outputDir: config.outputDir } : {}),
21
+ };
22
+ if (config.breakpoints)
23
+ pluginOptions.breakpoints = config.breakpoints;
24
+ const inputFile = options.input;
25
+ const inputPath = inputFile
26
+ ? path.isAbsolute(inputFile)
27
+ ? inputFile
28
+ : path.join(cwd, inputFile)
29
+ : undefined;
30
+ if (inputPath && !fs.existsSync(inputPath)) {
31
+ return {
32
+ exitCode: 1,
33
+ messages: [`Input file not found: ${inputPath}`],
34
+ warnings: [],
35
+ };
36
+ }
37
+ const css = inputPath ? fs.readFileSync(inputPath, 'utf8') : '/* @airframeui/build */\n';
38
+ const result = await postcss([postcssAirframe(pluginOptions)]).process(css, {
39
+ from: inputPath ?? path.join(cwd, 'airframe.config.js'),
40
+ });
41
+ for (const warning of result.warnings()) {
42
+ warnings.push(warning.text);
43
+ }
44
+ if (inputPath) {
45
+ const outputPath = options.output
46
+ ? path.isAbsolute(options.output)
47
+ ? options.output
48
+ : path.join(cwd, options.output)
49
+ : inputPath;
50
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
51
+ fs.writeFileSync(outputPath, result.css);
52
+ messages.push(outputPath === inputPath ? `Wrote ${outputPath} (in-place)` : `Wrote ${outputPath}`);
53
+ const outputDir = pluginOptions.outputDir;
54
+ if (isOutputDirEnabled(outputDir)) {
55
+ messages.push(`Generated files in ${outputDir}`);
56
+ }
57
+ return { exitCode: 0, messages, warnings, outputPath };
58
+ }
59
+ const outputDir = pluginOptions.outputDir;
60
+ if (isOutputDirEnabled(outputDir)) {
61
+ messages.push(`Generated files in ${outputDir}`);
62
+ }
63
+ else {
64
+ messages.push('Nothing to do. Use the PostCSS plugin with @import "@airframeui/core/core.css", or set outputDir to write generated CSS.');
65
+ }
66
+ return { exitCode: 0, messages, warnings };
67
+ }
68
+ export const DEFAULT_CONFIG = `/** @type {import('@airframeui/build').AirframeBuildOptions} */
69
+ module.exports = {
70
+ // Only set values that differ from @airframeui/tokens.
71
+ breakpoints: {
72
+ md: '900px',
73
+ },
74
+ useDefaultBreakpoints: true,
75
+ // Optional. PostCSS keeps @import "@airframeui/core/core.css" without this.
76
+ // outputDir: '.airframeui',
77
+ };
78
+ `;
79
+ export function writeDefaultConfig(cwd = process.cwd()) {
80
+ const configPath = path.join(cwd, 'airframe.config.js');
81
+ if (fs.existsSync(configPath)) {
82
+ return { created: false, path: configPath };
83
+ }
84
+ fs.writeFileSync(configPath, DEFAULT_CONFIG);
85
+ return { created: true, path: configPath };
86
+ }
87
+ //# sourceMappingURL=run-build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-build.js","sourceRoot":"","sources":["../src/run-build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type ThemeReport } from '@airframeui/theme';
2
+ export declare function getFlag(args: string[], name: string): string | undefined;
3
+ export declare function hasFlag(args: string[], name: string): boolean;
4
+ export declare function getFlags(args: string[], name: string): string[];
5
+ export declare function exitCodeFromReport(report: ThemeReport, strict: boolean): number;
6
+ export declare function printDiagnostics(report: ThemeReport, strict: boolean): number;
7
+ export declare const THEME_HELP = "\n@airframeui/build \u2014 theme commands\n\nUsage:\n af theme generate --from <path|url> [-o theme.css] [--format auto] [--report report.json] [--strict]\n af theme lint <theme.css> [--strict]\n af theme validate <tokens.json|theme.css> [--format auto] [--report report.json] [--strict]\n af theme export [-o dir] [--config path] [--css file] [--strict]\n\nFormats:\n auto | dtcg | tokens-studio | css | figma-variables | style-dictionary | airframe-spec\n\nNotes:\n --from accepts local files or HTTPS URLs to JSON/CSS artifacts (not HTML pages).\n export writes tokens.dtcg.json + airframe.resolver.json from catalog, config, and theme.files.\n";
8
+ export interface ThemeCommandResult {
9
+ exitCode: number;
10
+ stdout?: string;
11
+ }
12
+ /**
13
+ * Run theme subcommands. Returns an exit code instead of calling process.exit
14
+ * so unit tests can exercise the CLI without terminating the process.
15
+ */
16
+ export declare function runThemeCommand(args: string[]): Promise<ThemeCommandResult>;
17
+ //# sourceMappingURL=theme-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-cli.d.ts","sourceRoot":"","sources":["../src/theme-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAW3B,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxE;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAK/E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAmB7E;AAED,eAAO,MAAM,UAAU,mpBAetB,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgJjF"}
@@ -0,0 +1,198 @@
1
+ import { emitDtcg, emitResolver, generateFromContent, lintThemeCss, summarizeReport, } from '@airframeui/theme';
2
+ import { generateTheme, loadFromPathOrUrl, resolveThemeFromFiles, writeOutput, } from '@airframeui/theme/node';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { loadConfig } from './load-config.js';
6
+ export function getFlag(args, name) {
7
+ const eq = args.find((a) => a.startsWith(`${name}=`));
8
+ if (eq)
9
+ return eq.slice(name.length + 1);
10
+ const idx = args.indexOf(name);
11
+ if (idx >= 0 && idx + 1 < args.length)
12
+ return args[idx + 1];
13
+ return undefined;
14
+ }
15
+ export function hasFlag(args, name) {
16
+ return args.includes(name);
17
+ }
18
+ export function getFlags(args, name) {
19
+ const out = [];
20
+ for (let i = 0; i < args.length; i++) {
21
+ const a = args[i];
22
+ if (!a)
23
+ continue;
24
+ if (a === name && args[i + 1] && !args[i + 1].startsWith('-')) {
25
+ out.push(args[++i]);
26
+ }
27
+ else if (a.startsWith(`${name}=`)) {
28
+ out.push(a.slice(name.length + 1));
29
+ }
30
+ }
31
+ return out;
32
+ }
33
+ export function exitCodeFromReport(report, strict) {
34
+ const summary = summarizeReport(report);
35
+ if (!summary.ok)
36
+ return 1;
37
+ if (strict && summary.warnings > 0)
38
+ return 1;
39
+ return 0;
40
+ }
41
+ export function printDiagnostics(report, strict) {
42
+ for (const d of report.diagnostics) {
43
+ const prefix = d.severity === 'error' ? '✗' : d.severity === 'warning' ? '⚠' : '•';
44
+ console.log(`${prefix} [${d.severity}] ${d.message}`);
45
+ }
46
+ if (report.unmapped.length > 0) {
47
+ console.log(`\nUnmapped (${report.unmapped.length}):`);
48
+ for (const u of report.unmapped.slice(0, 30)) {
49
+ console.log(` - ${u.path}: ${u.value}${u.reason ? ` (${u.reason})` : ''}`);
50
+ }
51
+ if (report.unmapped.length > 30) {
52
+ console.log(` …and ${report.unmapped.length - 30} more`);
53
+ }
54
+ }
55
+ const summary = summarizeReport(report);
56
+ console.log(`\nMapped: ${report.mappedCount} · errors: ${summary.errors} · warnings: ${summary.warnings}`);
57
+ return exitCodeFromReport(report, strict);
58
+ }
59
+ export const THEME_HELP = `
60
+ @airframeui/build — theme commands
61
+
62
+ Usage:
63
+ af theme generate --from <path|url> [-o theme.css] [--format auto] [--report report.json] [--strict]
64
+ af theme lint <theme.css> [--strict]
65
+ af theme validate <tokens.json|theme.css> [--format auto] [--report report.json] [--strict]
66
+ af theme export [-o dir] [--config path] [--css file] [--strict]
67
+
68
+ Formats:
69
+ auto | dtcg | tokens-studio | css | figma-variables | style-dictionary | airframe-spec
70
+
71
+ Notes:
72
+ --from accepts local files or HTTPS URLs to JSON/CSS artifacts (not HTML pages).
73
+ export writes tokens.dtcg.json + airframe.resolver.json from catalog, config, and theme.files.
74
+ `;
75
+ /**
76
+ * Run theme subcommands. Returns an exit code instead of calling process.exit
77
+ * so unit tests can exercise the CLI without terminating the process.
78
+ */
79
+ export async function runThemeCommand(args) {
80
+ const sub = args[0];
81
+ const rest = args.slice(1);
82
+ const strict = hasFlag(rest, '--strict');
83
+ if (sub === 'generate') {
84
+ const from = getFlag(rest, '--from');
85
+ const out = getFlag(rest, '--out') ?? getFlag(rest, '-o') ?? 'theme.css';
86
+ const reportPath = getFlag(rest, '--report');
87
+ const format = (getFlag(rest, '--format') ?? 'auto');
88
+ if (!from) {
89
+ console.error('Usage: af theme generate --from <path|url> [-o theme.css]');
90
+ return { exitCode: 1 };
91
+ }
92
+ try {
93
+ const result = await generateTheme({
94
+ from,
95
+ format,
96
+ });
97
+ const written = writeOutput(out, result.css);
98
+ console.log(`✓ Wrote ${written} (from ${result.source})`);
99
+ console.log(` confidence: ${result.spec.meta.confidence ?? 'n/a'} · format: ${result.spec.meta.sourceFormat}`);
100
+ if (reportPath) {
101
+ writeOutput(reportPath, JSON.stringify({
102
+ source: result.source,
103
+ spec: result.spec,
104
+ report: result.report,
105
+ }, null, 2) + '\n');
106
+ console.log(`✓ Wrote report ${reportPath}`);
107
+ }
108
+ return { exitCode: printDiagnostics(result.report, strict) };
109
+ }
110
+ catch (error) {
111
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
112
+ return { exitCode: 1 };
113
+ }
114
+ }
115
+ if (sub === 'lint') {
116
+ const file = rest.find((a) => !a.startsWith('-'));
117
+ if (!file) {
118
+ console.error('Usage: af theme lint <theme.css> [--strict]');
119
+ return { exitCode: 1 };
120
+ }
121
+ try {
122
+ const { content } = await loadFromPathOrUrl(file);
123
+ const report = lintThemeCss(content);
124
+ return { exitCode: printDiagnostics(report, strict) };
125
+ }
126
+ catch (error) {
127
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
128
+ return { exitCode: 1 };
129
+ }
130
+ }
131
+ if (sub === 'validate') {
132
+ const file = rest.find((a) => !a.startsWith('-'));
133
+ const format = (getFlag(rest, '--format') ?? 'auto');
134
+ const reportPath = getFlag(rest, '--report');
135
+ if (!file) {
136
+ console.error('Usage: af theme validate <tokens.json|theme.css> [--format auto] [--report out.json] [--strict]');
137
+ return { exitCode: 1 };
138
+ }
139
+ try {
140
+ const { content, source } = await loadFromPathOrUrl(file);
141
+ if (file.endsWith('.css') || content.trim().startsWith(':root') || /--af-/.test(content)) {
142
+ const report = lintThemeCss(content);
143
+ if (reportPath) {
144
+ writeOutput(reportPath, JSON.stringify({ source, report }, null, 2) + '\n');
145
+ }
146
+ return { exitCode: printDiagnostics(report, strict) };
147
+ }
148
+ const result = generateFromContent(content, { format });
149
+ if (reportPath) {
150
+ writeOutput(reportPath, JSON.stringify({ source, spec: result.spec, report: result.report }, null, 2) + '\n');
151
+ }
152
+ console.log(`Validated ${source} → ${result.report.mappedCount} mapped tokens`);
153
+ return { exitCode: printDiagnostics(result.report, strict) };
154
+ }
155
+ catch (error) {
156
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
157
+ return { exitCode: 1 };
158
+ }
159
+ }
160
+ if (sub === 'export') {
161
+ const outDir = getFlag(rest, '--out') ?? getFlag(rest, '-o') ?? '.';
162
+ const configFlag = getFlag(rest, '--config');
163
+ const extraCss = getFlags(rest, '--css');
164
+ const cwd = process.cwd();
165
+ try {
166
+ const { config } = await loadConfig(cwd, configFlag);
167
+ const themeConfig = {};
168
+ if (config.breakpoints)
169
+ themeConfig.breakpoints = config.breakpoints;
170
+ if (config.theme)
171
+ themeConfig.theme = config.theme;
172
+ const { spec, files } = resolveThemeFromFiles({
173
+ cwd,
174
+ config: themeConfig,
175
+ extraCss,
176
+ });
177
+ const { document } = emitDtcg({ spec });
178
+ const resolver = emitResolver(spec, { tokensRef: './tokens.dtcg.json' });
179
+ const dir = path.isAbsolute(outDir) ? outDir : path.join(cwd, outDir);
180
+ fs.mkdirSync(dir, { recursive: true });
181
+ const dtcgPath = writeOutput(path.join(dir, 'tokens.dtcg.json'), `${JSON.stringify(document, null, 2)}\n`);
182
+ const resolverPath = writeOutput(path.join(dir, 'airframe.resolver.json'), `${JSON.stringify(resolver, null, 2)}\n`);
183
+ console.log(`✓ Wrote ${dtcgPath}`);
184
+ console.log(`✓ Wrote ${resolverPath}`);
185
+ if (files.length > 0) {
186
+ console.log(` from ${files.length} CSS file(s)`);
187
+ }
188
+ return { exitCode: 0 };
189
+ }
190
+ catch (error) {
191
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
192
+ return { exitCode: 1 };
193
+ }
194
+ }
195
+ console.log(THEME_HELP);
196
+ return { exitCode: sub ? 1 : 0 };
197
+ }
198
+ //# sourceMappingURL=theme-cli.js.map