@buildpad/cli 0.1.4

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 (44) hide show
  1. package/README.md +557 -0
  2. package/dist/chunk-J4KKVECI.js +365 -0
  3. package/dist/chunk-J4KKVECI.js.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +2582 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/outdated-JMAYAZ7W.js +110 -0
  8. package/dist/outdated-JMAYAZ7W.js.map +1 -0
  9. package/dist/templates/api/auth-callback-route.ts +36 -0
  10. package/dist/templates/api/auth-headers.ts +72 -0
  11. package/dist/templates/api/auth-login-route.ts +63 -0
  12. package/dist/templates/api/auth-logout-route.ts +41 -0
  13. package/dist/templates/api/auth-user-route.ts +71 -0
  14. package/dist/templates/api/collections-route.ts +54 -0
  15. package/dist/templates/api/fields-route.ts +44 -0
  16. package/dist/templates/api/files-id-route.ts +116 -0
  17. package/dist/templates/api/files-route.ts +83 -0
  18. package/dist/templates/api/items-id-route.ts +120 -0
  19. package/dist/templates/api/items-route.ts +88 -0
  20. package/dist/templates/api/login-page.tsx +142 -0
  21. package/dist/templates/api/permissions-me-route.ts +72 -0
  22. package/dist/templates/api/relations-route.ts +46 -0
  23. package/dist/templates/app/content/[collection]/[id]/page.tsx +35 -0
  24. package/dist/templates/app/content/[collection]/page.tsx +65 -0
  25. package/dist/templates/app/content/layout.tsx +64 -0
  26. package/dist/templates/app/content/page.tsx +66 -0
  27. package/dist/templates/app/design-tokens.css +183 -0
  28. package/dist/templates/app/globals.css +58 -0
  29. package/dist/templates/app/layout.tsx +49 -0
  30. package/dist/templates/app/page.tsx +23 -0
  31. package/dist/templates/components/ColorSchemeToggle.tsx +35 -0
  32. package/dist/templates/lib/common-utils.ts +156 -0
  33. package/dist/templates/lib/hooks/index.ts +98 -0
  34. package/dist/templates/lib/services/index.ts +31 -0
  35. package/dist/templates/lib/theme.ts +241 -0
  36. package/dist/templates/lib/types/index.ts +10 -0
  37. package/dist/templates/lib/utils-index.ts +32 -0
  38. package/dist/templates/lib/utils.ts +14 -0
  39. package/dist/templates/lib/vform/index.ts +24 -0
  40. package/dist/templates/middleware/middleware.ts +29 -0
  41. package/dist/templates/supabase/client.ts +25 -0
  42. package/dist/templates/supabase/middleware.ts +66 -0
  43. package/dist/templates/supabase/server.ts +45 -0
  44. package/package.json +61 -0
package/dist/index.js ADDED
@@ -0,0 +1,2582 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getRegistry,
4
+ init,
5
+ loadConfig,
6
+ resolveSourceFile,
7
+ saveConfig,
8
+ sourceFileExists
9
+ } from "./chunk-J4KKVECI.js";
10
+
11
+ // src/index.ts
12
+ import { Command } from "commander";
13
+
14
+ // src/commands/add.ts
15
+ import fs2 from "fs-extra";
16
+ import path3 from "path";
17
+ import chalk2 from "chalk";
18
+ import ora2 from "ora";
19
+ import prompts from "prompts";
20
+
21
+ // src/commands/transformer.ts
22
+ import path from "path";
23
+ function getImportMappings(config) {
24
+ const libAlias = config.aliases.lib;
25
+ const componentsAlias = config.aliases.components;
26
+ return [
27
+ // Types
28
+ {
29
+ from: /from ['"]@buildpad\/types['"]/g,
30
+ to: `from '${libAlias}/types'`
31
+ },
32
+ {
33
+ from: /from ['"]@buildpad\/types\/([^'"]+)['"]/g,
34
+ to: `from '${libAlias}/types/$1'`
35
+ },
36
+ // Services
37
+ {
38
+ from: /from ['"]@buildpad\/services['"]/g,
39
+ to: `from '${libAlias}/services'`
40
+ },
41
+ {
42
+ from: /from ['"]@buildpad\/services\/([^'"]+)['"]/g,
43
+ to: `from '${libAlias}/services/$1'`
44
+ },
45
+ // Hooks
46
+ {
47
+ from: /from ['"]@buildpad\/hooks['"]/g,
48
+ to: `from '${libAlias}/hooks'`
49
+ },
50
+ {
51
+ from: /from ['"]@buildpad\/hooks\/([^'"]+)['"]/g,
52
+ to: `from '${libAlias}/hooks/$1'`
53
+ },
54
+ // UI Interfaces (component to component imports)
55
+ {
56
+ from: /from ['"]@buildpad\/ui-interfaces['"]/g,
57
+ to: `from '${componentsAlias}'`
58
+ },
59
+ {
60
+ from: /from ['"]@buildpad\/ui-interfaces\/([^'"]+)['"]/g,
61
+ to: `from '${componentsAlias}/$1'`
62
+ },
63
+ // UI Collections (component to component imports)
64
+ {
65
+ from: /from ['"]@buildpad\/ui-collections['"]/g,
66
+ to: `from '${componentsAlias}'`
67
+ },
68
+ {
69
+ from: /from ['"]@buildpad\/ui-collections\/([^'"]+)['"]/g,
70
+ to: `from '${componentsAlias}/$1'`
71
+ },
72
+ // Utils
73
+ {
74
+ from: /from ['"]@buildpad\/utils['"]/g,
75
+ to: `from '${libAlias}/utils'`
76
+ },
77
+ {
78
+ from: /from ['"]@buildpad\/utils\/([^'"]+)['"]/g,
79
+ to: `from '${libAlias}/utils/$1'`
80
+ },
81
+ // UI Form (VForm and related components)
82
+ {
83
+ from: /from ['"]@buildpad\/ui-form['"]/g,
84
+ to: `from '${componentsAlias}/vform'`
85
+ },
86
+ {
87
+ from: /from ['"]@buildpad\/ui-form\/([^'"]+)['"]/g,
88
+ to: `from '${componentsAlias}/vform/$1'`
89
+ },
90
+ // UI Table (VTable - type-import pattern MUST come before general pattern
91
+ // so type imports resolve to vtable-types while value imports resolve to vtable)
92
+ {
93
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/ui-table['"]/g,
94
+ to: `import type {$1} from '${componentsAlias}/vtable-types'`
95
+ },
96
+ {
97
+ from: /from ['"]@buildpad\/ui-table['"]/g,
98
+ to: `from '${componentsAlias}/vtable'`
99
+ },
100
+ {
101
+ from: /from ['"]@buildpad\/ui-table\/([^'"]+)['"]/g,
102
+ to: `from '${componentsAlias}/$1'`
103
+ },
104
+ // Import type statements
105
+ {
106
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/types['"]/g,
107
+ to: `import type {$1} from '${libAlias}/types'`
108
+ },
109
+ {
110
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/hooks['"]/g,
111
+ to: `import type {$1} from '${libAlias}/hooks'`
112
+ },
113
+ {
114
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/services['"]/g,
115
+ to: `import type {$1} from '${libAlias}/services'`
116
+ },
117
+ // Import type for utils
118
+ {
119
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/utils['"]/g,
120
+ to: `import type {$1} from '${libAlias}/utils'`
121
+ },
122
+ // Import type for ui-form
123
+ {
124
+ from: /import type \{([^}]+)\} from ['"]@buildpad\/ui-form['"]/g,
125
+ to: `import type {$1} from '${componentsAlias}/vform'`
126
+ },
127
+ // Dynamic imports - import('@buildpad/services') etc.
128
+ {
129
+ from: /import\s*\(\s*['"]@buildpad\/services['"]\s*\)/g,
130
+ to: `import('${libAlias}/services')`
131
+ },
132
+ {
133
+ from: /import\s*\(\s*['"]@buildpad\/hooks['"]\s*\)/g,
134
+ to: `import('${libAlias}/hooks')`
135
+ },
136
+ {
137
+ from: /import\s*\(\s*['"]@buildpad\/types['"]\s*\)/g,
138
+ to: `import('${libAlias}/types')`
139
+ },
140
+ {
141
+ from: /import\s*\(\s*['"]@buildpad\/utils['"]\s*\)/g,
142
+ to: `import('${libAlias}/utils')`
143
+ }
144
+ ];
145
+ }
146
+ function transformImports(content, config, targetPath) {
147
+ const mappings = getImportMappings(config);
148
+ let result = content;
149
+ for (const mapping of mappings) {
150
+ result = result.replace(mapping.from, mapping.to);
151
+ }
152
+ result = normalizeImportPaths(result, targetPath);
153
+ return result;
154
+ }
155
+ function toKebabCase(str) {
156
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
157
+ }
158
+ function normalizeImportPaths(content, targetPath) {
159
+ if (content.includes("@buildpad-preserve-casing")) {
160
+ return content;
161
+ }
162
+ if (targetPath && (targetPath.includes("/vform/") || targetPath.includes("/ui-form/") || targetPath.includes("VForm") || targetPath.includes("FormField"))) {
163
+ return content;
164
+ }
165
+ const pascalCaseImportPattern = /from\s+['"](\.\.\/?|\.\/)([A-Z][a-zA-Z0-9]*(?:\/[A-Z][a-zA-Z0-9]*)?)['"]/g;
166
+ let result = content.replace(pascalCaseImportPattern, (_match, prefix, importPath) => {
167
+ const parts = importPath.split("/");
168
+ const fileName = parts[parts.length - 1];
169
+ const kebabFileName = toKebabCase(fileName);
170
+ if (prefix === "../" && parts.length >= 1) {
171
+ return `from './${kebabFileName}'`;
172
+ }
173
+ return `from '${prefix}${kebabFileName}'`;
174
+ });
175
+ const dynamicImportPattern = /import\s*\(\s*['"](\.\/)([A-Z][a-zA-Z0-9]*)['"]\s*\)/g;
176
+ result = result.replace(dynamicImportPattern, (_match, prefix, componentName) => {
177
+ const kebabName = toKebabCase(componentName);
178
+ return `import('${prefix}${kebabName}')`;
179
+ });
180
+ return result;
181
+ }
182
+ function transformIntraComponentImports(content, sourceFile, targetFile, fileMappings) {
183
+ if (!fileMappings || fileMappings.length <= 1) return content;
184
+ const sourceDir = path.posix.dirname(sourceFile);
185
+ const targetDir = path.posix.dirname(targetFile);
186
+ const sourceToTarget = /* @__PURE__ */ new Map();
187
+ const sourceToTargetExt = /* @__PURE__ */ new Map();
188
+ for (const fm of fileMappings) {
189
+ sourceToTarget.set(fm.source.replace(/\.\w+$/, ""), fm.target.replace(/\.\w+$/, ""));
190
+ sourceToTargetExt.set(fm.source, fm.target);
191
+ }
192
+ let result = content.replace(
193
+ /from\s+(['"])(\.[^'"]+)\1/g,
194
+ (match, _quote, importPath) => {
195
+ const resolved = path.posix.join(sourceDir, importPath);
196
+ const resolvedNoExt = resolved.replace(/\.\w+$/, "");
197
+ const mapped = sourceToTarget.get(resolvedNoExt) ?? sourceToTarget.get(resolved);
198
+ if (mapped) {
199
+ let rel = path.posix.relative(targetDir, mapped);
200
+ if (!rel.startsWith(".")) rel = "./" + rel;
201
+ return match.replace(importPath, rel);
202
+ }
203
+ return match;
204
+ }
205
+ );
206
+ result = result.replace(
207
+ /import\s+(['"])(\.[^'"]+)\1/g,
208
+ (match, _quote, importPath) => {
209
+ const resolved = path.posix.join(sourceDir, importPath);
210
+ const mappedExt = sourceToTargetExt.get(resolved);
211
+ if (mappedExt) {
212
+ let rel = path.posix.relative(targetDir, mappedExt);
213
+ if (!rel.startsWith(".")) rel = "./" + rel;
214
+ return match.replace(importPath, rel);
215
+ }
216
+ const resolvedNoExt = resolved.replace(/\.\w+$/, "");
217
+ const mapped = sourceToTarget.get(resolvedNoExt);
218
+ if (mapped) {
219
+ let rel = path.posix.relative(targetDir, mapped);
220
+ if (!rel.startsWith(".")) rel = "./" + rel;
221
+ return match.replace(importPath, rel);
222
+ }
223
+ return match;
224
+ }
225
+ );
226
+ return result;
227
+ }
228
+ var RELATIVE_IMPORT_MAPPINGS = {
229
+ // file-image/FileImage.tsx imports from ../upload → ./upload
230
+ "../upload": "./upload"
231
+ // file/File.tsx imports from ../upload → ./upload
232
+ // files/Files.tsx imports from ../upload → ./upload
233
+ // list-o2m imports from ../upload → ./upload
234
+ };
235
+ var VFORM_IMPORT_MAPPINGS = {
236
+ // Files in vform/components/ folder
237
+ "components": {
238
+ "../types": "../types",
239
+ "./types": "../types"
240
+ },
241
+ // Files in vform/utils/ folder
242
+ "utils": {
243
+ "../types": "../types",
244
+ "./types": "../types"
245
+ },
246
+ // Files in vform/ root folder
247
+ "root": {
248
+ "./types": "./types"
249
+ }
250
+ };
251
+ function transformVFormImports(content, sourceFile, _targetFile) {
252
+ let result = content;
253
+ let folder = "root";
254
+ if (sourceFile.includes("/components/")) {
255
+ folder = "components";
256
+ } else if (sourceFile.includes("/utils/")) {
257
+ folder = "utils";
258
+ }
259
+ const mappings = VFORM_IMPORT_MAPPINGS[folder] || {};
260
+ for (const [from, to] of Object.entries(mappings)) {
261
+ const importPattern = new RegExp(
262
+ `(from\\s+['"])${from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(['"])`,
263
+ "g"
264
+ );
265
+ result = result.replace(importPattern, `$1${to}$2`);
266
+ }
267
+ return result;
268
+ }
269
+ function transformRelativeImports(content, _sourceFile, _targetFile, _componentsAlias) {
270
+ let result = content;
271
+ for (const [from, to] of Object.entries(RELATIVE_IMPORT_MAPPINGS)) {
272
+ const importPattern = new RegExp(
273
+ `(from\\s+['"])${from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(['"])`,
274
+ "g"
275
+ );
276
+ result = result.replace(importPattern, `$1${to}$2`);
277
+ }
278
+ const siblingImportPattern = /from\s+['"](\.\.\/([a-z][-a-z0-9]*)(?:\/[A-Z][a-zA-Z]*)?)['"]/g;
279
+ result = result.replace(siblingImportPattern, (_match, _fullPath, componentFolder) => {
280
+ const kebabName = toKebabCase(componentFolder);
281
+ return `from './${kebabName}'`;
282
+ });
283
+ return result;
284
+ }
285
+ function generateOriginHeader(componentName, sourcePackage, version = "1.0.0") {
286
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
287
+ return `/**
288
+ * @buildpad-origin ${sourcePackage}/${componentName}
289
+ * @buildpad-version ${version}
290
+ * @buildpad-date ${timestamp}
291
+ *
292
+ * This file was copied from Buildpad UI Packages.
293
+ * To update, run: npx @buildpad/cli add ${componentName} --overwrite
294
+ *
295
+ * Docs: https://buildpad.dev/components/${componentName}
296
+ */
297
+
298
+ `;
299
+ }
300
+ function addOriginHeader(content, componentName, sourcePackage, version = "1.0.0") {
301
+ const header = generateOriginHeader(componentName, sourcePackage, version);
302
+ const useClientMatch = content.match(/^(["']use client["'];?\s*\n)/);
303
+ if (useClientMatch) {
304
+ return useClientMatch[1] + header + content.slice(useClientMatch[0].length);
305
+ }
306
+ return header + content;
307
+ }
308
+ function hasBuildpadOrigin(content) {
309
+ return content.includes("@buildpad-origin");
310
+ }
311
+ function extractOriginInfo(content) {
312
+ const originMatch = content.match(/@buildpad-origin\s+([^\n*]+)/);
313
+ const versionMatch = content.match(/@buildpad-version\s+([^\n*]+)/);
314
+ const dateMatch = content.match(/@buildpad-date\s+([^\n*]+)/);
315
+ if (!originMatch) return null;
316
+ return {
317
+ origin: originMatch[1].trim(),
318
+ version: versionMatch?.[1].trim(),
319
+ date: dateMatch?.[1].trim()
320
+ };
321
+ }
322
+
323
+ // src/commands/validate.ts
324
+ import fs from "fs-extra";
325
+ import path2 from "path";
326
+ import chalk from "chalk";
327
+ import ora from "ora";
328
+ import fg from "fast-glob";
329
+ import { execSync } from "child_process";
330
+ async function checkUntransformedImports(cwd, config) {
331
+ const errors = [];
332
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
333
+ const patterns = [
334
+ path2.join(srcDir, "components/**/*.{ts,tsx,js,jsx}"),
335
+ path2.join(srcDir, "lib/buildpad/**/*.{ts,tsx,js,jsx}")
336
+ ];
337
+ for (const pattern of patterns) {
338
+ const files = await fg(pattern, { ignore: ["**/node_modules/**"] });
339
+ for (const file of files) {
340
+ const content = await fs.readFile(file, "utf-8");
341
+ const lines = content.split("\n");
342
+ lines.forEach((line, index) => {
343
+ if ((line.includes("from '@buildpad/") || line.includes('from "@buildpad/')) && !line.trim().startsWith("//") && !line.trim().startsWith("*")) {
344
+ errors.push({
345
+ file: path2.relative(cwd, file),
346
+ line: index + 1,
347
+ message: `Untransformed import: ${line.trim()}`,
348
+ code: "UNTRANSFORMED_IMPORT"
349
+ });
350
+ }
351
+ });
352
+ }
353
+ }
354
+ return errors;
355
+ }
356
+ async function checkMissingCssFiles(cwd, config) {
357
+ const warnings = [];
358
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
359
+ const componentsDir = path2.join(srcDir, "components/ui");
360
+ const cssRequirements = {
361
+ "input-block-editor.tsx": "InputBlockEditor.css",
362
+ "rich-text-html.tsx": "RichTextHTML.css",
363
+ "rich-text-markdown.tsx": "RichTextMarkdown.css"
364
+ };
365
+ for (const [component, cssFile] of Object.entries(cssRequirements)) {
366
+ const componentPath = path2.join(componentsDir, component);
367
+ const cssPath = path2.join(componentsDir, cssFile);
368
+ if (fs.existsSync(componentPath) && !fs.existsSync(cssPath)) {
369
+ warnings.push({
370
+ file: cssFile,
371
+ message: `Missing CSS file for ${component}`,
372
+ code: "MISSING_CSS"
373
+ });
374
+ }
375
+ }
376
+ return warnings;
377
+ }
378
+ async function checkLibModules(cwd, config) {
379
+ const errors = [];
380
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
381
+ const libDir = path2.join(srcDir, "lib/buildpad");
382
+ const requiredModules = {
383
+ types: ["types/index.ts", "types/core.ts"],
384
+ services: ["services/index.ts", "services/api-request.ts"],
385
+ hooks: ["hooks/index.ts"],
386
+ utils: ["utils.ts", "field-interface-mapper.ts"]
387
+ };
388
+ for (const [module, files] of Object.entries(requiredModules)) {
389
+ if (config.installedLib.includes(module)) {
390
+ for (const file of files) {
391
+ const filePath = path2.join(libDir, file);
392
+ if (!fs.existsSync(filePath)) {
393
+ errors.push({
394
+ file: `lib/buildpad/${file}`,
395
+ message: `Missing required file for ${module} module`,
396
+ code: "MISSING_LIB_FILE"
397
+ });
398
+ }
399
+ }
400
+ }
401
+ }
402
+ const defineInterfacePath = path2.join(libDir, "define-interface.ts");
403
+ const interfaceRegistryPath = path2.join(libDir, "interface-registry.ts");
404
+ if (fs.existsSync(defineInterfacePath) && !fs.existsSync(interfaceRegistryPath)) {
405
+ errors.push({
406
+ file: "lib/buildpad/interface-registry.ts",
407
+ message: "Missing interface-registry.ts (required by define-interface.ts)",
408
+ code: "MISSING_INTERFACE_REGISTRY"
409
+ });
410
+ }
411
+ return errors;
412
+ }
413
+ async function checkSsrIssues(cwd, config) {
414
+ const warnings = [];
415
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
416
+ const componentsDir = path2.join(srcDir, "components/ui");
417
+ const indexPath = path2.join(componentsDir, "index.ts");
418
+ if (fs.existsSync(indexPath)) {
419
+ const content = await fs.readFile(indexPath, "utf-8");
420
+ if (content.includes("InputBlockEditor") && !content.includes("input-block-editor-wrapper") && content.includes("from './input-block-editor'")) {
421
+ warnings.push({
422
+ file: "components/ui/index.ts",
423
+ message: "InputBlockEditor exported directly may cause SSR errors. Use input-block-editor-wrapper instead.",
424
+ code: "SSR_UNSAFE_EXPORT"
425
+ });
426
+ }
427
+ }
428
+ return warnings;
429
+ }
430
+ async function checkApiRoutes(cwd) {
431
+ const warnings = [];
432
+ const srcDir = fs.existsSync(path2.join(cwd, "src/app")) ? path2.join(cwd, "src") : cwd;
433
+ const appDir = path2.join(srcDir, "app");
434
+ const apiDir = path2.join(appDir, "api");
435
+ const requiredRoutes = [
436
+ { path: "fields/[collection]/route.ts", description: "Fetch collection field schemas" },
437
+ { path: "items/[collection]/route.ts", description: "List/Create items" },
438
+ { path: "items/[collection]/[id]/route.ts", description: "Get/Update/Delete item" }
439
+ ];
440
+ const recommendedRoutes = [
441
+ { path: "relations/route.ts", description: "Relation definitions (for M2M/M2O/O2M)" },
442
+ { path: "files/route.ts", description: "File operations (for file components)" }
443
+ ];
444
+ if (!fs.existsSync(apiDir)) {
445
+ warnings.push({
446
+ file: "app/api/",
447
+ message: "Missing API directory. Forms require API routes to fetch data from DaaS.",
448
+ code: "MISSING_API_DIR"
449
+ });
450
+ return warnings;
451
+ }
452
+ for (const route of requiredRoutes) {
453
+ const routePath = path2.join(apiDir, route.path);
454
+ if (!fs.existsSync(routePath)) {
455
+ warnings.push({
456
+ file: `app/api/${route.path}`,
457
+ message: `Missing required API route: ${route.description}`,
458
+ code: "MISSING_API_ROUTE"
459
+ });
460
+ }
461
+ }
462
+ for (const route of recommendedRoutes) {
463
+ const routePath = path2.join(apiDir, route.path);
464
+ if (!fs.existsSync(routePath)) {
465
+ warnings.push({
466
+ file: `app/api/${route.path}`,
467
+ message: `Missing recommended API route: ${route.description}`,
468
+ code: "MISSING_OPTIONAL_API_ROUTE"
469
+ });
470
+ }
471
+ }
472
+ const authHeadersPath = path2.join(srcDir, "lib/api/auth-headers.ts");
473
+ if (!fs.existsSync(authHeadersPath)) {
474
+ warnings.push({
475
+ file: "lib/api/auth-headers.ts",
476
+ message: "Missing auth-headers helper. API routes need this to forward auth tokens.",
477
+ code: "MISSING_AUTH_HELPER"
478
+ });
479
+ }
480
+ return warnings;
481
+ }
482
+ async function checkBrokenRelativeImports(cwd, config) {
483
+ const errors = [];
484
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
485
+ const patterns = [
486
+ path2.join(srcDir, "components/**/*.{ts,tsx,js,jsx}"),
487
+ path2.join(srcDir, "lib/buildpad/**/*.{ts,tsx,js,jsx}")
488
+ ];
489
+ const relativeImportPattern = /from\s+['"](\.\.?\/[^'"]+)['"]/g;
490
+ for (const pattern of patterns) {
491
+ const files = await fg(pattern, { ignore: ["**/node_modules/**"] });
492
+ for (const file of files) {
493
+ const content = await fs.readFile(file, "utf-8");
494
+ const lines = content.split("\n");
495
+ const fileDir = path2.dirname(file);
496
+ lines.forEach((line, index) => {
497
+ if (line.trim().startsWith("//") || line.trim().startsWith("*")) {
498
+ return;
499
+ }
500
+ let match;
501
+ relativeImportPattern.lastIndex = 0;
502
+ while ((match = relativeImportPattern.exec(line)) !== null) {
503
+ const importPath = match[1];
504
+ const possibleExtensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx", ""];
505
+ const absolutePath = path2.resolve(fileDir, importPath);
506
+ const exists = possibleExtensions.some(
507
+ (ext) => fs.existsSync(absolutePath + ext)
508
+ );
509
+ if (!exists) {
510
+ errors.push({
511
+ file: path2.relative(cwd, file),
512
+ line: index + 1,
513
+ message: `Cannot find module '${importPath}'`,
514
+ code: "BROKEN_IMPORT"
515
+ });
516
+ }
517
+ }
518
+ });
519
+ }
520
+ }
521
+ return errors;
522
+ }
523
+ async function checkReact19Compatibility(cwd, config) {
524
+ const warnings = [];
525
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
526
+ const appDir = path2.join(srcDir, "app");
527
+ if (!fs.existsSync(appDir)) {
528
+ return warnings;
529
+ }
530
+ const serverComponentPattern = path2.join(appDir, "**/page.tsx");
531
+ const files = await fg(serverComponentPattern, { ignore: ["**/node_modules/**"] });
532
+ const componentPropPattern = /component=\{[A-Z][a-zA-Z]*\}/;
533
+ for (const file of files) {
534
+ const content = await fs.readFile(file, "utf-8");
535
+ if (content.includes('"use client"') || content.includes("'use client'")) {
536
+ continue;
537
+ }
538
+ const lines = content.split("\n");
539
+ lines.forEach((line, index) => {
540
+ if (componentPropPattern.test(line)) {
541
+ warnings.push({
542
+ file: path2.relative(cwd, file),
543
+ line: index + 1,
544
+ message: 'Passing component as prop in Server Component may cause React 19 errors. Add "use client" or use wrapper pattern.',
545
+ code: "REACT19_COMPONENT_PROP"
546
+ });
547
+ }
548
+ });
549
+ }
550
+ return warnings;
551
+ }
552
+ async function checkDuplicateExports(cwd, config) {
553
+ const warnings = [];
554
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
555
+ const indexPath = path2.join(srcDir, "components/ui/index.ts");
556
+ if (!fs.existsSync(indexPath)) {
557
+ return warnings;
558
+ }
559
+ const content = await fs.readFile(indexPath, "utf-8");
560
+ const exportPattern = /export \* from ['"]\.\/([^'"]+)['"]/g;
561
+ const exports = /* @__PURE__ */ new Map();
562
+ let match;
563
+ let lineNum = 0;
564
+ const lines = content.split("\n");
565
+ for (const line of lines) {
566
+ lineNum++;
567
+ exportPattern.lastIndex = 0;
568
+ while ((match = exportPattern.exec(line)) !== null) {
569
+ const exportPath = match[1];
570
+ if (exports.has(exportPath)) {
571
+ warnings.push({
572
+ file: "components/ui/index.ts",
573
+ line: lineNum,
574
+ message: `Duplicate export from './${exportPath}' (first at line ${exports.get(exportPath)})`,
575
+ code: "DUPLICATE_EXPORT"
576
+ });
577
+ } else {
578
+ exports.set(exportPath, lineNum);
579
+ }
580
+ }
581
+ }
582
+ const componentsDir = path2.join(srcDir, "components/ui");
583
+ const namedExports = /* @__PURE__ */ new Map();
584
+ for (const [exportFile] of exports) {
585
+ const filePath = path2.join(componentsDir, exportFile + ".tsx");
586
+ if (!fs.existsSync(filePath)) continue;
587
+ const fileContent = await fs.readFile(filePath, "utf-8");
588
+ const namedExportPattern = /export\s+(?:const|function|class|type|interface|enum)\s+(\w+)/g;
589
+ let namedMatch;
590
+ while ((namedMatch = namedExportPattern.exec(fileContent)) !== null) {
591
+ const exportName = namedMatch[1];
592
+ if (namedExports.has(exportName)) {
593
+ const firstExport = namedExports.get(exportName);
594
+ warnings.push({
595
+ file: `components/ui/${exportFile}.tsx`,
596
+ message: `Conflicting export '${exportName}' also exported from '${firstExport.file}'. This will cause 'export *' conflicts in index.ts`,
597
+ code: "CONFLICTING_NAMED_EXPORT"
598
+ });
599
+ } else {
600
+ namedExports.set(exportName, { file: exportFile, line: 0 });
601
+ }
602
+ }
603
+ }
604
+ return warnings;
605
+ }
606
+ async function checkTypeScriptErrors(cwd, config) {
607
+ const errors = [];
608
+ const tsconfigPath = path2.join(cwd, "tsconfig.json");
609
+ if (!fs.existsSync(tsconfigPath)) {
610
+ return errors;
611
+ }
612
+ try {
613
+ const srcDir = config.srcDir ? path2.join(cwd, "src") : cwd;
614
+ const componentsDir = path2.join(srcDir, "components/ui");
615
+ if (!fs.existsSync(componentsDir)) {
616
+ return errors;
617
+ }
618
+ const result = execSync(
619
+ `npx --yes tsc --noEmit --skipLibCheck --pretty false 2>&1 || true`,
620
+ { cwd, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024, timeout: 6e4, stdio: ["pipe", "pipe", "pipe"] }
621
+ );
622
+ const tsErrorPattern = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/gm;
623
+ let match;
624
+ while ((match = tsErrorPattern.exec(result)) !== null) {
625
+ const [, file, line, , severity, tsCode, message] = match;
626
+ const relativePath = path2.relative(cwd, file);
627
+ if (relativePath.includes("components/ui") || relativePath.includes("lib/buildpad")) {
628
+ if (severity === "error") {
629
+ errors.push({
630
+ file: relativePath,
631
+ line: parseInt(line, 10),
632
+ message: `${tsCode}: ${message}`,
633
+ code: "TYPESCRIPT_ERROR"
634
+ });
635
+ }
636
+ }
637
+ }
638
+ } catch {
639
+ }
640
+ return errors;
641
+ }
642
+ function generateSuggestions(errors, warnings) {
643
+ const suggestions = [];
644
+ const tsErrorCount = errors.filter((e) => e.code === "TYPESCRIPT_ERROR").length;
645
+ if (tsErrorCount > 0) {
646
+ suggestions.push(
647
+ `Fix ${tsErrorCount} TypeScript error(s) in component files`
648
+ );
649
+ }
650
+ const untransformedCount = errors.filter((e) => e.code === "UNTRANSFORMED_IMPORT").length;
651
+ if (untransformedCount > 0) {
652
+ suggestions.push(
653
+ `Fix ${untransformedCount} untransformed import(s) by running: pnpm cli add --all --overwrite --cwd .`
654
+ );
655
+ }
656
+ const brokenImportCount = errors.filter((e) => e.code === "BROKEN_IMPORT").length;
657
+ if (brokenImportCount > 0) {
658
+ suggestions.push(
659
+ `Fix ${brokenImportCount} broken import(s) by checking file paths or reinstalling components`
660
+ );
661
+ }
662
+ const missingLibCount = errors.filter((e) => e.code === "MISSING_LIB_FILE").length;
663
+ if (missingLibCount > 0) {
664
+ suggestions.push(
665
+ `Reinstall lib modules with: pnpm cli add vform --overwrite --cwd .`
666
+ );
667
+ }
668
+ if (errors.some((e) => e.code === "MISSING_INTERFACE_REGISTRY")) {
669
+ suggestions.push(
670
+ `Add missing interface-registry.ts by reinstalling utils module`
671
+ );
672
+ }
673
+ const missingCssCount = warnings.filter((w) => w.code === "MISSING_CSS").length;
674
+ if (missingCssCount > 0) {
675
+ suggestions.push(
676
+ `Copy missing CSS files from buildpad-ui/packages/ui-interfaces/src/`
677
+ );
678
+ }
679
+ if (warnings.some((w) => w.code === "SSR_UNSAFE_EXPORT")) {
680
+ suggestions.push(
681
+ `Update components/ui/index.ts to export InputBlockEditor from './input-block-editor-wrapper'`
682
+ );
683
+ }
684
+ const missingRouteCount = warnings.filter((w) => w.code === "MISSING_API_ROUTE").length;
685
+ const missingApiDir = warnings.some((w) => w.code === "MISSING_API_DIR");
686
+ const missingAuthHelper = warnings.some((w) => w.code === "MISSING_AUTH_HELPER");
687
+ if (missingApiDir || missingRouteCount > 0 || missingAuthHelper) {
688
+ suggestions.push(
689
+ `Install API routes and auth helpers: pnpm cli add api-routes --cwd .`
690
+ );
691
+ }
692
+ const react19Count = warnings.filter((w) => w.code === "REACT19_COMPONENT_PROP").length;
693
+ if (react19Count > 0) {
694
+ suggestions.push(
695
+ `Fix ${react19Count} React 19 compatibility issue(s): wrap component with "use client" or use Link directly`
696
+ );
697
+ }
698
+ const duplicateCount = warnings.filter((w) => w.code === "DUPLICATE_EXPORT").length;
699
+ if (duplicateCount > 0) {
700
+ suggestions.push(
701
+ `Remove ${duplicateCount} duplicate export(s) from components/ui/index.ts`
702
+ );
703
+ }
704
+ return suggestions;
705
+ }
706
+ async function validate(options) {
707
+ const { cwd, json = false, noExit = false } = options;
708
+ const config = await loadConfig(cwd);
709
+ if (!config) {
710
+ if (json) {
711
+ console.log(JSON.stringify({
712
+ valid: false,
713
+ errors: [{ file: "buildpad.json", message: "Not found", code: "NO_CONFIG" }],
714
+ warnings: [],
715
+ suggestions: ['Run "npx buildpad init" first']
716
+ }));
717
+ } else {
718
+ console.log(chalk.red('\n\u2717 buildpad.json not found. Run "npx buildpad init" first.\n'));
719
+ }
720
+ if (noExit) {
721
+ return { valid: false, errors: [{ file: "buildpad.json", message: "Not found", code: "NO_CONFIG" }], warnings: [], suggestions: ['Run "npx buildpad init" first'] };
722
+ }
723
+ process.exit(1);
724
+ }
725
+ const spinner = json ? null : ora("Validating Buildpad installation...").start();
726
+ try {
727
+ const [
728
+ untransformedErrors,
729
+ brokenImportErrors,
730
+ missingCssWarnings,
731
+ libModuleErrors,
732
+ ssrWarnings,
733
+ apiRouteWarnings,
734
+ react19Warnings,
735
+ duplicateExportWarnings
736
+ ] = await Promise.all([
737
+ checkUntransformedImports(cwd, config),
738
+ checkBrokenRelativeImports(cwd, config),
739
+ checkMissingCssFiles(cwd, config),
740
+ checkLibModules(cwd, config),
741
+ checkSsrIssues(cwd, config),
742
+ checkApiRoutes(cwd),
743
+ checkReact19Compatibility(cwd, config),
744
+ checkDuplicateExports(cwd, config)
745
+ ]);
746
+ if (spinner) {
747
+ spinner.text = "Running TypeScript check...";
748
+ }
749
+ const tsErrors = await checkTypeScriptErrors(cwd, config);
750
+ const errors = [...untransformedErrors, ...brokenImportErrors, ...libModuleErrors, ...tsErrors];
751
+ const warnings = [...missingCssWarnings, ...ssrWarnings, ...apiRouteWarnings, ...react19Warnings, ...duplicateExportWarnings];
752
+ const suggestions = generateSuggestions(errors, warnings);
753
+ const result = {
754
+ valid: errors.length === 0,
755
+ errors,
756
+ warnings,
757
+ suggestions
758
+ };
759
+ if (noExit) {
760
+ spinner?.stop();
761
+ return result;
762
+ }
763
+ if (json) {
764
+ console.log(JSON.stringify(result, null, 2));
765
+ return result;
766
+ }
767
+ if (errors.length === 0 && warnings.length === 0) {
768
+ spinner?.succeed(chalk.green("Buildpad installation is valid! \u2713"));
769
+ console.log(chalk.dim(`
770
+ ${config.installedComponents.length} components installed`));
771
+ console.log(chalk.dim(` ${config.installedLib.length} lib modules installed
772
+ `));
773
+ return result;
774
+ }
775
+ spinner?.stop();
776
+ if (errors.length > 0) {
777
+ console.log(chalk.red(`
778
+ \u2717 Found ${errors.length} error(s):
779
+ `));
780
+ errors.forEach((error) => {
781
+ const location = error.line ? `:${error.line}` : "";
782
+ console.log(chalk.red(` \u2717 ${error.file}${location}`));
783
+ console.log(chalk.dim(` ${error.message}`));
784
+ });
785
+ }
786
+ if (warnings.length > 0) {
787
+ console.log(chalk.yellow(`
788
+ \u26A0 Found ${warnings.length} warning(s):
789
+ `));
790
+ warnings.forEach((warning) => {
791
+ console.log(chalk.yellow(` \u26A0 ${warning.file}`));
792
+ console.log(chalk.dim(` ${warning.message}`));
793
+ });
794
+ }
795
+ if (suggestions.length > 0) {
796
+ console.log(chalk.cyan("\n\u{1F4A1} Suggestions:\n"));
797
+ suggestions.forEach((suggestion, i) => {
798
+ console.log(chalk.cyan(` ${i + 1}. ${suggestion}`));
799
+ });
800
+ }
801
+ console.log();
802
+ if (errors.length > 0) {
803
+ process.exit(1);
804
+ }
805
+ return result;
806
+ } catch (error) {
807
+ spinner?.fail("Validation failed");
808
+ console.error(error);
809
+ process.exit(1);
810
+ }
811
+ }
812
+
813
+ // src/commands/add.ts
814
+ async function getRegistry2() {
815
+ try {
816
+ return await getRegistry();
817
+ } catch (err) {
818
+ console.error(chalk2.red("Failed to load registry:", err.message));
819
+ process.exit(1);
820
+ }
821
+ }
822
+ var COMPONENT_ALIASES = {
823
+ "form": "vform",
824
+ "dynamicform": "vform",
825
+ "v-form": "vform",
826
+ "select": "select-dropdown",
827
+ "dropdown": "select-dropdown",
828
+ "checkbox": "boolean",
829
+ "switch": "toggle",
830
+ "date": "datetime",
831
+ "time": "datetime",
832
+ "datepicker": "datetime",
833
+ "text": "input",
834
+ "textinput": "input",
835
+ "textfield": "input",
836
+ "image": "file-image",
837
+ "imageupload": "file-image",
838
+ "wysiwyg": "rich-text-html",
839
+ "richtext": "rich-text-html",
840
+ "markdown": "rich-text-markdown",
841
+ "md": "rich-text-markdown",
842
+ "m2m": "list-m2m",
843
+ "m2o": "list-m2o",
844
+ "o2m": "list-o2m",
845
+ "m2a": "list-m2a",
846
+ "manytomany": "list-m2m",
847
+ "manytoone": "list-m2o",
848
+ "onetomany": "list-o2m",
849
+ "manytoany": "list-m2a",
850
+ "relation": "list-m2o",
851
+ "multiselect": "select-multiple-dropdown",
852
+ "checkboxes": "select-multiple-checkbox",
853
+ "radio": "select-radio",
854
+ "icon": "select-icon",
855
+ "colorpicker": "color",
856
+ "fileupload": "file",
857
+ "code": "input-code",
858
+ "blockeditor": "input-block-editor",
859
+ "editor": "input-block-editor"
860
+ };
861
+ function findComponentWithSuggestions(name, registry) {
862
+ const normalized = name.toLowerCase().replace(/-/g, "");
863
+ const directMatch = registry.components.find(
864
+ (c) => c.name.toLowerCase() === name.toLowerCase()
865
+ );
866
+ if (directMatch) return directMatch;
867
+ const titleMatch = registry.components.find(
868
+ (c) => c.title.toLowerCase() === name.toLowerCase()
869
+ );
870
+ if (titleMatch) return titleMatch;
871
+ const fuzzyMatch = registry.components.find(
872
+ (c) => c.name.toLowerCase().replace(/-/g, "") === normalized || c.title.toLowerCase().replace(/-/g, "") === normalized
873
+ );
874
+ if (fuzzyMatch) return fuzzyMatch;
875
+ const aliasedName = COMPONENT_ALIASES[normalized];
876
+ if (aliasedName) {
877
+ const aliasMatch = registry.components.find((c) => c.name === aliasedName);
878
+ if (aliasMatch) {
879
+ console.log(chalk2.yellow(`
880
+ \u{1F4A1} "${name}" matched alias \u2192 using "${aliasMatch.name}"
881
+ `));
882
+ return aliasMatch;
883
+ }
884
+ }
885
+ console.log(chalk2.red(`
886
+ \u2717 Component not found: ${name}
887
+ `));
888
+ const suggestions = registry.components.map((c) => ({
889
+ component: c,
890
+ score: calculateSimilarity(normalized, c.name.replace(/-/g, "")) + calculateSimilarity(normalized, c.title.toLowerCase().replace(/-/g, "")) + (c.description.toLowerCase().includes(name.toLowerCase()) ? 0.3 : 0)
891
+ })).filter((s) => s.score > 0.2).sort((a, b) => b.score - a.score).slice(0, 5);
892
+ if (suggestions.length > 0) {
893
+ console.log(chalk2.yellow("Did you mean one of these?\n"));
894
+ suggestions.forEach((s) => {
895
+ console.log(` ${chalk2.green(s.component.name.padEnd(28))} ${chalk2.dim(s.component.description)}`);
896
+ });
897
+ console.log();
898
+ }
899
+ const categoryHint = registry.categories.find(
900
+ (cat) => name.toLowerCase().includes(cat.name.toLowerCase())
901
+ );
902
+ if (categoryHint) {
903
+ console.log(chalk2.dim(`Try: buildpad add --category ${categoryHint.name}
904
+ `));
905
+ }
906
+ console.log(chalk2.dim("Commands to help you find components:"));
907
+ console.log(chalk2.dim(" buildpad list List all components"));
908
+ console.log(chalk2.dim(" buildpad list --category Filter by category"));
909
+ console.log(chalk2.dim(" buildpad info <name> Get component details\n"));
910
+ return null;
911
+ }
912
+ function calculateSimilarity(a, b) {
913
+ if (a === b) return 1;
914
+ if (a.includes(b) || b.includes(a)) return 0.7;
915
+ let matches = 0;
916
+ const shorter = a.length < b.length ? a : b;
917
+ const longer = a.length < b.length ? b : a;
918
+ for (let i = 0; i < shorter.length; i++) {
919
+ if (longer.includes(shorter[i])) matches++;
920
+ }
921
+ return matches / longer.length;
922
+ }
923
+ async function copyLibModule(moduleName, registry, config, cwd, spinner) {
924
+ const libModule = registry.lib[moduleName];
925
+ if (!libModule) {
926
+ spinner.warn(`Lib module not found: ${moduleName}`);
927
+ return false;
928
+ }
929
+ if (config.installedLib.includes(moduleName)) {
930
+ return true;
931
+ }
932
+ if (libModule.internalDependencies) {
933
+ for (const dep of libModule.internalDependencies) {
934
+ if (!config.installedLib.includes(dep)) {
935
+ await copyLibModule(dep, registry, config, cwd, spinner);
936
+ }
937
+ }
938
+ }
939
+ if (libModule.path && libModule.target) {
940
+ const targetPath = path3.join(
941
+ config.srcDir ? path3.join(cwd, "src") : cwd,
942
+ libModule.target
943
+ );
944
+ if (await sourceFileExists(libModule.path)) {
945
+ let content = await resolveSourceFile(libModule.path);
946
+ content = transformImports(content, config);
947
+ content = addOriginHeader(content, moduleName, "@buildpad/lib", registry.version);
948
+ await fs2.ensureDir(path3.dirname(targetPath));
949
+ await fs2.writeFile(targetPath, content);
950
+ }
951
+ }
952
+ if (libModule.files) {
953
+ for (const file of libModule.files) {
954
+ const targetPath = path3.join(
955
+ config.srcDir ? path3.join(cwd, "src") : cwd,
956
+ file.target
957
+ );
958
+ if (await sourceFileExists(file.source)) {
959
+ let content = await resolveSourceFile(file.source);
960
+ content = transformImports(content, config);
961
+ const fileName = path3.basename(file.source, path3.extname(file.source));
962
+ content = addOriginHeader(content, `${moduleName}/${fileName}`, "@buildpad/lib", registry.version);
963
+ await fs2.ensureDir(path3.dirname(targetPath));
964
+ await fs2.writeFile(targetPath, content);
965
+ } else {
966
+ spinner.warn(`Source file not found: ${file.source}`);
967
+ }
968
+ }
969
+ }
970
+ config.installedLib.push(moduleName);
971
+ spinner.succeed(`Installed lib: ${moduleName}`);
972
+ return true;
973
+ }
974
+ async function copyComponent(component, registry, config, cwd, overwrite, spinner, installing = /* @__PURE__ */ new Set(), dryRun = false, dryRunInfo) {
975
+ if (installing.has(component.name)) {
976
+ return true;
977
+ }
978
+ if (config.installedComponents.includes(component.name) && !overwrite && !dryRun) {
979
+ if (installing.has("__nonInteractive__")) {
980
+ return true;
981
+ }
982
+ const { shouldOverwrite } = await prompts({
983
+ type: "confirm",
984
+ name: "shouldOverwrite",
985
+ message: `${component.title} already installed. Overwrite?`,
986
+ initial: false
987
+ });
988
+ if (!shouldOverwrite) {
989
+ spinner.info(`Skipped ${component.title}`);
990
+ return false;
991
+ }
992
+ }
993
+ installing.add(component.name);
994
+ const info2 = {
995
+ component: component.name,
996
+ files: component.files.map((f) => ({ source: f.source, target: f.target })),
997
+ dependencies: component.dependencies,
998
+ libDependencies: component.internalDependencies ?? []
999
+ };
1000
+ for (const dep of component.internalDependencies ?? []) {
1001
+ if (!config.installedLib.includes(dep)) {
1002
+ spinner.text = `Installing dependency: ${dep}...`;
1003
+ if (!dryRun) {
1004
+ await copyLibModule(dep, registry, config, cwd, spinner);
1005
+ }
1006
+ }
1007
+ }
1008
+ if (component.registryDependencies) {
1009
+ for (const depName of component.registryDependencies) {
1010
+ if (!config.installedComponents.includes(depName) && !installing.has(depName)) {
1011
+ const depComponent = registry.components.find((c) => c.name === depName);
1012
+ if (depComponent) {
1013
+ spinner.text = `Installing component dependency: ${depComponent.title}...`;
1014
+ await copyComponent(depComponent, registry, config, cwd, overwrite, spinner, installing, dryRun, dryRunInfo);
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ if (dryRun) {
1020
+ if (dryRunInfo) {
1021
+ dryRunInfo.push(info2);
1022
+ }
1023
+ spinner.info(`Would add ${component.title}`);
1024
+ return true;
1025
+ }
1026
+ for (const file of component.files) {
1027
+ const targetPath = path3.join(
1028
+ config.srcDir ? path3.join(cwd, "src") : cwd,
1029
+ file.target
1030
+ );
1031
+ if (!await sourceFileExists(file.source)) {
1032
+ spinner.warn(`Source not found: ${file.source}`);
1033
+ continue;
1034
+ }
1035
+ let content = await resolveSourceFile(file.source);
1036
+ content = transformIntraComponentImports(content, file.source, file.target, component.files);
1037
+ content = transformImports(content, config, file.target);
1038
+ content = transformRelativeImports(content, file.source, file.target, config.aliases.components);
1039
+ if (component.name === "vform" || file.target.includes("/vform/")) {
1040
+ content = transformVFormImports(content, file.source, file.target);
1041
+ }
1042
+ content = addOriginHeader(content, component.name, "@buildpad/ui-interfaces", registry.version);
1043
+ await fs2.ensureDir(path3.dirname(targetPath));
1044
+ const ext = config.tsx ? ".tsx" : ".jsx";
1045
+ const finalPath = targetPath.replace(/\.tsx?$/, ext);
1046
+ await fs2.writeFile(finalPath, content);
1047
+ }
1048
+ if (!config.installedComponents.includes(component.name)) {
1049
+ config.installedComponents.push(component.name);
1050
+ }
1051
+ if (!config.componentVersions) {
1052
+ config.componentVersions = {};
1053
+ }
1054
+ config.componentVersions[component.name] = {
1055
+ version: registry.version,
1056
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1057
+ source: "@buildpad/ui-interfaces"
1058
+ };
1059
+ spinner.succeed(`Added ${component.title}`);
1060
+ return true;
1061
+ }
1062
+ async function generateComponentsIndex(config, cwd, registry, spinner) {
1063
+ const srcDir = config.srcDir ? path3.join(cwd, "src") : cwd;
1064
+ const componentsDir = path3.join(srcDir, "components/ui");
1065
+ const indexPath = path3.join(componentsDir, "index.ts");
1066
+ spinner.text = "Generating components/ui/index.ts...";
1067
+ const exportLines = [
1068
+ "/**",
1069
+ " * Buildpad UI Components Index",
1070
+ " * ",
1071
+ " * Auto-generated by Buildpad CLI.",
1072
+ ' * Re-run "buildpad add" to update after adding new components.',
1073
+ " */",
1074
+ ""
1075
+ ];
1076
+ const exportedPaths = /* @__PURE__ */ new Set();
1077
+ const namedExportMap = /* @__PURE__ */ new Map();
1078
+ const sortedComponents = [...config.installedComponents].sort();
1079
+ const ssrUnsafeComponents = {
1080
+ "input-block-editor": "input-block-editor-wrapper"
1081
+ };
1082
+ const skippedForWrapper = [];
1083
+ for (const componentName of sortedComponents) {
1084
+ const component = registry.components.find((c) => c.name === componentName);
1085
+ if (!component) continue;
1086
+ const mainFile = component.files[0];
1087
+ if (!mainFile) continue;
1088
+ const targetPath = mainFile.target;
1089
+ let exportPath;
1090
+ if (targetPath.includes("/vform/")) {
1091
+ exportPath = "./vform";
1092
+ } else {
1093
+ const fileName = path3.basename(targetPath, path3.extname(targetPath));
1094
+ if (ssrUnsafeComponents[fileName]) {
1095
+ const wrapperPath = path3.join(componentsDir, `${ssrUnsafeComponents[fileName]}.tsx`);
1096
+ if (fs2.existsSync(wrapperPath)) {
1097
+ exportPath = `./${ssrUnsafeComponents[fileName]}`;
1098
+ skippedForWrapper.push(fileName);
1099
+ } else {
1100
+ exportPath = `./${fileName}`;
1101
+ }
1102
+ } else {
1103
+ exportPath = `./${fileName}`;
1104
+ }
1105
+ }
1106
+ if (!exportedPaths.has(exportPath)) {
1107
+ exportedPaths.add(exportPath);
1108
+ exportLines.push(`export * from '${exportPath}';`);
1109
+ const filePath = path3.join(componentsDir, exportPath.slice(2) + ".tsx");
1110
+ if (fs2.existsSync(filePath)) {
1111
+ try {
1112
+ const content = await fs2.readFile(filePath, "utf-8");
1113
+ const namedExportPattern = /export\s+(?:const|function|class)\s+(\w+)/g;
1114
+ let match;
1115
+ while ((match = namedExportPattern.exec(content)) !== null) {
1116
+ const exportName = match[1];
1117
+ if (!namedExportMap.has(exportName)) {
1118
+ namedExportMap.set(exportName, []);
1119
+ }
1120
+ namedExportMap.get(exportName).push(exportPath);
1121
+ }
1122
+ } catch {
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+ await fs2.writeFile(indexPath, exportLines.join("\n") + "\n");
1128
+ const duplicates = Array.from(namedExportMap.entries()).filter(([_, files]) => files.length > 1);
1129
+ if (duplicates.length > 0) {
1130
+ spinner.warn("Generated components/ui/index.ts (with duplicate warnings)");
1131
+ console.log(chalk2.yellow("\n\u26A0 Duplicate export names detected:"));
1132
+ for (const [exportName, files] of duplicates) {
1133
+ console.log(chalk2.dim(` "${exportName}" exported from: ${files.join(", ")}`));
1134
+ }
1135
+ console.log(chalk2.dim(" Consider using named imports or renaming exports.\n"));
1136
+ } else {
1137
+ spinner.info("Generated components/ui/index.ts");
1138
+ }
1139
+ if (skippedForWrapper.length > 0) {
1140
+ console.log(chalk2.dim(` \u2139 Using SSR-safe wrappers for: ${skippedForWrapper.join(", ")}`));
1141
+ }
1142
+ }
1143
+ async function add(components, options) {
1144
+ const { cwd, all, withApi, category, overwrite = false, dryRun = false, nonInteractive = false } = options;
1145
+ if (dryRun) {
1146
+ console.log(chalk2.yellow("\n\u{1F50D} Dry Run Mode - No files will be modified\n"));
1147
+ }
1148
+ const config = await loadConfig(cwd);
1149
+ if (!config) {
1150
+ console.log(chalk2.red('\n\u2717 buildpad.json not found. Run "npx buildpad init" first.\n'));
1151
+ process.exit(1);
1152
+ }
1153
+ if (!config.componentVersions) {
1154
+ config.componentVersions = {};
1155
+ }
1156
+ const registry = await getRegistry2();
1157
+ if (withApi || all) {
1158
+ console.log(chalk2.bold("\n\u{1F50C} Installing API routes and Supabase auth...\n"));
1159
+ const spinner2 = ora2("Processing lib modules...").start();
1160
+ if (registry.lib["supabase-auth"] && !config.installedLib.includes("supabase-auth")) {
1161
+ await copyLibModule("supabase-auth", registry, config, cwd, spinner2);
1162
+ }
1163
+ if (registry.lib["api-routes"] && !config.installedLib.includes("api-routes")) {
1164
+ await copyLibModule("api-routes", registry, config, cwd, spinner2);
1165
+ }
1166
+ spinner2.succeed("API routes and auth installed!");
1167
+ await saveConfig(cwd, config);
1168
+ }
1169
+ let componentsToAdd = [];
1170
+ if (all) {
1171
+ componentsToAdd = registry.components;
1172
+ } else if (category) {
1173
+ componentsToAdd = registry.components.filter((c) => c.category === category);
1174
+ if (componentsToAdd.length === 0) {
1175
+ console.log(chalk2.red(`
1176
+ \u2717 No components found in category: ${category}
1177
+ `));
1178
+ const categories = registry.categories.map((c) => c.name).join(", ");
1179
+ console.log(chalk2.dim(`Available categories: ${categories}
1180
+ `));
1181
+ process.exit(1);
1182
+ }
1183
+ } else if (components.length > 0) {
1184
+ for (const name of components) {
1185
+ const component = findComponentWithSuggestions(name, registry);
1186
+ if (!component) {
1187
+ process.exit(1);
1188
+ }
1189
+ componentsToAdd.push(component);
1190
+ }
1191
+ } else {
1192
+ const choices = registry.categories.map((cat) => ({
1193
+ title: chalk2.bold(cat.title),
1194
+ value: cat.name,
1195
+ description: cat.description
1196
+ }));
1197
+ const { selectedCategory } = await prompts({
1198
+ type: "select",
1199
+ name: "selectedCategory",
1200
+ message: "Select a category",
1201
+ choices
1202
+ });
1203
+ if (!selectedCategory) {
1204
+ console.log(chalk2.yellow("\n\u2713 No category selected\n"));
1205
+ return;
1206
+ }
1207
+ const categoryComponents = registry.components.filter(
1208
+ (c) => c.category === selectedCategory
1209
+ );
1210
+ const { selected } = await prompts({
1211
+ type: "multiselect",
1212
+ name: "selected",
1213
+ message: "Select components to add",
1214
+ choices: categoryComponents.map((c) => ({
1215
+ title: `${c.title} - ${c.description}`,
1216
+ value: c.name,
1217
+ selected: false
1218
+ })),
1219
+ hint: "- Space to select. Return to submit"
1220
+ });
1221
+ componentsToAdd = registry.components.filter((c) => selected?.includes(c.name));
1222
+ }
1223
+ if (componentsToAdd.length === 0) {
1224
+ console.log(chalk2.yellow("\n\u2713 No components selected\n"));
1225
+ return;
1226
+ }
1227
+ if (dryRun) {
1228
+ console.log(chalk2.bold(`
1229
+ \u{1F50D} Dry Run: Would add ${componentsToAdd.length} component(s)
1230
+ `));
1231
+ const dryRunInfo = [];
1232
+ const spinner2 = ora2("Analyzing...").start();
1233
+ for (const component of componentsToAdd) {
1234
+ spinner2.text = `Analyzing ${component.title}...`;
1235
+ await copyComponent(component, registry, config, cwd, overwrite, spinner2, /* @__PURE__ */ new Set(), true, dryRunInfo);
1236
+ }
1237
+ spinner2.stop();
1238
+ console.log(chalk2.bold("\n\u{1F4CB} Files that would be created:\n"));
1239
+ for (const info2 of dryRunInfo) {
1240
+ console.log(chalk2.cyan(` ${info2.component}:`));
1241
+ for (const file of info2.files) {
1242
+ console.log(chalk2.dim(` \u2192 ${file.target}`));
1243
+ }
1244
+ }
1245
+ const allDryRunDeps = /* @__PURE__ */ new Set();
1246
+ dryRunInfo.forEach((info2) => info2.dependencies.forEach((dep) => allDryRunDeps.add(dep)));
1247
+ if (allDryRunDeps.size > 0) {
1248
+ console.log(chalk2.bold("\n\u{1F4E6} External dependencies needed:\n"));
1249
+ Array.from(allDryRunDeps).forEach((dep) => console.log(chalk2.dim(` ${dep}`)));
1250
+ }
1251
+ console.log(chalk2.dim("\n Run without --dry-run to install components.\n"));
1252
+ return;
1253
+ }
1254
+ console.log(chalk2.bold(`
1255
+ \u{1F4E6} Adding ${componentsToAdd.length} component(s)...
1256
+ `));
1257
+ const spinner = ora2("Processing...").start();
1258
+ const allDeps = /* @__PURE__ */ new Set();
1259
+ try {
1260
+ const sharedInstalling = /* @__PURE__ */ new Set();
1261
+ if (nonInteractive || all) {
1262
+ sharedInstalling.add("__nonInteractive__");
1263
+ }
1264
+ for (const component of componentsToAdd) {
1265
+ spinner.text = `Adding ${component.title}...`;
1266
+ await copyComponent(component, registry, config, cwd, overwrite, spinner, sharedInstalling, false);
1267
+ component.dependencies.forEach((dep) => allDeps.add(dep));
1268
+ }
1269
+ for (const libName of config.installedLib) {
1270
+ const libModule = registry.lib[libName];
1271
+ if (libModule?.dependencies) {
1272
+ for (const dep of libModule.dependencies) {
1273
+ allDeps.add(dep.replace(/@[^@/]*$/, ""));
1274
+ }
1275
+ }
1276
+ }
1277
+ config.registryVersion = registry.version;
1278
+ await saveConfig(cwd, config);
1279
+ await generateComponentsIndex(config, cwd, registry, spinner);
1280
+ spinner.succeed("All components added!");
1281
+ if (!nonInteractive) {
1282
+ console.log(chalk2.bold("\n\u{1F50D} Running post-install validation...\n"));
1283
+ try {
1284
+ await validate({ cwd, json: false });
1285
+ } catch {
1286
+ }
1287
+ }
1288
+ console.log(chalk2.bold("\n\u{1F4E6} External dependencies...\n"));
1289
+ const packageJsonPath = path3.join(cwd, "package.json");
1290
+ let missingDeps = [];
1291
+ if (fs2.existsSync(packageJsonPath)) {
1292
+ const packageJson = await fs2.readJSON(packageJsonPath);
1293
+ const installed = {
1294
+ ...packageJson.dependencies,
1295
+ ...packageJson.devDependencies
1296
+ };
1297
+ missingDeps = Array.from(allDeps).filter((dep) => !installed[dep]);
1298
+ } else {
1299
+ missingDeps = Array.from(allDeps);
1300
+ }
1301
+ if (missingDeps.length > 0) {
1302
+ console.log(chalk2.yellow("\u26A0 Missing dependencies:"));
1303
+ missingDeps.forEach((dep) => console.log(chalk2.dim(` - ${dep}`)));
1304
+ let autoInstall = nonInteractive;
1305
+ if (!nonInteractive) {
1306
+ const answer = await prompts({
1307
+ type: "confirm",
1308
+ name: "autoInstall",
1309
+ message: "Install missing dependencies automatically?",
1310
+ initial: true
1311
+ });
1312
+ autoInstall = answer.autoInstall;
1313
+ }
1314
+ if (autoInstall) {
1315
+ const installSpinner = ora2("Installing dependencies...").start();
1316
+ try {
1317
+ const hasYarnLock = fs2.existsSync(path3.join(cwd, "yarn.lock"));
1318
+ const hasPnpmLock = fs2.existsSync(path3.join(cwd, "pnpm-lock.yaml"));
1319
+ const hasBunLock = fs2.existsSync(path3.join(cwd, "bun.lockb"));
1320
+ let installCmd;
1321
+ if (hasPnpmLock) {
1322
+ installCmd = `pnpm add ${missingDeps.join(" ")}`;
1323
+ } else if (hasYarnLock) {
1324
+ installCmd = `yarn add ${missingDeps.join(" ")}`;
1325
+ } else if (hasBunLock) {
1326
+ installCmd = `bun add ${missingDeps.join(" ")}`;
1327
+ } else {
1328
+ installCmd = `npm install ${missingDeps.join(" ")}`;
1329
+ }
1330
+ const { execSync: execSync3 } = await import("child_process");
1331
+ execSync3(installCmd, { cwd, stdio: "pipe" });
1332
+ installSpinner.succeed("Dependencies installed!");
1333
+ } catch (error) {
1334
+ installSpinner.fail("Failed to install dependencies");
1335
+ console.log(chalk2.dim("\nInstall manually with:"));
1336
+ console.log(chalk2.cyan(` pnpm add ${missingDeps.join(" ")}
1337
+ `));
1338
+ }
1339
+ } else {
1340
+ console.log(chalk2.dim("\nInstall manually with:"));
1341
+ console.log(chalk2.cyan(` pnpm add ${missingDeps.join(" ")}
1342
+ `));
1343
+ }
1344
+ } else {
1345
+ console.log(chalk2.green("\u2713 All external dependencies installed\n"));
1346
+ }
1347
+ console.log(chalk2.bold.blue("\u{1F4CB} Summary:\n"));
1348
+ console.log(chalk2.dim("Components installed:"));
1349
+ config.installedComponents.forEach((name) => {
1350
+ console.log(chalk2.green(` \u2713 ${name}`));
1351
+ });
1352
+ if (config.installedLib.length > 0) {
1353
+ console.log(chalk2.dim("\nLib modules installed:"));
1354
+ config.installedLib.forEach((name) => {
1355
+ console.log(chalk2.green(` \u2713 ${name}`));
1356
+ });
1357
+ }
1358
+ console.log(chalk2.bold.green("\n\u2728 Done!\n"));
1359
+ console.log(chalk2.dim("Components are now part of your codebase. Customize freely!"));
1360
+ console.log(chalk2.dim(`Location: ${config.aliases.components}
1361
+ `));
1362
+ } catch (error) {
1363
+ spinner.fail("Failed to add components");
1364
+ console.error(chalk2.red(error));
1365
+ process.exit(1);
1366
+ }
1367
+ }
1368
+
1369
+ // src/commands/list.ts
1370
+ import chalk3 from "chalk";
1371
+ async function getRegistry3() {
1372
+ try {
1373
+ return await getRegistry();
1374
+ } catch (err) {
1375
+ console.error(chalk3.red("Failed to load registry:", err.message));
1376
+ process.exit(1);
1377
+ }
1378
+ }
1379
+ async function list(options) {
1380
+ const { category, json, cwd = process.cwd() } = options;
1381
+ const registry = await getRegistry3();
1382
+ const config = await loadConfig(cwd);
1383
+ let components = category ? registry.components.filter((c) => c.category === category) : registry.components;
1384
+ if (json) {
1385
+ console.log(JSON.stringify(components, null, 2));
1386
+ return;
1387
+ }
1388
+ console.log(chalk3.bold("\n\u{1F4E6} Buildpad Components (Copy & Own)\n"));
1389
+ console.log(chalk3.dim("Components are copied to your project as source files.\n"));
1390
+ const byCategory = components.reduce((acc, c) => {
1391
+ if (!acc[c.category]) acc[c.category] = [];
1392
+ acc[c.category].push(c);
1393
+ return acc;
1394
+ }, {});
1395
+ const categoryTitles = registry.categories.reduce((acc, cat) => {
1396
+ acc[cat.name] = cat.title;
1397
+ return acc;
1398
+ }, {});
1399
+ for (const [cat, items] of Object.entries(byCategory)) {
1400
+ const catTitle = categoryTitles[cat] || cat.toUpperCase();
1401
+ console.log(chalk3.bold.cyan(`
1402
+ ${catTitle}`));
1403
+ items.forEach((item) => {
1404
+ const installed = config?.installedComponents.includes(item.name);
1405
+ const status2 = installed ? chalk3.green("\u2713") : " ";
1406
+ const name = item.name.padEnd(28);
1407
+ console.log(
1408
+ ` ${status2} ${chalk3.green(name)} ${chalk3.dim(item.description)}`
1409
+ );
1410
+ if (item.internalDependencies.length > 0) {
1411
+ console.log(
1412
+ ` ${chalk3.dim("requires:")} ${chalk3.yellow(item.internalDependencies.join(", "))}`
1413
+ );
1414
+ }
1415
+ });
1416
+ }
1417
+ console.log(chalk3.bold("\n\u{1F4C2} Categories"));
1418
+ registry.categories.forEach((cat) => {
1419
+ const count = registry.components.filter((c) => c.category === cat.name).length;
1420
+ console.log(` ${chalk3.cyan(cat.name.padEnd(15))} ${count} components - ${chalk3.dim(cat.description)}`);
1421
+ });
1422
+ if (config) {
1423
+ console.log(chalk3.bold("\n\u{1F4CB} Installed"));
1424
+ console.log(` Components: ${chalk3.green(config.installedComponents.length)}`);
1425
+ console.log(` Lib modules: ${chalk3.green(config.installedLib.length)} ${chalk3.dim(`(${config.installedLib.join(", ") || "none"})`)}`);
1426
+ }
1427
+ console.log(chalk3.bold("\n\u{1F4A1} Usage"));
1428
+ console.log(chalk3.dim(" npx buildpad add input"));
1429
+ console.log(chalk3.dim(" npx buildpad add input select-dropdown datetime"));
1430
+ console.log(chalk3.dim(" npx buildpad add --category selection"));
1431
+ console.log(chalk3.dim(" npx buildpad add --all\n"));
1432
+ }
1433
+
1434
+ // src/commands/diff.ts
1435
+ import fs3 from "fs-extra";
1436
+ import path4 from "path";
1437
+ import chalk4 from "chalk";
1438
+ async function getRegistry4() {
1439
+ try {
1440
+ return await getRegistry();
1441
+ } catch (err) {
1442
+ console.error(chalk4.red("Failed to load registry:", err.message));
1443
+ process.exit(1);
1444
+ }
1445
+ }
1446
+ async function diff(component, options) {
1447
+ const { cwd } = options;
1448
+ console.log(chalk4.bold(`
1449
+ \u{1F4CB} Preview: ${component}
1450
+ `));
1451
+ const config = await loadConfig(cwd);
1452
+ if (!config) {
1453
+ console.log(chalk4.red('\u2717 buildpad.json not found. Run "npx buildpad init" first.\n'));
1454
+ process.exit(1);
1455
+ }
1456
+ const registry = await getRegistry4();
1457
+ const comp = registry.components.find(
1458
+ (c) => c.name.toLowerCase() === component.toLowerCase() || c.title.toLowerCase() === component.toLowerCase()
1459
+ );
1460
+ if (!comp) {
1461
+ console.log(chalk4.red(`\u2717 Component not found: ${component}
1462
+ `));
1463
+ console.log(chalk4.dim('Run "npx buildpad list" to see available components.\n'));
1464
+ process.exit(1);
1465
+ }
1466
+ console.log(chalk4.bold.cyan(`${comp.title}`));
1467
+ console.log(chalk4.dim(comp.description));
1468
+ console.log();
1469
+ console.log(chalk4.bold("Files to be created:"));
1470
+ for (const file of comp.files) {
1471
+ const targetPath = path4.join(
1472
+ config.srcDir ? path4.join(cwd, "src") : cwd,
1473
+ file.target
1474
+ );
1475
+ const relativePath = path4.relative(cwd, targetPath);
1476
+ const exists = fs3.existsSync(targetPath);
1477
+ if (exists) {
1478
+ console.log(chalk4.yellow(` \u26A0 ${relativePath} (exists, will be overwritten)`));
1479
+ } else {
1480
+ console.log(chalk4.green(` + ${relativePath}`));
1481
+ }
1482
+ }
1483
+ if (comp.internalDependencies.length > 0) {
1484
+ console.log();
1485
+ console.log(chalk4.bold("Lib modules required:"));
1486
+ for (const dep of comp.internalDependencies) {
1487
+ const installed = config.installedLib.includes(dep);
1488
+ if (installed) {
1489
+ console.log(chalk4.dim(` \u2713 ${dep} (already installed)`));
1490
+ } else {
1491
+ console.log(chalk4.cyan(` \u2192 ${dep}`));
1492
+ const libModule = registry.lib[dep];
1493
+ if (libModule?.files) {
1494
+ for (const file of libModule.files) {
1495
+ console.log(chalk4.dim(` + ${file.target}`));
1496
+ }
1497
+ }
1498
+ }
1499
+ }
1500
+ }
1501
+ if (comp.dependencies.length > 0) {
1502
+ console.log();
1503
+ console.log(chalk4.bold("External dependencies:"));
1504
+ const packageJsonPath = path4.join(cwd, "package.json");
1505
+ let installed = {};
1506
+ if (fs3.existsSync(packageJsonPath)) {
1507
+ const pkg = await fs3.readJSON(packageJsonPath);
1508
+ installed = { ...pkg.dependencies, ...pkg.devDependencies };
1509
+ }
1510
+ for (const dep of comp.dependencies) {
1511
+ if (installed[dep]) {
1512
+ console.log(chalk4.dim(` \u2713 ${dep} (v${installed[dep]})`));
1513
+ } else {
1514
+ console.log(chalk4.yellow(` \u26A0 ${dep} (not installed)`));
1515
+ }
1516
+ }
1517
+ }
1518
+ console.log();
1519
+ console.log(chalk4.bold("Import transformation preview:"));
1520
+ const sampleSource = comp.files[0]?.source;
1521
+ if (sampleSource) {
1522
+ try {
1523
+ const content = await resolveSourceFile(sampleSource);
1524
+ const lines = content.split("\n").slice(0, 15);
1525
+ console.log(chalk4.dim("\nOriginal imports:"));
1526
+ lines.filter((l) => l.startsWith("import")).forEach((line) => {
1527
+ if (line.includes("@buildpad/")) {
1528
+ console.log(chalk4.red(` ${line}`));
1529
+ }
1530
+ });
1531
+ const transformed = transformImports(content, config);
1532
+ const transformedLines = transformed.split("\n").slice(0, 15);
1533
+ console.log(chalk4.dim("\nTransformed imports:"));
1534
+ transformedLines.filter((l) => l.startsWith("import")).forEach((line) => {
1535
+ if (line.includes(config.aliases.lib) || line.includes(config.aliases.components)) {
1536
+ console.log(chalk4.green(` ${line}`));
1537
+ }
1538
+ });
1539
+ } catch {
1540
+ console.log(chalk4.dim("\n (source preview not available in remote mode)"));
1541
+ }
1542
+ }
1543
+ console.log();
1544
+ console.log(chalk4.dim("Run the following to add this component:"));
1545
+ console.log(chalk4.cyan(` npx buildpad add ${comp.name}
1546
+ `));
1547
+ }
1548
+
1549
+ // src/commands/status.ts
1550
+ import fs4 from "fs-extra";
1551
+ import path5 from "path";
1552
+ import chalk5 from "chalk";
1553
+ async function findBuildpadFiles(dir) {
1554
+ const files = [];
1555
+ if (!fs4.existsSync(dir)) {
1556
+ return files;
1557
+ }
1558
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1559
+ for (const entry of entries) {
1560
+ const fullPath = path5.join(dir, entry.name);
1561
+ if (entry.isDirectory()) {
1562
+ if (entry.name !== "node_modules" && !entry.name.startsWith(".")) {
1563
+ const subFiles = await findBuildpadFiles(fullPath);
1564
+ files.push(...subFiles);
1565
+ }
1566
+ } else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
1567
+ const content = await fs4.readFile(fullPath, "utf-8");
1568
+ if (hasBuildpadOrigin(content)) {
1569
+ const info2 = extractOriginInfo(content);
1570
+ if (info2 && info2.origin) {
1571
+ files.push({
1572
+ path: fullPath,
1573
+ origin: info2.origin,
1574
+ version: info2.version || "unknown",
1575
+ date: info2.date || "unknown",
1576
+ modified: false
1577
+ // TODO: Compare with registry hash
1578
+ });
1579
+ }
1580
+ }
1581
+ }
1582
+ }
1583
+ return files;
1584
+ }
1585
+ function groupByOrigin(files) {
1586
+ const groups = /* @__PURE__ */ new Map();
1587
+ for (const file of files) {
1588
+ const [pkg, component] = file.origin.split("/").slice(0, 2);
1589
+ const key = `${pkg}/${component || "root"}`;
1590
+ if (!groups.has(key)) {
1591
+ groups.set(key, []);
1592
+ }
1593
+ groups.get(key).push(file);
1594
+ }
1595
+ return groups;
1596
+ }
1597
+ async function status(options) {
1598
+ const { cwd, json = false } = options;
1599
+ const config = await loadConfig(cwd);
1600
+ if (!config) {
1601
+ console.log(chalk5.red("\n\u2717 buildpad.json not found.\n"));
1602
+ console.log("This project may not be initialized with Buildpad.");
1603
+ console.log('Run "npx @buildpad/cli init" to initialize.\n');
1604
+ return;
1605
+ }
1606
+ const srcDir = config.srcDir ? path5.join(cwd, "src") : cwd;
1607
+ const files = await findBuildpadFiles(srcDir);
1608
+ if (json) {
1609
+ console.log(JSON.stringify({
1610
+ config: {
1611
+ installedLib: config.installedLib,
1612
+ installedComponents: config.installedComponents
1613
+ },
1614
+ files: files.map((f) => ({
1615
+ ...f,
1616
+ path: path5.relative(cwd, f.path)
1617
+ }))
1618
+ }, null, 2));
1619
+ return;
1620
+ }
1621
+ console.log("\n" + chalk5.bold("\u{1F4E6} Buildpad Status\n"));
1622
+ console.log(chalk5.gray("Config file: ") + "buildpad.json");
1623
+ console.log(chalk5.gray("Lib modules: ") + (config.installedLib.join(", ") || "none"));
1624
+ console.log(chalk5.gray("Components: ") + (config.installedComponents.join(", ") || "none"));
1625
+ if (files.length === 0) {
1626
+ console.log("\n" + chalk5.yellow("No files with @buildpad-origin headers found."));
1627
+ console.log(chalk5.gray("This may mean components were installed before origin tracking was added."));
1628
+ return;
1629
+ }
1630
+ const groups = groupByOrigin(files);
1631
+ console.log("\n" + chalk5.bold("Installed Files:\n"));
1632
+ for (const [origin, groupFiles] of groups) {
1633
+ console.log(chalk5.cyan(` ${origin}`));
1634
+ for (const file of groupFiles) {
1635
+ const relativePath = path5.relative(cwd, file.path);
1636
+ console.log(chalk5.gray(` \u2514\u2500 ${relativePath}`));
1637
+ console.log(chalk5.gray(` v${file.version} (${file.date})`));
1638
+ }
1639
+ console.log();
1640
+ }
1641
+ console.log(chalk5.gray(`
1642
+ Total: ${files.length} files from Buildpad
1643
+ `));
1644
+ }
1645
+
1646
+ // src/commands/info.ts
1647
+ import chalk6 from "chalk";
1648
+ async function getRegistry5() {
1649
+ try {
1650
+ return await getRegistry();
1651
+ } catch (err) {
1652
+ console.error(chalk6.red("Failed to load registry:", err.message));
1653
+ process.exit(1);
1654
+ }
1655
+ }
1656
+ function findComponent(name, registry) {
1657
+ const normalized = name.toLowerCase().replace(/-/g, "");
1658
+ const direct = registry.components.find(
1659
+ (c) => c.name.toLowerCase() === name.toLowerCase() || c.title.toLowerCase() === name.toLowerCase()
1660
+ );
1661
+ if (direct) return direct;
1662
+ const fuzzy = registry.components.find(
1663
+ (c) => c.name.toLowerCase().replace(/-/g, "") === normalized || c.title.toLowerCase().replace(/-/g, "") === normalized
1664
+ );
1665
+ if (fuzzy) return fuzzy;
1666
+ const aliases = {
1667
+ "form": "vform",
1668
+ "dynamicform": "vform",
1669
+ "select": "select-dropdown",
1670
+ "dropdown": "select-dropdown",
1671
+ "checkbox": "boolean",
1672
+ "switch": "toggle",
1673
+ "date": "datetime",
1674
+ "time": "datetime",
1675
+ "text": "input",
1676
+ "textinput": "input",
1677
+ "image": "file-image",
1678
+ "wysiwyg": "rich-text-html",
1679
+ "markdown": "rich-text-markdown",
1680
+ "m2m": "list-m2m",
1681
+ "m2o": "list-m2o",
1682
+ "o2m": "list-o2m",
1683
+ "m2a": "list-m2a",
1684
+ "manytomany": "list-m2m",
1685
+ "manytoone": "list-m2o",
1686
+ "onetomany": "list-o2m",
1687
+ "manytoany": "list-m2a"
1688
+ };
1689
+ const aliased = aliases[normalized];
1690
+ if (aliased) {
1691
+ return registry.components.find((c) => c.name === aliased);
1692
+ }
1693
+ return void 0;
1694
+ }
1695
+ function calculateTotalDependencies(component, registry, visited = /* @__PURE__ */ new Set()) {
1696
+ if (visited.has(component.name)) {
1697
+ return { components: [], libs: [], npm: [] };
1698
+ }
1699
+ visited.add(component.name);
1700
+ const result = {
1701
+ components: [component.name],
1702
+ libs: [...component.internalDependencies],
1703
+ npm: [...component.dependencies]
1704
+ };
1705
+ if (component.registryDependencies) {
1706
+ for (const depName of component.registryDependencies) {
1707
+ const dep = registry.components.find((c) => c.name === depName);
1708
+ if (dep) {
1709
+ const subDeps = calculateTotalDependencies(dep, registry, visited);
1710
+ result.components.push(...subDeps.components);
1711
+ result.libs.push(...subDeps.libs);
1712
+ result.npm.push(...subDeps.npm);
1713
+ }
1714
+ }
1715
+ }
1716
+ return {
1717
+ components: [...new Set(result.components)],
1718
+ libs: [...new Set(result.libs)],
1719
+ npm: [...new Set(result.npm)]
1720
+ };
1721
+ }
1722
+ async function info(componentName, options) {
1723
+ const { json } = options;
1724
+ const registry = await getRegistry5();
1725
+ const component = findComponent(componentName, registry);
1726
+ if (!component) {
1727
+ console.log(chalk6.red(`
1728
+ \u2717 Component not found: ${componentName}
1729
+ `));
1730
+ const suggestions = registry.components.filter(
1731
+ (c) => c.name.includes(componentName.toLowerCase()) || c.title.toLowerCase().includes(componentName.toLowerCase()) || c.description.toLowerCase().includes(componentName.toLowerCase())
1732
+ ).slice(0, 5);
1733
+ if (suggestions.length > 0) {
1734
+ console.log(chalk6.yellow("Did you mean one of these?\n"));
1735
+ suggestions.forEach((s) => {
1736
+ console.log(` ${chalk6.green(s.name.padEnd(25))} ${chalk6.dim(s.description)}`);
1737
+ });
1738
+ console.log();
1739
+ }
1740
+ console.log(chalk6.dim('Run "buildpad list" to see all available components.\n'));
1741
+ process.exit(1);
1742
+ }
1743
+ const totals = calculateTotalDependencies(component, registry);
1744
+ const category = registry.categories.find((c) => c.name === component.category);
1745
+ if (json) {
1746
+ console.log(JSON.stringify({
1747
+ ...component,
1748
+ categoryTitle: category?.title,
1749
+ totalDependencies: totals
1750
+ }, null, 2));
1751
+ return;
1752
+ }
1753
+ console.log(chalk6.bold.blue(`
1754
+ \u{1F4E6} ${component.title}`));
1755
+ console.log(chalk6.dim(` ${component.name} \u2022 ${category?.title || component.category}
1756
+ `));
1757
+ console.log(`${component.description}
1758
+ `);
1759
+ console.log(chalk6.bold("\u{1F4C1} Source Files"));
1760
+ component.files.forEach((file) => {
1761
+ console.log(` ${chalk6.green(file.source)}`);
1762
+ console.log(` ${chalk6.dim("\u2192")} ${chalk6.cyan(file.target)}`);
1763
+ });
1764
+ if (component.dependencies.length > 0) {
1765
+ console.log(chalk6.bold("\n\u{1F4E6} NPM Dependencies"));
1766
+ console.log(` ${chalk6.yellow(component.dependencies.join(", "))}`);
1767
+ }
1768
+ if (component.internalDependencies.length > 0) {
1769
+ console.log(chalk6.bold("\n\u{1F527} Lib Modules"));
1770
+ component.internalDependencies.forEach((lib) => {
1771
+ const libModule = registry.lib[lib];
1772
+ console.log(` ${chalk6.magenta(lib)} ${chalk6.dim(`- ${libModule?.description || ""}`)}`);
1773
+ });
1774
+ }
1775
+ if (component.registryDependencies && component.registryDependencies.length > 0) {
1776
+ console.log(chalk6.bold("\n\u{1F517} Component Dependencies"));
1777
+ console.log(` ${chalk6.cyan(component.registryDependencies.length)} components will be installed:`);
1778
+ const chunks = [];
1779
+ for (let i = 0; i < component.registryDependencies.length; i += 6) {
1780
+ chunks.push(component.registryDependencies.slice(i, i + 6).join(", "));
1781
+ }
1782
+ chunks.forEach((chunk) => console.log(` ${chalk6.dim(chunk)}`));
1783
+ }
1784
+ if (component.interface) {
1785
+ console.log(chalk6.bold("\n\u{1F3A8} Interface Metadata"));
1786
+ console.log(` ID: ${chalk6.green(component.interface.id)}`);
1787
+ console.log(` Icon: ${chalk6.cyan(component.interface.icon)}`);
1788
+ console.log(` Field Types: ${chalk6.yellow(component.interface.types.join(", "))}`);
1789
+ if (component.interface.recommended) {
1790
+ console.log(` ${chalk6.green("\u2605")} Recommended interface for its field types`);
1791
+ }
1792
+ }
1793
+ console.log(chalk6.bold("\n\u{1F4CA} Installation Summary"));
1794
+ console.log(` Components: ${chalk6.green(totals.components.length)}`);
1795
+ console.log(` Lib modules: ${chalk6.green(totals.libs.length)} ${chalk6.dim(`(${totals.libs.join(", ") || "none"})`)}`);
1796
+ console.log(` NPM packages: ${chalk6.yellow(totals.npm.length)}`);
1797
+ console.log(chalk6.bold("\n\u{1F4A1} Usage"));
1798
+ console.log(chalk6.dim(` buildpad add ${component.name}`));
1799
+ console.log(chalk6.dim(` buildpad tree ${component.name}
1800
+ `));
1801
+ }
1802
+
1803
+ // src/commands/tree.ts
1804
+ import chalk7 from "chalk";
1805
+ async function getRegistry6() {
1806
+ try {
1807
+ return await getRegistry();
1808
+ } catch (err) {
1809
+ console.error(chalk7.red("Failed to load registry:", err.message));
1810
+ process.exit(1);
1811
+ }
1812
+ }
1813
+ function findComponent2(name, registry) {
1814
+ return registry.components.find(
1815
+ (c) => c.name.toLowerCase() === name.toLowerCase() || c.title.toLowerCase() === name.toLowerCase()
1816
+ );
1817
+ }
1818
+ function buildTree(component, registry, visited = /* @__PURE__ */ new Set(), depth = 0, maxDepth = 3) {
1819
+ const node = {
1820
+ name: component.name,
1821
+ type: "component",
1822
+ description: component.description,
1823
+ children: []
1824
+ };
1825
+ if (visited.has(component.name) || depth >= maxDepth) {
1826
+ return node;
1827
+ }
1828
+ visited.add(component.name);
1829
+ for (const libName of component.internalDependencies) {
1830
+ const libModule = registry.lib[libName];
1831
+ const libNode = {
1832
+ name: libName,
1833
+ type: "lib",
1834
+ description: libModule?.description,
1835
+ children: []
1836
+ };
1837
+ if (libModule?.internalDependencies) {
1838
+ for (const subLib of libModule.internalDependencies) {
1839
+ if (!visited.has(`lib:${subLib}`)) {
1840
+ visited.add(`lib:${subLib}`);
1841
+ const subLibModule = registry.lib[subLib];
1842
+ libNode.children.push({
1843
+ name: subLib,
1844
+ type: "lib",
1845
+ description: subLibModule?.description,
1846
+ children: []
1847
+ });
1848
+ }
1849
+ }
1850
+ }
1851
+ node.children.push(libNode);
1852
+ }
1853
+ if (component.registryDependencies) {
1854
+ for (const depName of component.registryDependencies) {
1855
+ const dep = findComponent2(depName, registry);
1856
+ if (dep) {
1857
+ node.children.push(buildTree(dep, registry, visited, depth + 1, maxDepth));
1858
+ }
1859
+ }
1860
+ }
1861
+ for (const npmDep of component.dependencies) {
1862
+ node.children.push({
1863
+ name: npmDep,
1864
+ type: "npm",
1865
+ children: []
1866
+ });
1867
+ }
1868
+ return node;
1869
+ }
1870
+ function renderTree(node, prefix = "", isLast = true, isRoot = true) {
1871
+ const connector = isRoot ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1872
+ const childPrefix = isRoot ? "" : isLast ? " " : "\u2502 ";
1873
+ let icon = "";
1874
+ let color = chalk7.white;
1875
+ switch (node.type) {
1876
+ case "component":
1877
+ icon = "\u{1F4E6}";
1878
+ color = chalk7.green;
1879
+ break;
1880
+ case "lib":
1881
+ icon = "\u{1F527}";
1882
+ color = chalk7.magenta;
1883
+ break;
1884
+ case "npm":
1885
+ icon = "\u{1F4DA}";
1886
+ color = chalk7.yellow;
1887
+ break;
1888
+ }
1889
+ console.log(
1890
+ prefix + connector + icon + " " + color(node.name) + (node.description && isRoot ? chalk7.dim(` - ${node.description}`) : "")
1891
+ );
1892
+ for (let i = 0; i < node.children.length; i++) {
1893
+ const child = node.children[i];
1894
+ const isChildLast = i === node.children.length - 1;
1895
+ renderTree(child, prefix + childPrefix, isChildLast, false);
1896
+ }
1897
+ }
1898
+ function flattenTree(node) {
1899
+ const result = {
1900
+ components: /* @__PURE__ */ new Set(),
1901
+ libs: /* @__PURE__ */ new Set(),
1902
+ npm: /* @__PURE__ */ new Set()
1903
+ };
1904
+ function traverse(n) {
1905
+ switch (n.type) {
1906
+ case "component":
1907
+ result.components.add(n.name);
1908
+ break;
1909
+ case "lib":
1910
+ result.libs.add(n.name);
1911
+ break;
1912
+ case "npm":
1913
+ result.npm.add(n.name);
1914
+ break;
1915
+ }
1916
+ n.children.forEach(traverse);
1917
+ }
1918
+ traverse(node);
1919
+ return {
1920
+ components: Array.from(result.components),
1921
+ libs: Array.from(result.libs),
1922
+ npm: Array.from(result.npm)
1923
+ };
1924
+ }
1925
+ async function tree(componentName, options) {
1926
+ const { json, depth = 2 } = options;
1927
+ const registry = await getRegistry6();
1928
+ const component = findComponent2(componentName, registry);
1929
+ if (!component) {
1930
+ console.log(chalk7.red(`
1931
+ \u2717 Component not found: ${componentName}
1932
+ `));
1933
+ const suggestions = registry.components.filter(
1934
+ (c) => c.name.includes(componentName.toLowerCase()) || c.title.toLowerCase().includes(componentName.toLowerCase())
1935
+ ).slice(0, 5);
1936
+ if (suggestions.length > 0) {
1937
+ console.log(chalk7.yellow("Did you mean one of these?\n"));
1938
+ suggestions.forEach((s) => {
1939
+ console.log(` ${chalk7.green(s.name.padEnd(25))} ${chalk7.dim(s.description)}`);
1940
+ });
1941
+ console.log();
1942
+ }
1943
+ console.log(chalk7.dim('Run "buildpad list" to see all available components.\n'));
1944
+ process.exit(1);
1945
+ }
1946
+ const treeData = buildTree(component, registry, /* @__PURE__ */ new Set(), 0, depth);
1947
+ const flattened = flattenTree(treeData);
1948
+ if (json) {
1949
+ console.log(JSON.stringify({
1950
+ tree: treeData,
1951
+ summary: flattened
1952
+ }, null, 2));
1953
+ return;
1954
+ }
1955
+ console.log(chalk7.bold.blue(`
1956
+ \u{1F333} Dependency Tree: ${component.title}
1957
+ `));
1958
+ renderTree(treeData);
1959
+ console.log(chalk7.bold("\n\u{1F4CA} Summary"));
1960
+ console.log(` ${chalk7.green("\u{1F4E6} Components:")} ${flattened.components.length}`);
1961
+ console.log(` ${chalk7.magenta("\u{1F527} Lib modules:")} ${flattened.libs.length} ${chalk7.dim(`(${flattened.libs.join(", ") || "none"})`)}`);
1962
+ console.log(` ${chalk7.yellow("\u{1F4DA} NPM packages:")} ${flattened.npm.length}`);
1963
+ if (flattened.npm.length > 0) {
1964
+ console.log(chalk7.bold("\n\u{1F4E6} Install NPM Dependencies"));
1965
+ console.log(chalk7.dim(` pnpm add ${flattened.npm.join(" ")}
1966
+ `));
1967
+ }
1968
+ console.log(chalk7.bold("\u{1F4A1} Add this component"));
1969
+ console.log(chalk7.dim(` buildpad add ${component.name}
1970
+ `));
1971
+ }
1972
+
1973
+ // src/commands/fix.ts
1974
+ import fs5 from "fs-extra";
1975
+ import path6 from "path";
1976
+ import chalk8 from "chalk";
1977
+ import ora3 from "ora";
1978
+ import fg2 from "fast-glob";
1979
+ import prompts2 from "prompts";
1980
+ import { execSync as execSync2 } from "child_process";
1981
+ async function fixUntransformedImports(cwd, config, dryRun) {
1982
+ const result = { fixed: 0, skipped: 0, errors: [] };
1983
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
1984
+ const patterns = [
1985
+ path6.join(srcDir, "components/**/*.{ts,tsx,js,jsx}"),
1986
+ path6.join(srcDir, "lib/buildpad/**/*.{ts,tsx,js,jsx}")
1987
+ ];
1988
+ for (const pattern of patterns) {
1989
+ const files = await fg2(pattern, { ignore: ["**/node_modules/**"] });
1990
+ for (const file of files) {
1991
+ const content = await fs5.readFile(file, "utf-8");
1992
+ if (content.includes("from '@buildpad/") || content.includes('from "@buildpad/')) {
1993
+ const transformed = transformImports(content, config);
1994
+ if (transformed !== content) {
1995
+ if (dryRun) {
1996
+ console.log(chalk8.dim(` Would fix: ${path6.relative(cwd, file)}`));
1997
+ } else {
1998
+ await fs5.writeFile(file, transformed);
1999
+ }
2000
+ result.fixed++;
2001
+ }
2002
+ }
2003
+ }
2004
+ }
2005
+ return result;
2006
+ }
2007
+ async function fixSsrUnsafeExports(cwd, config, dryRun) {
2008
+ const result = { fixed: 0, skipped: 0, errors: [] };
2009
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2010
+ const indexPath = path6.join(srcDir, "components/ui/index.ts");
2011
+ if (!fs5.existsSync(indexPath)) {
2012
+ return result;
2013
+ }
2014
+ let content = await fs5.readFile(indexPath, "utf-8");
2015
+ let modified = false;
2016
+ if (content.includes("from './input-block-editor'") && !content.includes("from './input-block-editor-wrapper'")) {
2017
+ const wrapperPath = path6.join(srcDir, "components/ui/input-block-editor-wrapper.tsx");
2018
+ if (fs5.existsSync(wrapperPath)) {
2019
+ content = content.replace(
2020
+ /export \* from ['"]\.\/input-block-editor['"]/g,
2021
+ "export * from './input-block-editor-wrapper'"
2022
+ );
2023
+ modified = true;
2024
+ result.fixed++;
2025
+ }
2026
+ }
2027
+ if (modified && !dryRun) {
2028
+ await fs5.writeFile(indexPath, content);
2029
+ } else if (modified && dryRun) {
2030
+ console.log(chalk8.dim(` Would fix SSR exports in: components/ui/index.ts`));
2031
+ }
2032
+ return result;
2033
+ }
2034
+ async function fixDuplicateExports(cwd, config, dryRun) {
2035
+ const result = { fixed: 0, skipped: 0, errors: [] };
2036
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2037
+ const indexPath = path6.join(srcDir, "components/ui/index.ts");
2038
+ if (!fs5.existsSync(indexPath)) {
2039
+ return result;
2040
+ }
2041
+ const content = await fs5.readFile(indexPath, "utf-8");
2042
+ const componentsDir = path6.join(srcDir, "components/ui");
2043
+ const exportPattern = /export \* from ['"]\.\/([^'"]+)['"]/g;
2044
+ const namedExports = /* @__PURE__ */ new Map();
2045
+ const namedExportPattern = /export\s+(?:const|function|class|type|interface|enum)\s+(\w+)/g;
2046
+ let match;
2047
+ while ((match = exportPattern.exec(content)) !== null) {
2048
+ const exportFile = match[1];
2049
+ const filePath = path6.join(componentsDir, exportFile + ".tsx");
2050
+ if (!fs5.existsSync(filePath)) continue;
2051
+ const fileContent = await fs5.readFile(filePath, "utf-8");
2052
+ let namedMatch;
2053
+ while ((namedMatch = namedExportPattern.exec(fileContent)) !== null) {
2054
+ const exportName = namedMatch[1];
2055
+ if (!namedExports.has(exportName)) {
2056
+ namedExports.set(exportName, { files: [exportFile], count: 1 });
2057
+ } else {
2058
+ const existing = namedExports.get(exportName);
2059
+ existing.files.push(exportFile);
2060
+ existing.count++;
2061
+ }
2062
+ }
2063
+ }
2064
+ const conflicts = Array.from(namedExports.entries()).filter(([_, info2]) => info2.count > 1);
2065
+ if (conflicts.length > 0 && !dryRun) {
2066
+ console.log(chalk8.yellow(`
2067
+ \u26A0 Found ${conflicts.length} conflicting export(s):`));
2068
+ conflicts.forEach(([name, info2]) => {
2069
+ console.log(chalk8.dim(` ${name}: exported from ${info2.files.join(", ")}`));
2070
+ });
2071
+ console.log(chalk8.dim("\nTo fix, manually update components/ui/index.ts to use explicit named exports."));
2072
+ console.log(chalk8.dim('Example: export { ComponentA } from "./file-a";'));
2073
+ result.skipped = conflicts.length;
2074
+ } else if (conflicts.length > 0 && dryRun) {
2075
+ console.log(chalk8.dim(` Would report ${conflicts.length} conflicting exports`));
2076
+ }
2077
+ return result;
2078
+ }
2079
+ async function fixBrokenImports(cwd, config, dryRun) {
2080
+ const result = { fixed: 0, skipped: 0, errors: [] };
2081
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2082
+ const componentsDir = path6.join(srcDir, "components/ui");
2083
+ const files = await fg2(path6.join(componentsDir, "**/*.{ts,tsx}"), {
2084
+ ignore: ["**/node_modules/**"]
2085
+ });
2086
+ for (const file of files) {
2087
+ const content = await fs5.readFile(file, "utf-8");
2088
+ const fileDir = path6.dirname(file);
2089
+ let modified = false;
2090
+ let newContent = content;
2091
+ const relativeImportPattern = /from\s+['"](\.\.?\/[^'"]+)['"]/g;
2092
+ let match;
2093
+ while ((match = relativeImportPattern.exec(content)) !== null) {
2094
+ const importPath = match[1];
2095
+ const absolutePath = path6.resolve(fileDir, importPath);
2096
+ const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", ""];
2097
+ const exists = extensions.some((ext) => fs5.existsSync(absolutePath + ext));
2098
+ if (!exists) {
2099
+ const baseName = path6.basename(importPath);
2100
+ const kebabName = toKebabCase(baseName);
2101
+ const dirName = path6.dirname(importPath);
2102
+ const newImportPath = dirName === "." ? `./${kebabName}` : `${dirName}/${kebabName}`;
2103
+ const newAbsolutePath = path6.resolve(fileDir, newImportPath);
2104
+ const newExists = extensions.some((ext) => fs5.existsSync(newAbsolutePath + ext));
2105
+ if (newExists) {
2106
+ newContent = newContent.replace(
2107
+ new RegExp(`from\\s+['"]${importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"]`),
2108
+ `from '${newImportPath}'`
2109
+ );
2110
+ modified = true;
2111
+ result.fixed++;
2112
+ }
2113
+ }
2114
+ }
2115
+ if (modified && !dryRun) {
2116
+ await fs5.writeFile(file, newContent);
2117
+ } else if (modified && dryRun) {
2118
+ console.log(chalk8.dim(` Would fix imports in: ${path6.relative(cwd, file)}`));
2119
+ }
2120
+ }
2121
+ return result;
2122
+ }
2123
+ async function fixMissingCss(cwd, config, dryRun) {
2124
+ const result = { fixed: 0, skipped: 0, errors: [] };
2125
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2126
+ const componentsDir = path6.join(srcDir, "components/ui");
2127
+ const cssRequirements = {
2128
+ "input-block-editor.tsx": {
2129
+ component: "input-block-editor",
2130
+ cssFile: "InputBlockEditor.css"
2131
+ },
2132
+ "rich-text-html.tsx": {
2133
+ component: "rich-text-html",
2134
+ cssFile: "RichTextHTML.css"
2135
+ },
2136
+ "rich-text-markdown.tsx": {
2137
+ component: "rich-text-markdown",
2138
+ cssFile: "RichTextMarkdown.css"
2139
+ }
2140
+ };
2141
+ for (const [componentFile, info2] of Object.entries(cssRequirements)) {
2142
+ const componentPath = path6.join(componentsDir, componentFile);
2143
+ const cssPath = path6.join(componentsDir, info2.cssFile);
2144
+ if (fs5.existsSync(componentPath) && !fs5.existsSync(cssPath)) {
2145
+ if (dryRun) {
2146
+ console.log(chalk8.dim(` Would copy missing CSS: ${info2.cssFile}`));
2147
+ result.fixed++;
2148
+ } else {
2149
+ console.log(chalk8.yellow(` Missing CSS: ${info2.cssFile}`));
2150
+ console.log(chalk8.dim(` Reinstall component: npx buildpad add ${info2.component} --overwrite`));
2151
+ result.skipped++;
2152
+ }
2153
+ }
2154
+ }
2155
+ return result;
2156
+ }
2157
+ var KNOWN_NPM_PACKAGES = /* @__PURE__ */ new Set([
2158
+ // Mantine ecosystem
2159
+ "@mantine/core",
2160
+ "@mantine/hooks",
2161
+ "@mantine/dates",
2162
+ "@mantine/notifications",
2163
+ "@mantine/tiptap",
2164
+ "@mantine/form",
2165
+ "@mantine/dropzone",
2166
+ "@mantine/modals",
2167
+ // Icons
2168
+ "@tabler/icons-react",
2169
+ // TipTap rich text
2170
+ "@tiptap/react",
2171
+ "@tiptap/starter-kit",
2172
+ "@tiptap/extension-highlight",
2173
+ "@tiptap/extension-text-align",
2174
+ "@tiptap/extension-superscript",
2175
+ "@tiptap/extension-subscript",
2176
+ "@tiptap/extension-placeholder",
2177
+ "@tiptap/extension-color",
2178
+ "@tiptap/extension-text-style",
2179
+ "@tiptap/extension-underline",
2180
+ "@tiptap/extension-link",
2181
+ "@tiptap/extension-code-block-lowlight",
2182
+ // EditorJS block editor
2183
+ "@editorjs/editorjs",
2184
+ "@editorjs/header",
2185
+ "@editorjs/nested-list",
2186
+ "@editorjs/paragraph",
2187
+ "@editorjs/code",
2188
+ "@editorjs/quote",
2189
+ "@editorjs/checklist",
2190
+ "@editorjs/delimiter",
2191
+ "@editorjs/table",
2192
+ "@editorjs/underline",
2193
+ "@editorjs/inline-code",
2194
+ // Utilities
2195
+ "lowlight",
2196
+ "highlight.js",
2197
+ "dayjs",
2198
+ "axios",
2199
+ // Maps
2200
+ "maplibre-gl",
2201
+ "@mapbox/mapbox-gl-draw",
2202
+ // Supabase auth
2203
+ "@supabase/ssr",
2204
+ "@supabase/supabase-js",
2205
+ // React / Next.js (framework — usually present)
2206
+ "react",
2207
+ "react-dom",
2208
+ "next",
2209
+ // Styling utility
2210
+ "clsx",
2211
+ "tailwind-merge"
2212
+ ]);
2213
+ function extractPackageName(moduleSpec) {
2214
+ if (moduleSpec.startsWith("@")) {
2215
+ const parts = moduleSpec.split("/");
2216
+ return parts.slice(0, 2).join("/");
2217
+ }
2218
+ return moduleSpec.split("/")[0];
2219
+ }
2220
+ function collectTypeScriptErrors(cwd, config) {
2221
+ const tsconfigPath = path6.join(cwd, "tsconfig.json");
2222
+ if (!fs5.existsSync(tsconfigPath)) return [];
2223
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2224
+ const componentsDir = path6.join(srcDir, "components/ui");
2225
+ if (!fs5.existsSync(componentsDir)) return [];
2226
+ try {
2227
+ const output = execSync2(
2228
+ `npx --yes tsc --noEmit --skipLibCheck --pretty false 2>&1 || true`,
2229
+ { cwd, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024, timeout: 12e4, stdio: ["pipe", "pipe", "pipe"] }
2230
+ );
2231
+ const errors = [];
2232
+ const pattern = /^(.+?)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.+)$/gm;
2233
+ let match;
2234
+ while ((match = pattern.exec(output)) !== null) {
2235
+ const [, file, line, col, code, message] = match;
2236
+ const rel = path6.relative(cwd, file);
2237
+ if (rel.includes("components/ui") || rel.includes("lib/buildpad")) {
2238
+ errors.push({ file, line: parseInt(line, 10), col: parseInt(col, 10), code, message });
2239
+ }
2240
+ }
2241
+ return errors;
2242
+ } catch {
2243
+ return [];
2244
+ }
2245
+ }
2246
+ async function fixTypeScriptErrors(cwd, config, dryRun, spinner) {
2247
+ const result = { fixed: 0, skipped: 0, errors: [] };
2248
+ if (spinner) spinner.text = "Running TypeScript check...";
2249
+ const tsErrors = collectTypeScriptErrors(cwd, config);
2250
+ if (tsErrors.length === 0) return result;
2251
+ const missingModules = /* @__PURE__ */ new Set();
2252
+ const missingNpmPackages = /* @__PURE__ */ new Set();
2253
+ const undeclaredModules = /* @__PURE__ */ new Set();
2254
+ const suppressTargets = [];
2255
+ for (const err of tsErrors) {
2256
+ if (err.code === "TS2307" || err.code === "TS7016") {
2257
+ const moduleMatch = err.message.match(/(?:Cannot find module|Could not find a declaration file for module)\s+'([^']+)'/);
2258
+ if (moduleMatch) {
2259
+ const moduleSpec = moduleMatch[1];
2260
+ if (moduleSpec.startsWith(".")) continue;
2261
+ if (moduleSpec.startsWith("@buildpad/")) continue;
2262
+ const pkgName = extractPackageName(moduleSpec);
2263
+ missingModules.add(moduleSpec);
2264
+ if (KNOWN_NPM_PACKAGES.has(pkgName)) {
2265
+ missingNpmPackages.add(pkgName);
2266
+ } else {
2267
+ undeclaredModules.add(moduleSpec);
2268
+ }
2269
+ }
2270
+ } else {
2271
+ suppressTargets.push({ file: err.file, line: err.line, message: err.message });
2272
+ }
2273
+ }
2274
+ if (missingNpmPackages.size > 0) {
2275
+ const packageJsonPath = path6.join(cwd, "package.json");
2276
+ let installedDeps = {};
2277
+ if (fs5.existsSync(packageJsonPath)) {
2278
+ const pkg = await fs5.readJSON(packageJsonPath);
2279
+ installedDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2280
+ }
2281
+ const toInstall = Array.from(missingNpmPackages).filter((p) => !installedDeps[p]);
2282
+ if (toInstall.length > 0) {
2283
+ if (dryRun) {
2284
+ console.log(chalk8.dim(` Would install ${toInstall.length} missing package(s): ${toInstall.join(", ")}`));
2285
+ result.fixed += toInstall.length;
2286
+ } else {
2287
+ if (spinner) spinner.text = `Installing ${toInstall.length} missing package(s)...`;
2288
+ const hasPnpmLock = fs5.existsSync(path6.join(cwd, "pnpm-lock.yaml"));
2289
+ const hasYarnLock = fs5.existsSync(path6.join(cwd, "yarn.lock"));
2290
+ const hasBunLock = fs5.existsSync(path6.join(cwd, "bun.lockb"));
2291
+ let installCmd;
2292
+ if (hasPnpmLock || !hasYarnLock && !hasBunLock) {
2293
+ installCmd = `pnpm add ${toInstall.join(" ")}`;
2294
+ } else if (hasYarnLock) {
2295
+ installCmd = `yarn add ${toInstall.join(" ")}`;
2296
+ } else {
2297
+ installCmd = `bun add ${toInstall.join(" ")}`;
2298
+ }
2299
+ try {
2300
+ execSync2(installCmd, { cwd, stdio: "pipe", timeout: 12e4 });
2301
+ result.fixed += toInstall.length;
2302
+ if (spinner) spinner.text = "Packages installed";
2303
+ } catch {
2304
+ result.errors.push(`Failed to install: ${toInstall.join(", ")}`);
2305
+ result.skipped += toInstall.length;
2306
+ }
2307
+ }
2308
+ }
2309
+ }
2310
+ if (undeclaredModules.size > 0) {
2311
+ const srcDir = config.srcDir ? path6.join(cwd, "src") : cwd;
2312
+ const declFile = path6.join(srcDir, "types", "modules.d.ts");
2313
+ let existingContent = "";
2314
+ if (fs5.existsSync(declFile)) {
2315
+ existingContent = await fs5.readFile(declFile, "utf-8");
2316
+ }
2317
+ const newDeclarations = [];
2318
+ for (const mod of undeclaredModules) {
2319
+ const pkgName = extractPackageName(mod);
2320
+ if (!existingContent.includes(`'${mod}'`) && !existingContent.includes(`'${pkgName}'`)) {
2321
+ const declTarget = mod.includes("/") && mod !== pkgName ? pkgName : mod;
2322
+ if (!newDeclarations.some((d) => d.includes(`'${declTarget}'`))) {
2323
+ newDeclarations.push(`declare module '${declTarget}';`);
2324
+ }
2325
+ }
2326
+ }
2327
+ if (newDeclarations.length > 0) {
2328
+ if (dryRun) {
2329
+ console.log(chalk8.dim(` Would add ${newDeclarations.length} module declaration(s) to types/modules.d.ts`));
2330
+ result.fixed += newDeclarations.length;
2331
+ } else {
2332
+ await fs5.ensureDir(path6.dirname(declFile));
2333
+ const separator = existingContent && !existingContent.endsWith("\n") ? "\n" : "";
2334
+ const header = existingContent ? "" : "// Auto-generated module declarations for untyped packages\n";
2335
+ const content = existingContent + separator + header + newDeclarations.join("\n") + "\n";
2336
+ await fs5.writeFile(declFile, content);
2337
+ result.fixed += newDeclarations.length;
2338
+ }
2339
+ }
2340
+ }
2341
+ if (suppressTargets.length > 0) {
2342
+ const byFile = /* @__PURE__ */ new Map();
2343
+ for (const t of suppressTargets) {
2344
+ const arr = byFile.get(t.file) || [];
2345
+ arr.push({ line: t.line, message: t.message });
2346
+ byFile.set(t.file, arr);
2347
+ }
2348
+ for (const [filePath, targets] of byFile) {
2349
+ if (dryRun) {
2350
+ console.log(chalk8.dim(` Would suppress ${targets.length} error(s) in ${path6.relative(cwd, filePath)}`));
2351
+ result.fixed += targets.length;
2352
+ continue;
2353
+ }
2354
+ try {
2355
+ const content = await fs5.readFile(filePath, "utf-8");
2356
+ const lines = content.split("\n");
2357
+ const sorted = [...targets].sort((a, b) => b.line - a.line);
2358
+ const alreadySuppressed = /* @__PURE__ */ new Set();
2359
+ for (const { line, message } of sorted) {
2360
+ const idx = line - 1;
2361
+ if (idx < 0 || idx >= lines.length) continue;
2362
+ if (alreadySuppressed.has(line)) continue;
2363
+ if (idx > 0 && (lines[idx - 1].includes("@ts-expect-error") || lines[idx - 1].includes("@ts-ignore"))) {
2364
+ alreadySuppressed.add(line);
2365
+ continue;
2366
+ }
2367
+ const indent = lines[idx].match(/^(\s*)/)?.[1] || "";
2368
+ const shortMsg = message.length > 80 ? message.slice(0, 77) + "..." : message;
2369
+ lines.splice(idx, 0, `${indent}// @ts-expect-error \u2014 auto-suppressed: ${shortMsg}`);
2370
+ result.fixed++;
2371
+ alreadySuppressed.add(line);
2372
+ }
2373
+ await fs5.writeFile(filePath, lines.join("\n"));
2374
+ } catch {
2375
+ result.skipped += targets.length;
2376
+ }
2377
+ }
2378
+ }
2379
+ return result;
2380
+ }
2381
+ async function fix(options) {
2382
+ const { cwd, dryRun = false, yes = false } = options;
2383
+ console.log(chalk8.bold("\n\u{1F527} Buildpad Fix\n"));
2384
+ if (dryRun) {
2385
+ console.log(chalk8.yellow("Dry run mode - no changes will be made\n"));
2386
+ }
2387
+ const config = await loadConfig(cwd);
2388
+ if (!config) {
2389
+ console.log(chalk8.red('\u2717 buildpad.json not found. Run "npx buildpad init" first.\n'));
2390
+ process.exit(1);
2391
+ }
2392
+ if (!yes && !dryRun) {
2393
+ const { confirm } = await prompts2({
2394
+ type: "confirm",
2395
+ name: "confirm",
2396
+ message: "This will modify files in your project. Continue?",
2397
+ initial: true
2398
+ });
2399
+ if (!confirm) {
2400
+ console.log(chalk8.yellow("\n\u2713 Cancelled\n"));
2401
+ return;
2402
+ }
2403
+ }
2404
+ const spinner = ora3("Scanning for issues...").start();
2405
+ try {
2406
+ spinner.text = "Fixing untransformed imports...";
2407
+ const importResult = await fixUntransformedImports(cwd, config, dryRun);
2408
+ spinner.text = "Fixing broken relative imports...";
2409
+ const brokenResult = await fixBrokenImports(cwd, config, dryRun);
2410
+ spinner.text = "Fixing SSR-unsafe exports...";
2411
+ const ssrResult = await fixSsrUnsafeExports(cwd, config, dryRun);
2412
+ spinner.text = "Checking duplicate exports...";
2413
+ const duplicateResult = await fixDuplicateExports(cwd, config, dryRun);
2414
+ spinner.text = "Checking missing CSS files...";
2415
+ const cssResult = await fixMissingCss(cwd, config, dryRun);
2416
+ const tsResult = await fixTypeScriptErrors(cwd, config, dryRun, spinner);
2417
+ spinner.stop();
2418
+ const totalFixed = importResult.fixed + brokenResult.fixed + ssrResult.fixed + cssResult.fixed + tsResult.fixed;
2419
+ const totalSkipped = importResult.skipped + brokenResult.skipped + ssrResult.skipped + duplicateResult.skipped + cssResult.skipped + tsResult.skipped;
2420
+ console.log(chalk8.bold("\n\u{1F4CB} Fix Summary:\n"));
2421
+ if (importResult.fixed > 0) {
2422
+ console.log(chalk8.green(` \u2713 Fixed ${importResult.fixed} untransformed import(s)`));
2423
+ }
2424
+ if (brokenResult.fixed > 0) {
2425
+ console.log(chalk8.green(` \u2713 Fixed ${brokenResult.fixed} broken import(s)`));
2426
+ }
2427
+ if (ssrResult.fixed > 0) {
2428
+ console.log(chalk8.green(` \u2713 Fixed ${ssrResult.fixed} SSR-unsafe export(s)`));
2429
+ }
2430
+ if (cssResult.fixed > 0) {
2431
+ console.log(chalk8.green(` \u2713 Fixed ${cssResult.fixed} missing CSS file(s)`));
2432
+ }
2433
+ if (tsResult.fixed > 0) {
2434
+ console.log(chalk8.green(` \u2713 Fixed ${tsResult.fixed} TypeScript error(s)`));
2435
+ }
2436
+ if (tsResult.errors.length > 0) {
2437
+ tsResult.errors.forEach((e) => console.log(chalk8.yellow(` \u26A0 ${e}`)));
2438
+ }
2439
+ if (totalSkipped > 0) {
2440
+ console.log(chalk8.yellow(`
2441
+ \u26A0 Skipped ${totalSkipped} issue(s) requiring manual fix`));
2442
+ }
2443
+ if (totalFixed === 0 && totalSkipped === 0) {
2444
+ console.log(chalk8.green(" \u2713 No issues found!\n"));
2445
+ } else if (dryRun) {
2446
+ console.log(chalk8.dim("\n Run without --dry-run to apply fixes\n"));
2447
+ } else {
2448
+ console.log(chalk8.green("\n\u2728 Fixes applied!\n"));
2449
+ console.log(chalk8.dim('Run "npx buildpad validate" to verify.\n'));
2450
+ }
2451
+ } catch (error) {
2452
+ spinner.fail("Fix failed");
2453
+ console.error(chalk8.red(error));
2454
+ process.exit(1);
2455
+ }
2456
+ }
2457
+
2458
+ // src/commands/bootstrap.ts
2459
+ import fs6 from "fs-extra";
2460
+ import path7 from "path";
2461
+ import chalk9 from "chalk";
2462
+ async function bootstrap(options) {
2463
+ const { cwd, skipDeps = false, skipValidate = false } = options;
2464
+ const startTime = Date.now();
2465
+ console.log(chalk9.bold.blue("\n\u{1F680} Buildpad Bootstrap - Full Project Setup\n"));
2466
+ console.log(chalk9.dim(`Target: ${cwd}
2467
+ `));
2468
+ console.log(chalk9.bold("\u2501".repeat(60)));
2469
+ console.log(chalk9.bold("Step 1/3: Initializing project...\n"));
2470
+ try {
2471
+ await init({ yes: true, cwd });
2472
+ } catch (error) {
2473
+ console.error(chalk9.red("\n\u2717 Init failed:"), error);
2474
+ process.exit(1);
2475
+ }
2476
+ console.log(chalk9.bold("\n" + "\u2501".repeat(60)));
2477
+ console.log(chalk9.bold("Step 2/3: Adding all components...\n"));
2478
+ try {
2479
+ await add([], {
2480
+ all: true,
2481
+ withApi: true,
2482
+ cwd,
2483
+ nonInteractive: true
2484
+ });
2485
+ } catch (error) {
2486
+ console.error(chalk9.red("\n\u2717 Component installation failed:"), error);
2487
+ process.exit(1);
2488
+ }
2489
+ if (!skipDeps) {
2490
+ console.log(chalk9.bold("\n" + "\u2501".repeat(60)));
2491
+ console.log(chalk9.bold("Step 3/3: Installing npm dependencies...\n"));
2492
+ try {
2493
+ const { execSync: execSync3 } = await import("child_process");
2494
+ const hasPnpmLock = fs6.existsSync(path7.join(cwd, "pnpm-lock.yaml"));
2495
+ const hasYarnLock = fs6.existsSync(path7.join(cwd, "yarn.lock"));
2496
+ const hasBunLock = fs6.existsSync(path7.join(cwd, "bun.lockb"));
2497
+ let installCmd;
2498
+ if (hasPnpmLock || !hasYarnLock && !hasBunLock) {
2499
+ installCmd = "pnpm install";
2500
+ } else if (hasYarnLock) {
2501
+ installCmd = "yarn install";
2502
+ } else {
2503
+ installCmd = "bun install";
2504
+ }
2505
+ console.log(chalk9.dim(` Running: ${installCmd}
2506
+ `));
2507
+ execSync3(installCmd, { cwd, stdio: "inherit" });
2508
+ console.log(chalk9.green("\n\u2713 Dependencies installed!"));
2509
+ } catch (error) {
2510
+ console.log(chalk9.yellow('\n\u26A0 Dependency installation had issues. Run "pnpm install" manually.'));
2511
+ }
2512
+ } else {
2513
+ console.log(chalk9.dim("\nSkipped dependency installation (--skip-deps)\n"));
2514
+ }
2515
+ if (!skipValidate) {
2516
+ console.log(chalk9.bold("\n" + "\u2501".repeat(60)));
2517
+ console.log(chalk9.bold("Validating installation...\n"));
2518
+ try {
2519
+ const result = await validate({ cwd, json: false, noExit: true });
2520
+ if (result && !result.valid) {
2521
+ const tsErrors = result.errors.filter((e) => e.code === "TYPESCRIPT_ERROR");
2522
+ const structuralErrors = result.errors.filter((e) => e.code !== "TYPESCRIPT_ERROR");
2523
+ if (tsErrors.length > 0) {
2524
+ console.log(chalk9.yellow(`
2525
+ \u26A0 ${tsErrors.length} TypeScript type error(s) detected in component files.`));
2526
+ console.log(chalk9.yellow(' These may cause build failures. Run "pnpm cli fix --cwd ." to attempt auto-fix,'));
2527
+ console.log(chalk9.yellow(" or ensure all component dependencies are installed.\n"));
2528
+ }
2529
+ if (structuralErrors.length > 0) {
2530
+ console.log(chalk9.red(`
2531
+ \u2717 ${structuralErrors.length} structural error(s) found that need fixing.`));
2532
+ }
2533
+ }
2534
+ } catch {
2535
+ console.log(chalk9.yellow("\n\u26A0 Validation encountered issues (see above). Bootstrap still completed.\n"));
2536
+ }
2537
+ }
2538
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
2539
+ console.log(chalk9.bold("\n" + "\u2501".repeat(60)));
2540
+ console.log(chalk9.bold.green(`
2541
+ \u2728 Bootstrap complete! (${elapsed}s)
2542
+ `));
2543
+ console.log(chalk9.dim("Your project is ready with:"));
2544
+ console.log(chalk9.dim(" \u2022 buildpad.json configuration"));
2545
+ console.log(chalk9.dim(" \u2022 40+ UI components in components/ui/"));
2546
+ console.log(chalk9.dim(" \u2022 Types, services, hooks in lib/buildpad/"));
2547
+ console.log(chalk9.dim(" \u2022 API proxy routes in app/api/"));
2548
+ console.log(chalk9.dim(" \u2022 Supabase auth utilities"));
2549
+ console.log(chalk9.dim(" \u2022 Next.js skeleton (layout, page, config)"));
2550
+ console.log(chalk9.bold("\nNext steps:"));
2551
+ console.log(chalk9.cyan(" 1. ") + chalk9.dim("Configure .env.local with your DaaS URL"));
2552
+ console.log(chalk9.cyan(" 2. ") + chalk9.dim("pnpm dev"));
2553
+ console.log(chalk9.cyan(" 3. ") + chalk9.dim("Create pages that import from @/components/ui/\n"));
2554
+ }
2555
+
2556
+ // src/index.ts
2557
+ var program = new Command();
2558
+ program.name("buildpad").description("Copy & Own CLI - Add Buildpad components to your project").version("1.0.0");
2559
+ program.command("init").description("Initialize Buildpad in your project (creates buildpad.json)").option("-y, --yes", "Skip prompts and use defaults").option("-c, --cwd <path>", "Project directory", process.cwd()).action(init);
2560
+ program.command("add").description("Copy components to your project (with transformed imports)").argument("[components...]", "Component names to add").option("-a, --all", "Add all components").option("--with-api", "Also add API routes and Supabase auth templates").option("--category <name>", "Add all components from a category").option("-o, --overwrite", "Overwrite existing components").option("-n, --dry-run", "Preview changes without modifying files").option("--cwd <path>", "Project directory", process.cwd()).action(add);
2561
+ program.command("list").description("List all available components").option("--category <name>", "Filter by category").option("--json", "Output as JSON").option("--cwd <path>", "Project directory", process.cwd()).action(list);
2562
+ program.command("diff").description("Preview changes before adding a component").argument("<component>", "Component name").option("--cwd <path>", "Project directory", process.cwd()).action(diff);
2563
+ program.command("status").description("Show installed Buildpad components and their origins").option("--json", "Output as JSON").option("--cwd <path>", "Project directory", process.cwd()).action(status);
2564
+ program.command("info").description("Show detailed information about a component (sources, dependencies, interface)").argument("<component>", "Component name").option("--json", "Output as JSON").action(info);
2565
+ program.command("tree").description("Display dependency tree for a component").argument("<component>", "Component name").option("--json", "Output as JSON").option("-d, --depth <number>", "Max depth to display", "2").action((component, options) => tree(component, { ...options, depth: parseInt(options.depth) }));
2566
+ program.command("validate").description("Validate Buildpad installation (check imports, missing files, SSR issues)").option("--json", "Output as JSON").option("--cwd <path>", "Project directory", process.cwd()).action(async (options) => {
2567
+ await validate(options);
2568
+ });
2569
+ program.command("bootstrap").description("Full project setup: init + add --all + install deps + validate (single command for AI agents)").option("--cwd <path>", "Project directory", process.cwd()).option("--skip-deps", "Skip npm dependency installation").option("--skip-validate", "Skip post-install validation").action(async (options) => {
2570
+ await bootstrap({
2571
+ cwd: options.cwd,
2572
+ skipDeps: options.skipDeps,
2573
+ skipValidate: options.skipValidate
2574
+ });
2575
+ });
2576
+ program.command("fix").description("Automatically fix common issues (untransformed imports, broken paths, SSR exports)").option("-n, --dry-run", "Preview fixes without modifying files").option("-y, --yes", "Skip confirmation prompts").option("--cwd <path>", "Project directory", process.cwd()).action(fix);
2577
+ program.command("outdated").description("Check for component updates (compares installed versions to registry)").option("--json", "Output as JSON").option("--cwd <path>", "Project directory", process.cwd()).action(async (options) => {
2578
+ const { outdated } = await import("./outdated-JMAYAZ7W.js");
2579
+ await outdated(options);
2580
+ });
2581
+ program.parse();
2582
+ //# sourceMappingURL=index.js.map