@bezel-labs/bezel-kit 0.3.0 → 0.4.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.
- package/README.md +29 -0
- package/dist/{chunk-FGX7KW2Z.js → chunk-RLMGV52B.js} +39 -6
- package/dist/chunk-RLMGV52B.js.map +1 -0
- package/dist/cli.cjs +69 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +32 -6
- package/dist/cli.js.map +1 -1
- package/dist/node.cjs +38 -5
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +27 -0
- package/dist/node.d.ts +27 -0
- package/dist/node.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FGX7KW2Z.js.map +0 -1
package/README.md
CHANGED
|
@@ -51,9 +51,38 @@ any of it with `--dir`, `--variables-output`, `--contexts-output`, `--fonts-outp
|
|
|
51
51
|
`--no-contexts`, `--no-fonts`, `--color`, `--unit`. An existing `bezel.json` is never
|
|
52
52
|
overwritten without `--force`.
|
|
53
53
|
|
|
54
|
+
Link the repo to a Bezel project with `--project <uuid>`, and pin which tokens the Bezel
|
|
55
|
+
MCP fetches with `--tokens-version <v>` (`latest` or a published semver like `1.4.0`;
|
|
56
|
+
default `latest`). These two flags update only their own key in an existing `bezel.json`,
|
|
57
|
+
so no `--force` is needed:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
bezel init --project 0f7a4c2e-1b3d-4e5f-8a9b-0c1d2e3f4a5b --tokens-version 1.4.0
|
|
61
|
+
```
|
|
62
|
+
|
|
54
63
|
`build` auto-loads `bezel.json` from the working directory when present. Run
|
|
55
64
|
`bezel --help` for all options.
|
|
56
65
|
|
|
66
|
+
### Config — `bezel.json`
|
|
67
|
+
|
|
68
|
+
Any `BezelOptions` key can be set in `bezel.json` (`variablesOutput`, `contextsOutput`,
|
|
69
|
+
`fontsOutput`, `colorFormat`, `dimensionUnit`, `nameExtension`, ...). Two keys describe
|
|
70
|
+
the project link rather than the build:
|
|
71
|
+
|
|
72
|
+
- `projectId` (optional) — the Bezel project this repo is linked to (a UUID).
|
|
73
|
+
- `version` (recommended, default `latest`) — the tokens the Bezel MCP fetches: `latest`
|
|
74
|
+
for the project's latest snapshot, or a published semver like `1.4.0`.
|
|
75
|
+
|
|
76
|
+
Both are read only by the Bezel MCP and ignored by `build`.
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"projectId": "0f7a4c2e-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
|
|
81
|
+
"version": "latest",
|
|
82
|
+
"variablesOutput": "src/bezel/variables.css"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
57
86
|
Generated outputs live in their own directory because `build` overwrites them without
|
|
58
87
|
asking and adds them to `.gitignore` — keeping them out of a hand-written `src/styles`
|
|
59
88
|
means a generic name like `variables.css` can never clobber a file you wrote.
|
|
@@ -71,8 +71,18 @@ ${block}
|
|
|
71
71
|
import { existsSync } from "fs";
|
|
72
72
|
import { readFile, writeFile } from "fs/promises";
|
|
73
73
|
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
74
|
+
var isUuid = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
75
|
+
var isTokensVersion = (value) => value === "latest" || /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(value);
|
|
74
76
|
var joinPath = (dir, file) => dir === "" ? file : `${dir.replace(/\/+$/, "")}/${file}`;
|
|
75
77
|
async function initConfig(options = {}) {
|
|
78
|
+
if (options.projectId !== void 0 && !isUuid(options.projectId)) {
|
|
79
|
+
throw new Error(`bezel: --project must be a UUID, got "${options.projectId}"`);
|
|
80
|
+
}
|
|
81
|
+
if (options.version !== void 0 && !isTokensVersion(options.version)) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`bezel: --tokens-version must be "latest" or a semver like 1.4.0, got "${options.version}"`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
76
86
|
const cwd = options.cwd ?? process.cwd();
|
|
77
87
|
const toAbs = (p) => isAbsolute2(p) ? p : resolve2(cwd, p);
|
|
78
88
|
const configPath = toAbs(DEFAULT_CONFIG_FILE);
|
|
@@ -82,9 +92,10 @@ async function initConfig(options = {}) {
|
|
|
82
92
|
const outputDir = options.outputDir ?? (hasSrc ? DEFAULT_OUTPUT_DIR : FALLBACK_OUTPUT_DIR);
|
|
83
93
|
const wantsContexts = options.contextsOutput !== void 0 || (options.contexts ?? isTypeScript);
|
|
84
94
|
const wantsFonts = options.fontsOutput !== void 0 || (options.fonts ?? isTypeScript);
|
|
85
|
-
const config = {
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
const config = {};
|
|
96
|
+
if (options.projectId !== void 0) config.projectId = options.projectId;
|
|
97
|
+
config.version = options.version ?? "latest";
|
|
98
|
+
config.variablesOutput = options.variablesOutput ?? joinPath(outputDir, VARIABLES_FILE);
|
|
88
99
|
if (wantsContexts) {
|
|
89
100
|
config.contextsOutput = options.contextsOutput ?? joinPath(outputDir, CONTEXTS_FILE);
|
|
90
101
|
}
|
|
@@ -94,11 +105,33 @@ async function initConfig(options = {}) {
|
|
|
94
105
|
if (options.colorFormat) config.colorFormat = options.colorFormat;
|
|
95
106
|
if (options.dimensionUnit) config.dimensionUnit = options.dimensionUnit;
|
|
96
107
|
if (existsSync(configPath) && !options.force) {
|
|
97
|
-
|
|
108
|
+
const wantsLinkUpdate = options.projectId !== void 0 || options.version !== void 0;
|
|
109
|
+
if (!wantsLinkUpdate) {
|
|
110
|
+
return { configPath, config, written: false, updated: false, hasTokensFile, isTypeScript };
|
|
111
|
+
}
|
|
112
|
+
const existing = JSON.parse(await readFile(configPath, "utf8"));
|
|
113
|
+
const { projectId: previousProjectId, version: previousVersion, ...rest } = existing;
|
|
114
|
+
const merged = {};
|
|
115
|
+
const projectId = options.projectId ?? previousProjectId;
|
|
116
|
+
if (projectId !== void 0) merged.projectId = projectId;
|
|
117
|
+
merged.version = options.version ?? previousVersion ?? "latest";
|
|
118
|
+
Object.assign(merged, rest);
|
|
119
|
+
await writeFile(configPath, `${JSON.stringify(merged, null, 2)}
|
|
120
|
+
`, "utf8");
|
|
121
|
+
return {
|
|
122
|
+
configPath,
|
|
123
|
+
config: merged,
|
|
124
|
+
written: true,
|
|
125
|
+
updated: true,
|
|
126
|
+
previousProjectId,
|
|
127
|
+
previousVersion,
|
|
128
|
+
hasTokensFile,
|
|
129
|
+
isTypeScript
|
|
130
|
+
};
|
|
98
131
|
}
|
|
99
132
|
await writeFile(configPath, `${JSON.stringify(config, null, 2)}
|
|
100
133
|
`, "utf8");
|
|
101
|
-
return { configPath, config, written: true, hasTokensFile, isTypeScript };
|
|
134
|
+
return { configPath, config, written: true, updated: false, hasTokensFile, isTypeScript };
|
|
102
135
|
}
|
|
103
136
|
async function readPackageScripts(cwd) {
|
|
104
137
|
try {
|
|
@@ -187,4 +220,4 @@ export {
|
|
|
187
220
|
readPackageScripts,
|
|
188
221
|
generateVariablesCss
|
|
189
222
|
};
|
|
190
|
-
//# sourceMappingURL=chunk-
|
|
223
|
+
//# sourceMappingURL=chunk-RLMGV52B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/node.ts","../src/config.ts","../src/gitignore.ts","../src/init.ts"],"sourcesContent":["/**\n * bezel/node — Node entry.\n *\n * Re-exports the full isomorphic core, plus the filesystem conveniences:\n * read a DTCG tokens file and write the generated `variables.css` (and optional\n * `contexts.ts`/`fonts.ts`) to disk. This module imports `node:*` and is not\n * browser-safe; for the pure transform use `bezel`.\n */\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { dirname, relative, sep } from \"node:path\"\nimport { resolveOptions, type BezelOptions, type ResolvedOptions } from \"./config\"\nimport { emitCss } from \"./emit\"\nimport { formatContextsModule, getContexts } from \"./contexts\"\nimport { formatFontsModule, getFonts } from \"./fonts\"\nimport { mergeGitignore } from \"./gitignore\"\nimport type { DtcgNode } from \"./types\"\n\nexport * from \"./index\"\nexport { resolveOptions } from \"./config\"\nexport type { BezelOptions, ResolvedOptions } from \"./config\"\nexport { initConfig, readPackageScripts } from \"./init\"\nexport type { InitOptions, InitResult } from \"./init\"\nexport { DEFAULT_CONFIG_FILE, DEFAULT_OUTPUT_DIR, FALLBACK_OUTPUT_DIR } from \"./defaults\"\n\n/**\n * Generate a scoped `variables.css` from a W3C DTCG tokens file.\n *\n * Reads the tokens file, resolves each configured context (with `$root` fallback\n * and reference resolution), and writes one CSS scope per context — the base\n * context in full, others as override-only blocks. Returns the generated CSS.\n *\n * The tokens file is always read from `design-tokens.json` at the project root\n * (resolved against `cwd`); the name and location are fixed and not configurable.\n *\n * @example\n * // zero-config: ./design-tokens.json → ./src/bezel/variables.css\n * await generateVariablesCss()\n *\n * @example\n * await generateVariablesCss({\n * variablesOutput: \"packages/ui/src/bezel/variables.css\",\n * })\n */\nexport async function generateVariablesCss(options: BezelOptions = {}): Promise<string> {\n const resolved = resolveOptions(options)\n\n let raw: string\n try {\n raw = await readFile(resolved.inputPath, \"utf8\")\n } catch (err) {\n throw new Error(\n `bezel: could not read tokens file at \"${resolved.inputPath}\". ` +\n `A \"design-tokens.json\" file must exist at the project root.\\n${(err as Error).message}`,\n )\n }\n\n let tokens: DtcgNode\n try {\n tokens = JSON.parse(raw) as DtcgNode\n } catch (err) {\n throw new Error(`bezel: \"${resolved.inputPath}\" is not valid JSON.\\n${(err as Error).message}`)\n }\n\n const css = emitCss(tokens, resolved)\n\n if (resolved.write) {\n await mkdir(dirname(resolved.variablesOutputPath), { recursive: true })\n await writeFile(resolved.variablesOutputPath, css, \"utf8\")\n\n if (resolved.contextsOutput) {\n const contextsModule = formatContextsModule(getContexts(tokens))\n await mkdir(dirname(resolved.contextsOutput), { recursive: true })\n await writeFile(resolved.contextsOutput, contextsModule, \"utf8\")\n }\n\n if (resolved.fontsOutput) {\n const fontsModule = formatFontsModule(getFonts(tokens))\n await mkdir(dirname(resolved.fontsOutput), { recursive: true })\n await writeFile(resolved.fontsOutput, fontsModule, \"utf8\")\n }\n\n if (resolved.gitignore) {\n await updateGitignore(resolved)\n }\n }\n\n return css\n}\n\n/**\n * Create or update the project `.gitignore` so the generated outputs are ignored.\n * Outputs are written as paths relative to the `.gitignore`, anchored with a leading\n * `/`. Outputs outside the `.gitignore`'s directory can't be anchored from it and are\n * skipped with a warning. The file is only rewritten when its contents actually change.\n */\nasync function updateGitignore(resolved: ResolvedOptions): Promise<void> {\n const dir = dirname(resolved.gitignorePath)\n const outputs = [\n resolved.variablesOutputPath,\n resolved.contextsOutput,\n resolved.fontsOutput,\n ].filter((p): p is string => p !== null)\n\n const entries: string[] = []\n for (const output of outputs) {\n const rel = relative(dir, output)\n if (rel === \"\" || rel.startsWith(\"..\")) {\n process.stderr.write(\n `bezel: skipping .gitignore entry for \"${output}\" — outside ${resolved.gitignorePath}\\n`,\n )\n continue\n }\n entries.push(`/${rel.split(sep).join(\"/\")}`)\n }\n\n if (entries.length === 0) return\n\n let existing = \"\"\n try {\n existing = await readFile(resolved.gitignorePath, \"utf8\")\n } catch {\n // No existing .gitignore — create one.\n }\n\n const next = mergeGitignore(existing, entries)\n if (next !== existing) {\n await mkdir(dir, { recursive: true })\n await writeFile(resolved.gitignorePath, next, \"utf8\")\n }\n}\n","import { isAbsolute, resolve } from \"node:path\"\nimport { DEFAULT_INPUT, DEFAULT_VARIABLES_OUTPUT } from \"./defaults\"\nimport { resolveCssOptions, type CssOptions } from \"./options\"\n\nexport {\n DEFAULT_CONFIG_FILE,\n DEFAULT_CONTEXTS,\n DEFAULT_INPUT,\n DEFAULT_OUTPUT_DIR,\n DEFAULT_VARIABLES_OUTPUT,\n DEFAULT_NAME_EXTENSION,\n FALLBACK_OUTPUT_DIR,\n} from \"./defaults\"\n\n/**\n * Options for {@link generateVariablesCss} — the Node, file-oriented surface.\n * Extends the pure {@link CssOptions} with input/output paths. All optional;\n * defaults make the zero-config case work.\n */\nexport interface BezelOptions extends CssOptions {\n /**\n * The Bezel project this repo is linked to (a UUID). Read only by the Bezel MCP to\n * know which project to pull `design-tokens.json` from — ignored by `build`.\n */\n projectId?: string\n /**\n * Which tokens the Bezel MCP fetches: `\"latest\"` (the project's latest snapshot) or a\n * published semver like `\"1.4.0\"`. Missing means `\"latest\"`. Ignored by `build`.\n */\n version?: string\n /** Path to write the generated CSS. Default: `\"src/bezel/variables.css\"` (resolved from `cwd`). */\n variablesOutput?: string\n /** Base directory paths are resolved against. Default: `process.cwd()`. */\n cwd?: string\n /**\n * When set, also write a generated TypeScript module exporting `CONTEXTS` (the list of\n * context names derived from the tokens). Path resolved against `cwd`. Default: none.\n */\n contextsOutput?: string\n /**\n * When set, also write a generated TypeScript module exporting `FONTS` (the web fonts\n * derived from the tokens). Path resolved against `cwd`. Default: none.\n */\n fontsOutput?: string\n /** When `false`, return the CSS string without touching the filesystem. Default: `true`. */\n write?: boolean\n /**\n * Create/update a project `.gitignore` so the generated outputs are ignored. The\n * entries live in a marked block, rewritten on each run so renamed outputs are\n * cleaned up. Default: `true`.\n */\n gitignore?: boolean\n /** Path to the `.gitignore` to manage. Default: `\".gitignore\"` (resolved from `cwd`). */\n gitignorePath?: string\n}\n\n/** Fully-resolved options (no `undefined`), produced by {@link resolveOptions}. */\nexport interface ResolvedOptions {\n inputPath: string\n variablesOutputPath: string\n contexts: Record<string, string>\n colorFormat: \"oklch\" | \"hex\"\n dimensionUnit: \"preserve\" | \"rem\"\n nameExtension: string\n contextsOutput: string | null\n fontsOutput: string | null\n write: boolean\n gitignore: boolean\n gitignorePath: string\n}\n\n/** Apply defaults and resolve input/output paths against `cwd`. */\nexport function resolveOptions(options: BezelOptions = {}): ResolvedOptions {\n const cwd = options.cwd ?? process.cwd()\n const toAbs = (p: string) => (isAbsolute(p) ? p : resolve(cwd, p))\n\n const css = resolveCssOptions(options)\n\n return {\n // The tokens file is always `design-tokens.json` at the project root — not configurable.\n inputPath: toAbs(DEFAULT_INPUT),\n variablesOutputPath: toAbs(options.variablesOutput ?? DEFAULT_VARIABLES_OUTPUT),\n ...css,\n contextsOutput: options.contextsOutput ? toAbs(options.contextsOutput) : null,\n fontsOutput: options.fontsOutput ? toAbs(options.fontsOutput) : null,\n write: options.write ?? true,\n gitignore: options.gitignore ?? true,\n gitignorePath: toAbs(options.gitignorePath ?? \".gitignore\"),\n }\n}\n","/**\n * bezel — `.gitignore` managed-block merging.\n *\n * Pure string transforms (no `node:fs`): given the existing `.gitignore` contents\n * and the list of generated outputs to ignore, produce the new contents. Bezel's\n * entries live inside a marked block so repeated runs rewrite just that span —\n * renamed/removed outputs drop out automatically and entries never duplicate, while\n * the user's own rules outside the markers are preserved.\n */\n\n/** Opening marker for the Bezel-managed block. */\nexport const START = \"# bezel:start (generated — do not edit)\"\n/** Closing marker for the Bezel-managed block. */\nexport const END = \"# bezel:end\"\n\n/**\n * Merge Bezel's managed block into existing `.gitignore` contents.\n *\n * - Empty `existing` → just the block.\n * - An existing block (delimited by {@link START}…{@link END}) → replaced in place,\n * preserving everything before and after.\n * - No block → appended after the existing content, separated by a blank line.\n *\n * The result always ends with a single trailing newline.\n */\nexport function mergeGitignore(existing: string, entries: string[]): string {\n const block = [START, ...entries, END].join(\"\\n\")\n\n if (existing.trim() === \"\") return `${block}\\n`\n\n const start = existing.indexOf(START)\n if (start !== -1) {\n const endMarker = existing.indexOf(END, start)\n if (endMarker !== -1) {\n const before = existing.slice(0, start).replace(/\\n+$/, \"\")\n const after = existing.slice(endMarker + END.length).replace(/^\\n+/, \"\")\n const head = before === \"\" ? \"\" : `${before}\\n\\n`\n const tail = after === \"\" ? \"\" : `\\n\\n${after.replace(/\\n+$/, \"\")}`\n return `${head}${block}${tail}\\n`\n }\n }\n\n return `${existing.replace(/\\n+$/, \"\")}\\n\\n${block}\\n`\n}\n","/**\n * bezel — `bezel.json` scaffolding.\n *\n * Writes the config file `bezel build` auto-loads, so a project doesn't have to\n * hand-author it. Defaults are derived from the project itself: outputs land in\n * `src/bezel/` when there's a `src/` (otherwise `bezel/`), and the TypeScript\n * modules are only scaffolded for a TypeScript project — `contexts.ts`/`fonts.ts`\n * are emitted as TS (`as const`, `export type`) and would be dead weight in a\n * JavaScript one.\n *\n * Non-destructive: an existing `bezel.json` is never overwritten without `force`.\n * The one exception is the project link: passing `projectId` and/or `version` to an\n * existing config updates just those keys in place, leaving everything else as-is.\n */\nimport { existsSync } from \"node:fs\"\nimport { readFile, writeFile } from \"node:fs/promises\"\nimport { isAbsolute, resolve } from \"node:path\"\nimport type { BezelOptions } from \"./config\"\nimport {\n CONTEXTS_FILE,\n DEFAULT_CONFIG_FILE,\n DEFAULT_INPUT,\n DEFAULT_OUTPUT_DIR,\n FALLBACK_OUTPUT_DIR,\n FONTS_FILE,\n VARIABLES_FILE,\n} from \"./defaults\"\n\n/** Options for {@link initConfig}. All optional — the zero-arg call is the happy path. */\nexport interface InitOptions {\n /** Project root to scaffold into. Default: `process.cwd()`. */\n cwd?: string\n /**\n * Directory for all three outputs, relative to `cwd`. Default: `\"src/bezel\"` when\n * the project has a `src/`, otherwise `\"bezel\"`. Ignored for any output whose path\n * is set explicitly.\n */\n outputDir?: string\n /** Explicit path for the generated CSS. Overrides {@link InitOptions.outputDir}. */\n variablesOutput?: string\n /** Explicit path for the generated contexts module. Implies `contexts: true`. */\n contextsOutput?: string\n /** Explicit path for the generated fonts module. Implies `fonts: true`. */\n fontsOutput?: string\n /** Include `contextsOutput`. Default: true for a TypeScript project. */\n contexts?: boolean\n /** Include `fontsOutput`. Default: true for a TypeScript project. */\n fonts?: boolean\n /** Written to the config only when set — otherwise the build default applies. */\n colorFormat?: \"oklch\" | \"hex\"\n /** Written to the config only when set — otherwise the build default applies. */\n dimensionUnit?: \"preserve\" | \"rem\"\n /** Overwrite an existing `bezel.json`. Default: `false`. */\n force?: boolean\n /**\n * The Bezel project to link this repo to (a UUID). Written as `projectId`; on an\n * existing config it updates just that key without needing `force`.\n */\n projectId?: string\n /**\n * Tokens version the Bezel MCP fetches: `\"latest\"` or a semver like `\"1.4.0\"`.\n * Always written — defaults to `\"latest\"` on a fresh init; on an existing config it\n * updates just that key without needing `force`.\n */\n version?: string\n}\n\n/** Outcome of {@link initConfig}. */\nexport interface InitResult {\n /** Absolute path of the config file. */\n configPath: string\n /** The config that was written — or the one that would have been, when `written` is false. */\n config: BezelOptions\n /** False when an existing `bezel.json` was left untouched (no `force`). */\n written: boolean\n /** Whether a `design-tokens.json` is already present at the project root. */\n hasTokensFile: boolean\n /** Whether the project looks like TypeScript (drives the contexts/fonts defaults). */\n isTypeScript: boolean\n /** True when only `projectId`/`version` were merged into an existing `bezel.json`. */\n updated: boolean\n /** The `projectId` that was in the file before an update, if any. */\n previousProjectId?: string\n /** The `version` that was in the file before an update, if any. */\n previousVersion?: string\n}\n\n/** Whether `value` is a UUID — the shape of a Bezel project id. */\nexport const isUuid = (value: string): boolean =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)\n\n/** Whether `value` is a valid tokens version: `\"latest\"` or a semver like `1.4.0`. */\nexport const isTokensVersion = (value: string): boolean =>\n value === \"latest\" || /^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$/.test(value)\n\n/**\n * Join config path segments with forward slashes.\n *\n * Config values are portable JSON, so they always use `/` — `node:path.join` would\n * emit backslashes on Windows and bake a platform into a committed file.\n */\nconst joinPath = (dir: string, file: string): string =>\n dir === \"\" ? file : `${dir.replace(/\\/+$/, \"\")}/${file}`\n\n/**\n * Create a `bezel.json` in the project root, inferring sensible defaults from the\n * project layout.\n *\n * @example\n * // src/bezel/{variables.css,contexts.ts,fonts.ts} for a TS project with a src/\n * await initConfig()\n *\n * @example\n * await initConfig({ outputDir: \"packages/ui/src/bezel\", fonts: false })\n */\nexport async function initConfig(options: InitOptions = {}): Promise<InitResult> {\n if (options.projectId !== undefined && !isUuid(options.projectId)) {\n throw new Error(`bezel: --project must be a UUID, got \"${options.projectId}\"`)\n }\n if (options.version !== undefined && !isTokensVersion(options.version)) {\n throw new Error(\n `bezel: --tokens-version must be \"latest\" or a semver like 1.4.0, got \"${options.version}\"`,\n )\n }\n\n const cwd = options.cwd ?? process.cwd()\n const toAbs = (p: string) => (isAbsolute(p) ? p : resolve(cwd, p))\n\n const configPath = toAbs(DEFAULT_CONFIG_FILE)\n const hasTokensFile = existsSync(toAbs(DEFAULT_INPUT))\n const isTypeScript = existsSync(toAbs(\"tsconfig.json\"))\n\n const hasSrc = existsSync(toAbs(\"src\"))\n const outputDir = options.outputDir ?? (hasSrc ? DEFAULT_OUTPUT_DIR : FALLBACK_OUTPUT_DIR)\n\n // An explicit path is itself an opt-in; otherwise TypeScript projects get the\n // generated modules and JavaScript ones don't.\n const wantsContexts = options.contextsOutput !== undefined || (options.contexts ?? isTypeScript)\n const wantsFonts = options.fontsOutput !== undefined || (options.fonts ?? isTypeScript)\n\n // Link keys come first so the file reads project → version → outputs.\n const config: BezelOptions = {}\n if (options.projectId !== undefined) config.projectId = options.projectId\n config.version = options.version ?? \"latest\"\n config.variablesOutput = options.variablesOutput ?? joinPath(outputDir, VARIABLES_FILE)\n if (wantsContexts) {\n config.contextsOutput = options.contextsOutput ?? joinPath(outputDir, CONTEXTS_FILE)\n }\n if (wantsFonts) {\n config.fontsOutput = options.fontsOutput ?? joinPath(outputDir, FONTS_FILE)\n }\n // Only persist the transform options that were asked for, so the file stays minimal\n // and keeps tracking the library defaults for everything else.\n if (options.colorFormat) config.colorFormat = options.colorFormat\n if (options.dimensionUnit) config.dimensionUnit = options.dimensionUnit\n\n if (existsSync(configPath) && !options.force) {\n const wantsLinkUpdate = options.projectId !== undefined || options.version !== undefined\n if (!wantsLinkUpdate) {\n return { configPath, config, written: false, updated: false, hasTokensFile, isTypeScript }\n }\n\n // Link-only update: replace just the two link keys, keep every other key in place.\n const existing = JSON.parse(await readFile(configPath, \"utf8\")) as BezelOptions\n const { projectId: previousProjectId, version: previousVersion, ...rest } = existing\n const merged: BezelOptions = {}\n const projectId = options.projectId ?? previousProjectId\n if (projectId !== undefined) merged.projectId = projectId\n merged.version = options.version ?? previousVersion ?? \"latest\"\n Object.assign(merged, rest)\n\n await writeFile(configPath, `${JSON.stringify(merged, null, 2)}\\n`, \"utf8\")\n\n return {\n configPath,\n config: merged,\n written: true,\n updated: true,\n previousProjectId,\n previousVersion,\n hasTokensFile,\n isTypeScript,\n }\n }\n\n await writeFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf8\")\n\n return { configPath, config, written: true, updated: false, hasTokensFile, isTypeScript }\n}\n\n/**\n * Read the `\"scripts\"` block of the project's `package.json`, if there is one.\n * Used only to tailor the post-init hint — a missing or unparsable file is not an error.\n */\nexport async function readPackageScripts(cwd: string): Promise<Record<string, string>> {\n try {\n const raw = await readFile(resolve(cwd, \"package.json\"), \"utf8\")\n const parsed = JSON.parse(raw) as { scripts?: Record<string, string> }\n return parsed.scripts ?? {}\n } catch {\n return {}\n }\n}\n"],"mappings":"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","names":["readFile","writeFile","isAbsolute","resolve","isAbsolute","resolve","readFile","writeFile"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -422,8 +422,18 @@ ${block}
|
|
|
422
422
|
var import_node_fs = require("fs");
|
|
423
423
|
var import_promises = require("fs/promises");
|
|
424
424
|
var import_node_path2 = require("path");
|
|
425
|
+
var isUuid = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
426
|
+
var isTokensVersion = (value) => value === "latest" || /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(value);
|
|
425
427
|
var joinPath = (dir, file) => dir === "" ? file : `${dir.replace(/\/+$/, "")}/${file}`;
|
|
426
428
|
async function initConfig(options = {}) {
|
|
429
|
+
if (options.projectId !== void 0 && !isUuid(options.projectId)) {
|
|
430
|
+
throw new Error(`bezel: --project must be a UUID, got "${options.projectId}"`);
|
|
431
|
+
}
|
|
432
|
+
if (options.version !== void 0 && !isTokensVersion(options.version)) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
`bezel: --tokens-version must be "latest" or a semver like 1.4.0, got "${options.version}"`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
427
437
|
const cwd = options.cwd ?? process.cwd();
|
|
428
438
|
const toAbs = (p) => (0, import_node_path2.isAbsolute)(p) ? p : (0, import_node_path2.resolve)(cwd, p);
|
|
429
439
|
const configPath = toAbs(DEFAULT_CONFIG_FILE);
|
|
@@ -433,9 +443,10 @@ async function initConfig(options = {}) {
|
|
|
433
443
|
const outputDir = options.outputDir ?? (hasSrc ? DEFAULT_OUTPUT_DIR : FALLBACK_OUTPUT_DIR);
|
|
434
444
|
const wantsContexts = options.contextsOutput !== void 0 || (options.contexts ?? isTypeScript);
|
|
435
445
|
const wantsFonts = options.fontsOutput !== void 0 || (options.fonts ?? isTypeScript);
|
|
436
|
-
const config = {
|
|
437
|
-
|
|
438
|
-
|
|
446
|
+
const config = {};
|
|
447
|
+
if (options.projectId !== void 0) config.projectId = options.projectId;
|
|
448
|
+
config.version = options.version ?? "latest";
|
|
449
|
+
config.variablesOutput = options.variablesOutput ?? joinPath(outputDir, VARIABLES_FILE);
|
|
439
450
|
if (wantsContexts) {
|
|
440
451
|
config.contextsOutput = options.contextsOutput ?? joinPath(outputDir, CONTEXTS_FILE);
|
|
441
452
|
}
|
|
@@ -445,11 +456,33 @@ async function initConfig(options = {}) {
|
|
|
445
456
|
if (options.colorFormat) config.colorFormat = options.colorFormat;
|
|
446
457
|
if (options.dimensionUnit) config.dimensionUnit = options.dimensionUnit;
|
|
447
458
|
if ((0, import_node_fs.existsSync)(configPath) && !options.force) {
|
|
448
|
-
|
|
459
|
+
const wantsLinkUpdate = options.projectId !== void 0 || options.version !== void 0;
|
|
460
|
+
if (!wantsLinkUpdate) {
|
|
461
|
+
return { configPath, config, written: false, updated: false, hasTokensFile, isTypeScript };
|
|
462
|
+
}
|
|
463
|
+
const existing = JSON.parse(await (0, import_promises.readFile)(configPath, "utf8"));
|
|
464
|
+
const { projectId: previousProjectId, version: previousVersion, ...rest } = existing;
|
|
465
|
+
const merged = {};
|
|
466
|
+
const projectId = options.projectId ?? previousProjectId;
|
|
467
|
+
if (projectId !== void 0) merged.projectId = projectId;
|
|
468
|
+
merged.version = options.version ?? previousVersion ?? "latest";
|
|
469
|
+
Object.assign(merged, rest);
|
|
470
|
+
await (0, import_promises.writeFile)(configPath, `${JSON.stringify(merged, null, 2)}
|
|
471
|
+
`, "utf8");
|
|
472
|
+
return {
|
|
473
|
+
configPath,
|
|
474
|
+
config: merged,
|
|
475
|
+
written: true,
|
|
476
|
+
updated: true,
|
|
477
|
+
previousProjectId,
|
|
478
|
+
previousVersion,
|
|
479
|
+
hasTokensFile,
|
|
480
|
+
isTypeScript
|
|
481
|
+
};
|
|
449
482
|
}
|
|
450
483
|
await (0, import_promises.writeFile)(configPath, `${JSON.stringify(config, null, 2)}
|
|
451
484
|
`, "utf8");
|
|
452
|
-
return { configPath, config, written: true, hasTokensFile, isTypeScript };
|
|
485
|
+
return { configPath, config, written: true, updated: false, hasTokensFile, isTypeScript };
|
|
453
486
|
}
|
|
454
487
|
async function readPackageScripts(cwd) {
|
|
455
488
|
try {
|
|
@@ -551,6 +584,8 @@ init options:
|
|
|
551
584
|
--no-fonts Skip the generated FONTS module
|
|
552
585
|
--color <format> Color output: oklch | hex (default: oklch)
|
|
553
586
|
--unit <mode> Dimension unit: preserve | rem (default: preserve)
|
|
587
|
+
--project <uuid> Link this repo to a Bezel project (writes projectId)
|
|
588
|
+
--tokens-version <v> Tokens version the Bezel MCP fetches: latest | semver (default: latest)
|
|
554
589
|
-f, --force Overwrite an existing bezel.json
|
|
555
590
|
|
|
556
591
|
build options:
|
|
@@ -568,6 +603,7 @@ build options:
|
|
|
568
603
|
A ${DEFAULT_CONFIG_FILE} file in the working directory is loaded automatically when -c is omitted.
|
|
569
604
|
The contexts/fonts modules are TypeScript, so init only scaffolds them for a
|
|
570
605
|
TypeScript project unless you pass the paths explicitly.
|
|
606
|
+
--project and --tokens-version update only their own key in an existing bezel.json; no --force needed.
|
|
571
607
|
`;
|
|
572
608
|
async function loadConfig(path) {
|
|
573
609
|
const abs = (0, import_node_path4.resolve)(process.cwd(), path);
|
|
@@ -585,6 +621,8 @@ var OPTIONS = {
|
|
|
585
621
|
"fonts-output": { type: "string" },
|
|
586
622
|
color: { type: "string" },
|
|
587
623
|
unit: { type: "string" },
|
|
624
|
+
project: { type: "string" },
|
|
625
|
+
"tokens-version": { type: "string" },
|
|
588
626
|
stdout: { type: "boolean" },
|
|
589
627
|
"no-gitignore": { type: "boolean" },
|
|
590
628
|
"no-contexts": { type: "boolean" },
|
|
@@ -592,6 +630,7 @@ var OPTIONS = {
|
|
|
592
630
|
force: { type: "boolean", short: "f" },
|
|
593
631
|
help: { type: "boolean", short: "h" }
|
|
594
632
|
};
|
|
633
|
+
var TOKENS_FILE_HINT = "Add design-tokens.json to the project root \u2014 ask your AI agent for it via the\n Bezel MCP, or download it from your project.";
|
|
595
634
|
async function runInit(values) {
|
|
596
635
|
const cwd = process.cwd();
|
|
597
636
|
const result = await initConfig({
|
|
@@ -604,12 +643,34 @@ async function runInit(values) {
|
|
|
604
643
|
fonts: values["no-fonts"] ? false : void 0,
|
|
605
644
|
colorFormat: values.color,
|
|
606
645
|
dimensionUnit: values.unit,
|
|
607
|
-
force: values.force
|
|
646
|
+
force: values.force,
|
|
647
|
+
projectId: values.project,
|
|
648
|
+
version: values["tokens-version"]
|
|
608
649
|
});
|
|
609
650
|
const configName = (0, import_node_path4.relative)(cwd, result.configPath) || DEFAULT_CONFIG_FILE;
|
|
651
|
+
if (result.updated) {
|
|
652
|
+
if (values.project) {
|
|
653
|
+
const changed = result.previousProjectId && result.previousProjectId !== values.project;
|
|
654
|
+
process.stderr.write(
|
|
655
|
+
changed ? `bezel: updated projectId in ${configName} (was ${result.previousProjectId})
|
|
656
|
+
` : `bezel: set projectId in ${configName}
|
|
657
|
+
`
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
if (result.config.version !== result.previousVersion) {
|
|
661
|
+
process.stderr.write(`bezel: set version to ${result.config.version} in ${configName}
|
|
662
|
+
`);
|
|
663
|
+
}
|
|
664
|
+
if (!result.hasTokensFile) {
|
|
665
|
+
process.stderr.write(`
|
|
666
|
+
${TOKENS_FILE_HINT}
|
|
667
|
+
`);
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
610
671
|
if (!result.written) {
|
|
611
672
|
process.stderr.write(
|
|
612
|
-
`bezel: ${configName} already exists \u2014 leaving it alone. Re-run with --force to overwrite.
|
|
673
|
+
`bezel: ${configName} already exists \u2014 leaving it alone. Re-run with --force to overwrite, or pass --project <uuid> / --tokens-version <v> to just update the project link.
|
|
613
674
|
`
|
|
614
675
|
);
|
|
615
676
|
process.exitCode = 1;
|
|
@@ -629,9 +690,7 @@ async function runInit(values) {
|
|
|
629
690
|
const scripts = await readPackageScripts(cwd);
|
|
630
691
|
const steps = [];
|
|
631
692
|
if (!result.hasTokensFile) {
|
|
632
|
-
steps.push(
|
|
633
|
-
"Add design-tokens.json to the project root \u2014 ask your AI agent for it via the\n Bezel MCP, or download it from your project."
|
|
634
|
-
);
|
|
693
|
+
steps.push(TOKENS_FILE_HINT);
|
|
635
694
|
}
|
|
636
695
|
if (!scripts["tokens"]) {
|
|
637
696
|
steps.push('Add "tokens": "bezel build" to your package.json scripts.');
|