@lenne.tech/cli 1.9.6 → 1.11.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.md +88 -3
- package/build/commands/config/validate.js +2 -0
- package/build/commands/frontend/convert-mode.js +198 -0
- package/build/commands/fullstack/convert-mode.js +368 -0
- package/build/commands/fullstack/init.js +150 -4
- package/build/commands/fullstack/update.js +177 -0
- package/build/commands/server/add-property.js +29 -2
- package/build/commands/server/convert-mode.js +197 -0
- package/build/commands/server/create.js +41 -3
- package/build/commands/server/module.js +58 -25
- package/build/commands/server/object.js +26 -5
- package/build/commands/server/permissions.js +20 -6
- package/build/commands/server/test.js +7 -1
- package/build/commands/status.js +94 -3
- package/build/config/vendor-frontend-runtime-deps.json +4 -0
- package/build/config/vendor-runtime-deps.json +9 -0
- package/build/extensions/api-mode.js +19 -3
- package/build/extensions/frontend-helper.js +652 -0
- package/build/extensions/server.js +1475 -3
- package/build/lib/framework-detection.js +167 -0
- package/build/lib/frontend-framework-detection.js +129 -0
- package/build/templates/nest-server-module/inputs/template-create.input.ts.ejs +1 -1
- package/build/templates/nest-server-module/inputs/template.input.ts.ejs +1 -1
- package/build/templates/nest-server-module/outputs/template-fac-result.output.ts.ejs +1 -1
- package/build/templates/nest-server-module/template.controller.ts.ejs +1 -1
- package/build/templates/nest-server-module/template.model.ts.ejs +1 -1
- package/build/templates/nest-server-module/template.module.ts.ejs +1 -1
- package/build/templates/nest-server-module/template.resolver.ts.ejs +1 -1
- package/build/templates/nest-server-module/template.service.ts.ejs +1 -1
- package/build/templates/nest-server-object/template-create.input.ts.ejs +1 -1
- package/build/templates/nest-server-object/template.input.ts.ejs +1 -1
- package/build/templates/nest-server-object/template.object.ts.ejs +1 -1
- package/build/templates/nest-server-tests/tests.e2e-spec.ts.ejs +1 -1
- package/docs/LT-ECOSYSTEM-GUIDE.md +973 -0
- package/docs/VENDOR-MODE-WORKFLOW.md +471 -0
- package/docs/commands.md +196 -0
- package/docs/lt.config.md +9 -7
- package/package.json +17 -8
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const path_1 = require("path");
|
|
16
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
16
17
|
const object_1 = __importDefault(require("./object"));
|
|
17
18
|
/**
|
|
18
19
|
* Detect controller type based on existing modules
|
|
@@ -272,27 +273,43 @@ const NewCommand = {
|
|
|
272
273
|
const inputTemplate = server.propsForInput(props, { modelName: name, nullable: true });
|
|
273
274
|
const createTemplate = server.propsForInput(props, { create: true, modelName: name, nullable: false });
|
|
274
275
|
const modelTemplate = server.propsForModel(props, { modelName: name });
|
|
276
|
+
// Compute the correct framework-import specifier for each generated file.
|
|
277
|
+
// In vendored projects this resolves to a relative path to src/core (depth
|
|
278
|
+
// depends on file location); in npm projects it stays '@lenne.tech/nest-server'.
|
|
279
|
+
const importFor = (target) => (0, framework_detection_1.getFrameworkImportSpecifier)(path, target);
|
|
275
280
|
// nest-server-module/inputs/xxx.input.ts
|
|
281
|
+
const inputTarget = (0, path_1.join)(directory, 'inputs', `${nameKebab}.input.ts`);
|
|
276
282
|
yield template.generate({
|
|
277
|
-
props: {
|
|
278
|
-
|
|
283
|
+
props: {
|
|
284
|
+
frameworkImport: importFor(inputTarget),
|
|
285
|
+
imports: inputTemplate.imports,
|
|
286
|
+
nameCamel,
|
|
287
|
+
nameKebab,
|
|
288
|
+
namePascal,
|
|
289
|
+
props: inputTemplate.props,
|
|
290
|
+
},
|
|
291
|
+
target: inputTarget,
|
|
279
292
|
template: 'nest-server-module/inputs/template.input.ts.ejs',
|
|
280
293
|
});
|
|
281
294
|
if (controller === 'Rest' || controller === 'Both') {
|
|
295
|
+
const controllerTarget = (0, path_1.join)(directory, `${nameKebab}.controller.ts`);
|
|
282
296
|
yield template.generate({
|
|
283
297
|
props: {
|
|
298
|
+
frameworkImport: importFor(controllerTarget),
|
|
284
299
|
lowercase: name.toLowerCase(),
|
|
285
300
|
nameCamel: camelCase(name),
|
|
286
301
|
nameKebab: kebabCase(name),
|
|
287
302
|
namePascal: pascalCase(name),
|
|
288
303
|
},
|
|
289
|
-
target:
|
|
304
|
+
target: controllerTarget,
|
|
290
305
|
template: 'nest-server-module/template.controller.ts.ejs',
|
|
291
306
|
});
|
|
292
307
|
}
|
|
293
308
|
// nest-server-module/inputs/xxx-create.input.ts
|
|
309
|
+
const createInputTarget = (0, path_1.join)(directory, 'inputs', `${nameKebab}-create.input.ts`);
|
|
294
310
|
yield template.generate({
|
|
295
311
|
props: {
|
|
312
|
+
frameworkImport: importFor(createInputTarget),
|
|
296
313
|
imports: createTemplate.imports,
|
|
297
314
|
isGql: controller === 'GraphQL' || controller === 'Both',
|
|
298
315
|
nameCamel,
|
|
@@ -300,18 +317,27 @@ const NewCommand = {
|
|
|
300
317
|
namePascal,
|
|
301
318
|
props: createTemplate.props,
|
|
302
319
|
},
|
|
303
|
-
target:
|
|
320
|
+
target: createInputTarget,
|
|
304
321
|
template: 'nest-server-module/inputs/template-create.input.ts.ejs',
|
|
305
322
|
});
|
|
306
323
|
// nest-server-module/output/find-and-count-xxxs-result.output.ts
|
|
324
|
+
const facOutputTarget = (0, path_1.join)(directory, 'outputs', `find-and-count-${nameKebab}s-result.output.ts`);
|
|
307
325
|
yield template.generate({
|
|
308
|
-
props: {
|
|
309
|
-
|
|
326
|
+
props: {
|
|
327
|
+
frameworkImport: importFor(facOutputTarget),
|
|
328
|
+
isGql: controller === 'GraphQL' || controller === 'Both',
|
|
329
|
+
nameCamel,
|
|
330
|
+
nameKebab,
|
|
331
|
+
namePascal,
|
|
332
|
+
},
|
|
333
|
+
target: facOutputTarget,
|
|
310
334
|
template: 'nest-server-module/outputs/template-fac-result.output.ts.ejs',
|
|
311
335
|
});
|
|
312
336
|
// nest-server-module/xxx.model.ts
|
|
337
|
+
const modelTarget = (0, path_1.join)(directory, `${nameKebab}.model.ts`);
|
|
313
338
|
yield template.generate({
|
|
314
339
|
props: {
|
|
340
|
+
frameworkImport: importFor(modelTarget),
|
|
315
341
|
imports: modelTemplate.imports,
|
|
316
342
|
isGql: controller === 'GraphQL' || controller === 'Both',
|
|
317
343
|
mappings: modelTemplate.mappings,
|
|
@@ -320,27 +346,36 @@ const NewCommand = {
|
|
|
320
346
|
namePascal,
|
|
321
347
|
props: modelTemplate.props,
|
|
322
348
|
},
|
|
323
|
-
target:
|
|
349
|
+
target: modelTarget,
|
|
324
350
|
template: 'nest-server-module/template.model.ts.ejs',
|
|
325
351
|
});
|
|
326
352
|
// nest-server-module/xxx.module.ts
|
|
353
|
+
const moduleTarget = (0, path_1.join)(directory, `${nameKebab}.module.ts`);
|
|
327
354
|
yield template.generate({
|
|
328
|
-
props: { controller, nameCamel, nameKebab, namePascal },
|
|
329
|
-
target:
|
|
355
|
+
props: { controller, frameworkImport: importFor(moduleTarget), nameCamel, nameKebab, namePascal },
|
|
356
|
+
target: moduleTarget,
|
|
330
357
|
template: 'nest-server-module/template.module.ts.ejs',
|
|
331
358
|
});
|
|
332
359
|
if (controller === 'GraphQL' || controller === 'Both') {
|
|
333
360
|
// nest-server-module/xxx.resolver.ts
|
|
361
|
+
const resolverTarget = (0, path_1.join)(directory, `${nameKebab}.resolver.ts`);
|
|
334
362
|
yield template.generate({
|
|
335
|
-
props: { nameCamel, nameKebab, namePascal },
|
|
336
|
-
target:
|
|
363
|
+
props: { frameworkImport: importFor(resolverTarget), nameCamel, nameKebab, namePascal },
|
|
364
|
+
target: resolverTarget,
|
|
337
365
|
template: 'nest-server-module/template.resolver.ts.ejs',
|
|
338
366
|
});
|
|
339
367
|
}
|
|
340
368
|
// nest-server-module/xxx.service.ts
|
|
369
|
+
const serviceTarget = (0, path_1.join)(directory, `${nameKebab}.service.ts`);
|
|
341
370
|
yield template.generate({
|
|
342
|
-
props: {
|
|
343
|
-
|
|
371
|
+
props: {
|
|
372
|
+
frameworkImport: importFor(serviceTarget),
|
|
373
|
+
isGql: controller === 'GraphQL' || controller === 'Both',
|
|
374
|
+
nameCamel,
|
|
375
|
+
nameKebab,
|
|
376
|
+
namePascal,
|
|
377
|
+
},
|
|
378
|
+
target: serviceTarget,
|
|
344
379
|
template: 'nest-server-module/template.service.ts.ejs',
|
|
345
380
|
});
|
|
346
381
|
generateSpinner.succeed('Files generated');
|
|
@@ -365,20 +400,18 @@ const NewCommand = {
|
|
|
365
400
|
});
|
|
366
401
|
// Ensure forwardRef is imported from @nestjs/common
|
|
367
402
|
const serverModuleContent = filesystem.read(serverModule);
|
|
368
|
-
if (serverModuleContent &&
|
|
369
|
-
// Add forwardRef to @nestjs/common import
|
|
370
|
-
yield patching.patch(serverModule, {
|
|
371
|
-
insert: '$1, forwardRef$2',
|
|
372
|
-
replace: /from '@nestjs\/common'(.*?)}/,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
else if (serverModuleContent &&
|
|
403
|
+
if (serverModuleContent &&
|
|
376
404
|
serverModuleContent.includes('@nestjs/common') &&
|
|
377
|
-
!serverModuleContent.match(/forwardRef
|
|
378
|
-
// forwardRef
|
|
405
|
+
!serverModuleContent.match(/import\s*\{[^}]*forwardRef[^}]*}\s*from\s+['"]@nestjs\/common['"]/)) {
|
|
406
|
+
// Add forwardRef into the existing `import { ... } from '@nestjs/common'`
|
|
407
|
+
// statement by inserting it before the closing brace. The regex
|
|
408
|
+
// captures two groups: (1) everything up to (but not including) the
|
|
409
|
+
// closing brace of the named-import list and (2) the closing brace
|
|
410
|
+
// plus the `from '@nestjs/common'` clause. The replacement wedges
|
|
411
|
+
// `, forwardRef` in between.
|
|
379
412
|
yield patching.patch(serverModule, {
|
|
380
|
-
insert:
|
|
381
|
-
replace: /(\
|
|
413
|
+
insert: "$1, forwardRef$2",
|
|
414
|
+
replace: /(import\s*\{\s*[^}]*?)(\s*\}\s*from\s+['"]@nestjs\/common['"])/,
|
|
382
415
|
});
|
|
383
416
|
}
|
|
384
417
|
}
|
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const path_1 = require("path");
|
|
16
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
16
17
|
const module_1 = __importDefault(require("./module"));
|
|
17
18
|
/**
|
|
18
19
|
* Create a new server object
|
|
@@ -133,21 +134,41 @@ const NewCommand = {
|
|
|
133
134
|
const inputTemplate = server.propsForInput(props, { modelName: name, nullable: true });
|
|
134
135
|
const createTemplate = server.propsForInput(props, { create: true, modelName: name, nullable: false });
|
|
135
136
|
const objectTemplate = server.propsForModel(props, { modelName: name });
|
|
137
|
+
// Framework-import specifier (bare in npm mode, relative in vendored mode)
|
|
138
|
+
const importFor = (target) => (0, framework_detection_1.getFrameworkImportSpecifier)(path, target);
|
|
136
139
|
// nest-server-module/inputs/xxx.input.ts
|
|
140
|
+
const inputTarget = (0, path_1.join)(directory, `${nameKebab}.input.ts`);
|
|
137
141
|
yield template.generate({
|
|
138
|
-
props: {
|
|
139
|
-
|
|
142
|
+
props: {
|
|
143
|
+
frameworkImport: importFor(inputTarget),
|
|
144
|
+
imports: inputTemplate.imports,
|
|
145
|
+
nameCamel,
|
|
146
|
+
nameKebab,
|
|
147
|
+
namePascal,
|
|
148
|
+
props: inputTemplate.props,
|
|
149
|
+
},
|
|
150
|
+
target: inputTarget,
|
|
140
151
|
template: 'nest-server-object/template.input.ts.ejs',
|
|
141
152
|
});
|
|
142
153
|
// nest-server-object/inputs/xxx-create.input.ts
|
|
154
|
+
const createInputTarget = (0, path_1.join)(directory, `${nameKebab}-create.input.ts`);
|
|
143
155
|
yield template.generate({
|
|
144
|
-
props: {
|
|
145
|
-
|
|
156
|
+
props: {
|
|
157
|
+
frameworkImport: importFor(createInputTarget),
|
|
158
|
+
imports: createTemplate.imports,
|
|
159
|
+
nameCamel,
|
|
160
|
+
nameKebab,
|
|
161
|
+
namePascal,
|
|
162
|
+
props: createTemplate.props,
|
|
163
|
+
},
|
|
164
|
+
target: createInputTarget,
|
|
146
165
|
template: 'nest-server-object/template-create.input.ts.ejs',
|
|
147
166
|
});
|
|
148
167
|
// nest-server-module/xxx.model.ts
|
|
168
|
+
const objectTarget = (0, path_1.join)(directory, `${nameKebab}.object.ts`);
|
|
149
169
|
yield template.generate({
|
|
150
170
|
props: {
|
|
171
|
+
frameworkImport: importFor(objectTarget),
|
|
151
172
|
imports: objectTemplate.imports,
|
|
152
173
|
mappings: objectTemplate.mappings,
|
|
153
174
|
nameCamel,
|
|
@@ -155,7 +176,7 @@ const NewCommand = {
|
|
|
155
176
|
namePascal,
|
|
156
177
|
props: objectTemplate.props,
|
|
157
178
|
},
|
|
158
|
-
target:
|
|
179
|
+
target: objectTarget,
|
|
159
180
|
template: 'nest-server-object/template.object.ts.ejs',
|
|
160
181
|
});
|
|
161
182
|
generateSpinner.succeed('Files generated');
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const child_process_1 = require("child_process");
|
|
13
13
|
const fs_1 = require("fs");
|
|
14
14
|
const path_1 = require("path");
|
|
15
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
15
16
|
// ────────────────────────────────────────────────────────────────────────────
|
|
16
17
|
// Helper functions (alphabetically sorted per ESLint rules)
|
|
17
18
|
// ────────────────────────────────────────────────────────────────────────────
|
|
@@ -82,11 +83,23 @@ function generateJson(report, projectPath) {
|
|
|
82
83
|
*/
|
|
83
84
|
function loadScanner(projectPath) {
|
|
84
85
|
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
// Build an ordered list of candidate paths, mode-aware:
|
|
87
|
+
//
|
|
88
|
+
// - Vendored projects: the framework code lives in src/core/ as
|
|
89
|
+
// project-compiled TypeScript. After `nest build` the compiled output
|
|
90
|
+
// lands under dist/src/core/modules/permissions/. Before the build,
|
|
91
|
+
// the .ts source sits at src/core/modules/permissions/permissions-scanner.ts
|
|
92
|
+
// (which Node's `require()` cannot load directly). We try the dist
|
|
93
|
+
// variant first and fall back to the bundled CLI scanner if the
|
|
94
|
+
// project hasn't been built yet.
|
|
95
|
+
// - npm projects: classic path under node_modules/@lenne.tech/nest-server/dist.
|
|
96
|
+
const scannerPaths = [];
|
|
97
|
+
if ((0, framework_detection_1.isVendoredProject)(projectPath)) {
|
|
98
|
+
scannerPaths.push((0, path_1.join)(projectPath, 'dist', 'src', 'core', 'modules', 'permissions', 'permissions-scanner'), (0, path_1.join)(projectPath, 'dist', 'src', 'core', 'modules', 'permissions', 'permissions-scanner.js'), (0, path_1.join)(projectPath, 'dist', 'core', 'modules', 'permissions', 'permissions-scanner'), (0, path_1.join)(projectPath, 'dist', 'core', 'modules', 'permissions', 'permissions-scanner.js'));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
scannerPaths.push((0, path_1.join)(projectPath, 'node_modules', '@lenne.tech', 'nest-server', 'dist', 'core', 'modules', 'permissions', 'permissions-scanner'), (0, path_1.join)(projectPath, 'node_modules', '@lenne.tech', 'nest-server', 'dist', 'core', 'modules', 'permissions', 'permissions-scanner.js'));
|
|
102
|
+
}
|
|
90
103
|
for (const scannerPath of scannerPaths) {
|
|
91
104
|
try {
|
|
92
105
|
const mod = require(scannerPath);
|
|
@@ -101,7 +114,8 @@ function loadScanner(projectPath) {
|
|
|
101
114
|
// Not available at this path
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
|
-
//
|
|
117
|
+
// Fallback: Use CLI's bundled scanner. Covers both "not yet built" (vendor
|
|
118
|
+
// mode before `nest build`) and "framework not installed" (detached clone).
|
|
105
119
|
try {
|
|
106
120
|
const fallback = require('../../lib/fallback-scanner');
|
|
107
121
|
if (typeof fallback.scanPermissions === 'function') {
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const path_1 = require("path");
|
|
13
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
13
14
|
/**
|
|
14
15
|
* Create a new server
|
|
15
16
|
*/
|
|
@@ -55,7 +56,12 @@ const NewCommand = {
|
|
|
55
56
|
const generateSpinner = spin('Generate test file');
|
|
56
57
|
// nest-server-tests/tests.e2e-spec.ts.ejs
|
|
57
58
|
yield template.generate({
|
|
58
|
-
props: {
|
|
59
|
+
props: {
|
|
60
|
+
frameworkImport: (0, framework_detection_1.getFrameworkImportSpecifier)(path, filePath),
|
|
61
|
+
nameCamel,
|
|
62
|
+
nameKebab,
|
|
63
|
+
namePascal,
|
|
64
|
+
},
|
|
59
65
|
target: filePath,
|
|
60
66
|
template: 'nest-server-tests/tests.e2e-spec.ts.ejs',
|
|
61
67
|
});
|
package/build/commands/status.js
CHANGED
|
@@ -10,6 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const path_1 = require("path");
|
|
13
|
+
const framework_detection_1 = require("../lib/framework-detection");
|
|
14
|
+
const frontend_framework_detection_1 = require("../lib/frontend-framework-detection");
|
|
13
15
|
/**
|
|
14
16
|
* Show project status and context
|
|
15
17
|
*/
|
|
@@ -27,11 +29,14 @@ const StatusCommand = {
|
|
|
27
29
|
info(colors.dim('─'.repeat(50)));
|
|
28
30
|
const projectInfo = {
|
|
29
31
|
configFiles: [],
|
|
32
|
+
frameworkMode: null,
|
|
33
|
+
frontendFrameworkMode: null,
|
|
30
34
|
gitBranch: null,
|
|
31
35
|
gitRoot: null,
|
|
32
36
|
hasGit: false,
|
|
33
37
|
hasLtConfig: false,
|
|
34
38
|
hasPackageJson: false,
|
|
39
|
+
monorepoSubprojects: [],
|
|
35
40
|
nodeVersion: null,
|
|
36
41
|
npmVersion: null,
|
|
37
42
|
packageName: null,
|
|
@@ -56,14 +61,22 @@ const StatusCommand = {
|
|
|
56
61
|
projectInfo.packageVersion = packageJson.version || null;
|
|
57
62
|
// Detect project type
|
|
58
63
|
const deps = Object.assign(Object.assign({}, packageJson.dependencies), packageJson.devDependencies);
|
|
59
|
-
|
|
64
|
+
// A project is a nest-server project if it EITHER has the npm dep
|
|
65
|
+
// (classic) OR has vendored the core/ directory. The frameworkMode
|
|
66
|
+
// field records which of the two modes this project runs in.
|
|
67
|
+
if (deps['@lenne.tech/nest-server'] || (0, framework_detection_1.isVendoredProject)(cwd)) {
|
|
60
68
|
projectInfo.projectType = 'nest-server';
|
|
69
|
+
projectInfo.frameworkMode = (0, framework_detection_1.detectFrameworkMode)(cwd);
|
|
61
70
|
}
|
|
62
71
|
else if (deps['@nestjs/core']) {
|
|
63
72
|
projectInfo.projectType = 'nestjs';
|
|
64
73
|
}
|
|
65
74
|
else if (deps['nuxt']) {
|
|
66
75
|
projectInfo.projectType = 'nuxt';
|
|
76
|
+
// Detect frontend framework mode if nuxt-extensions is present
|
|
77
|
+
if (deps['@lenne.tech/nuxt-extensions'] || (0, frontend_framework_detection_1.isVendoredAppProject)(cwd)) {
|
|
78
|
+
projectInfo.frontendFrameworkMode = (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(cwd);
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
else if (deps['@angular/core']) {
|
|
69
82
|
projectInfo.projectType = 'angular';
|
|
@@ -85,6 +98,52 @@ const StatusCommand = {
|
|
|
85
98
|
// Ignore parse errors
|
|
86
99
|
}
|
|
87
100
|
}
|
|
101
|
+
// Monorepo subproject detection: scan projects/api and projects/app for
|
|
102
|
+
// framework modes so that `lt status` at the monorepo root surfaces
|
|
103
|
+
// backend + frontend framework consumption modes even when the root
|
|
104
|
+
// itself is not a Nest/Nuxt project.
|
|
105
|
+
const monorepoCandidates = [
|
|
106
|
+
{ kind: 'backend', path: (0, path_1.join)(cwd, 'projects', 'api') },
|
|
107
|
+
{ kind: 'backend', path: (0, path_1.join)(cwd, 'packages', 'api') },
|
|
108
|
+
{ kind: 'frontend', path: (0, path_1.join)(cwd, 'projects', 'app') },
|
|
109
|
+
{ kind: 'frontend', path: (0, path_1.join)(cwd, 'packages', 'app') },
|
|
110
|
+
];
|
|
111
|
+
for (const candidate of monorepoCandidates) {
|
|
112
|
+
if (!filesystem.exists((0, path_1.join)(candidate.path, 'package.json')))
|
|
113
|
+
continue;
|
|
114
|
+
if (candidate.kind === 'backend') {
|
|
115
|
+
try {
|
|
116
|
+
const subPkg = JSON.parse(filesystem.read((0, path_1.join)(candidate.path, 'package.json')) || '{}');
|
|
117
|
+
const subDeps = Object.assign(Object.assign({}, subPkg.dependencies), subPkg.devDependencies);
|
|
118
|
+
if (subDeps['@lenne.tech/nest-server'] || (0, framework_detection_1.isVendoredProject)(candidate.path)) {
|
|
119
|
+
projectInfo.monorepoSubprojects.push({
|
|
120
|
+
frameworkMode: (0, framework_detection_1.detectFrameworkMode)(candidate.path),
|
|
121
|
+
kind: 'backend',
|
|
122
|
+
path: candidate.path,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (_d) {
|
|
127
|
+
// ignore
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
try {
|
|
132
|
+
const subPkg = JSON.parse(filesystem.read((0, path_1.join)(candidate.path, 'package.json')) || '{}');
|
|
133
|
+
const subDeps = Object.assign(Object.assign({}, subPkg.dependencies), subPkg.devDependencies);
|
|
134
|
+
if (subDeps['@lenne.tech/nuxt-extensions'] || (0, frontend_framework_detection_1.isVendoredAppProject)(candidate.path)) {
|
|
135
|
+
projectInfo.monorepoSubprojects.push({
|
|
136
|
+
frameworkMode: (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(candidate.path),
|
|
137
|
+
kind: 'frontend',
|
|
138
|
+
path: candidate.path,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (_e) {
|
|
143
|
+
// ignore
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
88
147
|
// Check for git
|
|
89
148
|
try {
|
|
90
149
|
const gitRoot = yield system.run('git rev-parse --show-toplevel 2>/dev/null');
|
|
@@ -95,7 +154,7 @@ const StatusCommand = {
|
|
|
95
154
|
projectInfo.gitBranch = (branch === null || branch === void 0 ? void 0 : branch.trim()) || null;
|
|
96
155
|
}
|
|
97
156
|
}
|
|
98
|
-
catch (
|
|
157
|
+
catch (_f) {
|
|
99
158
|
// Not a git repository
|
|
100
159
|
}
|
|
101
160
|
// Get Node/npm versions
|
|
@@ -103,7 +162,7 @@ const StatusCommand = {
|
|
|
103
162
|
projectInfo.nodeVersion = ((_a = (yield system.run('node --version 2>/dev/null'))) === null || _a === void 0 ? void 0 : _a.trim()) || null;
|
|
104
163
|
projectInfo.npmVersion = ((_b = (yield system.run('npm --version 2>/dev/null'))) === null || _b === void 0 ? void 0 : _b.trim()) || null;
|
|
105
164
|
}
|
|
106
|
-
catch (
|
|
165
|
+
catch (_g) {
|
|
107
166
|
// Ignore errors
|
|
108
167
|
}
|
|
109
168
|
// Display project info
|
|
@@ -120,6 +179,38 @@ const StatusCommand = {
|
|
|
120
179
|
info(` Version: ${projectInfo.packageVersion}`);
|
|
121
180
|
}
|
|
122
181
|
info(` Type: ${formatProjectType(projectInfo.projectType)}`);
|
|
182
|
+
if (projectInfo.frameworkMode) {
|
|
183
|
+
const modeLabel = projectInfo.frameworkMode === 'vendor'
|
|
184
|
+
? 'vendor (src/core/, VENDOR.md)'
|
|
185
|
+
: 'npm (@lenne.tech/nest-server dependency)';
|
|
186
|
+
info(` Framework: ${modeLabel}`);
|
|
187
|
+
}
|
|
188
|
+
if (projectInfo.frontendFrameworkMode) {
|
|
189
|
+
const frontendModeLabel = projectInfo.frontendFrameworkMode === 'vendor'
|
|
190
|
+
? 'vendor (app/core/, VENDOR.md)'
|
|
191
|
+
: 'npm (@lenne.tech/nuxt-extensions dependency)';
|
|
192
|
+
info(` Frontend Framework: ${frontendModeLabel}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Show monorepo subprojects if we detected any (typically at monorepo root)
|
|
196
|
+
if (projectInfo.monorepoSubprojects.length > 0) {
|
|
197
|
+
info('');
|
|
198
|
+
info(colors.bold('Monorepo Subprojects:'));
|
|
199
|
+
for (const sub of projectInfo.monorepoSubprojects) {
|
|
200
|
+
const relPath = sub.path.replace(`${cwd}/`, '');
|
|
201
|
+
if (sub.kind === 'backend') {
|
|
202
|
+
const label = sub.frameworkMode === 'vendor'
|
|
203
|
+
? 'vendor (src/core/, VENDOR.md)'
|
|
204
|
+
: 'npm (@lenne.tech/nest-server dependency)';
|
|
205
|
+
info(` Backend: ${relPath} → ${label}`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const label = sub.frameworkMode === 'vendor'
|
|
209
|
+
? 'vendor (app/core/, VENDOR.md)'
|
|
210
|
+
: 'npm (@lenne.tech/nuxt-extensions dependency)';
|
|
211
|
+
info(` Frontend: ${relPath} → ${label}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
123
214
|
}
|
|
124
215
|
if (projectInfo.hasGit) {
|
|
125
216
|
info('');
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Upstream @lenne.tech/nuxt-extensions devDependencies that must be promoted to regular dependencies in vendor mode. Currently empty because nuxt-extensions has only @nuxt/kit as a direct dependency (which is handled explicitly during vendoring).",
|
|
3
|
+
"runtimeHelpers": []
|
|
4
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./vendor-runtime-deps.schema.json",
|
|
3
|
+
"description": "Upstream @lenne.tech/nest-server devDependencies that are actually needed at runtime in a consumer project. When vendoring, these entries are promoted from upstream devDependencies into the project's dependencies so production runtime has them available.",
|
|
4
|
+
"runtimeHelpers": [
|
|
5
|
+
"find-file-up"
|
|
6
|
+
]
|
|
7
|
+
}
|
|
8
|
+
</content>
|
|
9
|
+
</invoke>
|
|
@@ -373,9 +373,25 @@ class ApiMode {
|
|
|
373
373
|
}
|
|
374
374
|
i++;
|
|
375
375
|
}
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
|
|
376
|
+
// Build the "code content" view against which import usage is checked.
|
|
377
|
+
//
|
|
378
|
+
// Previous implementation: `lines.slice(maxImportEnd + 1)` — only the
|
|
379
|
+
// lines AFTER the last import. That breaks for files where imports and
|
|
380
|
+
// top-level code are interleaved (e.g. a helper `const` declared between
|
|
381
|
+
// two import groups). Those inter-import usages were never seen, so the
|
|
382
|
+
// still-used identifiers got pruned.
|
|
383
|
+
//
|
|
384
|
+
// Fix: build a mask where all import lines are blanked out but every
|
|
385
|
+
// other line is preserved, so inter-import usages still count.
|
|
386
|
+
const importLineSet = new Set();
|
|
387
|
+
for (const imp of importLines) {
|
|
388
|
+
for (let j = imp.start; j <= imp.end; j++) {
|
|
389
|
+
importLineSet.add(j);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const codeContent = lines
|
|
393
|
+
.map((line, idx) => (importLineSet.has(idx) ? '' : line))
|
|
394
|
+
.join('\n');
|
|
379
395
|
// Check each import
|
|
380
396
|
const linesToRemove = new Set();
|
|
381
397
|
for (const imp of importLines) {
|