@lenne.tech/cli 1.18.0 → 1.20.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/bin/lt CHANGED
@@ -155,8 +155,12 @@ function runCLI() {
155
155
  require(`${__dirname}/../build/cli`).run(process.argv);
156
156
  } else {
157
157
  // this runs from the typescript source (for dev only)
158
- // hook into ts-node so we can run typescript on the fly
159
- require('ts-node').register({ project: `${__dirname}/../tsconfig.json` });
158
+ // hook into ts-node so we can run typescript on the fly. Use
159
+ // `transpileOnly` to skip ts-node's runtime type-checking pass —
160
+ // it triples startup time (~4 s → ~1.2 s) without catching
161
+ // anything new, since `npm run lint` (eslint) and `npm run
162
+ // compile` (tsc) already type-check the project before publish.
163
+ require('ts-node').register({ project: `${__dirname}/../tsconfig.json`, transpileOnly: true });
160
164
  // run the CLI with the current process arguments
161
165
  require(`${__dirname}/../src/cli`).run(process.argv);
162
166
  }
@@ -9,8 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ const workspace_integration_1 = require("../../lib/workspace-integration");
12
13
  /**
13
14
  * Create a new Angular workspace
15
+ *
16
+ * Standalone counterpart to `lt fullstack init` / `lt fullstack add-app`
17
+ * for Angular: clones ng-base-starter into a brand-new directory.
18
+ * Mirrors the same dry-run / workspace-detection surface as the Nuxt
19
+ * sibling so behaviour is consistent across the four flows.
14
20
  */
15
21
  const NewCommand = {
16
22
  alias: ['a'],
@@ -21,6 +27,54 @@ const NewCommand = {
21
27
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
22
28
  // Retrieve the tools we need
23
29
  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
+ })) {
76
+ return;
77
+ }
24
78
  // Load configuration
25
79
  const ltConfig = config.loadConfig();
26
80
  const configLocalize = (_c = (_b = (_a = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b.angular) === null || _c === void 0 ? void 0 : _c.localize;
@@ -31,6 +85,11 @@ const NewCommand = {
31
85
  const cliBranch = parameters.options.branch || parameters.options.b;
32
86
  const cliCopy = parameters.options.copy || parameters.options.c;
33
87
  const cliLink = parameters.options.link;
88
+ const cliName = parameters.options.name;
89
+ const cliDryRun = parameters.options['dry-run'];
90
+ const cliForce = parameters.options.force;
91
+ const dryRun = cliDryRun === true || cliDryRun === 'true';
92
+ const force = cliForce === true || cliForce === 'true';
34
93
  // Determine branch and copy/link paths with priority: CLI > config
35
94
  const branch = cliBranch || configBranch;
36
95
  const copyPath = cliCopy || configCopy;
@@ -49,11 +108,28 @@ const NewCommand = {
49
108
  if (!(yield git.gitInstalled())) {
50
109
  return;
51
110
  }
52
- // Get name of the workspace
53
- const name = yield helper.getInput(parameters.first, {
54
- name: 'workspace name',
55
- showError: true,
111
+ // Workspace-awareness bundled into runStandaloneWorkspaceGate.
112
+ const proceed = yield (0, workspace_integration_1.runStandaloneWorkspaceGate)({
113
+ cwd: '.',
114
+ filesystem,
115
+ force,
116
+ fromGluegunMenu: Boolean(toolbox.parameters.options.fromGluegunMenu),
117
+ noConfirmFlag: noConfirm,
118
+ pieceName: 'app',
119
+ print: { confirm, error, info },
120
+ projectKind: 'Angular app',
121
+ suggestion: 'lt fullstack add-app --frontend angular',
56
122
  });
123
+ if (!proceed)
124
+ return;
125
+ // Get name of the workspace. Honour `--name` flag before falling
126
+ // back to the first positional or interactive prompt — same fix
127
+ // applied to `server create` in this iteration.
128
+ const name = cliName ||
129
+ (yield helper.getInput(parameters.first, {
130
+ name: 'workspace name',
131
+ showError: true,
132
+ }));
57
133
  if (!name) {
58
134
  return;
59
135
  }
@@ -94,6 +170,36 @@ const NewCommand = {
94
170
  showError: false,
95
171
  })).trim();
96
172
  }
173
+ if (dryRun) {
174
+ info('');
175
+ info('Dry-run plan:');
176
+ info(` name: ${name}`);
177
+ info(` projectDir: ${projectDir}`);
178
+ info(` branch: ${branch || '(default)'}`);
179
+ info(` copy: ${copyPath || '(none)'}`);
180
+ info(` link: ${linkPath || '(none)'}`);
181
+ info(` localize: ${localize}`);
182
+ info(` gitLink: ${gitLink || '(none)'}`);
183
+ info('');
184
+ info('Would execute:');
185
+ if (linkPath) {
186
+ info(` 1. symlink ${linkPath} → ./${projectDir}`);
187
+ }
188
+ else if (copyPath) {
189
+ info(` 1. copy ${copyPath} → ./${projectDir}`);
190
+ }
191
+ else {
192
+ info(` 1. clone ng-base-starter${branch ? ` (branch: ${branch})` : ''} → ./${projectDir}`);
193
+ }
194
+ info(` 2. pnpm install + git init + remove husky`);
195
+ if (gitLink)
196
+ info(` 3. push initial commit to ${gitLink}`);
197
+ info('');
198
+ if (!toolbox.parameters.options.fromGluegunMenu) {
199
+ process.exit();
200
+ }
201
+ return `dry-run angular workspace ${projectDir}`;
202
+ }
97
203
  const workspaceSpinner = spin(`Creating angular workspace ${projectDir}${linkPath ? ' (link)' : copyPath ? ' (copy)' : branch ? ` (branch: ${branch})` : ''}...`);
98
204
  // Use FrontendHelper for setup
99
205
  const result = yield frontendHelper.setupAngular(`./${projectDir}`, {
@@ -9,8 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ const workspace_integration_1 = require("../../lib/workspace-integration");
12
13
  /**
13
14
  * Create a new nuxt workspace
15
+ *
16
+ * Standalone counterpart to `lt fullstack init` / `lt fullstack add-app`
17
+ * for Nuxt: clones nuxt-base-starter (or invokes create-nuxt-base) into
18
+ * a brand-new directory. Mirrors the same surface area as add-app where
19
+ * applicable so behaviour is consistent across the four flows.
14
20
  */
15
21
  const NewCommand = {
16
22
  alias: ['n'],
@@ -18,30 +24,179 @@ const NewCommand = {
18
24
  hidden: false,
19
25
  name: 'nuxt',
20
26
  run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
21
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
22
- // Retrieve the tools we need
23
- const { config, frontendHelper, helper, parameters, print: { spin }, prompt: { ask }, strings: { kebabCase }, system, } = toolbox;
24
- // Load configuration
27
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
28
+ const { config, filesystem, frontendHelper, helper, parameters, print: { error, info, spin }, prompt: { ask, confirm }, strings: { kebabCase }, system, } = toolbox;
29
+ if (toolbox.tools.helpJson({
30
+ aliases: ['n'],
31
+ configuration: 'commands.frontend.nuxt.*',
32
+ description: 'Create a new Nuxt workspace from nuxt-base-starter',
33
+ name: 'nuxt',
34
+ options: [
35
+ { description: 'Workspace name', flag: '--name', required: false, type: 'string' },
36
+ { description: 'Branch of nuxt-base-starter to clone', flag: '--branch', required: false, type: 'string' },
37
+ { description: 'Copy from local template directory', flag: '--copy', required: false, type: 'string' },
38
+ { description: 'Symlink to local template directory', flag: '--link', required: false, type: 'string' },
39
+ {
40
+ description: 'Frontend framework consumption mode',
41
+ flag: '--frontend-framework-mode',
42
+ required: false,
43
+ type: 'string',
44
+ values: ['npm', 'vendor'],
45
+ },
46
+ {
47
+ default: false,
48
+ description: 'Default branch to nuxt-base-starter#next (auth basePath aligned with experimental --next API)',
49
+ flag: '--next',
50
+ required: false,
51
+ type: 'boolean',
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
+ })) {
76
+ return;
77
+ }
78
+ // Load configuration. Nuxt-specific keys live under
79
+ // `commands.frontend.nuxt.*`; vendoring + experimental fall back to
80
+ // the shared `commands.fullstack.*` block so a project's lt.config
81
+ // does not have to repeat them.
25
82
  const ltConfig = config.loadConfig();
26
83
  const configBranch = (_c = (_b = (_a = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b.nuxt) === null || _c === void 0 ? void 0 : _c.branch;
27
84
  const configCopy = (_f = (_e = (_d = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _d === void 0 ? void 0 : _d.frontend) === null || _e === void 0 ? void 0 : _e.nuxt) === null || _f === void 0 ? void 0 : _f.copy;
28
85
  const configLink = (_j = (_h = (_g = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _g === void 0 ? void 0 : _g.frontend) === null || _h === void 0 ? void 0 : _h.nuxt) === null || _j === void 0 ? void 0 : _j.link;
86
+ const configFrontendFrameworkMode = (_l = (_k = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _k === void 0 ? void 0 : _k.fullstack) === null || _l === void 0 ? void 0 : _l.frontendFrameworkMode;
29
87
  // Parse CLI arguments
30
88
  const cliBranch = parameters.options.branch || parameters.options.b;
31
89
  const cliCopy = parameters.options.copy || parameters.options.c;
32
90
  const cliLink = parameters.options.link;
33
- // Determine branch and copy/link paths with priority: CLI > config
34
- const branch = cliBranch || configBranch;
91
+ const cliName = parameters.options.name;
92
+ const cliFrontendFrameworkMode = parameters.options['frontend-framework-mode'];
93
+ const cliDryRun = parameters.options['dry-run'];
94
+ const cliForce = parameters.options.force;
95
+ const experimental = parameters.options.next === true || parameters.options.next === 'true';
96
+ const dryRun = cliDryRun === true || cliDryRun === 'true';
97
+ const force = cliForce === true || cliForce === 'true';
98
+ const noConfirm = config.getNoConfirm({
99
+ cliValue: parameters.options.noConfirm,
100
+ commandConfig: (_o = (_m = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _m === void 0 ? void 0 : _m.frontend) === null || _o === void 0 ? void 0 : _o.nuxt,
101
+ config: ltConfig,
102
+ });
103
+ // Workspace-awareness — bundled into runStandaloneWorkspaceGate.
104
+ const proceed = yield (0, workspace_integration_1.runStandaloneWorkspaceGate)({
105
+ cwd: '.',
106
+ filesystem,
107
+ force,
108
+ fromGluegunMenu: Boolean(toolbox.parameters.options.fromGluegunMenu),
109
+ noConfirmFlag: noConfirm,
110
+ pieceName: 'app',
111
+ print: { confirm, error, info },
112
+ projectKind: 'Nuxt app',
113
+ suggestion: 'lt fullstack add-app --frontend nuxt',
114
+ });
115
+ if (!proceed)
116
+ return;
117
+ // Resolve branch / copy / link with priority: CLI > config.
118
+ // Under `--next`, default to nuxt-base-starter#next so the cloned
119
+ // template ships an auth basePath aligned with the experimental
120
+ // nest-base API — same default as `lt fullstack init --next` and
121
+ // `lt fullstack add-app --next`.
122
+ const branch = cliBranch || configBranch || (experimental ? 'next' : undefined);
35
123
  const copyPath = cliCopy || configCopy;
36
124
  const linkPath = cliLink || configLink;
37
- const projName = (yield ask({
38
- message: "What is the project's name?",
39
- name: 'projectName',
40
- required: true,
41
- type: 'input',
42
- })).projectName;
125
+ // Resolve frontend framework mode (npm vs vendored nuxt-extensions).
126
+ let frontendFrameworkMode;
127
+ if (cliFrontendFrameworkMode === 'npm' || cliFrontendFrameworkMode === 'vendor') {
128
+ frontendFrameworkMode = cliFrontendFrameworkMode;
129
+ }
130
+ else if (cliFrontendFrameworkMode) {
131
+ error(`Invalid --frontend-framework-mode value "${cliFrontendFrameworkMode}". Use "npm" or "vendor".`);
132
+ return;
133
+ }
134
+ else if (configFrontendFrameworkMode === 'npm' || configFrontendFrameworkMode === 'vendor') {
135
+ frontendFrameworkMode = configFrontendFrameworkMode;
136
+ info(`Using frontend framework mode from lt.config: ${frontendFrameworkMode}`);
137
+ }
138
+ else {
139
+ frontendFrameworkMode = 'npm';
140
+ }
141
+ // Resolve workspace name with priority: --name > positional > interactive.
142
+ let projName = cliName;
143
+ if (!projName) {
144
+ if (noConfirm) {
145
+ // Without an explicit name, refuse rather than guess. Matches
146
+ // the safety stance taken by `fullstack init` under noConfirm.
147
+ error('Missing workspace name. Pass --name <slug> when using --noConfirm.');
148
+ return;
149
+ }
150
+ projName = (yield ask({
151
+ message: "What is the project's name?",
152
+ name: 'projectName',
153
+ required: true,
154
+ type: 'input',
155
+ })).projectName;
156
+ }
157
+ if (!projName) {
158
+ return;
159
+ }
43
160
  const projectDir = kebabCase(projName);
44
- // Start timer
161
+ if (filesystem.exists(projectDir)) {
162
+ info('');
163
+ error(`There's already a folder named "${projectDir}" here.`);
164
+ return;
165
+ }
166
+ if (dryRun) {
167
+ info('');
168
+ info('Dry-run plan:');
169
+ info(` name: ${projName}`);
170
+ info(` projectDir: ${projectDir}`);
171
+ info(` branch: ${branch || '(default — uses create-nuxt-base)'}`);
172
+ info(` copy: ${copyPath || '(none)'}`);
173
+ info(` link: ${linkPath || '(none)'}`);
174
+ info(` frontendFrameworkMode: ${frontendFrameworkMode}`);
175
+ info(` experimental (--next): ${experimental}`);
176
+ info('');
177
+ info('Would execute:');
178
+ if (linkPath) {
179
+ info(` 1. symlink ${linkPath} → ./${projectDir}`);
180
+ }
181
+ else if (copyPath) {
182
+ info(` 1. copy ${copyPath} → ./${projectDir}`);
183
+ }
184
+ else if (branch) {
185
+ info(` 1. clone nuxt-base-starter (branch: ${branch}) → ./${projectDir}`);
186
+ }
187
+ else {
188
+ info(` 1. exec create-nuxt-base@latest → ./${projectDir}`);
189
+ }
190
+ if (frontendFrameworkMode === 'vendor') {
191
+ info(` 2. clone @lenne.tech/nuxt-extensions → /tmp`);
192
+ info(` 3. vendor app/core/ (module.ts + runtime/)`);
193
+ }
194
+ info('');
195
+ if (!toolbox.parameters.options.fromGluegunMenu) {
196
+ process.exit();
197
+ }
198
+ return `dry-run nuxt workspace (${frontendFrameworkMode})`;
199
+ }
45
200
  const timer = system.startTimer();
46
201
  const baseSpinner = spin(`Creating nuxt-base with name '${projectDir}'${linkPath ? ' (link)' : copyPath ? ' (copy)' : branch ? ` (branch: ${branch})` : ''}`);
47
202
  // Use FrontendHelper for setup
@@ -56,6 +211,22 @@ const NewCommand = {
56
211
  return;
57
212
  }
58
213
  baseSpinner.succeed(`Successfully created nuxt workspace with name '${projectDir}' in ${helper.msToMinutesAndSeconds(timer())}m.`);
214
+ // Vendor the nuxt-extensions module if requested. Skipped for link
215
+ // mode because the linked checkout is shared with the upstream
216
+ // template and must not be mutated. Same guard as in init/add-app.
217
+ if (frontendFrameworkMode === 'vendor' && result.method !== 'link') {
218
+ const vendorSpinner = spin('Converting frontend to vendor mode...');
219
+ try {
220
+ yield frontendHelper.convertAppCloneToVendored({
221
+ dest: `./${projectDir}`,
222
+ projectName: projectDir,
223
+ });
224
+ vendorSpinner.succeed('Frontend converted to vendor mode (app/core/)');
225
+ }
226
+ catch (err) {
227
+ vendorSpinner.fail(`Frontend vendor conversion failed: ${err.message}`);
228
+ }
229
+ }
59
230
  if (!toolbox.parameters.options.fromGluegunMenu) {
60
231
  process.exit();
61
232
  }