magicpath-ai 1.3.0-beta.13 ā 1.3.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.js +354 -239
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/auth.js +10 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +154 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/info.js +4 -3
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/list-installed.d.ts +2 -0
- package/dist/commands/list-installed.js +112 -0
- package/dist/commands/list-installed.js.map +1 -0
- package/dist/commands/whoami.js +5 -13
- package/dist/commands/whoami.js.map +1 -1
- package/dist/util/auth.d.ts +19 -6
- package/dist/util/auth.js +67 -10
- package/dist/util/auth.js.map +1 -1
- package/dist/util/banner.d.ts +1 -0
- package/dist/util/banner.js +119 -0
- package/dist/util/banner.js.map +1 -0
- package/package.json +2 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.js +0 -113
- package/dist/commands/init.js.map +0 -1
package/dist/commands/add.js
CHANGED
|
@@ -31,14 +31,14 @@ export function registerAddCommand(program) {
|
|
|
31
31
|
program
|
|
32
32
|
.command('add')
|
|
33
33
|
.description('Add a MagicPath component to your project')
|
|
34
|
-
.argument('<
|
|
34
|
+
.argument('<generatedNames...>', 'One or more generated names of components (e.g., wispy-river-5234 bold-moon-2031)')
|
|
35
35
|
.option('-y, --yes', 'Skip confirmation prompts', false)
|
|
36
36
|
.option('--overwrite', 'Overwrite existing files', false)
|
|
37
37
|
.option('-p, --path <path>', `Custom path for components (default: ${DEFAULT_COMPONENTS_PATH})`)
|
|
38
38
|
.option('-d, --debug', 'Enable debug logging', false)
|
|
39
39
|
.option('--dry-run', 'Show what would happen without writing files', false)
|
|
40
40
|
.option('--inspect', 'Show component source code without installing (implies --dry-run)', false)
|
|
41
|
-
.action(async (
|
|
41
|
+
.action(async (generatedNames, options) => {
|
|
42
42
|
try {
|
|
43
43
|
let addOptions;
|
|
44
44
|
try {
|
|
@@ -58,7 +58,12 @@ export function registerAddCommand(program) {
|
|
|
58
58
|
const json = isJsonMode();
|
|
59
59
|
if (json)
|
|
60
60
|
addOptions.yes = true;
|
|
61
|
-
|
|
61
|
+
const isBatch = generatedNames.length > 1;
|
|
62
|
+
// For batch mode, force --yes to avoid repetitive prompts
|
|
63
|
+
if (isBatch) {
|
|
64
|
+
addOptions.yes = true;
|
|
65
|
+
}
|
|
66
|
+
logger.debug({ generatedNames, options: addOptions });
|
|
62
67
|
const cwd = process.cwd();
|
|
63
68
|
// Check if we're in a valid project directory
|
|
64
69
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
@@ -68,271 +73,381 @@ export function registerAddCommand(program) {
|
|
|
68
73
|
suggestion: 'Run this command from the root of your project where package.json is located.',
|
|
69
74
|
});
|
|
70
75
|
}
|
|
71
|
-
//
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
let componentData;
|
|
76
|
-
try {
|
|
77
|
-
componentData = await fetchComponent(generatedName);
|
|
78
|
-
fetchSpinner?.succeed(`Found component: ${componentData.name}`);
|
|
76
|
+
// Batch results collector
|
|
77
|
+
const batchResults = [];
|
|
78
|
+
if (isBatch && !json) {
|
|
79
|
+
console.log(`\n Adding ${generatedNames.length} components...\n`);
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
for (const generatedName of generatedNames) {
|
|
82
|
+
try {
|
|
83
|
+
// Step 1: Fetch component from registry
|
|
84
|
+
const fetchSpinner = json
|
|
85
|
+
? null
|
|
86
|
+
: brandSpinner(`Fetching component "${generatedName}"...`).start();
|
|
87
|
+
let componentData;
|
|
88
|
+
try {
|
|
89
|
+
componentData = await fetchComponent(generatedName);
|
|
90
|
+
fetchSpinner?.succeed(`Found component: ${componentData.name}`);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof AuthRequiredError) {
|
|
94
|
+
fetchSpinner?.fail('Not logged in');
|
|
95
|
+
if (json || isBatch) {
|
|
96
|
+
throw new MagicPathError('Not authenticated. Set MAGICPATH_TOKEN or run `magicpath-ai login`.', {
|
|
97
|
+
code: 'NOT_AUTHENTICATED',
|
|
98
|
+
suggestion: 'Run `magicpath-ai login` or set the MAGICPATH_TOKEN environment variable.',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const { shouldLogin } = await prompts({
|
|
102
|
+
type: 'confirm',
|
|
103
|
+
name: 'shouldLogin',
|
|
104
|
+
message: 'You need to be logged in to install components. Log in now?',
|
|
105
|
+
initial: true,
|
|
106
|
+
});
|
|
107
|
+
if (!shouldLogin) {
|
|
108
|
+
console.log('Installation cancelled.');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const loggedIn = await runLoginFlow();
|
|
112
|
+
if (!loggedIn) {
|
|
113
|
+
console.log('Installation cancelled.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Retry fetch after login
|
|
117
|
+
const retrySpinner = brandSpinner(`Fetching component "${generatedName}"...`).start();
|
|
118
|
+
try {
|
|
119
|
+
componentData = await fetchComponent(generatedName);
|
|
120
|
+
retrySpinner.succeed(`Found component: ${componentData.name}`);
|
|
121
|
+
}
|
|
122
|
+
catch (retryErr) {
|
|
123
|
+
retrySpinner.fail('Failed to fetch component');
|
|
124
|
+
if (retryErr instanceof MagicPathError) {
|
|
125
|
+
throw retryErr;
|
|
126
|
+
}
|
|
127
|
+
throw new MagicPathError('Something went wrong!');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
fetchSpinner?.fail('Failed to fetch component');
|
|
132
|
+
if (isBatch) {
|
|
133
|
+
const errMsg = err instanceof MagicPathError ? err.message : 'Something went wrong!';
|
|
134
|
+
batchResults.push({
|
|
135
|
+
component: generatedName,
|
|
136
|
+
generatedName,
|
|
137
|
+
filesWritten: [],
|
|
138
|
+
dependenciesInstalled: [],
|
|
139
|
+
dryRun: addOptions.dryRun,
|
|
140
|
+
error: errMsg,
|
|
141
|
+
});
|
|
142
|
+
if (!json)
|
|
143
|
+
console.log(` ā ļø Skipping "${generatedName}": ${errMsg}`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (err instanceof MagicPathError) {
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
throw new MagicPathError('Something went wrong!');
|
|
150
|
+
}
|
|
88
151
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
152
|
+
// Resolve paths - include component-specific subfolder
|
|
153
|
+
const basePath = resolveComponentsPath(addOptions.path, cwd);
|
|
154
|
+
const componentFolderName = sanitizeComponentFolderName(componentData.name);
|
|
155
|
+
const componentsPath = path.join(basePath, componentFolderName);
|
|
156
|
+
const utilsPath = resolveUtilsPath(cwd);
|
|
157
|
+
logger.debug({
|
|
158
|
+
basePath,
|
|
159
|
+
componentFolderName,
|
|
160
|
+
componentsPath,
|
|
161
|
+
utilsPath,
|
|
94
162
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
163
|
+
// Step 2: Check for existing files
|
|
164
|
+
const existingFiles = checkExistingFiles(componentData.files, componentsPath);
|
|
165
|
+
if (existingFiles.length > 0 && !addOptions.overwrite) {
|
|
166
|
+
if (!json) {
|
|
167
|
+
console.log('\nThe following files already exist:');
|
|
168
|
+
existingFiles.forEach((file) => console.log(` - ${file}`));
|
|
169
|
+
}
|
|
170
|
+
if (!addOptions.yes) {
|
|
171
|
+
const { shouldOverwrite } = await prompts({
|
|
172
|
+
type: 'confirm',
|
|
173
|
+
name: 'shouldOverwrite',
|
|
174
|
+
message: 'Do you want to overwrite these files?',
|
|
175
|
+
initial: false,
|
|
176
|
+
});
|
|
177
|
+
if (!shouldOverwrite) {
|
|
178
|
+
console.log('Installation cancelled.');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
if (isBatch) {
|
|
184
|
+
if (!json)
|
|
185
|
+
console.log(` ā ļø Skipping "${componentData.name}": files already exist. Use --overwrite.`);
|
|
186
|
+
batchResults.push({
|
|
187
|
+
component: componentData.name,
|
|
188
|
+
generatedName: componentData.generatedName,
|
|
189
|
+
filesWritten: [],
|
|
190
|
+
dependenciesInstalled: [],
|
|
191
|
+
dryRun: addOptions.dryRun,
|
|
192
|
+
error: 'Files already exist. Use --overwrite to replace.',
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (json) {
|
|
197
|
+
throw new MagicPathError(`Files already exist: ${existingFiles.join(', ')}. Use --overwrite to replace.`, {
|
|
198
|
+
code: 'FILES_EXIST',
|
|
199
|
+
suggestion: 'Pass `--overwrite` to replace existing files.',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
console.log('Use --overwrite to replace existing files.');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
98
205
|
}
|
|
99
|
-
|
|
100
|
-
if (!
|
|
101
|
-
console.log(
|
|
102
|
-
|
|
206
|
+
// Step 3: Confirm installation (unless --yes flag)
|
|
207
|
+
if (!addOptions.yes && existingFiles.length === 0) {
|
|
208
|
+
console.log(`\nThis will install:`);
|
|
209
|
+
console.log(` Component: ${componentData.name}`);
|
|
210
|
+
console.log(` Files: ${componentData.files.length} file(s) to ${path.relative(cwd, componentsPath) || '.'}`);
|
|
211
|
+
console.log(` Dependencies: ${componentData.dependencies.length} package(s)`);
|
|
212
|
+
const { shouldProceed } = await prompts({
|
|
213
|
+
type: 'confirm',
|
|
214
|
+
name: 'shouldProceed',
|
|
215
|
+
message: 'Proceed with installation?',
|
|
216
|
+
initial: true,
|
|
217
|
+
});
|
|
218
|
+
if (!shouldProceed) {
|
|
219
|
+
console.log('Installation cancelled.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
103
222
|
}
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
223
|
+
// Build result data for JSON output
|
|
224
|
+
const filesWritten = componentData.files.map((f) => path.relative(cwd, path.join(componentsPath, f.path)));
|
|
225
|
+
// Extract import info
|
|
226
|
+
const mainFile = componentData.files[0];
|
|
227
|
+
let importStatement;
|
|
228
|
+
let usageStr;
|
|
229
|
+
if (mainFile) {
|
|
230
|
+
const fileName = mainFile.name.replace(/\.tsx?$/, '');
|
|
231
|
+
const importPath = addOptions.path
|
|
232
|
+
? `@/${addOptions.path}/${componentFolderName}/${fileName}`
|
|
233
|
+
: `@/components/magicpath/${componentFolderName}/${fileName}`;
|
|
234
|
+
const exportInfo = extractComponentExports(mainFile.content);
|
|
235
|
+
if (exportInfo) {
|
|
236
|
+
importStatement = generateImportStatement(exportInfo, importPath);
|
|
237
|
+
usageStr = generateUsageExample(exportInfo);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
importStatement = `import { ${fileName} } from '${importPath}';`;
|
|
241
|
+
}
|
|
109
242
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
243
|
+
// Dry run: return what would happen without writing
|
|
244
|
+
if (addOptions.dryRun) {
|
|
245
|
+
const fileContents = addOptions.inspect || json
|
|
246
|
+
? componentData.files.map((f) => ({
|
|
247
|
+
path: f.path,
|
|
248
|
+
name: f.name,
|
|
249
|
+
content: f.content,
|
|
250
|
+
}))
|
|
251
|
+
: undefined;
|
|
252
|
+
if (!isBatch && json) {
|
|
253
|
+
jsonResult({
|
|
254
|
+
component: componentData.name,
|
|
255
|
+
generatedName: componentData.generatedName,
|
|
256
|
+
filesWritten,
|
|
257
|
+
files: fileContents,
|
|
258
|
+
dependenciesInstalled: componentData.dependencies,
|
|
259
|
+
importStatement,
|
|
260
|
+
usage: usageStr,
|
|
261
|
+
dryRun: true,
|
|
262
|
+
});
|
|
114
263
|
}
|
|
115
|
-
|
|
264
|
+
batchResults.push({
|
|
265
|
+
component: componentData.name,
|
|
266
|
+
generatedName: componentData.generatedName,
|
|
267
|
+
filesWritten,
|
|
268
|
+
dependenciesInstalled: componentData.dependencies,
|
|
269
|
+
importStatement,
|
|
270
|
+
usage: usageStr,
|
|
271
|
+
dryRun: true,
|
|
272
|
+
});
|
|
273
|
+
if (!json) {
|
|
274
|
+
console.log('\n[Dry run] Would install:');
|
|
275
|
+
console.log(` Component: ${componentData.name}`);
|
|
276
|
+
console.log(` Files: ${filesWritten.join(', ')}`);
|
|
277
|
+
console.log(` Dependencies: ${componentData.dependencies.join(', ') || 'none'}`);
|
|
278
|
+
if (importStatement)
|
|
279
|
+
console.log(` Import: ${importStatement}`);
|
|
280
|
+
// --inspect: show file contents
|
|
281
|
+
if (addOptions.inspect) {
|
|
282
|
+
for (const f of componentData.files) {
|
|
283
|
+
console.log(`\n${'ā'.repeat(60)}`);
|
|
284
|
+
console.log(` ${f.path}`);
|
|
285
|
+
console.log(`${'ā'.repeat(60)}`);
|
|
286
|
+
console.log(f.content);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
continue;
|
|
116
291
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
292
|
+
// Step 4: Write component files
|
|
293
|
+
const writeSpinner = json
|
|
294
|
+
? null
|
|
295
|
+
: brandSpinner('Writing component files...').start();
|
|
296
|
+
try {
|
|
297
|
+
writeComponentFiles(componentData.files, componentsPath, true);
|
|
298
|
+
writeSpinner?.succeed(`Wrote ${componentData.files.length} file(s) to ${path.relative(cwd, componentsPath) || '.'}`);
|
|
122
299
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Step 2: Check for existing files
|
|
138
|
-
const existingFiles = checkExistingFiles(componentData.files, componentsPath);
|
|
139
|
-
if (existingFiles.length > 0 && !addOptions.overwrite) {
|
|
140
|
-
if (!json) {
|
|
141
|
-
console.log('\nThe following files already exist:');
|
|
142
|
-
existingFiles.forEach((file) => console.log(` - ${file}`));
|
|
143
|
-
}
|
|
144
|
-
if (!addOptions.yes) {
|
|
145
|
-
const { shouldOverwrite } = await prompts({
|
|
146
|
-
type: 'confirm',
|
|
147
|
-
name: 'shouldOverwrite',
|
|
148
|
-
message: 'Do you want to overwrite these files?',
|
|
149
|
-
initial: false,
|
|
150
|
-
});
|
|
151
|
-
if (!shouldOverwrite) {
|
|
152
|
-
console.log('Installation cancelled.');
|
|
153
|
-
return;
|
|
300
|
+
catch (err) {
|
|
301
|
+
writeSpinner?.fail('Failed to write component files');
|
|
302
|
+
if (isBatch) {
|
|
303
|
+
batchResults.push({
|
|
304
|
+
component: componentData.name,
|
|
305
|
+
generatedName: componentData.generatedName,
|
|
306
|
+
filesWritten: [],
|
|
307
|
+
dependenciesInstalled: [],
|
|
308
|
+
dryRun: false,
|
|
309
|
+
error: 'Failed to write component files.',
|
|
310
|
+
});
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
throw new MagicPathError('Failed to write component files. Please check permissions and try again.');
|
|
154
314
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
315
|
+
// Step 5: Install utils if needed
|
|
316
|
+
const utilsExists = checkUtilsExists(utilsPath);
|
|
317
|
+
if (!utilsExists && componentData.utils.content) {
|
|
318
|
+
const utilsSpinner = json
|
|
319
|
+
? null
|
|
320
|
+
: brandSpinner('Installing utilities...').start();
|
|
321
|
+
try {
|
|
322
|
+
writeUtilsFile(componentData.utils.content, utilsPath);
|
|
323
|
+
utilsSpinner?.succeed(`Wrote utils.ts to ${path.relative(cwd, utilsPath) || '.'}`);
|
|
324
|
+
}
|
|
325
|
+
catch (err) {
|
|
326
|
+
utilsSpinner?.fail('Failed to write utils file');
|
|
327
|
+
logger.debug({ err });
|
|
328
|
+
if (!json) {
|
|
329
|
+
console.log(' Warning: Could not write utils.ts. You may need to create it manually.');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
162
332
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// Build result data for JSON output
|
|
185
|
-
const filesWritten = componentData.files.map((f) => path.relative(cwd, path.join(componentsPath, f.path)));
|
|
186
|
-
// Extract import info
|
|
187
|
-
const mainFile = componentData.files[0];
|
|
188
|
-
let importStatement;
|
|
189
|
-
let usageStr;
|
|
190
|
-
if (mainFile) {
|
|
191
|
-
const fileName = mainFile.name.replace(/\.tsx?$/, '');
|
|
192
|
-
const importPath = addOptions.path
|
|
193
|
-
? `@/${addOptions.path}/${componentFolderName}/${fileName}`
|
|
194
|
-
: `@/components/magicpath/${componentFolderName}/${fileName}`;
|
|
195
|
-
const exportInfo = extractComponentExports(mainFile.content);
|
|
196
|
-
if (exportInfo) {
|
|
197
|
-
importStatement = generateImportStatement(exportInfo, importPath);
|
|
198
|
-
usageStr = generateUsageExample(exportInfo);
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
importStatement = `import { ${fileName} } from '${importPath}';`;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Dry run: return what would happen without writing
|
|
205
|
-
if (addOptions.dryRun) {
|
|
206
|
-
const fileContents = addOptions.inspect || json
|
|
207
|
-
? componentData.files.map((f) => ({
|
|
208
|
-
path: f.path,
|
|
209
|
-
name: f.name,
|
|
210
|
-
content: f.content,
|
|
211
|
-
}))
|
|
212
|
-
: undefined;
|
|
213
|
-
if (json) {
|
|
214
|
-
jsonResult({
|
|
333
|
+
// Step 6: Install dependencies
|
|
334
|
+
const installedDeps = [];
|
|
335
|
+
if (componentData.dependencies.length > 0) {
|
|
336
|
+
try {
|
|
337
|
+
const { installed, skipped } = installPackages(componentData.dependencies, cwd);
|
|
338
|
+
installedDeps.push(...installed);
|
|
339
|
+
if (skipped.length > 0) {
|
|
340
|
+
logger.debug({
|
|
341
|
+
message: 'Skipped already installed packages',
|
|
342
|
+
skipped,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
if (err instanceof MagicPathError) {
|
|
348
|
+
if (!json)
|
|
349
|
+
console.log(`\nā ļø ${err.message}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
batchResults.push({
|
|
215
354
|
component: componentData.name,
|
|
216
355
|
generatedName: componentData.generatedName,
|
|
217
356
|
filesWritten,
|
|
218
|
-
|
|
219
|
-
dependenciesInstalled: componentData.dependencies,
|
|
357
|
+
dependenciesInstalled: installedDeps,
|
|
220
358
|
importStatement,
|
|
221
359
|
usage: usageStr,
|
|
222
|
-
dryRun:
|
|
360
|
+
dryRun: false,
|
|
223
361
|
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
console.log(` ${f.path}`);
|
|
236
|
-
console.log(`${'ā'.repeat(60)}`);
|
|
237
|
-
console.log(f.content);
|
|
362
|
+
// Single-component JSON output (non-batch)
|
|
363
|
+
if (!isBatch && json) {
|
|
364
|
+
jsonResult({
|
|
365
|
+
component: componentData.name,
|
|
366
|
+
generatedName: componentData.generatedName,
|
|
367
|
+
filesWritten,
|
|
368
|
+
dependenciesInstalled: installedDeps,
|
|
369
|
+
importStatement,
|
|
370
|
+
usage: usageStr,
|
|
371
|
+
dryRun: false,
|
|
372
|
+
});
|
|
238
373
|
}
|
|
239
|
-
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// Step 4: Write component files
|
|
243
|
-
const writeSpinner = json
|
|
244
|
-
? null
|
|
245
|
-
: brandSpinner('Writing component files...').start();
|
|
246
|
-
try {
|
|
247
|
-
writeComponentFiles(componentData.files, componentsPath, true);
|
|
248
|
-
writeSpinner?.succeed(`Wrote ${componentData.files.length} file(s) to ${path.relative(cwd, componentsPath) || '.'}`);
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
writeSpinner?.fail('Failed to write component files');
|
|
252
|
-
throw new MagicPathError('Failed to write component files. Please check permissions and try again.');
|
|
253
|
-
}
|
|
254
|
-
// Step 5: Install utils if needed
|
|
255
|
-
const utilsExists = checkUtilsExists(utilsPath);
|
|
256
|
-
if (!utilsExists && componentData.utils.content) {
|
|
257
|
-
const utilsSpinner = json
|
|
258
|
-
? null
|
|
259
|
-
: brandSpinner('Installing utilities...').start();
|
|
260
|
-
try {
|
|
261
|
-
writeUtilsFile(componentData.utils.content, utilsPath);
|
|
262
|
-
utilsSpinner?.succeed(`Wrote utils.ts to ${path.relative(cwd, utilsPath) || '.'}`);
|
|
263
|
-
}
|
|
264
|
-
catch (err) {
|
|
265
|
-
utilsSpinner?.fail('Failed to write utils file');
|
|
266
|
-
logger.debug({ err });
|
|
374
|
+
// Step 7: Show success message and write usage file
|
|
267
375
|
if (!json) {
|
|
268
|
-
console.log(
|
|
376
|
+
console.log(`\nā
Successfully added "${componentData.name}" to your project!`);
|
|
377
|
+
if (!isBatch) {
|
|
378
|
+
console.log(`\nUsage:`);
|
|
379
|
+
}
|
|
269
380
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
381
|
+
if (mainFile) {
|
|
382
|
+
const fileName = mainFile.name.replace(/\.tsx?$/, '');
|
|
383
|
+
const importPath = addOptions.path
|
|
384
|
+
? `@/${addOptions.path}/${componentFolderName}/${fileName}`
|
|
385
|
+
: `@/components/magicpath/${componentFolderName}/${fileName}`;
|
|
386
|
+
const exportInfo = extractComponentExports(mainFile.content);
|
|
387
|
+
if (!isBatch && !json) {
|
|
388
|
+
if (exportInfo) {
|
|
389
|
+
console.log(` ${generateImportStatement(exportInfo, importPath)}`);
|
|
390
|
+
console.log('');
|
|
391
|
+
console.log(` ${generateUsageExample(exportInfo)}`);
|
|
392
|
+
if (exportInfo.requiredProps.length > 0) {
|
|
393
|
+
console.log('');
|
|
394
|
+
console.log(` Required props: ${exportInfo.requiredProps.join(', ')}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
console.log(` import { ${fileName} } from '${importPath}';`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
writeUsageFile(componentsPath, {
|
|
403
|
+
componentName: componentData.name,
|
|
404
|
+
importPath,
|
|
405
|
+
exportInfo,
|
|
406
|
+
fileName,
|
|
407
|
+
});
|
|
408
|
+
if (!isBatch && !json) {
|
|
409
|
+
console.log(`\nš Usage documentation written to ${path.relative(cwd, path.join(componentsPath, 'usage.md'))}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch (err) {
|
|
413
|
+
logger.debug({ err });
|
|
414
|
+
}
|
|
283
415
|
}
|
|
284
416
|
}
|
|
285
417
|
catch (err) {
|
|
286
|
-
|
|
418
|
+
// In batch mode, record error and continue
|
|
419
|
+
if (isBatch) {
|
|
420
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
421
|
+
batchResults.push({
|
|
422
|
+
component: generatedName,
|
|
423
|
+
generatedName,
|
|
424
|
+
filesWritten: [],
|
|
425
|
+
dependenciesInstalled: [],
|
|
426
|
+
dryRun: addOptions.dryRun,
|
|
427
|
+
error: errMsg,
|
|
428
|
+
});
|
|
287
429
|
if (!json)
|
|
288
|
-
console.log(
|
|
430
|
+
console.log(` ā ļø Failed "${generatedName}": ${errMsg}`);
|
|
431
|
+
continue;
|
|
289
432
|
}
|
|
433
|
+
throw err;
|
|
290
434
|
}
|
|
291
435
|
}
|
|
292
|
-
//
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
usage: usageStr,
|
|
301
|
-
dryRun: false,
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
// Step 7: Show success message and write usage file
|
|
305
|
-
console.log(`\nā
Successfully added "${componentData.name}" to your project!`);
|
|
306
|
-
console.log(`\nUsage:`);
|
|
307
|
-
if (mainFile) {
|
|
308
|
-
const fileName = mainFile.name.replace(/\.tsx?$/, '');
|
|
309
|
-
const importPath = addOptions.path
|
|
310
|
-
? `@/${addOptions.path}/${componentFolderName}/${fileName}`
|
|
311
|
-
: `@/components/magicpath/${componentFolderName}/${fileName}`;
|
|
312
|
-
const exportInfo = extractComponentExports(mainFile.content);
|
|
313
|
-
if (exportInfo) {
|
|
314
|
-
console.log(` ${generateImportStatement(exportInfo, importPath)}`);
|
|
315
|
-
console.log('');
|
|
316
|
-
console.log(` ${generateUsageExample(exportInfo)}`);
|
|
317
|
-
if (exportInfo.requiredProps.length > 0) {
|
|
318
|
-
console.log('');
|
|
319
|
-
console.log(` Required props: ${exportInfo.requiredProps.join(', ')}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
console.log(` import { ${fileName} } from '${importPath}';`);
|
|
324
|
-
}
|
|
325
|
-
try {
|
|
326
|
-
writeUsageFile(componentsPath, {
|
|
327
|
-
componentName: componentData.name,
|
|
328
|
-
importPath,
|
|
329
|
-
exportInfo,
|
|
330
|
-
fileName,
|
|
436
|
+
// Batch summary
|
|
437
|
+
if (isBatch) {
|
|
438
|
+
if (json) {
|
|
439
|
+
jsonResult({
|
|
440
|
+
results: batchResults,
|
|
441
|
+
total: batchResults.length,
|
|
442
|
+
succeeded: batchResults.filter((r) => !r.error).length,
|
|
443
|
+
failed: batchResults.filter((r) => r.error).length,
|
|
331
444
|
});
|
|
332
|
-
console.log(`\nš Usage documentation written to ${path.relative(cwd, path.join(componentsPath, 'usage.md'))}`);
|
|
333
445
|
}
|
|
334
|
-
|
|
335
|
-
|
|
446
|
+
const succeeded = batchResults.filter((r) => !r.error);
|
|
447
|
+
const failed = batchResults.filter((r) => r.error);
|
|
448
|
+
console.log(`\n ${succeeded.length} of ${batchResults.length} component(s) added successfully.`);
|
|
449
|
+
if (failed.length > 0) {
|
|
450
|
+
console.log(` ${failed.length} failed: ${failed.map((f) => f.generatedName).join(', ')}`);
|
|
336
451
|
}
|
|
337
452
|
}
|
|
338
453
|
}
|