mr-md 2.1.2-beta → 2.2.1-beta
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/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +22 -24
- package/dist/cli/dev.d.ts +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +76 -61
- package/dist/cli.js +0 -0
- package/dist/renderer/html.js +1 -1
- package/dist/renderer/utils.d.ts.map +1 -1
- package/dist/renderer/utils.js +4 -2
- package/package.json +2 -2
- package/src/builder.ts +24 -25
- package/src/cli/dev.ts +80 -61
- package/src/renderer/html.ts +1 -1
- package/src/renderer/utils.ts +4 -2
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEX,gBAAgB,EAEhB,YAAY,EACZ,YAAY,EACZ,OAAO,EAGP,UAAU,EAEV,cAAc,EAId,YAAY,EACZ,MAAM,EACN,UAAU,EAGV,YAAY,EACZ,SAAS,EAIT,iBAAiB,EAEjB,cAAc,EACd,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/builder.js
CHANGED
|
@@ -5,31 +5,29 @@ import { fileURLToPath } from "url";
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
function getCallerDir() {
|
|
8
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
9
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
8
10
|
const err = new Error();
|
|
9
|
-
const stack = err.stack
|
|
10
|
-
|
|
11
|
+
const stack = err.stack;
|
|
12
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
13
|
+
if (!stack || !Array.isArray(stack))
|
|
11
14
|
return undefined;
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
if (p === builderFilePath) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
return path.dirname(p);
|
|
15
|
+
for (let i = 0; i < stack.length; i++) {
|
|
16
|
+
const callSite = stack[i];
|
|
17
|
+
let p = callSite.getFileName();
|
|
18
|
+
if (!p)
|
|
19
|
+
continue;
|
|
20
|
+
if (p.startsWith("file://")) {
|
|
21
|
+
p = fileURLToPath(p);
|
|
22
|
+
}
|
|
23
|
+
else if (p.startsWith("/") && p[2] === ":") {
|
|
24
|
+
p = p.substring(1);
|
|
25
|
+
}
|
|
26
|
+
const basename = path.basename(p);
|
|
27
|
+
if (basename === "builder.ts" || basename === "builder.js" || basename === "index.ts" || basename === "index.js") {
|
|
28
|
+
continue;
|
|
32
29
|
}
|
|
30
|
+
return path.dirname(p);
|
|
33
31
|
}
|
|
34
32
|
return undefined;
|
|
35
33
|
}
|
|
@@ -417,7 +415,7 @@ export class LessonBuilder {
|
|
|
417
415
|
const lesson = { meta: this.meta, blocks: this.blocks };
|
|
418
416
|
const html = render(lesson, this.options);
|
|
419
417
|
const outDir = path.resolve(this.options.outDir);
|
|
420
|
-
const outPath = path.join(outDir,
|
|
418
|
+
const outPath = path.join(outDir, "index.html");
|
|
421
419
|
const outPathDir = path.dirname(outPath);
|
|
422
420
|
if (!fs.existsSync(outPathDir))
|
|
423
421
|
fs.mkdirSync(outPathDir, { recursive: true });
|
|
@@ -596,7 +594,7 @@ export class ChapterBuilder {
|
|
|
596
594
|
const chapterData = { meta: this.meta, lessons };
|
|
597
595
|
const html = renderChapter(chapterData, this.options);
|
|
598
596
|
const outDir = path.resolve(this.options.outDir);
|
|
599
|
-
const outPath = path.join(outDir,
|
|
597
|
+
const outPath = path.join(outDir, "index.html");
|
|
600
598
|
const outPathDir = path.dirname(outPath);
|
|
601
599
|
if (!fs.existsSync(outPathDir))
|
|
602
600
|
fs.mkdirSync(outPathDir, { recursive: true });
|
package/dist/cli/dev.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function runDev(args: string[]):
|
|
1
|
+
export declare function runDev(args: string[]): void;
|
|
2
2
|
//# sourceMappingURL=dev.d.ts.map
|
package/dist/cli/dev.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAKA,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QA4HpC"}
|
package/dist/cli/dev.js
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
|
|
4
|
-
export async function runDev(args) {
|
|
3
|
+
export function runDev(args) {
|
|
5
4
|
const dir = args[0] || ".";
|
|
6
|
-
|
|
5
|
+
let outDir = path.resolve(process.cwd(), dir, "out");
|
|
7
6
|
console.log(`Starting dev server for directory: ${dir}`);
|
|
8
7
|
let server;
|
|
8
|
+
let currentBuild = null;
|
|
9
9
|
const rebuild = () => {
|
|
10
|
+
if (currentBuild) {
|
|
11
|
+
currentBuild.kill();
|
|
12
|
+
}
|
|
10
13
|
console.log("Rebuilding...");
|
|
11
|
-
const entryPoints = ["chapter.ts", "index.ts", "lesson.ts"];
|
|
14
|
+
const entryPoints = ["chapter.ts", "index.ts", "lesson.ts", "chapters/01-chapter/chapter.ts"];
|
|
12
15
|
for (const entry of entryPoints) {
|
|
13
16
|
const entryPath = path.join(dir, entry);
|
|
14
17
|
if (fs.existsSync(entryPath)) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
outDir = path.resolve(process.cwd(), path.dirname(entryPath), "out");
|
|
19
|
+
currentBuild = Bun.spawn([process.execPath, entryPath], {
|
|
20
|
+
env: { ...process.env, NODE_ENV: "development" },
|
|
21
|
+
onExit(proc, exitCode, signalCode, error) {
|
|
22
|
+
if (exitCode === 0) {
|
|
23
|
+
console.log("Build successful.");
|
|
24
|
+
server?.publish("livereload", "reload");
|
|
25
|
+
}
|
|
26
|
+
else if (exitCode !== null) {
|
|
27
|
+
console.error(`Build failed with exit code ${exitCode}`);
|
|
28
|
+
}
|
|
21
29
|
}
|
|
22
30
|
});
|
|
23
31
|
return;
|
|
@@ -39,62 +47,69 @@ export async function runDev(args) {
|
|
|
39
47
|
});
|
|
40
48
|
console.log(`Watching ${dir} for changes...`);
|
|
41
49
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const basePort = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
|
51
|
+
let port = basePort;
|
|
52
|
+
const maxPort = basePort + 10;
|
|
53
|
+
const fetchHandler = async (req, srv) => {
|
|
54
|
+
if (srv.upgrade(req))
|
|
55
|
+
return;
|
|
56
|
+
const url = new URL(req.url);
|
|
57
|
+
const decodedPath = decodeURIComponent(url.pathname);
|
|
58
|
+
let filePath = path.resolve(outDir, "." + decodedPath);
|
|
59
|
+
if (decodedPath.endsWith("/")) {
|
|
60
|
+
filePath = path.join(outDir, "index.html");
|
|
61
|
+
}
|
|
62
|
+
if (!filePath.startsWith(outDir + path.sep) && filePath !== outDir) {
|
|
63
|
+
return new Response("Forbidden", { status: 403 });
|
|
64
|
+
}
|
|
65
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
66
|
+
if (filePath.endsWith(".html")) {
|
|
67
|
+
const file = Bun.file(filePath);
|
|
68
|
+
let text = await file.text();
|
|
69
|
+
const script = `<script>
|
|
70
|
+
const ws = new WebSocket(\`ws://\${location.host}/\`);
|
|
71
|
+
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
72
|
+
</script>`;
|
|
73
|
+
const bodyRegex = /<\/body>/i;
|
|
74
|
+
const match = text.match(bodyRegex);
|
|
75
|
+
if (match && match.index !== undefined) {
|
|
76
|
+
text = text.slice(0, match.index) + script + text.slice(match.index);
|
|
55
77
|
}
|
|
56
78
|
else {
|
|
57
|
-
|
|
58
|
-
if (chapterFile) {
|
|
59
|
-
filePath = path.join(outDir, chapterFile);
|
|
60
|
-
}
|
|
61
|
-
else if (htmlFiles.length > 0) {
|
|
62
|
-
filePath = path.join(outDir, htmlFiles[0]);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
filePath = path.join(outDir, "index.html");
|
|
66
|
-
}
|
|
79
|
+
text += script;
|
|
67
80
|
}
|
|
81
|
+
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
68
82
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
return new Response(Bun.file(filePath));
|
|
84
|
+
}
|
|
85
|
+
return new Response("Not found", { status: 404 });
|
|
86
|
+
};
|
|
87
|
+
const wsHandler = {
|
|
88
|
+
message() { },
|
|
89
|
+
open(ws) { ws.subscribe("livereload"); }
|
|
90
|
+
};
|
|
91
|
+
while (port <= maxPort) {
|
|
92
|
+
try {
|
|
93
|
+
server = Bun.serve({
|
|
94
|
+
port,
|
|
95
|
+
fetch: fetchHandler,
|
|
96
|
+
websocket: wsHandler
|
|
97
|
+
});
|
|
98
|
+
console.log(`Dev server listening on http://localhost:${server.port}`);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (err.code === 'EADDRINUSE') {
|
|
103
|
+
console.warn(`Port ${port} is in use, trying ${port + 1}...`);
|
|
104
|
+
port++;
|
|
86
105
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (srcPath.startsWith(baseDir + path.sep) && fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
|
|
90
|
-
return new Response(Bun.file(srcPath));
|
|
106
|
+
else {
|
|
107
|
+
throw err;
|
|
91
108
|
}
|
|
92
|
-
return new Response("Not found", { status: 404 });
|
|
93
|
-
},
|
|
94
|
-
websocket: {
|
|
95
|
-
message() { },
|
|
96
|
-
open(ws) { ws.subscribe("livereload"); }
|
|
97
109
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
110
|
+
}
|
|
111
|
+
if (!server) {
|
|
112
|
+
console.error(`Could not find an open port between ${basePort} and ${maxPort}.`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
100
115
|
}
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/renderer/html.js
CHANGED
|
@@ -62,7 +62,7 @@ export function renderLayout(title, description, navHtml, contentHtml, opts, ext
|
|
|
62
62
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
63
63
|
<title>${escHtml(title)}</title>
|
|
64
64
|
${description ? `<meta name="description" content="${escHtml(description)}">` : ""}
|
|
65
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.
|
|
65
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/katex.min.css">
|
|
66
66
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,650;9..144,760&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&family=Archivo:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Syne:wght@600;700;800&family=Playfair+Display:ital,wght@0,400..700;1,400..700&family=Lora:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
|
|
67
67
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/github-dark.min.css">
|
|
68
68
|
${opts.head ?? ""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/renderer/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;CACpD;AAID,iBAAS,cAAc,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,EACrB,YAAY,GAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAe,GAClD,MAAM,CA0DR;AAED,iBAAS,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/renderer/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;CACpD;AAID,iBAAS,cAAc,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,EACrB,YAAY,GAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAe,GAClD,MAAM,CA0DR;AAED,iBAAS,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAyDnE;AAED,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/renderer/utils.js
CHANGED
|
@@ -35,7 +35,7 @@ function resolveContent(src, options, expectedType = "text") {
|
|
|
35
35
|
const stat = fs.statSync(filePath);
|
|
36
36
|
if (stat.isFile()) {
|
|
37
37
|
if (expectedType === "js" && (filePath.endsWith(".js") || filePath.endsWith(".ts") || filePath.endsWith(".jsx") || filePath.endsWith(".tsx"))) {
|
|
38
|
-
const out = spawnSync(
|
|
38
|
+
const out = spawnSync(process.execPath, ["build", "--target=browser", filePath]);
|
|
39
39
|
if (out.status === 0) {
|
|
40
40
|
return out.stdout.toString("utf-8");
|
|
41
41
|
}
|
|
@@ -92,7 +92,9 @@ function resolveAssetSrc(src, options) {
|
|
|
92
92
|
fs.mkdirSync(assetsDir, { recursive: true });
|
|
93
93
|
}
|
|
94
94
|
// Create a safe filename with hash to avoid collisions
|
|
95
|
-
|
|
95
|
+
// Use relative path for hashing to ensure deterministic builds across different machines
|
|
96
|
+
const relPathForHash = path.relative(options.contentBase ?? process.cwd(), filePath);
|
|
97
|
+
const hash = crypto.createHash("md5").update(relPathForHash).digest("hex").substring(0, 8);
|
|
96
98
|
const ext = path.extname(filePath);
|
|
97
99
|
const filename = `${path.basename(filePath, ext)}-${hash}${ext}`;
|
|
98
100
|
const outPath = path.join(assetsDir, filename);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mr-md",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1-beta",
|
|
4
4
|
"description": "Mr Markdown is an opinionated TypeScript SDK for building interactive learning pages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"src"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "tsc -p tsconfig.build.json &&
|
|
23
|
+
"build": "tsc -p tsconfig.build.json && bun -e \"import {mkdirSync, cpSync} from 'fs'; mkdirSync('dist/styles', {recursive:true}); mkdirSync('dist/client', {recursive:true}); cpSync('src/styles', 'dist/styles', {recursive:true}); cpSync('src/client', 'dist/client', {recursive:true})\"",
|
|
24
24
|
"prepublishOnly": "bun run build",
|
|
25
25
|
"build:docs": "bun docs-site/build.ts",
|
|
26
26
|
"serve:docs": "bunx serve docs-site/out",
|
package/src/builder.ts
CHANGED
|
@@ -36,31 +36,30 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
36
36
|
const __dirname = path.dirname(__filename);
|
|
37
37
|
|
|
38
38
|
function getCallerDir(): string | undefined {
|
|
39
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
40
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
39
41
|
const err = new Error();
|
|
40
|
-
const stack = err.stack
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (let i =
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (p === builderFilePath) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
return path.dirname(p);
|
|
42
|
+
const stack = err.stack as any as NodeJS.CallSite[];
|
|
43
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
44
|
+
|
|
45
|
+
if (!stack || !Array.isArray(stack)) return undefined;
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < stack.length; i++) {
|
|
48
|
+
const callSite = stack[i];
|
|
49
|
+
let p = callSite.getFileName();
|
|
50
|
+
if (!p) continue;
|
|
51
|
+
|
|
52
|
+
if (p.startsWith("file://")) {
|
|
53
|
+
p = fileURLToPath(p);
|
|
54
|
+
} else if (p.startsWith("/") && p[2] === ":") {
|
|
55
|
+
p = p.substring(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const basename = path.basename(p);
|
|
59
|
+
if (basename === "builder.ts" || basename === "builder.js" || basename === "index.ts" || basename === "index.js") {
|
|
60
|
+
continue;
|
|
63
61
|
}
|
|
62
|
+
return path.dirname(p);
|
|
64
63
|
}
|
|
65
64
|
return undefined;
|
|
66
65
|
}
|
|
@@ -531,7 +530,7 @@ export class LessonBuilder {
|
|
|
531
530
|
const html = render(lesson, this.options);
|
|
532
531
|
|
|
533
532
|
const outDir = path.resolve(this.options.outDir as string);
|
|
534
|
-
const outPath = path.join(outDir,
|
|
533
|
+
const outPath = path.join(outDir, "index.html");
|
|
535
534
|
const outPathDir = path.dirname(outPath);
|
|
536
535
|
|
|
537
536
|
if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
|
|
@@ -758,7 +757,7 @@ export class ChapterBuilder {
|
|
|
758
757
|
const html = renderChapter(chapterData, this.options);
|
|
759
758
|
|
|
760
759
|
const outDir = path.resolve(this.options.outDir as string);
|
|
761
|
-
const outPath = path.join(outDir,
|
|
760
|
+
const outPath = path.join(outDir, "index.html");
|
|
762
761
|
const outPathDir = path.dirname(outPath);
|
|
763
762
|
|
|
764
763
|
if (!fs.existsSync(outPathDir)) fs.mkdirSync(outPathDir, { recursive: true });
|
package/src/cli/dev.ts
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { exec } from "child_process";
|
|
4
3
|
|
|
5
4
|
declare const Bun: any;
|
|
6
5
|
|
|
7
|
-
export
|
|
6
|
+
export function runDev(args: string[]) {
|
|
8
7
|
const dir = args[0] || ".";
|
|
9
|
-
|
|
8
|
+
let outDir = path.resolve(process.cwd(), dir, "out");
|
|
10
9
|
|
|
11
10
|
console.log(`Starting dev server for directory: ${dir}`);
|
|
12
11
|
|
|
13
12
|
let server: any;
|
|
13
|
+
let currentBuild: any = null;
|
|
14
14
|
|
|
15
15
|
const rebuild = () => {
|
|
16
|
+
if (currentBuild) {
|
|
17
|
+
currentBuild.kill();
|
|
18
|
+
}
|
|
16
19
|
console.log("Rebuilding...");
|
|
17
|
-
const entryPoints = ["chapter.ts", "index.ts", "lesson.ts"];
|
|
20
|
+
const entryPoints = ["chapter.ts", "index.ts", "lesson.ts", "chapters/01-chapter/chapter.ts"];
|
|
18
21
|
for (const entry of entryPoints) {
|
|
19
22
|
const entryPath = path.join(dir, entry);
|
|
20
23
|
if (fs.existsSync(entryPath)) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
outDir = path.resolve(process.cwd(), path.dirname(entryPath), "out");
|
|
25
|
+
currentBuild = Bun.spawn([process.execPath, entryPath], {
|
|
26
|
+
env: { ...process.env, NODE_ENV: "development" },
|
|
27
|
+
onExit(proc: any, exitCode: number, signalCode: number, error: string) {
|
|
28
|
+
if (exitCode === 0) {
|
|
29
|
+
console.log("Build successful.");
|
|
30
|
+
server?.publish("livereload", "reload");
|
|
31
|
+
} else if (exitCode !== null) {
|
|
32
|
+
console.error(`Build failed with exit code ${exitCode}`);
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
});
|
|
28
36
|
return;
|
|
@@ -47,65 +55,76 @@ export async function runDev(args: string[]) {
|
|
|
47
55
|
console.log(`Watching ${dir} for changes...`);
|
|
48
56
|
}
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
const basePort = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
|
59
|
+
let port = basePort;
|
|
60
|
+
const maxPort = basePort + 10;
|
|
61
|
+
|
|
62
|
+
const fetchHandler = async (req: any, srv: any) => {
|
|
63
|
+
if (srv.upgrade(req)) return;
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
const url = new URL(req.url);
|
|
66
|
+
const decodedPath = decodeURIComponent(url.pathname);
|
|
67
|
+
let filePath = path.resolve(outDir, "." + decodedPath);
|
|
68
|
+
|
|
69
|
+
if (decodedPath.endsWith("/")) {
|
|
70
|
+
filePath = path.join(outDir, "index.html");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!filePath.startsWith(outDir + path.sep) && filePath !== outDir) {
|
|
74
|
+
return new Response("Forbidden", { status: 403 });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
78
|
+
if (filePath.endsWith(".html")) {
|
|
79
|
+
const file = Bun.file(filePath);
|
|
80
|
+
let text = await file.text();
|
|
81
|
+
|
|
82
|
+
const script = `<script>
|
|
83
|
+
const ws = new WebSocket(\`ws://\${location.host}/\`);
|
|
84
|
+
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
85
|
+
</script>`;
|
|
86
|
+
|
|
87
|
+
const bodyRegex = /<\/body>/i;
|
|
88
|
+
const match = text.match(bodyRegex);
|
|
89
|
+
if (match && match.index !== undefined) {
|
|
90
|
+
text = text.slice(0, match.index) + script + text.slice(match.index);
|
|
64
91
|
} else {
|
|
65
|
-
|
|
66
|
-
if (chapterFile) {
|
|
67
|
-
filePath = path.join(outDir, chapterFile);
|
|
68
|
-
} else if (htmlFiles.length > 0) {
|
|
69
|
-
filePath = path.join(outDir, htmlFiles[0]);
|
|
70
|
-
} else {
|
|
71
|
-
filePath = path.join(outDir, "index.html");
|
|
72
|
-
}
|
|
92
|
+
text += script;
|
|
73
93
|
}
|
|
94
|
+
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
74
95
|
}
|
|
96
|
+
return new Response(Bun.file(filePath));
|
|
97
|
+
}
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
99
|
+
return new Response("Not found", { status: 404 });
|
|
100
|
+
};
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const lastBodyIndex = text.lastIndexOf("</body>");
|
|
85
|
-
if (lastBodyIndex !== -1) {
|
|
86
|
-
text = text.slice(0, lastBodyIndex) + `<script>
|
|
87
|
-
const ws = new WebSocket(\`ws://\${location.host}/\`);
|
|
88
|
-
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
89
|
-
</script></body>` + text.slice(lastBodyIndex + 7);
|
|
90
|
-
}
|
|
91
|
-
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
92
|
-
}
|
|
93
|
-
return new Response(Bun.file(filePath));
|
|
94
|
-
}
|
|
102
|
+
const wsHandler = {
|
|
103
|
+
message() {},
|
|
104
|
+
open(ws: any) { ws.subscribe("livereload"); }
|
|
105
|
+
};
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
while (port <= maxPort) {
|
|
108
|
+
try {
|
|
109
|
+
server = Bun.serve({
|
|
110
|
+
port,
|
|
111
|
+
fetch: fetchHandler,
|
|
112
|
+
websocket: wsHandler
|
|
113
|
+
});
|
|
114
|
+
console.log(`Dev server listening on http://localhost:${server.port}`);
|
|
115
|
+
break;
|
|
116
|
+
} catch (err: any) {
|
|
117
|
+
if (err.code === 'EADDRINUSE') {
|
|
118
|
+
console.warn(`Port ${port} is in use, trying ${port + 1}...`);
|
|
119
|
+
port++;
|
|
120
|
+
} else {
|
|
121
|
+
throw err;
|
|
100
122
|
}
|
|
101
|
-
|
|
102
|
-
return new Response("Not found", { status: 404 });
|
|
103
|
-
},
|
|
104
|
-
websocket: {
|
|
105
|
-
message() {},
|
|
106
|
-
open(ws: any) { ws.subscribe("livereload"); }
|
|
107
123
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!server) {
|
|
127
|
+
console.error(`Could not find an open port between ${basePort} and ${maxPort}.`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
111
130
|
}
|
package/src/renderer/html.ts
CHANGED
|
@@ -78,7 +78,7 @@ export function renderLayout(
|
|
|
78
78
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
79
79
|
<title>${escHtml(title)}</title>
|
|
80
80
|
${description ? `<meta name="description" content="${escHtml(description)}">` : ""}
|
|
81
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.
|
|
81
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/katex.min.css">
|
|
82
82
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,650;9..144,760&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&family=Archivo:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Syne:wght@600;700;800&family=Playfair+Display:ital,wght@0,400..700;1,400..700&family=Lora:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
|
|
83
83
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/github-dark.min.css">
|
|
84
84
|
${opts.head ?? ""}
|
package/src/renderer/utils.ts
CHANGED
|
@@ -54,7 +54,7 @@ function resolveContent(
|
|
|
54
54
|
const stat = fs.statSync(filePath);
|
|
55
55
|
if (stat.isFile()) {
|
|
56
56
|
if (expectedType === "js" && (filePath.endsWith(".js") || filePath.endsWith(".ts") || filePath.endsWith(".jsx") || filePath.endsWith(".tsx"))) {
|
|
57
|
-
const out = spawnSync(
|
|
57
|
+
const out = spawnSync(process.execPath, ["build", "--target=browser", filePath]);
|
|
58
58
|
if (out.status === 0) {
|
|
59
59
|
return out.stdout.toString("utf-8");
|
|
60
60
|
} else {
|
|
@@ -121,7 +121,9 @@ function resolveAssetSrc(src: string, options: BuildOptions): string {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// Create a safe filename with hash to avoid collisions
|
|
124
|
-
|
|
124
|
+
// Use relative path for hashing to ensure deterministic builds across different machines
|
|
125
|
+
const relPathForHash = path.relative(options.contentBase ?? process.cwd(), filePath);
|
|
126
|
+
const hash = crypto.createHash("md5").update(relPathForHash).digest("hex").substring(0, 8);
|
|
125
127
|
const ext = path.extname(filePath);
|
|
126
128
|
const filename = `${path.basename(filePath, ext)}-${hash}${ext}`;
|
|
127
129
|
const outPath = path.join(assetsDir, filename);
|