@lessonkit/cli 0.9.3 → 1.0.1
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 +20 -34
- package/dist/bin.js +110 -139
- package/dist/index.js +110 -139
- package/package.json +3 -3
- package/template/vite-react/README.md +13 -11
- package/template/vite-react/lessonkit.json +6 -1
- package/template/vite-react/package.json +6 -6
- package/template/vite-react/dist/assets/index-CP3MNJ8s.css +0 -1
- package/template/vite-react/dist/assets/index-Dg5lKLi0.js +0 -8
- package/template/vite-react/dist/index.html +0 -14
package/README.md
CHANGED
|
@@ -1,55 +1,41 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lessonkit/cli
|
|
2
2
|
|
|
3
|
-
[](https://github.com/eddiethedean/lessonkit/actions/workflows/ci.yml)
|
|
4
|
-
[](https://lessonkit.readthedocs.io/en/latest/)
|
|
5
3
|
[](https://www.npmjs.com/package/@lessonkit/cli)
|
|
4
|
+
[](https://lessonkit.readthedocs.io/en/latest/reference/cli.html)
|
|
6
5
|
[](https://github.com/eddiethedean/lessonkit/blob/main/LICENSE)
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
**Docs:** [CLI reference](https://lessonkit.readthedocs.io/en/latest/reference/cli.html) · [Packaging & CLI guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/packaging-and-cli.html) · [Vibe coding: shipping to LMS](https://lessonkit.readthedocs.io/en/latest/guides/vibe-coding/shipping-to-lms.html)
|
|
7
|
+
Scaffold, develop, build, and package LessonKit courses. Node.js **18+**.
|
|
11
8
|
|
|
12
9
|
## Install
|
|
13
10
|
|
|
14
11
|
```bash
|
|
15
12
|
npm install -g @lessonkit/cli
|
|
16
|
-
# or
|
|
13
|
+
# or one-shot:
|
|
17
14
|
npx @lessonkit/cli init my-course
|
|
18
15
|
```
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## Quick start
|
|
17
|
+
## Commands
|
|
23
18
|
|
|
24
19
|
```bash
|
|
25
|
-
lessonkit init my-course
|
|
26
|
-
|
|
27
|
-
lessonkit
|
|
28
|
-
lessonkit
|
|
29
|
-
lessonkit package --target scorm12
|
|
20
|
+
lessonkit init my-course # scaffold Vite + React project
|
|
21
|
+
lessonkit dev # Vite dev server
|
|
22
|
+
lessonkit build # production build → dist/
|
|
23
|
+
lessonkit package --target scorm12 # LMS artifact
|
|
30
24
|
```
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `lessonkit build` | Production Vite build |
|
|
39
|
-
| `lessonkit package --target <target>` | Build or package for web / LMS |
|
|
40
|
-
| `lessonkit publish` | Stub — see [`RELEASING.md`](https://github.com/eddiethedean/lessonkit/blob/main/RELEASING.md) |
|
|
41
|
-
|
|
42
|
-
### Package targets
|
|
26
|
+
| Target | Output |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| `react-vite` | Vite build only |
|
|
29
|
+
| `scorm12`, `scorm2004` | SCORM package |
|
|
30
|
+
| `standalone` | Self-contained web bundle |
|
|
31
|
+
| `xapi`, `cmi5` | xAPI / cmi5 packages |
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
- `scorm12`, `scorm2004`, `xapi`, `cmi5`, `standalone` — via `@lessonkit/lxpack`
|
|
33
|
+
Every project includes a root `lessonkit.json` manifest (`schemaVersion: 1`).
|
|
46
34
|
|
|
47
|
-
##
|
|
35
|
+
## Docs
|
|
48
36
|
|
|
49
|
-
|
|
37
|
+
[CLI reference](https://lessonkit.readthedocs.io/en/latest/reference/cli.html) · [Packaging guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/packaging-and-cli.html) · [Template source](https://github.com/eddiethedean/lessonkit/tree/main/templates/vite-react)
|
|
50
38
|
|
|
51
|
-
##
|
|
39
|
+
## License
|
|
52
40
|
|
|
53
|
-
-
|
|
54
|
-
- [React quickstart](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/quickstart.html)
|
|
55
|
-
- [`templates/vite-react`](https://github.com/eddiethedean/lessonkit/tree/main/templates/vite-react) — starter template
|
|
41
|
+
Apache-2.0
|
package/dist/bin.js
CHANGED
|
@@ -158,6 +158,12 @@ async function runInit(opts, logger) {
|
|
|
158
158
|
exitCode: EXIT_INVALID_PROJECT
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
|
+
if (opts.force && !opts.here) {
|
|
162
|
+
throw new CliError("--force requires --here (initialize in the current directory).", {
|
|
163
|
+
code: "INVALID_PROJECT",
|
|
164
|
+
exitCode: EXIT_INVALID_PROJECT
|
|
165
|
+
});
|
|
166
|
+
}
|
|
161
167
|
const slug = slugifyId(rawName ?? "my-course");
|
|
162
168
|
const projectName = rawName ?? slug;
|
|
163
169
|
const projectDir = opts.here ? cwd : resolve(cwd, slug);
|
|
@@ -209,18 +215,13 @@ async function runInit(opts, logger) {
|
|
|
209
215
|
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
210
216
|
import { readFile as readFile2 } from "fs/promises";
|
|
211
217
|
import { dirname as dirname2, join as join2, parse, resolve as resolve2 } from "path";
|
|
212
|
-
import {
|
|
218
|
+
import { parseLessonkitManifest } from "@lessonkit/lxpack";
|
|
213
219
|
var LESSONKIT_JSON = "lessonkit.json";
|
|
214
220
|
var PACKAGE_JSON = "package.json";
|
|
215
|
-
var DEFAULT_PATHS = {
|
|
216
|
-
spaDistDir: "dist",
|
|
217
|
-
lxpackOutDir: ".lxpack/course",
|
|
218
|
-
outputBaseDir: ".lxpack/out"
|
|
219
|
-
};
|
|
220
221
|
function isProjectManifest(configPath) {
|
|
221
222
|
try {
|
|
222
223
|
const raw = JSON.parse(readFileSync(configPath, "utf8"));
|
|
223
|
-
return raw.schemaVersion === 1 && typeof raw.name === "string" && raw.course !== null && typeof raw.course === "object";
|
|
224
|
+
return raw.schemaVersion === 1 && typeof raw.name === "string" && raw.course !== null && typeof raw.course === "object" && !Array.isArray(raw.course);
|
|
224
225
|
} catch {
|
|
225
226
|
return false;
|
|
226
227
|
}
|
|
@@ -253,124 +254,63 @@ async function loadLessonkitJson(projectRoot) {
|
|
|
253
254
|
exitCode: EXIT_INVALID_PROJECT
|
|
254
255
|
});
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
exitCode: EXIT_INVALID_PROJECT
|
|
260
|
-
});
|
|
257
|
+
const parsed = parseLessonkitManifest(raw, configPath, projectRoot);
|
|
258
|
+
if (!parsed.ok) {
|
|
259
|
+
throwManifestCliError(configPath, parsed.issues);
|
|
261
260
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
exitCode: EXIT_INVALID_PROJECT
|
|
275
|
-
});
|
|
261
|
+
return {
|
|
262
|
+
...parsed.manifest,
|
|
263
|
+
root: projectRoot
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function throwManifestCliError(configPath, issues) {
|
|
267
|
+
const layoutIssue = issues.find((i) => i.path === "course.layout");
|
|
268
|
+
if (layoutIssue?.message.includes("per-lesson-spa")) {
|
|
269
|
+
throw new CliError(
|
|
270
|
+
`${configPath}: per-lesson-spa layout is not supported by lessonkit package yet. Use single-spa or package via @lessonkit/lxpack directly.`,
|
|
271
|
+
{ code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
|
|
272
|
+
);
|
|
276
273
|
}
|
|
277
|
-
const
|
|
278
|
-
if (
|
|
279
|
-
throw new CliError(`${configPath}: "course" must be an
|
|
274
|
+
const lessonsIssue = issues.find((i) => i.path === "course.lessons");
|
|
275
|
+
if (lessonsIssue) {
|
|
276
|
+
throw new CliError(`${configPath}: "course.lessons" must be an array.`, {
|
|
280
277
|
code: "INVALID_PROJECT",
|
|
281
278
|
exitCode: EXIT_INVALID_PROJECT
|
|
282
279
|
});
|
|
283
280
|
}
|
|
284
|
-
const
|
|
285
|
-
if (
|
|
286
|
-
throw new CliError(`${configPath}: "
|
|
281
|
+
const spaDistTypeIssue = issues.find((i) => i.path === "paths.spaDistDir");
|
|
282
|
+
if (spaDistTypeIssue && spaDistTypeIssue.message.includes("non-empty string")) {
|
|
283
|
+
throw new CliError(`${configPath}: "paths.spaDistDir" must be a non-empty string.`, {
|
|
287
284
|
code: "INVALID_PROJECT",
|
|
288
285
|
exitCode: EXIT_INVALID_PROJECT
|
|
289
286
|
});
|
|
290
287
|
}
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
const courseSpaIssue = issues.find((i) => i.path === "course.spaDistDir");
|
|
289
|
+
if (courseSpaIssue) {
|
|
290
|
+
throw new CliError(`${configPath}: ${courseSpaIssue.message}`, {
|
|
293
291
|
code: "INVALID_PROJECT",
|
|
294
292
|
exitCode: EXIT_INVALID_PROJECT
|
|
295
293
|
});
|
|
296
294
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
throw new CliError(`${configPath}: invalid course descriptor.`, {
|
|
295
|
+
if (issues.some((i) => i.path.startsWith("paths."))) {
|
|
296
|
+
throw new CliError(`${configPath}: invalid paths.`, {
|
|
300
297
|
code: "INVALID_PROJECT",
|
|
301
298
|
exitCode: EXIT_INVALID_PROJECT,
|
|
302
|
-
issues:
|
|
303
|
-
path: i.path,
|
|
304
|
-
message: i.message
|
|
305
|
-
}))
|
|
299
|
+
issues: issues.map((i) => ({ path: i.path, message: i.message }))
|
|
306
300
|
});
|
|
307
301
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{ code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
const pathsRaw = config.paths;
|
|
315
|
-
const paths = { ...DEFAULT_PATHS };
|
|
316
|
-
if (pathsRaw !== void 0 && (typeof pathsRaw !== "object" || pathsRaw === null)) {
|
|
317
|
-
throw new CliError(`${configPath}: "paths" must be an object.`, {
|
|
302
|
+
const schemaIssue = issues.find((i) => i.path === "schemaVersion");
|
|
303
|
+
if (schemaIssue) {
|
|
304
|
+
throw new CliError(`${configPath}: schemaVersion must be 1 (got ${schemaIssue.message.replace(/^must be 1 \(got /, "").replace(/\)$/, "")}).`, {
|
|
318
305
|
code: "INVALID_PROJECT",
|
|
319
306
|
exitCode: EXIT_INVALID_PROJECT
|
|
320
307
|
});
|
|
321
308
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
code: "INVALID_PROJECT",
|
|
328
|
-
exitCode: EXIT_INVALID_PROJECT
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
paths.spaDistDir = p.spaDistDir;
|
|
332
|
-
}
|
|
333
|
-
if (p.lxpackOutDir !== void 0) {
|
|
334
|
-
if (typeof p.lxpackOutDir !== "string" || !p.lxpackOutDir.trim()) {
|
|
335
|
-
throw new CliError(`${configPath}: "paths.lxpackOutDir" must be a non-empty string.`, {
|
|
336
|
-
code: "INVALID_PROJECT",
|
|
337
|
-
exitCode: EXIT_INVALID_PROJECT
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
paths.lxpackOutDir = p.lxpackOutDir;
|
|
341
|
-
}
|
|
342
|
-
if (p.outputBaseDir !== void 0) {
|
|
343
|
-
if (typeof p.outputBaseDir !== "string" || !p.outputBaseDir.trim()) {
|
|
344
|
-
throw new CliError(`${configPath}: "paths.outputBaseDir" must be a non-empty string.`, {
|
|
345
|
-
code: "INVALID_PROJECT",
|
|
346
|
-
exitCode: EXIT_INVALID_PROJECT
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
paths.outputBaseDir = p.outputBaseDir;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const courseSpaDistDir = validation.descriptor.spaDistDir?.trim();
|
|
353
|
-
if (courseSpaDistDir && courseSpaDistDir !== paths.spaDistDir) {
|
|
354
|
-
throw new CliError(
|
|
355
|
-
`${configPath}: "course.spaDistDir" (${courseSpaDistDir}) differs from "paths.spaDistDir" (${paths.spaDistDir}). Use paths.spaDistDir for CLI build and package.`,
|
|
356
|
-
{ code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
const pathIssues = validateProjectPaths(projectRoot, paths);
|
|
360
|
-
if (pathIssues.length) {
|
|
361
|
-
throw new CliError(`${configPath}: invalid paths.`, {
|
|
362
|
-
code: "INVALID_PROJECT",
|
|
363
|
-
exitCode: EXIT_INVALID_PROJECT,
|
|
364
|
-
issues: pathIssues
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
return {
|
|
368
|
-
root: projectRoot,
|
|
369
|
-
schemaVersion: 1,
|
|
370
|
-
name,
|
|
371
|
-
course: validation.descriptor,
|
|
372
|
-
paths
|
|
373
|
-
};
|
|
309
|
+
throw new CliError(`${configPath}: invalid lessonkit manifest.`, {
|
|
310
|
+
code: "INVALID_PROJECT",
|
|
311
|
+
exitCode: EXIT_INVALID_PROJECT,
|
|
312
|
+
issues: issues.map((i) => ({ path: i.path, message: i.message }))
|
|
313
|
+
});
|
|
374
314
|
}
|
|
375
315
|
async function loadProject(cwd = process.cwd()) {
|
|
376
316
|
const root = findProjectRoot(cwd);
|
|
@@ -388,7 +328,7 @@ async function readPackageJson(projectRoot) {
|
|
|
388
328
|
}
|
|
389
329
|
}
|
|
390
330
|
function assertViteProject(pkg, projectRoot) {
|
|
391
|
-
const vite = pkg.devDependencies?.vite ?? pkg.dependencies?.vite ?? (existsSync2(join2(projectRoot, "node_modules", ".bin", "vite")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite.cmd")) ? "present" : void 0);
|
|
331
|
+
const vite = pkg.devDependencies?.vite ?? pkg.dependencies?.vite ?? (existsSync2(join2(projectRoot, "node_modules", "vite", "bin", "vite.js")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite.cmd")) ? "present" : void 0);
|
|
392
332
|
if (!vite) {
|
|
393
333
|
throw new CliError(
|
|
394
334
|
`No Vite dependency found in ${join2(projectRoot, PACKAGE_JSON)}. LessonKit projects require Vite.`,
|
|
@@ -396,28 +336,25 @@ function assertViteProject(pkg, projectRoot) {
|
|
|
396
336
|
);
|
|
397
337
|
}
|
|
398
338
|
}
|
|
399
|
-
function
|
|
339
|
+
function resolveViteJs(projectRoot) {
|
|
400
340
|
let dir = resolve2(projectRoot);
|
|
401
341
|
const fsRoot = parse(dir).root;
|
|
402
342
|
while (true) {
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
if (existsSync2(bin)) return bin;
|
|
406
|
-
const binCmd = join2(binDir, "vite.cmd");
|
|
407
|
-
if (existsSync2(binCmd)) return binCmd;
|
|
343
|
+
const viteJs = join2(dir, "node_modules", "vite", "bin", "vite.js");
|
|
344
|
+
if (existsSync2(viteJs)) return viteJs;
|
|
408
345
|
if (dir === fsRoot) break;
|
|
409
346
|
dir = dirname2(dir);
|
|
410
347
|
}
|
|
411
348
|
throw new CliError(
|
|
412
|
-
`Vite
|
|
349
|
+
`Vite not found near ${projectRoot}. Run npm install in the project first.`,
|
|
413
350
|
{ code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
|
|
414
351
|
);
|
|
415
352
|
}
|
|
416
|
-
function
|
|
353
|
+
function assertNode18ForLxpack() {
|
|
417
354
|
const major = Number(process.versions.node.split(".")[0]);
|
|
418
|
-
if (major <
|
|
355
|
+
if (major < 18) {
|
|
419
356
|
throw new CliError(
|
|
420
|
-
`LMS packaging requires Node.js
|
|
357
|
+
`LMS packaging requires Node.js 18+ (current: ${process.versions.node}). See docs/PACKAGING.md.`,
|
|
421
358
|
{ code: "NODE_VERSION", exitCode: EXIT_INVALID_PROJECT }
|
|
422
359
|
);
|
|
423
360
|
}
|
|
@@ -426,6 +363,18 @@ function assertNode20ForLxpack() {
|
|
|
426
363
|
// src/lib/paths.ts
|
|
427
364
|
import { resolve as resolve3 } from "path";
|
|
428
365
|
import { resolveSafePackageOutputOverride } from "@lessonkit/lxpack";
|
|
366
|
+
|
|
367
|
+
// src/lib/targetTypes.ts
|
|
368
|
+
var PACKAGE_TARGETS = [
|
|
369
|
+
"react-vite",
|
|
370
|
+
"scorm12",
|
|
371
|
+
"scorm2004",
|
|
372
|
+
"xapi",
|
|
373
|
+
"cmi5",
|
|
374
|
+
"standalone"
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
// src/lib/paths.ts
|
|
429
378
|
function resolveDistDir(project) {
|
|
430
379
|
return resolve3(project.root, project.paths.spaDistDir);
|
|
431
380
|
}
|
|
@@ -456,14 +405,6 @@ function resolveViteBuildArgs(project) {
|
|
|
456
405
|
}
|
|
457
406
|
return args;
|
|
458
407
|
}
|
|
459
|
-
var PACKAGE_TARGETS = [
|
|
460
|
-
"react-vite",
|
|
461
|
-
"scorm12",
|
|
462
|
-
"scorm2004",
|
|
463
|
-
"xapi",
|
|
464
|
-
"cmi5",
|
|
465
|
-
"standalone"
|
|
466
|
-
];
|
|
467
408
|
function parsePackageTarget(value) {
|
|
468
409
|
if (!value) {
|
|
469
410
|
throw new Error("TARGET_REQUIRED");
|
|
@@ -479,17 +420,19 @@ async function runDev(opts) {
|
|
|
479
420
|
const project = await loadProject(opts.cwd ?? process.cwd());
|
|
480
421
|
const pkg = await readPackageJson(project.root);
|
|
481
422
|
assertViteProject(pkg, project.root);
|
|
482
|
-
const
|
|
483
|
-
await runCommand(
|
|
423
|
+
const viteJs = resolveViteJs(project.root);
|
|
424
|
+
await runCommand(process.execPath, [viteJs, ...opts.viteArgs ?? []], { cwd: project.root });
|
|
484
425
|
return { ok: true, command: "dev", projectRoot: project.root };
|
|
485
426
|
}
|
|
486
427
|
async function runBuild(opts) {
|
|
487
428
|
const project = await loadProject(opts.cwd ?? process.cwd());
|
|
488
429
|
const pkg = await readPackageJson(project.root);
|
|
489
430
|
assertViteProject(pkg, project.root);
|
|
490
|
-
const
|
|
431
|
+
const viteJs = resolveViteJs(project.root);
|
|
491
432
|
const buildArgs = resolveViteBuildArgs(project);
|
|
492
|
-
await runCommand(
|
|
433
|
+
await runCommand(process.execPath, [viteJs, ...buildArgs, ...opts.viteArgs ?? []], {
|
|
434
|
+
cwd: project.root
|
|
435
|
+
});
|
|
493
436
|
return { ok: true, command: "build", projectRoot: project.root };
|
|
494
437
|
}
|
|
495
438
|
|
|
@@ -512,8 +455,17 @@ async function runPackage(opts) {
|
|
|
512
455
|
}
|
|
513
456
|
const project = await loadProject(opts.cwd ?? process.cwd());
|
|
514
457
|
const distDir = resolveDistDir(project);
|
|
458
|
+
if (opts.noBuild && !existsSync3(distDir)) {
|
|
459
|
+
throw new CliError(
|
|
460
|
+
`dist directory not found at ${distDir}. Run lessonkit build before packaging with --no-build.`,
|
|
461
|
+
{
|
|
462
|
+
code: "INVALID_PROJECT",
|
|
463
|
+
exitCode: EXIT_INVALID_PROJECT
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
515
467
|
if (target === "react-vite") {
|
|
516
|
-
if (!opts.noBuild
|
|
468
|
+
if (!opts.noBuild) {
|
|
517
469
|
await runBuild({ cwd: project.root, json: opts.json });
|
|
518
470
|
}
|
|
519
471
|
if (!existsSync3(distDir)) {
|
|
@@ -522,10 +474,10 @@ async function runPackage(opts) {
|
|
|
522
474
|
exitCode: EXIT_INVALID_PROJECT
|
|
523
475
|
});
|
|
524
476
|
}
|
|
525
|
-
return { ok: true, target, projectRoot: project.root, distDir };
|
|
477
|
+
return { ok: true, command: "package", target, projectRoot: project.root, distDir };
|
|
526
478
|
}
|
|
527
|
-
|
|
528
|
-
if (!opts.noBuild
|
|
479
|
+
assertNode18ForLxpack();
|
|
480
|
+
if (!opts.noBuild) {
|
|
529
481
|
await runBuild({ cwd: project.root, json: opts.json });
|
|
530
482
|
}
|
|
531
483
|
if (!existsSync3(distDir)) {
|
|
@@ -555,6 +507,7 @@ async function runPackage(opts) {
|
|
|
555
507
|
}
|
|
556
508
|
return {
|
|
557
509
|
ok: true,
|
|
510
|
+
command: "package",
|
|
558
511
|
target,
|
|
559
512
|
projectRoot: project.root,
|
|
560
513
|
outputPath: result.outputPath,
|
|
@@ -598,7 +551,10 @@ async function handleCommand(fn, logger, json) {
|
|
|
598
551
|
function createProgram(baseLogger = console) {
|
|
599
552
|
const program = new Command();
|
|
600
553
|
program.name("lessonkit").description("LessonKit CLI").version(version);
|
|
601
|
-
program.command("init").description("Initialize a LessonKit project from the Vite + React template").argument("[name]", "Project directory name").option("--here", "Initialize in the current directory").option("--skip-install", "Skip npm install").option(
|
|
554
|
+
program.command("init").description("Initialize a LessonKit project from the Vite + React template").argument("[name]", "Project directory name").option("--here", "Initialize in the current directory").option("--skip-install", "Skip npm install").option(
|
|
555
|
+
"--force",
|
|
556
|
+
"Requires --here: allow init when the directory is empty or contains only dotfiles"
|
|
557
|
+
).option("--json", "Emit structured JSON result").action(async (name, opts) => {
|
|
602
558
|
const logger = createLogger({ json: opts.json });
|
|
603
559
|
await handleCommand(
|
|
604
560
|
() => runInit({ name, here: opts.here, skipInstall: opts.skipInstall, force: opts.force, json: opts.json }, logger),
|
|
@@ -618,7 +574,9 @@ function createProgram(baseLogger = console) {
|
|
|
618
574
|
Boolean(opts.json)
|
|
619
575
|
);
|
|
620
576
|
});
|
|
621
|
-
addCwdAndJson(
|
|
577
|
+
addCwdAndJson(
|
|
578
|
+
program.command("build").description("Production Vite build").allowUnknownOption().allowExcessArguments()
|
|
579
|
+
).action(
|
|
622
580
|
async (opts, command) => {
|
|
623
581
|
const logger = createLogger({ json: opts.json });
|
|
624
582
|
const viteArgs = command.args;
|
|
@@ -632,13 +590,26 @@ function createProgram(baseLogger = console) {
|
|
|
632
590
|
program.command("package").description("Build or package for web / LMS delivery").requiredOption("--target <target>", `Export target (${PACKAGE_TARGETS.join(", ")})`).option("--cwd <dir>", "Project root directory").option("--no-build", "Skip implicit Vite build for LMS targets").option("--out <path>", "Override output artifact path").option("--json", "Emit structured JSON result").action(async (opts) => {
|
|
633
591
|
const logger = createLogger({ json: opts.json });
|
|
634
592
|
await handleCommand(
|
|
635
|
-
() =>
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
593
|
+
async () => {
|
|
594
|
+
const result = await runPackage({
|
|
595
|
+
target: opts.target,
|
|
596
|
+
cwd: opts.cwd,
|
|
597
|
+
noBuild: opts.build === false,
|
|
598
|
+
out: opts.out,
|
|
599
|
+
json: opts.json
|
|
600
|
+
});
|
|
601
|
+
if (!opts.json && result.ok) {
|
|
602
|
+
if (result.command === "package" && result.target === "react-vite") {
|
|
603
|
+
logger.log(`Built react-vite \u2192 ${result.distDir}`);
|
|
604
|
+
} else if (result.command === "package") {
|
|
605
|
+
const dest = result.outputPath ?? result.outputDir;
|
|
606
|
+
logger.log(
|
|
607
|
+
`Packaged ${result.target}${dest ? ` \u2192 ${dest}` : ""} (${result.fileCount} files)`
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return result;
|
|
612
|
+
},
|
|
642
613
|
logger,
|
|
643
614
|
Boolean(opts.json)
|
|
644
615
|
);
|