stera-ui 0.5.0 → 0.6.1

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,209 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CHECK,
4
+ CROSS,
5
+ brand
6
+ } from "./chunk-KGMF6UW4.js";
7
+
8
+ // src/schema/index.ts
9
+ import { z } from "zod";
10
+ var registryItemTypeSchema = z.enum([
11
+ "registry:ui",
12
+ "registry:component",
13
+ "registry:hook",
14
+ "registry:lib",
15
+ "registry:style",
16
+ "registry:font"
17
+ ]);
18
+ var registryItemFileSchema = z.object({
19
+ path: z.string(),
20
+ type: registryItemTypeSchema,
21
+ content: z.string().optional(),
22
+ target: z.string().optional()
23
+ });
24
+ var registryItemFontSchema = z.object({
25
+ family: z.string(),
26
+ provider: z.literal("google"),
27
+ import: z.string(),
28
+ variable: z.string(),
29
+ weight: z.array(z.string()).optional(),
30
+ subsets: z.array(z.string()).optional(),
31
+ selector: z.string().optional(),
32
+ dependency: z.string().optional()
33
+ });
34
+ var registryItemCommonSchema = z.object({
35
+ name: z.string(),
36
+ dependencies: z.array(z.string()).optional(),
37
+ registryDependencies: z.array(z.string()).optional(),
38
+ files: z.array(registryItemFileSchema).optional()
39
+ });
40
+ var registryItemSchema = z.discriminatedUnion("type", [
41
+ registryItemCommonSchema.extend({
42
+ type: z.literal("registry:font"),
43
+ font: registryItemFontSchema,
44
+ files: z.array(registryItemFileSchema).optional()
45
+ }),
46
+ registryItemCommonSchema.extend({
47
+ type: registryItemTypeSchema.exclude(["registry:font"]),
48
+ files: z.array(registryItemFileSchema)
49
+ })
50
+ ]);
51
+ var registryIndexEntrySchema = z.object({
52
+ name: z.string(),
53
+ type: z.string(),
54
+ dependencies: z.array(z.string()).optional(),
55
+ registryDependencies: z.array(z.string()).optional()
56
+ });
57
+ var registryIndexSchema = z.object({
58
+ name: z.string(),
59
+ items: z.array(registryIndexEntrySchema)
60
+ });
61
+ var configSchema = z.object({
62
+ $schema: z.string().optional(),
63
+ version: z.literal(1),
64
+ css: z.string(),
65
+ aliases: z.object({
66
+ components: z.string(),
67
+ utils: z.string(),
68
+ ui: z.string(),
69
+ lib: z.string(),
70
+ hooks: z.string()
71
+ }),
72
+ fonts: z.object({
73
+ strategy: z.enum(["stera-default", "keep-existing", "skip"]),
74
+ nextFont: z.boolean().optional()
75
+ }).optional()
76
+ });
77
+
78
+ // src/registry/index.ts
79
+ var REGISTRY_URL = process.env.REGISTRY_URL ?? "https://ui.stera.sh/r";
80
+ var FETCH_TIMEOUT_MS = 15e3;
81
+ var ComponentNotFoundError = class extends Error {
82
+ components;
83
+ constructor(components) {
84
+ super(
85
+ components.length === 1 ? `Component "${components[0]}" not found in registry.` : `Components not found in registry: ${components.map((c) => `"${c}"`).join(", ")}.`
86
+ );
87
+ this.name = "ComponentNotFoundError";
88
+ this.components = components;
89
+ }
90
+ };
91
+ var _indexCache = null;
92
+ async function fetchJSON(url) {
93
+ let res;
94
+ try {
95
+ res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
96
+ } catch (err) {
97
+ if (err instanceof Error && err.name === "TimeoutError") {
98
+ throw new Error(
99
+ `Timed out fetching ${url} after ${FETCH_TIMEOUT_MS / 1e3}s. Check your network connection.`
100
+ );
101
+ }
102
+ throw new Error(
103
+ `Failed to fetch ${url}: ${err instanceof Error ? err.message : String(err)}`
104
+ );
105
+ }
106
+ if (!res.ok) {
107
+ throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
108
+ }
109
+ return res.json();
110
+ }
111
+ async function fetchRegistryIndex() {
112
+ if (_indexCache) return _indexCache;
113
+ const data = await fetchJSON(`${REGISTRY_URL}/index.json`);
114
+ const parsed = registryIndexSchema.safeParse(data);
115
+ if (!parsed.success) {
116
+ throw new Error(
117
+ `Registry index at ${REGISTRY_URL}/index.json returned unexpected data.`
118
+ );
119
+ }
120
+ _indexCache = parsed.data.items;
121
+ return _indexCache;
122
+ }
123
+ async function fetchRegistryItem(name) {
124
+ const data = await fetchJSON(`${REGISTRY_URL}/${name}.json`);
125
+ const parsed = registryItemSchema.safeParse(data);
126
+ if (!parsed.success) {
127
+ throw new Error(
128
+ `Registry item "${name}" returned unexpected data from ${REGISTRY_URL}/${name}.json.`
129
+ );
130
+ }
131
+ return parsed.data;
132
+ }
133
+ async function getAllComponents() {
134
+ return fetchRegistryIndex();
135
+ }
136
+ async function resolveDependencies(names) {
137
+ const index = await fetchRegistryIndex();
138
+ const byName = new Map(index.map((entry) => [entry.name, entry]));
139
+ const needed = [];
140
+ const seen = /* @__PURE__ */ new Set();
141
+ const missing = [];
142
+ const queue = [...names];
143
+ while (queue.length > 0) {
144
+ const name = queue.shift();
145
+ if (seen.has(name)) continue;
146
+ seen.add(name);
147
+ const entry = byName.get(name);
148
+ if (!entry) {
149
+ missing.push(name);
150
+ continue;
151
+ }
152
+ needed.push(name);
153
+ queue.push(...entry.registryDependencies ?? []);
154
+ }
155
+ if (missing.length > 0) {
156
+ throw new ComponentNotFoundError(missing);
157
+ }
158
+ return Promise.all(needed.map((name) => fetchRegistryItem(name)));
159
+ }
160
+
161
+ // src/utils/spinner.ts
162
+ var FRAMES = ["\u2022", "\u2726", "\u2736", "\u273B"].map(brand);
163
+ var INTERVAL_MS = 150;
164
+ function createSpinner(text) {
165
+ const isTTY = process.stdout.isTTY;
166
+ if (!isTTY) {
167
+ process.stdout.write(` ${text}...
168
+ `);
169
+ return {
170
+ succeed(msg) {
171
+ process.stdout.write(` ${CHECK} ${msg}
172
+ `);
173
+ },
174
+ fail(msg) {
175
+ process.stdout.write(` ${CROSS} ${msg}
176
+ `);
177
+ }
178
+ };
179
+ }
180
+ let frameIndex = 0;
181
+ const interval = setInterval(() => {
182
+ process.stdout.write(`\r ${FRAMES[frameIndex % FRAMES.length]} ${text}`);
183
+ frameIndex++;
184
+ }, INTERVAL_MS);
185
+ const clear = () => {
186
+ clearInterval(interval);
187
+ process.stdout.write("\r" + " ".repeat(text.length + 6) + "\r");
188
+ };
189
+ return {
190
+ succeed(msg) {
191
+ clear();
192
+ process.stdout.write(` ${CHECK} ${msg}
193
+ `);
194
+ },
195
+ fail(msg) {
196
+ clear();
197
+ process.stdout.write(` ${CROSS} ${msg}
198
+ `);
199
+ }
200
+ };
201
+ }
202
+
203
+ export {
204
+ ComponentNotFoundError,
205
+ fetchRegistryItem,
206
+ getAllComponents,
207
+ resolveDependencies,
208
+ createSpinner
209
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  fetchRegistryItem
3
- } from "./chunk-T3QWOVJR.js";
3
+ } from "./chunk-BJNLGEZJ.js";
4
4
 
5
5
  // src/utils/transform.ts
6
6
  function applyTransforms(content, ctx, transforms) {
@@ -22,19 +22,31 @@ var transformImports = (content, ctx) => {
22
22
  const { aliases } = ctx.config;
23
23
  const rules = [];
24
24
  if (aliases.utils !== DEFAULT_ALIASES.utils) {
25
- rules.push({ type: "exact", from: DEFAULT_ALIASES.utils, to: aliases.utils });
25
+ rules.push({
26
+ type: "exact",
27
+ from: DEFAULT_ALIASES.utils,
28
+ to: aliases.utils
29
+ });
26
30
  }
27
31
  if (aliases.ui !== DEFAULT_ALIASES.ui) {
28
32
  rules.push({ type: "prefix", from: DEFAULT_ALIASES.ui, to: aliases.ui });
29
33
  }
30
34
  if (aliases.components !== DEFAULT_ALIASES.components) {
31
- rules.push({ type: "prefix", from: DEFAULT_ALIASES.components, to: aliases.components });
35
+ rules.push({
36
+ type: "prefix",
37
+ from: DEFAULT_ALIASES.components,
38
+ to: aliases.components
39
+ });
32
40
  }
33
41
  if (aliases.lib !== DEFAULT_ALIASES.lib) {
34
42
  rules.push({ type: "prefix", from: DEFAULT_ALIASES.lib, to: aliases.lib });
35
43
  }
36
44
  if (aliases.hooks !== DEFAULT_ALIASES.hooks) {
37
- rules.push({ type: "prefix", from: DEFAULT_ALIASES.hooks, to: aliases.hooks });
45
+ rules.push({
46
+ type: "prefix",
47
+ from: DEFAULT_ALIASES.hooks,
48
+ to: aliases.hooks
49
+ });
38
50
  }
39
51
  if (rules.length === 0) {
40
52
  return content;
@@ -68,16 +80,30 @@ var TYPE_TO_ALIAS = {
68
80
  "registry:hook": "hooks",
69
81
  "registry:component": "components"
70
82
  };
71
- function resolveAlias(alias, projectRoot) {
83
+ var matcherCache = /* @__PURE__ */ new Map();
84
+ function getTsConfigMatcher(projectRoot) {
85
+ let matcher = matcherCache.get(projectRoot);
86
+ if (matcher !== void 0) return matcher;
72
87
  const tsConfig = loadConfig(projectRoot);
73
- if (tsConfig.resultType === "success" && Object.keys(tsConfig.paths).length > 0) {
74
- const hasMatchingPrefix = Object.keys(tsConfig.paths).some((key) => {
88
+ matcher = tsConfig.resultType === "success" && Object.keys(tsConfig.paths).length > 0 ? {
89
+ paths: tsConfig.paths,
90
+ match: createMatchPath(tsConfig.absoluteBaseUrl, tsConfig.paths)
91
+ } : null;
92
+ matcherCache.set(projectRoot, matcher);
93
+ return matcher;
94
+ }
95
+ function resolveAlias(alias, projectRoot) {
96
+ const matcher = getTsConfigMatcher(projectRoot);
97
+ if (matcher) {
98
+ const hasMatchingPrefix = Object.keys(matcher.paths).some((key) => {
75
99
  const prefix = key.replace(/\/\*$/, "");
76
100
  return alias === prefix || alias.startsWith(`${prefix}/`);
77
101
  });
78
102
  if (hasMatchingPrefix) {
79
- const match = createMatchPath(tsConfig.absoluteBaseUrl, tsConfig.paths);
80
- const resolved = match(alias, void 0, () => true, [".ts", ".tsx"]);
103
+ const resolved = matcher.match(alias, void 0, () => true, [
104
+ ".ts",
105
+ ".tsx"
106
+ ]);
81
107
  if (resolved) {
82
108
  return path.relative(projectRoot, resolved);
83
109
  }
@@ -379,7 +405,9 @@ async function installDependencies(deps, cwd) {
379
405
  if (fs5.existsSync(pkgPath)) {
380
406
  try {
381
407
  const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
382
- existing = new Set(Object.keys({ ...pkg.dependencies, ...pkg.devDependencies }));
408
+ existing = new Set(
409
+ Object.keys({ ...pkg.dependencies, ...pkg.devDependencies })
410
+ );
383
411
  } catch {
384
412
  }
385
413
  }
@@ -407,19 +435,18 @@ or use "pnpm add -w <package>" manually to install at the root.`
407
435
  // src/utils/write-files.ts
408
436
  import fs6 from "fs";
409
437
  import path6 from "path";
410
- import readline from "readline/promises";
438
+ import { confirm } from "@inquirer/prompts";
411
439
  async function confirmOverwrite(relativePath) {
412
440
  if (!process.stdin.isTTY) {
413
- console.log(` Skipping ${relativePath} (already exists, use --overwrite to replace)`);
441
+ console.log(
442
+ ` Skipping ${relativePath} (already exists, use --overwrite to replace)`
443
+ );
414
444
  return false;
415
445
  }
416
- const rl = readline.createInterface({
417
- input: process.stdin,
418
- output: process.stdout
446
+ return confirm({
447
+ message: `${relativePath} already exists. Overwrite?`,
448
+ default: false
419
449
  });
420
- const answer = await rl.question(` ${relativePath} already exists. Overwrite? (y/N) `);
421
- rl.close();
422
- return answer.toLowerCase() === "y";
423
450
  }
424
451
  async function writeComponentFiles(items, config, projectRoot, options = {}) {
425
452
  const written = [];
@@ -431,13 +458,13 @@ async function writeComponentFiles(items, config, projectRoot, options = {}) {
431
458
  if (fs6.existsSync(outputPath)) {
432
459
  const existing = fs6.readFileSync(outputPath, "utf-8");
433
460
  if (existing === file.content) {
434
- skipped.push(relativePath);
461
+ skipped.push({ path: relativePath, reason: "unchanged" });
435
462
  continue;
436
463
  }
437
464
  if (!options.overwrite) {
438
465
  const confirmed = await confirmOverwrite(relativePath);
439
466
  if (!confirmed) {
440
- skipped.push(relativePath);
467
+ skipped.push({ path: relativePath, reason: "declined" });
441
468
  continue;
442
469
  }
443
470
  }