@xsynaptic/unified-tools 4.2.1 → 5.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.
@@ -1,4 +1,3 @@
1
1
  # Unified Tools
2
2
 
3
3
  A set of Unified tools and pipelines for markup and text manipulation. This isn't meant to be a public library others install; it is a common library for several of my own projects, but you're welcome to lift some code if there's anything interesting in here.
4
-
package/dist/index.d.mts CHANGED
@@ -1,7 +1,38 @@
1
- import { wrapChinese, wrapCjk, wrapJapanese, wrapKorean } from "./html-cjk.mjs";
2
- import { sanitizeHtml, stripTags } from "./html.mjs";
3
- import { transformMarkdown } from "./markdown.mjs";
4
- import { sanitizeMdx } from "./mdx.mjs";
5
- import { stylizeText } from "./text.mjs";
6
- import { defaultSchema } from "rehype-sanitize";
7
- export { defaultSchema, sanitizeHtml, sanitizeMdx, stripTags, stylizeText, transformMarkdown, wrapChinese, wrapCjk, wrapJapanese, wrapKorean };
1
+ import { RehypeWrapCjkOptions } from "@xsynaptic/rehype-wrap-cjk";
2
+ import { Options, defaultSchema } from "rehype-sanitize";
3
+ import { Options as Options$1 } from "retext-smartypants";
4
+
5
+ //#region src/html-cjk.d.ts
6
+ declare function wrapChinese(input: string): string;
7
+ declare function wrapJapanese(input: string): string;
8
+ declare function wrapKorean(input: string): string;
9
+ declare function wrapCjk({
10
+ input,
11
+ wrapCjkOptions
12
+ }: {
13
+ input: string;
14
+ wrapCjkOptions: Partial<RehypeWrapCjkOptions>;
15
+ }): string;
16
+ //#endregion
17
+ //#region src/html.d.ts
18
+ declare function sanitizeHtml(input: string, options?: Options): string;
19
+ declare function stripTags(input: string, options?: Options): string;
20
+ //#endregion
21
+ //#region src/markdown.d.ts
22
+ interface TransformMarkdownOptions {
23
+ input: string;
24
+ wrapCjkOptions?: Partial<RehypeWrapCjkOptions> | undefined;
25
+ }
26
+ declare function transformMarkdown({
27
+ input,
28
+ wrapCjkOptions
29
+ }: TransformMarkdownOptions): string;
30
+ //#endregion
31
+ //#region src/mdx.d.ts
32
+ declare function sanitizeMdx(input: string, options?: Options): string;
33
+ //#endregion
34
+ //#region src/text.d.ts
35
+ declare function stylizeText(input: string, options?: Options$1): string;
36
+ //#endregion
37
+ export { defaultSchema, sanitizeHtml, sanitizeMdx, stripTags, stylizeText, transformMarkdown, wrapChinese, wrapCjk, wrapJapanese, wrapKorean };
38
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1 +1,138 @@
1
- import{wrapChinese as e,wrapCjk as t,wrapJapanese as n,wrapKorean as r}from"./html-cjk.mjs";import{sanitizeHtml as i,stripTags as a}from"./html.mjs";import{transformMarkdown as o}from"./markdown.mjs";import{sanitizeMdx as s}from"./mdx.mjs";import{stylizeText as c}from"./text.mjs";import{defaultSchema as l}from"rehype-sanitize";export{l as defaultSchema,i as sanitizeHtml,s as sanitizeMdx,a as stripTags,c as stylizeText,o as transformMarkdown,e as wrapChinese,t as wrapCjk,n as wrapJapanese,r as wrapKorean};
1
+ import { rehypeWrapCjk } from "@xsynaptic/rehype-wrap-cjk";
2
+ import { hash } from "ohash";
3
+ import rehypeParse from "rehype-parse";
4
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
5
+ import rehypeStringify from "rehype-stringify";
6
+ import { unified } from "unified";
7
+ import remarkParse from "remark-parse";
8
+ import remarkRehype from "remark-rehype";
9
+ import remarkSmartyPants from "remark-smartypants";
10
+ import remarkMdx from "remark-mdx";
11
+ import { retext } from "retext";
12
+ import retextSmartypants from "retext-smartypants";
13
+ //#region src/html-cjk.ts
14
+ const processorCache$4 = /* @__PURE__ */ new Map();
15
+ function createProcessor$3(wrapCjkOptions) {
16
+ return unified().use(rehypeParse, { fragment: true }).use(rehypeSanitize).use(rehypeWrapCjk, wrapCjkOptions).use(rehypeStringify).freeze();
17
+ }
18
+ function getProcessor$4(wrapCjkOptions) {
19
+ const cacheKey = hash(wrapCjkOptions);
20
+ let processor = processorCache$4.get(cacheKey);
21
+ if (!processor) {
22
+ processor = createProcessor$3(wrapCjkOptions);
23
+ processorCache$4.set(cacheKey, processor);
24
+ }
25
+ return processor;
26
+ }
27
+ const zhOptions = {
28
+ attribute: "lang",
29
+ value: "zh"
30
+ };
31
+ const jaOptions = {
32
+ attribute: "lang",
33
+ value: "ja"
34
+ };
35
+ const koOptions = {
36
+ attribute: "lang",
37
+ value: "ko"
38
+ };
39
+ function wrapChinese(input) {
40
+ return getProcessor$4(zhOptions).processSync(input).toString();
41
+ }
42
+ function wrapJapanese(input) {
43
+ return getProcessor$4(jaOptions).processSync(input).toString();
44
+ }
45
+ function wrapKorean(input) {
46
+ return getProcessor$4(koOptions).processSync(input).toString();
47
+ }
48
+ function wrapCjk({ input, wrapCjkOptions }) {
49
+ return getProcessor$4(wrapCjkOptions).processSync(input).toString();
50
+ }
51
+ //#endregion
52
+ //#region src/html.ts
53
+ const processorCache$3 = /* @__PURE__ */ new Map();
54
+ function createProcessor$2(options) {
55
+ return unified().use(rehypeParse, { fragment: true }).use(rehypeSanitize, options).use(rehypeStringify).freeze();
56
+ }
57
+ function getProcessor$3(options) {
58
+ const cacheKey = options ? hash(options) : "";
59
+ let processor = processorCache$3.get(cacheKey);
60
+ if (!processor) {
61
+ processor = createProcessor$2(options);
62
+ processorCache$3.set(cacheKey, processor);
63
+ }
64
+ return processor;
65
+ }
66
+ function sanitizeHtml(input, options) {
67
+ const processor = getProcessor$3(options);
68
+ return String(processor.processSync(input));
69
+ }
70
+ function stripTags(input, options) {
71
+ return sanitizeHtml(input, {
72
+ ...options,
73
+ tagNames: []
74
+ });
75
+ }
76
+ //#endregion
77
+ //#region src/markdown.ts
78
+ const processorCache$2 = /* @__PURE__ */ new Map();
79
+ function createProcessorWithCjk(wrapCjkOptions) {
80
+ return unified().use(remarkParse).use(remarkSmartyPants).use(remarkRehype).use(rehypeSanitize).use(rehypeWrapCjk, wrapCjkOptions).use(rehypeStringify).freeze();
81
+ }
82
+ function createProcessorWithoutCjk() {
83
+ return unified().use(remarkParse).use(remarkSmartyPants).use(remarkRehype).use(rehypeSanitize).use(rehypeStringify).freeze();
84
+ }
85
+ function getProcessor$2(wrapCjkOptions) {
86
+ const cacheKey = wrapCjkOptions ? hash(wrapCjkOptions) : "";
87
+ let processor = processorCache$2.get(cacheKey);
88
+ if (!processor) {
89
+ processor = wrapCjkOptions ? createProcessorWithCjk(wrapCjkOptions) : createProcessorWithoutCjk();
90
+ processorCache$2.set(cacheKey, processor);
91
+ }
92
+ return processor;
93
+ }
94
+ function transformMarkdown({ input, wrapCjkOptions }) {
95
+ return getProcessor$2(wrapCjkOptions).processSync(input).toString().trim();
96
+ }
97
+ //#endregion
98
+ //#region src/mdx.ts
99
+ const processorCache$1 = /* @__PURE__ */ new Map();
100
+ const defaultOptions = { tagNames: [] };
101
+ function createProcessor$1(options) {
102
+ return unified().use(remarkParse).use(remarkMdx).use(remarkRehype).use(rehypeSanitize, options).use(rehypeStringify).freeze();
103
+ }
104
+ function getProcessor$1(options) {
105
+ const effectiveOptions = options ?? defaultOptions;
106
+ const cacheKey = hash(effectiveOptions);
107
+ let processor = processorCache$1.get(cacheKey);
108
+ if (!processor) {
109
+ processor = createProcessor$1(effectiveOptions);
110
+ processorCache$1.set(cacheKey, processor);
111
+ }
112
+ return processor;
113
+ }
114
+ function sanitizeMdx(input, options) {
115
+ return String(getProcessor$1(options).processSync(input)).replaceAll(/\s+/g, " ").trim();
116
+ }
117
+ //#endregion
118
+ //#region src/text.ts
119
+ const processorCache = /* @__PURE__ */ new Map();
120
+ function createProcessor(options) {
121
+ return retext().use(retextSmartypants, options).freeze();
122
+ }
123
+ function getProcessor(options) {
124
+ const cacheKey = options ? hash(options) : "";
125
+ let processor = processorCache.get(cacheKey);
126
+ if (!processor) {
127
+ processor = createProcessor(options);
128
+ processorCache.set(cacheKey, processor);
129
+ }
130
+ return processor;
131
+ }
132
+ function stylizeText(input, options) {
133
+ return String(getProcessor(options).processSync(input)).trim();
134
+ }
135
+ //#endregion
136
+ export { defaultSchema, sanitizeHtml, sanitizeMdx, stripTags, stylizeText, transformMarkdown, wrapChinese, wrapCjk, wrapJapanese, wrapKorean };
137
+
138
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["processorCache","createProcessor","getProcessor","processorCache","createProcessor","getProcessor","processorCache","getProcessor","processorCache","createProcessor","getProcessor"],"sources":["../src/html-cjk.ts","../src/html.ts","../src/markdown.ts","../src/mdx.ts","../src/text.ts"],"sourcesContent":["import type { RehypeWrapCjkOptions } from '@xsynaptic/rehype-wrap-cjk';\n\nimport { rehypeWrapCjk } from '@xsynaptic/rehype-wrap-cjk';\nimport { hash } from 'ohash';\nimport rehypeParse from 'rehype-parse';\nimport rehypeSanitize from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport { unified } from 'unified';\n\n// Cache frozen processors by options hash\nconst processorCache = new Map<string, unknown>();\n\nfunction createProcessor(wrapCjkOptions: Partial<RehypeWrapCjkOptions>) {\n\treturn unified()\n\t\t.use(rehypeParse, { fragment: true })\n\t\t.use(rehypeSanitize)\n\t\t.use(rehypeWrapCjk, wrapCjkOptions)\n\t\t.use(rehypeStringify)\n\t\t.freeze();\n}\n\nfunction getProcessor(wrapCjkOptions: Partial<RehypeWrapCjkOptions>) {\n\tconst cacheKey = hash(wrapCjkOptions);\n\n\tlet processor = processorCache.get(cacheKey);\n\n\tif (!processor) {\n\t\tprocessor = createProcessor(wrapCjkOptions);\n\t\tprocessorCache.set(cacheKey, processor);\n\t}\n\n\treturn processor as ReturnType<typeof createProcessor>;\n}\n\n// Pre-defined options for common language codes\nconst zhOptions: Partial<RehypeWrapCjkOptions> = {\n\tattribute: 'lang',\n\tvalue: 'zh',\n};\nconst jaOptions: Partial<RehypeWrapCjkOptions> = {\n\tattribute: 'lang',\n\tvalue: 'ja',\n};\nconst koOptions: Partial<RehypeWrapCjkOptions> = {\n\tattribute: 'lang',\n\tvalue: 'ko',\n};\n\nexport function wrapChinese(input: string): string {\n\treturn getProcessor(zhOptions).processSync(input).toString();\n}\n\nexport function wrapJapanese(input: string): string {\n\treturn getProcessor(jaOptions).processSync(input).toString();\n}\n\nexport function wrapKorean(input: string): string {\n\treturn getProcessor(koOptions).processSync(input).toString();\n}\n\n// wrapCjkOptions is required so a no-op call cannot be written; pass `{}` for the default `cjk` preset\nexport function wrapCjk({\n\tinput,\n\twrapCjkOptions,\n}: {\n\tinput: string;\n\twrapCjkOptions: Partial<RehypeWrapCjkOptions>;\n}): string {\n\treturn getProcessor(wrapCjkOptions).processSync(input).toString();\n}\n","import type { Options as RehypeSanitizeOptions } from 'rehype-sanitize';\n\nimport { hash } from 'ohash';\nimport rehypeParse from 'rehype-parse';\nimport rehypeSanitize from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport { unified } from 'unified';\n\n// Cache frozen processors by options hash\nconst processorCache = new Map<string, unknown>();\n\nfunction createProcessor(options?: RehypeSanitizeOptions) {\n\treturn unified()\n\t\t.use(rehypeParse, { fragment: true })\n\t\t.use(rehypeSanitize, options)\n\t\t.use(rehypeStringify)\n\t\t.freeze();\n}\n\nfunction getProcessor(options?: RehypeSanitizeOptions) {\n\tconst cacheKey = options ? hash(options) : '';\n\n\tlet processor = processorCache.get(cacheKey);\n\n\tif (!processor) {\n\t\tprocessor = createProcessor(options);\n\t\tprocessorCache.set(cacheKey, processor);\n\t}\n\n\treturn processor as ReturnType<typeof createProcessor>;\n}\n\nexport function sanitizeHtml(input: string, options?: RehypeSanitizeOptions): string {\n\tconst processor = getProcessor(options);\n\n\treturn String(processor.processSync(input));\n}\n\n// Handy shortcut for when you just want to strip tags from text\nexport function stripTags(input: string, options?: RehypeSanitizeOptions): string {\n\treturn sanitizeHtml(input, { ...options, tagNames: [] });\n}\n","import type { RehypeWrapCjkOptions } from '@xsynaptic/rehype-wrap-cjk';\n\nimport { rehypeWrapCjk } from '@xsynaptic/rehype-wrap-cjk';\nimport { hash } from 'ohash';\nimport rehypeSanitize from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport remarkSmartyPants from 'remark-smartypants';\nimport { unified } from 'unified';\n\ninterface TransformMarkdownOptions {\n\tinput: string;\n\twrapCjkOptions?: Partial<RehypeWrapCjkOptions> | undefined;\n}\n\n// Cache frozen processors by options hash\nconst processorCache = new Map<string, unknown>();\n\nfunction createProcessorWithCjk(wrapCjkOptions: Partial<RehypeWrapCjkOptions>) {\n\treturn unified()\n\t\t.use(remarkParse)\n\t\t.use(remarkSmartyPants)\n\t\t.use(remarkRehype)\n\t\t.use(rehypeSanitize)\n\t\t.use(rehypeWrapCjk, wrapCjkOptions)\n\t\t.use(rehypeStringify)\n\t\t.freeze();\n}\n\n// Processor without CJK wrapping\nfunction createProcessorWithoutCjk() {\n\treturn unified()\n\t\t.use(remarkParse)\n\t\t.use(remarkSmartyPants)\n\t\t.use(remarkRehype)\n\t\t.use(rehypeSanitize)\n\t\t.use(rehypeStringify)\n\t\t.freeze();\n}\n\nfunction getProcessor(wrapCjkOptions?: Partial<RehypeWrapCjkOptions>) {\n\t// Generate stable cache key from options (empty string for no options)\n\tconst cacheKey = wrapCjkOptions ? hash(wrapCjkOptions) : '';\n\n\tlet processor = processorCache.get(cacheKey);\n\n\tif (!processor) {\n\t\tprocessor = wrapCjkOptions\n\t\t\t? createProcessorWithCjk(wrapCjkOptions)\n\t\t\t: createProcessorWithoutCjk();\n\t\tprocessorCache.set(cacheKey, processor);\n\t}\n\n\treturn processor as ReturnType<typeof createProcessorWithoutCjk>;\n}\n\nexport function transformMarkdown({ input, wrapCjkOptions }: TransformMarkdownOptions): string {\n\treturn getProcessor(wrapCjkOptions).processSync(input).toString().trim();\n}\n","import type { Options as RehypeSanitizeOptions } from 'rehype-sanitize';\n\nimport { hash } from 'ohash';\nimport rehypeSanitize from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport remarkMdx from 'remark-mdx';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport { unified } from 'unified';\n\n// Cache frozen processors by options hash\nconst processorCache = new Map<string, unknown>();\n\n// Default options for stripping all tags\nconst defaultOptions: RehypeSanitizeOptions = { tagNames: [] };\n\nfunction createProcessor(options: RehypeSanitizeOptions) {\n\treturn unified()\n\t\t.use(remarkParse)\n\t\t.use(remarkMdx)\n\t\t.use(remarkRehype)\n\t\t.use(rehypeSanitize, options)\n\t\t.use(rehypeStringify)\n\t\t.freeze();\n}\n\nfunction getProcessor(options?: RehypeSanitizeOptions) {\n\tconst effectiveOptions = options ?? defaultOptions;\n\tconst cacheKey = hash(effectiveOptions);\n\n\tlet processor = processorCache.get(cacheKey);\n\n\tif (!processor) {\n\t\tprocessor = createProcessor(effectiveOptions);\n\t\tprocessorCache.set(cacheKey, processor);\n\t}\n\n\treturn processor as ReturnType<typeof createProcessor>;\n}\n\nexport function sanitizeMdx(input: string, options?: RehypeSanitizeOptions): string {\n\treturn String(getProcessor(options).processSync(input))\n\t\t.replaceAll(/\\s+/g, ' ') // Normalize whitespace\n\t\t.trim();\n}\n","import type { Options as RetextSmartypantsOptions } from 'retext-smartypants';\n\nimport { hash } from 'ohash';\nimport { retext } from 'retext';\nimport retextSmartypants from 'retext-smartypants';\n\n// Cache frozen processors by options hash\nconst processorCache = new Map<string, unknown>();\n\nfunction createProcessor(options?: RetextSmartypantsOptions) {\n\treturn retext().use(retextSmartypants, options).freeze();\n}\n\nfunction getProcessor(options?: RetextSmartypantsOptions) {\n\tconst cacheKey = options ? hash(options) : '';\n\n\tlet processor = processorCache.get(cacheKey);\n\n\tif (!processor) {\n\t\tprocessor = createProcessor(options);\n\t\tprocessorCache.set(cacheKey, processor);\n\t}\n\n\treturn processor as ReturnType<typeof createProcessor>;\n}\n\nexport function stylizeText(input: string, options?: RetextSmartypantsOptions): string {\n\treturn String(getProcessor(options).processSync(input)).trim();\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,33 +1,18 @@
1
1
  {
2
2
  "name": "@xsynaptic/unified-tools",
3
- "version": "4.2.1",
3
+ "version": "5.0.0",
4
4
  "description": "A common set of tools for transforming and manipulating markup and text",
5
- "exports": {
6
- ".": {
7
- "types": "./dist/index.d.mts",
8
- "import": "./dist/index.mjs"
9
- }
10
- },
11
- "main": "dist/index.mjs",
12
- "types": "dist/index.d.mts",
13
- "sideEffects": false,
14
- "files": [
15
- "dist/**/*",
16
- "src/**/*"
17
- ],
18
5
  "type": "module",
19
- "scripts": {
20
- "build": "tsdown",
21
- "build-tsc": "tsc --build",
22
- "check-types": "tsc --project tsconfig.json --noemit",
23
- "lint": "eslint",
24
- "format": "prettier --write . && eslint --fix && tsc --noEmit --extendedDiagnostics",
25
- "test": "vitest",
26
- "prepublishOnly": "pnpm build"
27
- },
6
+ "license": "MIT",
7
+ "author": "Alexander Synaptic <x@synapticism.com>",
28
8
  "repository": {
29
9
  "type": "git",
30
- "url": "git+https://github.com/xsynaptic/unified-tools.git"
10
+ "url": "git+https://github.com/xsynaptic/astro-lab.git",
11
+ "directory": "packages/unified-tools"
12
+ },
13
+ "homepage": "https://github.com/xsynaptic/astro-lab/tree/main/packages/unified-tools#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/xsynaptic/astro-lab/issues"
31
16
  },
32
17
  "keywords": [
33
18
  "unified",
@@ -36,38 +21,35 @@
36
21
  "remark",
37
22
  "remark-plugin"
38
23
  ],
39
- "author": "Alexander Synaptic <x@synapticism.com>",
40
- "bugs": {
41
- "url": "https://github.com/xsynaptic/unified-tools/issues"
24
+ "main": "./dist/index.mjs",
25
+ "types": "./dist/index.d.mts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.mts",
29
+ "import": "./dist/index.mjs"
30
+ }
42
31
  },
43
- "homepage": "https://github.com/xsynaptic/unified-tools#readme",
44
- "license": "MIT",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "sideEffects": false,
45
36
  "dependencies": {
37
+ "ohash": "^2.0.11",
46
38
  "rehype-parse": "^9.0.1",
47
39
  "rehype-sanitize": "^6.0.0",
48
40
  "rehype-stringify": "^10.0.1",
49
- "rehype-wrap-cjk": "^2.1.1",
50
41
  "remark-mdx": "^3.1.1",
51
42
  "remark-parse": "^11.0.0",
52
43
  "remark-rehype": "^11.1.2",
53
44
  "remark-smartypants": "^3.0.2",
54
45
  "retext": "^9.0.0",
55
46
  "retext-smartypants": "^6.2.0",
56
- "unified": "^11.0.5"
47
+ "unified": "^11.0.5",
48
+ "@xsynaptic/rehype-wrap-cjk": "3.0.0"
57
49
  },
58
- "devDependencies": {
59
- "@eslint/js": "^9.39.4",
60
- "@types/jest": "^30.0.0",
61
- "eslint": "^9.39.4",
62
- "eslint-plugin-perfectionist": "^5.9.0",
63
- "eslint-plugin-unicorn": "^64.0.0",
64
- "globals": "^17.6.0",
65
- "ohash": "^2.0.11",
66
- "prettier": "^3.8.3",
67
- "tsdown": "^0.22.0",
68
- "typescript": "^6.0.3",
69
- "typescript-eslint": "^8.59.3",
70
- "vitest": "^4.1.6"
71
- },
72
- "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
73
- }
50
+ "scripts": {
51
+ "build": "tsdown",
52
+ "dev": "tsdown --watch",
53
+ "check-types": "tsc --noEmit"
54
+ }
55
+ }
@@ -1 +0,0 @@
1
- import{createHash as e}from"node:crypto";function t(e){return typeof e==`string`?`'${e}'`:new n().serialize(e)}const n=function(){class e{#e=new Map;compare(e,t){let n=typeof e,r=typeof t;return n===`string`&&r===`string`?e.localeCompare(t):n===`number`&&r===`number`?e-t:String.prototype.localeCompare.call(this.serialize(e,!0),this.serialize(t,!0))}serialize(e,t){if(e===null)return`null`;switch(typeof e){case`string`:return t?e:`'${e}'`;case`bigint`:return`${e}n`;case`object`:return this.$object(e);case`function`:return this.$function(e)}return String(e)}serializeObject(e){let t=Object.prototype.toString.call(e);if(t!==`[object Object]`)return this.serializeBuiltInType(t.length<10?`unknown:${t}`:t.slice(8,-1),e);let n=e.constructor,r=n===Object||n===void 0?``:n.name;if(r!==``&&globalThis[r]===n)return this.serializeBuiltInType(r,e);if(typeof e.toJSON==`function`){let t=e.toJSON();return r+(typeof t==`object`&&t?this.$object(t):`(${this.serialize(t)})`)}return this.serializeObjectEntries(r,Object.entries(e))}serializeBuiltInType(e,t){let n=this[`$`+e];if(n)return n.call(this,t);if(typeof t?.entries==`function`)return this.serializeObjectEntries(e,t.entries());throw Error(`Cannot serialize ${e}`)}serializeObjectEntries(e,t){let n=Array.from(t).sort((e,t)=>this.compare(e[0],t[0])),r=`${e}{`;for(let e=0;e<n.length;e++){let[t,i]=n[e];r+=`${this.serialize(t,!0)}:${this.serialize(i)}`,e<n.length-1&&(r+=`,`)}return r+`}`}$object(e){let t=this.#e.get(e);return t===void 0&&(this.#e.set(e,`#${this.#e.size}`),t=this.serializeObject(e),this.#e.set(e,t)),t}$function(e){let t=Function.prototype.toString.call(e);return t.slice(-15)===`[native code] }`?`${e.name||``}()[native]`:`${e.name}(${e.length})${t.replace(/\s*\n\s*/g,``)}`}$Array(e){let t=`[`;for(let n=0;n<e.length;n++)t+=this.serialize(e[n]),n<e.length-1&&(t+=`,`);return t+`]`}$Date(e){try{return`Date(${e.toISOString()})`}catch{return`Date(null)`}}$ArrayBuffer(e){return`ArrayBuffer[${new Uint8Array(e).join(`,`)}]`}$Set(e){return`Set${this.$Array(Array.from(e).sort((e,t)=>this.compare(e,t)))}`}$Map(e){return this.serializeObjectEntries(`Map`,e.entries())}}for(let t of[`Error`,`RegExp`,`URL`])e.prototype[`$`+t]=function(e){return`${t}(${e})`};for(let t of[`Int8Array`,`Uint8Array`,`Uint8ClampedArray`,`Int16Array`,`Uint16Array`,`Int32Array`,`Uint32Array`,`Float32Array`,`Float64Array`])e.prototype[`$`+t]=function(e){return`${t}[${e.join(`,`)}]`};for(let t of[`BigInt64Array`,`BigUint64Array`])e.prototype[`$`+t]=function(e){return`${t}[${e.join(`n,`)}${e.length>0?`n`:``}]`};return e}(),r=globalThis.process?.getBuiltinModule?.(`crypto`)?.hash,i=`sha256`,a=`base64url`;function o(t){if(r)return r(i,t,a);let n=e(i).update(t);return globalThis.process?.versions?.webcontainer?n.digest().toString(a):n.digest(a)}function s(e){return o(t(e))}export{s as t};
@@ -1,15 +0,0 @@
1
- import { RehypeWrapCjkOptions } from "rehype-wrap-cjk";
2
-
3
- //#region src/html-cjk.d.ts
4
- declare function wrapChinese(input: string): string;
5
- declare function wrapJapanese(input: string): string;
6
- declare function wrapKorean(input: string): string;
7
- declare function wrapCjk({
8
- input,
9
- wrapCjkOptions
10
- }: {
11
- input: string;
12
- wrapCjkOptions?: Partial<RehypeWrapCjkOptions>;
13
- }): string;
14
- //#endregion
15
- export { wrapChinese, wrapCjk, wrapJapanese, wrapKorean };
package/dist/html-cjk.mjs DELETED
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-Dfh4058I.mjs";import t from"rehype-parse";import n from"rehype-sanitize";import r from"rehype-stringify";import{rehypeWrapCjk as i}from"rehype-wrap-cjk";import{unified as a}from"unified";const o=new Map;function s(e){return a().use(t,{fragment:!0}).use(i,e).use(n).use(r).freeze()}function c(){return a().use(t,{fragment:!0}).use(n).use(r).freeze()}function l(t){let n=t?e(t):``,r=o.get(n);return r||(r=t?s(t):c(),o.set(n,r)),r}const u={attribute:`lang`,value:`zh`},d={attribute:`lang`,value:`ja`},f={attribute:`lang`,value:`ko`};function p(e){return l(u).processSync(e).toString()}function m(e){return l(d).processSync(e).toString()}function h(e){return l(f).processSync(e).toString()}function g({input:e,wrapCjkOptions:t}){return l(t).processSync(e).toString()}export{p as wrapChinese,g as wrapCjk,m as wrapJapanese,h as wrapKorean};
package/dist/html.d.mts DELETED
@@ -1,7 +0,0 @@
1
- import { Options } from "rehype-sanitize";
2
-
3
- //#region src/html.d.ts
4
- declare function sanitizeHtml(input: string, options?: Options): string;
5
- declare function stripTags(input: string, options?: Options): string;
6
- //#endregion
7
- export { sanitizeHtml, stripTags };
package/dist/html.mjs DELETED
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-Dfh4058I.mjs";import t from"rehype-parse";import n from"rehype-sanitize";import r from"rehype-stringify";import{unified as i}from"unified";const a=new Map;function o(e){return i().use(t,{fragment:!0}).use(n,e).use(r).freeze()}function s(t){let n=t?e(t):``,r=a.get(n);return r||(r=o(t),a.set(n,r)),r}function c(e,t){let n=s(t);return String(n.processSync(e))}function l(e,t){return c(e,{...t,tagNames:[]})}export{c as sanitizeHtml,l as stripTags};
@@ -1,13 +0,0 @@
1
- import { RehypeWrapCjkOptions } from "rehype-wrap-cjk";
2
-
3
- //#region src/markdown.d.ts
4
- interface TransformMarkdownOptions {
5
- input: string;
6
- wrapCjkOptions?: Partial<RehypeWrapCjkOptions> | undefined;
7
- }
8
- declare function transformMarkdown({
9
- input,
10
- wrapCjkOptions
11
- }: TransformMarkdownOptions): string;
12
- //#endregion
13
- export { transformMarkdown };
package/dist/markdown.mjs DELETED
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-Dfh4058I.mjs";import t from"rehype-sanitize";import n from"rehype-stringify";import{rehypeWrapCjk as r}from"rehype-wrap-cjk";import{unified as i}from"unified";import a from"remark-parse";import o from"remark-rehype";import s from"remark-smartypants";const c=new Map;function l(e){return i().use(a).use(s).use(o).use(r,e).use(t).use(n).freeze()}function u(){return i().use(a).use(s).use(o).use(t).use(n).freeze()}function d(t){let n=t?e(t):``,r=c.get(n);return r||(r=t?l(t):u(),c.set(n,r)),r}function f({input:e,wrapCjkOptions:t}){return d(t).processSync(e).toString().trim()}export{f as transformMarkdown};
package/dist/mdx.d.mts DELETED
@@ -1,6 +0,0 @@
1
- import { Options } from "rehype-sanitize";
2
-
3
- //#region src/mdx.d.ts
4
- declare function sanitizeMdx(input: string, options?: Options): string;
5
- //#endregion
6
- export { sanitizeMdx };
package/dist/mdx.mjs DELETED
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-Dfh4058I.mjs";import t from"rehype-sanitize";import n from"rehype-stringify";import{unified as r}from"unified";import i from"remark-parse";import a from"remark-rehype";import o from"remark-mdx";const s=new Map,c={tagNames:[]};function l(e){return r().use(i).use(o).use(a).use(t,e).use(n).freeze()}function u(t){let n=t??c,r=e(n),i=s.get(r);return i||(i=l(n),s.set(r,i)),i}function d(e,t){return String(u(t).processSync(e)).replaceAll(/\s+/g,` `).trim()}export{d as sanitizeMdx};
package/dist/text.d.mts DELETED
@@ -1,6 +0,0 @@
1
- import { Options } from "retext-smartypants";
2
-
3
- //#region src/text.d.ts
4
- declare function stylizeText(input: string, options?: Options): string;
5
- //#endregion
6
- export { stylizeText };
package/dist/text.mjs DELETED
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-Dfh4058I.mjs";import{retext as t}from"retext";import n from"retext-smartypants";const r=new Map;function i(e){return t().use(n,e).freeze()}function a(t){let n=t?e(t):``,a=r.get(n);return a||(a=i(t),r.set(n,a)),a}function o(e,t){return String(a(t).processSync(e)).trim()}export{o as stylizeText};
@@ -1,9 +0,0 @@
1
- import { transformMarkdown } from '../markdown.js';
2
-
3
- describe('transformMarkdown', () => {
4
- test('converts markdown to sanitized HTML with smartypants', () => {
5
- expect(transformMarkdown({ input: '"Hello" -- world' })).toBe(
6
- '<p>\u201CHello\u201D \u2014 world</p>'
7
- );
8
- });
9
- });
@@ -1,9 +0,0 @@
1
- import { sanitizeMdx } from '../mdx.js';
2
-
3
- describe('sanitizeMdx', () => {
4
- test('strips MDX components from content', () => {
5
- expect(sanitizeMdx('Text with <Component prop="value" /> inside')).toBe(
6
- 'Text with inside'
7
- );
8
- });
9
- });
@@ -1,22 +0,0 @@
1
- import { stripTags } from '../html.js';
2
-
3
- const sampleText = [
4
- [`No change to plain text`, `No change to plain text`],
5
- [`Emphasis <em>should</em> be removed`, `Emphasis should be removed`],
6
- [
7
- `This <a href="https://example.com">link</a> should be stripped`,
8
- `This link should be stripped`,
9
- ],
10
- [
11
- `The following MDX component should not appear at all: <Img src="./test1.jpg" />`,
12
- `The following MDX component should not appear at all: `,
13
- ],
14
- ] as const;
15
-
16
- describe('html tags should be stripped', () => {
17
- for (const [input, output] of sampleText) {
18
- test(input, () => {
19
- expect(stripTags(input)).toEqual(output);
20
- });
21
- }
22
- });
@@ -1,9 +0,0 @@
1
- import { stylizeText } from '../text.js';
2
-
3
- describe('stylizeText', () => {
4
- test('converts straight quotes to curly quotes', () => {
5
- expect(stylizeText('"Hello" -- world')).toBe(
6
- '\u201CHello\u201D \u2014 world'
7
- );
8
- });
9
- });
package/src/html-cjk.ts DELETED
@@ -1,81 +0,0 @@
1
- import type { RehypeWrapCjkOptions } from 'rehype-wrap-cjk';
2
-
3
- import { hash } from 'ohash';
4
- import rehypeParse from 'rehype-parse';
5
- import rehypeSanitize from 'rehype-sanitize';
6
- import rehypeStringify from 'rehype-stringify';
7
- import { rehypeWrapCjk } from 'rehype-wrap-cjk';
8
- import { unified } from 'unified';
9
-
10
- // Cache frozen processors by options hash
11
- const processorCache = new Map<string, unknown>();
12
-
13
- // Processor with CJK wrapping
14
- function createProcessorWithCjk(wrapCjkOptions: Partial<RehypeWrapCjkOptions>) {
15
- return unified()
16
- .use(rehypeParse, { fragment: true })
17
- .use(rehypeWrapCjk, wrapCjkOptions)
18
- .use(rehypeSanitize)
19
- .use(rehypeStringify)
20
- .freeze();
21
- }
22
-
23
- // Processor without CJK wrapping
24
- function createProcessorWithoutCjk() {
25
- return unified()
26
- .use(rehypeParse, { fragment: true })
27
- .use(rehypeSanitize)
28
- .use(rehypeStringify)
29
- .freeze();
30
- }
31
-
32
- function getProcessor(wrapCjkOptions?: Partial<RehypeWrapCjkOptions>) {
33
- const cacheKey = wrapCjkOptions ? hash(wrapCjkOptions) : '';
34
-
35
- let processor = processorCache.get(cacheKey);
36
-
37
- if (!processor) {
38
- processor = wrapCjkOptions
39
- ? createProcessorWithCjk(wrapCjkOptions)
40
- : createProcessorWithoutCjk();
41
- processorCache.set(cacheKey, processor);
42
- }
43
-
44
- return processor as ReturnType<typeof createProcessorWithoutCjk>;
45
- }
46
-
47
- // Pre-defined options for common language codes
48
- const zhOptions: Partial<RehypeWrapCjkOptions> = {
49
- attribute: 'lang',
50
- value: 'zh',
51
- };
52
- const jaOptions: Partial<RehypeWrapCjkOptions> = {
53
- attribute: 'lang',
54
- value: 'ja',
55
- };
56
- const koOptions: Partial<RehypeWrapCjkOptions> = {
57
- attribute: 'lang',
58
- value: 'ko',
59
- };
60
-
61
- export function wrapChinese(input: string): string {
62
- return getProcessor(zhOptions).processSync(input).toString();
63
- }
64
-
65
- export function wrapJapanese(input: string): string {
66
- return getProcessor(jaOptions).processSync(input).toString();
67
- }
68
-
69
- export function wrapKorean(input: string): string {
70
- return getProcessor(koOptions).processSync(input).toString();
71
- }
72
-
73
- export function wrapCjk({
74
- input,
75
- wrapCjkOptions,
76
- }: {
77
- input: string;
78
- wrapCjkOptions?: Partial<RehypeWrapCjkOptions>;
79
- }): string {
80
- return getProcessor(wrapCjkOptions).processSync(input).toString();
81
- }
package/src/html.ts DELETED
@@ -1,48 +0,0 @@
1
- import type { Options as RehypeSanitizeOptions } from 'rehype-sanitize';
2
-
3
- import { hash } from 'ohash';
4
- import rehypeParse from 'rehype-parse';
5
- import rehypeSanitize from 'rehype-sanitize';
6
- import rehypeStringify from 'rehype-stringify';
7
- import { unified } from 'unified';
8
-
9
- // Cache frozen processors by options hash
10
- const processorCache = new Map<string, unknown>();
11
-
12
- function createProcessor(options?: RehypeSanitizeOptions) {
13
- return unified()
14
- .use(rehypeParse, { fragment: true })
15
- .use(rehypeSanitize, options)
16
- .use(rehypeStringify)
17
- .freeze();
18
- }
19
-
20
- function getProcessor(options?: RehypeSanitizeOptions) {
21
- const cacheKey = options ? hash(options) : '';
22
-
23
- let processor = processorCache.get(cacheKey);
24
-
25
- if (!processor) {
26
- processor = createProcessor(options);
27
- processorCache.set(cacheKey, processor);
28
- }
29
-
30
- return processor as ReturnType<typeof createProcessor>;
31
- }
32
-
33
- export function sanitizeHtml(
34
- input: string,
35
- options?: RehypeSanitizeOptions
36
- ): string {
37
- const processor = getProcessor(options);
38
-
39
- return String(processor.processSync(input));
40
- }
41
-
42
- // Handy shortcut for when you just want to strip tags from text
43
- export function stripTags(
44
- input: string,
45
- options?: RehypeSanitizeOptions
46
- ): string {
47
- return sanitizeHtml(input, { ...options, tagNames: [] });
48
- }
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export { wrapChinese, wrapCjk, wrapJapanese, wrapKorean } from './html-cjk.js';
2
- export { sanitizeHtml, stripTags } from './html.js';
3
- export { transformMarkdown } from './markdown.js';
4
- export { sanitizeMdx } from './mdx.js';
5
- export { stylizeText } from './text.js';
6
- export { defaultSchema } from 'rehype-sanitize';
package/src/markdown.ts DELETED
@@ -1,65 +0,0 @@
1
- import type { RehypeWrapCjkOptions } from 'rehype-wrap-cjk';
2
-
3
- import { hash } from 'ohash';
4
- import rehypeSanitize from 'rehype-sanitize';
5
- import rehypeStringify from 'rehype-stringify';
6
- import { rehypeWrapCjk } from 'rehype-wrap-cjk';
7
- import remarkParse from 'remark-parse';
8
- import remarkRehype from 'remark-rehype';
9
- import remarkSmartyPants from 'remark-smartypants';
10
- import { unified } from 'unified';
11
-
12
- interface TransformMarkdownOptions {
13
- input: string;
14
- wrapCjkOptions?: Partial<RehypeWrapCjkOptions> | undefined;
15
- }
16
-
17
- // Cache frozen processors by options hash
18
- // Using unknown to avoid unified's complex generic types
19
- const processorCache = new Map<string, unknown>();
20
-
21
- // Processor with CJK wrapping
22
- function createProcessorWithCjk(wrapCjkOptions: Partial<RehypeWrapCjkOptions>) {
23
- return unified()
24
- .use(remarkParse)
25
- .use(remarkSmartyPants)
26
- .use(remarkRehype)
27
- .use(rehypeWrapCjk, wrapCjkOptions)
28
- .use(rehypeSanitize)
29
- .use(rehypeStringify)
30
- .freeze();
31
- }
32
-
33
- // Processor without CJK wrapping
34
- function createProcessorWithoutCjk() {
35
- return unified()
36
- .use(remarkParse)
37
- .use(remarkSmartyPants)
38
- .use(remarkRehype)
39
- .use(rehypeSanitize)
40
- .use(rehypeStringify)
41
- .freeze();
42
- }
43
-
44
- function getProcessor(wrapCjkOptions?: Partial<RehypeWrapCjkOptions>) {
45
- // Generate stable cache key from options (empty string for no options)
46
- const cacheKey = wrapCjkOptions ? hash(wrapCjkOptions) : '';
47
-
48
- let processor = processorCache.get(cacheKey);
49
-
50
- if (!processor) {
51
- processor = wrapCjkOptions
52
- ? createProcessorWithCjk(wrapCjkOptions)
53
- : createProcessorWithoutCjk();
54
- processorCache.set(cacheKey, processor);
55
- }
56
-
57
- return processor as ReturnType<typeof createProcessorWithoutCjk>;
58
- }
59
-
60
- export function transformMarkdown({
61
- input,
62
- wrapCjkOptions,
63
- }: TransformMarkdownOptions): string {
64
- return getProcessor(wrapCjkOptions).processSync(input).toString().trim();
65
- }
package/src/mdx.ts DELETED
@@ -1,48 +0,0 @@
1
- import type { Options as RehypeSanitizeOptions } from 'rehype-sanitize';
2
-
3
- import { hash } from 'ohash';
4
- import rehypeSanitize from 'rehype-sanitize';
5
- import rehypeStringify from 'rehype-stringify';
6
- import remarkMdx from 'remark-mdx';
7
- import remarkParse from 'remark-parse';
8
- import remarkRehype from 'remark-rehype';
9
- import { unified } from 'unified';
10
-
11
- // Cache frozen processors by options hash
12
- const processorCache = new Map<string, unknown>();
13
-
14
- // Default options for stripping all tags
15
- const defaultOptions: RehypeSanitizeOptions = { tagNames: [] };
16
-
17
- function createProcessor(options: RehypeSanitizeOptions) {
18
- return unified()
19
- .use(remarkParse)
20
- .use(remarkMdx)
21
- .use(remarkRehype)
22
- .use(rehypeSanitize, options)
23
- .use(rehypeStringify)
24
- .freeze();
25
- }
26
-
27
- function getProcessor(options?: RehypeSanitizeOptions) {
28
- const effectiveOptions = options ?? defaultOptions;
29
- const cacheKey = hash(effectiveOptions);
30
-
31
- let processor = processorCache.get(cacheKey);
32
-
33
- if (!processor) {
34
- processor = createProcessor(effectiveOptions);
35
- processorCache.set(cacheKey, processor);
36
- }
37
-
38
- return processor as ReturnType<typeof createProcessor>;
39
- }
40
-
41
- export function sanitizeMdx(
42
- input: string,
43
- options?: RehypeSanitizeOptions
44
- ): string {
45
- return String(getProcessor(options).processSync(input))
46
- .replaceAll(/\s+/g, ' ') // Normalize whitespace
47
- .trim();
48
- }
package/src/text.ts DELETED
@@ -1,32 +0,0 @@
1
- import type { Options as RetextSmartypantsOptions } from 'retext-smartypants';
2
-
3
- import { hash } from 'ohash';
4
- import { retext } from 'retext';
5
- import retextSmartypants from 'retext-smartypants';
6
-
7
- // Cache frozen processors by options hash
8
- const processorCache = new Map<string, unknown>();
9
-
10
- function createProcessor(options?: RetextSmartypantsOptions) {
11
- return retext().use(retextSmartypants, options).freeze();
12
- }
13
-
14
- function getProcessor(options?: RetextSmartypantsOptions) {
15
- const cacheKey = options ? hash(options) : '';
16
-
17
- let processor = processorCache.get(cacheKey);
18
-
19
- if (!processor) {
20
- processor = createProcessor(options);
21
- processorCache.set(cacheKey, processor);
22
- }
23
-
24
- return processor as ReturnType<typeof createProcessor>;
25
- }
26
-
27
- export function stylizeText(
28
- input: string,
29
- options?: RetextSmartypantsOptions
30
- ): string {
31
- return String(getProcessor(options).processSync(input)).trim();
32
- }