@lenne.tech/cli 1.33.0 → 1.35.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.
Files changed (52) hide show
  1. package/README.md +6 -5
  2. package/build/commands/codex/codex.js +23 -0
  3. package/build/commands/codex/plugins.js +103 -0
  4. package/build/commands/codex/shortcuts.js +110 -0
  5. package/build/commands/config/validate.js +0 -4
  6. package/build/commands/deployment/create.js +218 -213
  7. package/build/commands/dev/init.js +2 -1
  8. package/build/commands/dev/status.js +2 -0
  9. package/build/commands/dev/test.js +12 -6
  10. package/build/commands/dev/up.js +17 -7
  11. package/build/commands/frontend/nuxt.js +4 -7
  12. package/build/commands/fullstack/add-api.js +6 -9
  13. package/build/commands/fullstack/add-app.js +5 -7
  14. package/build/commands/fullstack/init.js +34 -20
  15. package/build/commands/status.js +2 -0
  16. package/build/commands/ticket/list.js +2 -0
  17. package/build/commands/ticket/start.js +6 -4
  18. package/build/extensions/frontend-helper.js +44 -39
  19. package/build/extensions/server.js +354 -22
  20. package/build/lib/angular-environments.js +108 -0
  21. package/build/lib/codex-cli.js +56 -0
  22. package/build/lib/codex-plugin-utils.js +102 -0
  23. package/build/lib/dev-identity.js +38 -18
  24. package/build/lib/dev-package-manager.js +89 -0
  25. package/build/lib/dev-patches.js +4 -1
  26. package/build/lib/dev-project.js +20 -7
  27. package/build/lib/dev-state.js +17 -1
  28. package/build/lib/dev-test-session.js +61 -19
  29. package/build/lib/dev-ticket.js +12 -6
  30. package/build/lib/ensure-root-dockerignore.js +92 -0
  31. package/build/lib/fs-utils.js +19 -0
  32. package/build/lib/hoist-workspace-pnpm-config.js +2 -11
  33. package/build/lib/remove-nested-lockfiles.js +53 -0
  34. package/build/lib/turboops-config.js +93 -0
  35. package/build/lib/vendor-claude-md.js +7 -2
  36. package/build/lib/workspace-integration.js +96 -3
  37. package/build/templates/deployment/turboops.json.ejs +3 -0
  38. package/build/templates/vendor-scripts/migrate-store.js +25 -0
  39. package/docs/LT-ECOSYSTEM-GUIDE.md +1 -1
  40. package/docs/commands.md +30 -9
  41. package/docs/lt.config.md +9 -19
  42. package/package.json +1 -1
  43. package/build/templates/deployment/.github/workflows/pre-release.yml.ejs +0 -41
  44. package/build/templates/deployment/.github/workflows/release.yml.ejs +0 -41
  45. package/build/templates/deployment/.gitlab-ci.yml.ejs +0 -181
  46. package/build/templates/deployment/Dockerfile.app.ejs +0 -13
  47. package/build/templates/deployment/Dockerfile.ejs +0 -18
  48. package/build/templates/deployment/docker-compose.dev.yml.ejs +0 -99
  49. package/build/templates/deployment/docker-compose.prod.yml.ejs +0 -92
  50. package/build/templates/deployment/docker-compose.test.yml.ejs +0 -98
  51. package/build/templates/deployment/scripts/build-push.sh.ejs +0 -20
  52. package/build/templates/deployment/scripts/deploy.sh.ejs +0 -7
@@ -14,6 +14,7 @@ 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_package_manager_1 = require("../../lib/dev-package-manager");
17
18
  const dev_patches_1 = require("../../lib/dev-patches");
18
19
  const dev_process_1 = require("../../lib/dev-process");
19
20
  const dev_project_1 = require("../../lib/dev-project");
@@ -85,7 +86,8 @@ const UpCommand = {
85
86
  const { filesystem, parameters, print: { colors, error, info, success, warning }, } = toolbox;
86
87
  const layout = (0, dev_project_1.resolveLayout)(filesystem.cwd(), filesystem);
87
88
  if (!layout.apiDir && !layout.appDir) {
88
- error('No API or App project detected at this path. Run `lt dev init` first.');
89
+ error('No lt project detected at this path. Expected an API (src/config.env.ts or nest-cli.json), ' +
90
+ 'an App (nuxt.config.ts), or a monorepo with projects/api and/or projects/app.');
89
91
  if (!parameters.options.fromGluegunMenu)
90
92
  process.exit(1);
91
93
  return 'dev up: not a project';
@@ -270,7 +272,9 @@ const UpCommand = {
270
272
  apiUpstreamPort: existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.internalPorts.api,
271
273
  appHostname: (_d = identity.subdomains.app) === null || _d === void 0 ? void 0 : _d.hostname,
272
274
  appUpstreamPort: existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.internalPorts.app,
273
- dbName: (_e = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.dbName) !== null && _e !== void 0 ? _e : dbName,
275
+ // App-only projects have no database don't advertise a Mongo URL
276
+ // that nothing listens on.
277
+ dbName: identity.subdomains.api ? ((_e = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.dbName) !== null && _e !== void 0 ? _e : dbName) : undefined,
274
278
  });
275
279
  info('Run `lt dev down` first to force a full restart.');
276
280
  if (!parameters.options.fromGluegunMenu)
@@ -316,7 +320,8 @@ const UpCommand = {
316
320
  info(` app: https://${identity.subdomains.app.hostname} → 127.0.0.1:${appPort}`);
317
321
  if (identity.subdomains.api)
318
322
  info(` api: https://${identity.subdomains.api.hostname} → 127.0.0.1:${apiPort}`);
319
- info(` db: mongodb://127.0.0.1/${dbName}`);
323
+ if (identity.subdomains.api)
324
+ info(` db: mongodb://127.0.0.1/${dbName}`);
320
325
  info('');
321
326
  // Build env per process.
322
327
  const devEnv = (0, dev_env_1.buildDevEnv)({
@@ -326,7 +331,6 @@ const UpCommand = {
326
331
  dbName,
327
332
  identity,
328
333
  });
329
- const pnpmBin = process.env.LT_PNPM_BIN || 'pnpm';
330
334
  const pids = {};
331
335
  const rotationNotes = [];
332
336
  const started = [];
@@ -350,7 +354,12 @@ const UpCommand = {
350
354
  }
351
355
  else {
352
356
  yield reclaimPort(existingSession === null || existingSession === void 0 ? void 0 : existingSession.pids.api, apiPort, apiHealth !== null && apiHealth !== void 0 ? apiHealth : 'dead');
353
- const apiResult = (0, dev_process_1.spawnDetached)(pnpmBin, ['start'], {
357
+ // Per-component PM detection: a monorepo may have an npm api and a
358
+ // pnpm app, and the legacy hard-coded `pnpm start` would silently
359
+ // regenerate a foreign lockfile + crash on un-approved build
360
+ // scripts when run against an npm-only project.
361
+ const apiPm = (0, dev_package_manager_1.pickPackageManager)(layout.apiDir);
362
+ const apiResult = (0, dev_process_1.spawnDetached)(apiPm.bin, apiPm.runScript('start'), {
354
363
  cwd: layout.apiDir,
355
364
  env: devEnv.api.env,
356
365
  logFile: (0, path_1.join)(layout.root, '.lt-dev', 'api.log'),
@@ -371,7 +380,8 @@ const UpCommand = {
371
380
  }
372
381
  else {
373
382
  yield reclaimPort(existingSession === null || existingSession === void 0 ? void 0 : existingSession.pids.app, appPort, appHealth !== null && appHealth !== void 0 ? appHealth : 'dead');
374
- const appResult = (0, dev_process_1.spawnDetached)(pnpmBin, ['dev'], {
383
+ const appPm = (0, dev_package_manager_1.pickPackageManager)(layout.appDir);
384
+ const appResult = (0, dev_process_1.spawnDetached)(appPm.bin, appPm.runScript('dev'), {
375
385
  cwd: layout.appDir,
376
386
  env: devEnv.app.env,
377
387
  logFile: (0, path_1.join)(layout.root, '.lt-dev', 'app.log'),
@@ -408,7 +418,7 @@ const UpCommand = {
408
418
  apiUpstreamPort: apiPort,
409
419
  appHostname: (_l = identity.subdomains.app) === null || _l === void 0 ? void 0 : _l.hostname,
410
420
  appUpstreamPort: appPort,
411
- dbName,
421
+ dbName: identity.subdomains.api ? dbName : undefined,
412
422
  });
413
423
  info(colors.dim('Logs: <root>/.lt-dev/api.log, <root>/.lt-dev/app.log'));
414
424
  for (const note of rotationNotes)
@@ -15,7 +15,7 @@ const workspace_integration_1 = require("../../lib/workspace-integration");
15
15
  * Create a new nuxt workspace
16
16
  *
17
17
  * Standalone counterpart to `lt fullstack init` / `lt fullstack add-app`
18
- * for Nuxt: clones nuxt-base-starter (or invokes create-nuxt-base) into
18
+ * for Nuxt: clones nuxt-base-starter into
19
19
  * a brand-new directory. Mirrors the same surface area as add-app where
20
20
  * applicable so behaviour is consistent across the four flows.
21
21
  */
@@ -170,7 +170,7 @@ const NewCommand = {
170
170
  info('Dry-run plan:');
171
171
  info(` name: ${projName}`);
172
172
  info(` projectDir: ${projectDir}`);
173
- info(` branch: ${branch || '(default — uses create-nuxt-base)'}`);
173
+ info(` branch: ${branch || '(default branch)'}`);
174
174
  info(` copy: ${copyPath || '(none)'}`);
175
175
  info(` link: ${linkPath || '(none)'}`);
176
176
  info(` frontendFrameworkMode: ${frontendFrameworkMode}`);
@@ -183,11 +183,8 @@ const NewCommand = {
183
183
  else if (copyPath) {
184
184
  info(` 1. copy ${copyPath} → ./${projectDir}`);
185
185
  }
186
- else if (branch) {
187
- info(` 1. clone nuxt-base-starter (branch: ${branch}) → ./${projectDir}`);
188
- }
189
186
  else {
190
- info(` 1. exec create-nuxt-base@latest → ./${projectDir}`);
187
+ info(` 1. clone nuxt-base-starter (${branch ? `branch: ${branch}` : 'default branch'}) → ./${projectDir}`);
191
188
  }
192
189
  if (frontendFrameworkMode === 'vendor') {
193
190
  info(` 2. clone @lenne.tech/nuxt-extensions → /tmp`);
@@ -206,7 +203,7 @@ const NewCommand = {
206
203
  branch,
207
204
  copyPath,
208
205
  linkPath,
209
- skipInstall: true, // Nuxt standalone doesn't need npm install (create-nuxt-base handles it)
206
+ skipInstall: false, // clone-based setup installs here (create-nuxt-base no longer used)
210
207
  });
211
208
  if (!result.success) {
212
209
  baseSpinner.fail(`Failed to setup nuxt workspace: ${result.path}`);
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.help = void 0;
13
- const hoist_workspace_pnpm_config_1 = require("../../lib/hoist-workspace-pnpm-config");
14
13
  const workspace_integration_1 = require("../../lib/workspace-integration");
15
14
  /**
16
15
  * Add an API (`projects/api/`) to a fullstack workspace that currently
@@ -320,14 +319,12 @@ const NewCommand = {
320
319
  if (apiResult.method !== 'link') {
321
320
  (0, workspace_integration_1.writeApiConfig)({ apiDir: apiDest, apiMode, filesystem, frameworkMode });
322
321
  }
323
- // Hoist pnpm config `setupServerForFullstack` may have produced a
324
- // sub-project package.json with `pnpm.overrides` etc. that pnpm
325
- // ignores at non-root level.
326
- (0, hoist_workspace_pnpm_config_1.hoistWorkspacePnpmConfig)({
327
- filesystem,
328
- projectDir: workspaceDir,
329
- subProjects: ['projects/api', 'projects/app'],
330
- });
322
+ // Normalize the workspace root: hoist pnpm config out of the sub-projects
323
+ // (`setupServerForFullstack` may have left `pnpm.overrides` etc. at a
324
+ // non-root level pnpm ignores), drop nested lockfiles the root supersedes,
325
+ // and guarantee a root `.dockerignore` (Docker never reads a sub-project's
326
+ // own when building from the root context).
327
+ (0, workspace_integration_1.finalizeWorkspaceRoot)({ filesystem, projectDir: workspaceDir });
331
328
  // Run install + format unless explicitly skipped (CI/agents may
332
329
  // want to chain multiple add-* calls before installing once).
333
330
  if (!skipInstall && !experimental) {
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.help = void 0;
13
- const hoist_workspace_pnpm_config_1 = require("../../lib/hoist-workspace-pnpm-config");
14
13
  const workspace_integration_1 = require("../../lib/workspace-integration");
15
14
  /**
16
15
  * Add a frontend app (`projects/app/`) to a fullstack workspace that
@@ -249,12 +248,11 @@ const NewCommand = {
249
248
  warning('Continuing with npm mode for frontend.');
250
249
  }
251
250
  }
252
- // Hoist pnpm config (frontend templates may carry pnpm.overrides too).
253
- (0, hoist_workspace_pnpm_config_1.hoistWorkspacePnpmConfig)({
254
- filesystem,
255
- projectDir: workspaceDir,
256
- subProjects: ['projects/api', 'projects/app'],
257
- });
251
+ // Normalize the workspace root: hoist pnpm config (frontend templates may
252
+ // carry pnpm.overrides too), drop the frontend starter's standalone
253
+ // pnpm-lock.yaml the root supersedes, and guarantee a root `.dockerignore`
254
+ // (else the image inherits the host's node_modules, .output and .env).
255
+ (0, workspace_integration_1.finalizeWorkspaceRoot)({ filesystem, projectDir: workspaceDir });
258
256
  if (!skipInstall) {
259
257
  const installSpinner = spin('Install workspace packages');
260
258
  try {
@@ -16,7 +16,6 @@ const gluegun_1 = require("gluegun");
16
16
  const caddy_1 = require("../../lib/caddy");
17
17
  const dev_migrate_helper_1 = require("../../lib/dev-migrate-helper");
18
18
  const dev_project_1 = require("../../lib/dev-project");
19
- const hoist_workspace_pnpm_config_1 = require("../../lib/hoist-workspace-pnpm-config");
20
19
  const package_name_1 = require("../../lib/package-name");
21
20
  const vendor_claude_md_1 = require("../../lib/vendor-claude-md");
22
21
  const workspace_integration_1 = require("../../lib/workspace-integration");
@@ -33,7 +32,7 @@ const NewCommand = {
33
32
  run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
34
33
  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;
35
34
  // Retrieve the tools we need
36
- const { config, filesystem, frontendHelper, git, helper, parameters, patching, print: { colors, error, info, spin, success }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, template, } = toolbox;
35
+ const { config, filesystem, frontendHelper, git, helper, parameters, patching, print: { colors, error, info, spin, success, warning }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, template, } = toolbox;
37
36
  // Start timer
38
37
  const timer = system.startTimer();
39
38
  // Info
@@ -209,7 +208,9 @@ const NewCommand = {
209
208
  // `/lt-dev:backend:update-nest-server-core`; local patches are logged
210
209
  // in src/core/VENDOR.md.
211
210
  //
212
- // Default is still 'npm' until the vendoring pilot is fully evaluated.
211
+ // Default is 'vendor' the lt CLI integrates the framework core directly
212
+ // (clones the starter, vendors core into src/core/), which is the
213
+ // recommended setup; pass `--framework-mode npm` for the classic npm dep.
213
214
  let frameworkMode;
214
215
  if (experimental) {
215
216
  frameworkMode = 'npm';
@@ -226,16 +227,16 @@ const NewCommand = {
226
227
  info(`Using framework mode from lt.config: ${frameworkMode}`);
227
228
  }
228
229
  else if (noConfirm) {
229
- frameworkMode = 'npm';
230
- info('Using default framework mode: npm (noConfirm mode)');
230
+ frameworkMode = 'vendor';
231
+ info('Using default framework mode: vendor (noConfirm mode)');
231
232
  }
232
233
  else {
233
234
  const frameworkModeChoice = yield ask({
234
235
  choices: [
235
236
  'npm - @lenne.tech/nest-server as npm dependency (classic, stable)',
236
- 'vendor - framework core vendored into projects/api/src/core/ (pilot, allows local patches)',
237
+ 'vendor - framework core vendored into projects/api/src/core/ (default, allows local patches)',
237
238
  ],
238
- initial: 0,
239
+ initial: 1,
239
240
  message: 'Framework consumption mode?',
240
241
  name: 'frameworkMode',
241
242
  type: 'select',
@@ -257,11 +258,11 @@ const NewCommand = {
257
258
  info(`Using frontend framework mode from lt.config: ${frontendFrameworkMode}`);
258
259
  }
259
260
  else if (noConfirm) {
260
- frontendFrameworkMode = 'npm';
261
+ frontendFrameworkMode = 'vendor';
261
262
  }
262
263
  else {
263
- // Default to npm without asking (unless user sets it explicitly)
264
- frontendFrameworkMode = 'npm';
264
+ // Default to vendor without asking (unless user sets it explicitly)
265
+ frontendFrameworkMode = 'vendor';
265
266
  }
266
267
  // Determine remote push settings with priority: CLI > config > interactive
267
268
  // Git is always initialized; the question is whether to push to a remote
@@ -273,8 +274,13 @@ const NewCommand = {
273
274
  if (pushToRemote) {
274
275
  gitLink = cliGitLink || configGitLink;
275
276
  if (!gitLink) {
276
- error('--git-link is required when --git is true (or configure gitLink in lt.config)');
277
- return;
277
+ // Git is always initialized locally (dev branch + initial commit);
278
+ // --git only controls the remote push. Without a link there is
279
+ // nothing to push to, so degrade to a local-only init instead of
280
+ // aborting the whole scaffold — consistent with the config and
281
+ // interactive branches below.
282
+ warning('--git true without --git-link: initializing local git only (no remote push).');
283
+ pushToRemote = false;
278
284
  }
279
285
  }
280
286
  }
@@ -314,6 +320,14 @@ const NewCommand = {
314
320
  }
315
321
  }
316
322
  }
323
+ else if (noConfirm) {
324
+ // Default in noConfirm mode: git true. Git is always initialized locally
325
+ // (dev branch + initial commit); push to a remote only when a gitLink is
326
+ // configured, otherwise stay local-only (nothing to push to).
327
+ gitLink = configGitLink;
328
+ pushToRemote = !!gitLink;
329
+ info(`Using default git: true${gitLink ? ' (push to configured gitLink)' : ' (local only — no --git-link configured)'}`);
330
+ }
317
331
  // Determine branches and copy/link paths with priority: CLI > config
318
332
  const apiBranch = cliApiBranch || configApiBranch;
319
333
  // Under `--next`, default the nuxt-base-starter ref to the `next`
@@ -606,14 +620,14 @@ const NewCommand = {
606
620
  else {
607
621
  serverSpinner.warn('Nest Server Starter not integrated');
608
622
  }
609
- // Hoist workspace-scoped pnpm config out of sub-projects. pnpm only
610
- // honors `pnpm.overrides`, `pnpm.onlyBuiltDependencies`, and
611
- // `pnpm.ignoredOptionalDependencies` at the workspace root; leaving
612
- // them in projects/api/package.json or projects/app/package.json
613
- // causes `WARN The field was found in This will not take
614
- // effect. You should configure at the root of the workspace
615
- // instead.` and silently disables CVE overrides.
616
- (0, hoist_workspace_pnpm_config_1.hoistWorkspacePnpmConfig)({ filesystem, projectDir, subProjects: ['projects/api', 'projects/app'] });
623
+ // Normalize the workspace root. Hoists workspace-scoped pnpm config out
624
+ // of the sub-projects (pnpm only honors `pnpm.overrides` etc. at the
625
+ // root otherwise CVE overrides are silently disabled), drops the
626
+ // starters' standalone pnpm-lock.yaml files the root supersedes, and
627
+ // guarantees a root `.dockerignore` (else the images carry the host's
628
+ // node_modules, a stale .output, and a local .env that `nuxt build`
629
+ // bakes in).
630
+ (0, workspace_integration_1.finalizeWorkspaceRoot)({ filesystem, projectDir });
617
631
  // Install all packages
618
632
  if (!experimental) {
619
633
  const installSpinner = spin('Install all packages');
@@ -251,6 +251,8 @@ const StatusCommand = {
251
251
  for (const [sub, host] of Object.entries(entry.subdomains)) {
252
252
  info(` ${sub.padEnd(6)} https://${host}`);
253
253
  }
254
+ // `loadRegistry` strips dbName from API-less entries, so its
255
+ // presence alone means there is a database to advertise.
254
256
  if (entry.dbName)
255
257
  info(` db mongodb://127.0.0.1/${entry.dbName}`);
256
258
  const session = (0, dev_state_1.loadSession)(registryRoot);
@@ -61,6 +61,8 @@ const ListCommand = {
61
61
  if (entry) {
62
62
  for (const [sub, host] of Object.entries(entry.subdomains))
63
63
  info(` ${sub.padEnd(4)} https://${host}`);
64
+ // `loadRegistry` strips dbName from API-less entries, so its presence
65
+ // alone means there is a database to show.
64
66
  if (entry.dbName)
65
67
  info(` db mongodb://127.0.0.1/${entry.dbName}`);
66
68
  }
@@ -97,14 +97,16 @@ const StartCommand = {
97
97
  }
98
98
  // 2. tag the worktree with its ticket id (makes lt dev * ticket-aware).
99
99
  (0, dev_ticket_1.writeTicketMarker)(worktreePath, id);
100
- // 3. install deps (pnpm hard-links from the shared store → fast).
100
+ // 3. install deps. Auto-detects pnpm/yarn/npm from the worktree's
101
+ // lockfile so an npm-only repo doesn't get a foreign pnpm-lock
102
+ // injected on every ticket start.
101
103
  if (parameters.options.install !== false) {
102
- info(colors.dim('Installing dependencies (pnpm) …'));
104
+ info(colors.dim('Installing dependencies …'));
103
105
  try {
104
- (0, dev_ticket_1.pnpmInstall)(worktreePath);
106
+ (0, dev_ticket_1.installWorktreeDeps)(worktreePath);
105
107
  }
106
108
  catch (e) {
107
- warning(`pnpm install failed (${e.message}) — continuing; run it manually in the worktree.`);
109
+ warning(`install failed (${e.message}) — continuing; run it manually in the worktree.`);
108
110
  }
109
111
  }
110
112
  // 4. bring the isolated stack up (re-invokes THIS lt build so the marker is
@@ -141,49 +141,54 @@ class FrontendHelper {
141
141
  return __awaiter(this, arguments, void 0, function* (dest, options = {}) {
142
142
  const { system, templateHelper } = this.toolbox;
143
143
  const { branch, copyPath, linkPath, skipInstall } = options;
144
- // Use template extension for link/copy/branch operations
145
- if (linkPath || copyPath || branch) {
146
- const result = yield templateHelper.setup(dest, {
147
- branch,
148
- copyPath,
149
- isNuxt: true,
150
- linkPath,
151
- repoUrl: branch ? 'https://github.com/lenneTech/nuxt-base-starter.git' : undefined,
152
- });
153
- if (!result.success) {
154
- return { method: result.method, path: result.path, success: false };
155
- }
156
- // After a clone, flatten the wrapper layout so `projects/app/`
157
- // IS the Nuxt app (the cloned root is the `create-nuxt-base`
158
- // scaffolder, not the app — see flattenNuxtBaseTemplate).
159
- // Skip on link mode: a symlink points at the user's local
160
- // checkout and must not have its template subdir torn out.
161
- if (result.method === 'clone') {
162
- yield this.flattenNuxtBaseTemplate(dest);
163
- }
164
- // Run install if not skipped and not a symlink
165
- if (!skipInstall && result.method !== 'link') {
166
- try {
167
- const { pm } = this.toolbox;
168
- yield system.run(`cd "${dest}" && ${pm.install(pm.detect(dest))}`);
169
- }
170
- catch (err) {
171
- return { method: result.method, path: dest, success: false };
172
- }
173
- }
174
- return { method: result.method, path: result.path, success: true };
144
+ // Source the Nuxt app from the nuxt-base-starter repo (priority:
145
+ // link > copy > clone), mirroring the backend (nest-server-starter)
146
+ // flow. The default no link/copy/branch — clones the repo's default
147
+ // branch, so a fresh init always integrates the CURRENT GitHub template.
148
+ //
149
+ // This deliberately replaces the former `create-nuxt-base@latest` npx
150
+ // path: that package is marked `private` and is no longer published
151
+ // (nuxt-base-starter commit b534af2 — "distribute via lt CLI fullstack
152
+ // template"), so `@latest` was frozen at an outdated copy and the
153
+ // bundled template drifted from GitHub. Cloning the repo directly is
154
+ // the same mechanism the backend already uses and removes that drift.
155
+ const result = yield templateHelper.setup(dest, {
156
+ branch,
157
+ copyPath,
158
+ isNuxt: true,
159
+ linkPath,
160
+ repoUrl: linkPath || copyPath ? undefined : 'https://github.com/lenneTech/nuxt-base-starter.git',
161
+ });
162
+ if (!result.success) {
163
+ return { method: result.method, path: result.path, success: false };
175
164
  }
176
- // Default: use create-nuxt-base
177
- try {
178
- const { pm } = this.toolbox;
179
- yield system.run(pm.exec(`create-nuxt-base@latest "${dest}"`));
180
- // Fix package name - create-nuxt-base uses path as name which is invalid for lerna
165
+ // After a clone, flatten the wrapper layout so `dest` IS the Nuxt app
166
+ // (the cloned root carries the scaffolder; the app lives in
167
+ // `nuxt-base-template/` see flattenNuxtBaseTemplate). Skip on link
168
+ // mode: a symlink points at the user's checkout and must not be torn out.
169
+ if (result.method === 'clone') {
170
+ yield this.flattenNuxtBaseTemplate(dest);
171
+ }
172
+ // Normalize the sub-project package name to a stable, valid workspace
173
+ // name ("app"). The template ships as `nuxt-base-template`; the former
174
+ // create-nuxt-base path renamed it the same way. Skip link mode — a
175
+ // symlinked checkout is shared with upstream and must not be mutated.
176
+ if (result.method !== 'link') {
181
177
  yield this.fixPackageName(dest);
182
- return { method: 'npx', path: dest, success: true };
183
178
  }
184
- catch (err) {
185
- return { method: 'npx', path: dest, success: false };
179
+ // Run install unless skipped or a symlink. Fullstack init installs at
180
+ // the monorepo level (skipInstall: true); standalone `lt frontend nuxt`
181
+ // installs here (create-nuxt-base used to handle that).
182
+ if (!skipInstall && result.method !== 'link') {
183
+ try {
184
+ const { pm } = this.toolbox;
185
+ yield system.run(`cd "${dest}" && ${pm.install(pm.detect(dest))}`);
186
+ }
187
+ catch (_a) {
188
+ return { method: result.method, path: dest, success: false };
189
+ }
186
190
  }
191
+ return { method: result.method, path: result.path, success: true };
187
192
  });
188
193
  }
189
194
  /**