@shivaedev/work-board 0.2.0 → 0.2.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/board.d.ts +9 -3
  3. package/dist/board.d.ts.map +1 -1
  4. package/dist/board.js +9 -9
  5. package/dist/board.js.map +1 -1
  6. package/dist/cli.js +3 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/files/changes.d.ts +3 -3
  9. package/dist/files/changes.d.ts.map +1 -1
  10. package/dist/files/changes.js +2 -2
  11. package/dist/files/changes.js.map +1 -1
  12. package/dist/files/list.d.ts +1 -1
  13. package/dist/files/list.d.ts.map +1 -1
  14. package/dist/files/list.js +1 -1
  15. package/dist/files/list.js.map +1 -1
  16. package/dist/http/assets.js +6 -6
  17. package/dist/http/assets.js.map +1 -1
  18. package/dist/http/events.d.ts +1 -1
  19. package/dist/http/events.d.ts.map +1 -1
  20. package/dist/http/events.js +1 -1
  21. package/dist/http/events.js.map +1 -1
  22. package/dist/http/loopback.js +2 -2
  23. package/dist/http/loopback.js.map +1 -1
  24. package/dist/http/page.d.ts +2 -2
  25. package/dist/http/page.d.ts.map +1 -1
  26. package/dist/http/page.js +8 -8
  27. package/dist/http/page.js.map +1 -1
  28. package/dist/page/board.d.ts +1 -1
  29. package/dist/page/board.d.ts.map +1 -1
  30. package/dist/page/board.js +2 -2
  31. package/dist/page/board.js.map +1 -1
  32. package/dist/page/document.d.ts +1 -1
  33. package/dist/page/document.d.ts.map +1 -1
  34. package/dist/page/document.js +1 -1
  35. package/dist/page/document.js.map +1 -1
  36. package/dist/page/escape.d.ts.map +1 -1
  37. package/dist/page/escape.js +2 -2
  38. package/dist/page/escape.js.map +1 -1
  39. package/dist/page/nav.d.ts +2 -2
  40. package/dist/page/nav.d.ts.map +1 -1
  41. package/dist/page/nav.js +1 -1
  42. package/dist/page/nav.js.map +1 -1
  43. package/dist/render/board.d.ts +8 -8
  44. package/dist/render/board.d.ts.map +1 -1
  45. package/dist/render/board.js +7 -7
  46. package/dist/render/board.js.map +1 -1
  47. package/dist/render/code.d.ts +1 -1
  48. package/dist/render/code.js +5 -5
  49. package/dist/render/code.js.map +1 -1
  50. package/dist/render/footnotes.d.ts +1 -1
  51. package/dist/render/footnotes.js +2 -2
  52. package/dist/render/footnotes.js.map +1 -1
  53. package/dist/render/highlighter.d.ts +1 -1
  54. package/dist/render/highlighter.js +2 -2
  55. package/dist/render/highlighter.js.map +1 -1
  56. package/dist/render/markdown.js +1 -1
  57. package/dist/render/markdown.js.map +1 -1
  58. package/dist/serve.d.ts +1 -1
  59. package/dist/serve.d.ts.map +1 -1
  60. package/dist/serve.js +1 -1
  61. package/dist/serve.js.map +1 -1
  62. package/package.json +37 -31
  63. package/src/board.ts +17 -12
  64. package/src/cli.ts +3 -3
  65. package/src/files/changes.ts +6 -6
  66. package/src/files/list.ts +3 -3
  67. package/src/http/assets.ts +6 -6
  68. package/src/http/events.ts +2 -2
  69. package/src/http/loopback.ts +2 -2
  70. package/src/http/page.ts +10 -10
  71. package/src/page/board.ts +2 -2
  72. package/src/page/document.ts +2 -2
  73. package/src/page/escape.ts +2 -2
  74. package/src/page/nav.ts +4 -4
  75. package/src/render/board.ts +19 -19
  76. package/src/render/code.ts +5 -5
  77. package/src/render/footnotes.ts +2 -2
  78. package/src/render/highlighter.ts +2 -2
  79. package/src/render/markdown.ts +1 -1
  80. package/src/serve.ts +1 -1
package/src/page/nav.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { MarkdownFile } from "../files/list.ts";
1
+ import type { MarkdownFile } from "#files/list.ts";
2
2
  import { escapeHtml } from "./escape.ts";
3
3
 
4
- const nameOf = (path: string): string => path.slice(path.lastIndexOf("/") + 1).replace(/\.md$/, "");
4
+ const nameOf = (path: string): string => path.slice(path.lastIndexOf("/") + 1).replace(/\.md$/u, "");
5
5
 
6
6
  const folderOf = (path: string): string => path.slice(0, Math.max(0, path.lastIndexOf("/")));
7
7
 
@@ -13,13 +13,13 @@ const link = (file: MarkdownFile, current: string): string => {
13
13
  return `<a href="${hrefOf(file.path)}"${here}>${escapeHtml(nameOf(file.path))}</a>${age}`;
14
14
  };
15
15
 
16
- const folder = (name: string, files: ReadonlyArray<MarkdownFile>, current: string): string => {
16
+ const folder = (name: string, files: readonly MarkdownFile[], current: string): string => {
17
17
  const links = files.map((file) => `<li>${link(file, current)}</li>`).join("");
18
18
  const open = files.some((file) => file.path === current) ? " open" : "";
19
19
  return `<details data-key="folder:${escapeHtml(name)}"${open}><summary>${escapeHtml(name)}/ (${files.length})</summary><ul>${links}</ul></details>`;
20
20
  };
21
21
 
22
- export const navHtml = (files: ReadonlyArray<MarkdownFile>, current: string, home: string | undefined): string => {
22
+ export const navHtml = (files: readonly MarkdownFile[], current: string, home: string | undefined): string => {
23
23
  const ordered = [...files.filter((file) => file.path === home), ...files.filter((file) => file.path !== home)];
24
24
  const folders = Map.groupBy(ordered, (file) => (file.path === home ? "" : folderOf(file.path)));
25
25
  return [...folders]
@@ -2,26 +2,26 @@ import type { RootContent } from "mdast";
2
2
  import { markdownToMdast } from "satteri";
3
3
 
4
4
  export interface Section {
5
- readonly title: string;
6
5
  readonly heading: string;
6
+ readonly items: readonly string[];
7
7
  readonly notes: string;
8
- readonly items: ReadonlyArray<string>;
8
+ readonly title: string;
9
9
  }
10
10
 
11
11
  export interface Board {
12
- readonly title: string;
13
- readonly intro: string;
14
- readonly sections: ReadonlyArray<Section>;
15
- readonly footer: string;
16
12
  readonly definitions: string;
13
+ readonly footer: string;
14
+ readonly intro: string;
15
+ readonly sections: readonly Section[];
16
+ readonly title: string;
17
17
  }
18
18
 
19
19
  interface Draft {
20
- title: string;
20
+ definitions: string[];
21
+ footer: string[] | undefined;
21
22
  intro: string[];
22
23
  sections: Array<{ title: string; heading: string; notes: string[]; items: string[][] }>;
23
- footer: string[] | undefined;
24
- definitions: string[];
24
+ title: string;
25
25
  }
26
26
 
27
27
  const textOf = (node: RootContent): string => {
@@ -42,7 +42,7 @@ const place = (draft: Draft, node: RootContent, source: string, closing: boolean
42
42
  } else if (node.type === "heading" && node.depth === 1 && draft.title === "") {
43
43
  draft.title = source;
44
44
  } else if (isSection(node)) {
45
- draft.sections.push({ title: textOf(node), heading: source, notes: [], items: [] });
45
+ draft.sections.push({ heading: source, items: [], notes: [], title: textOf(node) });
46
46
  } else if (section === undefined) {
47
47
  draft.intro.push(source);
48
48
  } else if (node.type === "heading" && node.depth === 3) {
@@ -54,10 +54,10 @@ const place = (draft: Draft, node: RootContent, source: string, closing: boolean
54
54
  }
55
55
  };
56
56
 
57
- const joined = (blocks: ReadonlyArray<string>): string => blocks.join("\n\n");
57
+ const joined = (blocks: readonly string[]): string => blocks.join("\n\n");
58
58
 
59
59
  export const boardOf = (source: string): Board => {
60
- const draft: Draft = { title: "", intro: [], sections: [], footer: undefined, definitions: [] };
60
+ const draft: Draft = { definitions: [], footer: undefined, intro: [], sections: [], title: "" };
61
61
  const root = markdownToMdast(source);
62
62
  const nodes = root.type === "root" ? root.children : [];
63
63
  const lastHeading = nodes.findLastIndex((node) => node.type === "heading");
@@ -65,18 +65,18 @@ export const boardOf = (source: string): Board => {
65
65
  place(draft, node, source.slice(node.position?.start.offset, node.position?.end.offset), index > lastHeading);
66
66
  });
67
67
  return {
68
- title: draft.title,
69
- intro: joined(draft.intro),
70
- sections: draft.sections.map((section) => ({ ...section, notes: joined(section.notes), items: section.items.map(joined) })),
71
- footer: joined(draft.footer ?? []),
72
68
  definitions: joined(draft.definitions),
69
+ footer: joined(draft.footer ?? []),
70
+ intro: joined(draft.intro),
71
+ sections: draft.sections.map((section) => ({ ...section, items: section.items.map(joined), notes: joined(section.notes) })),
72
+ title: draft.title,
73
73
  };
74
74
  };
75
75
 
76
76
  export interface Count {
77
- readonly title: string;
78
77
  readonly count: number;
78
+ readonly title: string;
79
79
  }
80
80
 
81
- export const countsOf = (board: Board): ReadonlyArray<Count> =>
82
- board.sections.flatMap((section) => (section.items.length === 0 ? [] : [{ title: section.title, count: section.items.length }]));
81
+ export const countsOf = (board: Board): readonly Count[] =>
82
+ board.sections.flatMap((section) => (section.items.length === 0 ? [] : [{ count: section.items.length, title: section.title }]));
@@ -17,10 +17,10 @@ const textOf = (node: ElementContent): string => {
17
17
  };
18
18
 
19
19
  const diagram = (source: string): Element => ({
20
- type: "element",
21
- tagName: "figure",
20
+ children: [{ children: [{ type: "text", value: source }], properties: { className: ["diagram-source"] }, tagName: "pre", type: "element" }],
22
21
  properties: { className: ["diagram"], dataState: "pending" },
23
- children: [{ type: "element", tagName: "pre", properties: { className: ["diagram-source"] }, children: [{ type: "text", value: source }] }],
22
+ tagName: "figure",
23
+ type: "element",
24
24
  });
25
25
 
26
26
  const isBundled = (language: string): language is BundledLanguage => Object.hasOwn(bundledLanguages, language);
@@ -30,12 +30,11 @@ const highlighted = async (highlighter: Shiki, source: string, language: string)
30
30
  return undefined;
31
31
  }
32
32
  await highlighter.loadLanguage(language);
33
- return highlighter.codeToHast(source, { lang: language, themes: THEMES, defaultColor: false }).children[0];
33
+ return highlighter.codeToHast(source, { defaultColor: false, lang: language, themes: THEMES }).children.at(0);
34
34
  };
35
35
 
36
36
  export const codeBlocks = (highlighter: Shiki) =>
37
37
  defineHastPlugin({
38
- name: "work-board-code",
39
38
  element: {
40
39
  filter: ["pre"],
41
40
  visit: async (pre) => {
@@ -51,4 +50,5 @@ export const codeBlocks = (highlighter: Shiki) =>
51
50
  return language === undefined ? undefined : await highlighted(highlighter, source, language);
52
51
  },
53
52
  },
53
+ name: "work-board-code",
54
54
  });
@@ -1,7 +1,7 @@
1
1
  import type { Element } from "hast";
2
2
  import { defineHastPlugin } from "satteri";
3
3
 
4
- const FOOTNOTE_IDS = /^(?:user-content-fn|footnote-label)/;
4
+ const FOOTNOTE_IDS = /^(?:user-content-fn|footnote-label)/u;
5
5
 
6
6
  type Value = Element["properties"][string];
7
7
 
@@ -19,7 +19,6 @@ const scoped = (prefix: string, value: Value): Value => {
19
19
 
20
20
  export const footnoteIds = (prefix: string) =>
21
21
  defineHastPlugin({
22
- name: "work-board-footnote-ids",
23
22
  element: {
24
23
  filter: ["a", "h2", "li"],
25
24
  visit: (element) => ({
@@ -29,4 +28,5 @@ export const footnoteIds = (prefix: string) =>
29
28
  ),
30
29
  }),
31
30
  },
31
+ name: "work-board-footnote-ids",
32
32
  });
@@ -2,14 +2,14 @@ import { Context, Effect, Layer } from "effect";
2
2
  import { createHighlighter, type Highlighter as Shiki } from "shiki";
3
3
  import { RenderFailed } from "./failed.ts";
4
4
 
5
- export const THEMES = { light: "github-light", dark: "github-dark" } as const;
5
+ export const THEMES = { dark: "github-dark", light: "github-light" } as const;
6
6
 
7
7
  export class Highlighter extends Context.Service<Highlighter, Shiki>()("WorkBoard/Highlighter") {
8
8
  static readonly layer = Layer.effect(Highlighter)(
9
9
  Effect.acquireRelease(
10
10
  Effect.tryPromise({
11
- try: () => createHighlighter({ themes: Object.values(THEMES), langs: [] }),
12
11
  catch: (cause) => new RenderFailed({ cause }),
12
+ try: () => createHighlighter({ langs: [], themes: Object.values(THEMES) }),
13
13
  }),
14
14
  (highlighter) => Effect.sync(() => highlighter.dispose()),
15
15
  ),
@@ -8,9 +8,9 @@ import { Highlighter } from "./highlighter.ts";
8
8
  export const renderMarkdown = Effect.fn("WorkBoard.renderMarkdown")(function* (source: string, idPrefix = "") {
9
9
  const highlighter = yield* Highlighter;
10
10
  const { html } = yield* Effect.tryPromise({
11
+ catch: (cause) => new RenderFailed({ cause }),
11
12
  try: async () =>
12
13
  markdownToHtml(source, { hastPlugins: idPrefix === "" ? [codeBlocks(highlighter)] : [codeBlocks(highlighter), footnoteIds(idPrefix)] }),
13
- catch: (cause) => new RenderFailed({ cause }),
14
14
  });
15
15
  return html;
16
16
  });
package/src/serve.ts CHANGED
@@ -10,7 +10,7 @@ export interface ServeOptions extends BoardOptions {
10
10
  readonly port: number;
11
11
  }
12
12
 
13
- export const listenOn = (port: number) => NodeHttpServer.layer(createServer, { host: HOST, port, disablePreemptiveShutdown: true });
13
+ export const listenOn = (port: number) => NodeHttpServer.layer(createServer, { disablePreemptiveShutdown: true, host: HOST, port });
14
14
 
15
15
  export const serveBoard = (options: ServeOptions) =>
16
16
  HttpRouter.serve(boardLayer(options), { disableLogger: true }).pipe(Layer.provideMerge(listenOn(options.port)));