@nebutra/design-sync 0.1.1 → 2.0.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/LICENSE +21 -676
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +32 -3
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +4 -2
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +5 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/io.js +1 -1
- package/dist/providers/design-md.d.ts +49 -0
- package/dist/providers/design-md.d.ts.map +1 -0
- package/dist/providers/design-md.js +264 -0
- package/dist/serialize/from-design-md.d.ts +80 -0
- package/dist/serialize/from-design-md.d.ts.map +1 -0
- package/dist/serialize/from-design-md.js +1329 -0
- package/dist/serialize/to-brand-package.d.ts +37 -0
- package/dist/serialize/to-brand-package.d.ts.map +1 -0
- package/dist/serialize/to-brand-package.js +87 -0
- package/dist/serialize/to-design-md.d.ts +48 -0
- package/dist/serialize/to-design-md.d.ts.map +1 -0
- package/dist/serialize/to-design-md.js +114 -0
- package/dist/serialize/to-design-md.prose.d.ts +55 -0
- package/dist/serialize/to-design-md.prose.d.ts.map +1 -0
- package/dist/serialize/to-design-md.prose.js +143 -0
- package/dist/serialize/to-design-md.resolve.d.ts +36 -0
- package/dist/serialize/to-design-md.resolve.d.ts.map +1 -0
- package/dist/serialize/to-design-md.resolve.js +248 -0
- package/dist/serialize/to-preview-html.d.ts +48 -0
- package/dist/serialize/to-preview-html.d.ts.map +1 -0
- package/dist/serialize/to-preview-html.js +250 -0
- package/dist/serialize/to-preview-html.template.d.ts +75 -0
- package/dist/serialize/to-preview-html.template.d.ts.map +1 -0
- package/dist/serialize/to-preview-html.template.js +267 -0
- package/dist/types.d.ts +22 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -6
- package/src/cli/index.ts +36 -3
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* to-preview-html.template.ts
|
|
3
|
+
*
|
|
4
|
+
* HTML / CSS template layer for the DTCG → preview.html serializer.
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - escapeHtml — generic HTML-attribute / content escaping
|
|
8
|
+
* - cssAttrValue — escapes a token value for use inside style="…"
|
|
9
|
+
* - cssVarsDecl — turns a CssVarBlock into CSS custom-property lines
|
|
10
|
+
* - buildDocument — assembles the full <!DOCTYPE html> string
|
|
11
|
+
*
|
|
12
|
+
* Pure: no filesystem I/O, no token resolution.
|
|
13
|
+
*/
|
|
14
|
+
// ─── HTML / CSS escaping ───────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Escape a string for safe insertion into HTML content or attribute values.
|
|
17
|
+
* Converts the five characters that have special meaning in HTML.
|
|
18
|
+
*/
|
|
19
|
+
export function escapeHtml(value) {
|
|
20
|
+
return value
|
|
21
|
+
.replace(/&/g, "&")
|
|
22
|
+
.replace(/</g, "<")
|
|
23
|
+
.replace(/>/g, ">")
|
|
24
|
+
.replace(/"/g, """)
|
|
25
|
+
.replace(/'/g, "'");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Escape a token-derived value for safe use inside a `style="…"` attribute.
|
|
29
|
+
*
|
|
30
|
+
* Strategy:
|
|
31
|
+
* 1. Strip CSS structural characters that could inject extra declarations or
|
|
32
|
+
* break the style attribute — specifically `;`, `{`, `}`, `<`, `>`.
|
|
33
|
+
* NOTE: We deliberately keep `"` in the string before step 2 so that
|
|
34
|
+
* font-family values like `"Geist", sans-serif` are preserved; `escapeHtml`
|
|
35
|
+
* will turn those `"` into `"` which the browser decodes correctly.
|
|
36
|
+
* 2. HTML-escape the result so embedded quotes survive as `"` and any
|
|
37
|
+
* remaining angle brackets become `<`/`>`.
|
|
38
|
+
*
|
|
39
|
+
* Example:
|
|
40
|
+
* cssAttrValue('"Geist", "Noto Sans SC", sans-serif')
|
|
41
|
+
* → '"Geist", "Noto Sans SC", sans-serif'
|
|
42
|
+
*
|
|
43
|
+
* cssAttrValue('red;display:none')
|
|
44
|
+
* → 'reddisplay:none' (semicolon stripped before HTML-escape)
|
|
45
|
+
*/
|
|
46
|
+
export function cssAttrValue(raw) {
|
|
47
|
+
return escapeHtml(raw.replace(/[;{}<>]/g, ""));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Render a CssVarBlock as indented CSS custom-property declarations (no braces).
|
|
51
|
+
* Values come from the validated toHex path so they are hex strings, but we
|
|
52
|
+
* wrap in cssAttrValue for consistency (harmless for safe hex values).
|
|
53
|
+
*/
|
|
54
|
+
export function cssVarsDecl(vars) {
|
|
55
|
+
return [
|
|
56
|
+
` --color-primary: ${vars.primary};`,
|
|
57
|
+
` --color-accent: ${vars.accent};`,
|
|
58
|
+
` --color-tertiary: ${vars.tertiary};`,
|
|
59
|
+
` --color-danger: ${vars.danger};`,
|
|
60
|
+
` --color-warning: ${vars.warning};`,
|
|
61
|
+
` --color-success: ${vars.success};`,
|
|
62
|
+
` --color-background: ${vars.background};`,
|
|
63
|
+
` --color-foreground: ${vars.foreground};`,
|
|
64
|
+
` --preview-bg: ${vars.background};`,
|
|
65
|
+
` --preview-fg: ${vars.foreground};`,
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Assemble the full <!DOCTYPE html> preview document.
|
|
70
|
+
*
|
|
71
|
+
* Dark-mode CSS is emitted as TWO separate, valid rules:
|
|
72
|
+
* 1. `:root[data-theme="dark"]` — explicit toggle
|
|
73
|
+
* 2. `@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) }`
|
|
74
|
+
* — OS/browser preference (but not when the user has explicitly chosen light)
|
|
75
|
+
*
|
|
76
|
+
* This avoids the invalid CSS form of comma-joining a selector with an at-rule.
|
|
77
|
+
*/
|
|
78
|
+
export function buildDocument(args) {
|
|
79
|
+
const { escapedName, lightVars, darkVars } = args;
|
|
80
|
+
const darkVarsDecl = cssVarsDecl(darkVars);
|
|
81
|
+
return `<!DOCTYPE html>
|
|
82
|
+
<html lang="en">
|
|
83
|
+
<head>
|
|
84
|
+
<meta charset="UTF-8" />
|
|
85
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
86
|
+
<title>${escapedName} — Design System Preview</title>
|
|
87
|
+
<style>
|
|
88
|
+
/* ── Light mode tokens (default) ────────────────────────── */
|
|
89
|
+
:root {
|
|
90
|
+
${cssVarsDecl(lightVars)}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ── Dark mode tokens — explicit toggle ─────────────────── */
|
|
94
|
+
:root[data-theme="dark"] {
|
|
95
|
+
${darkVarsDecl}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ── Dark mode tokens — OS/browser preference ───────────── */
|
|
99
|
+
@media (prefers-color-scheme: dark) {
|
|
100
|
+
:root:not([data-theme="light"]) {
|
|
101
|
+
${darkVarsDecl}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ── Base reset ─────────────────────────────────────────── */
|
|
106
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
107
|
+
|
|
108
|
+
body {
|
|
109
|
+
margin: 0;
|
|
110
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
111
|
+
font-size: 16px;
|
|
112
|
+
line-height: 1.6;
|
|
113
|
+
background: var(--color-background);
|
|
114
|
+
color: var(--color-foreground);
|
|
115
|
+
padding: 2rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ── Layout ─────────────────────────────────────────────── */
|
|
119
|
+
.preview-header {
|
|
120
|
+
border-bottom: 1.5px solid rgba(128,128,128,0.18);
|
|
121
|
+
padding-bottom: 1.5rem;
|
|
122
|
+
margin-bottom: 2.5rem;
|
|
123
|
+
}
|
|
124
|
+
.preview-title {
|
|
125
|
+
font-size: 2rem;
|
|
126
|
+
font-weight: 700;
|
|
127
|
+
margin: 0 0 0.25rem;
|
|
128
|
+
background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
|
|
129
|
+
-webkit-background-clip: text;
|
|
130
|
+
-webkit-text-fill-color: transparent;
|
|
131
|
+
background-clip: text;
|
|
132
|
+
}
|
|
133
|
+
.preview-subtitle {
|
|
134
|
+
font-size: 0.875rem;
|
|
135
|
+
opacity: 0.6;
|
|
136
|
+
margin: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.preview-section {
|
|
140
|
+
margin-bottom: 3rem;
|
|
141
|
+
}
|
|
142
|
+
.section-title {
|
|
143
|
+
font-size: 1.125rem;
|
|
144
|
+
font-weight: 600;
|
|
145
|
+
margin: 0 0 1rem;
|
|
146
|
+
padding-bottom: 0.5rem;
|
|
147
|
+
border-bottom: 1px solid rgba(128,128,128,0.12);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ── Swatches ────────────────────────────────────────────── */
|
|
151
|
+
.swatch-grid {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-wrap: wrap;
|
|
154
|
+
gap: 1rem;
|
|
155
|
+
}
|
|
156
|
+
.swatch {
|
|
157
|
+
width: 120px;
|
|
158
|
+
}
|
|
159
|
+
.swatch-color {
|
|
160
|
+
width: 120px;
|
|
161
|
+
height: 80px;
|
|
162
|
+
border-radius: 8px;
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: flex-end;
|
|
165
|
+
padding: 0.4rem;
|
|
166
|
+
border: 1px solid rgba(0,0,0,0.06);
|
|
167
|
+
}
|
|
168
|
+
.swatch-hex {
|
|
169
|
+
font-size: 0.65rem;
|
|
170
|
+
font-family: ui-monospace, monospace;
|
|
171
|
+
font-weight: 600;
|
|
172
|
+
letter-spacing: 0.02em;
|
|
173
|
+
}
|
|
174
|
+
.swatch-label {
|
|
175
|
+
margin-top: 0.4rem;
|
|
176
|
+
font-size: 0.75rem;
|
|
177
|
+
font-weight: 500;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ── Typography ──────────────────────────────────────────── */
|
|
181
|
+
.type-stack {
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 1.5rem;
|
|
185
|
+
}
|
|
186
|
+
.type-sample {
|
|
187
|
+
border-left: 3px solid var(--color-primary);
|
|
188
|
+
padding-left: 1rem;
|
|
189
|
+
}
|
|
190
|
+
.type-meta {
|
|
191
|
+
font-size: 0.7rem;
|
|
192
|
+
font-family: ui-monospace, monospace;
|
|
193
|
+
opacity: 0.55;
|
|
194
|
+
margin-bottom: 0.4rem;
|
|
195
|
+
}
|
|
196
|
+
.type-preview {
|
|
197
|
+
line-height: 1.3;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* ── Components ──────────────────────────────────────────── */
|
|
201
|
+
.component-grid {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-wrap: wrap;
|
|
204
|
+
gap: 2rem;
|
|
205
|
+
align-items: flex-start;
|
|
206
|
+
}
|
|
207
|
+
.component-item {
|
|
208
|
+
display: flex;
|
|
209
|
+
flex-direction: column;
|
|
210
|
+
gap: 0.5rem;
|
|
211
|
+
}
|
|
212
|
+
.component-label {
|
|
213
|
+
font-size: 0.7rem;
|
|
214
|
+
font-family: ui-monospace, monospace;
|
|
215
|
+
opacity: 0.55;
|
|
216
|
+
font-weight: 500;
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
letter-spacing: 0.06em;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ── Radius ──────────────────────────────────────────────── */
|
|
222
|
+
.radius-grid {
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-wrap: wrap;
|
|
225
|
+
gap: 1.5rem;
|
|
226
|
+
align-items: flex-end;
|
|
227
|
+
}
|
|
228
|
+
.radius-item {
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
align-items: center;
|
|
232
|
+
gap: 0.35rem;
|
|
233
|
+
}
|
|
234
|
+
.radius-demo {
|
|
235
|
+
width: 56px;
|
|
236
|
+
height: 56px;
|
|
237
|
+
background: var(--color-primary);
|
|
238
|
+
opacity: 0.8;
|
|
239
|
+
}
|
|
240
|
+
.radius-name {
|
|
241
|
+
font-size: 0.7rem;
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
}
|
|
244
|
+
.radius-value {
|
|
245
|
+
font-size: 0.65rem;
|
|
246
|
+
font-family: ui-monospace, monospace;
|
|
247
|
+
opacity: 0.55;
|
|
248
|
+
}
|
|
249
|
+
</style>
|
|
250
|
+
</head>
|
|
251
|
+
<body>
|
|
252
|
+
|
|
253
|
+
<header class="preview-header">
|
|
254
|
+
<h1 class="preview-title">${escapedName}</h1>
|
|
255
|
+
<p class="preview-subtitle">Design System Preview — generated from DTCG token sets</p>
|
|
256
|
+
</header>
|
|
257
|
+
|
|
258
|
+
<main>
|
|
259
|
+
${args.swatchesHtml}
|
|
260
|
+
${args.typographyHtml}
|
|
261
|
+
${args.componentsHtml}
|
|
262
|
+
${args.radiusHtml}
|
|
263
|
+
</main>
|
|
264
|
+
|
|
265
|
+
</body>
|
|
266
|
+
</html>`;
|
|
267
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,12 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
/**
|
|
3
3
|
* Supported design-sync backend providers.
|
|
4
4
|
*
|
|
5
|
-
* - `figma`
|
|
6
|
-
* - `penpot`
|
|
7
|
-
* - `git-only`
|
|
8
|
-
* - `memory`
|
|
5
|
+
* - `figma` — Figma + Tokens Studio plugin (DTCG via GitHub provider sync)
|
|
6
|
+
* - `penpot` — Penpot REST API (self-hostable, China-friendly)
|
|
7
|
+
* - `git-only` — No design tool; reads/writes local DTCG files directly
|
|
8
|
+
* - `memory` — In-memory test fixture (CI / unit tests only)
|
|
9
|
+
* - `design-md` — AI-native DESIGN.md format (@google/design.md) — markdown + YAML front matter
|
|
9
10
|
*/
|
|
10
|
-
export type DesignSyncProviderType = "figma" | "penpot" | "git-only" | "memory";
|
|
11
|
+
export type DesignSyncProviderType = "figma" | "penpot" | "git-only" | "memory" | "design-md";
|
|
11
12
|
/**
|
|
12
13
|
* A W3C Design Tokens Community Group (DTCG) leaf token.
|
|
13
14
|
* Every leaf MUST carry both `$value` and `$type`.
|
|
@@ -171,5 +172,20 @@ export interface MemoryProviderConfig extends BaseProviderConfig {
|
|
|
171
172
|
/** Optional initial in-memory token sets (used in tests). */
|
|
172
173
|
initialSets?: DesignTokenSet[];
|
|
173
174
|
}
|
|
174
|
-
export
|
|
175
|
+
export interface DesignMdProviderConfig extends BaseProviderConfig {
|
|
176
|
+
provider: "design-md";
|
|
177
|
+
/** Absolute path to the DESIGN.md file. Defaults to `process.cwd()/DESIGN.md`. */
|
|
178
|
+
designMdPath?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Design-system name emitted into the generated DESIGN.md front matter +
|
|
181
|
+
* Overview. Defaults to Nebutra branding.
|
|
182
|
+
*/
|
|
183
|
+
name?: string;
|
|
184
|
+
/**
|
|
185
|
+
* Design-system description emitted into the generated DESIGN.md front matter +
|
|
186
|
+
* Overview. Defaults to Nebutra branding.
|
|
187
|
+
*/
|
|
188
|
+
description?: string;
|
|
189
|
+
}
|
|
190
|
+
export type DesignSyncConfig = FigmaProviderConfig | PenpotProviderConfig | GitOnlyProviderConfig | MemoryProviderConfig | DesignMdProviderConfig;
|
|
175
191
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB;;;;;;;;GAQG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAI9F;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;iBAOlB,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,eAAe,CAAC;CAClD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,MAAM,EAAE,eAAe,CAAC;CACzB;AAID,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,oBAAoB;IACpB,QAAQ,EAAE,sBAAsB,CAAC;IACjC,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,oBAAoB;IACpB,QAAQ,EAAE,sBAAsB,CAAC;IACjC,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAID;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IAEtC;;;OAGG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEjD;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEjD;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;CACtC;AAID;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,QAAQ,EAAE,OAAO,CAAC;IAClB,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,QAAQ,EAAE,QAAQ,CAAC;IACnB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,QAAQ,EAAE,QAAQ,CAAC;IACnB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,QAAQ,EAAE,WAAW,CAAC;IACtB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,GACrB,oBAAoB,GACpB,sBAAsB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/design-sync",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Provider-agnostic design-tool sync. Figma + Tokens Studio | Penpot | git-only | memory.",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Provider-agnostic design-tool sync. Figma + Tokens Studio | Penpot | git-only | design-md | memory.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"nebutra": {
|
|
9
|
+
"graph": "core",
|
|
9
10
|
"status": "foundation",
|
|
10
11
|
"productionReady": false,
|
|
11
12
|
"requires": [
|
|
@@ -16,11 +17,12 @@
|
|
|
16
17
|
"gaps": [
|
|
17
18
|
"Figma push (Variables REST API) is dry-run; manual enable required",
|
|
18
19
|
"Penpot adapter implements healthcheck + pull; push is dry-run scaffolding",
|
|
19
|
-
"No real-time webhook listeners (manual pull/push only)"
|
|
20
|
+
"No real-time webhook listeners (manual pull/push only)",
|
|
21
|
+
"design-md provider depends on @google/design.md (alpha v0.x); pinned exactly — a 0.x bump may change linter rules and require re-validating the serializer output"
|
|
20
22
|
],
|
|
21
23
|
"featureId": "design-sync",
|
|
22
24
|
"category": "design",
|
|
23
|
-
"summary": "Provider-agnostic design-tool sync (Figma | Penpot | git-only)"
|
|
25
|
+
"summary": "Provider-agnostic design-tool sync (Figma | Penpot | git-only | design-md)"
|
|
24
26
|
},
|
|
25
27
|
"main": "./dist/index.js",
|
|
26
28
|
"types": "./dist/index.d.ts",
|
|
@@ -29,6 +31,14 @@
|
|
|
29
31
|
"types": "./dist/index.d.ts",
|
|
30
32
|
"import": "./dist/index.js"
|
|
31
33
|
},
|
|
34
|
+
"./design-md": {
|
|
35
|
+
"types": "./dist/providers/design-md.d.ts",
|
|
36
|
+
"import": "./dist/providers/design-md.js"
|
|
37
|
+
},
|
|
38
|
+
"./serialize/from-design-md": {
|
|
39
|
+
"types": "./dist/serialize/from-design-md.d.ts",
|
|
40
|
+
"import": "./dist/serialize/from-design-md.js"
|
|
41
|
+
},
|
|
32
42
|
"./figma": {
|
|
33
43
|
"types": "./dist/providers/figma.d.ts",
|
|
34
44
|
"import": "./dist/providers/figma.js"
|
|
@@ -48,6 +58,10 @@
|
|
|
48
58
|
"./cli": {
|
|
49
59
|
"types": "./dist/cli/index.d.ts",
|
|
50
60
|
"import": "./dist/cli/index.js"
|
|
61
|
+
},
|
|
62
|
+
"./to-brand-package": {
|
|
63
|
+
"types": "./dist/serialize/to-brand-package.d.ts",
|
|
64
|
+
"import": "./dist/serialize/to-brand-package.js"
|
|
51
65
|
}
|
|
52
66
|
},
|
|
53
67
|
"bin": {
|
|
@@ -58,11 +72,13 @@
|
|
|
58
72
|
"README.md"
|
|
59
73
|
],
|
|
60
74
|
"dependencies": {
|
|
75
|
+
"@google/design.md": "0.2.0",
|
|
61
76
|
"zod": "^4.3.6",
|
|
62
|
-
"@nebutra/logger": "0.
|
|
77
|
+
"@nebutra/logger": "2.0.0",
|
|
78
|
+
"@nebutra/tokens": "2.0.0"
|
|
63
79
|
},
|
|
64
80
|
"devDependencies": {
|
|
65
|
-
"@types/node": "^
|
|
81
|
+
"@types/node": "^25.9.1",
|
|
66
82
|
"tsx": "^4.21.0",
|
|
67
83
|
"typescript": "^5.9.3",
|
|
68
84
|
"vitest": "^4.1.4"
|
package/src/cli/index.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { describeEnv } from "../detect";
|
|
19
19
|
import { createDesignSync } from "../factory";
|
|
20
|
+
import { compileBrandFromTokenSets } from "../serialize/to-brand-package";
|
|
20
21
|
import type { DesignSyncProviderType } from "../types";
|
|
21
22
|
|
|
22
23
|
interface ParsedArgs {
|
|
@@ -25,6 +26,8 @@ interface ParsedArgs {
|
|
|
25
26
|
json: boolean;
|
|
26
27
|
themes: string[];
|
|
27
28
|
provider: DesignSyncProviderType | undefined;
|
|
29
|
+
brandId: string | undefined;
|
|
30
|
+
brandName: string | undefined;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -33,6 +36,8 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
33
36
|
let json = false;
|
|
34
37
|
const themes: string[] = [];
|
|
35
38
|
let provider: DesignSyncProviderType | undefined;
|
|
39
|
+
let brandId: string | undefined;
|
|
40
|
+
let brandName: string | undefined;
|
|
36
41
|
|
|
37
42
|
for (let i = 0; i < rest.length; i++) {
|
|
38
43
|
const arg = rest[i];
|
|
@@ -53,10 +58,14 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
53
58
|
provider = next.trim() as DesignSyncProviderType;
|
|
54
59
|
i++;
|
|
55
60
|
}
|
|
61
|
+
} else if (arg === "--id") {
|
|
62
|
+
brandId = rest[++i];
|
|
63
|
+
} else if (arg === "--name") {
|
|
64
|
+
brandName = rest[++i];
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
return { command, dryRun, json, themes, provider };
|
|
68
|
+
return { command, dryRun, json, themes, provider, brandId, brandName };
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
function emit(payload: unknown, json: boolean): void {
|
|
@@ -88,12 +97,15 @@ Commands:
|
|
|
88
97
|
detect Print the resolved provider + env diagnostics
|
|
89
98
|
healthcheck Run the provider's healthcheck()
|
|
90
99
|
pull Pull design-tool → repo (DTCG JSON)
|
|
91
|
-
push Push repo → design-tool (defaults to dry-run on figma/penpot)
|
|
100
|
+
push Push repo → design-tool (defaults to dry-run on figma/penpot/design-md)
|
|
101
|
+
brand Pull tokens then compile a Brand Package (Create Center)
|
|
92
102
|
help Show this message
|
|
93
103
|
|
|
94
104
|
Options:
|
|
95
|
-
--provider <X> Override provider (figma|penpot|git-only|memory)
|
|
105
|
+
--provider <X> Override provider (figma|penpot|git-only|memory|design-md)
|
|
96
106
|
--themes a,b Restrict to specific token sets
|
|
107
|
+
--id <id> Brand package id (for brand command)
|
|
108
|
+
--name <name> Brand package display name
|
|
97
109
|
--dry-run Force dry-run on push
|
|
98
110
|
--json Emit machine-readable JSON
|
|
99
111
|
`;
|
|
@@ -149,6 +161,27 @@ export async function run(argv: string[] = process.argv): Promise<number> {
|
|
|
149
161
|
return 0;
|
|
150
162
|
}
|
|
151
163
|
|
|
164
|
+
if (args.command === "brand") {
|
|
165
|
+
const themes = args.themes.length > 0 ? { themes: args.themes } : {};
|
|
166
|
+
const pull = await provider.pull(themes);
|
|
167
|
+
const compiled = compileBrandFromTokenSets(pull.sets, {
|
|
168
|
+
...(args.brandId ? { id: args.brandId } : {}),
|
|
169
|
+
...(args.brandName ? { name: args.brandName } : {}),
|
|
170
|
+
});
|
|
171
|
+
emit(
|
|
172
|
+
args.json
|
|
173
|
+
? {
|
|
174
|
+
provider: pull.provider,
|
|
175
|
+
brand: compiled.brand,
|
|
176
|
+
css: compiled.css,
|
|
177
|
+
warnings: compiled.warnings,
|
|
178
|
+
}
|
|
179
|
+
: `brand=${compiled.brand.id} recipe=${compiled.brand.recipe.buttonDefault} fonts=${compiled.brand.typography.faces?.length ?? 0} warnings=${compiled.warnings.length}`,
|
|
180
|
+
args.json,
|
|
181
|
+
);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
152
185
|
emitError(`unknown command: ${args.command}\n\n${HELP_TEXT}`, args.json);
|
|
153
186
|
return 2;
|
|
154
187
|
} catch (error) {
|