@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 +56 -0
- package/dist/index.d.mts +55 -0
- package/dist/index.mjs +110 -0
- package/package.json +33 -0
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`).
|
package/dist/index.d.mts
ADDED
|
@@ -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
|
+
}
|