@lenne.tech/cli 1.13.0 → 1.14.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 +31 -0
- package/build/commands/fullstack/init.js +44 -18
- package/build/commands/server/create.js +53 -26
- package/build/extensions/server.js +72 -43
- package/docs/LT-ECOSYSTEM-GUIDE.md +1 -0
- package/docs/commands.md +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,37 @@ $ lt fullstack init --name myapp --framework-mode vendor --dry-run --noConfirm
|
|
|
94
94
|
$ lt server create --name myapp --framework-mode vendor
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
### Experimental: `--next` (nest-base)
|
|
98
|
+
|
|
99
|
+
Both `lt fullstack init` and `lt server create` support an experimental
|
|
100
|
+
`--next` flag that swaps the API template from
|
|
101
|
+
[`nest-server-starter`](https://github.com/lenneTech/nest-server-starter)
|
|
102
|
+
(MongoDB) to [`nest-base`](https://github.com/lenneTech/nest-base) — a new
|
|
103
|
+
NestJS stack on **Bun + Prisma 7 + Postgres + Better-Auth** with a built-in
|
|
104
|
+
`/dev` cockpit.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# experimental standalone api
|
|
108
|
+
$ lt server create my-next-api --next --noConfirm
|
|
109
|
+
|
|
110
|
+
# experimental fullstack (nuxt + nest-base)
|
|
111
|
+
$ lt fullstack init --name my-next-app --frontend nuxt --next --noConfirm
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
When `--next` is set the CLI:
|
|
115
|
+
|
|
116
|
+
- clones `nest-base` instead of `nest-server-starter`,
|
|
117
|
+
- forces `--api-mode Rest` and `--framework-mode npm` (other modes are not
|
|
118
|
+
applicable to nest-base),
|
|
119
|
+
- skips `nest-server-starter`-specific patching (`config.env.ts`,
|
|
120
|
+
`main.ts` Swagger setup, `meta.json`, `lt.config.json`),
|
|
121
|
+
- skips the workspace install in fullstack mode — run `pnpm install` for
|
|
122
|
+
the frontend and `bun install` for the API yourself.
|
|
123
|
+
|
|
124
|
+
This option is **experimental** and may change. The downstream `lt server
|
|
125
|
+
module/object/addProp/test/permissions` commands target the classic
|
|
126
|
+
`nest-server` layout and are not yet compatible with `nest-base`.
|
|
127
|
+
|
|
97
128
|
### Working on an existing project
|
|
98
129
|
|
|
99
130
|
All `lt server …` commands (module, object, addProp, test, permissions)
|
|
@@ -27,7 +27,7 @@ const NewCommand = {
|
|
|
27
27
|
// Info
|
|
28
28
|
info('Create a new fullstack workspace');
|
|
29
29
|
// Hint for non-interactive callers (e.g. Claude Code)
|
|
30
|
-
toolbox.tools.nonInteractiveHint('lt fullstack init --name <name> --frontend <nuxt|angular> --api-mode <Rest|GraphQL|Both> --framework-mode <npm|vendor> [--framework-upstream-branch <ref>] [--dry-run] --noConfirm');
|
|
30
|
+
toolbox.tools.nonInteractiveHint('lt fullstack init --name <name> --frontend <nuxt|angular> --api-mode <Rest|GraphQL|Both> --framework-mode <npm|vendor> [--framework-upstream-branch <ref>] [--next] [--dry-run] --noConfirm');
|
|
31
31
|
// Check git
|
|
32
32
|
if (!(yield git.gitInstalled())) {
|
|
33
33
|
return;
|
|
@@ -46,8 +46,9 @@ const NewCommand = {
|
|
|
46
46
|
const configFrontendLink = (_v = (_u = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _u === void 0 ? void 0 : _u.fullstack) === null || _v === void 0 ? void 0 : _v.frontendLink;
|
|
47
47
|
const configFrameworkMode = (_x = (_w = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _w === void 0 ? void 0 : _w.fullstack) === null || _x === void 0 ? void 0 : _x.frameworkMode;
|
|
48
48
|
// Parse CLI arguments
|
|
49
|
-
const { 'api-branch': cliApiBranch, 'api-copy': cliApiCopy, 'api-link': cliApiLink, 'api-mode': cliApiMode, 'dry-run': cliDryRun, 'framework-mode': cliFrameworkMode, 'framework-upstream-branch': cliFrameworkUpstreamBranch, frontend: cliFrontend, 'frontend-branch': cliFrontendBranch, 'frontend-copy': cliFrontendCopy, 'frontend-framework-mode': cliFrontendFrameworkMode, 'frontend-link': cliFrontendLink, git: cliGit, 'git-link': cliGitLink, name: cliName, } = parameters.options;
|
|
49
|
+
const { 'api-branch': cliApiBranch, 'api-copy': cliApiCopy, 'api-link': cliApiLink, 'api-mode': cliApiMode, 'dry-run': cliDryRun, 'framework-mode': cliFrameworkMode, 'framework-upstream-branch': cliFrameworkUpstreamBranch, frontend: cliFrontend, 'frontend-branch': cliFrontendBranch, 'frontend-copy': cliFrontendCopy, 'frontend-framework-mode': cliFrontendFrameworkMode, 'frontend-link': cliFrontendLink, git: cliGit, 'git-link': cliGitLink, name: cliName, next: cliNext, } = parameters.options;
|
|
50
50
|
const dryRun = cliDryRun === true || cliDryRun === 'true';
|
|
51
|
+
const experimental = cliNext === true || cliNext === 'true';
|
|
51
52
|
const frameworkUpstreamBranch = typeof cliFrameworkUpstreamBranch === 'string' && cliFrameworkUpstreamBranch.length > 0
|
|
52
53
|
? cliFrameworkUpstreamBranch
|
|
53
54
|
: undefined;
|
|
@@ -109,7 +110,11 @@ const NewCommand = {
|
|
|
109
110
|
// Determine API mode with priority: CLI > config > global > interactive (default: Rest)
|
|
110
111
|
const globalApiMode = config.getGlobalDefault(ltConfig, 'apiMode');
|
|
111
112
|
let apiMode;
|
|
112
|
-
if (
|
|
113
|
+
if (experimental) {
|
|
114
|
+
apiMode = 'Rest';
|
|
115
|
+
info('Using experimental nest-base template (Bun + Prisma + Postgres + Better-Auth)');
|
|
116
|
+
}
|
|
117
|
+
else if (cliApiMode) {
|
|
113
118
|
apiMode = cliApiMode;
|
|
114
119
|
}
|
|
115
120
|
else if (configApiMode) {
|
|
@@ -154,7 +159,10 @@ const NewCommand = {
|
|
|
154
159
|
//
|
|
155
160
|
// Default is still 'npm' until the vendoring pilot is fully evaluated.
|
|
156
161
|
let frameworkMode;
|
|
157
|
-
if (
|
|
162
|
+
if (experimental) {
|
|
163
|
+
frameworkMode = 'npm';
|
|
164
|
+
}
|
|
165
|
+
else if (cliFrameworkMode === 'npm' || cliFrameworkMode === 'vendor') {
|
|
158
166
|
frameworkMode = cliFrameworkMode;
|
|
159
167
|
}
|
|
160
168
|
else if (cliFrameworkMode) {
|
|
@@ -291,7 +299,10 @@ const NewCommand = {
|
|
|
291
299
|
info('Would execute:');
|
|
292
300
|
info(` 1. git clone lt-monorepo → ${projectDir}/`);
|
|
293
301
|
info(` 2. setup frontend (${frontend}) → ${projectDir}/projects/app`);
|
|
294
|
-
if (
|
|
302
|
+
if (experimental) {
|
|
303
|
+
info(` 3. clone nest-base (experimental) → ${projectDir}/projects/api`);
|
|
304
|
+
}
|
|
305
|
+
else if (frameworkMode === 'vendor') {
|
|
295
306
|
info(` 3. clone nest-server-starter → ${projectDir}/projects/api`);
|
|
296
307
|
info(` 4. clone @lenne.tech/nest-server${frameworkUpstreamBranch ? ` (branch/tag: ${frameworkUpstreamBranch})` : ''} → /tmp`);
|
|
297
308
|
info(` 5. vendor core/ + flatten-fix + codemod consumer imports`);
|
|
@@ -420,6 +431,7 @@ const NewCommand = {
|
|
|
420
431
|
apiMode,
|
|
421
432
|
branch: apiBranch,
|
|
422
433
|
copyPath: apiCopy,
|
|
434
|
+
experimental,
|
|
423
435
|
frameworkMode,
|
|
424
436
|
frameworkUpstreamBranch,
|
|
425
437
|
linkPath: apiLink,
|
|
@@ -466,15 +478,20 @@ const NewCommand = {
|
|
|
466
478
|
// instead.` and silently disables CVE overrides.
|
|
467
479
|
(0, hoist_workspace_pnpm_config_1.hoistWorkspacePnpmConfig)({ filesystem, projectDir, subProjects: ['projects/api', 'projects/app'] });
|
|
468
480
|
// Install all packages
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
481
|
+
if (!experimental) {
|
|
482
|
+
const installSpinner = spin('Install all packages');
|
|
483
|
+
try {
|
|
484
|
+
const detectedPm = toolbox.pm.detect(projectDir);
|
|
485
|
+
yield system.run(`cd ${projectDir} && ${toolbox.pm.install(detectedPm)} && ${toolbox.pm.run('init', detectedPm)}`);
|
|
486
|
+
installSpinner.succeed('Successfully installed all packages');
|
|
487
|
+
}
|
|
488
|
+
catch (err) {
|
|
489
|
+
installSpinner.fail(`Failed to install packages: ${err.message}`);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
474
492
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return;
|
|
493
|
+
else {
|
|
494
|
+
info('Skipping workspace install — run `bun install` (api) and `pnpm install` (app) manually.');
|
|
478
495
|
}
|
|
479
496
|
// Post-install format pass. processApiMode (run earlier in
|
|
480
497
|
// setupServerForFullstack) and convertAppCloneToVendored rewrite
|
|
@@ -482,10 +499,10 @@ const NewCommand = {
|
|
|
482
499
|
// `pnpm run format:check` (multi-line arrays/imports after region
|
|
483
500
|
// stripping, import-path rewrites that now fit single-line). The
|
|
484
501
|
// formatter is only available after install, so we normalize here.
|
|
485
|
-
if (apiMode && filesystem.isDirectory(`${projectDir}/projects/api`)) {
|
|
502
|
+
if (!experimental && apiMode && filesystem.isDirectory(`${projectDir}/projects/api`)) {
|
|
486
503
|
yield toolbox.apiMode.formatProject(`${projectDir}/projects/api`);
|
|
487
504
|
}
|
|
488
|
-
if (isNuxt && filesystem.isDirectory(`${projectDir}/projects/app`)) {
|
|
505
|
+
if (!experimental && isNuxt && filesystem.isDirectory(`${projectDir}/projects/app`)) {
|
|
489
506
|
yield toolbox.apiMode.formatProject(`${projectDir}/projects/app`);
|
|
490
507
|
}
|
|
491
508
|
// Create initial commit after everything is set up
|
|
@@ -511,9 +528,18 @@ const NewCommand = {
|
|
|
511
528
|
success(`Generated fullstack workspace with ${frontend} in ${projectDir} with ${name} app in ${helper.msToMinutesAndSeconds(timer())}m.`);
|
|
512
529
|
info('');
|
|
513
530
|
info('Next:');
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
531
|
+
if (experimental) {
|
|
532
|
+
info(` $ cd ${projectDir}`);
|
|
533
|
+
info(' Frontend: cd projects/app && pnpm install');
|
|
534
|
+
info(' API: cd projects/api && bun install');
|
|
535
|
+
info(' Configure projects/api/.env (see .env.example)');
|
|
536
|
+
info(' Start Postgres + run prisma generate / migrate');
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
info(` Run ${name}`);
|
|
540
|
+
info(` $ cd ${projectDir}`);
|
|
541
|
+
info(` $ ${toolbox.pm.run('start')}`);
|
|
542
|
+
}
|
|
517
543
|
info('');
|
|
518
544
|
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
519
545
|
process.exit();
|
|
@@ -42,6 +42,13 @@ const NewCommand = {
|
|
|
42
42
|
{ description: 'Git branch to clone from', flag: '--branch', required: false, type: 'string' },
|
|
43
43
|
{ description: 'Copy from local path instead of cloning', flag: '--copy', required: false, type: 'string' },
|
|
44
44
|
{ description: 'Symlink to local path instead of cloning', flag: '--link', required: false, type: 'string' },
|
|
45
|
+
{
|
|
46
|
+
default: false,
|
|
47
|
+
description: 'Use experimental nest-base template (Bun + Prisma + Postgres)',
|
|
48
|
+
flag: '--next',
|
|
49
|
+
required: false,
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
},
|
|
45
52
|
{
|
|
46
53
|
default: false,
|
|
47
54
|
description: 'Skip all interactive prompts',
|
|
@@ -77,6 +84,7 @@ const NewCommand = {
|
|
|
77
84
|
const cliApiMode = parameters.options['api-mode'] || parameters.options.apiMode;
|
|
78
85
|
const cliFrameworkMode = parameters.options['framework-mode'];
|
|
79
86
|
const cliFrameworkUpstreamBranch = parameters.options['framework-upstream-branch'];
|
|
87
|
+
const experimental = parameters.options.next === true || parameters.options.next === 'true';
|
|
80
88
|
// Determine noConfirm with priority: CLI > config > global > default (false)
|
|
81
89
|
const noConfirm = config.getNoConfirm({
|
|
82
90
|
cliValue: cliNoConfirm,
|
|
@@ -88,7 +96,7 @@ const NewCommand = {
|
|
|
88
96
|
// Info
|
|
89
97
|
info('Create a new server');
|
|
90
98
|
// Hint for non-interactive callers (e.g. Claude Code)
|
|
91
|
-
toolbox.tools.nonInteractiveHint('lt server create --name <name> --api-mode <Rest|GraphQL|Both> --noConfirm');
|
|
99
|
+
toolbox.tools.nonInteractiveHint('lt server create --name <name> --api-mode <Rest|GraphQL|Both> [--next] --noConfirm');
|
|
92
100
|
// Check git
|
|
93
101
|
if (!(yield git.gitInstalled())) {
|
|
94
102
|
return;
|
|
@@ -150,7 +158,11 @@ const NewCommand = {
|
|
|
150
158
|
}
|
|
151
159
|
// Determine API mode with priority: CLI > config > global > interactive (default: Rest)
|
|
152
160
|
let apiMode;
|
|
153
|
-
if (
|
|
161
|
+
if (experimental) {
|
|
162
|
+
apiMode = 'Rest';
|
|
163
|
+
info('Using experimental nest-base template (Bun + Prisma + Postgres + Better-Auth)');
|
|
164
|
+
}
|
|
165
|
+
else if (cliApiMode) {
|
|
154
166
|
apiMode = cliApiMode;
|
|
155
167
|
}
|
|
156
168
|
else if (configApiMode) {
|
|
@@ -184,7 +196,10 @@ const NewCommand = {
|
|
|
184
196
|
// Determine framework consumption mode — same resolution cascade as
|
|
185
197
|
// lt fullstack init: CLI flag > lt.config > interactive (default npm).
|
|
186
198
|
let frameworkMode;
|
|
187
|
-
if (
|
|
199
|
+
if (experimental) {
|
|
200
|
+
frameworkMode = 'npm';
|
|
201
|
+
}
|
|
202
|
+
else if (cliFrameworkMode === 'npm' || cliFrameworkMode === 'vendor') {
|
|
188
203
|
frameworkMode = cliFrameworkMode;
|
|
189
204
|
}
|
|
190
205
|
else if (cliFrameworkMode) {
|
|
@@ -222,6 +237,7 @@ const NewCommand = {
|
|
|
222
237
|
branch,
|
|
223
238
|
copyPath,
|
|
224
239
|
description,
|
|
240
|
+
experimental,
|
|
225
241
|
frameworkMode,
|
|
226
242
|
frameworkUpstreamBranch,
|
|
227
243
|
linkPath,
|
|
@@ -279,36 +295,47 @@ const NewCommand = {
|
|
|
279
295
|
}
|
|
280
296
|
// Derive controller type from API mode and save project config
|
|
281
297
|
const controllerType = apiMode;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
298
|
+
if (!experimental) {
|
|
299
|
+
// Create lt.config.json
|
|
300
|
+
const projectConfig = {
|
|
301
|
+
commands: {
|
|
302
|
+
server: {
|
|
303
|
+
module: {
|
|
304
|
+
controller: controllerType,
|
|
305
|
+
},
|
|
288
306
|
},
|
|
289
307
|
},
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
308
|
+
meta: {
|
|
309
|
+
apiMode,
|
|
310
|
+
version: '1.0.0',
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
const configPath = filesystem.path(projectDir, 'lt.config.json');
|
|
314
|
+
filesystem.write(configPath, projectConfig, { jsonIndent: 2 });
|
|
315
|
+
info('');
|
|
316
|
+
success(`Configuration saved to ${projectDir}/lt.config.json`);
|
|
317
|
+
info(` API mode: ${apiMode}`);
|
|
318
|
+
info(` Default controller type: ${controllerType}`);
|
|
319
|
+
}
|
|
302
320
|
// We're done, so show what to do next
|
|
303
321
|
info('');
|
|
304
322
|
success(`Generated ${name} server with lenne.Tech CLI ${meta.version()} in ${helper.msToMinutesAndSeconds(timer())}m.`);
|
|
305
323
|
info('');
|
|
306
324
|
info('Next:');
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
if (experimental) {
|
|
326
|
+
info(` Go to project directory: cd ${projectDir}`);
|
|
327
|
+
info(' Install dependencies: bun install');
|
|
328
|
+
info(' Configure .env (see .env.example)');
|
|
329
|
+
info(' Start Postgres + run prisma generate / migrate');
|
|
330
|
+
info(' Start server: bun run dev');
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
info(' Start database server (e.g. MongoDB)');
|
|
334
|
+
info(` Check config: ${projectDir}/src/config.env.ts`);
|
|
335
|
+
info(` Go to project directory: cd ${projectDir}`);
|
|
336
|
+
info(` Run tests: ${toolbox.pm.run('test:e2e')}`);
|
|
337
|
+
info(` Start server: ${toolbox.pm.run('start')}`);
|
|
338
|
+
}
|
|
312
339
|
info('');
|
|
313
340
|
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
314
341
|
process.exit();
|
|
@@ -633,13 +633,16 @@ class Server {
|
|
|
633
633
|
setupServer(dest, options) {
|
|
634
634
|
return __awaiter(this, void 0, void 0, function* () {
|
|
635
635
|
const { apiMode: apiModeHelper, patching, system, template, templateHelper } = this.toolbox;
|
|
636
|
-
const { apiMode, author = '', branch, copyPath, description = '', frameworkMode = 'npm', frameworkUpstreamBranch, linkPath, name, projectDir, skipInstall = false, skipPatching = false, } = options;
|
|
636
|
+
const { apiMode, author = '', branch, copyPath, description = '', experimental = false, frameworkMode = 'npm', frameworkUpstreamBranch, linkPath, name, projectDir, skipInstall = false, skipPatching = false, } = options;
|
|
637
|
+
const repoUrl = experimental
|
|
638
|
+
? 'https://github.com/lenneTech/nest-base.git'
|
|
639
|
+
: 'https://github.com/lenneTech/nest-server-starter.git';
|
|
637
640
|
// Setup template
|
|
638
641
|
const result = yield templateHelper.setup(dest, {
|
|
639
642
|
branch,
|
|
640
643
|
copyPath,
|
|
641
644
|
linkPath,
|
|
642
|
-
repoUrl
|
|
645
|
+
repoUrl,
|
|
643
646
|
});
|
|
644
647
|
if (!result.success) {
|
|
645
648
|
return { method: result.method, path: result.path, success: false };
|
|
@@ -651,18 +654,20 @@ class Server {
|
|
|
651
654
|
// Apply patches (config.env.ts, package.json, main.ts, meta.json)
|
|
652
655
|
if (!skipPatching) {
|
|
653
656
|
try {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
.
|
|
665
|
-
|
|
657
|
+
if (!experimental) {
|
|
658
|
+
// Generate README
|
|
659
|
+
yield template.generate({
|
|
660
|
+
props: { description, name },
|
|
661
|
+
target: `${dest}/README.md`,
|
|
662
|
+
template: 'nest-server-starter/README.md.ejs',
|
|
663
|
+
});
|
|
664
|
+
// Replace secret or private keys and update database names via AST
|
|
665
|
+
this.patchConfigEnvTs(`${dest}/src/config.env.ts`, projectDir);
|
|
666
|
+
// Update Swagger configuration in main.ts
|
|
667
|
+
yield patching.update(`${dest}/src/main.ts`, (content) => content
|
|
668
|
+
.replace(/\.setTitle\('.*?'\)/, `.setTitle('${name}')`)
|
|
669
|
+
.replace(/\.setDescription\('.*?'\)/, `.setDescription('${description || name}')`));
|
|
670
|
+
}
|
|
666
671
|
// Update package.json
|
|
667
672
|
yield patching.update(`${dest}/package.json`, (config) => {
|
|
668
673
|
config.author = author;
|
|
@@ -674,13 +679,15 @@ class Server {
|
|
|
674
679
|
config.version = '0.0.1';
|
|
675
680
|
return config;
|
|
676
681
|
});
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
682
|
+
if (!experimental) {
|
|
683
|
+
// Update meta.json if exists
|
|
684
|
+
if (this.filesystem.exists(`${dest}/src/meta`)) {
|
|
685
|
+
yield patching.update(`${dest}/src/meta`, (config) => {
|
|
686
|
+
config.name = name;
|
|
687
|
+
config.description = description;
|
|
688
|
+
return config;
|
|
689
|
+
});
|
|
690
|
+
}
|
|
684
691
|
}
|
|
685
692
|
}
|
|
686
693
|
catch (err) {
|
|
@@ -703,7 +710,7 @@ class Server {
|
|
|
703
710
|
// manifest (same dance as in setupServerForFullstack).
|
|
704
711
|
let standaloneVendorUpstreamDeps = {};
|
|
705
712
|
let standaloneVendorCoreEssentials = [];
|
|
706
|
-
if (frameworkMode === 'vendor') {
|
|
713
|
+
if (!experimental && frameworkMode === 'vendor') {
|
|
707
714
|
try {
|
|
708
715
|
const converted = yield this.convertCloneToVendored({
|
|
709
716
|
dest,
|
|
@@ -718,7 +725,7 @@ class Server {
|
|
|
718
725
|
standaloneVendorCoreEssentials = this.readApiModeGraphqlEssentials(dest);
|
|
719
726
|
}
|
|
720
727
|
// Process API mode (before install so package.json is correct)
|
|
721
|
-
if (apiMode) {
|
|
728
|
+
if (!experimental && apiMode) {
|
|
722
729
|
try {
|
|
723
730
|
yield apiModeHelper.processApiMode(dest, apiMode);
|
|
724
731
|
}
|
|
@@ -727,7 +734,7 @@ class Server {
|
|
|
727
734
|
}
|
|
728
735
|
}
|
|
729
736
|
// Restore core essentials after processApiMode stripped them (vendor + REST only).
|
|
730
|
-
if (frameworkMode === 'vendor' && apiMode === 'Rest') {
|
|
737
|
+
if (!experimental && frameworkMode === 'vendor' && apiMode === 'Rest') {
|
|
731
738
|
try {
|
|
732
739
|
this.restoreVendorCoreEssentials({
|
|
733
740
|
dest,
|
|
@@ -740,9 +747,11 @@ class Server {
|
|
|
740
747
|
}
|
|
741
748
|
}
|
|
742
749
|
// Patch CLAUDE.md with API mode info
|
|
743
|
-
|
|
750
|
+
if (!experimental) {
|
|
751
|
+
this.patchClaudeMdApiMode(dest, apiMode);
|
|
752
|
+
}
|
|
744
753
|
// Install packages
|
|
745
|
-
if (!skipInstall) {
|
|
754
|
+
if (!skipInstall && !experimental) {
|
|
746
755
|
try {
|
|
747
756
|
const { pm } = this.toolbox;
|
|
748
757
|
yield system.run(`cd "${dest}" && ${pm.install(pm.detect(dest))}`);
|
|
@@ -771,7 +780,10 @@ class Server {
|
|
|
771
780
|
setupServerForFullstack(dest, options) {
|
|
772
781
|
return __awaiter(this, void 0, void 0, function* () {
|
|
773
782
|
const { apiMode: apiModeHelper, templateHelper } = this.toolbox;
|
|
774
|
-
const { apiMode, branch, copyPath, frameworkMode = 'npm', frameworkUpstreamBranch, linkPath, name, projectDir, } = options;
|
|
783
|
+
const { apiMode, branch, copyPath, experimental = false, frameworkMode = 'npm', frameworkUpstreamBranch, linkPath, name, projectDir, } = options;
|
|
784
|
+
const repoUrl = experimental
|
|
785
|
+
? 'https://github.com/lenneTech/nest-base.git'
|
|
786
|
+
: 'https://github.com/lenneTech/nest-server-starter';
|
|
775
787
|
// Both npm and vendor mode clone nest-server-starter as the base. The
|
|
776
788
|
// starter ships the minimal consumer conventions a project needs
|
|
777
789
|
// (src/server/common/models/persistence.model.ts, src/server/modules/user/,
|
|
@@ -791,7 +803,7 @@ class Server {
|
|
|
791
803
|
branch,
|
|
792
804
|
copyPath,
|
|
793
805
|
linkPath,
|
|
794
|
-
repoUrl
|
|
806
|
+
repoUrl,
|
|
795
807
|
});
|
|
796
808
|
if (!result.success) {
|
|
797
809
|
return { method: result.method, path: result.path, success: false };
|
|
@@ -801,18 +813,33 @@ class Server {
|
|
|
801
813
|
return { method: 'link', path: result.path, success: true };
|
|
802
814
|
}
|
|
803
815
|
// Apply minimal patches for fullstack
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
816
|
+
if (!experimental) {
|
|
817
|
+
try {
|
|
818
|
+
// Write meta.json
|
|
819
|
+
this.filesystem.write(`${dest}/src/meta.json`, {
|
|
820
|
+
description: `API for ${name} app`,
|
|
821
|
+
name: `${name}-api-server`,
|
|
822
|
+
version: '0.0.0',
|
|
823
|
+
});
|
|
824
|
+
// Replace secret or private keys and update database names via AST
|
|
825
|
+
this.patchConfigEnvTs(`${dest}/src/config.env.ts`, projectDir);
|
|
826
|
+
}
|
|
827
|
+
catch (err) {
|
|
828
|
+
return { method: result.method, path: dest, success: false };
|
|
829
|
+
}
|
|
813
830
|
}
|
|
814
|
-
|
|
815
|
-
|
|
831
|
+
else {
|
|
832
|
+
try {
|
|
833
|
+
yield this.toolbox.patching.update(`${dest}/package.json`, (config) => {
|
|
834
|
+
config.name = projectDir;
|
|
835
|
+
config.description = `API for ${name} app`;
|
|
836
|
+
config.version = '0.0.0';
|
|
837
|
+
return config;
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
catch (err) {
|
|
841
|
+
return { method: result.method, path: dest, success: false };
|
|
842
|
+
}
|
|
816
843
|
}
|
|
817
844
|
// Clean up copied template artifacts
|
|
818
845
|
if (result.method === 'copy') {
|
|
@@ -833,7 +860,7 @@ class Server {
|
|
|
833
860
|
// without hard-coding package lists.
|
|
834
861
|
let vendorUpstreamDeps = {};
|
|
835
862
|
let vendorCoreEssentials = [];
|
|
836
|
-
if (frameworkMode === 'vendor') {
|
|
863
|
+
if (!experimental && frameworkMode === 'vendor') {
|
|
837
864
|
try {
|
|
838
865
|
const converted = yield this.convertCloneToVendored({
|
|
839
866
|
dest,
|
|
@@ -856,7 +883,7 @@ class Server {
|
|
|
856
883
|
vendorCoreEssentials = this.readApiModeGraphqlEssentials(dest);
|
|
857
884
|
}
|
|
858
885
|
// Process API mode (before install which happens at monorepo level)
|
|
859
|
-
if (apiMode) {
|
|
886
|
+
if (!experimental && apiMode) {
|
|
860
887
|
try {
|
|
861
888
|
yield apiModeHelper.processApiMode(dest, apiMode);
|
|
862
889
|
}
|
|
@@ -867,7 +894,7 @@ class Server {
|
|
|
867
894
|
// In vendor mode + REST, re-add the graphql essentials that
|
|
868
895
|
// processApiMode just stripped. Both and GraphQL keep all packages
|
|
869
896
|
// by construction and don't need restoration.
|
|
870
|
-
if (frameworkMode === 'vendor' && apiMode === 'Rest') {
|
|
897
|
+
if (!experimental && frameworkMode === 'vendor' && apiMode === 'Rest') {
|
|
871
898
|
try {
|
|
872
899
|
this.restoreVendorCoreEssentials({
|
|
873
900
|
dest,
|
|
@@ -881,7 +908,9 @@ class Server {
|
|
|
881
908
|
}
|
|
882
909
|
}
|
|
883
910
|
// Patch CLAUDE.md with API mode info
|
|
884
|
-
|
|
911
|
+
if (!experimental) {
|
|
912
|
+
this.patchClaudeMdApiMode(dest, apiMode);
|
|
913
|
+
}
|
|
885
914
|
return { method: result.method, path: dest, success: true };
|
|
886
915
|
});
|
|
887
916
|
}
|
|
@@ -117,6 +117,7 @@ Flags:
|
|
|
117
117
|
- `--frontend-framework-mode npm|vendor` — Frontend mode
|
|
118
118
|
- `--framework-upstream-branch <tag>` — Specific nest-server version for vendor
|
|
119
119
|
- `--dry-run` — Show plan without making changes
|
|
120
|
+
- `--next` — **Experimental:** clone [`nest-base`](https://github.com/lenneTech/nest-base) (Bun + Prisma 7 + Postgres + Better-Auth) for the API instead of `nest-server-starter`. Forces `--api-mode Rest`, `--framework-mode npm`, and skips workspace install (run `pnpm install` for app and `bun install` for api manually). Downstream `lt server module/object/addProp/test/permissions` are NOT compatible with the resulting layout.
|
|
120
121
|
|
|
121
122
|
---
|
|
122
123
|
|
package/docs/commands.md
CHANGED
|
@@ -79,13 +79,16 @@ lt server create [name] [options]
|
|
|
79
79
|
|--------|-------------|
|
|
80
80
|
| `--description <text>` | Project description |
|
|
81
81
|
| `--author <name>` | Author name |
|
|
82
|
+
| `--api-mode <Rest\|GraphQL\|Both>` | API mode (ignored with `--next`) |
|
|
83
|
+
| `--framework-mode <npm\|vendor>` | Framework consumption mode (ignored with `--next`) |
|
|
82
84
|
| `--branch <branch>` / `-b` | Branch of nest-server-starter to use as template |
|
|
83
85
|
| `--copy <path>` / `-c` | Copy from local template directory instead of cloning |
|
|
84
86
|
| `--link <path>` | Symlink to local template directory (fastest, changes affect original) |
|
|
85
87
|
| `--git` | Initialize git repository |
|
|
88
|
+
| `--next` | **Experimental:** clone [`nest-base`](https://github.com/lenneTech/nest-base) (Bun + Prisma 7 + Postgres + Better-Auth) instead of `nest-server-starter`. Skips API-mode / vendor-mode / install / lt.config.json processing. |
|
|
86
89
|
| `--noConfirm` | Skip confirmation prompts |
|
|
87
90
|
|
|
88
|
-
**CLAUDE.md Patching:** If the project contains a `CLAUDE.md`, the generic API mode description is replaced with the selected mode. In single-mode projects (`Rest` or `GraphQL`), the "API Mode System" documentation section is condensed to a brief note.
|
|
91
|
+
**CLAUDE.md Patching:** If the project contains a `CLAUDE.md`, the generic API mode description is replaced with the selected mode. In single-mode projects (`Rest` or `GraphQL`), the "API Mode System" documentation section is condensed to a brief note. Skipped when `--next` is used.
|
|
89
92
|
|
|
90
93
|
**Configuration:** `commands.server.create.*`, `defaults.author`, `defaults.noConfirm`
|
|
91
94
|
|
|
@@ -532,6 +535,7 @@ lt fullstack init [options]
|
|
|
532
535
|
| `--framework-mode <mode>` | Backend framework consumption mode: `npm` (classic) or `vendor` (pilot, core copied to `src/core/`) |
|
|
533
536
|
| `--framework-upstream-branch <ref>` | Upstream `nest-server` branch/tag to vendor from (only with `--framework-mode vendor`) |
|
|
534
537
|
| `--frontend-framework-mode <mode>` | Frontend framework consumption mode: `npm` or `vendor` (nuxt-extensions copied to `app/core/`) |
|
|
538
|
+
| `--next` | **Experimental:** clone [`nest-base`](https://github.com/lenneTech/nest-base) (Bun + Prisma 7 + Postgres + Better-Auth) for the API instead of `nest-server-starter`. Forces `--api-mode Rest` and `--framework-mode npm`, skips workspace install (run `pnpm install` for app and `bun install` for api manually). |
|
|
535
539
|
| `--dry-run` | Print the resolved plan without making any changes |
|
|
536
540
|
| `--git` | Push initial commit to remote repository (git is always initialized) |
|
|
537
541
|
| `--git-link <url>` | Git remote repository URL (required when `--git` is true) |
|