inai-react-components 1.5.2 → 1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EAExB,MAAM,aAAa,CAAC;AAwBrB,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAChD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAsCA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE;IAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAAC,SAAS,EAAE,iBAAiB,EAAE,CAAA;CAAE,EAC1G,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,iBAAiB,GAAG,IAAI,CAc1B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,iBAAiB,EAC5B,cAAc,EAAE,kBAAkB,GACjC,MAAM,CAiBR;AAgUD,wBAAsB,MAAM,CAC1B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CAmMpB;AAqCD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,UAAU,CAC9B,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqIf"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EAExB,MAAM,aAAa,CAAC;AA0BrB,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAChD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAsCA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE;IAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAAC,SAAS,EAAE,iBAAiB,EAAE,CAAA;CAAE,EAC1G,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,iBAAiB,GAAG,IAAI,CAc1B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,iBAAiB,EAC5B,cAAc,EAAE,kBAAkB,GACjC,MAAM,CAiBR;AAgRD,wBAAsB,MAAM,CAC1B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CAmMpB;AAqCD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,UAAU,CAC9B,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqIf"}
@@ -10,7 +10,7 @@ import { saveSnapshot, sha256, } from "../utils/snapshot.js";
10
10
  import { fuzzySearchNames } from "../utils/fuzzy-search.js";
11
11
  import { normalizeThemeCss, getLocalTailwindCssTemplate, getLegacyTailwindCssTemplate, } from "./init.js";
12
12
  import { autoInstallDeps } from "../utils/auto-install.js";
13
- import { resolveLocalDir, getSourceRoot, rewriteCompanyUiImports, } from "../utils/paths.js";
13
+ import { resolveLocalDir, getSourceRoot, rewriteCompanyUiImports, rewriteLibImports, stripRelativeJsExtensions, } from "../utils/paths.js";
14
14
  /**
15
15
  * Parse a component spec into its registry namespace, type prefix and name.
16
16
  *
@@ -173,13 +173,25 @@ async function installOne(component, registry, componentsJson, registryDir, root
173
173
  }
174
174
  }
175
175
  let content = fs.readFileSync(registryFilePath, "utf-8");
176
- // Blocks and templates reference sibling components via
177
- // `from "@company/ui/<name>"`. Rewrite those to point at the user's
178
- // locally-installed components using the project's alias prefix, so
179
- // the block compiles without `@company/ui` being in node_modules.
180
- if (isMultiFile) {
181
- content = rewriteCompanyUiImports(content, componentsJson);
182
- }
176
+ // Normalize every import in the copied file so the installed code
177
+ // reads consistently regardless of where it came from:
178
+ //
179
+ // 1. `rewriteLibImports` — unifies `./lib/cn.js`, `../lib/cn.js`,
180
+ // and `@company/ui/lib/cn` onto the single `aliases.lib` alias.
181
+ // Runs first so `@company/ui/lib/*` is routed to `lib/`, not
182
+ // rewritten to `aliases.components/lib/` by the next step.
183
+ // 2. `rewriteCompanyUiImports` — blocks/templates reference sibling
184
+ // components via `from "@company/ui/<name>"`; rewrite those to
185
+ // the user's alias (`@/components/ui/<name>`). Components never
186
+ // use this pattern, but running it on everything is a no-op
187
+ // and keeps the pipeline uniform.
188
+ // 3. `stripRelativeJsExtensions` — the source uses `.js` suffixes
189
+ // for `moduleResolution: NodeNext`; consumer projects use
190
+ // bundler resolution so the suffix is unnecessary and visually
191
+ // confusing in `.tsx` files. Strip it from relative/alias specs.
192
+ content = rewriteLibImports(content, componentsJson);
193
+ content = rewriteCompanyUiImports(content, componentsJson);
194
+ content = stripRelativeJsExtensions(content);
183
195
  if (options.dryRun) {
184
196
  console.log(chalk.dim(` [dry-run] would write ${path.relative(rootDir, localFilePath)}`));
185
197
  }
@@ -195,15 +207,26 @@ async function installOne(component, registry, componentsJson, registryDir, root
195
207
  }
196
208
  copiedFiles.push(displayName);
197
209
  }
198
- // Internal deps (lib/cn, lib/animations, lib/i18n-context, ...).
210
+ // Internal deps (lib/cn, lib/animations, lib/i18n-context, lib/api-client, …).
211
+ //
212
+ // Destination: the unified `aliases.lib` directory (falls back to
213
+ // `<sourceRoot>/lib/` for legacy `components.json` files without the
214
+ // field). Every component, block, and template shares this one lib
215
+ // tree; their imports are rewritten above via `rewriteLibImports` to
216
+ // point at `aliases.lib`, so placement here must match that alias.
199
217
  //
200
- // Placement matters: source components import via `./lib/<name>.js`
201
- // (sibling), so lib files MUST land in `<componentDir>/lib/` NOT in
202
- // `<sourceRoot>/lib/`. Otherwise the relative imports break at runtime.
218
+ // Collision policy: if a file already exists AND its content matches
219
+ // something the CLI previously wrote (or it's about to be overwritten
220
+ // with the exact same bytes), we treat it as a no-op. Otherwise we
221
+ // respect `force` / `skipExisting` / prompt like any other file — this
222
+ // protects the user's own `lib/utils.ts`, custom `api-client.ts`, etc.
203
223
  //
204
224
  // Extensions: most lib files are `.ts` but `i18n-context.tsx` contains
205
225
  // JSX and must be copied as `.tsx`. We probe both in order.
206
- const componentsDir = resolveLocalDir(componentsJson.aliases.components, componentsJson);
226
+ const libDirRaw = componentsJson.aliases.lib;
227
+ const libDir = libDirRaw
228
+ ? resolveLocalDir(libDirRaw, componentsJson)
229
+ : `${getSourceRoot(componentsJson)}/lib`;
207
230
  const copiedInternalDeps = [];
208
231
  for (const dep of component.internalDeps) {
209
232
  const depBase = dep.split("/").pop();
@@ -222,102 +245,59 @@ async function installOne(component, registry, componentsJson, registryDir, root
222
245
  else {
223
246
  continue;
224
247
  }
225
- // e.g. componentsDir = "app/components/ui" → target:
226
- // "app/components/ui/lib/i18n-context.tsx"
227
- const depTargetPath = path.join(rootDir, componentsDir, "lib", depFileName);
228
- const content = fs.readFileSync(depSourcePath, "utf-8");
248
+ // e.g. libDir = "src/lib" → target: "src/lib/i18n-context.tsx"
249
+ const depTargetPath = path.join(rootDir, libDir, depFileName);
250
+ // Lib files must go through the same import normalization as
251
+ // everything else — otherwise `i18n-context.tsx` would still
252
+ // contain `from "./i18n-types.js"` (relative-sibling) which breaks
253
+ // under the unified layout if the user's tsconfig ever shifts.
254
+ let depContent = fs.readFileSync(depSourcePath, "utf-8");
255
+ depContent = rewriteLibImports(depContent, componentsJson);
256
+ depContent = rewriteCompanyUiImports(depContent, componentsJson);
257
+ depContent = stripRelativeJsExtensions(depContent);
258
+ // Protect the user's pre-existing lib files. If the destination
259
+ // exists and its content differs from what we're about to write,
260
+ // respect `force`/`skipExisting`/prompt like the main copy loop.
261
+ if (fs.existsSync(depTargetPath)) {
262
+ const existing = fs.readFileSync(depTargetPath, "utf-8");
263
+ if (existing === depContent) {
264
+ // Identical — no work to do.
265
+ copiedInternalDeps.push(dep);
266
+ continue;
267
+ }
268
+ if (!options.force) {
269
+ if (options.skipExisting) {
270
+ continue;
271
+ }
272
+ if (options.dryRun) {
273
+ // Treat as would-overwrite for dry-run logging.
274
+ }
275
+ else if (process.stdin.isTTY) {
276
+ const res = await prompts({
277
+ type: "confirm",
278
+ name: "overwrite",
279
+ message: `Lib file "${path.relative(rootDir, depTargetPath)}" already exists. Overwrite?`,
280
+ initial: false,
281
+ });
282
+ if (!res.overwrite)
283
+ continue;
284
+ }
285
+ else {
286
+ // Non-interactive and not forced — skip silently to avoid
287
+ // clobbering the user's own implementation (e.g. api-client).
288
+ continue;
289
+ }
290
+ }
291
+ }
229
292
  if (options.dryRun) {
230
293
  console.log(chalk.dim(` [dry-run] would write ${path.relative(rootDir, depTargetPath)}`));
231
294
  }
232
295
  else {
233
296
  fs.mkdirSync(path.dirname(depTargetPath), { recursive: true });
234
- fs.writeFileSync(depTargetPath, content);
297
+ fs.writeFileSync(depTargetPath, depContent);
235
298
  }
236
299
  copiedInternalDeps.push(dep);
237
300
  }
238
- // Block/template lib: blocks import via `../../lib/animations` and
239
- // `../../lib/styles` relative to `<blocksDir>/<blockName>/page.tsx`,
240
- // which resolves two levels up to `path.dirname(blocksDir)/lib/`.
241
- // Copy the global block lib from `apps/docs/src/registry/lib/` there.
242
- if (isMultiFile) {
243
- const blocksDir = resolveLocalDir(componentsJson.aliases.blocks, componentsJson);
244
- const blockLibDest = path.join(rootDir, path.dirname(blocksDir), "lib");
245
- for (const libFile of ["animations.ts", "styles.ts"]) {
246
- const src = path.join(registryDir, "apps", "docs", "src", "registry", "lib", libFile);
247
- if (!fs.existsSync(src))
248
- continue;
249
- const dest = path.join(blockLibDest, libFile);
250
- if (fs.existsSync(dest) && !options.force)
251
- continue;
252
- if (options.dryRun) {
253
- console.log(chalk.dim(` [dry-run] would write ${path.relative(rootDir, dest)}`));
254
- }
255
- else {
256
- fs.mkdirSync(blockLibDest, { recursive: true });
257
- fs.copyFileSync(src, dest);
258
- }
259
- }
260
- // Some data-heavy blocks (data-table-full, template-admin-dashboard,
261
- // detail-panel-table, etc.) import from `../../../lib/api-client` and
262
- // `../../../lib/api-types`, which resolves to `<sourceRoot>/lib/`.
263
- // These are integration points with the user's backend that can't be
264
- // shipped as real code — but we can write minimal stubs so the project
265
- // compiles out of the box. Stubs are only written if the file doesn't
266
- // already exist; if the user has a real implementation it stays intact.
267
- const sourceRoot = getSourceRoot(componentsJson);
268
- const userLibDir = path.join(rootDir, sourceRoot, "lib");
269
- const stubApiTypes = path.join(userLibDir, "api-types.ts");
270
- const stubApiClient = path.join(userLibDir, "api-client.ts");
271
- if (!fs.existsSync(stubApiTypes) && !options.dryRun) {
272
- fs.mkdirSync(userLibDir, { recursive: true });
273
- fs.writeFileSync(stubApiTypes, `// Stub generated by inai-react-components. Replace with your real API types.
274
- // These types satisfy the shape expected by data-heavy blocks
275
- // (data-table-full, template-admin-dashboard, detail-panel-table, ...).
276
- // If you already have a typed API layer, delete this file and point the
277
- // block imports at your own module instead.
278
-
279
- export interface PaginationMeta {
280
- totalRows: number;
281
- totalPages: number;
282
- page: number;
283
- pageSize: number;
284
- }
285
-
286
- export interface PaginatedResponse<T> {
287
- data: T[];
288
- pagination: PaginationMeta;
289
- }
290
- `);
291
- }
292
- if (!fs.existsSync(stubApiClient) && !options.dryRun) {
293
- fs.mkdirSync(userLibDir, { recursive: true });
294
- fs.writeFileSync(stubApiClient, `// Stub generated by inai-react-components. Replace with your real API client.
295
- // Data-heavy blocks call \`fetchPaginated\` and \`downloadCsv\` at runtime.
296
- // Without a real implementation the app will throw when you interact with
297
- // those features — wire these up to your backend (fetch, tRPC, REST, etc.).
298
-
299
- import type { PaginatedResponse } from "./api-types.js";
300
-
301
- export async function fetchPaginated<T>(
302
- endpoint: string,
303
- _params: Record<string, unknown>
304
- ): Promise<PaginatedResponse<T>> {
305
- throw new Error(
306
- \`fetchPaginated stub called for "\${endpoint}". Replace <sourceRoot>/lib/api-client.ts with your real API client.\`
307
- );
308
- }
309
-
310
- export async function downloadCsv(
311
- endpoint: string,
312
- _params: Record<string, unknown>
313
- ): Promise<void> {
314
- throw new Error(
315
- \`downloadCsv stub called for "\${endpoint}". Replace <sourceRoot>/lib/api-client.ts with your real CSV exporter.\`
316
- );
317
- }
318
- `);
319
- }
320
- }
321
301
  // Update components.json (skipped in dry-run).
322
302
  if (!options.dryRun) {
323
303
  const registryVersion = registry.version;
@@ -245,6 +245,7 @@ export interface ComponentsJson {
245
245
  components: string;
246
246
  blocks: string;
247
247
  utils: string;
248
+ lib?: string;
248
249
  };
249
250
  theme: Theme;
250
251
  font: {
@@ -273,12 +274,14 @@ export declare function getLegacyTailwindCssTemplate(theme: Theme, fontBody: Fon
273
274
  export declare function normalizeThemeCss(css: string, theme: string): string;
274
275
  /**
275
276
  * Copies the i18n base files (context, types, defaults) from the registry
276
- * to `<componentPath>/lib/`. Required because ~75 components import
277
- * `useUIMessages` from `./lib/i18n-context.js` the lib directory must
278
- * live as a SIBLING of the component `.tsx` files so relative imports
279
- * resolve correctly.
277
+ * to `<sourceRoot>/lib/`. ~75 components import `useUIMessages` from the
278
+ * i18n context; the CLI rewrites those imports at install-time to the
279
+ * unified `aliases.lib` directory, so the files must land there.
280
+ *
281
+ * Never overwrites pre-existing files so a user's custom i18n provider
282
+ * stays intact across re-runs.
280
283
  */
281
- export declare function copyI18nFiles(registryDir: string | undefined, targetDir: string, componentPath: string): string[];
284
+ export declare function copyI18nFiles(registryDir: string | undefined, targetDir: string, libDirRelative: string): string[];
282
285
  export declare function runInit(config: InitConfig, targetDir: string, repoUrl?: string): Promise<{
283
286
  success: boolean;
284
287
  filesCreated: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -180,6 +180,11 @@ export function buildComponentsJson(config, repoUrl) {
180
180
  aliasPrefix: "@/",
181
181
  };
182
182
  const utilsAlias = `${layout.aliasPrefix}lib/utils`;
183
+ // Unified destination for every shared lib file shipped by the CLI
184
+ // (cn, animations, i18n context, api stubs, ...). Stored in alias form
185
+ // so it can be used as an import specifier verbatim and also resolved
186
+ // back to a filesystem path via `resolveLocalDir`.
187
+ const libAlias = `${layout.aliasPrefix}lib`;
183
188
  const json = {
184
189
  $schema: "https://inai-ui.dev/schema.json",
185
190
  style: "default",
@@ -193,6 +198,7 @@ export function buildComponentsJson(config, repoUrl) {
193
198
  components: config.componentPath,
194
199
  blocks: config.blockPath,
195
200
  utils: utilsAlias,
201
+ lib: libAlias,
196
202
  },
197
203
  theme: config.theme,
198
204
  font: {
@@ -347,25 +353,30 @@ function copyTokensToProject(registryDir, targetDir, theme, sourceRoot) {
347
353
  }
348
354
  /**
349
355
  * Copies the i18n base files (context, types, defaults) from the registry
350
- * to `<componentPath>/lib/`. Required because ~75 components import
351
- * `useUIMessages` from `./lib/i18n-context.js` the lib directory must
352
- * live as a SIBLING of the component `.tsx` files so relative imports
353
- * resolve correctly.
356
+ * to `<sourceRoot>/lib/`. ~75 components import `useUIMessages` from the
357
+ * i18n context; the CLI rewrites those imports at install-time to the
358
+ * unified `aliases.lib` directory, so the files must land there.
359
+ *
360
+ * Never overwrites pre-existing files so a user's custom i18n provider
361
+ * stays intact across re-runs.
354
362
  */
355
- export function copyI18nFiles(registryDir, targetDir, componentPath) {
363
+ export function copyI18nFiles(registryDir, targetDir, libDirRelative) {
356
364
  if (!registryDir)
357
365
  return [];
358
366
  const files = ["i18n-context.tsx", "i18n-types.ts", "i18n-defaults.ts"];
359
367
  const srcBase = path.join(registryDir, "packages", "ui", "src", "lib");
360
- const destBase = path.join(targetDir, componentPath, "lib");
368
+ const destBase = path.join(targetDir, libDirRelative);
361
369
  fs.mkdirSync(destBase, { recursive: true });
362
370
  const copied = [];
363
371
  for (const file of files) {
364
372
  const src = path.join(srcBase, file);
365
373
  if (!fs.existsSync(src))
366
374
  continue;
367
- fs.copyFileSync(src, path.join(destBase, file));
368
- copied.push(`${componentPath}/lib/${file}`);
375
+ const dest = path.join(destBase, file);
376
+ if (fs.existsSync(dest))
377
+ continue; // don't clobber user files
378
+ fs.copyFileSync(src, dest);
379
+ copied.push(`${libDirRelative}/${file}`);
369
380
  }
370
381
  return copied;
371
382
  }
@@ -382,15 +393,20 @@ export async function runInit(config, targetDir, repoUrl) {
382
393
  fs.mkdirSync(componentDir, { recursive: true });
383
394
  const blockDir = path.join(targetDir, config.blockPath);
384
395
  fs.mkdirSync(blockDir, { recursive: true });
385
- // 3. Create cn.ts utility as a sibling of the components directory.
386
- // Components import via `./lib/cn.js`, so lib/ MUST live directly
387
- // next to the installed component files `<componentPath>/lib/`,
388
- // not `<sourceRoot>/lib/`.
389
- const libDir = path.join(targetDir, config.componentPath, "lib");
396
+ // 3. Create the unified lib directory under `<sourceRoot>/lib/` and
397
+ // seed it with `cn.ts`. Every component/block lib file ships here;
398
+ // the CLI rewrites all imports at install-time to point at the
399
+ // `aliases.lib` alias so there's a single source of truth.
400
+ //
401
+ // Never overwrite an existing `cn.ts` — the user may have their own.
402
+ const libDirRelative = `${sourceRoot}/lib`;
403
+ const libDir = path.join(targetDir, libDirRelative);
390
404
  fs.mkdirSync(libDir, { recursive: true });
391
405
  const cnPath = path.join(libDir, "cn.ts");
392
- fs.writeFileSync(cnPath, getCnTemplate());
393
- filesCreated.push(`${config.componentPath}/lib/cn.ts`);
406
+ if (!fs.existsSync(cnPath)) {
407
+ fs.writeFileSync(cnPath, getCnTemplate());
408
+ filesCreated.push(`${libDirRelative}/cn.ts`);
409
+ }
394
410
  // 4. Copy tokens and create CSS
395
411
  const cssRelPath = config.cssEntryPoint ?? `${sourceRoot}/index.css`;
396
412
  const cssPath = path.join(targetDir, cssRelPath);
@@ -400,9 +416,10 @@ export async function runInit(config, targetDir, repoUrl) {
400
416
  const registryDir = getCacheDir();
401
417
  const tokenFiles = copyTokensToProject(registryDir, targetDir, config.theme, sourceRoot);
402
418
  filesCreated.push(...tokenFiles);
403
- // Copy i18n base files next to the components so their
404
- // `./lib/i18n-context.js` imports resolve.
405
- const i18nFiles = copyI18nFiles(registryDir, targetDir, config.componentPath);
419
+ // Copy i18n base files to the unified lib directory so that components'
420
+ // `./lib/i18n-context.js` imports (rewritten at install-time to the
421
+ // `aliases.lib` alias) resolve.
422
+ const i18nFiles = copyI18nFiles(registryDir, targetDir, libDirRelative);
406
423
  filesCreated.push(...i18nFiles);
407
424
  fs.writeFileSync(cssPath, getLocalTailwindCssTemplate(config.theme, config.fontBody, config.fontHeading, config.radius));
408
425
  }
@@ -17,6 +17,17 @@ export interface ComponentsJsonFile {
17
17
  blocks: string;
18
18
  templates?: string;
19
19
  utils: string;
20
+ /**
21
+ * Destination directory for shared library utilities (`cn`, animation
22
+ * helpers, i18n context, API client stubs, …). When set, all lib files
23
+ * copied by `add` land here and imports inside components/blocks are
24
+ * rewritten to use this alias uniformly.
25
+ *
26
+ * Stored in alias form (e.g. `"@/lib"`) so it can be used directly as
27
+ * an import specifier. Falls back to `"${layout.aliasPrefix}lib"` when
28
+ * absent (legacy pre-1.6 configs).
29
+ */
30
+ lib?: string;
20
31
  };
21
32
  theme: string;
22
33
  font?: {
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const program = new Command();
17
17
  program
18
18
  .name("inai-ui")
19
19
  .description("CLI for InAI UI component library")
20
- .version("1.5.2");
20
+ .version("1.6.1");
21
21
  program
22
22
  .command("init [repo-url]")
23
23
  .description("Initialize InAI UI in your project. Optionally pass a Git repo URL to fetch components remotely.")
@@ -41,16 +41,65 @@ export declare function getSourceRoot(componentsJson: Pick<ComponentsJsonFile, "
41
41
  * assume their tsconfig handles it).
42
42
  */
43
43
  export declare function getComponentsImportAlias(componentsJson: Pick<ComponentsJsonFile, "aliases" | "layout">): string;
44
+ /**
45
+ * Compute the import-alias form of `aliases.lib`. Used when rewriting
46
+ * every lib import (relative `./lib/cn.js`, compound `../lib/cn.js`, and
47
+ * package `@company/ui/lib/cn`) to a single, consistent alias so the
48
+ * installed code never depends on a particular on-disk layout.
49
+ *
50
+ * Falls back to `${aliasPrefix}lib` when `aliases.lib` is missing — this
51
+ * keeps the CLI compatible with pre-1.6 `components.json` files that
52
+ * don't yet declare the field.
53
+ */
54
+ export declare function getLibImportAlias(componentsJson: Pick<ComponentsJsonFile, "aliases" | "layout">): string;
55
+ /**
56
+ * Rewrite every lib import in a copied source file to the unified
57
+ * `aliases.lib` alias. This handles:
58
+ * - Component sibling imports: `./lib/cn.js` → `@/lib/cn`
59
+ * - Compound-component imports: `../lib/cn.js` → `@/lib/cn`
60
+ * - Package imports: `@company/ui/lib/cn` → `@/lib/cn`
61
+ *
62
+ * The `.js` suffix (required in the source for `moduleResolution: NodeNext`)
63
+ * is stripped in the process so the resulting code reads naturally in a
64
+ * consumer project using bundler-style resolution (Vite, Next, TanStack
65
+ * Start, Webpack).
66
+ *
67
+ * Side-effect imports (`import "@company/ui/lib/xxx"`) get the same
68
+ * treatment.
69
+ */
70
+ export declare function rewriteLibImports(source: string, componentsJson: Pick<ComponentsJsonFile, "aliases" | "layout">): string;
71
+ /**
72
+ * Strip `.js` extensions from relative and alias-prefixed imports in a
73
+ * copied source file. This is safe for consumer projects because they
74
+ * almost always use bundler-style module resolution (Vite, Next.js,
75
+ * TanStack Start, Webpack, esbuild), which resolves extensionless
76
+ * imports directly.
77
+ *
78
+ * Handles every common module reference form:
79
+ * - Static: `from "./x.js"`, `import "./x.js"` (side-effect)
80
+ * - Dynamic: `import("./x.js")` and `import(\`./x.js\`)`
81
+ * - Type-only: `typeof import("./x.js")`, `import("./x.js").Foo` (TS
82
+ * inline import types appearing mid-line, not at statement start)
83
+ *
84
+ * Only touches specifiers that start with `./`, `../`, `@/`, `~/`, or
85
+ * `$/` — package specifiers (`react`, `motion/react`, ...) and URLs are
86
+ * left untouched because they might legitimately end in `.js`.
87
+ */
88
+ export declare function stripRelativeJsExtensions(source: string): string;
44
89
  /**
45
90
  * Rewrite `@company/ui/*` imports in a block or template file to point
46
91
  * at the user's locally-installed components. Without this, block files
47
92
  * that `import { Button } from "@company/ui/button"` would fail at
48
93
  * compile time because `@company/ui` doesn't exist in the consumer project.
49
94
  *
95
+ * Note: `@company/ui/lib/*` is handled separately by `rewriteLibImports`
96
+ * (which must run BEFORE this function) so lib files route to
97
+ * `aliases.lib` instead of `aliases.components/lib`. This function only
98
+ * rewrites the non-lib specifiers that remain.
99
+ *
50
100
  * Transformations:
51
101
  * - `from "@company/ui/data-table"` → `from "~/components/ui/data-table"`
52
102
  * - `from "@company/ui/data-table/sub"` → `from "~/components/ui/data-table/sub"`
53
- * - `from "@company/ui/lib/animations"` → `from "~/components/ui/lib/animations"`
54
103
  * - `from "@company/ui"` (bare barrel) → no rewrite (logged as unsupported)
55
104
  *
56
105
  * The alias prefix (`~/`, `@/`, etc.) is derived from the project layout
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GACjD,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GACjD,MAAM,CAER;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAYR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAaR"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GACjD,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GACjD,MAAM,CAER;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAYR;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAiBR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAqBR;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAYhE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAC7D,MAAM,CAaR"}
@@ -64,16 +64,102 @@ export function getComponentsImportAlias(componentsJson) {
64
64
  }
65
65
  return raw;
66
66
  }
67
+ /**
68
+ * Compute the import-alias form of `aliases.lib`. Used when rewriting
69
+ * every lib import (relative `./lib/cn.js`, compound `../lib/cn.js`, and
70
+ * package `@company/ui/lib/cn`) to a single, consistent alias so the
71
+ * installed code never depends on a particular on-disk layout.
72
+ *
73
+ * Falls back to `${aliasPrefix}lib` when `aliases.lib` is missing — this
74
+ * keeps the CLI compatible with pre-1.6 `components.json` files that
75
+ * don't yet declare the field.
76
+ */
77
+ export function getLibImportAlias(componentsJson) {
78
+ const raw = componentsJson.aliases.lib;
79
+ if (raw) {
80
+ // Already in alias form — return verbatim.
81
+ for (const prefix of KNOWN_PREFIXES) {
82
+ if (raw.startsWith(prefix))
83
+ return raw;
84
+ }
85
+ // Raw path — try to convert via layout.
86
+ const layout = componentsJson.layout;
87
+ if (layout && raw.startsWith(`${layout.sourceRoot}/`)) {
88
+ return `${layout.aliasPrefix}${raw.slice(layout.sourceRoot.length + 1)}`;
89
+ }
90
+ return raw;
91
+ }
92
+ // Fallback for legacy configs: derive from layout.
93
+ const prefix = componentsJson.layout?.aliasPrefix ?? "@/";
94
+ return `${prefix}lib`;
95
+ }
96
+ /**
97
+ * Rewrite every lib import in a copied source file to the unified
98
+ * `aliases.lib` alias. This handles:
99
+ * - Component sibling imports: `./lib/cn.js` → `@/lib/cn`
100
+ * - Compound-component imports: `../lib/cn.js` → `@/lib/cn`
101
+ * - Package imports: `@company/ui/lib/cn` → `@/lib/cn`
102
+ *
103
+ * The `.js` suffix (required in the source for `moduleResolution: NodeNext`)
104
+ * is stripped in the process so the resulting code reads naturally in a
105
+ * consumer project using bundler-style resolution (Vite, Next, TanStack
106
+ * Start, Webpack).
107
+ *
108
+ * Side-effect imports (`import "@company/ui/lib/xxx"`) get the same
109
+ * treatment.
110
+ */
111
+ export function rewriteLibImports(source, componentsJson) {
112
+ const libAlias = getLibImportAlias(componentsJson);
113
+ // 1) Relative imports: `./lib/foo.js`, `../lib/foo.js`, `../../lib/foo.js`
114
+ let result = source.replace(/(from\s+["'])(?:\.\.?\/)+lib\/([a-zA-Z0-9_-]+)(?:\.js)?(["'])/g, (_, pre, name, post) => `${pre}${libAlias}/${name}${post}`);
115
+ result = result.replace(/(import\s+["'])(?:\.\.?\/)+lib\/([a-zA-Z0-9_-]+)(?:\.js)?(["'])/g, (_, pre, name, post) => `${pre}${libAlias}/${name}${post}`);
116
+ // 2) Package imports: `@company/ui/lib/foo`
117
+ result = result.replace(/(from\s+["'])@company\/ui\/lib\/([a-zA-Z0-9_-]+)(?:\.js)?(["'])/g, (_, pre, name, post) => `${pre}${libAlias}/${name}${post}`);
118
+ result = result.replace(/(import\s+["'])@company\/ui\/lib\/([a-zA-Z0-9_-]+)(?:\.js)?(["'])/g, (_, pre, name, post) => `${pre}${libAlias}/${name}${post}`);
119
+ return result;
120
+ }
121
+ /**
122
+ * Strip `.js` extensions from relative and alias-prefixed imports in a
123
+ * copied source file. This is safe for consumer projects because they
124
+ * almost always use bundler-style module resolution (Vite, Next.js,
125
+ * TanStack Start, Webpack, esbuild), which resolves extensionless
126
+ * imports directly.
127
+ *
128
+ * Handles every common module reference form:
129
+ * - Static: `from "./x.js"`, `import "./x.js"` (side-effect)
130
+ * - Dynamic: `import("./x.js")` and `import(\`./x.js\`)`
131
+ * - Type-only: `typeof import("./x.js")`, `import("./x.js").Foo` (TS
132
+ * inline import types appearing mid-line, not at statement start)
133
+ *
134
+ * Only touches specifiers that start with `./`, `../`, `@/`, `~/`, or
135
+ * `$/` — package specifiers (`react`, `motion/react`, ...) and URLs are
136
+ * left untouched because they might legitimately end in `.js`.
137
+ */
138
+ export function stripRelativeJsExtensions(source) {
139
+ // 1) Static `from "..."` and `import "..."` statements.
140
+ const statement = /((?:from|import)\s+[^"'`]*["'`])((?:\.{1,2}\/|[@~$]\/)[^"'`]*)\.js(["'`])/g;
141
+ // 2) Dynamic / inline type `import("...")` calls. These can appear
142
+ // anywhere in a type position, so the preceding keyword isn't
143
+ // constrained to start of a statement.
144
+ const dynamic = /(import\s*\(\s*["'`])((?:\.{1,2}\/|[@~$]\/)[^"'`]*)\.js(["'`])/g;
145
+ return source
146
+ .replace(statement, (_, pre, spec, post) => `${pre}${spec}${post}`)
147
+ .replace(dynamic, (_, pre, spec, post) => `${pre}${spec}${post}`);
148
+ }
67
149
  /**
68
150
  * Rewrite `@company/ui/*` imports in a block or template file to point
69
151
  * at the user's locally-installed components. Without this, block files
70
152
  * that `import { Button } from "@company/ui/button"` would fail at
71
153
  * compile time because `@company/ui` doesn't exist in the consumer project.
72
154
  *
155
+ * Note: `@company/ui/lib/*` is handled separately by `rewriteLibImports`
156
+ * (which must run BEFORE this function) so lib files route to
157
+ * `aliases.lib` instead of `aliases.components/lib`. This function only
158
+ * rewrites the non-lib specifiers that remain.
159
+ *
73
160
  * Transformations:
74
161
  * - `from "@company/ui/data-table"` → `from "~/components/ui/data-table"`
75
162
  * - `from "@company/ui/data-table/sub"` → `from "~/components/ui/data-table/sub"`
76
- * - `from "@company/ui/lib/animations"` → `from "~/components/ui/lib/animations"`
77
163
  * - `from "@company/ui"` (bare barrel) → no rewrite (logged as unsupported)
78
164
  *
79
165
  * The alias prefix (`~/`, `@/`, etc.) is derived from the project layout
@@ -5,6 +5,11 @@ export declare function getRegistryCacheDir(name: string): string;
5
5
  * Clone (or fast-forward) a git registry into the legacy single-cache
6
6
  * directory. Preserved for backward compatibility with the v0.5.0
7
7
  * code path that called this directly.
8
+ *
9
+ * If a prior cache is present but refuses to fast-forward (dirty tree,
10
+ * diverged history, interrupted previous run), the cache is reset to
11
+ * match origin; if that also fails, the directory is nuked and a fresh
12
+ * clone is done. Users should never have to touch `~/.inai-ui/registry`.
8
13
  */
9
14
  export declare function cloneRegistry(repoUrl: string): string;
10
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"registry-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/registry-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAS7E,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWrD;AAmBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,CAkGjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAS7D"}
1
+ {"version":3,"file":"registry-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/registry-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAS7E,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AA8CD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAcrD;AAmBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,CA+FjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAS7D"}
@@ -15,22 +15,71 @@ export function getCacheDir() {
15
15
  export function getRegistryCacheDir(name) {
16
16
  return path.join(REGISTRIES_CACHE_DIR, name);
17
17
  }
18
+ /**
19
+ * Attempt to refresh an existing git cache to the latest upstream state.
20
+ * Tries `git pull --ff-only` first, and if the working tree is dirty or
21
+ * has diverged from upstream (either of which aborts the fast-forward),
22
+ * falls back to a hard reset + clean so the cache matches origin exactly.
23
+ *
24
+ * The cache is an implementation detail of the CLI — users should never
25
+ * have to edit files inside it, so any local changes are unconditionally
26
+ * discarded in favor of whatever origin currently ships.
27
+ *
28
+ * Returns `true` on success, `false` if both pull and reset failed (in
29
+ * which case the caller should nuke the directory and re-clone).
30
+ */
31
+ function refreshGitCache(cacheDir) {
32
+ try {
33
+ execSync("git pull --ff-only", { cwd: cacheDir, stdio: "pipe" });
34
+ return true;
35
+ }
36
+ catch {
37
+ // Dirty tree, diverged history, or both. Salvage the clone by
38
+ // fetching fresh refs and resetting hard onto origin's default
39
+ // branch. `git clean -fd` drops any untracked files the user (or a
40
+ // previous botched run) left behind.
41
+ try {
42
+ execSync("git fetch --all --prune", { cwd: cacheDir, stdio: "pipe" });
43
+ // Derive the remote HEAD dynamically so we respect whatever branch
44
+ // origin tracks (main, master, develop, ...).
45
+ const remoteHead = execSync("git symbolic-ref refs/remotes/origin/HEAD", { cwd: cacheDir, stdio: ["pipe", "pipe", "pipe"] })
46
+ .toString()
47
+ .trim();
48
+ execSync(`git reset --hard ${remoteHead}`, {
49
+ cwd: cacheDir,
50
+ stdio: "pipe",
51
+ });
52
+ execSync("git clean -fd", { cwd: cacheDir, stdio: "pipe" });
53
+ return true;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ }
59
+ }
18
60
  /**
19
61
  * Clone (or fast-forward) a git registry into the legacy single-cache
20
62
  * directory. Preserved for backward compatibility with the v0.5.0
21
63
  * code path that called this directly.
64
+ *
65
+ * If a prior cache is present but refuses to fast-forward (dirty tree,
66
+ * diverged history, interrupted previous run), the cache is reset to
67
+ * match origin; if that also fails, the directory is nuked and a fresh
68
+ * clone is done. Users should never have to touch `~/.inai-ui/registry`.
22
69
  */
23
70
  export function cloneRegistry(repoUrl) {
24
71
  if (fs.existsSync(path.join(LEGACY_CACHE_DIR, ".git"))) {
25
- execSync("git pull --ff-only", { cwd: LEGACY_CACHE_DIR, stdio: "pipe" });
26
- }
27
- else {
28
- fs.mkdirSync(CACHE_BASE, { recursive: true });
29
- if (fs.existsSync(LEGACY_CACHE_DIR)) {
30
- fs.rmSync(LEGACY_CACHE_DIR, { recursive: true });
72
+ if (refreshGitCache(LEGACY_CACHE_DIR)) {
73
+ return LEGACY_CACHE_DIR;
31
74
  }
32
- execSync(`git clone "${repoUrl}" "${LEGACY_CACHE_DIR}"`, { stdio: "pipe" });
75
+ // Salvage failed nuke and re-clone below.
76
+ fs.rmSync(LEGACY_CACHE_DIR, { recursive: true, force: true });
77
+ }
78
+ fs.mkdirSync(CACHE_BASE, { recursive: true });
79
+ if (fs.existsSync(LEGACY_CACHE_DIR)) {
80
+ fs.rmSync(LEGACY_CACHE_DIR, { recursive: true });
33
81
  }
82
+ execSync(`git clone "${repoUrl}" "${LEGACY_CACHE_DIR}"`, { stdio: "pipe" });
34
83
  return LEGACY_CACHE_DIR;
35
84
  }
36
85
  /**
@@ -89,18 +138,13 @@ export async function resolveRegistry(registry, projectDir, options = {}) {
89
138
  const cloneUrl = token ? embedTokenInGitUrl(registry.url, token) : registry.url;
90
139
  const branch = registry.branch ?? "main";
91
140
  if (fs.existsSync(path.join(cacheDir, ".git"))) {
92
- try {
93
- execSync("git pull --ff-only", { cwd: cacheDir, stdio: "pipe" });
94
- }
95
- catch {
96
- // If pull fails (network hiccup, diverged), fall back to cached copy.
141
+ if (!refreshGitCache(cacheDir)) {
142
+ // Salvage failed nuke and re-clone below.
143
+ fs.rmSync(cacheDir, { recursive: true, force: true });
97
144
  }
98
145
  }
99
- else {
146
+ if (!fs.existsSync(path.join(cacheDir, ".git"))) {
100
147
  fs.mkdirSync(REGISTRIES_CACHE_DIR, { recursive: true });
101
- if (fs.existsSync(cacheDir)) {
102
- fs.rmSync(cacheDir, { recursive: true });
103
- }
104
148
  execSync(`git clone --branch "${branch}" "${cloneUrl}" "${cacheDir}"`, { stdio: "pipe" });
105
149
  }
106
150
  return cacheDir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inai-react-components",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
4
4
  "description": "CLI for InAI UI component library — install components from a private registry into your React project",
5
5
  "private": false,
6
6
  "type": "module",