@pantoken/typedoc-plugin-live-example 0.1.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 ADDED
@@ -0,0 +1,56 @@
1
+ # @pantoken/typedoc-plugin-live-example
2
+
3
+ Embed a live HTML preview beneath each `@example` on a CSS reference page.
4
+
5
+ `@cssdoc/markdown` renders an `@example` as a plain ` ```html ` code fence and keeps it that way — a
6
+ generic renderer can't assume the host page loads the component CSS. When your docs _do_ load the
7
+ component stylesheet globally (pantoken's do), this plugin makes each example render live under its
8
+ source: after every ` ```html ` fence it appends a `<div class="css-example">` holding the same markup.
9
+ Overlay examples (`<dialog>`, `[popover]`) are skipped — they're hidden until opened, so a `## Demo`
10
+ iframe drives their preview instead.
11
+
12
+ By default the preview is wrapped in `<div class="css-example">`, but the **wrapper structure is
13
+ yours** — pass a `wrap` function (or a `liveExampleWrapper` template in config) to render each example in
14
+ any element you like, e.g. a card:
15
+
16
+ ```html
17
+ <div class="instui-card">
18
+ <!-- your example markup -->
19
+ </div>
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Register it as a TypeDoc plugin (rewrites pages under `liveExampleDir`, default `css`, on render end).
25
+ `liveExampleWrapper` is a template whose `%s` is replaced by the example markup:
26
+
27
+ ```json
28
+ {
29
+ "plugin": ["typedoc-plugin-markdown", "@cssdoc/typedoc", "@pantoken/typedoc-plugin-live-example"],
30
+ "liveExampleDir": "css",
31
+ "liveExampleWrapper": "<div class=\"instui-card\">%s</div>"
32
+ }
33
+ ```
34
+
35
+ Or call it directly when your CSS pages are written by a post-render step — `wrap` gives full control of
36
+ the wrapper structure:
37
+
38
+ ```ts
39
+ import { injectLiveExamples, withLiveExamples } from "@pantoken/typedoc-plugin-live-example";
40
+
41
+ // Default `<div class="css-example">` wrapper:
42
+ injectLiveExamples("docs/api/css");
43
+ // Custom structure — a card:
44
+ injectLiveExamples("docs/api/css", {
45
+ wrap: (html) => `<div class="instui-card">\n${html}\n</div>`,
46
+ });
47
+ const page = withLiveExamples(markdown, { wrap }); // or transform one page's markdown
48
+ ```
49
+
50
+ ## API
51
+
52
+ - **`withLiveExamples(markdown, options?): string`** — append a live-preview block after each ` ```html ` fence in a markdown string.
53
+ - **`injectLiveExamples(dir, options?): number`** — rewrite every `.md` under `dir` (recursively) in place; returns the count changed.
54
+ - **`LiveExampleOptions.wrap(html): string`** — build the block inserted after each fence; controls the wrapper structure (default `<div class="css-example">…</div>`).
55
+ - **`defaultWrap(html): string`** — the default `css-example` wrapper, exported for composition.
56
+ - **`load(app): void`** — the TypeDoc plugin entry point (options: `liveExampleDir`, `liveExampleWrapper`).
@@ -0,0 +1,55 @@
1
+ import { Application } from "typedoc";
2
+
3
+ //#region src/index.d.ts
4
+ /** Options controlling how a live-example preview is built. */
5
+ interface LiveExampleOptions {
6
+ /**
7
+ * Build the block inserted after each ` ```html ` fence from the example's markup — override this to
8
+ * control the wrapper STRUCTURE (a card element, extra nesting, data attributes, …). It's called only
9
+ * for non-overlay examples. Defaults to {@link defaultWrap} (`<div class="css-example">…</div>`).
10
+ *
11
+ * @param html - The example's raw HTML (verbatim from the fence).
12
+ * @returns The markdown/HTML block to place beneath the source fence.
13
+ */
14
+ wrap?: (html: string) => string;
15
+ }
16
+ /** The default wrapper: the example markup in a `<div class="css-example">`. */
17
+ declare function defaultWrap(html: string): string;
18
+ /**
19
+ * Append a live preview after each ` ```html ` fence in `markdown`, echoing the fence's markup through
20
+ * {@link LiveExampleOptions.wrap} so it renders beneath the source. Overlay examples are left source-only.
21
+ *
22
+ * @param markdown - The rendered page markdown.
23
+ * @param options - Wrapper structure ({@link LiveExampleOptions}).
24
+ * @returns The markdown with live-preview blocks inserted.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { withLiveExamples } from "@pantoken/typedoc-plugin-live-example";
29
+ *
30
+ * // Default wrapper:
31
+ * withLiveExamples(page);
32
+ * // Custom structure — a card:
33
+ * withLiveExamples(page, { wrap: (html) => `<div class="instui-card">\n${html}\n</div>` });
34
+ * ```
35
+ */
36
+ declare function withLiveExamples(markdown: string, options?: LiveExampleOptions): string;
37
+ /**
38
+ * Rewrite every `.md` under `dir` (recursively) through {@link withLiveExamples}.
39
+ *
40
+ * @param dir - The directory of CSS reference pages.
41
+ * @param options - Wrapper structure ({@link LiveExampleOptions}).
42
+ * @returns How many files were changed.
43
+ */
44
+ declare function injectLiveExamples(dir: string, options?: LiveExampleOptions): number;
45
+ /**
46
+ * TypeDoc entry point. On `RendererEvent.END`, rewrites the CSS reference pages (default `<output>/css`,
47
+ * override with `liveExampleDir`) to embed a live preview beneath each `@example`. Set `liveExampleWrapper`
48
+ * to a template whose `%s` is replaced by the example markup — e.g. `<div class="instui-card">%s</div>` —
49
+ * to control the wrapper structure from config; omit it for the default `<div class="css-example">`.
50
+ *
51
+ * @param app - The TypeDoc application.
52
+ */
53
+ declare function load(app: Application): void;
54
+ //#endregion
55
+ export { LiveExampleOptions, defaultWrap, injectLiveExamples, load, withLiveExamples };
package/dist/index.mjs ADDED
@@ -0,0 +1,110 @@
1
+ import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { ParameterType, RendererEvent } from "typedoc";
4
+ //#region src/index.ts
5
+ /**
6
+ * `@pantoken/typedoc-plugin-live-example` — embed a live HTML preview beneath each `@example` on a CSS
7
+ * reference page.
8
+ *
9
+ * `@cssdoc/markdown` renders an `@example` as a plain ` ```html ` code fence, and stays that way on
10
+ * purpose — a generic renderer can't assume the host page loads the component CSS globally. pantoken's
11
+ * docs DO load `@pantoken/components` globally, so each example can render live under its source. This
12
+ * plugin post-processes the emitted markdown: after every ` ```html ` fence it appends a
13
+ * `<div class="css-example">` holding the same markup, which the docs theme styles as one joined
14
+ * "source + preview" card. Overlay examples (`<dialog>`, `[popover]`) are skipped — they're hidden until
15
+ * opened, so their `## Demo` iframe drives the preview instead.
16
+ *
17
+ * Usable two ways: call {@link withLiveExamples} / {@link injectLiveExamples} directly when your CSS
18
+ * pages are written by a post-render step, or register it as a TypeDoc plugin — {@link load} rewrites the
19
+ * pages under `liveExampleDir` on `RendererEvent.END`.
20
+ *
21
+ * @module
22
+ * @beta
23
+ */
24
+ /** A fenced ` ```html ` block and its inner markup. */
25
+ const HTML_FENCE = /```html\n([\s\S]*?)\n```/gu;
26
+ /** An example that's hidden until opened (a `<dialog>` or a `[popover]`), so its live preview is skipped. */
27
+ function isOverlay(html) {
28
+ return /^<dialog\b/u.test(html.trim()) || /\spopover(?:=|\s|>)/u.test(html);
29
+ }
30
+ /** The default wrapper: the example markup in a `<div class="css-example">`. */
31
+ function defaultWrap(html) {
32
+ return `<div class="css-example">\n${html}\n</div>`;
33
+ }
34
+ /**
35
+ * Append a live preview after each ` ```html ` fence in `markdown`, echoing the fence's markup through
36
+ * {@link LiveExampleOptions.wrap} so it renders beneath the source. Overlay examples are left source-only.
37
+ *
38
+ * @param markdown - The rendered page markdown.
39
+ * @param options - Wrapper structure ({@link LiveExampleOptions}).
40
+ * @returns The markdown with live-preview blocks inserted.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * import { withLiveExamples } from "@pantoken/typedoc-plugin-live-example";
45
+ *
46
+ * // Default wrapper:
47
+ * withLiveExamples(page);
48
+ * // Custom structure — a card:
49
+ * withLiveExamples(page, { wrap: (html) => `<div class="instui-card">\n${html}\n</div>` });
50
+ * ```
51
+ */
52
+ function withLiveExamples(markdown, options = {}) {
53
+ const wrap = options.wrap ?? defaultWrap;
54
+ return markdown.replace(HTML_FENCE, (fence, html) => isOverlay(html) ? fence : `${fence}\n\n${wrap(html)}`);
55
+ }
56
+ /**
57
+ * Rewrite every `.md` under `dir` (recursively) through {@link withLiveExamples}.
58
+ *
59
+ * @param dir - The directory of CSS reference pages.
60
+ * @param options - Wrapper structure ({@link LiveExampleOptions}).
61
+ * @returns How many files were changed.
62
+ */
63
+ function injectLiveExamples(dir, options = {}) {
64
+ let changed = 0;
65
+ for (const name of readdirSync(dir)) {
66
+ const path = join(dir, name);
67
+ if (statSync(path).isDirectory()) changed += injectLiveExamples(path, options);
68
+ else if (name.endsWith(".md")) {
69
+ const before = readFileSync(path, "utf8");
70
+ const after = withLiveExamples(before, options);
71
+ if (after !== before) {
72
+ writeFileSync(path, after, "utf8");
73
+ changed++;
74
+ }
75
+ }
76
+ }
77
+ return changed;
78
+ }
79
+ /**
80
+ * TypeDoc entry point. On `RendererEvent.END`, rewrites the CSS reference pages (default `<output>/css`,
81
+ * override with `liveExampleDir`) to embed a live preview beneath each `@example`. Set `liveExampleWrapper`
82
+ * to a template whose `%s` is replaced by the example markup — e.g. `<div class="instui-card">%s</div>` —
83
+ * to control the wrapper structure from config; omit it for the default `<div class="css-example">`.
84
+ *
85
+ * @param app - The TypeDoc application.
86
+ */
87
+ function load(app) {
88
+ app.options.addDeclaration({
89
+ name: "liveExampleDir",
90
+ help: "Subdirectory under the docs output whose pages get live-example previews.",
91
+ type: ParameterType.String,
92
+ defaultValue: "css"
93
+ });
94
+ app.options.addDeclaration({
95
+ name: "liveExampleWrapper",
96
+ help: "Wrapper template; `%s` is replaced by the example markup. Empty → `<div class=\"css-example\">`.",
97
+ type: ParameterType.String,
98
+ defaultValue: ""
99
+ });
100
+ app.renderer.on(RendererEvent.END, (event) => {
101
+ const dir = join(event.outputDirectory, app.options.getValue("liveExampleDir"));
102
+ const template = app.options.getValue("liveExampleWrapper");
103
+ const wrap = template ? (html) => template.replace("%s", html) : void 0;
104
+ try {
105
+ injectLiveExamples(dir, { wrap });
106
+ } catch {}
107
+ });
108
+ }
109
+ //#endregion
110
+ export { defaultWrap, injectLiveExamples, load, withLiveExamples };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@pantoken/typedoc-plugin-live-example",
3
+ "version": "0.1.0",
4
+ "description": "TypeDoc plugin: embed a live HTML preview beneath each @example on a CSS reference page.",
5
+ "license": "MIT",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "type": "module",
10
+ "exports": {
11
+ ".": "./dist/index.mjs",
12
+ "./package.json": "./package.json"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^24.13.3",
19
+ "typedoc": "^0.28.20",
20
+ "typescript": "^6.0.3",
21
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.2.4",
22
+ "vite-plus": "0.2.4"
23
+ },
24
+ "peerDependencies": {
25
+ "typedoc": ">=0.28"
26
+ },
27
+ "scripts": {
28
+ "build": "vp pack",
29
+ "dev": "vp pack --watch",
30
+ "test": "vp test",
31
+ "check": "vp check"
32
+ }
33
+ }