@xsynaptic/rehype-wrap-cjk 3.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexander Synaptic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # @xsynaptic/rehype-wrap-cjk
2
+
3
+ This package is a [unified][]/[rehype][] plugin that wraps [CJK character][cjk-wiki] sequences in an element (defaulting to `span`) with a configurable attribute and value, useful for applying different CSS styling rules in multilingual contexts.
4
+
5
+ By default it emits `<span class="cjk">...</span>` as a pure styling hook. To emit semantic `lang` tags instead, pass `attribute: 'lang'` together with a `value` such as `'zh'`, `'ja'`, or `'ko'`. Custom attribute names (e.g. `data-lang`) are also supported.
6
+
7
+ _Note_: this package is ESM-only. It was formerly published unscoped as `rehype-wrap-cjk`; the default export has been removed in favor of the named `rehypeWrapCjk` export.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install @xsynaptic/rehype-wrap-cjk
13
+ ```
14
+
15
+ ## Use
16
+
17
+ A typical pipeline transforming Markdown into HTML with [remark][] and [rehype][]:
18
+
19
+ ```ts
20
+ import rehypeSanitize from 'rehype-sanitize';
21
+ import rehypeStringify from 'rehype-stringify';
22
+ import { rehypeWrapCjk } from '@xsynaptic/rehype-wrap-cjk';
23
+ import remarkParse from 'remark-parse';
24
+ import remarkRehype from 'remark-rehype';
25
+ import { unified } from 'unified';
26
+
27
+ export function processMarkdown(markdownContent: string): string {
28
+ const htmlOutput = unified()
29
+ .use(remarkParse)
30
+ .use(remarkRehype)
31
+ .use(rehypeWrapCjk)
32
+ .use(rehypeSanitize)
33
+ .use(rehypeStringify)
34
+ .processSync(markdownContent);
35
+
36
+ return String(htmlOutput);
37
+ }
38
+ ```
39
+
40
+ Example plain text input:
41
+
42
+ ```text
43
+ Sample text with CJK characters (中日韓字符) interspersed. 中文 can appear anywhere in the text and will be appropriately wrapped.
44
+ ```
45
+
46
+ Example HTML output (default `class="cjk"` styling hook):
47
+
48
+ ```html
49
+ Sample text with CJK characters (<span class="cjk">中日韓字符</span>) interspersed.
50
+ <span class="cjk">中文</span> can appear anywhere in the text and will be appropriately wrapped.
51
+ ```
52
+
53
+ Example CSS rules (for you to implement in your own projects):
54
+
55
+ ```css
56
+ .cjk {
57
+ font-style: normal !important;
58
+ text-decoration: none !important;
59
+ word-break: keep-all !important;
60
+ }
61
+ ```
62
+
63
+ ## Options
64
+
65
+ Pass options as the second argument to `.use(rehypeWrapCjk, { ... })`.
66
+
67
+ - `element` (default `'span'`): wrapper element name.
68
+ - `attribute` (default `'class'`): attribute written to the wrapper. Use `'lang'` for semantic language tagging, `'class'` for a styling hook, or any other attribute name (e.g. `'data-lang'`).
69
+ - `value` (default `'cjk'`): value written to `attribute`. Also selects a preset regex when set to `'zh'`, `'ja'`, `'ko'`, or `'cjk'`.
70
+ - `regex` (default derived from `value`): custom pattern. The `g` flag is added if missing.
71
+ - `skipTags` (default `['code', 'pre', 'kbd', 'samp', 'script', 'style']`): elements whose descendants are left alone. Pass `[]` to disable.
72
+
73
+ Text inside any ancestor that already carries the target `attribute`/`value` is not re-wrapped.
74
+
75
+ ## Reference
76
+
77
+ - [CJK Unified Ideographs][cjk-unified-ideographs]
78
+ - [CJK Ideographs in Unicode][cjk-ideographs-in-unicode]
79
+ - [Halfwidth and Fullwidth Forms][halfwidth-and-fullwidth-forms]
80
+ - [HTMLElement lang property][html-element-lang-property]
81
+ - [word-break CSS property][wordbreak-css-property]
82
+
83
+ ## License
84
+
85
+ [MIT][mit-license]
86
+
87
+ [cjk-wiki]: https://en.wikipedia.org/wiki/CJK_characters
88
+ [cjk-unified-ideographs]: https://en.wikipedia.org/wiki/CJK_Unified_Ideographs
89
+ [cjk-ideographs-in-unicode]: https://en.wikipedia.org/wiki/Template:CJK_ideographs_in_Unicode
90
+ [halfwidth-and-fullwidth-forms]: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
91
+ [html-element-lang-property]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lang
92
+ [mit-license]: https://opensource.org/licenses/MIT
93
+ [rehype]: https://github.com/rehypejs/rehype
94
+ [remark]: https://github.com/remarkjs/remark
95
+ [unified]: https://github.com/unifiedjs/unified
96
+ [wordbreak-css-property]: https://developer.mozilla.org/en-US/docs/Web/CSS/word-break
@@ -0,0 +1,34 @@
1
+ import { Root } from "hast";
2
+ import { Plugin } from "unified";
3
+
4
+ //#region src/index.d.ts
5
+ interface RehypeWrapCjkOptions {
6
+ element?: string;
7
+ attribute?: string;
8
+ value?: string;
9
+ regex?: RegExp;
10
+ skipTags?: Array<string>;
11
+ }
12
+ /**
13
+ * Script-based ranges using Unicode Script Extensions property
14
+ * Authoritative and auto-updates with the JS engine's Unicode version
15
+ */
16
+ declare const zhScriptRange: string;
17
+ declare const jaScriptRange: string;
18
+ declare const koScriptRange: string;
19
+ declare const cjkScriptRange: string;
20
+ /**
21
+ * Full-width ASCII variants (FF01-FF5E): digits,letters, punctuation
22
+ * Not covered by script extensions but sometimes used in CJK contexts
23
+ */
24
+ declare const fullwidthAsciiRange: string;
25
+ declare const cjkRegexPresets: {
26
+ zh: RegExp;
27
+ ja: RegExp;
28
+ ko: RegExp;
29
+ cjk: RegExp;
30
+ };
31
+ declare const rehypeWrapCjk: Plugin<[RehypeWrapCjkOptions?], Root>;
32
+ //#endregion
33
+ export { RehypeWrapCjkOptions, cjkRegexPresets, cjkScriptRange, fullwidthAsciiRange, jaScriptRange, koScriptRange, rehypeWrapCjk, zhScriptRange };
34
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,83 @@
1
+ import { h } from "hastscript";
2
+ import { visitParents } from "unist-util-visit-parents";
3
+ //#region src/index.ts
4
+ const defaultSkipTags = [
5
+ "code",
6
+ "pre",
7
+ "kbd",
8
+ "samp",
9
+ "script",
10
+ "style"
11
+ ];
12
+ function isAncestorWrapped(ancestor, attribute, value) {
13
+ if (attribute === "class" || attribute === "className") {
14
+ const classes = ancestor.properties.className;
15
+ if (Array.isArray(classes)) return classes.includes(value);
16
+ if (typeof classes === "string") return classes.split(/\s+/).includes(value);
17
+ return false;
18
+ }
19
+ return ancestor.properties[attribute] === value;
20
+ }
21
+ /**
22
+ * Script-based ranges using Unicode Script Extensions property
23
+ * Authoritative and auto-updates with the JS engine's Unicode version
24
+ */
25
+ const zhScriptRange = String.raw`\p{scx=Han}\p{scx=Bopomofo}`;
26
+ const jaScriptRange = String.raw`\p{scx=Hiragana}\p{scx=Katakana}\p{scx=Han}`;
27
+ const koScriptRange = String.raw`\p{scx=Hangul}\p{scx=Han}`;
28
+ const cjkScriptRange = String.raw`\p{scx=Han}\p{scx=Hiragana}\p{scx=Katakana}\p{scx=Hangul}\p{scx=Bopomofo}`;
29
+ /**
30
+ * Full-width ASCII variants (FF01-FF5E): digits,letters, punctuation
31
+ * Not covered by script extensions but sometimes used in CJK contexts
32
+ */
33
+ const fullwidthAsciiRange = String.raw`\uFF01-\uFF5E`;
34
+ const cjkRegexPresets = {
35
+ zh: new RegExp(`[${zhScriptRange}${fullwidthAsciiRange}]+`, "gu"),
36
+ ja: new RegExp(`[${jaScriptRange}${fullwidthAsciiRange}]+`, "gu"),
37
+ ko: new RegExp(`[${koScriptRange}${fullwidthAsciiRange}]+`, "gu"),
38
+ cjk: new RegExp(`[${cjkScriptRange}${fullwidthAsciiRange}]+`, "gu")
39
+ };
40
+ const rehypeWrapCjk = (options) => {
41
+ const settings = {
42
+ element: options?.element ?? "span",
43
+ attribute: options?.attribute ?? "class",
44
+ value: options?.value ?? "cjk",
45
+ regex: options?.regex,
46
+ skipTags: options?.skipTags ?? defaultSkipTags
47
+ };
48
+ const baseRegex = settings.regex ?? (settings.value in cjkRegexPresets ? cjkRegexPresets[settings.value] : cjkRegexPresets.cjk);
49
+ const flags = baseRegex.flags.includes("g") ? baseRegex.flags : baseRegex.flags + "g";
50
+ const regex = new RegExp(baseRegex.source, flags);
51
+ function transformer(tree) {
52
+ visitParents(tree, "text", function visitor(node, ancestors) {
53
+ const parent = ancestors.at(-1);
54
+ if (!parent || !("children" in parent) || typeof node.value !== "string") return;
55
+ if (ancestors.some((ancestor) => ancestor.type === "element" && settings.skipTags.includes(ancestor.tagName))) return;
56
+ if (ancestors.some((ancestor) => ancestor.type === "element" && isAncestorWrapped(ancestor, settings.attribute, settings.value))) return;
57
+ const index = parent.children.indexOf(node);
58
+ if (index === -1) return;
59
+ const parts = [];
60
+ let lastIndex = 0;
61
+ for (const match of node.value.matchAll(regex)) {
62
+ const matchIndex = match.index;
63
+ if (matchIndex > lastIndex) parts.push({
64
+ type: "text",
65
+ value: node.value.slice(lastIndex, matchIndex)
66
+ });
67
+ parts.push(h(settings.element, { [settings.attribute]: settings.value }, [match[0]]));
68
+ lastIndex = matchIndex + match[0].length;
69
+ }
70
+ if (lastIndex < node.value.length) parts.push({
71
+ type: "text",
72
+ value: node.value.slice(lastIndex)
73
+ });
74
+ parent.children.splice(index, 1, ...parts);
75
+ });
76
+ return tree;
77
+ }
78
+ return transformer;
79
+ };
80
+ //#endregion
81
+ export { cjkRegexPresets, cjkScriptRange, fullwidthAsciiRange, jaScriptRange, koScriptRange, rehypeWrapCjk, zhScriptRange };
82
+
83
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Element, Root, Text } from 'hast';\nimport type { Plugin, Transformer } from 'unified';\n\nimport { h } from 'hastscript';\nimport { visitParents } from 'unist-util-visit-parents';\n\nexport interface RehypeWrapCjkOptions {\n\telement?: string;\n\tattribute?: string;\n\tvalue?: string;\n\tregex?: RegExp;\n\tskipTags?: Array<string>;\n}\n\nconst defaultSkipTags = ['code', 'pre', 'kbd', 'samp', 'script', 'style'];\n\nfunction isAncestorWrapped(ancestor: Element, attribute: string, value: string): boolean {\n\tif (attribute === 'class' || attribute === 'className') {\n\t\tconst classes = ancestor.properties.className;\n\n\t\tif (Array.isArray(classes)) return classes.includes(value);\n\n\t\tif (typeof classes === 'string') return classes.split(/\\s+/).includes(value);\n\n\t\treturn false;\n\t}\n\treturn ancestor.properties[attribute] === value;\n}\n\n/**\n * Script-based ranges using Unicode Script Extensions property\n * Authoritative and auto-updates with the JS engine's Unicode version\n */\nexport const zhScriptRange = String.raw`\\p{scx=Han}\\p{scx=Bopomofo}`;\nexport const jaScriptRange = String.raw`\\p{scx=Hiragana}\\p{scx=Katakana}\\p{scx=Han}`;\nexport const koScriptRange = String.raw`\\p{scx=Hangul}\\p{scx=Han}`;\nexport const cjkScriptRange = String.raw`\\p{scx=Han}\\p{scx=Hiragana}\\p{scx=Katakana}\\p{scx=Hangul}\\p{scx=Bopomofo}`;\n\n/**\n * Full-width ASCII variants (FF01-FF5E): digits,letters, punctuation\n * Not covered by script extensions but sometimes used in CJK contexts\n */\nexport const fullwidthAsciiRange = String.raw`\\uFF01-\\uFF5E`;\n\nexport const cjkRegexPresets = {\n\tzh: new RegExp(`[${zhScriptRange}${fullwidthAsciiRange}]+`, 'gu'),\n\tja: new RegExp(`[${jaScriptRange}${fullwidthAsciiRange}]+`, 'gu'),\n\tko: new RegExp(`[${koScriptRange}${fullwidthAsciiRange}]+`, 'gu'),\n\tcjk: new RegExp(`[${cjkScriptRange}${fullwidthAsciiRange}]+`, 'gu'),\n};\n\nexport const rehypeWrapCjk: Plugin<[RehypeWrapCjkOptions?], Root> = (options) => {\n\tconst settings = {\n\t\telement: options?.element ?? 'span',\n\t\tattribute: options?.attribute ?? 'class',\n\t\tvalue: options?.value ?? 'cjk',\n\t\tregex: options?.regex,\n\t\tskipTags: options?.skipTags ?? defaultSkipTags,\n\t};\n\n\tconst baseRegex =\n\t\tsettings.regex ??\n\t\t(settings.value in cjkRegexPresets\n\t\t\t? cjkRegexPresets[settings.value as keyof typeof cjkRegexPresets]\n\t\t\t: cjkRegexPresets.cjk);\n\n\t// always clone so a caller-supplied global regex never carries its lastIndex into matchAll\n\tconst flags = baseRegex.flags.includes('g') ? baseRegex.flags : baseRegex.flags + 'g';\n\tconst regex = new RegExp(baseRegex.source, flags);\n\n\tfunction transformer(tree: Root) {\n\t\tvisitParents(tree, 'text', function visitor(node, ancestors) {\n\t\t\tconst parent = ancestors.at(-1);\n\t\t\tif (!parent || !('children' in parent) || typeof node.value !== 'string') return;\n\n\t\t\t// Skip text inside excluded tags (code, pre, script, etc.)\n\t\t\tif (\n\t\t\t\tancestors.some(\n\t\t\t\t\t(ancestor) => ancestor.type === 'element' && settings.skipTags.includes(ancestor.tagName),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn;\n\n\t\t\t// Skip if any ancestor is already wrapped\n\t\t\tif (\n\t\t\t\tancestors.some(\n\t\t\t\t\t(ancestor) =>\n\t\t\t\t\t\tancestor.type === 'element' &&\n\t\t\t\t\t\tisAncestorWrapped(ancestor, settings.attribute, settings.value),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn;\n\n\t\t\tconst index = parent.children.indexOf(node);\n\t\t\tif (index === -1) return;\n\n\t\t\tconst parts: Array<Element | Text> = [];\n\t\t\tlet lastIndex = 0;\n\n\t\t\tfor (const match of node.value.matchAll(regex)) {\n\t\t\t\tconst matchIndex = match.index;\n\n\t\t\t\tif (matchIndex > lastIndex) {\n\t\t\t\t\tparts.push({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\tvalue: node.value.slice(lastIndex, matchIndex),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tparts.push(h(settings.element, { [settings.attribute]: settings.value }, [match[0]]));\n\t\t\t\tlastIndex = matchIndex + match[0].length;\n\t\t\t}\n\n\t\t\tif (lastIndex < node.value.length) {\n\t\t\t\tparts.push({ type: 'text', value: node.value.slice(lastIndex) });\n\t\t\t}\n\n\t\t\tparent.children.splice(index, 1, ...parts);\n\t\t});\n\n\t\treturn tree;\n\t}\n\n\treturn transformer satisfies Transformer<Root>;\n};\n"],"mappings":";;;AAcA,MAAM,kBAAkB;CAAC;CAAQ;CAAO;CAAO;CAAQ;CAAU;AAAO;AAExE,SAAS,kBAAkB,UAAmB,WAAmB,OAAwB;CACxF,IAAI,cAAc,WAAW,cAAc,aAAa;EACvD,MAAM,UAAU,SAAS,WAAW;EAEpC,IAAI,MAAM,QAAQ,OAAO,GAAG,OAAO,QAAQ,SAAS,KAAK;EAEzD,IAAI,OAAO,YAAY,UAAU,OAAO,QAAQ,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK;EAE3E,OAAO;CACR;CACA,OAAO,SAAS,WAAW,eAAe;AAC3C;;;;;AAMA,MAAa,gBAAgB,OAAO,GAAG;AACvC,MAAa,gBAAgB,OAAO,GAAG;AACvC,MAAa,gBAAgB,OAAO,GAAG;AACvC,MAAa,iBAAiB,OAAO,GAAG;;;;;AAMxC,MAAa,sBAAsB,OAAO,GAAG;AAE7C,MAAa,kBAAkB;CAC9B,IAAI,IAAI,OAAO,IAAI,gBAAgB,oBAAoB,KAAK,IAAI;CAChE,IAAI,IAAI,OAAO,IAAI,gBAAgB,oBAAoB,KAAK,IAAI;CAChE,IAAI,IAAI,OAAO,IAAI,gBAAgB,oBAAoB,KAAK,IAAI;CAChE,KAAK,IAAI,OAAO,IAAI,iBAAiB,oBAAoB,KAAK,IAAI;AACnE;AAEA,MAAa,iBAAwD,YAAY;CAChF,MAAM,WAAW;EAChB,SAAS,SAAS,WAAW;EAC7B,WAAW,SAAS,aAAa;EACjC,OAAO,SAAS,SAAS;EACzB,OAAO,SAAS;EAChB,UAAU,SAAS,YAAY;CAChC;CAEA,MAAM,YACL,SAAS,UACR,SAAS,SAAS,kBAChB,gBAAgB,SAAS,SACzB,gBAAgB;CAGpB,MAAM,QAAQ,UAAU,MAAM,SAAS,GAAG,IAAI,UAAU,QAAQ,UAAU,QAAQ;CAClF,MAAM,QAAQ,IAAI,OAAO,UAAU,QAAQ,KAAK;CAEhD,SAAS,YAAY,MAAY;EAChC,aAAa,MAAM,QAAQ,SAAS,QAAQ,MAAM,WAAW;GAC5D,MAAM,SAAS,UAAU,GAAG,EAAE;GAC9B,IAAI,CAAC,UAAU,EAAE,cAAc,WAAW,OAAO,KAAK,UAAU,UAAU;GAG1E,IACC,UAAU,MACR,aAAa,SAAS,SAAS,aAAa,SAAS,SAAS,SAAS,SAAS,OAAO,CACzF,GAEA;GAGD,IACC,UAAU,MACR,aACA,SAAS,SAAS,aAClB,kBAAkB,UAAU,SAAS,WAAW,SAAS,KAAK,CAChE,GAEA;GAED,MAAM,QAAQ,OAAO,SAAS,QAAQ,IAAI;GAC1C,IAAI,UAAU,IAAI;GAElB,MAAM,QAA+B,CAAC;GACtC,IAAI,YAAY;GAEhB,KAAK,MAAM,SAAS,KAAK,MAAM,SAAS,KAAK,GAAG;IAC/C,MAAM,aAAa,MAAM;IAEzB,IAAI,aAAa,WAChB,MAAM,KAAK;KACV,MAAM;KACN,OAAO,KAAK,MAAM,MAAM,WAAW,UAAU;IAC9C,CAAC;IAEF,MAAM,KAAK,EAAE,SAAS,SAAS,GAAG,SAAS,YAAY,SAAS,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACpF,YAAY,aAAa,MAAM,EAAE,CAAC;GACnC;GAEA,IAAI,YAAY,KAAK,MAAM,QAC1B,MAAM,KAAK;IAAE,MAAM;IAAQ,OAAO,KAAK,MAAM,MAAM,SAAS;GAAE,CAAC;GAGhE,OAAO,SAAS,OAAO,OAAO,GAAG,GAAG,KAAK;EAC1C,CAAC;EAED,OAAO;CACR;CAEA,OAAO;AACR"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@xsynaptic/rehype-wrap-cjk",
3
+ "version": "3.0.0",
4
+ "description": "Rehype plugin for wrapping CJK character sequences in an HTML element with a configurable attribute for styling or language tagging.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Alexander Synaptic <x@synapticism.com>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/xsynaptic/astro-lab.git",
11
+ "directory": "packages/rehype-wrap-cjk"
12
+ },
13
+ "homepage": "https://github.com/xsynaptic/astro-lab/tree/main/packages/rehype-wrap-cjk#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/xsynaptic/astro-lab/issues"
16
+ },
17
+ "keywords": [
18
+ "unified",
19
+ "rehype",
20
+ "rehype-plugin",
21
+ "cjk"
22
+ ],
23
+ "main": "./dist/index.mjs",
24
+ "types": "./dist/index.d.mts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.mts",
28
+ "import": "./dist/index.mjs"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "sideEffects": false,
35
+ "dependencies": {
36
+ "@types/hast": "^3.0.4",
37
+ "hastscript": "^9.0.1",
38
+ "unist-util-visit-parents": "^6.0.2"
39
+ },
40
+ "peerDependencies": {
41
+ "unified": "^11.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "rehype-parse": "^9.0.1",
45
+ "rehype-stringify": "^10.0.1",
46
+ "remark-parse": "^11.0.0",
47
+ "remark-rehype": "^11.1.2",
48
+ "unified": "^11.0.5",
49
+ "vfile": "^6.0.3"
50
+ },
51
+ "scripts": {
52
+ "build": "tsdown",
53
+ "dev": "tsdown --watch",
54
+ "check-types": "tsc --noEmit"
55
+ }
56
+ }