mr-md 1.0.3 → 1.1.0-alpha.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 +10 -5
  2. package/dist/builder.d.ts +5 -19
  3. package/dist/builder.d.ts.map +1 -1
  4. package/dist/builder.js +38 -95
  5. package/dist/cli/dev.d.ts +2 -0
  6. package/dist/cli/dev.d.ts.map +1 -0
  7. package/dist/cli/dev.js +92 -0
  8. package/dist/cli/generate.d.ts +2 -0
  9. package/dist/cli/generate.d.ts.map +1 -0
  10. package/dist/cli/generate.js +171 -0
  11. package/dist/cli/init.d.ts +2 -0
  12. package/dist/cli/init.d.ts.map +1 -0
  13. package/dist/cli/init.js +48 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +27 -0
  17. package/dist/client/app.js +154 -99
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/renderer/blocks.d.ts.map +1 -1
  22. package/dist/renderer/blocks.js +55 -16
  23. package/dist/renderer/html.d.ts +2 -3
  24. package/dist/renderer/html.d.ts.map +1 -1
  25. package/dist/renderer/html.js +23 -17
  26. package/dist/renderer/index.d.ts +1 -2
  27. package/dist/renderer/index.d.ts.map +1 -1
  28. package/dist/renderer/index.js +241 -523
  29. package/dist/renderer/markdown.d.ts +1 -1
  30. package/dist/renderer/markdown.d.ts.map +1 -1
  31. package/dist/renderer/markdown.js +3 -3
  32. package/dist/renderer/utils.d.ts +1 -1
  33. package/dist/renderer/utils.d.ts.map +1 -1
  34. package/dist/renderer/utils.js +47 -28
  35. package/dist/styles/theme.css +97 -40
  36. package/dist/types.d.ts +1 -10
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +61 -60
  39. package/src/builder.ts +43 -116
  40. package/src/cli/dev.ts +102 -0
  41. package/src/cli/generate.ts +191 -0
  42. package/src/cli/init.ts +54 -0
  43. package/src/cli.ts +29 -0
  44. package/src/client/app.js +154 -99
  45. package/src/index.ts +1 -1
  46. package/src/renderer/blocks.ts +56 -15
  47. package/src/renderer/html.ts +22 -20
  48. package/src/renderer/index.ts +240 -536
  49. package/src/renderer/markdown.ts +3 -2
  50. package/src/renderer/utils.ts +52 -30
  51. package/src/styles/theme.css +97 -40
  52. package/src/types.ts +1 -12
package/README.md CHANGED
@@ -13,12 +13,17 @@ The documentation has been split into dedicated guides. Start with the Quick Sta
13
13
  5. [Quizzes](./docs/quizzes.md) - Learn the JSON format for injecting interactive quizzes.
14
14
  6. [Production Checks](./docs/production-checks.md) - Understand Mr Markdown's strict mode and quality enforcement.
15
15
 
16
- ## Example
16
+ ## Quick Start (CLI)
17
17
 
18
- To see a full example in action, compile the included `charge.lesson.ts`:
18
+ The easiest way to use Mr Markdown is via the CLI which provides a smart scaffold and dev server.
19
19
 
20
20
  ```bash
21
- npx ts-node example/charge.lesson.ts
22
- # or
23
- bun example/charge.lesson.ts
21
+ mkdir my-course && cd my-course
22
+ bunx mr-md init
23
+ bunx mr-md g ch physics
24
+ cd chapters/01-physics
25
+ bunx mr-md g lesson intro
26
+
27
+ # Start the dev server
28
+ bunx mr-md dev .
24
29
  ```
package/dist/builder.d.ts CHANGED
@@ -46,7 +46,7 @@ export declare class LessonBuilder {
46
46
  add(src: `${string}.png` | `${string}.jpg` | `${string}.jpeg` | `${string}.gif` | `${string}.svg` | `${string}.webp` | `${string}.avif`, opts?: Omit<MediaOptions, "kind">): this;
47
47
  add(src: string, opts?: any): this;
48
48
  /**
49
- * Creates a major chapter heading (H2) and a top-level sidebar navigation entry.
49
+ * Creates a major chapter heading (H1) and a top-level sidebar navigation entry.
50
50
  * @param src Path to a markdown file or raw markdown string.
51
51
  * @param title Optional title override for the sidebar and heading.
52
52
  * @example lesson.heading("# Welcome to Physics")
@@ -64,10 +64,10 @@ export declare class LessonBuilder {
64
64
  */
65
65
  content(src: string): this;
66
66
  /**
67
- * Creates a new subsection heading (H3) and a sub-entry in the sidebar.
67
+ * Creates a new subsection heading (H2) and a sub-entry in the sidebar.
68
68
  * @param src Path to a markdown file or raw markdown string.
69
69
  * @param label Optional title override for the sidebar label.
70
- * @example lesson.section("### 1. Kinematics")
70
+ * @example lesson.section("## 1. Kinematics")
71
71
  */
72
72
  section(src: string, label?: string): this;
73
73
  /**
@@ -141,7 +141,7 @@ export declare class LessonBuilder {
141
141
  * @param options Build configuration for this specific lesson.
142
142
  * @example const l = lesson("Introduction to Kinematics").markdown("intro.md");
143
143
  */
144
- export declare function lesson(title: string, options?: BuildOptions): LessonBuilder;
144
+ export declare function lesson(title: string, optionsOrSetup?: BuildOptions | ((ctx: LessonBuilder) => void), setup?: (ctx: LessonBuilder) => void): LessonBuilder;
145
145
  export declare class ChapterBuilder {
146
146
  private meta;
147
147
  private lessonBuilders;
@@ -149,8 +149,6 @@ export declare class ChapterBuilder {
149
149
  private _rawOptions;
150
150
  constructor(title: string, options?: BuildOptions, callerDir?: string);
151
151
  slug(slug: string): this;
152
- /** @internal Used by CourseBuilder to push down shared config */
153
- _inheritOptions(parentOpts: BuildOptions, parentRawOpts?: BuildOptions): void;
154
152
  description(text: string): this;
155
153
  status(status: "completed" | "active" | "locked"): this;
156
154
  lesson(lessonBuilder: LessonBuilder): this;
@@ -168,17 +166,5 @@ export declare class ChapterBuilder {
168
166
  * .lesson(dynamicsLesson)
169
167
  * .build();
170
168
  */
171
- export declare function chapter(title: string, options?: BuildOptions): ChapterBuilder;
172
- export declare class CourseBuilder {
173
- private meta;
174
- private chapterBuilders;
175
- private options;
176
- private _rawOptions;
177
- constructor(title: string, options?: BuildOptions, callerDir?: string);
178
- slug(slug: string): this;
179
- description(text: string): this;
180
- chapter(chapterBuilder: ChapterBuilder): this;
181
- build(): string;
182
- }
183
- export declare function course(title: string, options?: BuildOptions): CourseBuilder;
169
+ export declare function chapter(title: string, optionsOrSetup?: BuildOptions | ((ctx: ChapterBuilder) => void), setup?: (ctx: ChapterBuilder) => void): ChapterBuilder;
184
170
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
package/dist/builder.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import { render, renderChapter, renderCourse } from "./renderer/index.js";
3
+ import { render, renderChapter } from "./renderer/index.js";
4
+ import { copyCoreAssets } from "./renderer/html.js";
4
5
  function getCallerDir() {
5
6
  const err = new Error();
6
7
  const stack = err.stack?.split("\n");
@@ -49,11 +50,11 @@ export class LessonBuilder {
49
50
  slug,
50
51
  };
51
52
  this.options = {
52
- outDir: options.outDir ?? "./out",
53
+ outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
53
54
  contentBase: options.contentBase ?? callerDir ?? ".",
54
- theme: options.theme ?? "auto",
55
+ theme: options.theme ?? "light",
55
56
  palette: options.palette ?? "ink",
56
- strict: options.strict ?? true,
57
+ strict: options.strict ?? process.env.NODE_ENV !== "development",
57
58
  preset: {
58
59
  layout: "lesson",
59
60
  density: "comfortable",
@@ -165,7 +166,7 @@ export class LessonBuilder {
165
166
  return this.markdown(src);
166
167
  }
167
168
  /**
168
- * Creates a major chapter heading (H2) and a top-level sidebar navigation entry.
169
+ * Creates a major chapter heading (H1) and a top-level sidebar navigation entry.
169
170
  * @param src Path to a markdown file or raw markdown string.
170
171
  * @param title Optional title override for the sidebar and heading.
171
172
  * @example lesson.heading("# Welcome to Physics")
@@ -191,10 +192,10 @@ export class LessonBuilder {
191
192
  return this.markdown(src);
192
193
  }
193
194
  /**
194
- * Creates a new subsection heading (H3) and a sub-entry in the sidebar.
195
+ * Creates a new subsection heading (H2) and a sub-entry in the sidebar.
195
196
  * @param src Path to a markdown file or raw markdown string.
196
197
  * @param label Optional title override for the sidebar label.
197
- * @example lesson.section("### 1. Kinematics")
198
+ * @example lesson.section("## 1. Kinematics")
198
199
  */
199
200
  section(src, label) {
200
201
  this.blocks.push({ type: "section", src, label });
@@ -378,6 +379,7 @@ export class LessonBuilder {
378
379
  fs.mkdirSync(outDir, { recursive: true });
379
380
  const outPath = path.join(outDir, `${this.meta.slug}.html`);
380
381
  fs.writeFileSync(outPath, html, "utf-8");
382
+ copyCoreAssets(outDir);
381
383
  const relPath = path.relative(process.cwd(), outPath);
382
384
  console.log(` ✓ Built lesson (${this.blocks.length} blocks) → ${relPath}`);
383
385
  return outPath;
@@ -390,8 +392,20 @@ export class LessonBuilder {
390
392
  * @param options Build configuration for this specific lesson.
391
393
  * @example const l = lesson("Introduction to Kinematics").markdown("intro.md");
392
394
  */
393
- export function lesson(title, options = {}) {
394
- return new LessonBuilder(title, options, getCallerDir());
395
+ export function lesson(title, optionsOrSetup, setup) {
396
+ let options = {};
397
+ let cb = setup;
398
+ if (typeof optionsOrSetup === "function") {
399
+ cb = optionsOrSetup;
400
+ }
401
+ else if (optionsOrSetup) {
402
+ options = optionsOrSetup;
403
+ }
404
+ const builder = new LessonBuilder(title, options, getCallerDir());
405
+ if (cb) {
406
+ cb(builder);
407
+ }
408
+ return builder;
395
409
  }
396
410
  function normalizeSimulationOptions(opts, legacyHeight, fileConfig = null) {
397
411
  let inline;
@@ -494,11 +508,11 @@ export class ChapterBuilder {
494
508
  slug,
495
509
  };
496
510
  this.options = {
497
- outDir: options.outDir ?? "./out",
511
+ outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
498
512
  contentBase: options.contentBase ?? callerDir ?? ".",
499
- theme: options.theme ?? "auto",
513
+ theme: options.theme ?? "light",
500
514
  palette: options.palette ?? "ink",
501
- strict: options.strict ?? true,
515
+ strict: options.strict ?? process.env.NODE_ENV !== "development",
502
516
  preset: {
503
517
  layout: "lesson",
504
518
  density: "comfortable",
@@ -512,26 +526,6 @@ export class ChapterBuilder {
512
526
  this.meta.slug = slug;
513
527
  return this;
514
528
  }
515
- /** @internal Used by CourseBuilder to push down shared config */
516
- _inheritOptions(parentOpts, parentRawOpts) {
517
- this.options = {
518
- outDir: this._rawOptions.outDir ?? (parentRawOpts?.outDir || parentOpts.outDir) ?? this.options.outDir,
519
- contentBase: this._rawOptions.contentBase ??
520
- parentRawOpts?.contentBase ??
521
- this.options.contentBase,
522
- theme: this._rawOptions.theme ?? parentOpts.theme ?? this.options.theme,
523
- palette: this._rawOptions.palette ?? parentOpts.palette ?? this.options.palette,
524
- strict: this._rawOptions.strict ?? parentOpts.strict ?? this.options.strict,
525
- preset: {
526
- ...parentOpts.preset,
527
- ...this._rawOptions.preset,
528
- ...this.options.preset,
529
- },
530
- };
531
- for (const lb of this.lessonBuilders) {
532
- lb._inheritOptions(this.options, this._rawOptions);
533
- }
534
- }
535
529
  description(text) {
536
530
  this.meta.description = text;
537
531
  return this;
@@ -574,6 +568,7 @@ export class ChapterBuilder {
574
568
  fs.mkdirSync(outDir, { recursive: true });
575
569
  const outPath = path.join(outDir, `${this.meta.slug}.html`);
576
570
  fs.writeFileSync(outPath, html, "utf-8");
571
+ copyCoreAssets(outDir);
577
572
  const relPath = path.relative(process.cwd(), outPath);
578
573
  console.log(` ✓ Built chapter (${this.lessonBuilders.length} lessons) → ${relPath}`);
579
574
  return outPath;
@@ -597,70 +592,18 @@ export class ChapterBuilder {
597
592
  * .lesson(dynamicsLesson)
598
593
  * .build();
599
594
  */
600
- export function chapter(title, options = {}) {
601
- return new ChapterBuilder(title, options, getCallerDir());
602
- }
603
- // ─── CourseBuilder ──────────────────────────────────────────────────────────
604
- export class CourseBuilder {
605
- meta;
606
- chapterBuilders = [];
607
- options;
608
- _rawOptions;
609
- constructor(title, options = {}, callerDir) {
610
- this._rawOptions = options;
611
- let slug = title
612
- .toLowerCase()
613
- .replace(/[^a-z0-9]+/g, "-")
614
- .replace(/(^-|-$)/g, "");
615
- if (!slug)
616
- slug = "course";
617
- this.meta = { title, slug };
618
- this.options = {
619
- outDir: options.outDir ?? "./out",
620
- contentBase: options.contentBase ?? callerDir ?? ".",
621
- theme: options.theme ?? "auto",
622
- palette: options.palette ?? "ink",
623
- strict: options.strict ?? true,
624
- preset: {
625
- layout: "lesson",
626
- density: "comfortable",
627
- tone: "scholarly",
628
- ...options.preset,
629
- },
630
- ...options,
631
- };
595
+ export function chapter(title, optionsOrSetup, setup) {
596
+ let options = {};
597
+ let cb = setup;
598
+ if (typeof optionsOrSetup === "function") {
599
+ cb = optionsOrSetup;
632
600
  }
633
- slug(slug) {
634
- this.meta.slug = slug;
635
- return this;
601
+ else if (optionsOrSetup) {
602
+ options = optionsOrSetup;
636
603
  }
637
- description(text) {
638
- this.meta.description = text;
639
- return this;
640
- }
641
- chapter(chapterBuilder) {
642
- chapterBuilder._inheritOptions(this.options, this._rawOptions);
643
- this.chapterBuilders.push(chapterBuilder);
644
- return this;
604
+ const builder = new ChapterBuilder(title, options, getCallerDir());
605
+ if (cb) {
606
+ cb(builder);
645
607
  }
646
- build() {
647
- const chapters = [];
648
- for (const cb of this.chapterBuilders) {
649
- cb.build();
650
- chapters.push(cb.toJSON());
651
- }
652
- const courseData = { meta: this.meta, chapters };
653
- const html = renderCourse(courseData, this.options);
654
- const outDir = path.resolve(this.options.outDir);
655
- if (!fs.existsSync(outDir))
656
- fs.mkdirSync(outDir, { recursive: true });
657
- const outPath = path.join(outDir, `${this.meta.slug}.html`);
658
- fs.writeFileSync(outPath, html, "utf-8");
659
- const relPath = path.relative(process.cwd(), outPath);
660
- console.log(` ✓ Built course (${this.chapterBuilders.length} chapters) → ${relPath}`);
661
- return outPath;
662
- }
663
- }
664
- export function course(title, options = {}) {
665
- return new CourseBuilder(title, options, getCallerDir());
608
+ return builder;
666
609
  }
@@ -0,0 +1,2 @@
1
+ export declare function runDev(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,iBA+F1C"}
@@ -0,0 +1,92 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { exec } from "child_process";
4
+ export async function runDev(args) {
5
+ const dir = args[0] || ".";
6
+ const outDir = path.resolve(process.cwd(), dir, "out");
7
+ console.log(`Starting dev server for directory: ${dir}`);
8
+ let server;
9
+ const rebuild = () => {
10
+ console.log("Rebuilding...");
11
+ const entryPoints = ["chapter.ts", "index.ts", "lesson.ts"];
12
+ for (const entry of entryPoints) {
13
+ const entryPath = path.join(dir, entry);
14
+ if (fs.existsSync(entryPath)) {
15
+ exec(`NODE_ENV=development bun ${entryPath}`, (err, stdout, stderr) => {
16
+ if (err)
17
+ console.error("Build failed:", stderr);
18
+ else {
19
+ console.log("Build successful.");
20
+ server?.publish("livereload", "reload");
21
+ }
22
+ });
23
+ return;
24
+ }
25
+ }
26
+ console.log("No chapter.ts or lesson.ts found to build automatically.");
27
+ };
28
+ rebuild();
29
+ if (fs.existsSync(dir)) {
30
+ let timeout;
31
+ fs.watch(dir, { recursive: true }, (eventType, filename) => {
32
+ if (!filename || filename.includes("out/") || filename.includes(".git/"))
33
+ return;
34
+ clearTimeout(timeout);
35
+ timeout = setTimeout(() => {
36
+ console.log(`File changed: ${filename}`);
37
+ rebuild();
38
+ }, 200);
39
+ });
40
+ console.log(`Watching ${dir} for changes...`);
41
+ }
42
+ server = Bun.serve({
43
+ port: 3000,
44
+ async fetch(req, srv) {
45
+ if (srv.upgrade(req))
46
+ return;
47
+ const url = new URL(req.url);
48
+ let filePath = path.join(outDir, url.pathname);
49
+ if (filePath.endsWith("/")) {
50
+ const files = fs.existsSync(outDir) ? fs.readdirSync(outDir) : [];
51
+ const htmlFiles = files.filter(f => f.endsWith(".html"));
52
+ if (htmlFiles.includes("index.html")) {
53
+ filePath = path.join(outDir, "index.html");
54
+ }
55
+ else {
56
+ const chapterFile = htmlFiles.find(f => f.includes("chapter"));
57
+ if (chapterFile) {
58
+ filePath = path.join(outDir, chapterFile);
59
+ }
60
+ else if (htmlFiles.length > 0) {
61
+ filePath = path.join(outDir, htmlFiles[0]);
62
+ }
63
+ else {
64
+ filePath += "index.html";
65
+ }
66
+ }
67
+ }
68
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
69
+ if (filePath.endsWith(".html")) {
70
+ const file = Bun.file(filePath);
71
+ let text = await file.text();
72
+ text = text.replace("</body>", `<script>
73
+ const ws = new WebSocket("ws://localhost:3000/");
74
+ ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
75
+ </script></body>`);
76
+ return new Response(text, { headers: { "Content-Type": "text/html" } });
77
+ }
78
+ return new Response(Bun.file(filePath));
79
+ }
80
+ const srcPath = path.join(process.cwd(), dir, url.pathname);
81
+ if (fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
82
+ return new Response(Bun.file(srcPath));
83
+ }
84
+ return new Response("Not found", { status: 404 });
85
+ },
86
+ websocket: {
87
+ message() { },
88
+ open(ws) { ws.subscribe("livereload"); }
89
+ }
90
+ });
91
+ console.log(`Dev server listening on http://localhost:3000`);
92
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runGenerate(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAyBA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,iBAqK/C"}
@@ -0,0 +1,171 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ function getNextPrefix(dirPath) {
4
+ if (!fs.existsSync(dirPath))
5
+ return "01";
6
+ const files = fs.readdirSync(dirPath, { withFileTypes: true });
7
+ let maxPrefix = 0;
8
+ for (const file of files) {
9
+ if (file.isDirectory()) {
10
+ const match = file.name.match(/^(\d+)-/);
11
+ if (match) {
12
+ const num = parseInt(match[1], 10);
13
+ if (num > maxPrefix) {
14
+ maxPrefix = num;
15
+ }
16
+ }
17
+ }
18
+ }
19
+ const next = maxPrefix + 1;
20
+ return next.toString().padStart(2, "0");
21
+ }
22
+ export async function runGenerate(args) {
23
+ const type = args[0];
24
+ const rawName = args[1];
25
+ if (!type || !rawName) {
26
+ console.error("Usage: md g <ch|le|qu|chapter|lesson|quiz> <name>");
27
+ process.exit(1);
28
+ }
29
+ const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
30
+ const cwd = process.cwd();
31
+ switch (type) {
32
+ case "ch":
33
+ case "chapter": {
34
+ const chaptersDir = path.join(cwd, "chapters");
35
+ if (!fs.existsSync(chaptersDir))
36
+ fs.mkdirSync(chaptersDir, { recursive: true });
37
+ const prefix = getNextPrefix(chaptersDir);
38
+ const chapterDirName = `${prefix}-${name}`;
39
+ const chapterPath = path.join(chaptersDir, chapterDirName);
40
+ fs.mkdirSync(chapterPath, { recursive: true });
41
+ fs.mkdirSync(path.join(chapterPath, "lessons"), { recursive: true });
42
+ const varName = name.replace(/-([a-z0-9])/g, g => g[1].toUpperCase());
43
+ const chapterTitle = rawName.replace(/-/g, " ").replace(/\b\w/g, l => l.toUpperCase());
44
+ const content = `import { chapter } from "mr-md";
45
+
46
+ export const ${varName}Chapter = chapter("${chapterTitle}", ctx => {
47
+ // Add lessons here
48
+ });
49
+
50
+ if (import.meta.main) {
51
+ ${varName}Chapter.build();
52
+ }
53
+ `;
54
+ fs.writeFileSync(path.join(chapterPath, "chapter.ts"), content, "utf-8");
55
+ console.log(`Generated Chapter: chapters/${chapterDirName}`);
56
+ break;
57
+ }
58
+ case "le":
59
+ case "lesson": {
60
+ // Assume we are in a chapter directory
61
+ const lessonsDir = path.join(cwd, "lessons");
62
+ if (!fs.existsSync(lessonsDir))
63
+ fs.mkdirSync(lessonsDir, { recursive: true });
64
+ const prefix = getNextPrefix(lessonsDir);
65
+ const lessonDirName = `${prefix}-${name}`;
66
+ const lessonPath = path.join(lessonsDir, lessonDirName);
67
+ fs.mkdirSync(lessonPath, { recursive: true });
68
+ fs.mkdirSync(path.join(lessonPath, "sims"), { recursive: true });
69
+ fs.mkdirSync(path.join(lessonPath, "media"), { recursive: true });
70
+ fs.mkdirSync(path.join(lessonPath, "quizzes"), { recursive: true });
71
+ fs.mkdirSync(path.join(lessonPath, "content"), { recursive: true });
72
+ const lessonTitle = rawName.replace(/-/g, " ").replace(/\b\w/g, l => l.toUpperCase());
73
+ const varName = name.replace(/-([a-z0-9])/g, g => g[1].toUpperCase());
74
+ const content = `import { lesson } from "mr-md";
75
+
76
+ export const ${varName}Lesson = lesson("${lessonTitle}", { contentBase: import.meta.dir }, ctx => {
77
+ });
78
+ `;
79
+ fs.writeFileSync(path.join(lessonPath, "lesson.ts"), content, "utf-8");
80
+ console.log(`Generated Lesson: lessons/${lessonDirName}`);
81
+ // Auto import into chapter.ts
82
+ const chapterFile = path.join(cwd, "chapter.ts");
83
+ if (fs.existsSync(chapterFile)) {
84
+ const { Project, SyntaxKind } = await import("ts-morph");
85
+ const project = new Project();
86
+ const sourceFile = project.addSourceFileAtPath(chapterFile);
87
+ // 1. Add the import
88
+ sourceFile.addImportDeclaration({
89
+ namedImports: [`${varName}Lesson`],
90
+ moduleSpecifier: `./lessons/${lessonDirName}/lesson.js`
91
+ });
92
+ // 2. Find the chapter(...) call and inject ctx.lesson()
93
+ const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
94
+ const chapterCall = calls.find((c) => c.getExpression().getText() === "chapter");
95
+ if (chapterCall) {
96
+ const args = chapterCall.getArguments();
97
+ const callback = args.find((a) => a.getKind() === SyntaxKind.ArrowFunction || a.getKind() === SyntaxKind.FunctionExpression);
98
+ if (callback) {
99
+ const arrow = callback.asKind(SyntaxKind.ArrowFunction);
100
+ const func = callback.asKind(SyntaxKind.FunctionExpression);
101
+ const body = arrow ? arrow.getBody() : func ? func.getBody() : null;
102
+ if (body && body.getKind() === SyntaxKind.Block) {
103
+ body.asKind(SyntaxKind.Block)?.addStatements(`ctx.lesson(${varName}Lesson);`);
104
+ }
105
+ else {
106
+ console.warn(" ⚠ Could not auto-import: chapter callback must use { } block syntax.");
107
+ }
108
+ }
109
+ else {
110
+ console.warn(" ⚠ Could not auto-import: could not find chapter callback.");
111
+ }
112
+ }
113
+ sourceFile.saveSync();
114
+ console.log(`Auto-imported ${varName}Lesson into chapter.ts`);
115
+ }
116
+ break;
117
+ }
118
+ case "qu":
119
+ case "quiz": {
120
+ // Assume we are in a lesson directory
121
+ const quizzesDir = path.join(cwd, "quizzes");
122
+ if (!fs.existsSync(quizzesDir))
123
+ fs.mkdirSync(quizzesDir, { recursive: true });
124
+ const quizPath = path.join(quizzesDir, `${name}.json`);
125
+ const content = `{
126
+ "questions": [
127
+ {
128
+ "q": "Sample question?",
129
+ "options": ["A", "B"],
130
+ "answer": 0
131
+ }
132
+ ]
133
+ }
134
+ `;
135
+ fs.writeFileSync(quizPath, content, "utf-8");
136
+ console.log(`Generated Quiz: quizzes/${name}.json`);
137
+ // Auto import into lesson.ts
138
+ const lessonFile = path.join(cwd, "lesson.ts");
139
+ if (fs.existsSync(lessonFile)) {
140
+ const { Project, SyntaxKind } = await import("ts-morph");
141
+ const project = new Project();
142
+ const sourceFile = project.addSourceFileAtPath(lessonFile);
143
+ const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
144
+ const lessonCall = calls.find((c) => c.getExpression().getText() === "lesson");
145
+ if (lessonCall) {
146
+ const args = lessonCall.getArguments();
147
+ const callback = args.find((a) => a.getKind() === SyntaxKind.ArrowFunction || a.getKind() === SyntaxKind.FunctionExpression);
148
+ if (callback) {
149
+ const arrow = callback.asKind(SyntaxKind.ArrowFunction);
150
+ const func = callback.asKind(SyntaxKind.FunctionExpression);
151
+ const body = arrow ? arrow.getBody() : func ? func.getBody() : null;
152
+ if (body && body.getKind() === SyntaxKind.Block) {
153
+ body.asKind(SyntaxKind.Block)?.addStatements(`ctx.quiz("quizzes/${name}.json");`);
154
+ }
155
+ else {
156
+ console.warn(" ⚠ Could not auto-import quiz: lesson callback must use { } block syntax.");
157
+ }
158
+ }
159
+ else {
160
+ console.warn(" ⚠ Could not auto-import quiz: could not find lesson callback.");
161
+ }
162
+ }
163
+ sourceFile.saveSync();
164
+ console.log(`Auto-added quiz to lesson.ts`);
165
+ }
166
+ break;
167
+ }
168
+ default:
169
+ console.error(`Unknown generator type: ${type}`);
170
+ }
171
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runInit(): Promise<void>;
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAGA,wBAAsB,OAAO,kBAkD5B"}
@@ -0,0 +1,48 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ export async function runInit() {
4
+ console.log("Initializing md project structure...");
5
+ const dirs = [
6
+ "chapters",
7
+ "chapters/01-chapter",
8
+ "chapters/01-chapter/lessons",
9
+ "chapters/01-chapter/lessons/01-lesson",
10
+ "chapters/01-chapter/lessons/01-lesson/sims",
11
+ "chapters/01-chapter/lessons/01-lesson/media",
12
+ "chapters/01-chapter/lessons/01-lesson/quizzes",
13
+ "chapters/01-chapter/lessons/01-lesson/content",
14
+ ];
15
+ for (const dir of dirs) {
16
+ const fullPath = path.resolve(process.cwd(), dir);
17
+ if (!fs.existsSync(fullPath)) {
18
+ fs.mkdirSync(fullPath, { recursive: true });
19
+ console.log(` Created directory: ${dir}`);
20
+ }
21
+ }
22
+ const chapterTsPath = path.resolve(process.cwd(), "chapters/01-chapter/chapter.ts");
23
+ if (!fs.existsSync(chapterTsPath)) {
24
+ fs.writeFileSync(chapterTsPath, `import { chapter } from "mr-md";
25
+ import { firstLesson } from "./lessons/01-lesson/lesson.js";
26
+
27
+ export const firstChapter = chapter("First Chapter", ctx => {
28
+ ctx.lesson(firstLesson);
29
+ });
30
+
31
+ if (import.meta.main) {
32
+ firstChapter.build();
33
+ }
34
+ `, "utf-8");
35
+ console.log(" Created: chapters/01-chapter/chapter.ts");
36
+ }
37
+ const lessonTsPath = path.resolve(process.cwd(), "chapters/01-chapter/lessons/01-lesson/lesson.ts");
38
+ if (!fs.existsSync(lessonTsPath)) {
39
+ fs.writeFileSync(lessonTsPath, `import { lesson } from "mr-md";
40
+
41
+ export const firstLesson = lesson("First Lesson", { contentBase: import.meta.dir }, ctx => {
42
+ ctx.markdown("Welcome to your first lesson!");
43
+ });
44
+ `, "utf-8");
45
+ console.log(" Created: chapters/01-chapter/lessons/01-lesson/lesson.ts");
46
+ }
47
+ console.log("Done! You can now run `bun chapters/01-chapter/chapter.ts` to build your project.");
48
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}