@fluojs/cli 1.0.0-beta.4 → 1.0.0-beta.6
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/README.ko.md +97 -3
- package/README.md +97 -3
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +201 -4
- package/dist/commands/diagnostics.d.ts +15 -0
- package/dist/commands/diagnostics.d.ts.map +1 -0
- package/dist/commands/diagnostics.js +163 -0
- package/dist/commands/new.js +2 -2
- package/dist/commands/package-manager.d.ts +9 -0
- package/dist/commands/package-manager.d.ts.map +1 -0
- package/dist/commands/package-manager.js +63 -0
- package/dist/commands/package-workflow.d.ts +20 -0
- package/dist/commands/package-workflow.d.ts.map +1 -0
- package/dist/commands/package-workflow.js +137 -0
- package/dist/commands/scripts.d.ts +38 -0
- package/dist/commands/scripts.d.ts.map +1 -0
- package/dist/commands/scripts.js +570 -0
- package/dist/dev-runner/node-restart-runner.d.ts +55 -0
- package/dist/dev-runner/node-restart-runner.d.ts.map +1 -0
- package/dist/dev-runner/node-restart-runner.js +317 -0
- package/dist/dev-runner/preserve-color-tty.d.ts +2 -0
- package/dist/dev-runner/preserve-color-tty.d.ts.map +1 -0
- package/dist/dev-runner/preserve-color-tty.js +12 -0
- package/dist/generators/manifest.d.ts +24 -0
- package/dist/generators/manifest.d.ts.map +1 -1
- package/dist/generators/manifest.js +9 -0
- package/dist/generators/resource.d.ts +10 -0
- package/dist/generators/resource.d.ts.map +1 -0
- package/dist/generators/resource.js +23 -0
- package/dist/generators/templates/controller.ts.ejs +5 -1
- package/dist/generators/templates/request-dto.ts.ejs +3 -0
- package/dist/new/scaffold.d.ts.map +1 -1
- package/dist/new/scaffold.js +193 -148
- package/dist/new/starter-profiles.d.ts.map +1 -1
- package/dist/new/starter-profiles.js +13 -13
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +7 -5
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { diagnosticsUsage, runAnalyzeCommand, runDoctorCommand, runInfoCommand } from './commands/diagnostics.js';
|
|
4
5
|
import { runGenerateCommand } from './commands/generate.js';
|
|
5
6
|
import { inspectUsage, runInspectCommand } from './commands/inspect.js';
|
|
6
7
|
import { migrateUsage, runMigrateCommand } from './commands/migrate.js';
|
|
7
8
|
import { newUsage, runNewCommand } from './commands/new.js';
|
|
9
|
+
import { addUsage, runAddCommand, runUpgradeCommand, upgradeUsage } from './commands/package-workflow.js';
|
|
10
|
+
import { runScriptCommand, scriptUsage } from './commands/scripts.js';
|
|
11
|
+
import { runNodeRestartRunner } from './dev-runner/node-restart-runner.js';
|
|
8
12
|
import { builtInGeneratorCollection, generatorManifest, generatorOptionSchemas, resolveGeneratorKind } from './generators/manifest.js';
|
|
9
13
|
import { renderAliasList, renderHelpTable } from './help.js';
|
|
10
14
|
import { removeUpdateCheckFlags, runCliUpdateCheck } from './update-check.js';
|
|
@@ -33,6 +37,34 @@ const TOP_LEVEL_COMMAND_HELP = [{
|
|
|
33
37
|
aliases: ['g'],
|
|
34
38
|
command: 'generate',
|
|
35
39
|
description: 'Generate a schematic inside an existing fluo application.'
|
|
40
|
+
}, {
|
|
41
|
+
aliases: ['info'],
|
|
42
|
+
command: 'doctor',
|
|
43
|
+
description: 'Print CLI, registry, update-cache, runtime, and project diagnostics.'
|
|
44
|
+
}, {
|
|
45
|
+
aliases: [],
|
|
46
|
+
command: 'analyze',
|
|
47
|
+
description: 'Summarize project diagnostics and point to deeper inspection flows.'
|
|
48
|
+
}, {
|
|
49
|
+
aliases: [],
|
|
50
|
+
command: 'dev',
|
|
51
|
+
description: 'Run the generated project development lifecycle.'
|
|
52
|
+
}, {
|
|
53
|
+
aliases: [],
|
|
54
|
+
command: 'start',
|
|
55
|
+
description: 'Run the generated project production lifecycle.'
|
|
56
|
+
}, {
|
|
57
|
+
aliases: [],
|
|
58
|
+
command: 'build',
|
|
59
|
+
description: 'Run the generated project build lifecycle.'
|
|
60
|
+
}, {
|
|
61
|
+
aliases: [],
|
|
62
|
+
command: 'add',
|
|
63
|
+
description: 'Install @fluojs packages with the detected package manager.'
|
|
64
|
+
}, {
|
|
65
|
+
aliases: [],
|
|
66
|
+
command: 'upgrade',
|
|
67
|
+
description: 'Report latest CLI state and migration workflow guidance.'
|
|
36
68
|
}, {
|
|
37
69
|
aliases: [],
|
|
38
70
|
command: 'inspect',
|
|
@@ -41,17 +73,66 @@ const TOP_LEVEL_COMMAND_HELP = [{
|
|
|
41
73
|
aliases: [],
|
|
42
74
|
command: 'migrate',
|
|
43
75
|
description: 'Run NestJS-to-fluo codemods (dry-run by default).'
|
|
76
|
+
}, {
|
|
77
|
+
aliases: ['--version', '-v'],
|
|
78
|
+
command: 'version',
|
|
79
|
+
description: 'Print the installed fluo CLI version.'
|
|
44
80
|
}, {
|
|
45
81
|
aliases: [],
|
|
46
82
|
command: 'help',
|
|
47
83
|
description: 'Show top-level or command-specific help.'
|
|
48
84
|
}];
|
|
85
|
+
const NODE_DEV_RUNNER_COMMAND = '__node-dev-runner';
|
|
86
|
+
const DEV_RUNNER_COMMAND = '__dev-runner';
|
|
87
|
+
function parseDevRunnerRuntime(value) {
|
|
88
|
+
if (value === 'bun' || value === 'cloudflare-workers' || value === 'deno' || value === 'node') {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
throw new Error(`Invalid dev runner runtime "${value ?? ''}".`);
|
|
92
|
+
}
|
|
93
|
+
function parseDevRunnerInvocation(argv) {
|
|
94
|
+
if (argv[0] === NODE_DEV_RUNNER_COMMAND) {
|
|
95
|
+
const separatorIndex = argv.indexOf('--');
|
|
96
|
+
return {
|
|
97
|
+
appArgs: separatorIndex >= 0 ? argv.slice(separatorIndex + 1) : argv.slice(1),
|
|
98
|
+
runtime: 'node'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const runtimeFlagIndex = argv.indexOf('--runtime');
|
|
102
|
+
const runtime = parseDevRunnerRuntime(argv[runtimeFlagIndex + 1]);
|
|
103
|
+
const separatorIndex = argv.indexOf('--');
|
|
104
|
+
if (separatorIndex >= 0) {
|
|
105
|
+
return {
|
|
106
|
+
appArgs: argv.slice(separatorIndex + 1),
|
|
107
|
+
runtime
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const appArgs = argv.slice(1).filter((arg, index, args) => arg !== '--runtime' && args[index - 1] !== '--runtime');
|
|
111
|
+
return {
|
|
112
|
+
appArgs,
|
|
113
|
+
runtime
|
|
114
|
+
};
|
|
115
|
+
}
|
|
49
116
|
function normalizeGeneratorKind(value) {
|
|
50
117
|
return resolveGeneratorKind(value);
|
|
51
118
|
}
|
|
52
119
|
function isHelpFlag(value) {
|
|
53
120
|
return value === '--help' || value === '-h';
|
|
54
121
|
}
|
|
122
|
+
function isVersionCommand(value) {
|
|
123
|
+
return value === 'version' || value === '--version' || value === '-v';
|
|
124
|
+
}
|
|
125
|
+
function isCreationCommand(value) {
|
|
126
|
+
return value === 'new' || value === 'create';
|
|
127
|
+
}
|
|
128
|
+
function readCliVersion() {
|
|
129
|
+
const packageJsonPath = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
130
|
+
const manifest = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
131
|
+
if (typeof manifest !== 'object' || manifest === null || !('version' in manifest) || typeof manifest.version !== 'string') {
|
|
132
|
+
throw new Error('Unable to determine the installed fluo CLI version.');
|
|
133
|
+
}
|
|
134
|
+
return manifest.version;
|
|
135
|
+
}
|
|
55
136
|
function generateUsage() {
|
|
56
137
|
return ['Usage: fluo generate|g <kind> <name> [options]', ' fluo generate|g request-dto|req <feature> <name> [options]', '', 'Schematics', renderHelpTable(GENERATE_KIND_HELP, [{
|
|
57
138
|
header: 'Schematic',
|
|
@@ -173,6 +254,42 @@ function parseGenerateArgs(argv) {
|
|
|
173
254
|
}
|
|
174
255
|
function parseCommand(argv) {
|
|
175
256
|
const [command] = argv;
|
|
257
|
+
if (command === 'analyze') {
|
|
258
|
+
return {
|
|
259
|
+
argv: argv.slice(1),
|
|
260
|
+
command: 'analyze'
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (command === 'add') {
|
|
264
|
+
return {
|
|
265
|
+
argv: argv.slice(1),
|
|
266
|
+
command: 'add'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (command === 'doctor') {
|
|
270
|
+
return {
|
|
271
|
+
argv: argv.slice(1),
|
|
272
|
+
command: 'doctor'
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (command === 'info') {
|
|
276
|
+
return {
|
|
277
|
+
argv: argv.slice(1),
|
|
278
|
+
command: 'info'
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (command === 'build' || command === 'dev' || command === 'start') {
|
|
282
|
+
return {
|
|
283
|
+
argv: argv.slice(1),
|
|
284
|
+
command
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (command === 'upgrade') {
|
|
288
|
+
return {
|
|
289
|
+
argv: argv.slice(1),
|
|
290
|
+
command: 'upgrade'
|
|
291
|
+
};
|
|
292
|
+
}
|
|
176
293
|
if (command === 'new' || command === 'create') {
|
|
177
294
|
return {
|
|
178
295
|
argv: argv.slice(1),
|
|
@@ -225,14 +342,33 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
225
342
|
const stdout = runtime.stdout ?? process.stdout;
|
|
226
343
|
const stderr = runtime.stderr ?? process.stderr;
|
|
227
344
|
const env = runtime.env ?? process.env;
|
|
345
|
+
const commandRuntime = {
|
|
346
|
+
...runtime,
|
|
347
|
+
env
|
|
348
|
+
};
|
|
228
349
|
const updateFlagResult = removeUpdateCheckFlags(argv);
|
|
229
350
|
const commandArgv = updateFlagResult.argv;
|
|
230
351
|
try {
|
|
352
|
+
if (commandArgv[0] === NODE_DEV_RUNNER_COMMAND || commandArgv[0] === DEV_RUNNER_COMMAND) {
|
|
353
|
+
const runnerInvocation = parseDevRunnerInvocation(commandArgv);
|
|
354
|
+
return runNodeRestartRunner({
|
|
355
|
+
appArgs: runnerInvocation.appArgs,
|
|
356
|
+
env,
|
|
357
|
+
runtime: runnerInvocation.runtime,
|
|
358
|
+
stderr,
|
|
359
|
+
stdout
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
if (isVersionCommand(commandArgv[0])) {
|
|
363
|
+
stdout.write(`${readCliVersion()}\n`);
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
231
366
|
const updateCheckOptions = runtime.updateCheck === false ? undefined : runtime.updateCheck;
|
|
232
367
|
const updateCheckResult = await runCliUpdateCheck(commandArgv, {
|
|
233
368
|
...updateCheckOptions,
|
|
234
369
|
ci: runtime.ci,
|
|
235
370
|
env,
|
|
371
|
+
bypassCache: isCreationCommand(commandArgv[0]),
|
|
236
372
|
interactive: runtime.interactive,
|
|
237
373
|
skip: updateFlagResult.skipUpdateCheck || runtime.updateCheck === false,
|
|
238
374
|
stderr,
|
|
@@ -255,6 +391,26 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
255
391
|
stdout.write(`${generateUsage()}\n`);
|
|
256
392
|
return 0;
|
|
257
393
|
}
|
|
394
|
+
if (topic === 'doctor' || topic === 'info') {
|
|
395
|
+
stdout.write(`${diagnosticsUsage('doctor')}\n`);
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
if (topic === 'analyze') {
|
|
399
|
+
stdout.write(`${diagnosticsUsage('analyze')}\n`);
|
|
400
|
+
return 0;
|
|
401
|
+
}
|
|
402
|
+
if (topic === 'build' || topic === 'dev' || topic === 'start') {
|
|
403
|
+
stdout.write(`${scriptUsage(topic)}\n`);
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
if (topic === 'add') {
|
|
407
|
+
stdout.write(`${addUsage()}\n`);
|
|
408
|
+
return 0;
|
|
409
|
+
}
|
|
410
|
+
if (topic === 'upgrade') {
|
|
411
|
+
stdout.write(`${upgradeUsage()}\n`);
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
258
414
|
if (topic === 'migrate') {
|
|
259
415
|
stdout.write(`${migrateUsage()}\n`);
|
|
260
416
|
return 0;
|
|
@@ -274,6 +430,26 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
274
430
|
stdout.write(`${generateUsage()}\n`);
|
|
275
431
|
return 0;
|
|
276
432
|
}
|
|
433
|
+
if ((commandArgv[0] === 'doctor' || commandArgv[0] === 'info') && commandArgv.slice(1).some(isHelpFlag)) {
|
|
434
|
+
stdout.write(`${diagnosticsUsage('doctor')}\n`);
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
if (commandArgv[0] === 'analyze' && commandArgv.slice(1).some(isHelpFlag)) {
|
|
438
|
+
stdout.write(`${diagnosticsUsage('analyze')}\n`);
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
if ((commandArgv[0] === 'build' || commandArgv[0] === 'dev' || commandArgv[0] === 'start') && commandArgv.slice(1).some(isHelpFlag)) {
|
|
442
|
+
stdout.write(`${scriptUsage(commandArgv[0])}\n`);
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
if (commandArgv[0] === 'add' && commandArgv.slice(1).some(isHelpFlag)) {
|
|
446
|
+
stdout.write(`${addUsage()}\n`);
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
if (commandArgv[0] === 'upgrade' && commandArgv.slice(1).some(isHelpFlag)) {
|
|
450
|
+
stdout.write(`${upgradeUsage()}\n`);
|
|
451
|
+
return 0;
|
|
452
|
+
}
|
|
277
453
|
if (commandArgv[0] === 'migrate' && commandArgv.slice(1).some(isHelpFlag)) {
|
|
278
454
|
stdout.write(`${migrateUsage()}\n`);
|
|
279
455
|
return 0;
|
|
@@ -283,14 +459,35 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
283
459
|
return 0;
|
|
284
460
|
}
|
|
285
461
|
const parsedCommand = parseCommand(commandArgv);
|
|
462
|
+
if (parsedCommand.command === 'analyze') {
|
|
463
|
+
return runAnalyzeCommand(parsedCommand.argv, commandRuntime);
|
|
464
|
+
}
|
|
465
|
+
if (parsedCommand.command === 'add') {
|
|
466
|
+
return runAddCommand(parsedCommand.argv, commandRuntime);
|
|
467
|
+
}
|
|
468
|
+
if (parsedCommand.command === 'doctor') {
|
|
469
|
+
return runDoctorCommand(parsedCommand.argv, commandRuntime);
|
|
470
|
+
}
|
|
471
|
+
if (parsedCommand.command === 'info') {
|
|
472
|
+
return runInfoCommand(parsedCommand.argv, commandRuntime);
|
|
473
|
+
}
|
|
474
|
+
if (parsedCommand.command === 'build' || parsedCommand.command === 'dev' || parsedCommand.command === 'start') {
|
|
475
|
+
return runScriptCommand(parsedCommand.command, parsedCommand.argv, commandRuntime);
|
|
476
|
+
}
|
|
477
|
+
if (parsedCommand.command === 'upgrade') {
|
|
478
|
+
return runUpgradeCommand(parsedCommand.argv, commandRuntime);
|
|
479
|
+
}
|
|
286
480
|
if (parsedCommand.command === 'new') {
|
|
287
|
-
return runNewCommand(parsedCommand.argv,
|
|
481
|
+
return runNewCommand(parsedCommand.argv, commandRuntime);
|
|
288
482
|
}
|
|
289
483
|
if (parsedCommand.command === 'migrate') {
|
|
290
|
-
return runMigrateCommand(parsedCommand.argv,
|
|
484
|
+
return runMigrateCommand(parsedCommand.argv, commandRuntime);
|
|
291
485
|
}
|
|
292
486
|
if (parsedCommand.command === 'inspect') {
|
|
293
|
-
return runInspectCommand(parsedCommand.argv,
|
|
487
|
+
return runInspectCommand(parsedCommand.argv, commandRuntime);
|
|
488
|
+
}
|
|
489
|
+
if (parsedCommand.command !== 'generate') {
|
|
490
|
+
throw new Error(usage());
|
|
294
491
|
}
|
|
295
492
|
const targetDirectory = resolve(cwd, parsedCommand.parsed.targetDirectory ?? resolveDefaultTargetDirectory(cwd));
|
|
296
493
|
const result = runGenerateCommand(parsedCommand.parsed.kind, parsedCommand.parsed.name, targetDirectory, parsedCommand.parsed.options);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type CliStream = {
|
|
2
|
+
write(message: string): unknown;
|
|
3
|
+
};
|
|
4
|
+
type DiagnosticRuntimeOptions = {
|
|
5
|
+
cwd?: string;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
fetchDistTags?: (packageName: string) => Promise<Record<string, string> | undefined>;
|
|
8
|
+
stdout?: CliStream;
|
|
9
|
+
};
|
|
10
|
+
export declare function diagnosticsUsage(command?: 'analyze' | 'doctor' | 'info'): string;
|
|
11
|
+
export declare function runDoctorCommand(argv: string[], runtime?: DiagnosticRuntimeOptions): Promise<number>;
|
|
12
|
+
export declare function runInfoCommand(argv: string[], runtime?: DiagnosticRuntimeOptions): Promise<number>;
|
|
13
|
+
export declare function runAnalyzeCommand(argv: string[], runtime?: DiagnosticRuntimeOptions): Promise<number>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/commands/diagnostics.ts"],"names":[],"mappings":"AAKA,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACrF,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAuHF,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,SAAS,GAAG,QAAQ,GAAG,MAAiB,GAAG,MAAM,CAoB1F;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAgC9G;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAO5G;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB/G"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const DEFAULT_PACKAGE_NAME = '@fluojs/cli';
|
|
6
|
+
const DEFAULT_REGISTRY_TIMEOUT_MS = 5_000;
|
|
7
|
+
const EMPTY_ENV = {};
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === 'object' && value !== null;
|
|
10
|
+
}
|
|
11
|
+
function readJsonFile(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
14
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
15
|
+
} catch (_error) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function readCliVersion() {
|
|
20
|
+
const packageJsonPath = fileURLToPath(new URL('../../package.json', import.meta.url));
|
|
21
|
+
const manifest = readJsonFile(packageJsonPath);
|
|
22
|
+
return typeof manifest?.version === 'string' ? manifest.version : 'unknown';
|
|
23
|
+
}
|
|
24
|
+
function resolveCacheFile(env) {
|
|
25
|
+
const cacheRoot = env.XDG_CACHE_HOME ?? join(homedir(), '.cache');
|
|
26
|
+
return join(cacheRoot, 'fluo', 'cli-update-check.json');
|
|
27
|
+
}
|
|
28
|
+
function readUpdateCache(env) {
|
|
29
|
+
const cacheFile = resolveCacheFile(env);
|
|
30
|
+
const cache = readJsonFile(cacheFile);
|
|
31
|
+
const checkedAt = typeof cache?.checkedAt === 'number' ? cache.checkedAt : undefined;
|
|
32
|
+
return {
|
|
33
|
+
ageMs: checkedAt === undefined ? undefined : Date.now() - checkedAt,
|
|
34
|
+
checkedAt: checkedAt === undefined ? undefined : new Date(checkedAt).toISOString(),
|
|
35
|
+
latestVersion: typeof cache?.latestVersion === 'string' ? cache.latestVersion : undefined,
|
|
36
|
+
path: cacheFile
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function findProjectManifest(startDirectory) {
|
|
40
|
+
let current = resolve(startDirectory);
|
|
41
|
+
while (true) {
|
|
42
|
+
const candidate = join(current, 'package.json');
|
|
43
|
+
if (existsSync(candidate)) {
|
|
44
|
+
return {
|
|
45
|
+
manifest: readJsonFile(candidate),
|
|
46
|
+
path: candidate
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const parent = dirname(current);
|
|
50
|
+
if (parent === current) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
current = parent;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function fetchNpmDistTags(packageName) {
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timeout = setTimeout(() => controller.abort(), DEFAULT_REGISTRY_TIMEOUT_MS);
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(`https://registry.npmjs.org/-/package/${encodeURIComponent(packageName)}/dist-tags`, {
|
|
61
|
+
headers: {
|
|
62
|
+
accept: 'application/json'
|
|
63
|
+
},
|
|
64
|
+
signal: controller.signal
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const payload = await response.json();
|
|
70
|
+
if (!isRecord(payload)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const distTags = {};
|
|
74
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
75
|
+
if (typeof value === 'string') {
|
|
76
|
+
distTags[key] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return distTags;
|
|
80
|
+
} catch (_error) {
|
|
81
|
+
return undefined;
|
|
82
|
+
} finally {
|
|
83
|
+
clearTimeout(timeout);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function formatAge(ageMs) {
|
|
87
|
+
if (ageMs === undefined) {
|
|
88
|
+
return 'unknown';
|
|
89
|
+
}
|
|
90
|
+
const minutes = Math.floor(ageMs / 60_000);
|
|
91
|
+
if (minutes < 60) {
|
|
92
|
+
return `${minutes}m`;
|
|
93
|
+
}
|
|
94
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
95
|
+
}
|
|
96
|
+
function listScripts(manifest) {
|
|
97
|
+
const scripts = manifest?.scripts;
|
|
98
|
+
if (!isRecord(scripts)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return Object.keys(scripts).sort();
|
|
102
|
+
}
|
|
103
|
+
export function diagnosticsUsage(command = 'doctor') {
|
|
104
|
+
if (command === 'analyze') {
|
|
105
|
+
return ['Usage: fluo analyze [options]', '', 'Summarize the current project and point to deeper inspect/report diagnostics.', '', 'Options', ' --help Show help for the analyze command.'].join('\n');
|
|
106
|
+
}
|
|
107
|
+
return [`Usage: fluo ${command} [options]`, '', 'Print CLI, registry, update-cache, runtime, and project diagnostics.', '', 'Options', ` --help Show help for the ${command} command.`].join('\n');
|
|
108
|
+
}
|
|
109
|
+
export async function runDoctorCommand(argv, runtime = {}) {
|
|
110
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
111
|
+
(runtime.stdout ?? process.stdout).write(`${diagnosticsUsage('doctor')}\n`);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
if (argv.length > 0) {
|
|
115
|
+
throw new Error(`Unknown doctor option: ${argv[0]}`);
|
|
116
|
+
}
|
|
117
|
+
const env = runtime.env ?? EMPTY_ENV;
|
|
118
|
+
const stdout = runtime.stdout ?? process.stdout;
|
|
119
|
+
const cwd = resolve(runtime.cwd ?? process.cwd());
|
|
120
|
+
const cache = readUpdateCache(env);
|
|
121
|
+
const distTags = await (runtime.fetchDistTags ?? fetchNpmDistTags)(DEFAULT_PACKAGE_NAME);
|
|
122
|
+
const project = findProjectManifest(cwd);
|
|
123
|
+
const scripts = listScripts(project.manifest);
|
|
124
|
+
stdout.write('fluo doctor\n');
|
|
125
|
+
stdout.write(` CLI version: ${readCliVersion()}\n`);
|
|
126
|
+
stdout.write(` Node.js: ${process.version}\n`);
|
|
127
|
+
stdout.write(` Platform: ${process.platform}/${process.arch}\n`);
|
|
128
|
+
stdout.write(` Package manager signal: ${env.npm_config_user_agent ?? 'unknown'}\n`);
|
|
129
|
+
stdout.write(` npm latest: ${distTags?.latest ?? 'unavailable'}\n`);
|
|
130
|
+
stdout.write(` npm beta: ${distTags?.beta ?? 'unavailable'}\n`);
|
|
131
|
+
stdout.write(` Update cache: ${cache.path}\n`);
|
|
132
|
+
stdout.write(` Cached latest: ${cache.latestVersion ?? 'none'}\n`);
|
|
133
|
+
stdout.write(` Cache checked: ${cache.checkedAt ?? 'never'} (${formatAge(cache.ageMs)} ago)\n`);
|
|
134
|
+
stdout.write(` Project manifest: ${project.path ?? 'not found'}\n`);
|
|
135
|
+
stdout.write(` Project scripts: ${scripts.length > 0 ? scripts.join(', ') : 'none'}\n`);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
export async function runInfoCommand(argv, runtime = {}) {
|
|
139
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
140
|
+
(runtime.stdout ?? process.stdout).write(`${diagnosticsUsage('info')}\n`);
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
return runDoctorCommand(argv, runtime);
|
|
144
|
+
}
|
|
145
|
+
export async function runAnalyzeCommand(argv, runtime = {}) {
|
|
146
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
147
|
+
(runtime.stdout ?? process.stdout).write(`${diagnosticsUsage('analyze')}\n`);
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
if (argv.length > 0) {
|
|
151
|
+
throw new Error(`Unknown analyze option: ${argv[0]}`);
|
|
152
|
+
}
|
|
153
|
+
const stdout = runtime.stdout ?? process.stdout;
|
|
154
|
+
const cwd = resolve(runtime.cwd ?? process.cwd());
|
|
155
|
+
const project = findProjectManifest(cwd);
|
|
156
|
+
const scripts = listScripts(project.manifest);
|
|
157
|
+
stdout.write('fluo analyze\n');
|
|
158
|
+
stdout.write(` Project manifest: ${project.path ?? 'not found'}\n`);
|
|
159
|
+
stdout.write(` Available scripts: ${scripts.length > 0 ? scripts.join(', ') : 'none'}\n`);
|
|
160
|
+
stdout.write(' Deep inspection: run `fluo inspect <module-path> --report --output <file>` for runtime graph diagnostics.\n');
|
|
161
|
+
stdout.write(' Migration preview: run `fluo migrate <path> --json` for codemod diagnostics.\n');
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
package/dist/commands/new.js
CHANGED
|
@@ -264,7 +264,7 @@ export function newUsage() {
|
|
|
264
264
|
}, {
|
|
265
265
|
header: 'Description',
|
|
266
266
|
render: entry => entry.description
|
|
267
|
-
}]), '', 'Next steps:', ' cd <app-name>', ' pnpm dev #
|
|
267
|
+
}]), '', 'Next steps:', ' cd <app-name>', ' pnpm dev # runs fluo dev from the generated package.json script', '', 'Docs: https://github.com/fluojs/fluo/tree/main/docs/getting-started/quick-start.md'].join('\n');
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
/**
|
|
@@ -364,7 +364,7 @@ export async function runNewCommand(argv, runtime = {}) {
|
|
|
364
364
|
clackLog.step('Dependency installation skipped');
|
|
365
365
|
}
|
|
366
366
|
stdout.write('Done.\n');
|
|
367
|
-
stdout.write(`Next steps:\n cd ${answers.targetDirectory}\n ${answers.packageManager === 'npm' ? 'npm run dev' : answers.packageManager === 'bun' ? 'bun run dev' : `${answers.packageManager} dev`}\n`);
|
|
367
|
+
stdout.write(`Next steps:\n cd ${answers.targetDirectory}\n ${answers.packageManager === 'npm' ? 'npm run dev' : answers.packageManager === 'bun' ? 'bun run dev' : `${answers.packageManager} dev`} # runs fluo dev\n`);
|
|
368
368
|
return 0;
|
|
369
369
|
} catch (error) {
|
|
370
370
|
if (isCliPromptCancelledError(error)) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type JsonRecord = Record<string, unknown>;
|
|
2
|
+
export declare const SUPPORTED_PACKAGE_MANAGERS: Set<string>;
|
|
3
|
+
export declare function detectPackageManager(options: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
env: NodeJS.ProcessEnv;
|
|
6
|
+
manifest?: JsonRecord;
|
|
7
|
+
}): string;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=package-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../../src/commands/package-manager.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,eAAO,MAAM,0BAA0B,aAA0C,CAAC;AA+ClF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,MAAM,CA0BpH"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
export const SUPPORTED_PACKAGE_MANAGERS = new Set(['bun', 'npm', 'pnpm', 'yarn']);
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null;
|
|
6
|
+
}
|
|
7
|
+
function readJsonFile(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
10
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
11
|
+
} catch (_error) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function packageManagerFromManifest(manifest) {
|
|
16
|
+
const packageManager = manifest.packageManager;
|
|
17
|
+
if (typeof packageManager !== 'string') {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const manager = packageManager.split('@')[0];
|
|
21
|
+
return manager && SUPPORTED_PACKAGE_MANAGERS.has(manager) ? manager : undefined;
|
|
22
|
+
}
|
|
23
|
+
function packageManagerFromLockfile(directory) {
|
|
24
|
+
if (existsSync(join(directory, 'pnpm-lock.yaml'))) {
|
|
25
|
+
return 'pnpm';
|
|
26
|
+
}
|
|
27
|
+
if (existsSync(join(directory, 'bun.lockb')) || existsSync(join(directory, 'bun.lock'))) {
|
|
28
|
+
return 'bun';
|
|
29
|
+
}
|
|
30
|
+
if (existsSync(join(directory, 'yarn.lock'))) {
|
|
31
|
+
return 'yarn';
|
|
32
|
+
}
|
|
33
|
+
if (existsSync(join(directory, 'package-lock.json')) || existsSync(join(directory, 'npm-shrinkwrap.json'))) {
|
|
34
|
+
return 'npm';
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
function packageManagerFromUserAgent(env) {
|
|
39
|
+
const userAgentName = env.npm_config_user_agent?.split(' ')[0]?.split('/')[0];
|
|
40
|
+
return userAgentName && SUPPORTED_PACKAGE_MANAGERS.has(userAgentName) ? userAgentName : undefined;
|
|
41
|
+
}
|
|
42
|
+
export function detectPackageManager(options) {
|
|
43
|
+
const startDirectory = resolve(options.cwd);
|
|
44
|
+
let current = startDirectory;
|
|
45
|
+
while (true) {
|
|
46
|
+
const manifestPath = join(current, 'package.json');
|
|
47
|
+
const manifest = current === startDirectory && options.manifest ? options.manifest : existsSync(manifestPath) ? readJsonFile(manifestPath) : undefined;
|
|
48
|
+
const manifestManager = manifest ? packageManagerFromManifest(manifest) : undefined;
|
|
49
|
+
if (manifestManager) {
|
|
50
|
+
return manifestManager;
|
|
51
|
+
}
|
|
52
|
+
const lockfileManager = packageManagerFromLockfile(current);
|
|
53
|
+
if (lockfileManager) {
|
|
54
|
+
return lockfileManager;
|
|
55
|
+
}
|
|
56
|
+
const parent = dirname(current);
|
|
57
|
+
if (parent === current) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
current = parent;
|
|
61
|
+
}
|
|
62
|
+
return packageManagerFromUserAgent(options.env) ?? 'pnpm';
|
|
63
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type CliStream = {
|
|
2
|
+
write(message: string): unknown;
|
|
3
|
+
};
|
|
4
|
+
type PackageWorkflowRuntimeOptions = {
|
|
5
|
+
cwd?: string;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
fetchDistTags?: (packageName: string) => Promise<Record<string, string> | undefined>;
|
|
8
|
+
spawnCommand?: (command: string, args: string[], options: {
|
|
9
|
+
cwd: string;
|
|
10
|
+
env: NodeJS.ProcessEnv;
|
|
11
|
+
stdio: 'inherit';
|
|
12
|
+
}) => Promise<number>;
|
|
13
|
+
stdout?: CliStream;
|
|
14
|
+
};
|
|
15
|
+
export declare function addUsage(): string;
|
|
16
|
+
export declare function upgradeUsage(): string;
|
|
17
|
+
export declare function runAddCommand(argv: string[], runtime?: PackageWorkflowRuntimeOptions): Promise<number>;
|
|
18
|
+
export declare function runUpgradeCommand(argv: string[], runtime?: PackageWorkflowRuntimeOptions): Promise<number>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=package-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-workflow.d.ts","sourceRoot":"","sources":["../../src/commands/package-workflow.ts"],"names":[],"mappings":"AAGA,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACrF,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxI,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAgFF,wBAAgB,QAAQ,IAAI,MAAM,CAYjC;AAED,wBAAgB,YAAY,IAAI,MAAM,CASrC;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,6BAAkC,GAAG,OAAO,CAAC,MAAM,CAAC,CAkDhH;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,6BAAkC,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBpH"}
|