@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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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");
|
|
15
|
+
/**
|
|
16
|
+
* Update a fullstack workspace — mode-aware.
|
|
17
|
+
*
|
|
18
|
+
* lenne.tech fullstack projects currently run in one of two framework
|
|
19
|
+
* consumption modes:
|
|
20
|
+
*
|
|
21
|
+
* - npm mode: `@lenne.tech/nest-server` is an npm dependency. Updates
|
|
22
|
+
* happen via `pnpm update @lenne.tech/nest-server` plus
|
|
23
|
+
* the migration guides, orchestrated by the
|
|
24
|
+
* `lt-dev:nest-server-updater` Claude Code agent.
|
|
25
|
+
*
|
|
26
|
+
* - vendor mode: The framework `core/` tree is vendored into
|
|
27
|
+
* `projects/api/src/core/`. Updates happen via the
|
|
28
|
+
* `lt-dev:nest-server-core-updater` Claude Code agent,
|
|
29
|
+
* which clones the upstream repo, computes a delta,
|
|
30
|
+
* applies the approved hunks, and re-runs the flatten-fix.
|
|
31
|
+
*
|
|
32
|
+
* Detection is based on the presence of `src/core/VENDOR.md` in the api
|
|
33
|
+
* project. This command prints the right instructions for the caller's
|
|
34
|
+
* project; actual update orchestration lives in the Claude Code agents,
|
|
35
|
+
* not in the CLI.
|
|
36
|
+
*/
|
|
37
|
+
const NewCommand = {
|
|
38
|
+
alias: ['up', 'upd'],
|
|
39
|
+
description: 'Show the mode-specific update instructions for this fullstack workspace',
|
|
40
|
+
hidden: false,
|
|
41
|
+
name: 'update',
|
|
42
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
const { filesystem, print: { colors, info, success, warning }, } = toolbox;
|
|
44
|
+
info('');
|
|
45
|
+
info(colors.bold('Fullstack Update'));
|
|
46
|
+
info(colors.dim('─'.repeat(60)));
|
|
47
|
+
// Walk from cwd DOWN into projects/api if it exists, otherwise assume
|
|
48
|
+
// the caller is already inside an api project. Users can also point at
|
|
49
|
+
// a specific directory via `--api <path>`.
|
|
50
|
+
const cwd = filesystem.cwd();
|
|
51
|
+
const candidates = [
|
|
52
|
+
toolbox.parameters.options.api ? String(toolbox.parameters.options.api) : null,
|
|
53
|
+
(0, path_1.join)(cwd, 'projects', 'api'),
|
|
54
|
+
(0, path_1.join)(cwd, 'packages', 'api'),
|
|
55
|
+
cwd,
|
|
56
|
+
].filter((p) => Boolean(p));
|
|
57
|
+
let apiDir;
|
|
58
|
+
for (const candidate of candidates) {
|
|
59
|
+
if (filesystem.exists((0, path_1.join)(candidate, 'package.json'))) {
|
|
60
|
+
apiDir = candidate;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!apiDir) {
|
|
65
|
+
warning(' Could not locate an api project (no package.json found in cwd or projects/api/).');
|
|
66
|
+
info('');
|
|
67
|
+
info(' Pass --api <path> to point at the api project explicitly.');
|
|
68
|
+
info('');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const mode = (0, framework_detection_1.detectFrameworkMode)(apiDir);
|
|
72
|
+
const vendored = (0, framework_detection_1.isVendoredProject)(apiDir);
|
|
73
|
+
info(` API project: ${apiDir}`);
|
|
74
|
+
info(` Framework mode: ${mode}${vendored ? ' (src/core/VENDOR.md present)' : ''}`);
|
|
75
|
+
info('');
|
|
76
|
+
if (mode === 'vendor') {
|
|
77
|
+
info(colors.bold('Vendor-mode update flow:'));
|
|
78
|
+
info('');
|
|
79
|
+
info(' The framework core/ tree lives directly in this project at');
|
|
80
|
+
info(' src/core/');
|
|
81
|
+
info(' and is managed as first-class project code. Local patches are');
|
|
82
|
+
info(' allowed and tracked in src/core/VENDOR.md.');
|
|
83
|
+
info('');
|
|
84
|
+
info(colors.bold(' Recommended update commands:'));
|
|
85
|
+
info('');
|
|
86
|
+
info(' 1. Refresh the upstream baseline + check for new versions');
|
|
87
|
+
info(` ${colors.cyan('(run from the api project)')}`);
|
|
88
|
+
info('');
|
|
89
|
+
info(' /lt-dev:backend:update-nest-server-core');
|
|
90
|
+
info('');
|
|
91
|
+
info(' 2. After the updater completes, run a freshness check:');
|
|
92
|
+
info('');
|
|
93
|
+
info(' pnpm run check:vendor-freshness');
|
|
94
|
+
info('');
|
|
95
|
+
info(' 3. If local changes have become generally useful, propose');
|
|
96
|
+
info(' them as upstream PRs via:');
|
|
97
|
+
info('');
|
|
98
|
+
info(' /lt-dev:backend:contribute-nest-server-core');
|
|
99
|
+
info('');
|
|
100
|
+
success(' All of these operate on src/core/ in-place; no npm dep bump.');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
info(colors.bold('npm-mode update flow:'));
|
|
104
|
+
info('');
|
|
105
|
+
info(' The framework lives in node_modules/@lenne.tech/nest-server as');
|
|
106
|
+
info(' a pinned npm dependency.');
|
|
107
|
+
info('');
|
|
108
|
+
info(colors.bold(' Recommended update commands:'));
|
|
109
|
+
info('');
|
|
110
|
+
info(' 1. Run the nest-server-updater agent:');
|
|
111
|
+
info('');
|
|
112
|
+
info(' /lt-dev:backend:update-nest-server');
|
|
113
|
+
info('');
|
|
114
|
+
info(' (or manually: pnpm update @lenne.tech/nest-server');
|
|
115
|
+
info(' and walk the migration guides)');
|
|
116
|
+
info('');
|
|
117
|
+
info(' 2. After upgrade, run the full check suite:');
|
|
118
|
+
info('');
|
|
119
|
+
info(' pnpm run check');
|
|
120
|
+
info('');
|
|
121
|
+
success(' The nest-server-updater agent auto-detects vendor projects');
|
|
122
|
+
success(' and delegates to nest-server-core-updater when VENDOR.md is present.');
|
|
123
|
+
}
|
|
124
|
+
// ── Frontend mode-aware instructions ──────────────────────────────
|
|
125
|
+
info('');
|
|
126
|
+
info(colors.dim('─'.repeat(60)));
|
|
127
|
+
info('');
|
|
128
|
+
// Detect frontend project
|
|
129
|
+
const appCandidates = [
|
|
130
|
+
(0, path_1.join)(cwd, 'projects', 'app'),
|
|
131
|
+
(0, path_1.join)(cwd, 'packages', 'app'),
|
|
132
|
+
].filter((p) => Boolean(p));
|
|
133
|
+
let appDir;
|
|
134
|
+
for (const candidate of appCandidates) {
|
|
135
|
+
if (filesystem.exists((0, path_1.join)(candidate, 'nuxt.config.ts')) || filesystem.exists((0, path_1.join)(candidate, 'package.json'))) {
|
|
136
|
+
appDir = candidate;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (appDir) {
|
|
141
|
+
const frontendMode = (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(appDir);
|
|
142
|
+
const frontendVendored = (0, frontend_framework_detection_1.isVendoredAppProject)(appDir);
|
|
143
|
+
info(` App project: ${appDir}`);
|
|
144
|
+
info(` Frontend framework mode: ${frontendMode}${frontendVendored ? ' (app/core/VENDOR.md present)' : ''}`);
|
|
145
|
+
info('');
|
|
146
|
+
if (frontendMode === 'vendor') {
|
|
147
|
+
info(colors.bold('Frontend vendor-mode update flow:'));
|
|
148
|
+
info('');
|
|
149
|
+
info(' The nuxt-extensions module lives directly in this project at');
|
|
150
|
+
info(' app/core/');
|
|
151
|
+
info(' and is managed as first-class project code.');
|
|
152
|
+
info('');
|
|
153
|
+
info(colors.bold(' Recommended update commands:'));
|
|
154
|
+
info('');
|
|
155
|
+
info(' /lt-dev:frontend:update-nuxt-extensions-core');
|
|
156
|
+
info('');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
info(colors.bold('Frontend npm-mode update flow:'));
|
|
160
|
+
info('');
|
|
161
|
+
info(' /lt-dev:fullstack:update --skip-backend');
|
|
162
|
+
info('');
|
|
163
|
+
info(' (or manually: pnpm update @lenne.tech/nuxt-extensions)');
|
|
164
|
+
info('');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
info('');
|
|
168
|
+
info(colors.bold('For a comprehensive update of everything, use:'));
|
|
169
|
+
info('');
|
|
170
|
+
info(' /lt-dev:fullstack:update-all');
|
|
171
|
+
info('');
|
|
172
|
+
info(colors.dim('─'.repeat(60)));
|
|
173
|
+
info('');
|
|
174
|
+
return `fullstack update (backend: ${mode}, frontend: ${appDir ? (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(appDir) : 'not found'})`;
|
|
175
|
+
}),
|
|
176
|
+
};
|
|
177
|
+
exports.default = NewCommand;
|
|
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const path_1 = require("path");
|
|
16
16
|
const ts_morph_1 = require("ts-morph");
|
|
17
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
17
18
|
const module_1 = __importDefault(require("./module"));
|
|
18
19
|
const object_1 = __importDefault(require("./object"));
|
|
19
20
|
/**
|
|
@@ -427,8 +428,34 @@ const NewCommand = {
|
|
|
427
428
|
const mappingPairs = Object.entries(mappings).map(([k, v]) => `${k}: ${v}`);
|
|
428
429
|
returnStatement.replaceWithText(`return mapClasses(input, { ${mappingPairs.join(', ')} }, this);`);
|
|
429
430
|
}
|
|
430
|
-
// Ensure mapClasses is imported
|
|
431
|
-
|
|
431
|
+
// Ensure mapClasses is imported. The import specifier differs by
|
|
432
|
+
// framework-consumption mode — in npm projects it's
|
|
433
|
+
// '@lenne.tech/nest-server'; in vendored projects it's a relative
|
|
434
|
+
// path to src/core whose depth depends on the model file location.
|
|
435
|
+
// We search for BOTH forms so this works regardless of how the file
|
|
436
|
+
// was originally generated.
|
|
437
|
+
const vendoredSpec = (0, framework_detection_1.isVendoredProject)(path)
|
|
438
|
+
? (0, framework_detection_1.getFrameworkImportSpecifier)(path, modelPath)
|
|
439
|
+
: null;
|
|
440
|
+
let existingImports = moduleFile.getImportDeclaration('@lenne.tech/nest-server');
|
|
441
|
+
if (!existingImports && vendoredSpec) {
|
|
442
|
+
existingImports = moduleFile.getImportDeclaration(vendoredSpec);
|
|
443
|
+
}
|
|
444
|
+
if (!existingImports) {
|
|
445
|
+
// Final fallback: scan all imports and match any that resolves to
|
|
446
|
+
// the framework path (covers hand-edited files or unusual depths).
|
|
447
|
+
const allImports = moduleFile.getImportDeclarations();
|
|
448
|
+
existingImports = allImports.find((imp) => {
|
|
449
|
+
const spec = imp.getModuleSpecifierValue();
|
|
450
|
+
if (spec === '@lenne.tech/nest-server')
|
|
451
|
+
return true;
|
|
452
|
+
if (!vendoredSpec)
|
|
453
|
+
return false;
|
|
454
|
+
// Accept any relative path ending with '/core' or '../core' —
|
|
455
|
+
// covers variations across file depths.
|
|
456
|
+
return /(^|\/)core(\/.*)?$/.test(spec) && spec.startsWith('.');
|
|
457
|
+
});
|
|
458
|
+
}
|
|
432
459
|
if (existingImports) {
|
|
433
460
|
const namedImports = existingImports.getNamedImports();
|
|
434
461
|
const hasMapClasses = namedImports.some((ni) => ni.getName() === 'mapClasses');
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const framework_detection_1 = require("../../lib/framework-detection");
|
|
13
|
+
/**
|
|
14
|
+
* Convert an existing API project between npm mode and vendor mode.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* lt server convert-mode --to vendor [--upstream-branch 11.24.2]
|
|
18
|
+
* lt server convert-mode --to npm [--version 11.24.2]
|
|
19
|
+
*/
|
|
20
|
+
const ConvertModeCommand = {
|
|
21
|
+
description: 'Convert API project between npm and vendor framework modes',
|
|
22
|
+
hidden: false,
|
|
23
|
+
name: 'convert-mode',
|
|
24
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const { filesystem, parameters, print: { error, info, spin, success, warning }, prompt: { confirm }, server, } = toolbox;
|
|
26
|
+
// Handle --help-json flag
|
|
27
|
+
if (toolbox.tools.helpJson({
|
|
28
|
+
description: 'Convert API project between npm and vendor framework modes',
|
|
29
|
+
name: 'convert-mode',
|
|
30
|
+
options: [
|
|
31
|
+
{
|
|
32
|
+
description: 'Target mode',
|
|
33
|
+
flag: '--to',
|
|
34
|
+
required: true,
|
|
35
|
+
type: 'string',
|
|
36
|
+
values: ['vendor', 'npm'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
description: 'Upstream branch/tag to vendor from (only with --to vendor)',
|
|
40
|
+
flag: '--upstream-branch',
|
|
41
|
+
required: false,
|
|
42
|
+
type: 'string',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
description: 'nest-server version to install (only with --to npm, default: from VENDOR.md baseline)',
|
|
46
|
+
flag: '--version',
|
|
47
|
+
required: false,
|
|
48
|
+
type: 'string',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
default: false,
|
|
52
|
+
description: 'Skip confirmation prompt',
|
|
53
|
+
flag: '--noConfirm',
|
|
54
|
+
required: false,
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
default: false,
|
|
59
|
+
description: 'Show the resolved plan without making any changes',
|
|
60
|
+
flag: '--dry-run',
|
|
61
|
+
required: false,
|
|
62
|
+
type: 'boolean',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
})) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const targetMode = parameters.options.to;
|
|
69
|
+
if (!targetMode || !['npm', 'vendor'].includes(targetMode)) {
|
|
70
|
+
error('Missing or invalid --to flag. Use: --to vendor or --to npm');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Find the API project root
|
|
74
|
+
const cwd = filesystem.cwd();
|
|
75
|
+
const projectDir = (0, framework_detection_1.findProjectDir)(cwd);
|
|
76
|
+
if (!projectDir) {
|
|
77
|
+
error('Could not find a package.json in the current directory or any parent. Are you inside an API project?');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Detect current mode
|
|
81
|
+
const currentMode = (0, framework_detection_1.detectFrameworkMode)(projectDir);
|
|
82
|
+
info(`Detected current mode: ${currentMode}`);
|
|
83
|
+
if (currentMode === targetMode) {
|
|
84
|
+
warning(`Project is already in ${targetMode} mode. Nothing to do.`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Dry-run: print the resolved plan and exit without any disk changes.
|
|
88
|
+
const dryRun = parameters.options['dry-run'] === true || parameters.options['dry-run'] === 'true';
|
|
89
|
+
if (dryRun) {
|
|
90
|
+
info('');
|
|
91
|
+
info('Dry-run plan:');
|
|
92
|
+
info(` projectDir: ${projectDir}`);
|
|
93
|
+
info(` currentMode: ${currentMode}`);
|
|
94
|
+
info(` targetMode: ${targetMode}`);
|
|
95
|
+
if (targetMode === 'vendor') {
|
|
96
|
+
const upstreamBranch = parameters.options['upstream-branch'];
|
|
97
|
+
info(` upstreamBranch: ${upstreamBranch || '(auto-detect from package.json)'}`);
|
|
98
|
+
info('');
|
|
99
|
+
info('Would execute:');
|
|
100
|
+
info(' 1. Clone @lenne.tech/nest-server → /tmp/lt-vendor-nest-server-*');
|
|
101
|
+
info(' 2. Copy src/core/, src/index.ts, src/core.module.ts, test/, templates/, types/ → src/core/');
|
|
102
|
+
info(' 3. Apply flatten-fix (index.ts, core.module.ts, test.helper.ts)');
|
|
103
|
+
info(' 4. Rewrite consumer imports: @lenne.tech/nest-server → relative paths');
|
|
104
|
+
info(' 5. Merge upstream deps dynamically into package.json');
|
|
105
|
+
info(' 6. Rewrite migrate scripts to use local bin/migrate.js');
|
|
106
|
+
info(' 7. Add check:vendor-freshness script');
|
|
107
|
+
info(' 8. Create src/core/VENDOR.md with baseline metadata');
|
|
108
|
+
info(' 9. Prepend vendor-mode notice to CLAUDE.md');
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const targetVersion = parameters.options.version;
|
|
112
|
+
info(` targetVersion: ${targetVersion || '(from VENDOR.md baseline)'}`);
|
|
113
|
+
info('');
|
|
114
|
+
info('Would execute:');
|
|
115
|
+
info(' 1. Read baseline version from src/core/VENDOR.md');
|
|
116
|
+
info(' 2. Warn if local patches exist in VENDOR.md');
|
|
117
|
+
info(' 3. Rewrite consumer imports: relative → @lenne.tech/nest-server');
|
|
118
|
+
info(' 4. Delete src/core/');
|
|
119
|
+
info(' 5. Restore @lenne.tech/nest-server in package.json');
|
|
120
|
+
info(' 6. Restore migrate scripts to node_modules paths');
|
|
121
|
+
info(' 7. Remove vendor artifacts and CLAUDE.md marker');
|
|
122
|
+
}
|
|
123
|
+
info('');
|
|
124
|
+
return `server convert-mode dry-run (${currentMode} → ${targetMode})`;
|
|
125
|
+
}
|
|
126
|
+
// Confirm
|
|
127
|
+
const noConfirm = parameters.options.noConfirm;
|
|
128
|
+
if (!noConfirm) {
|
|
129
|
+
const proceed = yield confirm(`Convert project from ${currentMode} mode to ${targetMode} mode?\n` +
|
|
130
|
+
` Project: ${projectDir}\n` +
|
|
131
|
+
` This will modify package.json, imports, tsconfig, and CLAUDE.md.`);
|
|
132
|
+
if (!proceed) {
|
|
133
|
+
info('Aborted.');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Execute conversion
|
|
138
|
+
if (targetMode === 'vendor') {
|
|
139
|
+
// npm → vendor
|
|
140
|
+
const upstreamBranch = parameters.options['upstream-branch'];
|
|
141
|
+
// Auto-detect version from current @lenne.tech/nest-server dep
|
|
142
|
+
let branch = upstreamBranch;
|
|
143
|
+
if (!branch) {
|
|
144
|
+
try {
|
|
145
|
+
const pkg = filesystem.read(`${projectDir}/package.json`, 'json');
|
|
146
|
+
const deps = Object.assign(Object.assign({}, ((pkg === null || pkg === void 0 ? void 0 : pkg.dependencies) || {})), ((pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies) || {}));
|
|
147
|
+
const version = deps['@lenne.tech/nest-server'];
|
|
148
|
+
if (version) {
|
|
149
|
+
// Strip semver range chars (^, ~, >=, etc.) to get the bare version
|
|
150
|
+
branch = version.replace(/^[^0-9]*/, '');
|
|
151
|
+
info(`Auto-detected @lenne.tech/nest-server version: ${branch}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (_a) {
|
|
155
|
+
// Will prompt or use HEAD
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const spinner = spin('Converting to vendor mode...');
|
|
159
|
+
try {
|
|
160
|
+
yield server.convertToVendorMode({
|
|
161
|
+
dest: projectDir,
|
|
162
|
+
upstreamBranch: branch,
|
|
163
|
+
});
|
|
164
|
+
spinner.succeed('Converted to vendor mode successfully.');
|
|
165
|
+
success('\nNext steps:');
|
|
166
|
+
info(' 1. Run: pnpm install');
|
|
167
|
+
info(' 2. Run: pnpm exec tsc --noEmit');
|
|
168
|
+
info(' 3. Run: pnpm test');
|
|
169
|
+
info(' 4. Commit the changes');
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
spinner.fail(`Conversion failed: ${err.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// vendor → npm
|
|
177
|
+
const targetVersion = parameters.options.version;
|
|
178
|
+
const spinner = spin('Converting to npm mode...');
|
|
179
|
+
try {
|
|
180
|
+
yield server.convertToNpmMode({
|
|
181
|
+
dest: projectDir,
|
|
182
|
+
targetVersion,
|
|
183
|
+
});
|
|
184
|
+
spinner.succeed('Converted to npm mode successfully.');
|
|
185
|
+
success('\nNext steps:');
|
|
186
|
+
info(' 1. Run: pnpm install');
|
|
187
|
+
info(' 2. Run: pnpm exec tsc --noEmit');
|
|
188
|
+
info(' 3. Run: pnpm test');
|
|
189
|
+
info(' 4. Commit the changes');
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
spinner.fail(`Conversion failed: ${err.message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}),
|
|
196
|
+
};
|
|
197
|
+
exports.default = ConvertModeCommand;
|
|
@@ -18,7 +18,7 @@ const NewCommand = {
|
|
|
18
18
|
hidden: false,
|
|
19
19
|
name: 'create',
|
|
20
20
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
21
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
22
22
|
// Retrieve the tools we need
|
|
23
23
|
const { config, filesystem, git, helper, meta, parameters, print: { error, info, spin, success }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, } = toolbox;
|
|
24
24
|
// Handle --help-json flag
|
|
@@ -62,6 +62,7 @@ const NewCommand = {
|
|
|
62
62
|
const configCopy = (_q = (_p = (_o = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _o === void 0 ? void 0 : _o.server) === null || _p === void 0 ? void 0 : _p.create) === null || _q === void 0 ? void 0 : _q.copy;
|
|
63
63
|
const configLink = (_t = (_s = (_r = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _r === void 0 ? void 0 : _r.server) === null || _s === void 0 ? void 0 : _s.create) === null || _t === void 0 ? void 0 : _t.link;
|
|
64
64
|
const configApiMode = (_w = (_v = (_u = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _u === void 0 ? void 0 : _u.server) === null || _v === void 0 ? void 0 : _v.create) === null || _w === void 0 ? void 0 : _w.apiMode;
|
|
65
|
+
const configFrameworkMode = (_z = (_y = (_x = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _x === void 0 ? void 0 : _x.server) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.frameworkMode;
|
|
65
66
|
// Load global defaults
|
|
66
67
|
const globalAuthor = config.getGlobalDefault(ltConfig, 'author');
|
|
67
68
|
const globalApiMode = config.getGlobalDefault(ltConfig, 'apiMode');
|
|
@@ -74,10 +75,12 @@ const NewCommand = {
|
|
|
74
75
|
const cliCopy = parameters.options.copy || parameters.options.c;
|
|
75
76
|
const cliLink = parameters.options.link;
|
|
76
77
|
const cliApiMode = parameters.options['api-mode'] || parameters.options.apiMode;
|
|
78
|
+
const cliFrameworkMode = parameters.options['framework-mode'];
|
|
79
|
+
const cliFrameworkUpstreamBranch = parameters.options['framework-upstream-branch'];
|
|
77
80
|
// Determine noConfirm with priority: CLI > config > global > default (false)
|
|
78
81
|
const noConfirm = config.getNoConfirm({
|
|
79
82
|
cliValue: cliNoConfirm,
|
|
80
|
-
commandConfig: (
|
|
83
|
+
commandConfig: (_1 = (_0 = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _0 === void 0 ? void 0 : _0.server) === null || _1 === void 0 ? void 0 : _1.create,
|
|
81
84
|
config: ltConfig,
|
|
82
85
|
});
|
|
83
86
|
// Start timer
|
|
@@ -178,6 +181,39 @@ const NewCommand = {
|
|
|
178
181
|
]);
|
|
179
182
|
apiMode = apiModeChoice.apiMode.split(' - ')[0];
|
|
180
183
|
}
|
|
184
|
+
// Determine framework consumption mode — same resolution cascade as
|
|
185
|
+
// lt fullstack init: CLI flag > lt.config > interactive (default npm).
|
|
186
|
+
let frameworkMode;
|
|
187
|
+
if (cliFrameworkMode === 'npm' || cliFrameworkMode === 'vendor') {
|
|
188
|
+
frameworkMode = cliFrameworkMode;
|
|
189
|
+
}
|
|
190
|
+
else if (cliFrameworkMode) {
|
|
191
|
+
error(`Invalid --framework-mode value "${cliFrameworkMode}". Use "npm" or "vendor".`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
else if (configFrameworkMode === 'npm' || configFrameworkMode === 'vendor') {
|
|
195
|
+
frameworkMode = configFrameworkMode;
|
|
196
|
+
info(`Using framework mode from lt.config: ${frameworkMode}`);
|
|
197
|
+
}
|
|
198
|
+
else if (noConfirm) {
|
|
199
|
+
frameworkMode = 'npm';
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const frameworkModeChoice = yield ask({
|
|
203
|
+
choices: [
|
|
204
|
+
'npm - @lenne.tech/nest-server as npm dependency (classic, stable)',
|
|
205
|
+
'vendor - framework core vendored into src/core/ (pilot, allows local patches)',
|
|
206
|
+
],
|
|
207
|
+
initial: 0,
|
|
208
|
+
message: 'Framework consumption mode?',
|
|
209
|
+
name: 'frameworkMode',
|
|
210
|
+
type: 'select',
|
|
211
|
+
});
|
|
212
|
+
frameworkMode = frameworkModeChoice.frameworkMode.startsWith('vendor') ? 'vendor' : 'npm';
|
|
213
|
+
}
|
|
214
|
+
const frameworkUpstreamBranch = typeof cliFrameworkUpstreamBranch === 'string' && cliFrameworkUpstreamBranch.length > 0
|
|
215
|
+
? cliFrameworkUpstreamBranch
|
|
216
|
+
: undefined;
|
|
181
217
|
// Setup server using Server extension
|
|
182
218
|
const setupSpinner = spin(`Setting up server${linkPath ? ' (link)' : copyPath ? ' (copy)' : branch ? ` (branch: ${branch})` : ''}`);
|
|
183
219
|
const result = yield server.setupServer(`./${projectDir}`, {
|
|
@@ -186,6 +222,8 @@ const NewCommand = {
|
|
|
186
222
|
branch,
|
|
187
223
|
copyPath,
|
|
188
224
|
description,
|
|
225
|
+
frameworkMode,
|
|
226
|
+
frameworkUpstreamBranch,
|
|
189
227
|
linkPath,
|
|
190
228
|
name,
|
|
191
229
|
projectDir,
|
|
@@ -213,7 +251,7 @@ const NewCommand = {
|
|
|
213
251
|
}
|
|
214
252
|
// Git initialization (after npm install which is done in setupServer)
|
|
215
253
|
if (git) {
|
|
216
|
-
const inGit = (
|
|
254
|
+
const inGit = (_2 = (yield system.run('git rev-parse --is-inside-work-tree'))) === null || _2 === void 0 ? void 0 : _2.trim();
|
|
217
255
|
if (inGit !== 'true') {
|
|
218
256
|
// Determine initGit with priority: CLI > config > interactive
|
|
219
257
|
let initializeGit;
|