@proto.ui/cli 0.0.1 → 0.0.2

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.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/package.json +1 -1
  3. package/src/cli.mjs +262 -12
package/README.md CHANGED
@@ -4,6 +4,13 @@ Local CLI package for generating Proto UI Tailwind assets.
4
4
 
5
5
  ## Commands
6
6
 
7
+ - `proto-ui shadcn [--styles-dir <dir>]`
7
8
  - `proto-ui tokens --input <dir> --out <file>`
8
9
  - `proto-ui tailwindcss --out <file> [--theme-import <path>] [--tokens-import <path>]`
9
10
  - `proto-ui theme shadcn --out <file>`
11
+
12
+ `proto-ui shadcn` writes these preset files directly:
13
+
14
+ - `prototype-tokens.generated.css`
15
+ - `tailwindcss.css`
16
+ - `shadcn-theme.css`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proto.ui/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -7,6 +7,203 @@ const DEFAULT_THEME_NAME = 'shadcn';
7
7
  const DEFAULT_THEME_IMPORT = './shadcn-theme.css';
8
8
  const DEFAULT_TOKENS_IMPORT = './prototype-tokens.generated.css';
9
9
 
10
+ const SHADCN_TOKENS_CSS = `/* This file is auto-generated by apps/www/scripts/generate-prototype-tailwind-sources.mjs. */
11
+ /* Do not edit by hand. */
12
+
13
+ @source inline("absolute");
14
+ @source inline("active:bg-background");
15
+ @source inline("active:bg-muted");
16
+ @source inline("active:bg-muted/80");
17
+ @source inline("active:scale-[0.98]");
18
+ @source inline("active:scale-[0.99]");
19
+ @source inline("active:shadow-xs");
20
+ @source inline("active:text-foreground");
21
+ @source inline("active:translate-y-px");
22
+ @source inline("aria-checked:bg-accent");
23
+ @source inline("aria-checked:bg-muted");
24
+ @source inline("aria-checked:bg-primary");
25
+ @source inline("aria-checked:pl-[22px]");
26
+ @source inline("aria-checked:pr-0.5");
27
+ @source inline("aria-checked:text-accent-foreground");
28
+ @source inline("aria-checked:text-muted-foreground");
29
+ @source inline("aria-checked:text-primary-foreground");
30
+ @source inline("aria-current:hidden");
31
+ @source inline("aria-expanded:bg-muted");
32
+ @source inline("aria-expanded:bg-secondary");
33
+ @source inline("aria-expanded:text-foreground");
34
+ @source inline("aria-expanded:text-secondary-foreground");
35
+ @source inline("aria-invalid:border-destructive");
36
+ @source inline("aria-invalid:ring-3");
37
+ @source inline("aria-invalid:ring-destructive/20");
38
+ @source inline("aria-selected:bg-background");
39
+ @source inline("aria-selected:shadow-xs");
40
+ @source inline("aria-selected:text-foreground");
41
+ @source inline("backdrop-blur-xs");
42
+ @source inline("bg-accent");
43
+ @source inline("bg-background");
44
+ @source inline("bg-background/70");
45
+ @source inline("bg-clip-padding");
46
+ @source inline("bg-destructive/10");
47
+ @source inline("bg-destructive/20");
48
+ @source inline("bg-destructive/30");
49
+ @source inline("bg-input");
50
+ @source inline("bg-input/30");
51
+ @source inline("bg-input/50");
52
+ @source inline("bg-input/80");
53
+ @source inline("bg-muted");
54
+ @source inline("bg-muted/60");
55
+ @source inline("bg-muted/80");
56
+ @source inline("bg-primary");
57
+ @source inline("bg-primary/80");
58
+ @source inline("bg-primary/90");
59
+ @source inline("bg-secondary");
60
+ @source inline("bg-secondary/80");
61
+ @source inline("bg-transparent");
62
+ @source inline("block");
63
+ @source inline("border");
64
+ @source inline("border-border");
65
+ @source inline("border-border/50");
66
+ @source inline("border-border/60");
67
+ @source inline("border-destructive");
68
+ @source inline("border-destructive/40");
69
+ @source inline("border-destructive/50");
70
+ @source inline("border-input");
71
+ @source inline("border-ring");
72
+ @source inline("border-transparent");
73
+ @source inline("cursor-default");
74
+ @source inline("dark:aria-checked:bg-input/50");
75
+ @source inline("dark:aria-checked:bg-primary");
76
+ @source inline("dark:aria-invalid:border-destructive/50");
77
+ @source inline("dark:aria-invalid:ring-destructive/40");
78
+ @source inline("dark:bg-destructive/20");
79
+ @source inline("dark:bg-input/30");
80
+ @source inline("dark:border-input");
81
+ @source inline("dark:data-[focus-visible]:ring-destructive/40");
82
+ @source inline("dark:hover:bg-destructive/30");
83
+ @source inline("dark:hover:bg-input/50");
84
+ @source inline("data-[disabled]:opacity-50");
85
+ @source inline("data-[disabled]:pointer-events-none");
86
+ @source inline("data-[focus-visible]:bg-background");
87
+ @source inline("data-[focus-visible]:border-destructive/40");
88
+ @source inline("data-[focus-visible]:border-ring");
89
+ @source inline("data-[focus-visible]:ring-2");
90
+ @source inline("data-[focus-visible]:ring-3");
91
+ @source inline("data-[focus-visible]:ring-destructive/20");
92
+ @source inline("data-[focus-visible]:ring-inset");
93
+ @source inline("data-[focus-visible]:ring-offset-2");
94
+ @source inline("data-[focus-visible]:ring-offset-background");
95
+ @source inline("data-[focus-visible]:ring-ring/40");
96
+ @source inline("data-[focus-visible]:ring-ring/50");
97
+ @source inline("data-[focus-visible]:shadow-xs");
98
+ @source inline("data-[focus-visible]:text-foreground");
99
+ @source inline("duration-200");
100
+ @source inline("ease-in-out");
101
+ @source inline("flex");
102
+ @source inline("flex-col");
103
+ @source inline("font-medium");
104
+ @source inline("gap-1");
105
+ @source inline("gap-1.5");
106
+ @source inline("gap-2");
107
+ @source inline("gap-3");
108
+ @source inline("group/button");
109
+ @source inline("h-10");
110
+ @source inline("h-6");
111
+ @source inline("h-7");
112
+ @source inline("h-8");
113
+ @source inline("h-9");
114
+ @source inline("hidden");
115
+ @source inline("hover:aria-checked:bg-input");
116
+ @source inline("hover:aria-checked:bg-muted/60");
117
+ @source inline("hover:aria-checked:bg-primary/90");
118
+ @source inline("hover:aria-checked:text-foreground");
119
+ @source inline("hover:aria-selected:bg-background/70");
120
+ @source inline("hover:aria-selected:shadow-xs");
121
+ @source inline("hover:aria-selected:text-foreground");
122
+ @source inline("hover:bg-destructive/20");
123
+ @source inline("hover:bg-muted");
124
+ @source inline("hover:bg-primary/80");
125
+ @source inline("hover:bg-secondary/80");
126
+ @source inline("hover:data-[focus-visible]:data-[focused]:bg-muted");
127
+ @source inline("hover:data-[focus-visible]:data-[focused]:text-foreground");
128
+ @source inline("hover:text-foreground");
129
+ @source inline("hover:underline");
130
+ @source inline("inline-flex");
131
+ @source inline("items-center");
132
+ @source inline("items-start");
133
+ @source inline("justify-center");
134
+ @source inline("leading-6");
135
+ @source inline("left-0");
136
+ @source inline("min-h-28");
137
+ @source inline("min-w-10");
138
+ @source inline("min-w-56");
139
+ @source inline("min-w-8");
140
+ @source inline("min-w-9");
141
+ @source inline("mt-2");
142
+ @source inline("opacity-50");
143
+ @source inline("outline-none");
144
+ @source inline("overflow-hidden");
145
+ @source inline("p-1");
146
+ @source inline("p-4");
147
+ @source inline("peer");
148
+ @source inline("pl-0.5");
149
+ @source inline("pl-[22px]");
150
+ @source inline("pointer-events-none");
151
+ @source inline("pr-0.5");
152
+ @source inline("pr-[22px]");
153
+ @source inline("px-2.5");
154
+ @source inline("px-3");
155
+ @source inline("px-5");
156
+ @source inline("py-1.5");
157
+ @source inline("py-2");
158
+ @source inline("relative");
159
+ @source inline("ring-0");
160
+ @source inline("ring-2");
161
+ @source inline("ring-3");
162
+ @source inline("ring-destructive/20");
163
+ @source inline("ring-destructive/40");
164
+ @source inline("ring-inset");
165
+ @source inline("ring-offset-2");
166
+ @source inline("ring-offset-background");
167
+ @source inline("ring-ring/40");
168
+ @source inline("ring-ring/50");
169
+ @source inline("rounded-[min(var(--radius-md),12px)]");
170
+ @source inline("rounded-full");
171
+ @source inline("rounded-lg");
172
+ @source inline("rounded-md");
173
+ @source inline("rounded-xl");
174
+ @source inline("scale-[0.98]");
175
+ @source inline("scale-[0.99]");
176
+ @source inline("select-none");
177
+ @source inline("shadow-lg");
178
+ @source inline("shadow-xs");
179
+ @source inline("shrink-0");
180
+ @source inline("size-5");
181
+ @source inline("size-8");
182
+ @source inline("text-[0.8rem]");
183
+ @source inline("text-accent-foreground");
184
+ @source inline("text-destructive");
185
+ @source inline("text-foreground");
186
+ @source inline("text-left");
187
+ @source inline("text-muted-foreground");
188
+ @source inline("text-primary");
189
+ @source inline("text-primary-foreground");
190
+ @source inline("text-secondary-foreground");
191
+ @source inline("text-sm");
192
+ @source inline("top-full");
193
+ @source inline("transition-all");
194
+ @source inline("transition-colors");
195
+ @source inline("translate-x-0");
196
+ @source inline("translate-y-px");
197
+ @source inline("underline");
198
+ @source inline("underline-offset-4");
199
+ @source inline("w-11");
200
+ @source inline("w-80");
201
+ @source inline("w-full");
202
+ @source inline("whitespace-nowrap");
203
+ @source inline("will-change-transform");
204
+ @source inline("z-50");
205
+ `;
206
+
10
207
  const SHADCN_THEME_CSS = `:root {
11
208
  --radius: 0.625rem;
12
209
  --background: lab(100% 0 0);
@@ -150,6 +347,11 @@ const TAILWIND_BASE_TEMPLATE = `@import 'tailwindcss';
150
347
  --color-selection-foreground: var(--selection-foreground);
151
348
  }
152
349
 
350
+ @font-face {
351
+ font-family: 'GeistSans';
352
+ src: url('./assets/font/GeistVF.woff2') format('woff2-variations');
353
+ }
354
+
153
355
  @layer base {
154
356
  :root {
155
357
  --radius-xl: calc(var(--radius) + 4px);
@@ -166,6 +368,31 @@ const TAILWIND_BASE_TEMPLATE = `@import 'tailwindcss';
166
368
  body {
167
369
  background-color: var(--color-background);
168
370
  color: var(--color-foreground);
371
+ font-family:
372
+ var(--color-font-geist-sans),
373
+ ui-sans-serif,
374
+ system-ui,
375
+ -apple-system,
376
+ BlinkMacSystemFont,
377
+ 'Segoe UI',
378
+ Roboto,
379
+ 'Helvetica Neue',
380
+ Arial,
381
+ 'Noto Sans',
382
+ sans-serif,
383
+ 'Apple Color Emoji',
384
+ 'Segoe UI Emoji',
385
+ 'Segoe UI Symbol',
386
+ 'Noto Color Emoji';
387
+ text-rendering: optimizeLegibility;
388
+ -webkit-font-smoothing: antialiased;
389
+ -moz-osx-font-smoothing: grayscale;
390
+ unicode-bidi: isolate;
391
+ font-feature-settings: normal;
392
+ font-synthesis-weight: none;
393
+ font-variation-settings: normal;
394
+ text-size-adjust: 100%;
395
+ -webkit-text-size-adjust: 100%;
169
396
  }
170
397
  `;
171
398
 
@@ -199,13 +426,13 @@ function printHelp() {
199
426
  console.log(`proto-ui
200
427
 
201
428
  Usage:
202
- proto-ui <theme> [--prototypes <dir>] [--styles-dir <dir>]
429
+ proto-ui <theme> [--styles-dir <dir>]
203
430
  proto-ui tokens --input <dir> --out <file>
204
431
  proto-ui tailwindcss [--theme-import <path>] [--tokens-import <path>] --out <file>
205
432
  proto-ui theme <name> --out <file>
206
433
 
207
434
  Examples:
208
- proto-ui shadcn --prototypes ./src/prototypes --styles-dir ./src/styles
435
+ proto-ui shadcn --styles-dir ./src/styles
209
436
  proto-ui tokens --input ./packages/prototypes --out ./src/styles/prototype-tokens.generated.css
210
437
  proto-ui tailwindcss --out ./src/styles/tailwindcss.css
211
438
  proto-ui theme shadcn --out ./src/styles/shadcn-theme.css
@@ -214,18 +441,27 @@ Examples:
214
441
 
215
442
  async function runPreset(themeName, args) {
216
443
  const options = parseOptions(args);
217
- const prototypesDir = options.prototypes ?? './src/prototypes';
444
+ const normalizedTheme = themeName.toLowerCase();
445
+ if (normalizedTheme !== DEFAULT_THEME_NAME) {
446
+ throw new Error(
447
+ `unsupported theme "${themeName}". currently supported: ${DEFAULT_THEME_NAME}.`
448
+ );
449
+ }
218
450
  const stylesDir = options['styles-dir'] ?? './src/styles';
219
451
  const tokensFileName = options['tokens-file'] ?? 'prototype-tokens.generated.css';
220
452
  const tailwindFileName = options['tailwind-file'] ?? 'tailwindcss.css';
221
- const themeFileName = options['theme-file'] ?? `${themeName}-theme.css`;
453
+ const themeFileName = options['theme-file'] ?? `${normalizedTheme}-theme.css`;
222
454
 
223
455
  const tokensOut = path.join(stylesDir, tokensFileName);
224
456
  const themeOut = path.join(stylesDir, themeFileName);
225
457
  const tailwindOut = path.join(stylesDir, tailwindFileName);
226
458
 
227
- await runGenerateTokens(['--input', prototypesDir, '--out', tokensOut]);
228
- await runGenerateTheme([themeName, '--out', themeOut]);
459
+ const tokensOutputFile = path.resolve(process.cwd(), tokensOut);
460
+ await ensureDirectory(tokensOutputFile);
461
+ await fs.writeFile(tokensOutputFile, SHADCN_TOKENS_CSS, 'utf8');
462
+ console.log(`[proto-ui] tokens(preset): wrote ${relativeToCwd(tokensOutputFile)}`);
463
+
464
+ await runGenerateTheme([normalizedTheme, '--out', themeOut]);
229
465
 
230
466
  const tailwindAbs = path.resolve(process.cwd(), tailwindOut);
231
467
  const themeImport = toCssImportPath(tailwindAbs, path.resolve(process.cwd(), themeOut));
@@ -241,7 +477,7 @@ async function runPreset(themeName, args) {
241
477
  ]);
242
478
 
243
479
  console.log(
244
- `[proto-ui] setup(${themeName}): completed tokens + theme + tailwindcss in ${stylesDir}`
480
+ `[proto-ui] setup(${normalizedTheme}): completed tokens + theme + tailwindcss in ${stylesDir}`
245
481
  );
246
482
  }
247
483
 
@@ -252,7 +488,7 @@ async function runGenerateTokens(args) {
252
488
  const root = path.resolve(process.cwd(), input);
253
489
  const outputFile = path.resolve(process.cwd(), outFile);
254
490
 
255
- const files = await collectTsFiles(root);
491
+ const files = await collectSourceFiles(root);
256
492
  const tokens = new Set();
257
493
 
258
494
  for (const file of files) {
@@ -262,7 +498,7 @@ async function runGenerateTokens(args) {
262
498
  sourceText,
263
499
  ts.ScriptTarget.Latest,
264
500
  true,
265
- file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS
501
+ scriptKindForFile(file)
266
502
  );
267
503
  const scope = createScope();
268
504
  walk(sourceFile, scope, tokens);
@@ -775,17 +1011,31 @@ function escapeForCss(token) {
775
1011
  return token.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
776
1012
  }
777
1013
 
778
- async function collectTsFiles(dir) {
1014
+ function scriptKindForFile(file) {
1015
+ const ext = path.extname(file).toLowerCase();
1016
+ if (ext === '.tsx') return ts.ScriptKind.TSX;
1017
+ if (ext === '.jsx') return ts.ScriptKind.JSX;
1018
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') return ts.ScriptKind.JS;
1019
+ return ts.ScriptKind.TS;
1020
+ }
1021
+
1022
+ async function collectSourceFiles(dir) {
779
1023
  const out = [];
780
1024
  const entries = await fs.readdir(dir, { withFileTypes: true });
781
1025
  for (const entry of entries) {
782
1026
  const fullPath = path.join(dir, entry.name);
783
1027
  if (entry.isDirectory()) {
784
1028
  if (entry.name === 'dist' || entry.name === 'test' || entry.name === 'node_modules') continue;
785
- out.push(...(await collectTsFiles(fullPath)));
1029
+ out.push(...(await collectSourceFiles(fullPath)));
786
1030
  continue;
787
1031
  }
788
- if (entry.isFile() && /\.(ts|tsx|mts|cts)$/.test(entry.name)) out.push(fullPath);
1032
+ if (
1033
+ entry.isFile() &&
1034
+ /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/.test(entry.name) &&
1035
+ !/\.d\.ts$/i.test(entry.name)
1036
+ ) {
1037
+ out.push(fullPath);
1038
+ }
789
1039
  }
790
1040
  return out;
791
1041
  }