/** * Token JS Compiler -- `TokenDef[]` to JavaScript const + `.d.ts` type declaration. * * Emits a fully typed `tokens` object grouped by category with const * assertion, plus a companion `.d.ts` declaration using `typeof`. * * @module */ import type { Token } from '@czap/core'; import { groupTokensByCategory } from './css-utils.js'; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- /** * Output of {@link TokenJSCompiler.compile}. * * Two parallel artifacts for the same token set: a runtime ES module and * a companion ambient declaration. The type declaration uses `typeof` so * values round-trip exactly through the compiler without hand-maintained * duplication. */ export interface TokenJSResult { /** Runtime `.ts` source declaring `export const tokens` with const assertion. */ readonly code: string; /** Ambient `.d.ts` declaration exposing the same shape via `typeof`. */ readonly typeDeclaration: string; } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /** * Serialize a token value for JS/TS output. * Strings are JSON-quoted, numbers and booleans are plain, everything else * falls back to JSON.stringify. Distinct from stringifyCSSValue which emits * raw CSS text without quoting. */ function serializeJSValue(value: unknown): string { if (typeof value === 'string') return JSON.stringify(value); if (typeof value === 'number') return String(value); if (typeof value === 'boolean') return String(value); return JSON.stringify(value); } // --------------------------------------------------------------------------- // TokenJSCompiler // --------------------------------------------------------------------------- /** * Compile a list of {@link Token.Shape} into a JS object + companion type * declaration, grouped by category. */ function compile(tokens: readonly Token.Shape[]): TokenJSResult { // Group tokens by category const grouped = groupTokensByCategory(tokens); // Build the JS object literal const categoryBlocks: string[] = []; for (const [category, categoryTokens] of grouped) { const entries: string[] = []; for (const tok of categoryTokens) { entries.push(` ${JSON.stringify(tok.name)}: ${serializeJSValue(tok.fallback)}`); } categoryBlocks.push(` ${JSON.stringify(category)}: {\n${entries.join(',\n')}\n }`); } const objectLiteral = `{\n${categoryBlocks.join(',\n')}\n}`; const code = [ `// Generated by @czap/compiler -- do not edit manually.`, ``, `export const tokens = ${objectLiteral} as const;`, ``, `export type TokenMap = typeof tokens;`, ``, ].join('\n'); const typeDeclaration = [ `// Generated by @czap/compiler -- do not edit manually.`, ``, `export declare const tokens: ${objectLiteral};`, ``, `export type TokenMap = typeof tokens;`, ``, ].join('\n'); return { code, typeDeclaration }; } /** * Token JS compiler namespace. * * Serializes a token set to a runtime ES module and an ambient `.d.ts` * declaration in parallel so consumers import a single typed object while * the build artifact stays 100% generated. */ export const TokenJSCompiler = { /** Compile a token array into parallel `.ts` source and `.d.ts` declaration. */ compile, } as const;