@fluojs/cli 1.1.0 → 3.0.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/README.ko.md +183 -22
- package/README.md +186 -22
- package/dist/cli.d.ts +5 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +128 -35
- package/dist/commands/diagnostics.d.ts +35 -0
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +60 -0
- package/dist/commands/generate.d.ts +1 -49
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +1 -214
- package/dist/commands/inspect.d.ts +5 -7
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +85 -65
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +27 -15
- package/dist/commands/migration-transform-tokens.d.ts +15 -0
- package/dist/commands/migration-transform-tokens.d.ts.map +1 -0
- package/dist/commands/migration-transform-tokens.js +41 -0
- package/dist/commands/new.d.ts +0 -6
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +42 -96
- package/dist/commands/output-path-safety.d.ts +14 -0
- package/dist/commands/output-path-safety.d.ts.map +1 -0
- package/dist/commands/output-path-safety.js +34 -0
- package/dist/commands/scripts.d.ts +1 -1
- package/dist/commands/scripts.d.ts.map +1 -1
- package/dist/commands/scripts.js +13 -7
- package/dist/commands/typegen-artifact.d.ts +54 -0
- package/dist/commands/typegen-artifact.d.ts.map +1 -0
- package/dist/commands/typegen-artifact.js +117 -0
- package/dist/commands/typegen-generation-child.d.ts +2 -0
- package/dist/commands/typegen-generation-child.d.ts.map +1 -0
- package/dist/commands/typegen-generation-child.js +59 -0
- package/dist/commands/typegen-generation-process.d.ts +46 -0
- package/dist/commands/typegen-generation-process.d.ts.map +1 -0
- package/dist/commands/typegen-generation-process.js +131 -0
- package/dist/commands/typegen-generation-protocol.d.ts +16 -0
- package/dist/commands/typegen-generation-protocol.d.ts.map +1 -0
- package/dist/commands/typegen-generation-protocol.js +35 -0
- package/dist/commands/typegen-isolated-source.d.ts +10 -0
- package/dist/commands/typegen-isolated-source.d.ts.map +1 -0
- package/dist/commands/typegen-isolated-source.js +75 -0
- package/dist/commands/typegen-options.d.ts +20 -0
- package/dist/commands/typegen-options.d.ts.map +1 -0
- package/dist/commands/typegen-options.js +71 -0
- package/dist/commands/typegen-source.d.ts +59 -0
- package/dist/commands/typegen-source.d.ts.map +1 -0
- package/dist/commands/typegen-source.js +183 -0
- package/dist/commands/typegen-watch.d.ts +46 -0
- package/dist/commands/typegen-watch.d.ts.map +1 -0
- package/dist/commands/typegen-watch.js +212 -0
- package/dist/commands/typegen.d.ts +29 -0
- package/dist/commands/typegen.d.ts.map +1 -0
- package/dist/commands/typegen.js +119 -0
- package/dist/dev-runner/node-restart-runner.d.ts +6 -0
- package/dist/dev-runner/node-restart-runner.d.ts.map +1 -1
- package/dist/dev-runner/node-restart-runner.js +64 -13
- package/dist/fixtures/inspect-react-app.module.d.ts +4 -0
- package/dist/fixtures/inspect-react-app.module.d.ts.map +1 -0
- package/dist/fixtures/inspect-react-app.module.js +32 -0
- package/dist/fixtures/typegen-react-app.module.d.ts +4 -0
- package/dist/fixtures/typegen-react-app.module.d.ts.map +1 -0
- package/dist/fixtures/typegen-react-app.module.js +33 -0
- package/dist/generate-command.d.ts +50 -0
- package/dist/generate-command.d.ts.map +1 -0
- package/dist/generate-command.js +214 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/new/install.js +1 -1
- package/dist/new/package-spec-resolver.d.ts.map +1 -1
- package/dist/new/package-spec-resolver.js +50 -59
- package/dist/new/prompt.d.ts +1 -0
- package/dist/new/prompt.d.ts.map +1 -1
- package/dist/new/prompt.js +40 -12
- package/dist/new/published-internal-dependencies.d.ts +20 -0
- package/dist/new/published-internal-dependencies.d.ts.map +1 -0
- package/dist/new/published-internal-dependencies.js +20 -0
- package/dist/new/react-vite-ssr-scaffold.d.ts +19 -0
- package/dist/new/react-vite-ssr-scaffold.d.ts.map +1 -0
- package/dist/new/react-vite-ssr-scaffold.js +108 -0
- package/dist/new/resolver.d.ts +2 -2
- package/dist/new/resolver.d.ts.map +1 -1
- package/dist/new/resolver.js +5 -1
- package/dist/new/scaffold.d.ts.map +1 -1
- package/dist/new/scaffold.js +177 -54
- package/dist/new/starter-profiles.d.ts +8 -4
- package/dist/new/starter-profiles.d.ts.map +1 -1
- package/dist/new/starter-profiles.js +52 -18
- package/dist/new/templates/react-vite-ssr/README.md.ejs +74 -0
- package/dist/new/templates/react-vite-ssr/playwright.config.ts.ejs +26 -0
- package/dist/new/templates/react-vite-ssr/src/app.test.ts.ejs +82 -0
- package/dist/new/templates/react-vite-ssr/src/app.ts.ejs +91 -0
- package/dist/new/templates/react-vite-ssr/src/entry-client.tsx.ejs +29 -0
- package/dist/new/templates/react-vite-ssr/src/entry-server.tsx.ejs +52 -0
- package/dist/new/templates/react-vite-ssr/src/load-manifest.test.ts.ejs +55 -0
- package/dist/new/templates/react-vite-ssr/src/load-manifest.ts.ejs +53 -0
- package/dist/new/templates/react-vite-ssr/src/main.ts.ejs +18 -0
- package/dist/new/templates/react-vite-ssr/src/page.tsx.ejs +43 -0
- package/dist/new/templates/react-vite-ssr/src/react-app.test.tsx.ejs +67 -0
- package/dist/new/templates/react-vite-ssr/src/react-app.tsx.ejs +46 -0
- package/dist/new/templates/react-vite-ssr/src/styles.css.ejs +25 -0
- package/dist/new/templates/react-vite-ssr/src/styles.d.ts.ejs +1 -0
- package/dist/new/templates/react-vite-ssr/tests/production-hydration.spec.ts.ejs +54 -0
- package/dist/new/templates/react-vite-ssr/tsconfig.json.ejs +16 -0
- package/dist/new/templates/react-vite-ssr/vite.client.config.ts.ejs +24 -0
- package/dist/new/templates/react-vite-ssr/vite.server.config.ts.ejs +17 -0
- package/dist/new/templates/react-vite-ssr/vitest.config.ts.ejs +10 -0
- package/dist/new/types.d.ts +4 -2
- package/dist/new/types.d.ts.map +1 -1
- package/dist/public-generate.d.ts +2 -0
- package/dist/public-generate.d.ts.map +1 -0
- package/dist/public-generate.js +1 -0
- package/dist/public-inspect.d.ts +13 -0
- package/dist/public-inspect.d.ts.map +1 -0
- package/dist/public-inspect.js +16 -0
- package/dist/public-new.d.ts +13 -0
- package/dist/public-new.d.ts.map +1 -0
- package/dist/public-new.js +16 -0
- package/dist/public-typegen.d.ts +14 -0
- package/dist/public-typegen.d.ts.map +1 -0
- package/dist/public-typegen.js +17 -0
- package/dist/run-cli.d.ts +2 -1
- package/dist/run-cli.d.ts.map +1 -1
- package/dist/studio/sidecar.d.ts.map +1 -1
- package/dist/studio/sidecar.js +106 -14
- package/dist/transforms/nestjs-migrate.d.ts +4 -1
- package/dist/transforms/nestjs-migrate.d.ts.map +1 -1
- package/dist/transforms/nestjs-migrate.js +625 -65
- package/dist/typegen-contract.d.ts +10 -0
- package/dist/typegen-contract.d.ts.map +1 -0
- package/dist/typegen-contract.js +9 -0
- package/dist/usage.d.ts +19 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +178 -0
- package/package.json +19 -10
|
@@ -1,214 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { basename, dirname, join, normalize, relative, resolve, sep } from 'node:path';
|
|
3
|
-
import { findGeneratorDefinition } from '../generators/manifest.js';
|
|
4
|
-
import { ensureModuleImport, generateModuleFiles, registerInModule } from '../generators/module.js';
|
|
5
|
-
import { toKebabCase, toPascalCase, toPlural } from '../generators/utils.js';
|
|
6
|
-
function writeFileIfChanged(filePath, content) {
|
|
7
|
-
if (existsSync(filePath) && readFileSync(filePath, 'utf8') === content) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
writeFileSync(filePath, content, 'utf8');
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Describes how one generated artifact would interact with the workspace. */
|
|
15
|
-
|
|
16
|
-
/** One path-level action reported by generate dry-run previews and structured results. */
|
|
17
|
-
|
|
18
|
-
function planFileWrite(filePath, content, options) {
|
|
19
|
-
if (!existsSync(filePath)) {
|
|
20
|
-
return {
|
|
21
|
-
action: 'create',
|
|
22
|
-
path: filePath
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
if (!options.force) {
|
|
26
|
-
return {
|
|
27
|
-
action: 'skip',
|
|
28
|
-
path: filePath
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
if (readFileSync(filePath, 'utf8') === content) {
|
|
32
|
-
return {
|
|
33
|
-
action: 'unchanged',
|
|
34
|
-
path: filePath
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
action: 'overwrite',
|
|
39
|
-
path: filePath
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function planModuleWrite(modulePath, content) {
|
|
43
|
-
if (!existsSync(modulePath)) {
|
|
44
|
-
return {
|
|
45
|
-
action: 'module-create',
|
|
46
|
-
path: modulePath
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
if (readFileSync(modulePath, 'utf8') === content) {
|
|
50
|
-
return {
|
|
51
|
-
action: 'module-unchanged',
|
|
52
|
-
path: modulePath
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
action: 'module-update',
|
|
57
|
-
path: modulePath
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function createGeneratorOptions(kind, domainDirectory, kebab, options, resolvedBase) {
|
|
61
|
-
return {
|
|
62
|
-
...options,
|
|
63
|
-
e2eRootModuleImport: options.e2eRootModuleImport ?? (kind === 'e2e' ? resolveE2eRootModuleImport(domainDirectory, resolvedBase) : undefined),
|
|
64
|
-
hasRepo: options.hasRepo ?? (kind === 'service' ? existsSync(join(domainDirectory, `${kebab}.repo.ts`)) : undefined),
|
|
65
|
-
hasService: options.hasService ?? (kind === 'controller' ? existsSync(join(domainDirectory, `${kebab}.service.ts`)) : undefined)
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function toImportSpecifier(path) {
|
|
69
|
-
const normalized = path.split(sep).join('/');
|
|
70
|
-
return normalized.startsWith('.') ? normalized : `./${normalized}`;
|
|
71
|
-
}
|
|
72
|
-
function resolveE2eRootModuleImport(domainDirectory, resolvedBase) {
|
|
73
|
-
return toImportSpecifier(relative(domainDirectory, join(resolvedBase, 'app')));
|
|
74
|
-
}
|
|
75
|
-
function resolveDomainDirectory(kind, resolvedBase, kebab, options) {
|
|
76
|
-
if (kind === 'e2e') {
|
|
77
|
-
return basename(resolvedBase) === 'src' ? join(dirname(resolvedBase), 'test') : join(resolvedBase, 'test');
|
|
78
|
-
}
|
|
79
|
-
if (kind === 'request-dto' && options.targetFeature !== undefined) {
|
|
80
|
-
const normalizedFeature = options.targetFeature.trim();
|
|
81
|
-
const featureKebab = assertValidResourceName(normalizedFeature);
|
|
82
|
-
const featureDirectory = /^[A-Z]/u.test(normalizedFeature) ? toPlural(featureKebab) : featureKebab;
|
|
83
|
-
return join(resolvedBase, featureDirectory);
|
|
84
|
-
}
|
|
85
|
-
return join(resolvedBase, toPlural(kebab));
|
|
86
|
-
}
|
|
87
|
-
function assertValidResourceName(name) {
|
|
88
|
-
const kebab = toKebabCase(name);
|
|
89
|
-
if (name.trim().length === 0) {
|
|
90
|
-
throw new Error('Invalid resource name: name must not be empty.');
|
|
91
|
-
}
|
|
92
|
-
if (kebab !== normalize(kebab) || kebab.includes('/') || kebab.includes('\\') || kebab.includes('..')) {
|
|
93
|
-
throw new Error(`Invalid resource name "${name}": must not contain path separators or traversal sequences.`);
|
|
94
|
-
}
|
|
95
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(kebab)) {
|
|
96
|
-
throw new Error(`Invalid resource name "${name}": use letters, numbers, spaces, underscores, or hyphens only.`);
|
|
97
|
-
}
|
|
98
|
-
return kebab;
|
|
99
|
-
}
|
|
100
|
-
function resolveModulePath(domainDirectory, name) {
|
|
101
|
-
const kebab = toKebabCase(name);
|
|
102
|
-
return join(domainDirectory, `${kebab}.module.ts`);
|
|
103
|
-
}
|
|
104
|
-
function readOrCreateModuleSource(modulePath, name) {
|
|
105
|
-
if (existsSync(modulePath)) {
|
|
106
|
-
return readFileSync(modulePath, 'utf8');
|
|
107
|
-
}
|
|
108
|
-
const [moduleFile] = generateModuleFiles(name);
|
|
109
|
-
if (!moduleFile) {
|
|
110
|
-
throw new Error(`Unable to generate module file for resource "${name}".`);
|
|
111
|
-
}
|
|
112
|
-
return moduleFile.content;
|
|
113
|
-
}
|
|
114
|
-
function buildUpdatedModuleSource(moduleSource, arrayKey, className, importPath) {
|
|
115
|
-
let source = moduleSource;
|
|
116
|
-
source = ensureModuleImport(source, className, importPath);
|
|
117
|
-
source = registerInModule(source, arrayKey, className);
|
|
118
|
-
return source;
|
|
119
|
-
}
|
|
120
|
-
function prepareModuleUpdate(domainDirectory, normalizedName, kind, classSuffix, arrayKey) {
|
|
121
|
-
const kebab = toKebabCase(normalizedName);
|
|
122
|
-
const modulePath = resolveModulePath(domainDirectory, normalizedName);
|
|
123
|
-
const className = `${toPascalCase(normalizedName)}${classSuffix}`;
|
|
124
|
-
const importPath = `${kebab}.${kind}`;
|
|
125
|
-
const moduleSource = readOrCreateModuleSource(modulePath, normalizedName);
|
|
126
|
-
return {
|
|
127
|
-
modulePath,
|
|
128
|
-
source: buildUpdatedModuleSource(moduleSource, arrayKey, className, importPath)
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Structured result returned by {@link runGenerateCommand} for tooling-friendly automation.
|
|
134
|
-
*
|
|
135
|
-
* `generatedFiles` only includes files whose on-disk content changed during the command.
|
|
136
|
-
* `moduleRegistered` reports whether the target schematic participates in automatic module wiring,
|
|
137
|
-
* even when the target module file was already up to date.
|
|
138
|
-
*/
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Generates one CLI schematic into a source directory and returns structured wiring metadata.
|
|
142
|
-
*
|
|
143
|
-
* The command keeps generation idempotent where possible: unchanged files are not rewritten, and
|
|
144
|
-
* auto-registered schematics reuse an existing module file when it already contains the required import
|
|
145
|
-
* and registration entry.
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
* ```ts
|
|
149
|
-
* const result = runGenerateCommand('service', 'Post', './src');
|
|
150
|
-
*
|
|
151
|
-
* console.log(result.wiringBehavior);
|
|
152
|
-
* console.log(result.nextStepHint);
|
|
153
|
-
* ```
|
|
154
|
-
*
|
|
155
|
-
* @param kind Generator kind to execute.
|
|
156
|
-
* @param name Resource name supplied by the caller before normalization.
|
|
157
|
-
* @param baseDirectory Source directory that should receive the generated domain folder.
|
|
158
|
-
* @param options Optional generation flags that control overwrites, request DTO feature placement, and sibling-aware templates.
|
|
159
|
-
* @returns Structured file and wiring metadata for the completed generation run.
|
|
160
|
-
* @throws {Error} When the resource name is invalid, the generator kind is unknown, or the target module source cannot be updated safely.
|
|
161
|
-
*/
|
|
162
|
-
export function runGenerateCommand(kind, name, baseDirectory, options = {}) {
|
|
163
|
-
const normalizedName = name.trim();
|
|
164
|
-
const kebab = assertValidResourceName(normalizedName);
|
|
165
|
-
const generator = findGeneratorDefinition(kind);
|
|
166
|
-
const resolvedBase = resolve(baseDirectory);
|
|
167
|
-
const domainDirectory = resolveDomainDirectory(kind, resolvedBase, kebab, options);
|
|
168
|
-
const generatorOptions = createGeneratorOptions(kind, domainDirectory, kebab, options, resolvedBase);
|
|
169
|
-
const files = generator.factory(normalizedName, generatorOptions);
|
|
170
|
-
const moduleRegistration = 'moduleRegistration' in generator ? generator.moduleRegistration : undefined;
|
|
171
|
-
const moduleUpdate = moduleRegistration ? prepareModuleUpdate(domainDirectory, normalizedName, kind, moduleRegistration.classSuffix, moduleRegistration.arrayKey) : undefined;
|
|
172
|
-
const plannedFiles = files.map(file => planFileWrite(join(domainDirectory, file.path), file.content, options));
|
|
173
|
-
const modulePlan = moduleUpdate ? planModuleWrite(moduleUpdate.modulePath, moduleUpdate.source) : undefined;
|
|
174
|
-
if (options.dryRun) {
|
|
175
|
-
return {
|
|
176
|
-
generatedFiles: [],
|
|
177
|
-
moduleRegistered: moduleUpdate !== undefined,
|
|
178
|
-
modulePath: moduleUpdate?.modulePath,
|
|
179
|
-
nextStepHint: generator.nextStepHint,
|
|
180
|
-
plannedFiles: modulePlan ? [...plannedFiles, modulePlan] : plannedFiles,
|
|
181
|
-
wiringBehavior: generator.wiringBehavior
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
mkdirSync(domainDirectory, {
|
|
185
|
-
recursive: true
|
|
186
|
-
});
|
|
187
|
-
const writtenPaths = files.map(file => {
|
|
188
|
-
const filePath = join(domainDirectory, file.path);
|
|
189
|
-
if (!options.force && existsSync(filePath)) {
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
return writeFileIfChanged(filePath, file.content) ? filePath : null;
|
|
193
|
-
}).filter(filePath => filePath !== null);
|
|
194
|
-
let moduleRegistered = false;
|
|
195
|
-
let resolvedModulePath;
|
|
196
|
-
if (moduleUpdate && writeFileIfChanged(moduleUpdate.modulePath, moduleUpdate.source)) {
|
|
197
|
-
moduleRegistered = true;
|
|
198
|
-
resolvedModulePath = moduleUpdate.modulePath;
|
|
199
|
-
if (!writtenPaths.includes(moduleUpdate.modulePath)) {
|
|
200
|
-
writtenPaths.push(moduleUpdate.modulePath);
|
|
201
|
-
}
|
|
202
|
-
} else if (moduleUpdate) {
|
|
203
|
-
moduleRegistered = true;
|
|
204
|
-
resolvedModulePath = moduleUpdate.modulePath;
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
generatedFiles: writtenPaths,
|
|
208
|
-
moduleRegistered: moduleRegistered,
|
|
209
|
-
modulePath: resolvedModulePath,
|
|
210
|
-
nextStepHint: generator.nextStepHint,
|
|
211
|
-
plannedFiles: modulePlan ? [...plannedFiles, modulePlan] : plannedFiles,
|
|
212
|
-
wiringBehavior: generator.wiringBehavior
|
|
213
|
-
};
|
|
214
|
-
}
|
|
1
|
+
export { runGenerateCommand } from '../generate-command.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PlatformShellSnapshot } from '@fluojs/runtime';
|
|
2
2
|
type CliStream = {
|
|
3
3
|
write(message: string): unknown;
|
|
4
4
|
};
|
|
@@ -11,6 +11,8 @@ type ReadableStream = {
|
|
|
11
11
|
};
|
|
12
12
|
type StudioMermaidRenderer = (snapshot: PlatformShellSnapshot) => string;
|
|
13
13
|
type StudioMermaidRendererLoader = (cwd: string) => Promise<StudioMermaidRenderer | undefined>;
|
|
14
|
+
type RuntimeInspectionModule = Pick<typeof import('@fluojs/runtime'), 'createRuntimeInspectionSnapshot' | 'FluoFactory' | 'PLATFORM_SHELL'>;
|
|
15
|
+
type RuntimeInspectionModuleLoader = (cwd: string) => Promise<RuntimeInspectionModule | undefined>;
|
|
14
16
|
/**
|
|
15
17
|
* Runtime options for the inspect command when used programmatically.
|
|
16
18
|
*/
|
|
@@ -21,6 +23,8 @@ export interface InspectCommandRuntimeOptions {
|
|
|
21
23
|
cwd?: string;
|
|
22
24
|
/** Force or disable interactive prompts for optional Studio guidance. */
|
|
23
25
|
interactive?: boolean;
|
|
26
|
+
/** Optional test/editor hook for resolving the runtime inspection dependency. */
|
|
27
|
+
loadRuntimeInspectionModule?: RuntimeInspectionModuleLoader;
|
|
24
28
|
/** Optional test/editor hook for resolving Studio's Mermaid renderer. */
|
|
25
29
|
loadStudioMermaidRenderer?: StudioMermaidRendererLoader;
|
|
26
30
|
/** Custom prompt implementation used only when Studio is missing for Mermaid output. */
|
|
@@ -32,12 +36,6 @@ export interface InspectCommandRuntimeOptions {
|
|
|
32
36
|
/** Custom stream for standard output. */
|
|
33
37
|
stdout?: CliStream;
|
|
34
38
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Returns the usage information string for the inspect command.
|
|
37
|
-
*
|
|
38
|
-
* @returns Formatted help text including usage and options.
|
|
39
|
-
*/
|
|
40
|
-
export declare function inspectUsage(): string;
|
|
41
39
|
/**
|
|
42
40
|
* Executes the inspect command to visualize the application module graph.
|
|
43
41
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/commands/inspect.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/commands/inspect.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAKV,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AAQzB,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,qBAAqB,GAAG,CAAC,QAAQ,EAAE,qBAAqB,KAAK,MAAM,CAAC;AAEzE,KAAK,2BAA2B,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAC;AAE/F,KAAK,uBAAuB,GAAG,IAAI,CACjC,cAAc,iBAAiB,CAAC,EAChC,iCAAiC,GAAG,aAAa,GAAG,gBAAgB,CACrE,CAAC;AAEF,KAAK,6BAA6B,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAAC;AAEnG;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,yEAAyE;IACzE,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iFAAiF;IACjF,2BAA2B,CAAC,EAAE,6BAA6B,CAAC;IAC5D,yEAAyE;IACzE,yBAAyB,CAAC,EAAE,2BAA2B,CAAC;IACxD,wFAAwF;IACxF,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,sCAAsC;IACtC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,yCAAyC;IACzC,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AA2WD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DnH"}
|
package/dist/commands/inspect.js
CHANGED
|
@@ -1,70 +1,27 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { createRequire } from 'node:module';
|
|
3
4
|
import { dirname, extname, resolve } from 'node:path';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import * as clack from '@clack/prompts';
|
|
6
|
-
import { FluoFactory, PLATFORM_SHELL } from '@fluojs/runtime';
|
|
7
6
|
import { tsImport } from 'tsx/esm/api';
|
|
8
|
-
import { renderAliasList, renderHelpTable } from '../help.js';
|
|
9
7
|
import { CliPromptCancelledError, isCliPromptCancelledError } from '../prompt-cancel.js';
|
|
8
|
+
import { inspectUsage } from '../usage.js';
|
|
9
|
+
import { createCliDiagnosticsLogger } from './diagnostics.js';
|
|
10
|
+
import { assertOutputDoesNotAliasModule } from './output-path-safety.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Runtime options for the inspect command when used programmatically.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
|
-
const INSPECT_OPTION_HELP = [{
|
|
16
|
-
aliases: [],
|
|
17
|
-
description: 'Emit the runtime platform snapshot/diagnostics payload as JSON (default when no output mode is selected).',
|
|
18
|
-
option: '--json'
|
|
19
|
-
}, {
|
|
20
|
-
aliases: [],
|
|
21
|
-
description: 'Emit a Mermaid graph through the optional @fluojs/studio rendering contract.',
|
|
22
|
-
option: '--mermaid'
|
|
23
|
-
}, {
|
|
24
|
-
aliases: [],
|
|
25
|
-
description: 'Include bootstrap timing diagnostics next to JSON inspect output.',
|
|
26
|
-
option: '--timing'
|
|
27
|
-
}, {
|
|
28
|
-
aliases: [],
|
|
29
|
-
description: 'Emit a CI-friendly JSON report with summary, snapshot, diagnostics, and timing.',
|
|
30
|
-
option: '--report'
|
|
31
|
-
}, {
|
|
32
|
-
aliases: [],
|
|
33
|
-
description: 'Write the selected inspect payload to a file instead of stdout.',
|
|
34
|
-
option: '--output <path>'
|
|
35
|
-
}, {
|
|
36
|
-
aliases: [],
|
|
37
|
-
description: 'Select the exported module symbol name (default: AppModule).',
|
|
38
|
-
option: '--export <name>'
|
|
39
|
-
}, {
|
|
40
|
-
aliases: ['-h'],
|
|
41
|
-
description: 'Show help for the inspect command.',
|
|
42
|
-
option: '--help'
|
|
43
|
-
}];
|
|
44
16
|
const STUDIO_CONTRACT_ENTRYPOINT = '@fluojs/studio/contracts';
|
|
17
|
+
const RUNTIME_ENTRYPOINT = '@fluojs/runtime';
|
|
45
18
|
const TYPESCRIPT_MODULE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);
|
|
19
|
+
const RUNTIME_MISSING_MESSAGE = ['Runtime inspection requires @fluojs/runtime, but it is not resolvable from this project.', 'Install a version whose Node.js engine supports your current Node.js version (for example: pnpm add -D @fluojs/runtime) and rerun fluo inspect.'].join('\n');
|
|
46
20
|
const STUDIO_MISSING_MESSAGE = ['Mermaid graph rendering is owned by @fluojs/studio, but @fluojs/studio is not resolvable from this project.', 'Install @fluojs/studio explicitly (for example: pnpm add -D @fluojs/studio) and rerun fluo inspect --mermaid.'].join('\n');
|
|
21
|
+
const CLACK_MISSING_MESSAGE = ['Interactive inspect guidance requires @clack/prompts, but it is not resolvable from this installation.', 'Install a version whose Node.js engine supports your current Node.js version and rerun fluo inspect.'].join('\n');
|
|
47
22
|
function isHelpFlag(value) {
|
|
48
23
|
return value === '--help' || value === '-h';
|
|
49
24
|
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Returns the usage information string for the inspect command.
|
|
53
|
-
*
|
|
54
|
-
* @returns Formatted help text including usage and options.
|
|
55
|
-
*/
|
|
56
|
-
export function inspectUsage() {
|
|
57
|
-
return ['Usage: fluo inspect <module-path> [options]', '', 'Options', renderHelpTable(INSPECT_OPTION_HELP, [{
|
|
58
|
-
header: 'Option',
|
|
59
|
-
render: entry => entry.option
|
|
60
|
-
}, {
|
|
61
|
-
header: 'Aliases',
|
|
62
|
-
render: entry => renderAliasList(entry.aliases)
|
|
63
|
-
}, {
|
|
64
|
-
header: 'Description',
|
|
65
|
-
render: entry => entry.description
|
|
66
|
-
}]), '', 'Docs: https://github.com/fluojs/fluo/tree/main/docs/getting-started/quick-start.md'].join('\n');
|
|
67
|
-
}
|
|
68
25
|
function parseInspectArgs(argv) {
|
|
69
26
|
let modulePath;
|
|
70
27
|
let exportName = 'AppModule';
|
|
@@ -82,6 +39,18 @@ function parseInspectArgs(argv) {
|
|
|
82
39
|
json = true;
|
|
83
40
|
continue;
|
|
84
41
|
}
|
|
42
|
+
if (option === '--format') {
|
|
43
|
+
const format = argv[index + 1];
|
|
44
|
+
if (!format || format.startsWith('-')) {
|
|
45
|
+
throw new Error('Expected --format to have a value of "json".');
|
|
46
|
+
}
|
|
47
|
+
if (format !== 'json') {
|
|
48
|
+
throw new Error(`Invalid --format value "${format}". Use "json".`);
|
|
49
|
+
}
|
|
50
|
+
json = true;
|
|
51
|
+
index += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
85
54
|
if (option === '--mermaid') {
|
|
86
55
|
mermaid = true;
|
|
87
56
|
continue;
|
|
@@ -152,6 +121,13 @@ function resolveRootModule(exportedValue, exportName) {
|
|
|
152
121
|
async function importInspectModule(modulePath) {
|
|
153
122
|
const moduleUrl = pathToFileURL(modulePath).href;
|
|
154
123
|
if (TYPESCRIPT_MODULE_EXTENSIONS.has(extname(modulePath))) {
|
|
124
|
+
const adjacentTsconfigPath = resolve(dirname(modulePath), 'tsconfig.json');
|
|
125
|
+
if (existsSync(adjacentTsconfigPath)) {
|
|
126
|
+
return tsImport(moduleUrl, {
|
|
127
|
+
parentURL: import.meta.url,
|
|
128
|
+
tsconfig: adjacentTsconfigPath
|
|
129
|
+
});
|
|
130
|
+
}
|
|
155
131
|
return tsImport(moduleUrl, import.meta.url);
|
|
156
132
|
}
|
|
157
133
|
return import(moduleUrl);
|
|
@@ -192,18 +168,26 @@ function stringifySnapshotWithTiming(snapshot, timing) {
|
|
|
192
168
|
timing: timing ?? createEmptyTimingDiagnostics()
|
|
193
169
|
}, null, 2);
|
|
194
170
|
}
|
|
195
|
-
async function emitInspectPayload(payload,
|
|
196
|
-
if (!
|
|
171
|
+
async function emitInspectPayload(payload, outputPath, stdout) {
|
|
172
|
+
if (!outputPath) {
|
|
197
173
|
stdout.write(`${payload}\n`);
|
|
198
174
|
return;
|
|
199
175
|
}
|
|
200
|
-
const outputPath = resolve(cwd, parsed.outputPath);
|
|
201
176
|
await mkdir(dirname(outputPath), {
|
|
202
177
|
recursive: true
|
|
203
178
|
});
|
|
204
179
|
await writeFile(outputPath, `${payload}\n`, 'utf8');
|
|
205
180
|
}
|
|
206
|
-
function createInspectPrompter() {
|
|
181
|
+
async function createInspectPrompter() {
|
|
182
|
+
let clack;
|
|
183
|
+
try {
|
|
184
|
+
clack = await import('@clack/prompts');
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (isModuleNotFoundError(error)) {
|
|
187
|
+
throw new Error(CLACK_MISSING_MESSAGE);
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
207
191
|
return {
|
|
208
192
|
async confirm(message, defaultValue) {
|
|
209
193
|
const result = await clack.confirm({
|
|
@@ -227,6 +211,29 @@ function shouldPromptForStudio(runtime) {
|
|
|
227
211
|
}
|
|
228
212
|
return runtime.stdout === undefined && runtime.stderr === undefined && (runtime.interactive ?? true) && Boolean(runtime.stdin?.isTTY ?? process.stdin.isTTY);
|
|
229
213
|
}
|
|
214
|
+
function isModuleNotFoundError(error) {
|
|
215
|
+
const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;
|
|
216
|
+
return code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND';
|
|
217
|
+
}
|
|
218
|
+
async function loadRuntimeInspectionModule(cwd) {
|
|
219
|
+
const resolver = createRequire(resolve(cwd, 'package.json'));
|
|
220
|
+
try {
|
|
221
|
+
const resolvedEntrypoint = resolver.resolve(RUNTIME_ENTRYPOINT);
|
|
222
|
+
return await import(pathToFileURL(resolvedEntrypoint).href);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (isModuleNotFoundError(error)) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function resolveRuntimeInspectionModule(cwd, runtime) {
|
|
231
|
+
const runtimeModule = await (runtime.loadRuntimeInspectionModule ?? loadRuntimeInspectionModule)(cwd);
|
|
232
|
+
if (!runtimeModule) {
|
|
233
|
+
throw new Error(RUNTIME_MISSING_MESSAGE);
|
|
234
|
+
}
|
|
235
|
+
return runtimeModule;
|
|
236
|
+
}
|
|
230
237
|
async function loadStudioMermaidRenderer(cwd) {
|
|
231
238
|
const resolvers = [createRequire(resolve(cwd, 'package.json')), createRequire(import.meta.url)];
|
|
232
239
|
for (const resolver of resolvers) {
|
|
@@ -238,8 +245,7 @@ async function loadStudioMermaidRenderer(cwd) {
|
|
|
238
245
|
}
|
|
239
246
|
return importedContract.renderMermaid;
|
|
240
247
|
} catch (error) {
|
|
241
|
-
|
|
242
|
-
if (code !== 'MODULE_NOT_FOUND' && code !== 'ERR_MODULE_NOT_FOUND') {
|
|
248
|
+
if (!isModuleNotFoundError(error)) {
|
|
243
249
|
throw error;
|
|
244
250
|
}
|
|
245
251
|
}
|
|
@@ -254,7 +260,7 @@ async function resolveStudioMermaidRenderer(cwd, runtime) {
|
|
|
254
260
|
if (!shouldPromptForStudio(runtime)) {
|
|
255
261
|
throw new Error(STUDIO_MISSING_MESSAGE);
|
|
256
262
|
}
|
|
257
|
-
const prompt = runtime.prompt ?? createInspectPrompter();
|
|
263
|
+
const prompt = runtime.prompt ?? (await createInspectPrompter());
|
|
258
264
|
try {
|
|
259
265
|
const approvedInstall = await prompt.confirm('Install @fluojs/studio before rendering Mermaid output?', false);
|
|
260
266
|
if (!approvedInstall) {
|
|
@@ -284,28 +290,42 @@ export async function runInspectCommand(argv, runtime = {}) {
|
|
|
284
290
|
}
|
|
285
291
|
const parsed = parseInspectArgs(argv);
|
|
286
292
|
const modulePath = resolve(cwd, parsed.modulePath);
|
|
293
|
+
const outputPath = parsed.outputPath === undefined ? undefined : resolve(cwd, parsed.outputPath);
|
|
294
|
+
if (outputPath !== undefined) {
|
|
295
|
+
await assertOutputDoesNotAliasModule(modulePath, outputPath);
|
|
296
|
+
}
|
|
297
|
+
const {
|
|
298
|
+
createRuntimeInspectionSnapshot,
|
|
299
|
+
FluoFactory,
|
|
300
|
+
PLATFORM_SHELL
|
|
301
|
+
} = await resolveRuntimeInspectionModule(cwd, runtime);
|
|
287
302
|
const importedModule = await importInspectModule(modulePath);
|
|
288
303
|
const rootModule = resolveRootModule(importedModule[parsed.exportName], parsed.exportName);
|
|
289
|
-
const
|
|
304
|
+
const application = await FluoFactory.create(rootModule, {
|
|
290
305
|
diagnostics: parsed.timing || parsed.report ? {
|
|
291
306
|
timing: true
|
|
292
|
-
} : undefined
|
|
307
|
+
} : undefined,
|
|
308
|
+
...(parsed.json || parsed.report ? {
|
|
309
|
+
logger: createCliDiagnosticsLogger(stderr)
|
|
310
|
+
} : {})
|
|
293
311
|
});
|
|
294
312
|
try {
|
|
295
|
-
const platformShell = await
|
|
296
|
-
const
|
|
313
|
+
const platformShell = await application.get(PLATFORM_SHELL);
|
|
314
|
+
const platformSnapshot = await platformShell.snapshot();
|
|
315
|
+
const routes = application.dispatcher.describeRoutes?.() ?? [];
|
|
316
|
+
const snapshot = createRuntimeInspectionSnapshot(platformSnapshot, routes);
|
|
297
317
|
if (parsed.json) {
|
|
298
|
-
await emitInspectPayload(parsed.timing ? stringifySnapshotWithTiming(snapshot,
|
|
318
|
+
await emitInspectPayload(parsed.timing ? stringifySnapshotWithTiming(snapshot, application.bootstrapTiming) : stringifySnapshot(snapshot), outputPath, stdout);
|
|
299
319
|
}
|
|
300
320
|
if (parsed.report) {
|
|
301
|
-
await emitInspectPayload(JSON.stringify(createInspectReport(snapshot,
|
|
321
|
+
await emitInspectPayload(JSON.stringify(createInspectReport(snapshot, application.bootstrapTiming), null, 2), outputPath, stdout);
|
|
302
322
|
}
|
|
303
323
|
if (parsed.mermaid) {
|
|
304
324
|
const renderMermaid = await resolveStudioMermaidRenderer(cwd, runtime);
|
|
305
|
-
await emitInspectPayload(renderMermaid(snapshot),
|
|
325
|
+
await emitInspectPayload(renderMermaid(snapshot), outputPath, stdout);
|
|
306
326
|
}
|
|
307
327
|
} finally {
|
|
308
|
-
await
|
|
328
|
+
await application.close();
|
|
309
329
|
}
|
|
310
330
|
return 0;
|
|
311
331
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAgBA,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,yCAAyC;IACzC,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAqLD;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,MAAM,CAAC,CAyEnH"}
|
package/dist/commands/migrate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { renderAliasList, renderHelpTable } from '../help.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getWarningCategoryLabel, groupWarningsByCategory, MIGRATION_TRANSFORMS, renderTransformList, runNestJsMigration } from '../transforms/nestjs-migrate.js';
|
|
4
|
+
import { MIGRATION_TRANSFORM_CLI_TOKENS, parseMigrationTransformList } from './migration-transform-tokens.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Runtime configuration for the migrate command.
|
|
@@ -16,12 +17,16 @@ const MIGRATE_OPTION_HELP = [{
|
|
|
16
17
|
option: '--json'
|
|
17
18
|
}, {
|
|
18
19
|
aliases: [],
|
|
19
|
-
description: `Run only selected transforms. Available: ${
|
|
20
|
+
description: `Run only selected transforms. Available: ${MIGRATION_TRANSFORM_CLI_TOKENS.join(', ')}.`,
|
|
20
21
|
option: '--only <comma-list>'
|
|
21
22
|
}, {
|
|
22
23
|
aliases: [],
|
|
23
|
-
description: `Skip selected transforms. Available: ${
|
|
24
|
+
description: `Skip selected transforms. Available: ${MIGRATION_TRANSFORM_CLI_TOKENS.join(', ')}.`,
|
|
24
25
|
option: '--skip <comma-list>'
|
|
26
|
+
}, {
|
|
27
|
+
aliases: [],
|
|
28
|
+
description: 'Select the HTTP platform for bootstrap rewrites. Required for automatic bootstrap migration; supported: express.',
|
|
29
|
+
option: '--platform <name>'
|
|
25
30
|
}, {
|
|
26
31
|
aliases: ['-h'],
|
|
27
32
|
description: 'Show help for the migrate command.',
|
|
@@ -30,22 +35,12 @@ const MIGRATE_OPTION_HELP = [{
|
|
|
30
35
|
function isHelpFlag(value) {
|
|
31
36
|
return value === '--help' || value === '-h';
|
|
32
37
|
}
|
|
33
|
-
function parseTransformList(rawValue, optionName) {
|
|
34
|
-
const values = rawValue.split(',').map(value => value.trim()).filter(value => value.length > 0);
|
|
35
|
-
if (values.length === 0) {
|
|
36
|
-
throw new Error(`${optionName} requires a non-empty comma-separated transform list.`);
|
|
37
|
-
}
|
|
38
|
-
const invalid = values.filter(value => !MIGRATION_TRANSFORMS.includes(value));
|
|
39
|
-
if (invalid.length > 0) {
|
|
40
|
-
throw new Error(`Unknown transform(s): ${invalid.join(', ')}. Available transforms: ${MIGRATION_TRANSFORMS.join(', ')}.`);
|
|
41
|
-
}
|
|
42
|
-
return values;
|
|
43
|
-
}
|
|
44
38
|
function parseArgs(argv) {
|
|
45
39
|
let pathArgument;
|
|
46
40
|
let apply = false;
|
|
47
41
|
let json = false;
|
|
48
42
|
let onlyTransforms;
|
|
43
|
+
let platform;
|
|
49
44
|
let skipTransforms = [];
|
|
50
45
|
for (let index = 0; index < argv.length; index += 1) {
|
|
51
46
|
const arg = argv[index];
|
|
@@ -65,7 +60,7 @@ function parseArgs(argv) {
|
|
|
65
60
|
if (!rawValue || rawValue.startsWith('-')) {
|
|
66
61
|
throw new Error(`Expected ${arg} to have a comma-separated value.`);
|
|
67
62
|
}
|
|
68
|
-
const parsed =
|
|
63
|
+
const parsed = parseMigrationTransformList(rawValue, arg);
|
|
69
64
|
if (arg === '--only') {
|
|
70
65
|
if (onlyTransforms) {
|
|
71
66
|
throw new Error('Duplicate --only option.');
|
|
@@ -77,6 +72,21 @@ function parseArgs(argv) {
|
|
|
77
72
|
index += 1;
|
|
78
73
|
continue;
|
|
79
74
|
}
|
|
75
|
+
if (arg === '--platform') {
|
|
76
|
+
const rawValue = argv[index + 1];
|
|
77
|
+
if (!rawValue || rawValue.startsWith('-')) {
|
|
78
|
+
throw new Error('Expected --platform to have a platform name.');
|
|
79
|
+
}
|
|
80
|
+
if (platform) {
|
|
81
|
+
throw new Error('Duplicate --platform option.');
|
|
82
|
+
}
|
|
83
|
+
if (rawValue !== 'express') {
|
|
84
|
+
throw new Error(`Unsupported bootstrap platform: ${rawValue}. Supported platform: express.`);
|
|
85
|
+
}
|
|
86
|
+
platform = rawValue;
|
|
87
|
+
index += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
80
90
|
if (arg.startsWith('-')) {
|
|
81
91
|
throw new Error(`Unknown option for migrate command: ${arg}`);
|
|
82
92
|
}
|
|
@@ -99,6 +109,7 @@ function parseArgs(argv) {
|
|
|
99
109
|
apply,
|
|
100
110
|
json,
|
|
101
111
|
path: pathArgument,
|
|
112
|
+
platform,
|
|
102
113
|
transforms: enabled
|
|
103
114
|
};
|
|
104
115
|
}
|
|
@@ -166,6 +177,7 @@ export async function runMigrateCommand(argv, runtime = {}) {
|
|
|
166
177
|
const transforms = [...parsed.transforms];
|
|
167
178
|
const report = runNestJsMigration({
|
|
168
179
|
apply: parsed.apply,
|
|
180
|
+
bootstrapPlatform: parsed.platform,
|
|
169
181
|
enabledTransforms: parsed.transforms,
|
|
170
182
|
targetPath
|
|
171
183
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type MigrationTransformKind } from '../transforms/nestjs-migrate.js';
|
|
2
|
+
/**
|
|
3
|
+
* Provides canonical migration transform tokens accepted by CLI options.
|
|
4
|
+
*/
|
|
5
|
+
export declare const MIGRATION_TRANSFORM_CLI_TOKENS: readonly ["imports", "inject-params", "scope", "bootstrap", "tests", "tsconfig"];
|
|
6
|
+
/**
|
|
7
|
+
* Parse canonical or legacy migration transform tokens from one CLI option.
|
|
8
|
+
*
|
|
9
|
+
* @param rawValue Comma-separated transform tokens passed to the CLI.
|
|
10
|
+
* @param optionName CLI option that supplied the transform tokens.
|
|
11
|
+
* @returns Canonical migration transform kinds.
|
|
12
|
+
* @throws When the option has no tokens or includes an unsupported token.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseMigrationTransformList(rawValue: string, optionName: '--only' | '--skip'): MigrationTransformKind[];
|
|
15
|
+
//# sourceMappingURL=migration-transform-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-transform-tokens.d.ts","sourceRoot":"","sources":["../../src/commands/migration-transform-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEpG;;GAEG;AACH,eAAO,MAAM,8BAA8B,kFAAmF,CAAC;AAS/H;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,QAAQ,GAAG,QAAQ,GAC9B,sBAAsB,EAAE,CA2B1B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MIGRATION_TRANSFORMS } from '../transforms/nestjs-migrate.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provides canonical migration transform tokens accepted by CLI options.
|
|
5
|
+
*/
|
|
6
|
+
export const MIGRATION_TRANSFORM_CLI_TOKENS = ['imports', 'inject-params', 'scope', 'bootstrap', 'tests', 'tsconfig'];
|
|
7
|
+
const MIGRATION_TRANSFORM_ALIASES = {
|
|
8
|
+
'inject-params': 'injectable',
|
|
9
|
+
injectable: 'injectable',
|
|
10
|
+
tests: 'testing',
|
|
11
|
+
testing: 'testing'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse canonical or legacy migration transform tokens from one CLI option.
|
|
16
|
+
*
|
|
17
|
+
* @param rawValue Comma-separated transform tokens passed to the CLI.
|
|
18
|
+
* @param optionName CLI option that supplied the transform tokens.
|
|
19
|
+
* @returns Canonical migration transform kinds.
|
|
20
|
+
* @throws When the option has no tokens or includes an unsupported token.
|
|
21
|
+
*/
|
|
22
|
+
export function parseMigrationTransformList(rawValue, optionName) {
|
|
23
|
+
const values = rawValue.split(',').map(value => value.trim()).filter(value => value.length > 0);
|
|
24
|
+
if (values.length === 0) {
|
|
25
|
+
throw new Error(`${optionName} requires a non-empty comma-separated transform list.`);
|
|
26
|
+
}
|
|
27
|
+
const transforms = [];
|
|
28
|
+
const invalid = [];
|
|
29
|
+
for (const value of values) {
|
|
30
|
+
const transform = MIGRATION_TRANSFORMS.find(candidate => candidate === value) ?? (Object.hasOwn(MIGRATION_TRANSFORM_ALIASES, value) ? MIGRATION_TRANSFORM_ALIASES[value] : undefined);
|
|
31
|
+
if (transform) {
|
|
32
|
+
transforms.push(transform);
|
|
33
|
+
} else {
|
|
34
|
+
invalid.push(value);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (invalid.length > 0) {
|
|
38
|
+
throw new Error(`Unknown transform(s): ${invalid.join(', ')}. Available transforms: ${MIGRATION_TRANSFORM_CLI_TOKENS.join(', ')}.`);
|
|
39
|
+
}
|
|
40
|
+
return transforms;
|
|
41
|
+
}
|