@purestack/ts-highlight 1.1.15 → 1.1.17
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 +23 -1
- package/dist/ts-highlight.d.mts +21 -5
- package/dist/ts-highlight.d.mts.map +1 -1
- package/dist/ts-highlight.mjs +66 -17
- package/dist/ts-highlight.mjs.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -14,13 +14,35 @@ initializes the selected engine once per build/session, and respects
|
|
|
14
14
|
all use the same highlighter. Language aliases, inferred file extensions,
|
|
15
15
|
tagged TypeScript templates, and light/dark Shiki themes are preserved.
|
|
16
16
|
|
|
17
|
+
The plugin owns the `highlightjs` default and accepts `highlightjs` or `shiki`
|
|
18
|
+
when its factory is selected. Core treats `mdx.highlighter` as an optional,
|
|
19
|
+
provider-owned identifier. Omitting this plugin does not select or normalize an
|
|
20
|
+
engine. Custom providers can use their own identifiers; replacing the default
|
|
21
|
+
factory avoids initializing or validating the standard backends.
|
|
22
|
+
|
|
17
23
|
Pass `createShiki(options)` or `createHighlightJs()` to customize the native
|
|
18
24
|
backend. Both may return an instance synchronously or asynchronously; the
|
|
19
25
|
unused factory is never called. The Shiki options contain the default
|
|
20
26
|
languages and themes. For highlight.js, prefer a new instance with explicitly
|
|
21
27
|
registered grammars to avoid changing the shared registry.
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
The default Shiki backend caches highlighted HTML in the OS temporary
|
|
30
|
+
directory (`purestack-cache/shiki`), keyed by Shiki's version, the themes, the loaded languages, and the
|
|
31
|
+
code. Repeat builds reuse it, and Shiki loads only when a block is not cached.
|
|
32
|
+
Builds run with `--no-cache` skip it. Output from a custom `createShiki`
|
|
33
|
+
factory is never cached, because its grammars are unknown.
|
|
34
|
+
|
|
35
|
+
Storage uses `PersistentCache` from `@purestack/ts-util-node`: 32 lazy JSON shards,
|
|
36
|
+
buffered writes merged under shard locks, bounded memory, and cleanup of entries
|
|
37
|
+
unused for 30 days. Large entries stay in separate files. PureStack flushes after
|
|
38
|
+
build and development render batches. Standalone users should await
|
|
39
|
+
`highlighter.flush?.()` after rendering their batch; the periodic flush timer
|
|
40
|
+
does not keep Node alive.
|
|
41
|
+
Other build-time plugins and components can use the same utility with their own
|
|
42
|
+
namespace, version, and input keys.
|
|
43
|
+
|
|
44
|
+
`createMdxHighlighter(themes?, langs?, { create?, cache? })`,
|
|
45
|
+
`createHljsHighlighter`, `DEFAULT_MDX_CODE_LANGS`, and
|
|
24
46
|
`DEFAULT_MDX_CODE_THEMES` are exported for standalone use. The shared
|
|
25
47
|
`MdxCodeHighlighter` contract is exported by `ts-common` and `ts-ssg`.
|
|
26
48
|
|
package/dist/ts-highlight.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @purestack/ts-highlight v1.1.
|
|
1
|
+
/*! @purestack/ts-highlight v1.1.17 | (c) 2026 Ahmed Yasin Koculu | MIT License */
|
|
2
2
|
|
|
3
|
-
import { MdxCodeHighlighter, SiteMdxConfig } from "@purestack/ts-common";
|
|
3
|
+
import { MdxCodeHighlighter, SiteConfigResolution, SiteMdxConfig } from "@purestack/ts-common";
|
|
4
4
|
import { HLJSApi } from "highlight.js";
|
|
5
5
|
import { Highlighter, createHighlighter } from "shiki";
|
|
6
6
|
//#region packages/ts-highlight/src/shiki.d.ts
|
|
@@ -12,7 +12,13 @@ interface MdxCodeThemes {
|
|
|
12
12
|
type MdxCodeLangs = string[];
|
|
13
13
|
export declare const DEFAULT_MDX_CODE_THEMES: MdxCodeThemes;
|
|
14
14
|
export declare const DEFAULT_MDX_CODE_LANGS: MdxCodeLangs;
|
|
15
|
-
|
|
15
|
+
interface MdxHighlighterOptions {
|
|
16
|
+
/** A custom factory; its grammars are unknown, so its output is never cached. */
|
|
17
|
+
create?: MdxShikiFactory;
|
|
18
|
+
/** `false` disables the persistent cache for this run. Defaults to `true`. */
|
|
19
|
+
cache?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function createMdxHighlighter(themes?: MdxCodeThemes, langs?: MdxCodeLangs, { create, cache: useCache }?: MdxHighlighterOptions): Promise<MdxCodeHighlighter>;
|
|
16
22
|
//#endregion
|
|
17
23
|
//#region packages/ts-highlight/src/highlightjs.d.ts
|
|
18
24
|
export declare function createHljsHighlighter(highlighter?: HLJSApi): MdxCodeHighlighter;
|
|
@@ -26,10 +32,20 @@ export interface HighlightPluginOptions {
|
|
|
26
32
|
export declare function highlightPlugin(options?: HighlightPluginOptions): {
|
|
27
33
|
name: string;
|
|
28
34
|
defaults: boolean;
|
|
35
|
+
config: {
|
|
36
|
+
normalize({ config }: SiteConfigResolution): {
|
|
37
|
+
mdx: {
|
|
38
|
+
highlighter: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
29
42
|
markdown: {
|
|
30
|
-
createHighlighter(
|
|
43
|
+
createHighlighter({ mdx, cache }: {
|
|
44
|
+
mdx: Partial<SiteMdxConfig>;
|
|
45
|
+
cache: boolean;
|
|
46
|
+
}): Promise<import("@purestack/ts-common").MdxCodeHighlighter | undefined>;
|
|
31
47
|
};
|
|
32
48
|
};
|
|
33
49
|
//#endregion
|
|
34
|
-
export type { MdxCodeLangs, MdxCodeThemes, MdxShikiFactory };
|
|
50
|
+
export type { MdxCodeLangs, MdxCodeThemes, MdxHighlighterOptions, MdxShikiFactory };
|
|
35
51
|
//# sourceMappingURL=ts-highlight.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts-highlight.d.mts","names":[],"sources":["../src/shiki.ts","../src/highlightjs.ts","../src/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"ts-highlight.d.mts","names":[],"sources":["../src/shiki.ts","../src/highlightjs.ts","../src/index.ts"],"mappings":";;;;KAQY,mBACV,SAAS,kBAAkB,0BACxB,cAAc,QAAQ;UAEV;EACf;EACA;;KAGU;qBAEC,yBAAyB;qBAKzB,wBAAwB;UAIpB;;EAEf,SAAS;;EAET;;wBAGoB,qBACpB,SAAQ,eACR,QAAO,gBACL,QAAQ,OAAO,aAAmB,wBACnC,QAAQ;;;wBCzBK,sBACd,cAAa,UACZ;;;iBCAc;EACf,cAAc;EACd,0BAA0B,UAAU,QAAQ;;;wBAI9B,gBAAgB,UAAS;EAErC;EACA;EACA;IACE,YAAS,UAAa;MAElB;QAAO;;;;EAIb;IACQ,oBAAiB,KAAA;MAIrB,KAAK,QAAQ;MACb;QACD,uCAAA"}
|
package/dist/ts-highlight.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
/*! @purestack/ts-highlight v1.1.
|
|
1
|
+
/*! @purestack/ts-highlight v1.1.17 | (c) 2026 Ahmed Yasin Koculu | MIT License */
|
|
2
2
|
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { DEFAULT_CODE_LANGUAGES, normalizeCodeLanguage } from "@purestack/ts-common";
|
|
4
5
|
import hljs from "highlight.js";
|
|
5
6
|
import { createHighlighter } from "shiki";
|
|
7
|
+
import { PersistentCache } from "@purestack/ts-util-node";
|
|
6
8
|
|
|
7
9
|
//#region packages/ts-highlight/src/highlightjs.ts
|
|
8
10
|
const hljsLanguages = /* @__PURE__ */ new Map([
|
|
@@ -30,6 +32,26 @@ function escapeHtml(value) {
|
|
|
30
32
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region packages/ts-highlight/src/shikiCache.ts
|
|
37
|
+
const CACHE_SCHEMA = 1;
|
|
38
|
+
const SHIKI_VERSION = createRequire(import.meta.url)("shiki/package.json").version;
|
|
39
|
+
function openShikiCache(themes, languages) {
|
|
40
|
+
return new PersistentCache({
|
|
41
|
+
namespace: "shiki",
|
|
42
|
+
version: JSON.stringify([
|
|
43
|
+
CACHE_SCHEMA,
|
|
44
|
+
SHIKI_VERSION,
|
|
45
|
+
themes,
|
|
46
|
+
languages
|
|
47
|
+
]),
|
|
48
|
+
codec: {
|
|
49
|
+
encode: (html) => html,
|
|
50
|
+
decode: (data) => data.toString("utf8")
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
//#endregion
|
|
34
56
|
//#region packages/ts-highlight/src/shiki.ts
|
|
35
57
|
const DEFAULT_MDX_CODE_THEMES = {
|
|
@@ -38,21 +60,35 @@ const DEFAULT_MDX_CODE_THEMES = {
|
|
|
38
60
|
};
|
|
39
61
|
const DEFAULT_MDX_CODE_LANGS = DEFAULT_CODE_LANGUAGES;
|
|
40
62
|
const TYPESCRIPT_WITH_TAGS_LANG = "ts-tags";
|
|
41
|
-
async function createMdxHighlighter(themes = DEFAULT_MDX_CODE_THEMES, langs = DEFAULT_MDX_CODE_LANGS, create =
|
|
42
|
-
const
|
|
63
|
+
async function createMdxHighlighter(themes = DEFAULT_MDX_CODE_THEMES, langs = DEFAULT_MDX_CODE_LANGS, { create, cache: useCache = true } = {}) {
|
|
64
|
+
const options = {
|
|
43
65
|
themes: dedupe([themes.light, themes.dark]),
|
|
44
66
|
langs: resolveLoadedLanguages(langs)
|
|
67
|
+
};
|
|
68
|
+
const render = (highlighter, code, lang) => highlighter.codeToHtml(code, {
|
|
69
|
+
lang,
|
|
70
|
+
themes: {
|
|
71
|
+
light: themes.light,
|
|
72
|
+
dark: themes.dark
|
|
73
|
+
},
|
|
74
|
+
defaultColor: false
|
|
45
75
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
if (create || !useCache) {
|
|
77
|
+
const highlighter = await (create ?? createHighlighter)(options);
|
|
78
|
+
return { codeToHtml: (code, lang) => render(highlighter, code, resolveHighlightLanguage(lang)) };
|
|
79
|
+
}
|
|
80
|
+
const cache = openShikiCache(themes, options.langs);
|
|
81
|
+
let highlighter;
|
|
82
|
+
return {
|
|
83
|
+
flush: () => cache.flush(),
|
|
84
|
+
async codeToHtml(code, lang) {
|
|
85
|
+
const resolved = resolveHighlightLanguage(lang);
|
|
86
|
+
return cache.getOrSetAsync(`${resolved}\0${code}`, async () => {
|
|
87
|
+
highlighter ??= createHighlighter(options);
|
|
88
|
+
return render(await highlighter, code, resolved);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
56
92
|
}
|
|
57
93
|
function resolveLoadedLanguages(langs) {
|
|
58
94
|
const resolved = dedupe(langs.map(normalizeCodeLanguage));
|
|
@@ -74,13 +110,26 @@ function highlightPlugin(options = {}) {
|
|
|
74
110
|
return {
|
|
75
111
|
name: "purestack:highlight",
|
|
76
112
|
defaults: true,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
config: { normalize({ config }) {
|
|
114
|
+
return { mdx: { highlighter: backendIdentifier(config.mdx.highlighter) } };
|
|
115
|
+
} },
|
|
116
|
+
markdown: { async createHighlighter({ mdx, cache }) {
|
|
117
|
+
if (mdx.disableHighlighter) return void 0;
|
|
118
|
+
const backend = backendIdentifier(mdx.highlighter);
|
|
119
|
+
if (backend === "highlightjs") return createHljsHighlighter(await options.createHighlightJs?.());
|
|
120
|
+
if (backend === "shiki") return createMdxHighlighter(void 0, void 0, {
|
|
121
|
+
create: options.createShiki,
|
|
122
|
+
cache
|
|
123
|
+
});
|
|
124
|
+
throw new Error(`The standard highlighting plugin does not support backend "${backend}". Supply a custom markdown.createHighlighter provider.`);
|
|
81
125
|
} }
|
|
82
126
|
};
|
|
83
127
|
}
|
|
128
|
+
function backendIdentifier(value) {
|
|
129
|
+
if (value === void 0) return "highlightjs";
|
|
130
|
+
if (typeof value !== "string" || !value.trim()) throw new Error("mdx.highlighter must be a non-empty backend identifier.");
|
|
131
|
+
return value.trim();
|
|
132
|
+
}
|
|
84
133
|
|
|
85
134
|
//#endregion
|
|
86
135
|
export { DEFAULT_MDX_CODE_LANGS, DEFAULT_MDX_CODE_THEMES, createHljsHighlighter, createMdxHighlighter, highlightPlugin };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts-highlight.mjs","names":[],"sources":["../src/highlightjs.ts","../src/shiki.ts","../src/index.ts"],"sourcesContent":["import {\n type MdxCodeHighlighter,\n normalizeCodeLanguage,\n} from '@purestack/ts-common'\nimport hljs, { type HLJSApi } from 'highlight.js'\n\n// Use highlight.js names, or the closest grammar for Shiki-only languages.\nconst hljsLanguages = new Map([\n ['jsonc', 'json'],\n ['mdx', 'markdown'],\n ['objective-c', 'objectivec'],\n ['ts-tags', 'typescript'],\n])\n\nexport function createHljsHighlighter(\n highlighter: HLJSApi = hljs,\n): MdxCodeHighlighter {\n return {\n codeToHtml: (code: string, lang?: string) => {\n const normalizedLang = normalizeCodeLanguage(lang ?? '')\n const highlightLang = hljsLanguages.get(normalizedLang) ?? normalizedLang\n if (highlightLang && highlighter.getLanguage(highlightLang)) {\n const highlighted = highlighter.highlight(code, {\n language: highlightLang,\n ignoreIllegals: true,\n })\n return `<pre class=\"hljs\"><code class=\"hljs language-${highlighted.language}\">${highlighted.value}</code></pre>`\n }\n\n const escaped = escapeHtml(code)\n const langClass = normalizedLang ? ` language-${normalizedLang}` : ''\n return `<pre class=\"hljs\"><code class=\"hljs${langClass}\">${escaped}</code></pre>`\n },\n }\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n}\n","import {\n DEFAULT_CODE_LANGUAGES,\n type MdxCodeHighlighter,\n normalizeCodeLanguage,\n} from '@purestack/ts-common'\nimport { createHighlighter, type Highlighter } from 'shiki'\n\nexport type MdxShikiFactory = (\n options: Parameters<typeof createHighlighter>[0],\n) => Highlighter | Promise<Highlighter>\n\nexport interface MdxCodeThemes {\n light: string\n dark: string\n}\n\nexport type MdxCodeLangs = string[]\n\nexport const DEFAULT_MDX_CODE_THEMES: MdxCodeThemes = {\n light: 'github-light',\n dark: 'github-dark',\n}\n\nexport const DEFAULT_MDX_CODE_LANGS: MdxCodeLangs = DEFAULT_CODE_LANGUAGES\n\nconst TYPESCRIPT_WITH_TAGS_LANG = 'ts-tags'\n\nexport async function createMdxHighlighter(\n themes: MdxCodeThemes = DEFAULT_MDX_CODE_THEMES,\n langs: MdxCodeLangs = DEFAULT_MDX_CODE_LANGS,\n create: MdxShikiFactory = createHighlighter,\n): Promise<MdxCodeHighlighter> {\n const themeList = dedupe([themes.light, themes.dark])\n const langList = resolveLoadedLanguages(langs)\n const highlighter = await create({\n themes: themeList,\n langs: langList,\n })\n\n return {\n codeToHtml: (code: string, lang?: string) => {\n return highlighter.codeToHtml(code, {\n lang: resolveHighlightLanguage(lang),\n themes: { light: themes.light, dark: themes.dark },\n defaultColor: false,\n })\n },\n }\n}\n\nfunction resolveLoadedLanguages(langs: string[]) {\n const resolved = dedupe(langs.map(normalizeCodeLanguage))\n if (\n resolved.includes('typescript') &&\n !resolved.includes(TYPESCRIPT_WITH_TAGS_LANG)\n ) {\n resolved.push(TYPESCRIPT_WITH_TAGS_LANG)\n }\n return resolved\n}\n\nfunction resolveHighlightLanguage(lang: string | undefined) {\n const normalized = normalizeCodeLanguage(lang ?? 'text')\n return normalized === 'typescript'\n ? TYPESCRIPT_WITH_TAGS_LANG\n : normalized || 'text'\n}\n\nfunction dedupe(values: string[]) {\n return Array.from(new Set(values.filter((value) => value.length > 0)))\n}\n","import type { SiteMdxConfig } from '@purestack/ts-common'\nimport type { HLJSApi } from 'highlight.js'\nimport { createHljsHighlighter } from './highlightjs'\nimport { createMdxHighlighter, type MdxShikiFactory } from './shiki'\n\nexport { createHljsHighlighter } from './highlightjs'\nexport {\n createMdxHighlighter,\n DEFAULT_MDX_CODE_LANGS,\n DEFAULT_MDX_CODE_THEMES,\n type MdxCodeLangs,\n type MdxCodeThemes,\n type MdxShikiFactory,\n} from './shiki'\n\nexport interface HighlightPluginOptions {\n createShiki?: MdxShikiFactory\n createHighlightJs?: () => HLJSApi | Promise<HLJSApi>\n}\n\n/** The standard highlighter, replaceable through markdown.createHighlighter. */\nexport function highlightPlugin(options: HighlightPluginOptions = {}) {\n return {\n name: 'purestack:highlight',\n defaults: true,\n markdown: {\n async createHighlighter(config: Partial<SiteMdxConfig>) {\n if (config.disableHighlighter) return undefined\n if (config.highlighter === 'highlightjs')\n return createHljsHighlighter(await options.createHighlightJs?.())\n return createMdxHighlighter(undefined, undefined, options.createShiki)\n },\n },\n }\n}\n"],"mappings":";;;;;AAOA,MAAM,gCAAgB,IAAI,IAAI;CAC5B,CAAC,SAAS,MAAM;CAChB,CAAC,OAAO,UAAU;CAClB,CAAC,eAAe,YAAY;CAC5B,CAAC,WAAW,YAAY;AAC1B,CAAC;AAED,SAAgB,sBACd,cAAuB,MACH;CACpB,OAAO,EACL,aAAa,MAAc,SAAkB;EAC3C,MAAM,iBAAiB,sBAAsB,QAAQ,EAAE;EACvD,MAAM,gBAAgB,cAAc,IAAI,cAAc,KAAK;EAC3D,IAAI,iBAAiB,YAAY,YAAY,aAAa,GAAG;GAC3D,MAAM,cAAc,YAAY,UAAU,MAAM;IAC9C,UAAU;IACV,gBAAgB;GAClB,CAAC;GACD,OAAO,gDAAgD,YAAY,SAAS,IAAI,YAAY,MAAM;EACpG;EAEA,MAAM,UAAU,WAAW,IAAI;EAE/B,OAAO,sCADW,iBAAiB,aAAa,mBAAmB,GACZ,IAAI,QAAQ;CACrE,EACF;AACF;AAEA,SAAS,WAAW,OAAuB;CACzC,OAAO,MACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM;AAC3B;;;;ACvBA,MAAa,0BAAyC;CACpD,OAAO;CACP,MAAM;AACR;AAEA,MAAa,yBAAuC;AAEpD,MAAM,4BAA4B;AAElC,eAAsB,qBACpB,SAAwB,yBACxB,QAAsB,wBACtB,SAA0B,mBACG;CAG7B,MAAM,cAAc,MAAM,OAAO;EAC/B,QAHgB,OAAO,CAAC,OAAO,OAAO,OAAO,IAAI,CAGjC;EAChB,OAHe,uBAAuB,KAGxB;CAChB,CAAC;CAED,OAAO,EACL,aAAa,MAAc,SAAkB;EAC3C,OAAO,YAAY,WAAW,MAAM;GAClC,MAAM,yBAAyB,IAAI;GACnC,QAAQ;IAAE,OAAO,OAAO;IAAO,MAAM,OAAO;GAAK;GACjD,cAAc;EAChB,CAAC;CACH,EACF;AACF;AAEA,SAAS,uBAAuB,OAAiB;CAC/C,MAAM,WAAW,OAAO,MAAM,IAAI,qBAAqB,CAAC;CACxD,IACE,SAAS,SAAS,YAAY,KAC9B,CAAC,SAAS,SAAS,yBAAyB,GAE5C,SAAS,KAAK,yBAAyB;CAEzC,OAAO;AACT;AAEA,SAAS,yBAAyB,MAA0B;CAC1D,MAAM,aAAa,sBAAsB,QAAQ,MAAM;CACvD,OAAO,eAAe,eAClB,4BACA,cAAc;AACpB;AAEA,SAAS,OAAO,QAAkB;CAChC,OAAO,MAAM,KAAK,IAAI,IAAI,OAAO,QAAQ,UAAU,MAAM,SAAS,CAAC,CAAC,CAAC;AACvE;;;;;ACjDA,SAAgB,gBAAgB,UAAkC,CAAC,GAAG;CACpE,OAAO;EACL,MAAM;EACN,UAAU;EACV,UAAU,EACR,MAAM,kBAAkB,QAAgC;GACtD,IAAI,OAAO,oBAAoB,OAAO;GACtC,IAAI,OAAO,gBAAgB,eACzB,OAAO,sBAAsB,MAAM,QAAQ,oBAAoB,CAAC;GAClE,OAAO,qBAAqB,QAAW,QAAW,QAAQ,WAAW;EACvE,EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"ts-highlight.mjs","names":[],"sources":["../src/highlightjs.ts","../src/shikiCache.ts","../src/shiki.ts","../src/index.ts"],"sourcesContent":["import {\n type MdxCodeHighlighter,\n normalizeCodeLanguage,\n} from '@purestack/ts-common'\nimport hljs, { type HLJSApi } from 'highlight.js'\n\n// Use highlight.js names, or the closest grammar for Shiki-only languages.\nconst hljsLanguages = new Map([\n ['jsonc', 'json'],\n ['mdx', 'markdown'],\n ['objective-c', 'objectivec'],\n ['ts-tags', 'typescript'],\n])\n\nexport function createHljsHighlighter(\n highlighter: HLJSApi = hljs,\n): MdxCodeHighlighter {\n return {\n codeToHtml: (code: string, lang?: string) => {\n const normalizedLang = normalizeCodeLanguage(lang ?? '')\n const highlightLang = hljsLanguages.get(normalizedLang) ?? normalizedLang\n if (highlightLang && highlighter.getLanguage(highlightLang)) {\n const highlighted = highlighter.highlight(code, {\n language: highlightLang,\n ignoreIllegals: true,\n })\n return `<pre class=\"hljs\"><code class=\"hljs language-${highlighted.language}\">${highlighted.value}</code></pre>`\n }\n\n const escaped = escapeHtml(code)\n const langClass = normalizedLang ? ` language-${normalizedLang}` : ''\n return `<pre class=\"hljs\"><code class=\"hljs${langClass}\">${escaped}</code></pre>`\n },\n }\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n}\n","import { createRequire } from 'node:module'\nimport { PersistentCache } from '@purestack/ts-util-node'\n\n// Bump when the highlighter's rendering options change cached output.\nconst CACHE_SCHEMA = 1\nconst SHIKI_VERSION: string = createRequire(import.meta.url)(\n 'shiki/package.json',\n).version\n\nexport function openShikiCache(\n themes: Record<'light' | 'dark', string>,\n languages: readonly string[],\n) {\n return new PersistentCache<string>({\n namespace: 'shiki',\n version: JSON.stringify([CACHE_SCHEMA, SHIKI_VERSION, themes, languages]),\n codec: {\n encode: (html) => html,\n decode: (data) => data.toString('utf8'),\n },\n })\n}\n","import {\n DEFAULT_CODE_LANGUAGES,\n type MdxCodeHighlighter,\n normalizeCodeLanguage,\n} from '@purestack/ts-common'\nimport { createHighlighter, type Highlighter } from 'shiki'\nimport { openShikiCache } from './shikiCache'\n\nexport type MdxShikiFactory = (\n options: Parameters<typeof createHighlighter>[0],\n) => Highlighter | Promise<Highlighter>\n\nexport interface MdxCodeThemes {\n light: string\n dark: string\n}\n\nexport type MdxCodeLangs = string[]\n\nexport const DEFAULT_MDX_CODE_THEMES: MdxCodeThemes = {\n light: 'github-light',\n dark: 'github-dark',\n}\n\nexport const DEFAULT_MDX_CODE_LANGS: MdxCodeLangs = DEFAULT_CODE_LANGUAGES\n\nconst TYPESCRIPT_WITH_TAGS_LANG = 'ts-tags'\n\nexport interface MdxHighlighterOptions {\n /** A custom factory; its grammars are unknown, so its output is never cached. */\n create?: MdxShikiFactory\n /** `false` disables the persistent cache for this run. Defaults to `true`. */\n cache?: boolean\n}\n\nexport async function createMdxHighlighter(\n themes: MdxCodeThemes = DEFAULT_MDX_CODE_THEMES,\n langs: MdxCodeLangs = DEFAULT_MDX_CODE_LANGS,\n { create, cache: useCache = true }: MdxHighlighterOptions = {},\n): Promise<MdxCodeHighlighter> {\n const options = {\n themes: dedupe([themes.light, themes.dark]),\n langs: resolveLoadedLanguages(langs),\n }\n const render = (highlighter: Highlighter, code: string, lang: string) =>\n highlighter.codeToHtml(code, {\n lang,\n themes: { light: themes.light, dark: themes.dark },\n defaultColor: false,\n })\n\n if (create || !useCache) {\n const highlighter = await (create ?? createHighlighter)(options)\n return {\n codeToHtml: (code: string, lang?: string) =>\n render(highlighter, code, resolveHighlightLanguage(lang)),\n }\n }\n\n // Bundled grammars and themes are identified by name and Shiki's version, so\n // their output is reused across builds. Shiki loads on the first cache miss.\n const cache = openShikiCache(themes, options.langs)\n let highlighter: Promise<Highlighter> | undefined\n return {\n flush: () => cache.flush(),\n async codeToHtml(code: string, lang?: string) {\n const resolved = resolveHighlightLanguage(lang)\n return cache.getOrSetAsync(`${resolved}\\0${code}`, async () => {\n highlighter ??= createHighlighter(options)\n return render(await highlighter, code, resolved)\n })\n },\n }\n}\n\nfunction resolveLoadedLanguages(langs: string[]) {\n const resolved = dedupe(langs.map(normalizeCodeLanguage))\n if (\n resolved.includes('typescript') &&\n !resolved.includes(TYPESCRIPT_WITH_TAGS_LANG)\n ) {\n resolved.push(TYPESCRIPT_WITH_TAGS_LANG)\n }\n return resolved\n}\n\nfunction resolveHighlightLanguage(lang: string | undefined) {\n const normalized = normalizeCodeLanguage(lang ?? 'text')\n return normalized === 'typescript'\n ? TYPESCRIPT_WITH_TAGS_LANG\n : normalized || 'text'\n}\n\nfunction dedupe(values: string[]) {\n return Array.from(new Set(values.filter((value) => value.length > 0)))\n}\n","import type { SiteConfigResolution, SiteMdxConfig } from '@purestack/ts-common'\nimport type { HLJSApi } from 'highlight.js'\nimport { createHljsHighlighter } from './highlightjs'\nimport { createMdxHighlighter, type MdxShikiFactory } from './shiki'\n\nexport { createHljsHighlighter } from './highlightjs'\nexport {\n createMdxHighlighter,\n DEFAULT_MDX_CODE_LANGS,\n DEFAULT_MDX_CODE_THEMES,\n type MdxCodeLangs,\n type MdxCodeThemes,\n type MdxHighlighterOptions,\n type MdxShikiFactory,\n} from './shiki'\n\nexport interface HighlightPluginOptions {\n createShiki?: MdxShikiFactory\n createHighlightJs?: () => HLJSApi | Promise<HLJSApi>\n}\n\n/** The standard highlighter, replaceable through markdown.createHighlighter. */\nexport function highlightPlugin(options: HighlightPluginOptions = {}) {\n return {\n name: 'purestack:highlight',\n defaults: true,\n config: {\n normalize({ config }: SiteConfigResolution) {\n return {\n mdx: { highlighter: backendIdentifier(config.mdx.highlighter) },\n }\n },\n },\n markdown: {\n async createHighlighter({\n mdx,\n cache,\n }: {\n mdx: Partial<SiteMdxConfig>\n cache: boolean\n }) {\n if (mdx.disableHighlighter) return undefined\n const backend = backendIdentifier(mdx.highlighter)\n if (backend === 'highlightjs')\n return createHljsHighlighter(await options.createHighlightJs?.())\n if (backend === 'shiki')\n return createMdxHighlighter(undefined, undefined, {\n create: options.createShiki,\n cache,\n })\n throw new Error(\n `The standard highlighting plugin does not support backend \"${backend}\". Supply a custom markdown.createHighlighter provider.`,\n )\n },\n },\n }\n}\n\nfunction backendIdentifier(value: unknown): string {\n if (value === undefined) return 'highlightjs'\n if (typeof value !== 'string' || !value.trim())\n throw new Error('mdx.highlighter must be a non-empty backend identifier.')\n return value.trim()\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purestack/ts-highlight",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.17",
|
|
4
4
|
"packageManager": "yarn@4.9.2",
|
|
5
5
|
"module": "./dist/ts-highlight.mjs",
|
|
6
6
|
"types": "./dist/ts-highlight.d.mts",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"test": "vitest"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@purestack/ts-common": "^1.1.
|
|
42
|
+
"@purestack/ts-common": "^1.1.17",
|
|
43
|
+
"@purestack/ts-util-node": "^1.1.17",
|
|
43
44
|
"highlight.js": "^11.11.1",
|
|
44
45
|
"shiki": "^3.22.0"
|
|
45
46
|
}
|