@lessonkit/cli 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/bin.js +82 -26
- package/dist/index.js +82 -26
- package/package.json +4 -4
- package/template/vite-react/package.json +7 -7
- package/template/vite-react/vitest.config.ts +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@ lessonkit package --target scorm12 # LMS artifact
|
|
|
32
32
|
|
|
33
33
|
Every project includes a root `lessonkit.json` manifest (`schemaVersion: 1`).
|
|
34
34
|
|
|
35
|
+
Subprocess timeout defaults to **30 minutes** (`LESSONKIT_CMD_TIMEOUT_MS`). To disable the timeout, pass `timeoutMs: 0` in the exec API (the env var falls back to 30 minutes when set to `0` or omitted).
|
|
36
|
+
|
|
35
37
|
## Docs
|
|
36
38
|
|
|
37
39
|
[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)
|
package/dist/bin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
4
|
+
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
@@ -54,7 +54,19 @@ ${details}`;
|
|
|
54
54
|
|
|
55
55
|
// src/lib/exec.ts
|
|
56
56
|
import { spawn } from "child_process";
|
|
57
|
+
var DEFAULT_CMD_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
58
|
+
function resolveCommandTimeoutMs(explicit) {
|
|
59
|
+
if (explicit !== void 0) {
|
|
60
|
+
return explicit > 0 ? explicit : void 0;
|
|
61
|
+
}
|
|
62
|
+
const raw = process.env.LESSONKIT_CMD_TIMEOUT_MS;
|
|
63
|
+
if (raw === void 0 || raw === "") return DEFAULT_CMD_TIMEOUT_MS;
|
|
64
|
+
const parsed = Number(raw);
|
|
65
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_CMD_TIMEOUT_MS;
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
57
68
|
async function runCommand(command, args, opts) {
|
|
69
|
+
const timeoutMs = resolveCommandTimeoutMs(opts.timeoutMs);
|
|
58
70
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
59
71
|
const child = spawn(command, args, {
|
|
60
72
|
cwd: opts.cwd,
|
|
@@ -62,25 +74,48 @@ async function runCommand(command, args, opts) {
|
|
|
62
74
|
stdio: "inherit",
|
|
63
75
|
shell: false
|
|
64
76
|
});
|
|
77
|
+
let timedOut = false;
|
|
78
|
+
const timer = timeoutMs !== void 0 ? setTimeout(() => {
|
|
79
|
+
timedOut = true;
|
|
80
|
+
child.kill("SIGTERM");
|
|
81
|
+
setTimeout(() => child.kill("SIGKILL"), 5e3).unref?.();
|
|
82
|
+
}, timeoutMs) : void 0;
|
|
83
|
+
const settle = (fn) => {
|
|
84
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
85
|
+
fn();
|
|
86
|
+
};
|
|
65
87
|
child.on("error", (err) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
88
|
+
settle(() => {
|
|
89
|
+
rejectPromise(
|
|
90
|
+
new CliError(`Failed to run ${command}: ${err.message}`, {
|
|
91
|
+
code: "RUNTIME",
|
|
92
|
+
exitCode: EXIT_RUNTIME
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
});
|
|
72
96
|
});
|
|
73
97
|
child.on("close", (code) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
settle(() => {
|
|
99
|
+
if (timedOut) {
|
|
100
|
+
rejectPromise(
|
|
101
|
+
new CliError(`${command} timed out after ${timeoutMs}ms.`, {
|
|
102
|
+
code: "RUNTIME",
|
|
103
|
+
exitCode: EXIT_RUNTIME
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (code === 0) {
|
|
109
|
+
resolvePromise();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
rejectPromise(
|
|
113
|
+
new CliError(`${command} exited with code ${code ?? "unknown"}.`, {
|
|
114
|
+
code: "RUNTIME",
|
|
115
|
+
exitCode: EXIT_RUNTIME
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
});
|
|
84
119
|
});
|
|
85
120
|
});
|
|
86
121
|
}
|
|
@@ -215,6 +250,7 @@ async function runInit(opts, logger) {
|
|
|
215
250
|
// src/lib/project.ts
|
|
216
251
|
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
217
252
|
import { readFile as readFile2 } from "fs/promises";
|
|
253
|
+
import { createRequire } from "module";
|
|
218
254
|
import { dirname as dirname2, join as join2, parse, resolve as resolve2 } from "path";
|
|
219
255
|
import { parseLessonkitManifest } from "@lessonkit/lxpack";
|
|
220
256
|
var LESSONKIT_JSON = "lessonkit.json";
|
|
@@ -337,14 +373,22 @@ function assertViteProject(pkg, projectRoot) {
|
|
|
337
373
|
);
|
|
338
374
|
}
|
|
339
375
|
}
|
|
376
|
+
function projectDeclaresVite(pkg) {
|
|
377
|
+
return !!(pkg.devDependencies?.vite ?? pkg.dependencies?.vite);
|
|
378
|
+
}
|
|
340
379
|
function resolveViteJs(projectRoot) {
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
380
|
+
const root = resolve2(projectRoot);
|
|
381
|
+
const localViteJs = join2(root, "node_modules", "vite", "bin", "vite.js");
|
|
382
|
+
if (existsSync2(localViteJs)) return localViteJs;
|
|
383
|
+
try {
|
|
384
|
+
const pkg = JSON.parse(readFileSync(join2(root, PACKAGE_JSON), "utf8"));
|
|
385
|
+
if (projectDeclaresVite(pkg)) {
|
|
386
|
+
const req = createRequire(join2(root, "package.json"));
|
|
387
|
+
const vitePkg = req.resolve("vite/package.json");
|
|
388
|
+
const hoistedViteJs = join2(dirname2(vitePkg), "bin", "vite.js");
|
|
389
|
+
if (existsSync2(hoistedViteJs)) return hoistedViteJs;
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
348
392
|
}
|
|
349
393
|
throw new CliError(
|
|
350
394
|
`Vite not found near ${projectRoot}. Run npm install in the project first.`,
|
|
@@ -506,6 +550,17 @@ async function runPackage(opts) {
|
|
|
506
550
|
issues: result.issues
|
|
507
551
|
});
|
|
508
552
|
}
|
|
553
|
+
const warnings = result.validation?.issues?.filter((issue) => {
|
|
554
|
+
const severity = issue.severity?.toLowerCase();
|
|
555
|
+
return severity !== "error" && severity !== "fatal";
|
|
556
|
+
});
|
|
557
|
+
if (warnings?.length && !opts.json) {
|
|
558
|
+
for (const issue of warnings) {
|
|
559
|
+
const location = issue.path ?? "course";
|
|
560
|
+
process.stderr.write(`[lessonkit] packaging warning: ${location}: ${issue.message}
|
|
561
|
+
`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
509
564
|
return {
|
|
510
565
|
ok: true,
|
|
511
566
|
command: "package",
|
|
@@ -513,7 +568,8 @@ async function runPackage(opts) {
|
|
|
513
568
|
projectRoot: project.root,
|
|
514
569
|
outputPath: result.outputPath,
|
|
515
570
|
outputDir: result.outputDir,
|
|
516
|
-
fileCount: result.fileCount
|
|
571
|
+
fileCount: result.fileCount,
|
|
572
|
+
...warnings?.length ? { warnings } : {}
|
|
517
573
|
};
|
|
518
574
|
}
|
|
519
575
|
|
|
@@ -531,7 +587,7 @@ function createLogger(opts) {
|
|
|
531
587
|
}
|
|
532
588
|
|
|
533
589
|
// src/index.ts
|
|
534
|
-
var require2 =
|
|
590
|
+
var require2 = createRequire2(import.meta.url);
|
|
535
591
|
var { version } = require2("../package.json");
|
|
536
592
|
async function handleCommand(fn, logger, json) {
|
|
537
593
|
try {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { createRequire } from "module";
|
|
2
|
+
import { createRequire as createRequire2 } from "module";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
|
|
5
5
|
// src/commands/init.ts
|
|
@@ -52,7 +52,19 @@ ${details}`;
|
|
|
52
52
|
|
|
53
53
|
// src/lib/exec.ts
|
|
54
54
|
import { spawn } from "child_process";
|
|
55
|
+
var DEFAULT_CMD_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
56
|
+
function resolveCommandTimeoutMs(explicit) {
|
|
57
|
+
if (explicit !== void 0) {
|
|
58
|
+
return explicit > 0 ? explicit : void 0;
|
|
59
|
+
}
|
|
60
|
+
const raw = process.env.LESSONKIT_CMD_TIMEOUT_MS;
|
|
61
|
+
if (raw === void 0 || raw === "") return DEFAULT_CMD_TIMEOUT_MS;
|
|
62
|
+
const parsed = Number(raw);
|
|
63
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_CMD_TIMEOUT_MS;
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
55
66
|
async function runCommand(command, args, opts) {
|
|
67
|
+
const timeoutMs = resolveCommandTimeoutMs(opts.timeoutMs);
|
|
56
68
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
57
69
|
const child = spawn(command, args, {
|
|
58
70
|
cwd: opts.cwd,
|
|
@@ -60,25 +72,48 @@ async function runCommand(command, args, opts) {
|
|
|
60
72
|
stdio: "inherit",
|
|
61
73
|
shell: false
|
|
62
74
|
});
|
|
75
|
+
let timedOut = false;
|
|
76
|
+
const timer = timeoutMs !== void 0 ? setTimeout(() => {
|
|
77
|
+
timedOut = true;
|
|
78
|
+
child.kill("SIGTERM");
|
|
79
|
+
setTimeout(() => child.kill("SIGKILL"), 5e3).unref?.();
|
|
80
|
+
}, timeoutMs) : void 0;
|
|
81
|
+
const settle = (fn) => {
|
|
82
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
83
|
+
fn();
|
|
84
|
+
};
|
|
63
85
|
child.on("error", (err) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
settle(() => {
|
|
87
|
+
rejectPromise(
|
|
88
|
+
new CliError(`Failed to run ${command}: ${err.message}`, {
|
|
89
|
+
code: "RUNTIME",
|
|
90
|
+
exitCode: EXIT_RUNTIME
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
});
|
|
70
94
|
});
|
|
71
95
|
child.on("close", (code) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
settle(() => {
|
|
97
|
+
if (timedOut) {
|
|
98
|
+
rejectPromise(
|
|
99
|
+
new CliError(`${command} timed out after ${timeoutMs}ms.`, {
|
|
100
|
+
code: "RUNTIME",
|
|
101
|
+
exitCode: EXIT_RUNTIME
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (code === 0) {
|
|
107
|
+
resolvePromise();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
rejectPromise(
|
|
111
|
+
new CliError(`${command} exited with code ${code ?? "unknown"}.`, {
|
|
112
|
+
code: "RUNTIME",
|
|
113
|
+
exitCode: EXIT_RUNTIME
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
});
|
|
82
117
|
});
|
|
83
118
|
});
|
|
84
119
|
}
|
|
@@ -213,6 +248,7 @@ async function runInit(opts, logger) {
|
|
|
213
248
|
// src/lib/project.ts
|
|
214
249
|
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
215
250
|
import { readFile as readFile2 } from "fs/promises";
|
|
251
|
+
import { createRequire } from "module";
|
|
216
252
|
import { dirname as dirname2, join as join2, parse, resolve as resolve2 } from "path";
|
|
217
253
|
import { parseLessonkitManifest } from "@lessonkit/lxpack";
|
|
218
254
|
var LESSONKIT_JSON = "lessonkit.json";
|
|
@@ -335,14 +371,22 @@ function assertViteProject(pkg, projectRoot) {
|
|
|
335
371
|
);
|
|
336
372
|
}
|
|
337
373
|
}
|
|
374
|
+
function projectDeclaresVite(pkg) {
|
|
375
|
+
return !!(pkg.devDependencies?.vite ?? pkg.dependencies?.vite);
|
|
376
|
+
}
|
|
338
377
|
function resolveViteJs(projectRoot) {
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (
|
|
345
|
-
|
|
378
|
+
const root = resolve2(projectRoot);
|
|
379
|
+
const localViteJs = join2(root, "node_modules", "vite", "bin", "vite.js");
|
|
380
|
+
if (existsSync2(localViteJs)) return localViteJs;
|
|
381
|
+
try {
|
|
382
|
+
const pkg = JSON.parse(readFileSync(join2(root, PACKAGE_JSON), "utf8"));
|
|
383
|
+
if (projectDeclaresVite(pkg)) {
|
|
384
|
+
const req = createRequire(join2(root, "package.json"));
|
|
385
|
+
const vitePkg = req.resolve("vite/package.json");
|
|
386
|
+
const hoistedViteJs = join2(dirname2(vitePkg), "bin", "vite.js");
|
|
387
|
+
if (existsSync2(hoistedViteJs)) return hoistedViteJs;
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
346
390
|
}
|
|
347
391
|
throw new CliError(
|
|
348
392
|
`Vite not found near ${projectRoot}. Run npm install in the project first.`,
|
|
@@ -504,6 +548,17 @@ async function runPackage(opts) {
|
|
|
504
548
|
issues: result.issues
|
|
505
549
|
});
|
|
506
550
|
}
|
|
551
|
+
const warnings = result.validation?.issues?.filter((issue) => {
|
|
552
|
+
const severity = issue.severity?.toLowerCase();
|
|
553
|
+
return severity !== "error" && severity !== "fatal";
|
|
554
|
+
});
|
|
555
|
+
if (warnings?.length && !opts.json) {
|
|
556
|
+
for (const issue of warnings) {
|
|
557
|
+
const location = issue.path ?? "course";
|
|
558
|
+
process.stderr.write(`[lessonkit] packaging warning: ${location}: ${issue.message}
|
|
559
|
+
`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
507
562
|
return {
|
|
508
563
|
ok: true,
|
|
509
564
|
command: "package",
|
|
@@ -511,7 +566,8 @@ async function runPackage(opts) {
|
|
|
511
566
|
projectRoot: project.root,
|
|
512
567
|
outputPath: result.outputPath,
|
|
513
568
|
outputDir: result.outputDir,
|
|
514
|
-
fileCount: result.fileCount
|
|
569
|
+
fileCount: result.fileCount,
|
|
570
|
+
...warnings?.length ? { warnings } : {}
|
|
515
571
|
};
|
|
516
572
|
}
|
|
517
573
|
|
|
@@ -529,7 +585,7 @@ function createLogger(opts) {
|
|
|
529
585
|
}
|
|
530
586
|
|
|
531
587
|
// src/index.ts
|
|
532
|
-
var require2 =
|
|
588
|
+
var require2 = createRequire2(import.meta.url);
|
|
533
589
|
var { version } = require2("../package.json");
|
|
534
590
|
async function handleCommand(fn, logger, json) {
|
|
535
591
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "LessonKit CLI — init, dev, build, and package learning experiences.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/eddiethedean/lessonkit.git",
|
|
10
10
|
"directory": "packages/cli"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://
|
|
12
|
+
"homepage": "https://lessonkit.readthedocs.io/en/latest/reference/cli.html",
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/eddiethedean/lessonkit/issues"
|
|
15
15
|
},
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"lint": "echo \"(no lint configured yet)\""
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@lessonkit/core": "1.
|
|
46
|
-
"@lessonkit/lxpack": "1.
|
|
45
|
+
"@lessonkit/core": "1.3.0",
|
|
46
|
+
"@lessonkit/lxpack": "1.3.0",
|
|
47
47
|
"commander": "^14.0.1"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"test:coverage": "vitest run --coverage --passWithNoTests=false"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@lessonkit/core": "^1.
|
|
17
|
-
"@lessonkit/react": "^1.
|
|
18
|
-
"@lessonkit/themes": "^1.
|
|
19
|
-
"@lessonkit/xapi": "^1.
|
|
16
|
+
"@lessonkit/core": "^1.3.0",
|
|
17
|
+
"@lessonkit/react": "^1.3.0",
|
|
18
|
+
"@lessonkit/themes": "^1.3.0",
|
|
19
|
+
"@lessonkit/xapi": "^1.3.0",
|
|
20
20
|
"react": "^18.3.1",
|
|
21
21
|
"react-dom": "^18.3.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@lessonkit/cli": "^1.
|
|
25
|
-
"@lessonkit/lxpack": "^1.
|
|
24
|
+
"@lessonkit/cli": "^1.3.0",
|
|
25
|
+
"@lessonkit/lxpack": "^1.3.0",
|
|
26
26
|
"@testing-library/react": "^16.3.0",
|
|
27
27
|
"@types/react": "^18.3.23",
|
|
28
28
|
"@types/react-dom": "^18.3.7",
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"jsdom": "^26.1.0",
|
|
31
31
|
"typescript": "^5.8.3",
|
|
32
32
|
"vite": "^7.1.3",
|
|
33
|
-
"vitest": "^
|
|
33
|
+
"vitest": "^4.1.8"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -8,7 +8,7 @@ export default defineConfig({
|
|
|
8
8
|
provider: "v8",
|
|
9
9
|
include: ["src/**/*.{ts,tsx}"],
|
|
10
10
|
exclude: ["dist/**", "node_modules/**", "**/*.d.ts", "**/*.d.cts"],
|
|
11
|
-
thresholds: { statements:
|
|
11
|
+
thresholds: { statements: 85, branches: 85, functions: 85, lines: 85 },
|
|
12
12
|
},
|
|
13
13
|
},
|
|
14
14
|
});
|