@lenne.tech/cli 1.27.0 → 1.29.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/build/cli.js +7 -1
- package/build/commands/dev/doctor.js +27 -1
- package/build/commands/dev/down.js +22 -10
- package/build/commands/dev/status.js +4 -3
- package/build/commands/dev/test.js +12 -4
- package/build/commands/dev/up.js +90 -50
- package/build/commands/frontend/angular.js +48 -46
- package/build/commands/frontend/convert-mode.js +41 -39
- package/build/commands/frontend/nuxt.js +49 -47
- package/build/commands/fullstack/add-api.js +34 -32
- package/build/commands/fullstack/add-app.js +25 -23
- package/build/commands/fullstack/convert-mode.js +85 -65
- package/build/commands/fullstack/init.js +12 -0
- package/build/commands/fullstack/update.js +24 -0
- package/build/commands/server/add-property.js +42 -40
- package/build/commands/server/convert-mode.js +41 -39
- package/build/commands/server/create.js +65 -63
- package/build/commands/server/module.js +56 -54
- package/build/commands/server/object.js +42 -40
- package/build/commands/server/permissions.js +60 -58
- package/build/commands/ticket/list.js +78 -0
- package/build/commands/ticket/start.js +141 -0
- package/build/commands/ticket/stop.js +166 -0
- package/build/commands/ticket/switch.js +70 -0
- package/build/commands/ticket/test.js +80 -0
- package/build/commands/ticket/ticket.js +36 -0
- package/build/commands/tools/crawl.js +92 -90
- package/build/extensions/frontend-helper.js +8 -37
- package/build/extensions/server.js +8 -38
- package/build/lib/command-help.js +161 -0
- package/build/lib/dev-identity.js +18 -0
- package/build/lib/dev-patches.js +1 -1
- package/build/lib/dev-project.js +14 -0
- package/build/lib/dev-state.js +96 -0
- package/build/lib/dev-test-session.js +55 -35
- package/build/lib/dev-ticket.js +343 -0
- package/build/lib/vendor-claude-md.js +227 -0
- package/docs/lt-dev-ticket-workflow.html +603 -0
- package/docs/lt-dev-ticket-workflow.pdf +0 -0
- package/package.json +32 -1
package/build/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const gluegun_1 = require("gluegun");
|
|
13
13
|
const path_1 = require("path");
|
|
14
|
+
const command_help_1 = require("./lib/command-help");
|
|
14
15
|
/**
|
|
15
16
|
* Create the cli and kick it off
|
|
16
17
|
*/
|
|
@@ -26,9 +27,14 @@ function run(argv) {
|
|
|
26
27
|
commandFilePattern: ['*.js'],
|
|
27
28
|
extensionFilePattern: ['*.js'],
|
|
28
29
|
})
|
|
29
|
-
.help() // provides default for help, h, --help, -h
|
|
30
|
+
.help() // provides default for top-level help, h, --help, -h
|
|
30
31
|
.version() // provides default for version, v, --version, -v
|
|
31
32
|
.create();
|
|
33
|
+
// Generic per-command `--help` / `-h`: gluegun's `.help()` only covers the
|
|
34
|
+
// top level, so without this a subcommand like `lt fullstack convert-mode
|
|
35
|
+
// --help` would actually RUN. The interceptor makes every command print
|
|
36
|
+
// help and return without executing when help is requested.
|
|
37
|
+
(0, command_help_1.installHelpInterceptor)(cli.commands, cli.defaultCommand);
|
|
32
38
|
// Run cli
|
|
33
39
|
const toolbox = yield cli.run(argv);
|
|
34
40
|
// Record command in history (if history extension is available)
|
|
@@ -12,8 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const child_process_1 = require("child_process");
|
|
13
13
|
const caddy_1 = require("../../lib/caddy");
|
|
14
14
|
const dev_process_1 = require("../../lib/dev-process");
|
|
15
|
+
const dev_project_1 = require("../../lib/dev-project");
|
|
15
16
|
const dev_service_1 = require("../../lib/dev-service");
|
|
16
17
|
const dev_state_1 = require("../../lib/dev-state");
|
|
18
|
+
const dev_ticket_1 = require("../../lib/dev-ticket");
|
|
17
19
|
/**
|
|
18
20
|
* Diagnose Caddy / CA / DNS / port issues for `lt dev`.
|
|
19
21
|
*
|
|
@@ -30,7 +32,7 @@ const DoctorCommand = {
|
|
|
30
32
|
hidden: false,
|
|
31
33
|
name: 'doctor',
|
|
32
34
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
-
const { parameters, print: { colors, info }, } = toolbox;
|
|
35
|
+
const { filesystem, parameters, print: { colors, info }, } = toolbox;
|
|
34
36
|
info('');
|
|
35
37
|
info(colors.bold('lt dev doctor'));
|
|
36
38
|
info(colors.dim('─'.repeat(60)));
|
|
@@ -105,6 +107,30 @@ const DoctorCommand = {
|
|
|
105
107
|
const reg = (0, dev_state_1.loadRegistry)();
|
|
106
108
|
const count = Object.keys(reg.projects).length;
|
|
107
109
|
line('OK', colors.green, `registry: ${count} project(s) at ${dev_state_1.paths.registry}`);
|
|
110
|
+
// 7. Project-level (only when run inside a project): is a DB-wiping
|
|
111
|
+
// Playwright global-setup ticket/shard-safe? WARN (never auto-edit) if a
|
|
112
|
+
// bespoke allow-list would reject the per-ticket/shard `<base>-<id>-test`
|
|
113
|
+
// DBs that `lt ticket` / `lt dev test --shard` create.
|
|
114
|
+
const layout = (0, dev_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
115
|
+
if (layout.apiDir || layout.appDir) {
|
|
116
|
+
const gs = (0, dev_ticket_1.checkGlobalSetupTicketSafe)(layout);
|
|
117
|
+
if (gs.file && gs.hasDbReset && !gs.ticketSafe) {
|
|
118
|
+
line('WARN', colors.yellow, 'global-setup allow-list rejects per-ticket/shard test DBs — `lt ticket` / `--shard` E2E cannot reset its DB');
|
|
119
|
+
line('WARN', colors.yellow, ` ${gs.file}: widen isAllowedDb → /^<base>-(?:[a-z0-9-]+-)?test(?:-\\d+)?$/ (svl is the reference)`);
|
|
120
|
+
}
|
|
121
|
+
else if (gs.file && gs.hasDbReset) {
|
|
122
|
+
line('OK', colors.green, 'global-setup allow-list is ticket + shard safe');
|
|
123
|
+
}
|
|
124
|
+
// 8. Slug ↔ path: is this project's slug registered to a DIFFERENT checkout?
|
|
125
|
+
// Two clones of the same project (same package.json "name") share the
|
|
126
|
+
// slug → Caddy block / ports / DB and collide. Surface it proactively.
|
|
127
|
+
const { identity } = (0, dev_ticket_1.resolveDevIdentity)(layout);
|
|
128
|
+
const conflict = (0, dev_state_1.detectSlugConflict)(identity.slug, layout.root);
|
|
129
|
+
if (conflict) {
|
|
130
|
+
line('WARN', colors.yellow, `slug "${identity.slug}" is also registered to another checkout${conflict.otherSessionAlive ? ' (currently RUNNING)' : ''}: ${conflict.otherPath}`);
|
|
131
|
+
line('WARN', colors.yellow, ' two clones of the same project collide on URLs/ports/DB — rename one package.json "name", or run only one.');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
108
134
|
info('');
|
|
109
135
|
if (fails > 0)
|
|
110
136
|
info(colors.red(`✗ ${fails} fail(s) — see above`));
|
|
@@ -11,11 +11,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const caddy_1 = require("../../lib/caddy");
|
|
13
13
|
const dev_env_bridge_1 = require("../../lib/dev-env-bridge");
|
|
14
|
-
const dev_identity_1 = require("../../lib/dev-identity");
|
|
15
14
|
const dev_process_1 = require("../../lib/dev-process");
|
|
16
15
|
const dev_project_1 = require("../../lib/dev-project");
|
|
17
16
|
const dev_state_1 = require("../../lib/dev-state");
|
|
18
17
|
const dev_test_session_1 = require("../../lib/dev-test-session");
|
|
18
|
+
const dev_ticket_1 = require("../../lib/dev-ticket");
|
|
19
19
|
/**
|
|
20
20
|
* Stop the processes started by `lt dev up` and remove the project's
|
|
21
21
|
* Caddy block.
|
|
@@ -33,7 +33,9 @@ const DownCommand = {
|
|
|
33
33
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
34
|
const { filesystem, parameters, print: { colors, info, success, warning }, } = toolbox;
|
|
35
35
|
const layout = (0, dev_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
36
|
-
|
|
36
|
+
// Ticket-aware: in a ticket worktree the slug / Caddy block / test stack are
|
|
37
|
+
// suffixed (`<slug>-<id>`), so resolve the same identity `up` used.
|
|
38
|
+
const { identity } = (0, dev_ticket_1.resolveDevIdentity)(layout, { ticket: parameters.options.ticket });
|
|
37
39
|
const session = (0, dev_state_1.loadSession)(layout.root);
|
|
38
40
|
const stopped = [];
|
|
39
41
|
if (session) {
|
|
@@ -54,14 +56,24 @@ const DownCommand = {
|
|
|
54
56
|
else {
|
|
55
57
|
info(colors.dim('No running processes registered for this project.'));
|
|
56
58
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
// Don't clobber another checkout: when this slug is registered to a DIFFERENT
|
|
60
|
+
// checkout (two clones of the same project share a package.json "name" → slug),
|
|
61
|
+
// the Caddy block + registration belong to IT — stop only OUR processes
|
|
62
|
+
// (above) and leave its routing intact. Otherwise remove the block as usual.
|
|
63
|
+
const conflict = (0, dev_state_1.detectSlugConflict)(identity.slug, layout.root);
|
|
64
|
+
if (conflict) {
|
|
65
|
+
warning(`Slug "${identity.slug}" is registered to another checkout — leaving its Caddy block + registration untouched:`);
|
|
66
|
+
info(colors.dim(` ${conflict.otherPath}`));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const removed = (0, caddy_1.removeProjectBlock)(identity.slug);
|
|
70
|
+
if (removed) {
|
|
71
|
+
const r = yield (0, caddy_1.reloadCaddy)();
|
|
72
|
+
if (r.ok)
|
|
73
|
+
success(`Removed Caddy block for "${identity.slug}".`);
|
|
74
|
+
else
|
|
75
|
+
warning(`Removed Caddy block but reload failed: ${r.stderr.split('\n')[0]}`);
|
|
76
|
+
}
|
|
65
77
|
}
|
|
66
78
|
// Clear ENV bridge so subsequent test runs without `lt dev up`
|
|
67
79
|
// do not pick up stale URLs.
|
|
@@ -10,10 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const caddy_1 = require("../../lib/caddy");
|
|
13
|
-
const dev_identity_1 = require("../../lib/dev-identity");
|
|
14
13
|
const dev_process_1 = require("../../lib/dev-process");
|
|
15
14
|
const dev_project_1 = require("../../lib/dev-project");
|
|
16
15
|
const dev_state_1 = require("../../lib/dev-state");
|
|
16
|
+
const dev_ticket_1 = require("../../lib/dev-ticket");
|
|
17
17
|
/**
|
|
18
18
|
* Show what is running.
|
|
19
19
|
*
|
|
@@ -56,10 +56,11 @@ const StatusCommand = {
|
|
|
56
56
|
return 'dev status: all';
|
|
57
57
|
}
|
|
58
58
|
const layout = (0, dev_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
59
|
-
|
|
59
|
+
// Ticket-aware: a ticket worktree reports its OWN suffixed stack.
|
|
60
|
+
const { identity, ticket } = (0, dev_ticket_1.resolveDevIdentity)(layout, { ticket: parameters.options.ticket });
|
|
60
61
|
const entry = reg.projects[identity.slug];
|
|
61
62
|
info('');
|
|
62
|
-
info(colors.bold(`lt dev status: ${identity.slug}`));
|
|
63
|
+
info(colors.bold(`lt dev status: ${identity.slug}`) + (ticket ? colors.dim(` (ticket ${ticket})`) : ''));
|
|
63
64
|
info(colors.dim('─'.repeat(60)));
|
|
64
65
|
if (!entry) {
|
|
65
66
|
warning('Not registered. Run `lt dev init` first.');
|
|
@@ -12,10 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const fs_1 = require("fs");
|
|
13
13
|
const caddy_1 = require("../../lib/caddy");
|
|
14
14
|
const dev_env_bridge_1 = require("../../lib/dev-env-bridge");
|
|
15
|
-
const dev_identity_1 = require("../../lib/dev-identity");
|
|
16
15
|
const dev_process_1 = require("../../lib/dev-process");
|
|
17
16
|
const dev_project_1 = require("../../lib/dev-project");
|
|
18
17
|
const dev_test_session_1 = require("../../lib/dev-test-session");
|
|
18
|
+
const dev_ticket_1 = require("../../lib/dev-ticket");
|
|
19
19
|
/**
|
|
20
20
|
* One-shot E2E convenience wrapper.
|
|
21
21
|
*
|
|
@@ -54,7 +54,10 @@ const TestCommand = {
|
|
|
54
54
|
process.exit(1);
|
|
55
55
|
return 'dev test: not a project';
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
// Ticket-aware: a ticket worktree tests its OWN isolated stack + DB
|
|
58
|
+
// (`<slug>-<id>-test`), so resolve the ticket identity + pass the ticket dev
|
|
59
|
+
// DB so the test DB is derived per ticket (never shared between tickets).
|
|
60
|
+
const { dbName: devDbName, identity } = (0, dev_ticket_1.resolveDevIdentity)(layout, { ticket: parameters.options.ticket });
|
|
58
61
|
const log = { dim: colors.dim, info, warn: warning };
|
|
59
62
|
// Sub-command: `lt dev test down` — tear the test stack(s) down + exit.
|
|
60
63
|
// Reclaims both the unsharded stack and any leftover sharded stacks.
|
|
@@ -171,7 +174,12 @@ const TestCommand = {
|
|
|
171
174
|
try {
|
|
172
175
|
info('');
|
|
173
176
|
info(colors.bold(`Running isolated Playwright E2E for "${identity.slug}" sharded across ${shardTotal} stacks`));
|
|
174
|
-
shardExit = yield (0, dev_test_session_1.runShardedTestSession)(layout, identity, log, {
|
|
177
|
+
shardExit = yield (0, dev_test_session_1.runShardedTestSession)(layout, identity, log, {
|
|
178
|
+
devDbName,
|
|
179
|
+
forwarded,
|
|
180
|
+
pnpmBin,
|
|
181
|
+
total: shardTotal,
|
|
182
|
+
});
|
|
175
183
|
}
|
|
176
184
|
catch (e) {
|
|
177
185
|
error(`Failed to run sharded E2E: ${e.message}`);
|
|
@@ -213,7 +221,7 @@ const TestCommand = {
|
|
|
213
221
|
process.on('SIGTERM', onSignal);
|
|
214
222
|
let exitCode = 1;
|
|
215
223
|
try {
|
|
216
|
-
const ctx = yield (0, dev_test_session_1.bringUpTestSession)(layout, identity, log);
|
|
224
|
+
const ctx = yield (0, dev_test_session_1.bringUpTestSession)(layout, identity, log, { devDbName });
|
|
217
225
|
const env = Object.assign(Object.assign(Object.assign({}, process.env), readBridgeEnv(layout.root)), {
|
|
218
226
|
// Playwright global-setup resets THIS db (allow-listed) before the suite.
|
|
219
227
|
MONGO_URI: `mongodb://127.0.0.1/${ctx.dbName}` });
|
package/build/commands/dev/up.js
CHANGED
|
@@ -14,11 +14,11 @@ const path_1 = require("path");
|
|
|
14
14
|
const caddy_1 = require("../../lib/caddy");
|
|
15
15
|
const dev_env_1 = require("../../lib/dev-env");
|
|
16
16
|
const dev_env_bridge_1 = require("../../lib/dev-env-bridge");
|
|
17
|
-
const dev_identity_1 = require("../../lib/dev-identity");
|
|
18
17
|
const dev_patches_1 = require("../../lib/dev-patches");
|
|
19
18
|
const dev_process_1 = require("../../lib/dev-process");
|
|
20
19
|
const dev_project_1 = require("../../lib/dev-project");
|
|
21
20
|
const dev_state_1 = require("../../lib/dev-state");
|
|
21
|
+
const dev_ticket_1 = require("../../lib/dev-ticket");
|
|
22
22
|
/**
|
|
23
23
|
* Start API + App behind Caddy with project-specific URLs.
|
|
24
24
|
*
|
|
@@ -81,7 +81,7 @@ const UpCommand = {
|
|
|
81
81
|
hidden: false,
|
|
82
82
|
name: 'up',
|
|
83
83
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l
|
|
84
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
85
85
|
const { filesystem, parameters, print: { colors, error, info, success, warning }, } = toolbox;
|
|
86
86
|
const layout = (0, dev_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
87
87
|
if (!layout.apiDir && !layout.appDir) {
|
|
@@ -103,21 +103,48 @@ const UpCommand = {
|
|
|
103
103
|
process.exit(1);
|
|
104
104
|
return 'dev up: caddy daemon down';
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
// Ticket-aware: in a `lt ticket` worktree (tagged by a `.lt-dev/ticket`
|
|
107
|
+
// marker) — or with an explicit `--ticket <name>` — the slug / URLs / DB are
|
|
108
|
+
// suffixed so the stack is fully isolated from the base dev session and every
|
|
109
|
+
// other ticket. Without a ticket this is the plain project identity.
|
|
110
|
+
const { dbName, identity, ticket } = (0, dev_ticket_1.resolveDevIdentity)(layout, { ticket: parameters.options.ticket });
|
|
111
|
+
// Guard against two checkouts of the SAME project (same package.json "name"
|
|
112
|
+
// → same slug → shared URLs / ports / DB / Caddy block). If another checkout
|
|
113
|
+
// is already RUNNING under this slug, abort with a clear message — otherwise
|
|
114
|
+
// both fight over the same ports and one `lt dev down` unroutes the other.
|
|
115
|
+
{
|
|
116
|
+
const conflict = (0, dev_state_1.detectSlugConflict)(identity.slug, layout.root);
|
|
117
|
+
if (conflict === null || conflict === void 0 ? void 0 : conflict.otherSessionAlive) {
|
|
118
|
+
error(`Slug "${identity.slug}" is already in use by another RUNNING checkout:`);
|
|
119
|
+
info(colors.dim(` ${conflict.otherPath}`));
|
|
120
|
+
info('Two checkouts of the same project share URLs / ports / database and collide.');
|
|
121
|
+
info('Stop it there (`lt dev down`), or give THIS checkout a distinct package.json "name".');
|
|
122
|
+
if (!parameters.options.fromGluegunMenu)
|
|
123
|
+
process.exit(1);
|
|
124
|
+
return 'dev up: slug in use by another checkout';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
108
127
|
// Sanft auto-migrate sichere Operationen (ohne Code-Modifikation):
|
|
109
128
|
// CLAUDE.md-URL-Block einfügen + .gitignore ergänzen.
|
|
110
129
|
// Code-Patches (config.env.ts, nuxt.config.ts) bleiben explizit `lt dev init`.
|
|
111
130
|
{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
// NEVER patch the git-tracked CLAUDE.md for a ticket worktree: it would
|
|
132
|
+
// differ per worktree and risk committing ticket-specific URLs. The lt-dev
|
|
133
|
+
// plugin hook surfaces the ticket context per prompt instead (from the
|
|
134
|
+
// gitignored `.lt-dev/ticket` marker). For the base project we keep the
|
|
135
|
+
// committed URL block up to date as before.
|
|
136
|
+
if (!ticket) {
|
|
137
|
+
const claudeCandidates = [
|
|
138
|
+
(0, path_1.join)(layout.root, 'CLAUDE.md'),
|
|
139
|
+
...(layout.apiDir ? [(0, path_1.join)(layout.apiDir, 'CLAUDE.md')] : []),
|
|
140
|
+
...(layout.appDir ? [(0, path_1.join)(layout.appDir, 'CLAUDE.md')] : []),
|
|
141
|
+
];
|
|
142
|
+
const patched = claudeCandidates.map((f) => (0, dev_patches_1.patchClaudeMd)(f, { dbName, identity })).filter((r) => r.patched);
|
|
143
|
+
if (patched.length > 0) {
|
|
144
|
+
info(colors.dim(`updated CLAUDE.md URL block in ${patched.length} file(s)`));
|
|
145
|
+
}
|
|
120
146
|
}
|
|
147
|
+
// Always keep `.lt-dev/` (state, env bridge, ticket marker) out of git.
|
|
121
148
|
if ((0, dev_patches_1.addToGitignore)(layout.root, '.lt-dev/')) {
|
|
122
149
|
info(colors.dim('added `.lt-dev/` to .gitignore'));
|
|
123
150
|
}
|
|
@@ -154,7 +181,7 @@ const UpCommand = {
|
|
|
154
181
|
apiUpstreamPort: existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.internalPorts.api,
|
|
155
182
|
appHostname: (_d = identity.subdomains.app) === null || _d === void 0 ? void 0 : _d.hostname,
|
|
156
183
|
appUpstreamPort: existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.internalPorts.app,
|
|
157
|
-
dbName: (_e = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.dbName) !== null && _e !== void 0 ? _e :
|
|
184
|
+
dbName: (_e = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.dbName) !== null && _e !== void 0 ? _e : dbName,
|
|
158
185
|
});
|
|
159
186
|
info('Run `lt dev down` first.');
|
|
160
187
|
if (!parameters.options.fromGluegunMenu)
|
|
@@ -162,25 +189,48 @@ const UpCommand = {
|
|
|
162
189
|
return 'dev up: already running';
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
|
-
// Allocate internal ports (reuse existing if registered)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
// Allocate internal ports (reuse existing if registered), verify they are
|
|
193
|
+
// free, AND reserve them in the registry — all ATOMICALLY under a cross-
|
|
194
|
+
// process lock, so two simultaneous `lt ticket start` (each → `lt dev up`)
|
|
195
|
+
// can never grab the same ports.
|
|
196
|
+
let apiPort;
|
|
197
|
+
let appPort;
|
|
198
|
+
try {
|
|
199
|
+
yield (0, dev_state_1.withRegistryLock)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
200
|
+
var _a, _b;
|
|
201
|
+
const reg = (0, dev_state_1.loadRegistry)();
|
|
202
|
+
const entry = reg.projects[identity.slug];
|
|
203
|
+
const taken = (0, dev_state_1.takenInternalPorts)(reg, identity.slug);
|
|
204
|
+
apiPort = (_a = entry === null || entry === void 0 ? void 0 : entry.internalPorts.api) !== null && _a !== void 0 ? _a : (layout.apiDir ? (0, dev_state_1.allocateInternalPort)(4000, taken) : undefined);
|
|
205
|
+
if (apiPort)
|
|
206
|
+
taken.add(apiPort);
|
|
207
|
+
appPort = (_b = entry === null || entry === void 0 ? void 0 : entry.internalPorts.app) !== null && _b !== void 0 ? _b : (layout.appDir ? (0, dev_state_1.allocateInternalPort)(4000, taken) : undefined);
|
|
208
|
+
const portsToCheck = [apiPort, appPort].filter((p) => typeof p === 'number');
|
|
209
|
+
const snap = yield (0, dev_process_1.listenSnapshot)(portsToCheck);
|
|
210
|
+
for (const p of portsToCheck) {
|
|
211
|
+
const r = snap.get(p);
|
|
212
|
+
if (r)
|
|
213
|
+
throw new Error(`Internal port ${p} already in use by ${r.command} (pid ${r.pid}).`);
|
|
214
|
+
}
|
|
215
|
+
// Reserve immediately so a concurrent `lt dev up` sees these as taken.
|
|
216
|
+
const subdomainMap = {};
|
|
217
|
+
for (const [k, v] of Object.entries(identity.subdomains))
|
|
218
|
+
subdomainMap[k] = v.hostname;
|
|
219
|
+
reg.projects[identity.slug] = {
|
|
220
|
+
dbName,
|
|
221
|
+
internalPorts: { api: apiPort, app: appPort },
|
|
222
|
+
lastUsedAt: new Date().toISOString(),
|
|
223
|
+
path: layout.root,
|
|
224
|
+
subdomains: subdomainMap,
|
|
225
|
+
};
|
|
226
|
+
(0, dev_state_1.saveRegistry)(reg);
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
error(e.message);
|
|
231
|
+
if (!parameters.options.fromGluegunMenu)
|
|
232
|
+
process.exit(1);
|
|
233
|
+
return 'dev up: port in use';
|
|
184
234
|
}
|
|
185
235
|
// Caddy block + reload.
|
|
186
236
|
const routes = [];
|
|
@@ -203,7 +253,7 @@ const UpCommand = {
|
|
|
203
253
|
return 'dev up: caddy reload failed';
|
|
204
254
|
}
|
|
205
255
|
info('');
|
|
206
|
-
info(colors.bold(`Starting "${identity.slug}"`));
|
|
256
|
+
info(colors.bold(`Starting "${identity.slug}"`) + (ticket ? colors.dim(` (ticket ${ticket})`) : ''));
|
|
207
257
|
if (identity.subdomains.app)
|
|
208
258
|
info(` app: https://${identity.subdomains.app.hostname} → 127.0.0.1:${appPort}`);
|
|
209
259
|
if (identity.subdomains.api)
|
|
@@ -230,7 +280,7 @@ const UpCommand = {
|
|
|
230
280
|
if (apiResult) {
|
|
231
281
|
pids.api = apiResult.pid;
|
|
232
282
|
if (apiResult.rotated.rotated && apiResult.rotated.archivePath !== undefined) {
|
|
233
|
-
rotationNotes.push(formatRotationNote('api', apiResult.rotated.archivePath, (
|
|
283
|
+
rotationNotes.push(formatRotationNote('api', apiResult.rotated.archivePath, (_f = apiResult.rotated.previousSize) !== null && _f !== void 0 ? _f : 0));
|
|
234
284
|
}
|
|
235
285
|
}
|
|
236
286
|
}
|
|
@@ -243,35 +293,25 @@ const UpCommand = {
|
|
|
243
293
|
if (appResult) {
|
|
244
294
|
pids.app = appResult.pid;
|
|
245
295
|
if (appResult.rotated.rotated && appResult.rotated.archivePath !== undefined) {
|
|
246
|
-
rotationNotes.push(formatRotationNote('app', appResult.rotated.archivePath, (
|
|
296
|
+
rotationNotes.push(formatRotationNote('app', appResult.rotated.archivePath, (_g = appResult.rotated.previousSize) !== null && _g !== void 0 ? _g : 0));
|
|
247
297
|
}
|
|
248
298
|
}
|
|
249
299
|
}
|
|
250
|
-
// Persist.
|
|
251
|
-
|
|
252
|
-
for (const [k, v] of Object.entries(identity.subdomains))
|
|
253
|
-
subdomainMap[k] = v.hostname;
|
|
254
|
-
reg.projects[identity.slug] = {
|
|
255
|
-
dbName,
|
|
256
|
-
internalPorts: { api: apiPort, app: appPort },
|
|
257
|
-
lastUsedAt: new Date().toISOString(),
|
|
258
|
-
path: layout.root,
|
|
259
|
-
subdomains: subdomainMap,
|
|
260
|
-
};
|
|
261
|
-
(0, dev_state_1.saveRegistry)(reg);
|
|
300
|
+
// Persist the session (PIDs). The registry entry (ports) was already reserved
|
|
301
|
+
// atomically before the spawn, above.
|
|
262
302
|
(0, dev_state_1.saveSession)(layout.root, { pids, startedAt: new Date().toISOString() });
|
|
263
303
|
// Write the ENV bridge so external tools (Playwright, IDE test runners,
|
|
264
304
|
// custom shell scripts) can pick up the URLs without inheriting our shell.
|
|
265
305
|
const bridgePath = (0, dev_env_bridge_1.writeEnvBridge)(layout.root, devEnv, dbName);
|
|
266
306
|
info(colors.dim(`ENV bridge: ${bridgePath}`));
|
|
267
|
-
success(`Started: api pid=${(
|
|
307
|
+
success(`Started: api pid=${(_h = pids.api) !== null && _h !== void 0 ? _h : '-'}, app pid=${(_j = pids.app) !== null && _j !== void 0 ? _j : '-'}`);
|
|
268
308
|
// Echo the bound URLs next to the PIDs as well — the "Starting" block
|
|
269
309
|
// prints them before the spawn, but on a long boot log they scroll out
|
|
270
310
|
// of view, so repeating them here keeps PID + URL visually grouped.
|
|
271
311
|
printProjectUrls(info, {
|
|
272
|
-
apiHostname: (
|
|
312
|
+
apiHostname: (_k = identity.subdomains.api) === null || _k === void 0 ? void 0 : _k.hostname,
|
|
273
313
|
apiUpstreamPort: apiPort,
|
|
274
|
-
appHostname: (
|
|
314
|
+
appHostname: (_l = identity.subdomains.app) === null || _l === void 0 ? void 0 : _l.hostname,
|
|
275
315
|
appUpstreamPort: appPort,
|
|
276
316
|
dbName,
|
|
277
317
|
});
|
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.help = void 0;
|
|
12
13
|
const workspace_integration_1 = require("../../lib/workspace-integration");
|
|
13
14
|
/**
|
|
14
15
|
* Create a new Angular workspace
|
|
@@ -18,6 +19,52 @@ const workspace_integration_1 = require("../../lib/workspace-integration");
|
|
|
18
19
|
* Mirrors the same dry-run / workspace-detection surface as the Nuxt
|
|
19
20
|
* sibling so behaviour is consistent across the four flows.
|
|
20
21
|
*/
|
|
22
|
+
exports.help = {
|
|
23
|
+
aliases: ['a'],
|
|
24
|
+
configuration: 'commands.frontend.angular.*',
|
|
25
|
+
description: 'Create a new Angular workspace from ng-base-starter',
|
|
26
|
+
name: 'angular',
|
|
27
|
+
options: [
|
|
28
|
+
{ description: 'Workspace name', flag: '--name', required: false, type: 'string' },
|
|
29
|
+
{ description: 'Branch of ng-base-starter to clone', flag: '--branch', required: false, type: 'string' },
|
|
30
|
+
{ description: 'Copy from local template directory', flag: '--copy', required: false, type: 'string' },
|
|
31
|
+
{ description: 'Symlink to local template directory', flag: '--link', required: false, type: 'string' },
|
|
32
|
+
{ description: 'Initialize Angular localize', flag: '--localize', required: false, type: 'boolean' },
|
|
33
|
+
{
|
|
34
|
+
description: 'Skip Angular localize initialisation',
|
|
35
|
+
flag: '--noLocalize',
|
|
36
|
+
required: false,
|
|
37
|
+
type: 'boolean',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
description: 'Git remote URL to push initial commit to',
|
|
41
|
+
flag: '--gitLink',
|
|
42
|
+
required: false,
|
|
43
|
+
type: 'string',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
default: false,
|
|
47
|
+
description: 'Print resolved plan and exit without making any changes',
|
|
48
|
+
flag: '--dry-run',
|
|
49
|
+
required: false,
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
default: false,
|
|
54
|
+
description: 'Override the workspace-detection abort under --noConfirm',
|
|
55
|
+
flag: '--force',
|
|
56
|
+
required: false,
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
default: false,
|
|
61
|
+
description: 'Skip all interactive prompts',
|
|
62
|
+
flag: '--noConfirm',
|
|
63
|
+
required: false,
|
|
64
|
+
type: 'boolean',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
21
68
|
const NewCommand = {
|
|
22
69
|
alias: ['a'],
|
|
23
70
|
description: 'Create Angular workspace',
|
|
@@ -27,52 +74,7 @@ const NewCommand = {
|
|
|
27
74
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
28
75
|
// Retrieve the tools we need
|
|
29
76
|
const { config, filesystem, frontendHelper, git, helper, parameters, print: { error, info, spin, success }, prompt: { confirm }, strings: { kebabCase }, system, } = toolbox;
|
|
30
|
-
if (toolbox.tools.helpJson({
|
|
31
|
-
aliases: ['a'],
|
|
32
|
-
configuration: 'commands.frontend.angular.*',
|
|
33
|
-
description: 'Create a new Angular workspace from ng-base-starter',
|
|
34
|
-
name: 'angular',
|
|
35
|
-
options: [
|
|
36
|
-
{ description: 'Workspace name', flag: '--name', required: false, type: 'string' },
|
|
37
|
-
{ description: 'Branch of ng-base-starter to clone', flag: '--branch', required: false, type: 'string' },
|
|
38
|
-
{ description: 'Copy from local template directory', flag: '--copy', required: false, type: 'string' },
|
|
39
|
-
{ description: 'Symlink to local template directory', flag: '--link', required: false, type: 'string' },
|
|
40
|
-
{ description: 'Initialize Angular localize', flag: '--localize', required: false, type: 'boolean' },
|
|
41
|
-
{
|
|
42
|
-
description: 'Skip Angular localize initialisation',
|
|
43
|
-
flag: '--noLocalize',
|
|
44
|
-
required: false,
|
|
45
|
-
type: 'boolean',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
description: 'Git remote URL to push initial commit to',
|
|
49
|
-
flag: '--gitLink',
|
|
50
|
-
required: false,
|
|
51
|
-
type: 'string',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
default: false,
|
|
55
|
-
description: 'Print resolved plan and exit without making any changes',
|
|
56
|
-
flag: '--dry-run',
|
|
57
|
-
required: false,
|
|
58
|
-
type: 'boolean',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
default: false,
|
|
62
|
-
description: 'Override the workspace-detection abort under --noConfirm',
|
|
63
|
-
flag: '--force',
|
|
64
|
-
required: false,
|
|
65
|
-
type: 'boolean',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
default: false,
|
|
69
|
-
description: 'Skip all interactive prompts',
|
|
70
|
-
flag: '--noConfirm',
|
|
71
|
-
required: false,
|
|
72
|
-
type: 'boolean',
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
})) {
|
|
77
|
+
if (toolbox.tools.helpJson(exports.help)) {
|
|
76
78
|
return;
|
|
77
79
|
}
|
|
78
80
|
// Load configuration
|
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.help = void 0;
|
|
12
13
|
const frontend_framework_detection_1 = require("../../lib/frontend-framework-detection");
|
|
13
14
|
/**
|
|
14
15
|
* Convert an existing frontend project between npm mode and vendor mode.
|
|
@@ -17,6 +18,45 @@ const frontend_framework_detection_1 = require("../../lib/frontend-framework-det
|
|
|
17
18
|
* lt frontend convert-mode --to vendor [--upstream-branch 1.5.3]
|
|
18
19
|
* lt frontend convert-mode --to npm [--version 1.5.3]
|
|
19
20
|
*/
|
|
21
|
+
exports.help = {
|
|
22
|
+
description: 'Convert frontend project between npm and vendor framework modes',
|
|
23
|
+
name: 'convert-mode',
|
|
24
|
+
options: [
|
|
25
|
+
{
|
|
26
|
+
description: 'Target mode',
|
|
27
|
+
flag: '--to',
|
|
28
|
+
required: true,
|
|
29
|
+
type: 'string',
|
|
30
|
+
values: ['vendor', 'npm'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
description: 'Upstream branch/tag to vendor from (only with --to vendor)',
|
|
34
|
+
flag: '--upstream-branch',
|
|
35
|
+
required: false,
|
|
36
|
+
type: 'string',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
description: 'nuxt-extensions version to install (only with --to npm, default: from VENDOR.md baseline)',
|
|
40
|
+
flag: '--version',
|
|
41
|
+
required: false,
|
|
42
|
+
type: 'string',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
default: false,
|
|
46
|
+
description: 'Skip confirmation prompt',
|
|
47
|
+
flag: '--noConfirm',
|
|
48
|
+
required: false,
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
default: false,
|
|
53
|
+
description: 'Show the resolved plan without making any changes',
|
|
54
|
+
flag: '--dry-run',
|
|
55
|
+
required: false,
|
|
56
|
+
type: 'boolean',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
20
60
|
const ConvertModeCommand = {
|
|
21
61
|
description: 'Convert app framework mode',
|
|
22
62
|
hidden: false,
|
|
@@ -24,45 +64,7 @@ const ConvertModeCommand = {
|
|
|
24
64
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
65
|
const { filesystem, frontendHelper, parameters, print: { error, info, spin, success, warning }, prompt: { confirm }, } = toolbox;
|
|
26
66
|
// 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
|
-
})) {
|
|
67
|
+
if (toolbox.tools.helpJson(exports.help)) {
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
68
70
|
const targetMode = parameters.options.to;
|