vibefast-cli 0.1.3 → 0.2.0
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/AUTO-DETECT-DEPS.md +607 -0
- package/CHANGELOG.md +86 -0
- package/FINAL-PACKAGE-STRATEGY.md +583 -0
- package/FINAL-SIMPLE-PLAN.md +487 -0
- package/FLOW-DIAGRAM.md +1629 -0
- package/GOTCHAS-AND-RISKS.md +801 -0
- package/IMPLEMENTATION-COMPLETE.md +477 -0
- package/IMPLEMENTATION-PLAN.md +1360 -0
- package/PRE-PUBLISH-CHECKLIST.md +558 -0
- package/PRODUCTION-READINESS.md +684 -0
- package/PRODUCTION-TEST-RESULTS.md +465 -0
- package/README.md +73 -7
- package/READY-TO-PUBLISH.md +419 -0
- package/SIMPLIFIED-PLAN.md +578 -0
- package/TEST-SUMMARY.md +261 -0
- package/USER-MODIFICATIONS.md +448 -0
- package/cloudflare-worker/worker.js +39 -11
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +192 -15
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/checklist.d.ts +3 -0
- package/dist/commands/checklist.d.ts.map +1 -0
- package/dist/commands/checklist.js +64 -0
- package/dist/commands/checklist.js.map +1 -0
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +85 -2
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +40 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/core/__tests__/fsx.test.d.ts +2 -0
- package/dist/core/__tests__/fsx.test.d.ts.map +1 -0
- package/dist/core/__tests__/fsx.test.js +79 -0
- package/dist/core/__tests__/fsx.test.js.map +1 -0
- package/dist/core/__tests__/hash.test.d.ts +2 -0
- package/dist/core/__tests__/hash.test.d.ts.map +1 -0
- package/dist/core/__tests__/hash.test.js +84 -0
- package/dist/core/__tests__/hash.test.js.map +1 -0
- package/dist/core/__tests__/journal.test.js +65 -0
- package/dist/core/__tests__/journal.test.js.map +1 -1
- package/dist/core/__tests__/prompt.test.d.ts +2 -0
- package/dist/core/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/core/__tests__/prompt.test.js +56 -0
- package/dist/core/__tests__/prompt.test.js.map +1 -0
- package/dist/core/fsx.d.ts +7 -1
- package/dist/core/fsx.d.ts.map +1 -1
- package/dist/core/fsx.js +18 -3
- package/dist/core/fsx.js.map +1 -1
- package/dist/core/hash.d.ts +13 -0
- package/dist/core/hash.d.ts.map +1 -0
- package/dist/core/hash.js +69 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/journal.d.ts +10 -1
- package/dist/core/journal.d.ts.map +1 -1
- package/dist/core/journal.js +23 -1
- package/dist/core/journal.js.map +1 -1
- package/dist/core/prompt.d.ts +11 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +34 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/commands/add.ts +234 -16
- package/src/commands/checklist.ts +71 -0
- package/src/commands/remove.ts +105 -3
- package/src/commands/status.ts +47 -0
- package/src/core/__tests__/fsx.test.ts +101 -0
- package/src/core/__tests__/hash.test.ts +112 -0
- package/src/core/__tests__/journal.test.ts +76 -0
- package/src/core/__tests__/prompt.test.ts +72 -0
- package/src/core/fsx.ts +38 -5
- package/src/core/hash.ts +84 -0
- package/src/core/journal.ts +40 -2
- package/src/core/prompt.ts +40 -0
- package/src/index.ts +5 -1
- package/text.md +27 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# 🔍 Auto-Detecting Missing Dependencies
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
When we install a feature, we copy files that import packages:
|
|
6
|
+
following is an example we aint using that pck name in reality:
|
|
7
|
+
```typescript
|
|
8
|
+
// apps/native/src/features/charts/index.tsx
|
|
9
|
+
import { LineChart } from 'react-native-chart-kit'; // ← Package not installed!
|
|
10
|
+
import { Dimensions } from 'react-native';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
If `react-native-chart-kit` isn't installed, the app will crash or fail to build.
|
|
14
|
+
|
|
15
|
+
## The Question
|
|
16
|
+
|
|
17
|
+
Can we **automatically detect** what packages are missing instead of manually specifying them in recipe.json?
|
|
18
|
+
|
|
19
|
+
**Answer: YES!** Multiple approaches:
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Approach 1: Run TypeScript Type Checking ✅ BEST
|
|
24
|
+
|
|
25
|
+
### How It Works
|
|
26
|
+
|
|
27
|
+
TypeScript will error on missing packages:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
$ pnpm tsc --noEmit
|
|
31
|
+
# Output:
|
|
32
|
+
# error TS2307: Cannot find module 'react-native-chart-kit' or its corresponding type declarations.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Implementation
|
|
36
|
+
|
|
37
|
+
**File:** `src/core/typecheck.ts` (NEW)
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { executeBash } from './shell.js';
|
|
41
|
+
import { log } from './log.js';
|
|
42
|
+
|
|
43
|
+
export interface TypeCheckResult {
|
|
44
|
+
success: boolean;
|
|
45
|
+
missingPackages: string[];
|
|
46
|
+
errors: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runTypeCheck(cwd: string): Promise<TypeCheckResult> {
|
|
50
|
+
log.info('Running type check to detect missing packages...');
|
|
51
|
+
|
|
52
|
+
// Try different type checkers in order of preference
|
|
53
|
+
const checkers = [
|
|
54
|
+
{ cmd: 'pnpm tsc --noEmit', name: 'pnpm' },
|
|
55
|
+
{ cmd: 'yarn tsc --noEmit', name: 'yarn' },
|
|
56
|
+
{ cmd: 'npx tsc --noEmit', name: 'npm' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
let result;
|
|
60
|
+
for (const checker of checkers) {
|
|
61
|
+
result = await executeBash(checker.cmd, { cwd });
|
|
62
|
+
if (result.exitCode !== 127) { // 127 = command not found
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!result || result.exitCode === 127) {
|
|
68
|
+
log.warn('TypeScript not found, skipping type check');
|
|
69
|
+
return { success: true, missingPackages: [], errors: [] };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Parse TypeScript errors
|
|
73
|
+
const missingPackages = new Set<string>();
|
|
74
|
+
const errors: string[] = [];
|
|
75
|
+
|
|
76
|
+
const lines = result.stderr.split('\n');
|
|
77
|
+
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
// Match: error TS2307: Cannot find module 'package-name'
|
|
80
|
+
const match = line.match(/error TS2307: Cannot find module '([^']+)'/);
|
|
81
|
+
if (match) {
|
|
82
|
+
const moduleName = match[1];
|
|
83
|
+
|
|
84
|
+
// Extract package name (handle scoped packages and subpaths)
|
|
85
|
+
const packageName = extractPackageName(moduleName);
|
|
86
|
+
|
|
87
|
+
if (packageName && !isBuiltInModule(packageName)) {
|
|
88
|
+
missingPackages.add(packageName);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Collect all errors for debugging
|
|
93
|
+
if (line.includes('error TS')) {
|
|
94
|
+
errors.push(line);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
success: result.exitCode === 0,
|
|
100
|
+
missingPackages: Array.from(missingPackages),
|
|
101
|
+
errors,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function extractPackageName(modulePath: string): string | null {
|
|
106
|
+
// Handle different import patterns:
|
|
107
|
+
// 'react-native-chart-kit' → 'react-native-chart-kit'
|
|
108
|
+
// 'react-native-chart-kit/dist/LineChart' → 'react-native-chart-kit'
|
|
109
|
+
// '@react-navigation/native' → '@react-navigation/native'
|
|
110
|
+
// '@react-navigation/native/lib/index' → '@react-navigation/native'
|
|
111
|
+
|
|
112
|
+
if (modulePath.startsWith('@')) {
|
|
113
|
+
// Scoped package: @scope/package
|
|
114
|
+
const parts = modulePath.split('/');
|
|
115
|
+
if (parts.length >= 2) {
|
|
116
|
+
return `${parts[0]}/${parts[1]}`;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
// Regular package: package-name
|
|
120
|
+
const parts = modulePath.split('/');
|
|
121
|
+
return parts[0];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isBuiltInModule(packageName: string): boolean {
|
|
128
|
+
// Node.js built-in modules
|
|
129
|
+
const builtIns = [
|
|
130
|
+
'fs', 'path', 'crypto', 'http', 'https', 'url', 'util',
|
|
131
|
+
'stream', 'events', 'buffer', 'process', 'os', 'child_process',
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
// React Native built-ins
|
|
135
|
+
const rnBuiltIns = [
|
|
136
|
+
'react-native',
|
|
137
|
+
'react',
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
return builtIns.includes(packageName) || rnBuiltIns.includes(packageName);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Usage in add command
|
|
145
|
+
|
|
146
|
+
**File:** `src/commands/add.ts`
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// After copying files, BEFORE adding watermarks
|
|
150
|
+
|
|
151
|
+
if (!options.dryRun && !options.skipTypeCheck) {
|
|
152
|
+
log.plain('');
|
|
153
|
+
log.info('🔍 Checking for missing dependencies...');
|
|
154
|
+
|
|
155
|
+
const typeCheckResult = await runTypeCheck(paths.cwd);
|
|
156
|
+
|
|
157
|
+
if (typeCheckResult.missingPackages.length > 0) {
|
|
158
|
+
log.plain('');
|
|
159
|
+
log.warn('⚠ Missing packages detected:');
|
|
160
|
+
log.plain('');
|
|
161
|
+
|
|
162
|
+
typeCheckResult.missingPackages.forEach(pkg => {
|
|
163
|
+
log.plain(` • ${pkg}`);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
log.plain('');
|
|
167
|
+
|
|
168
|
+
// Try to find versions from recipe.json if available
|
|
169
|
+
const packagesWithVersions: Record<string, string> = {};
|
|
170
|
+
|
|
171
|
+
if (manifest.dependencies?.npm) {
|
|
172
|
+
typeCheckResult.missingPackages.forEach(pkg => {
|
|
173
|
+
if (manifest.dependencies.npm[pkg]) {
|
|
174
|
+
packagesWithVersions[pkg] = manifest.dependencies.npm[pkg];
|
|
175
|
+
} else {
|
|
176
|
+
// Default to latest
|
|
177
|
+
packagesWithVersions[pkg] = 'latest';
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
// No recipe dependencies, use latest for all
|
|
182
|
+
typeCheckResult.missingPackages.forEach(pkg => {
|
|
183
|
+
packagesWithVersions[pkg] = 'latest';
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Show install command
|
|
188
|
+
const pm = await detectPackageManager(paths.cwd);
|
|
189
|
+
const pkgList = Object.entries(packagesWithVersions)
|
|
190
|
+
.map(([name, version]) => version === 'latest' ? name : `${name}@${version}`)
|
|
191
|
+
.join(' ');
|
|
192
|
+
const installCmd = getInstallCommand(pm, pkgList.split(' '));
|
|
193
|
+
|
|
194
|
+
log.info('📦 Install with:');
|
|
195
|
+
log.plain(` ${installCmd}`);
|
|
196
|
+
log.plain('');
|
|
197
|
+
|
|
198
|
+
// Ask to install
|
|
199
|
+
if (!options.yes) {
|
|
200
|
+
const shouldInstall = promptYesNo(
|
|
201
|
+
'Install missing packages now? (Y/n): ',
|
|
202
|
+
true
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (shouldInstall) {
|
|
206
|
+
log.info('Installing packages...');
|
|
207
|
+
const result = await installPackages(packagesWithVersions, paths.cwd);
|
|
208
|
+
|
|
209
|
+
if (result.success) {
|
|
210
|
+
log.success('✓ Packages installed successfully!');
|
|
211
|
+
|
|
212
|
+
// Run type check again to verify
|
|
213
|
+
log.info('Verifying installation...');
|
|
214
|
+
const verifyResult = await runTypeCheck(paths.cwd);
|
|
215
|
+
|
|
216
|
+
if (verifyResult.missingPackages.length > 0) {
|
|
217
|
+
log.warn('⚠ Some packages are still missing:');
|
|
218
|
+
verifyResult.missingPackages.forEach(pkg => {
|
|
219
|
+
log.plain(` • ${pkg}`);
|
|
220
|
+
});
|
|
221
|
+
} else {
|
|
222
|
+
log.success('✓ All dependencies resolved!');
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
log.error(`✗ Installation failed: ${result.error}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
// Auto-install in --yes mode
|
|
230
|
+
log.info('Installing packages...');
|
|
231
|
+
const result = await installPackages(packagesWithVersions, paths.cwd);
|
|
232
|
+
|
|
233
|
+
if (!result.success) {
|
|
234
|
+
log.error(`✗ Installation failed: ${result.error}`);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} else if (typeCheckResult.success) {
|
|
239
|
+
log.success('✓ No missing dependencies detected');
|
|
240
|
+
} else {
|
|
241
|
+
log.warn('⚠ Type check completed with errors (but no missing packages)');
|
|
242
|
+
if (typeCheckResult.errors.length > 0 && process.env.DEBUG) {
|
|
243
|
+
log.plain('');
|
|
244
|
+
log.plain('Errors:');
|
|
245
|
+
typeCheckResult.errors.slice(0, 5).forEach(err => {
|
|
246
|
+
log.plain(` ${err}`);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Add CLI flag
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
export const addCommand = new Command('add')
|
|
257
|
+
.description('Add a VibeFast feature to your project')
|
|
258
|
+
.argument('<feature>', 'Feature name to install')
|
|
259
|
+
.option('--target <target>', 'Target platform (native or web)', 'native')
|
|
260
|
+
.option('--dry-run', 'Preview changes without applying')
|
|
261
|
+
.option('--force', 'Overwrite existing files without asking')
|
|
262
|
+
.option('--yes', 'Answer yes to all prompts')
|
|
263
|
+
.option('--skip-install', 'Skip package installation')
|
|
264
|
+
.option('--skip-type-check', 'Skip type checking for missing packages') // NEW
|
|
265
|
+
.action(async (feature: string, options) => {
|
|
266
|
+
// ...
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Approach 2: Parse Import Statements 🟡 FALLBACK
|
|
273
|
+
|
|
274
|
+
If TypeScript isn't available, parse imports manually.
|
|
275
|
+
|
|
276
|
+
### Implementation
|
|
277
|
+
|
|
278
|
+
**File:** `src/core/importParser.ts` (NEW)
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { readFile } from 'fs/promises';
|
|
282
|
+
import { join } from 'path';
|
|
283
|
+
|
|
284
|
+
export async function extractImports(filePath: string): Promise<string[]> {
|
|
285
|
+
const content = await readFile(filePath, 'utf-8');
|
|
286
|
+
const imports = new Set<string>();
|
|
287
|
+
|
|
288
|
+
// Match various import patterns
|
|
289
|
+
const patterns = [
|
|
290
|
+
// import X from 'package'
|
|
291
|
+
/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,
|
|
292
|
+
// import 'package'
|
|
293
|
+
/import\s+['"]([^'"]+)['"]/g,
|
|
294
|
+
// require('package')
|
|
295
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
296
|
+
// import('package')
|
|
297
|
+
/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
for (const pattern of patterns) {
|
|
301
|
+
let match;
|
|
302
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
303
|
+
const importPath = match[1];
|
|
304
|
+
|
|
305
|
+
// Skip relative imports
|
|
306
|
+
if (importPath.startsWith('.') || importPath.startsWith('/')) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
imports.add(importPath);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return Array.from(imports);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export async function extractImportsFromFiles(
|
|
318
|
+
filePaths: string[]
|
|
319
|
+
): Promise<string[]> {
|
|
320
|
+
const allImports = new Set<string>();
|
|
321
|
+
|
|
322
|
+
for (const filePath of filePaths) {
|
|
323
|
+
// Only parse JS/TS files
|
|
324
|
+
if (!/\.(js|jsx|ts|tsx)$/.test(filePath)) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const imports = await extractImports(filePath);
|
|
330
|
+
imports.forEach(imp => allImports.add(imp));
|
|
331
|
+
} catch (error) {
|
|
332
|
+
// Skip files that can't be read
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return Array.from(allImports).map(extractPackageName).filter(Boolean) as string[];
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Check Against Installed Packages
|
|
342
|
+
|
|
343
|
+
**File:** `src/core/packageChecker.ts` (NEW)
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { readFile } from 'fs/promises';
|
|
347
|
+
import { join } from 'path';
|
|
348
|
+
import { exists } from './fsx.js';
|
|
349
|
+
|
|
350
|
+
export async function getInstalledPackages(cwd: string): Promise<Set<string>> {
|
|
351
|
+
const packageJsonPath = join(cwd, 'package.json');
|
|
352
|
+
|
|
353
|
+
if (!await exists(packageJsonPath)) {
|
|
354
|
+
return new Set();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const content = await readFile(packageJsonPath, 'utf-8');
|
|
358
|
+
const packageJson = JSON.parse(content);
|
|
359
|
+
|
|
360
|
+
const installed = new Set<string>();
|
|
361
|
+
|
|
362
|
+
// Add dependencies
|
|
363
|
+
if (packageJson.dependencies) {
|
|
364
|
+
Object.keys(packageJson.dependencies).forEach(pkg => installed.add(pkg));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Add devDependencies
|
|
368
|
+
if (packageJson.devDependencies) {
|
|
369
|
+
Object.keys(packageJson.devDependencies).forEach(pkg => installed.add(pkg));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return installed;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function findMissingPackages(
|
|
376
|
+
requiredPackages: string[],
|
|
377
|
+
cwd: string
|
|
378
|
+
): Promise<string[]> {
|
|
379
|
+
const installed = await getInstalledPackages(cwd);
|
|
380
|
+
|
|
381
|
+
return requiredPackages.filter(pkg => !installed.has(pkg));
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Usage
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// After copying files
|
|
389
|
+
|
|
390
|
+
// Extract imports from copied files
|
|
391
|
+
const imports = await extractImportsFromFiles(copiedFiles);
|
|
392
|
+
|
|
393
|
+
// Check which are missing
|
|
394
|
+
const missing = await findMissingPackages(imports, paths.cwd);
|
|
395
|
+
|
|
396
|
+
if (missing.length > 0) {
|
|
397
|
+
log.warn('⚠ Missing packages detected:');
|
|
398
|
+
missing.forEach(pkg => log.plain(` • ${pkg}`));
|
|
399
|
+
// ... prompt to install
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Approach 3: Run ESLint 🟢 BONUS
|
|
406
|
+
|
|
407
|
+
If the project has ESLint configured:
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
export async function runESLint(cwd: string): Promise<string[]> {
|
|
411
|
+
const result = await executeBash('npx eslint . --format json', { cwd });
|
|
412
|
+
|
|
413
|
+
if (result.exitCode === 0) {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
const eslintOutput = JSON.parse(result.stdout);
|
|
419
|
+
const missingPackages = new Set<string>();
|
|
420
|
+
|
|
421
|
+
eslintOutput.forEach((file: any) => {
|
|
422
|
+
file.messages.forEach((msg: any) => {
|
|
423
|
+
// ESLint rule: import/no-unresolved
|
|
424
|
+
if (msg.ruleId === 'import/no-unresolved') {
|
|
425
|
+
const match = msg.message.match(/Unable to resolve path to module '([^']+)'/);
|
|
426
|
+
if (match) {
|
|
427
|
+
const packageName = extractPackageName(match[1]);
|
|
428
|
+
if (packageName) {
|
|
429
|
+
missingPackages.add(packageName);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
return Array.from(missingPackages);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
return [];
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Recommended Strategy: Hybrid Approach
|
|
446
|
+
|
|
447
|
+
### Step 1: Try TypeScript (Most Accurate)
|
|
448
|
+
```typescript
|
|
449
|
+
const typeCheckResult = await runTypeCheck(paths.cwd);
|
|
450
|
+
if (typeCheckResult.missingPackages.length > 0) {
|
|
451
|
+
return typeCheckResult.missingPackages;
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Step 2: Fallback to Import Parsing
|
|
456
|
+
```typescript
|
|
457
|
+
if (!typeCheckResult.success && typeCheckResult.missingPackages.length === 0) {
|
|
458
|
+
// TypeScript failed or not available, try parsing
|
|
459
|
+
const imports = await extractImportsFromFiles(copiedFiles);
|
|
460
|
+
const missing = await findMissingPackages(imports, paths.cwd);
|
|
461
|
+
return missing;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Step 3: Use Recipe as Source of Truth
|
|
466
|
+
```typescript
|
|
467
|
+
// If detection fails, fall back to recipe.json
|
|
468
|
+
if (manifest.dependencies?.npm) {
|
|
469
|
+
return Object.keys(manifest.dependencies.npm);
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Complete Flow
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
479
|
+
│ DEPENDENCY DETECTION FLOW │
|
|
480
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
481
|
+
|
|
482
|
+
Step 1: Install Feature Files
|
|
483
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
484
|
+
│ $ vf add charts │
|
|
485
|
+
│ ✓ Files copied: 17 │
|
|
486
|
+
└──────────────────────────────────────────────────────────────┘
|
|
487
|
+
│
|
|
488
|
+
▼
|
|
489
|
+
Step 2: Run Type Check
|
|
490
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
491
|
+
│ $ pnpm tsc --noEmit │
|
|
492
|
+
│ │
|
|
493
|
+
│ Output: │
|
|
494
|
+
│ error TS2307: Cannot find module 'react-native-chart-kit' │
|
|
495
|
+
│ error TS2307: Cannot find module 'react-native-svg' │
|
|
496
|
+
└──────────────────────────────────────────────────────────────┘
|
|
497
|
+
│
|
|
498
|
+
▼
|
|
499
|
+
Step 3: Parse Errors
|
|
500
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
501
|
+
│ Detected missing packages: │
|
|
502
|
+
│ • react-native-chart-kit │
|
|
503
|
+
│ • react-native-svg │
|
|
504
|
+
└──────────────────────────────────────────────────────────────┘
|
|
505
|
+
│
|
|
506
|
+
▼
|
|
507
|
+
Step 4: Check Recipe for Versions
|
|
508
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
509
|
+
│ recipe.json has: │
|
|
510
|
+
│ { │
|
|
511
|
+
│ "dependencies": { │
|
|
512
|
+
│ "npm": { │
|
|
513
|
+
│ "react-native-chart-kit": "^6.12.0", │
|
|
514
|
+
│ "react-native-svg": "^13.9.0" │
|
|
515
|
+
│ } │
|
|
516
|
+
│ } │
|
|
517
|
+
│ } │
|
|
518
|
+
│ │
|
|
519
|
+
│ Use these versions ✓ │
|
|
520
|
+
└──────────────────────────────────────────────────────────────┘
|
|
521
|
+
│
|
|
522
|
+
▼
|
|
523
|
+
Step 5: Prompt User
|
|
524
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
525
|
+
│ ⚠ Missing packages detected: │
|
|
526
|
+
│ • react-native-chart-kit@^6.12.0 │
|
|
527
|
+
│ • react-native-svg@^13.9.0 │
|
|
528
|
+
│ │
|
|
529
|
+
│ 📦 Install with: │
|
|
530
|
+
│ pnpm add react-native-chart-kit@^6.12.0 react-native-svg@^13.9.0
|
|
531
|
+
│ │
|
|
532
|
+
│ Install missing packages now? (Y/n): _ │
|
|
533
|
+
└──────────────────────────────────────────────────────────────┘
|
|
534
|
+
│
|
|
535
|
+
▼
|
|
536
|
+
Step 6: Install Packages
|
|
537
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
538
|
+
│ $ pnpm add react-native-chart-kit@^6.12.0 react-native-svg@^13.9.0
|
|
539
|
+
│ ✓ Packages installed │
|
|
540
|
+
└──────────────────────────────────────────────────────────────┘
|
|
541
|
+
│
|
|
542
|
+
▼
|
|
543
|
+
Step 7: Verify
|
|
544
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
545
|
+
│ $ pnpm tsc --noEmit │
|
|
546
|
+
│ ✓ No errors │
|
|
547
|
+
│ ✓ All dependencies resolved! │
|
|
548
|
+
└──────────────────────────────────────────────────────────────┘
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Advantages of This Approach
|
|
554
|
+
|
|
555
|
+
1. **Automatic Detection** - No need to manually specify in recipe.json
|
|
556
|
+
2. **Accurate** - TypeScript knows exactly what's missing
|
|
557
|
+
3. **Version Aware** - Can still use recipe.json for specific versions
|
|
558
|
+
4. **Fallback** - Import parsing if TypeScript not available
|
|
559
|
+
5. **Verification** - Can re-run type check after install to confirm
|
|
560
|
+
|
|
561
|
+
## Disadvantages
|
|
562
|
+
|
|
563
|
+
1. **Slower** - Type checking takes 5-30 seconds
|
|
564
|
+
2. **Requires TypeScript** - Not all projects have it
|
|
565
|
+
3. **False Positives** - Might detect unrelated errors
|
|
566
|
+
4. **Version Guessing** - If not in recipe.json, defaults to 'latest'
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Recommendation
|
|
571
|
+
|
|
572
|
+
**Use BOTH approaches:**
|
|
573
|
+
|
|
574
|
+
1. **Recipe.json as primary** (fast, explicit, version-controlled)
|
|
575
|
+
2. **Type checking as verification** (catch missing deps in recipe.json)
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
// After copying files
|
|
579
|
+
|
|
580
|
+
// 1. Check recipe.json first
|
|
581
|
+
if (manifest.dependencies?.npm) {
|
|
582
|
+
const recipeDeps = manifest.dependencies.npm;
|
|
583
|
+
// Show and install these
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// 2. Run type check to verify
|
|
587
|
+
const typeCheckResult = await runTypeCheck(paths.cwd);
|
|
588
|
+
if (typeCheckResult.missingPackages.length > 0) {
|
|
589
|
+
log.warn('⚠ Additional missing packages detected:');
|
|
590
|
+
// Show packages not in recipe.json
|
|
591
|
+
const additional = typeCheckResult.missingPackages.filter(
|
|
592
|
+
pkg => !recipeDeps[pkg]
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
if (additional.length > 0) {
|
|
596
|
+
log.warn('These packages are missing from recipe.json:');
|
|
597
|
+
additional.forEach(pkg => log.plain(` • ${pkg}`));
|
|
598
|
+
log.info('Consider updating the recipe to include these.');
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
This gives us:
|
|
604
|
+
- ✅ Fast installation (recipe.json)
|
|
605
|
+
- ✅ Verification (type check)
|
|
606
|
+
- ✅ Catches recipe mistakes
|
|
607
|
+
- ✅ Best of both worlds
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to VibeFast CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.2.0] - 2024-11-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Interactive Confirmation**: CLI now asks before overwriting existing files
|
|
9
|
+
- **Package Dependency Display**: Shows required packages with `npx expo install` command
|
|
10
|
+
- **Modification Detection**: Warns before deleting files you've modified
|
|
11
|
+
- **Manual Steps Display**: Shows setup instructions for services (Sentry, PostHog, etc.)
|
|
12
|
+
- **Status Command**: New `vf status` command to show installed features
|
|
13
|
+
- **Checklist Command**: New `vf checklist <feature>` command to show manual setup steps
|
|
14
|
+
- **File Hashing**: SHA-256 hashing for integrity checking
|
|
15
|
+
- **Auto-Migration**: Automatically migrates old journal format to new format
|
|
16
|
+
- **Automation Flags**: Added `--yes` flag for CI/CD and `--force` flag to skip prompts
|
|
17
|
+
- **Comprehensive Tests**: 34 unit tests covering all core functionality
|
|
18
|
+
|
|
19
|
+
### Improved
|
|
20
|
+
- **Error Messages**: User-friendly error messages with clear actionable solutions
|
|
21
|
+
- **Support Contact**: All error messages include support@vibefast.pro
|
|
22
|
+
- **CI/CD Compatibility**: Detects non-interactive environments and behaves appropriately
|
|
23
|
+
- **Performance**: Optimized file hashing (skips binary files, processes in batches)
|
|
24
|
+
- **Documentation**: Comprehensive documentation and examples
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Package Management**: Now uses `npx expo install` for native apps (no version management needed)
|
|
28
|
+
- **Journal Format**: Updated to store file hashes (auto-migrates old format)
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- Files being overwritten without confirmation
|
|
32
|
+
- No warning when deleting modified files
|
|
33
|
+
- Missing package installation information
|
|
34
|
+
- Generic error messages
|
|
35
|
+
|
|
36
|
+
## [0.1.4] - 2024-11-11
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- Initial release
|
|
40
|
+
- Basic `vf add` and `vf remove` commands
|
|
41
|
+
- Authentication with `vf login` and `vf logout`
|
|
42
|
+
- Device management with `vf devices`
|
|
43
|
+
- Repository validation with `vf doctor`
|
|
44
|
+
- Feature listing with `vf list`
|
|
45
|
+
- Navigation injection
|
|
46
|
+
- Watermarking
|
|
47
|
+
- Journal tracking
|
|
48
|
+
|
|
49
|
+
### Security
|
|
50
|
+
- Path security with directory traversal prevention
|
|
51
|
+
- Token storage in user home directory
|
|
52
|
+
- HTTPS-only communication
|
|
53
|
+
- Input validation
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Upgrade Guide
|
|
58
|
+
|
|
59
|
+
### From 0.1.x to 0.2.0
|
|
60
|
+
|
|
61
|
+
No breaking changes! The CLI will automatically migrate your journal to the new format.
|
|
62
|
+
|
|
63
|
+
**New Features You Can Use:**
|
|
64
|
+
```bash
|
|
65
|
+
# Check what's installed
|
|
66
|
+
vf status
|
|
67
|
+
|
|
68
|
+
# See manual setup steps
|
|
69
|
+
vf checklist sentry
|
|
70
|
+
|
|
71
|
+
# Use automation flags
|
|
72
|
+
vf add charts --yes
|
|
73
|
+
vf remove charts --force
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Improved Error Messages:**
|
|
77
|
+
The CLI now provides much clearer error messages with actionable solutions.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Support
|
|
82
|
+
|
|
83
|
+
For issues or questions:
|
|
84
|
+
- Email: support@vibefast.pro
|
|
85
|
+
- GitHub: https://github.com/vibefast/vibefast-cli/issues
|
|
86
|
+
- Website: https://vibefast.pro
|