@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
package/README.md
CHANGED
|
@@ -38,9 +38,11 @@ $ lt
|
|
|
38
38
|
|
|
39
39
|
## Documentation
|
|
40
40
|
|
|
41
|
-
- [
|
|
42
|
-
- [
|
|
43
|
-
- [
|
|
41
|
+
- **[LT-ECOSYSTEM-GUIDE](docs/LT-ECOSYSTEM-GUIDE.md)** — Complete reference for `lt` CLI **and** the `lt-dev` Claude-Code Plugin (architecture, functions, vendor-mode workflows, agents, skills)
|
|
42
|
+
- **[VENDOR-MODE-WORKFLOW](docs/VENDOR-MODE-WORKFLOW.md)** — Step-by-step guide for npm → vendor conversion, updates, and rollback
|
|
43
|
+
- [Command Reference](docs/commands.md) — Complete list of all commands with options
|
|
44
|
+
- [Configuration Guide](docs/lt.config.md) — Configuration file documentation
|
|
45
|
+
- [Plugin Guide](docs/plugins.md) — How to create plugins
|
|
44
46
|
|
|
45
47
|
## Quick Start
|
|
46
48
|
|
|
@@ -55,6 +57,89 @@ $ lt status
|
|
|
55
57
|
$ lt completion install
|
|
56
58
|
```
|
|
57
59
|
|
|
60
|
+
## Framework consumption modes (nest-server)
|
|
61
|
+
|
|
62
|
+
When you create a new api project (`lt fullstack init` or `lt server create`),
|
|
63
|
+
the CLI supports two framework consumption modes:
|
|
64
|
+
|
|
65
|
+
**`npm` mode (default)** — `@lenne.tech/nest-server` is installed as an npm
|
|
66
|
+
dependency. Framework source lives in `node_modules/@lenne.tech/nest-server/`.
|
|
67
|
+
Imports use the bare specifier `from '@lenne.tech/nest-server'`. Update path:
|
|
68
|
+
`/lt-dev:backend:update-nest-server` (Claude Code agent).
|
|
69
|
+
|
|
70
|
+
**`vendor` mode** — The framework's `core/` directory is copied directly into
|
|
71
|
+
`<api>/src/core/` as first-class project code. No `@lenne.tech/nest-server`
|
|
72
|
+
npm dependency. Generated imports use relative paths (`from '../../../core'`).
|
|
73
|
+
Local patches are allowed and tracked in `src/core/VENDOR.md`. Update path:
|
|
74
|
+
`/lt-dev:backend:update-nest-server-core` (Claude Code agent).
|
|
75
|
+
|
|
76
|
+
### Creating projects
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# npm mode (classic, default)
|
|
80
|
+
$ lt fullstack init --name myapp --frontend nuxt --api-mode Rest
|
|
81
|
+
|
|
82
|
+
# vendor mode, HEAD of upstream
|
|
83
|
+
$ lt fullstack init --name myapp --frontend nuxt --api-mode Rest \
|
|
84
|
+
--framework-mode vendor
|
|
85
|
+
|
|
86
|
+
# vendor mode, pinned to a specific upstream branch or tag
|
|
87
|
+
$ lt fullstack init --name myapp --framework-mode vendor \
|
|
88
|
+
--framework-upstream-branch 11.24.1
|
|
89
|
+
|
|
90
|
+
# dry-run: print the plan without touching the filesystem
|
|
91
|
+
$ lt fullstack init --name myapp --framework-mode vendor --dry-run --noConfirm
|
|
92
|
+
|
|
93
|
+
# standalone api project (vendor mode works here too)
|
|
94
|
+
$ lt server create --name myapp --framework-mode vendor
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Working on an existing project
|
|
98
|
+
|
|
99
|
+
All `lt server …` commands (module, object, addProp, test, permissions)
|
|
100
|
+
**auto-detect** the framework mode via `src/core/VENDOR.md` and generate
|
|
101
|
+
the correct import syntax automatically. You never pass `--framework-mode`
|
|
102
|
+
after `init`; it is persisted in the project's `lt.config.json`.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# inside projects/api — generates relative or bare imports automatically
|
|
106
|
+
$ lt server module --name Product --controller Rest
|
|
107
|
+
|
|
108
|
+
# shows the mode + project type
|
|
109
|
+
$ lt status
|
|
110
|
+
|
|
111
|
+
# prints the mode-specific update instructions
|
|
112
|
+
$ lt fullstack update
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Vendor-mode housekeeping
|
|
116
|
+
|
|
117
|
+
Vendor-mode projects ship three maintenance scripts under `scripts/vendor/`:
|
|
118
|
+
|
|
119
|
+
| Script | Purpose | Invocation |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `check-vendor-freshness.mjs` | Non-blocking warning when upstream has a newer release than the current baseline | `pnpm run check:vendor-freshness` (auto-invoked by `pnpm run check` / `check:fix` / `check:naf`) |
|
|
122
|
+
| `sync-from-upstream.ts` | Diff generator consumed by the `nest-server-core-updater` Claude Code agent | `pnpm run vendor:sync` |
|
|
123
|
+
| `propose-upstream-pr.ts` | Patch-list generator consumed by the `nest-server-core-contributor` agent | `pnpm run vendor:propose-upstream` |
|
|
124
|
+
|
|
125
|
+
The vendor-mode baseline (upstream version + commit SHA) is recorded in
|
|
126
|
+
`src/core/VENDOR.md`. Log any substantial local patch there so the updater
|
|
127
|
+
agent can classify it at sync time.
|
|
128
|
+
|
|
129
|
+
### Integration test
|
|
130
|
+
|
|
131
|
+
A full end-to-end smoke test for all four supported init combinations
|
|
132
|
+
(`npm/Rest`, `vendor/Rest`, `vendor/GraphQL`, `vendor/Both`) ships with the
|
|
133
|
+
CLI:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
$ pnpm run test:vendor-init
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Each scenario runs init → module → object → addProp → test → tsc → build →
|
|
140
|
+
migrate:list and asserts ~30 structural + functional invariants per scenario.
|
|
141
|
+
Run this before releasing a new CLI version to catch upstream drift early.
|
|
142
|
+
|
|
58
143
|
## Configuration
|
|
59
144
|
|
|
60
145
|
The CLI supports project-specific configuration via `lt.config` files. This allows you to set default values for commands, reducing repetitive input.
|
|
@@ -56,9 +56,11 @@ const KNOWN_KEYS = {
|
|
|
56
56
|
apiCopy: 'string',
|
|
57
57
|
apiLink: 'string',
|
|
58
58
|
apiMode: ['Rest', 'GraphQL', 'Both'],
|
|
59
|
+
frameworkMode: ['npm', 'vendor'],
|
|
59
60
|
frontend: ['angular', 'nuxt'],
|
|
60
61
|
frontendBranch: 'string',
|
|
61
62
|
frontendCopy: 'string',
|
|
63
|
+
frontendFrameworkMode: ['npm', 'vendor'],
|
|
62
64
|
frontendLink: 'string',
|
|
63
65
|
git: 'boolean',
|
|
64
66
|
gitLink: 'string',
|
|
@@ -0,0 +1,198 @@
|
|
|
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 frontend_framework_detection_1 = require("../../lib/frontend-framework-detection");
|
|
13
|
+
/**
|
|
14
|
+
* Convert an existing frontend project between npm mode and vendor mode.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* lt frontend convert-mode --to vendor [--upstream-branch 1.5.3]
|
|
18
|
+
* lt frontend convert-mode --to npm [--version 1.5.3]
|
|
19
|
+
*/
|
|
20
|
+
const ConvertModeCommand = {
|
|
21
|
+
description: 'Convert app framework mode',
|
|
22
|
+
hidden: false,
|
|
23
|
+
name: 'convert-mode',
|
|
24
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const { filesystem, frontendHelper, parameters, print: { error, info, spin, success, warning }, prompt: { confirm }, } = toolbox;
|
|
26
|
+
// Handle --help-json flag
|
|
27
|
+
if (toolbox.tools.helpJson({
|
|
28
|
+
description: 'Convert frontend 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: 'nuxt-extensions 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 frontend project root
|
|
74
|
+
const cwd = filesystem.cwd();
|
|
75
|
+
const appDir = (0, frontend_framework_detection_1.findAppDir)(cwd);
|
|
76
|
+
if (!appDir) {
|
|
77
|
+
error('Could not find a nuxt.config.ts in the current directory or any parent. Are you inside a Nuxt project?');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Detect current mode
|
|
81
|
+
const currentMode = (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(appDir);
|
|
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(` appDir: ${appDir}`);
|
|
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/nuxt-extensions → /tmp/lt-vendor-nuxt-ext-*');
|
|
101
|
+
info(' 2. Copy src/module.ts + src/runtime/ → app/core/');
|
|
102
|
+
info(' 3. Rewrite nuxt.config.ts: @lenne.tech/nuxt-extensions → ./app/core/module');
|
|
103
|
+
info(' 4. Codemod consumer imports (app/**/*.{ts,vue}, tests/**/*.ts)');
|
|
104
|
+
info(' 5. Remove @lenne.tech/nuxt-extensions from package.json');
|
|
105
|
+
info(' 6. Add check:vendor-freshness script');
|
|
106
|
+
info(' 7. Create app/core/VENDOR.md with baseline metadata');
|
|
107
|
+
info(' 8. Prepend vendor-mode notice to CLAUDE.md');
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const targetVersion = parameters.options.version;
|
|
111
|
+
info(` targetVersion: ${targetVersion || '(from VENDOR.md baseline)'}`);
|
|
112
|
+
info('');
|
|
113
|
+
info('Would execute:');
|
|
114
|
+
info(' 1. Read baseline version from app/core/VENDOR.md');
|
|
115
|
+
info(' 2. Warn if local patches exist in VENDOR.md');
|
|
116
|
+
info(' 3. Rewrite consumer imports: relative → @lenne.tech/nuxt-extensions');
|
|
117
|
+
info(' 4. Delete app/core/');
|
|
118
|
+
info(' 5. Restore @lenne.tech/nuxt-extensions in package.json');
|
|
119
|
+
info(' 6. Rewrite nuxt.config.ts: ./app/core/module → @lenne.tech/nuxt-extensions');
|
|
120
|
+
info(' 7. Remove vendor scripts and CLAUDE.md marker');
|
|
121
|
+
}
|
|
122
|
+
info('');
|
|
123
|
+
return `frontend convert-mode dry-run (${currentMode} → ${targetMode})`;
|
|
124
|
+
}
|
|
125
|
+
// Confirm
|
|
126
|
+
const noConfirm = parameters.options.noConfirm;
|
|
127
|
+
if (!noConfirm) {
|
|
128
|
+
const proceed = yield confirm(`Convert project from ${currentMode} mode to ${targetMode} mode?\n` +
|
|
129
|
+
` Project: ${appDir}\n` +
|
|
130
|
+
` This will modify nuxt.config.ts, package.json, imports, and CLAUDE.md.`);
|
|
131
|
+
if (!proceed) {
|
|
132
|
+
info('Aborted.');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Execute conversion
|
|
137
|
+
if (targetMode === 'vendor') {
|
|
138
|
+
// npm → vendor
|
|
139
|
+
const upstreamBranch = parameters.options['upstream-branch'];
|
|
140
|
+
// Auto-detect version from current @lenne.tech/nuxt-extensions dep
|
|
141
|
+
let branch = upstreamBranch;
|
|
142
|
+
if (!branch) {
|
|
143
|
+
try {
|
|
144
|
+
const pkg = filesystem.read(`${appDir}/package.json`, 'json');
|
|
145
|
+
const deps = Object.assign(Object.assign({}, ((pkg === null || pkg === void 0 ? void 0 : pkg.dependencies) || {})), ((pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies) || {}));
|
|
146
|
+
const version = deps['@lenne.tech/nuxt-extensions'];
|
|
147
|
+
if (version) {
|
|
148
|
+
// Strip semver range chars (^, ~, >=, etc.) to get the bare version
|
|
149
|
+
branch = version.replace(/^[^0-9]*/, '');
|
|
150
|
+
info(`Auto-detected @lenne.tech/nuxt-extensions version: ${branch}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (_a) {
|
|
154
|
+
// Will use HEAD
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const spinner = spin('Converting to vendor mode...');
|
|
158
|
+
try {
|
|
159
|
+
yield frontendHelper.convertAppToVendorMode({
|
|
160
|
+
dest: appDir,
|
|
161
|
+
upstreamBranch: branch,
|
|
162
|
+
});
|
|
163
|
+
spinner.succeed('Converted to vendor mode successfully.');
|
|
164
|
+
success('\nNext steps:');
|
|
165
|
+
info(' 1. Run: pnpm install');
|
|
166
|
+
info(' 2. Run: pnpm run build (or nuxt build)');
|
|
167
|
+
info(' 3. Commit the changes');
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
spinner.fail(`Conversion failed: ${err.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// vendor → npm
|
|
175
|
+
const targetVersion = parameters.options.version;
|
|
176
|
+
const spinner = spin('Converting to npm mode...');
|
|
177
|
+
try {
|
|
178
|
+
yield frontendHelper.convertAppToNpmMode({
|
|
179
|
+
dest: appDir,
|
|
180
|
+
targetVersion,
|
|
181
|
+
});
|
|
182
|
+
spinner.succeed('Converted to npm mode successfully.');
|
|
183
|
+
success('\nNext steps:');
|
|
184
|
+
info(' 1. Run: pnpm install');
|
|
185
|
+
info(' 2. Run: pnpm run build (or nuxt build)');
|
|
186
|
+
info(' 3. Commit the changes');
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
spinner.fail(`Conversion failed: ${err.message}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!parameters.options.fromGluegunMenu) {
|
|
193
|
+
process.exit();
|
|
194
|
+
}
|
|
195
|
+
return `converted frontend to ${targetMode} mode`;
|
|
196
|
+
}),
|
|
197
|
+
};
|
|
198
|
+
exports.default = ConvertModeCommand;
|