@aparte/plugin-shiki 0.13.0 → 0.14.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/dist/core.d.ts CHANGED
@@ -8,21 +8,50 @@ import { type AparteConfig } from '@aparte/core';
8
8
  export interface ShikiHighlighterLike {
9
9
  /** The grammars this highlighter carries — read once, then cached. */
10
10
  getLoadedLanguages(): string[];
11
- /** Render `code` as highlighted HTML with a loaded grammar + theme. */
12
- codeToHtml(code: string, options: {
13
- lang: string;
14
- theme: string;
15
- }): string;
11
+ /** Render `code` as highlighted HTML with a loaded grammar and one theme, or a light/dark pair. */
12
+ codeToHtml(code: string, options: ShikiRenderOptions): string;
16
13
  }
14
+ /**
15
+ * A theme for each of the two colour schemes core switches between. A type literal
16
+ * rather than an interface: shiki's `themes` option is a string-keyed record, and only
17
+ * a literal type is implicitly assignable to one.
18
+ */
19
+ export type ShikiThemePair = {
20
+ light: string;
21
+ dark: string;
22
+ };
23
+ /** What either entry hands to `codeToHtml` — shiki's own option names, nothing invented. */
24
+ export type ShikiRenderOptions = {
25
+ lang: string;
26
+ theme: string;
27
+ } | {
28
+ lang: string;
29
+ themes: ShikiThemePair;
30
+ defaultColor: false;
31
+ };
17
32
  export interface ShikiCoreProviderOptions {
18
33
  /**
19
- * Theme to render with — the name of a theme your highlighter was built with.
20
- * Default `'github-dark'`.
34
+ * Theme to render with — the name of a theme your highlighter was built with — or a
35
+ * `{ light, dark }` pair of them, which follows core's `[data-aparte-theme="dark"]`
36
+ * switch. Default `'github-dark'`.
21
37
  */
22
- theme?: string;
38
+ theme?: string | ShikiThemePair;
23
39
  }
24
40
  /** Languages that mean "don't highlight", plus the empty string. */
25
41
  export declare const PLAINTEXT: Set<string>;
42
+ /** One theme renders with `theme`; a pair renders both colours per token and no default. */
43
+ export declare function renderOptions(theme: string | ShikiThemePair, lang: string): ShikiRenderOptions;
44
+ /**
45
+ * The stylesheet a theme pair needs, added to the document once.
46
+ *
47
+ * With `defaultColor: false` shiki writes both colours on every token as CSS variables
48
+ * (`--shiki-light`, `--shiki-dark`, plus `-bg` on the block) and sets no colour of its
49
+ * own, so someone has to read the right one. That someone is not core — core knows
50
+ * nothing about shiki's variables — and not the consumer either, who asked for a pair
51
+ * precisely so the block would follow the theme by itself. The switch is core's public
52
+ * theme contract, `[data-aparte-theme="dark"]`, documented in theme.css.
53
+ */
54
+ export declare function ensureThemePairStyles(): void;
26
55
  /**
27
56
  * Register a highlighter **you** built as aparté's highlight provider.
28
57
  *
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,cAAc,CAAC;AACrE;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACjC,sEAAsE;IACtE,kBAAkB,IAAI,MAAM,EAAE,CAAC;IAC/B,uEAAuE;IACvE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAC9E;AAED,MAAM,WAAW,wBAAwB;IACrC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,oEAAoE;AACpE,eAAO,MAAM,SAAS,aAAoD,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,iCAAiC,CAC7C,WAAW,EAAE,oBAAoB,EACjC,OAAO,GAAE,wBAA6B,EACtC,MAAM,GAAE,YAAiC,GAC1C,IAAI,CAWN"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,cAAc,CAAC;AACrE;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACjC,sEAAsE;IACtE,kBAAkB,IAAI,MAAM,EAAE,CAAC;IAC/B,mGAAmG;IACnG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAAC;CACjE;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,4FAA4F;AAC5F,MAAM,MAAM,kBAAkB,GACxB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpE,MAAM,WAAW,wBAAwB;IACrC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;CACnC;AAED,oEAAoE;AACpE,eAAO,MAAM,SAAS,aAAoD,CAAC;AAE3E,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAE9F;AAID;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAU5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,iCAAiC,CAC7C,WAAW,EAAE,oBAAoB,EACjC,OAAO,GAAE,wBAA6B,EACtC,MAAM,GAAE,YAAiC,GAC1C,IAAI,CAYN"}
package/dist/core.js CHANGED
@@ -1,17 +1,31 @@
1
1
  import { aparteGlobalConfig } from "@aparte/core";
2
2
  const PLAINTEXT = /* @__PURE__ */ new Set(["text", "plaintext", "txt", "ansi", ""]);
3
+ function renderOptions(theme, lang) {
4
+ return typeof theme === "string" ? { lang, theme } : { lang, themes: theme, defaultColor: false };
5
+ }
6
+ const PAIR_STYLE_ID = "aparte-shiki-theme-pair";
7
+ function ensureThemePairStyles() {
8
+ if (typeof document === "undefined" || document.getElementById(PAIR_STYLE_ID)) return;
9
+ const style = document.createElement("style");
10
+ style.id = PAIR_STYLE_ID;
11
+ style.textContent = '.shiki{color:var(--shiki-light);background-color:var(--shiki-light-bg)}.shiki span{color:var(--shiki-light)}[data-aparte-theme="dark"] .shiki{color:var(--shiki-dark);background-color:var(--shiki-dark-bg)}[data-aparte-theme="dark"] .shiki span{color:var(--shiki-dark)}';
12
+ document.head.appendChild(style);
13
+ }
3
14
  function setupShikiProviderFromHighlighter(highlighter, options = {}, config = aparteGlobalConfig) {
4
15
  const theme = options.theme ?? "github-dark";
16
+ if (typeof theme !== "string") ensureThemePairStyles();
5
17
  let loaded = null;
6
18
  config.setHighlightProvider((code, lang) => {
7
19
  const requested = (lang || "text").toLowerCase();
8
20
  loaded ??= new Set(highlighter.getLoadedLanguages());
9
21
  const language = PLAINTEXT.has(requested) || !loaded.has(requested) ? "text" : requested;
10
- return highlighter.codeToHtml(code, { lang: language, theme });
22
+ return highlighter.codeToHtml(code, renderOptions(theme, language));
11
23
  });
12
24
  }
13
25
  export {
14
26
  PLAINTEXT,
27
+ ensureThemePairStyles,
28
+ renderOptions,
15
29
  setupShikiProviderFromHighlighter
16
30
  };
17
31
  //# sourceMappingURL=core.js.map
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sources":["../src/core.ts"],"sourcesContent":["import { aparteGlobalConfig , type AparteConfig} from '@aparte/core';\n/**\n * The slice of a shiki highlighter this plugin actually uses — declared here rather\n * than borrowed from `HighlighterCore`, so this module needs no import from shiki at\n * all, not even a type. A real `HighlighterCore` satisfies it (asserted at compile\n * time in the tests), and so does a wrapper of your own.\n */\nexport interface ShikiHighlighterLike {\n /** The grammars this highlighter carries — read once, then cached. */\n getLoadedLanguages(): string[];\n /** Render `code` as highlighted HTML with a loaded grammar + theme. */\n codeToHtml(code: string, options: { lang: string; theme: string }): string;\n}\n\nexport interface ShikiCoreProviderOptions {\n /**\n * Theme to render with — the name of a theme your highlighter was built with.\n * Default `'github-dark'`.\n */\n theme?: string;\n}\n\n/** Languages that mean \"don't highlight\", plus the empty string. */\nexport const PLAINTEXT = new Set(['text', 'plaintext', 'txt', 'ansi', '']);\n\n/**\n * Register a highlighter **you** built as aparté's highlight provider.\n *\n * Why this exists — measured, on a build whose only import was the plugin:\n *\n * | entry point | files emitted | weight |\n * | -------------------------------------------- | ------------- | ------ |\n * | `@aparte/plugin-shiki` (shiki's full bundle) | 302 | 11 MB |\n * | this one, with three grammars | 1 | 560 kB |\n *\n * The convenience entry imports `shiki`, whose bundle maps **every** known language\n * to a dynamic import, so a bundler emits one chunk per grammar —\n * `emacs-lisp` (780 kB), `wasm`, `wolfram` and 300 others — even for an app that\n * renders twenty. No runtime option can undo that: a static import is a static\n * import (verified — restricting `langs` still emits all 302). Controlling\n * distribution weight means not importing that bundle at all, which is what\n * importing THIS module instead achieves.\n *\n * ```ts\n * import { createHighlighterCore } from 'shiki/core';\n * import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';\n * import ts from '@shikijs/langs/typescript';\n * import githubDark from '@shikijs/themes/github-dark';\n * import { setupShikiProviderFromHighlighter } from '@aparte/plugin-shiki/core';\n *\n * setupShikiProviderFromHighlighter(\n * await createHighlighterCore({\n * themes: [githubDark],\n * langs: [ts],\n * engine: createJavaScriptRegexEngine(),\n * }),\n * );\n * ```\n *\n * Your highlighter's grammars are fixed, so a language it does not carry renders as\n * plain text rather than throwing — same degradation as the convenience entry, minus\n * the on-demand load it cannot do.\n *\n * Framework-agnostic, and needs no `AparteClient`. Call once at startup.\n */\nexport function setupShikiProviderFromHighlighter(\n highlighter: ShikiHighlighterLike,\n options: ShikiCoreProviderOptions = {},\n config: AparteConfig = aparteGlobalConfig,\n): void {\n const theme = options.theme ?? 'github-dark';\n let loaded: Set<string> | null = null;\n\n config.setHighlightProvider((code, lang) => {\n const requested = (lang || 'text').toLowerCase();\n // Cached after the first call: the grammar set of a core highlighter is fixed.\n loaded ??= new Set(highlighter.getLoadedLanguages());\n const language = PLAINTEXT.has(requested) || !loaded.has(requested) ? 'text' : requested;\n return highlighter.codeToHtml(code, { lang: language, theme });\n });\n}\n"],"names":[],"mappings":";AAuBO,MAAM,gCAAgB,IAAI,CAAC,QAAQ,aAAa,OAAO,QAAQ,EAAE,CAAC;AA0ClE,SAAS,kCACZ,aACA,UAAoC,CAAA,GACpC,SAAuB,oBACnB;AACJ,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,SAA6B;AAEjC,SAAO,qBAAqB,CAAC,MAAM,SAAS;AACxC,UAAM,aAAa,QAAQ,QAAQ,YAAA;AAEnC,eAAW,IAAI,IAAI,YAAY,mBAAA,CAAoB;AACnD,UAAM,WAAW,UAAU,IAAI,SAAS,KAAK,CAAC,OAAO,IAAI,SAAS,IAAI,SAAS;AAC/E,WAAO,YAAY,WAAW,MAAM,EAAE,MAAM,UAAU,OAAO;AAAA,EACjE,CAAC;AACL;"}
1
+ {"version":3,"file":"core.js","sources":["../src/core.ts"],"sourcesContent":["import { aparteGlobalConfig , type AparteConfig} from '@aparte/core';\n/**\n * The slice of a shiki highlighter this plugin actually uses — declared here rather\n * than borrowed from `HighlighterCore`, so this module needs no import from shiki at\n * all, not even a type. A real `HighlighterCore` satisfies it (asserted at compile\n * time in the tests), and so does a wrapper of your own.\n */\nexport interface ShikiHighlighterLike {\n /** The grammars this highlighter carries — read once, then cached. */\n getLoadedLanguages(): string[];\n /** Render `code` as highlighted HTML with a loaded grammar and one theme, or a light/dark pair. */\n codeToHtml(code: string, options: ShikiRenderOptions): string;\n}\n\n/**\n * A theme for each of the two colour schemes core switches between. A type literal\n * rather than an interface: shiki's `themes` option is a string-keyed record, and only\n * a literal type is implicitly assignable to one.\n */\nexport type ShikiThemePair = { light: string; dark: string };\n\n/** What either entry hands to `codeToHtml` — shiki's own option names, nothing invented. */\nexport type ShikiRenderOptions =\n | { lang: string; theme: string }\n | { lang: string; themes: ShikiThemePair; defaultColor: false };\n\nexport interface ShikiCoreProviderOptions {\n /**\n * Theme to render with — the name of a theme your highlighter was built with — or a\n * `{ light, dark }` pair of them, which follows core's `[data-aparte-theme=\"dark\"]`\n * switch. Default `'github-dark'`.\n */\n theme?: string | ShikiThemePair;\n}\n\n/** Languages that mean \"don't highlight\", plus the empty string. */\nexport const PLAINTEXT = new Set(['text', 'plaintext', 'txt', 'ansi', '']);\n\n/** One theme renders with `theme`; a pair renders both colours per token and no default. */\nexport function renderOptions(theme: string | ShikiThemePair, lang: string): ShikiRenderOptions {\n return typeof theme === 'string' ? { lang, theme } : { lang, themes: theme, defaultColor: false };\n}\n\nconst PAIR_STYLE_ID = 'aparte-shiki-theme-pair';\n\n/**\n * The stylesheet a theme pair needs, added to the document once.\n *\n * With `defaultColor: false` shiki writes both colours on every token as CSS variables\n * (`--shiki-light`, `--shiki-dark`, plus `-bg` on the block) and sets no colour of its\n * own, so someone has to read the right one. That someone is not core — core knows\n * nothing about shiki's variables — and not the consumer either, who asked for a pair\n * precisely so the block would follow the theme by itself. The switch is core's public\n * theme contract, `[data-aparte-theme=\"dark\"]`, documented in theme.css.\n */\nexport function ensureThemePairStyles(): void {\n if (typeof document === 'undefined' || document.getElementById(PAIR_STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = PAIR_STYLE_ID;\n style.textContent =\n '.shiki{color:var(--shiki-light);background-color:var(--shiki-light-bg)}'\n + '.shiki span{color:var(--shiki-light)}'\n + '[data-aparte-theme=\"dark\"] .shiki{color:var(--shiki-dark);background-color:var(--shiki-dark-bg)}'\n + '[data-aparte-theme=\"dark\"] .shiki span{color:var(--shiki-dark)}';\n document.head.appendChild(style);\n}\n\n/**\n * Register a highlighter **you** built as aparté's highlight provider.\n *\n * Why this exists — measured, on a build whose only import was the plugin:\n *\n * | entry point | files emitted | weight |\n * | -------------------------------------------- | ------------- | ------ |\n * | `@aparte/plugin-shiki` (shiki's full bundle) | 302 | 11 MB |\n * | this one, with three grammars | 1 | 560 kB |\n *\n * The convenience entry imports `shiki`, whose bundle maps **every** known language\n * to a dynamic import, so a bundler emits one chunk per grammar —\n * `emacs-lisp` (780 kB), `wasm`, `wolfram` and 300 others — even for an app that\n * renders twenty. No runtime option can undo that: a static import is a static\n * import (verified — restricting `langs` still emits all 302). Controlling\n * distribution weight means not importing that bundle at all, which is what\n * importing THIS module instead achieves.\n *\n * ```ts\n * import { createHighlighterCore } from 'shiki/core';\n * import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';\n * import ts from '@shikijs/langs/typescript';\n * import githubDark from '@shikijs/themes/github-dark';\n * import { setupShikiProviderFromHighlighter } from '@aparte/plugin-shiki/core';\n *\n * setupShikiProviderFromHighlighter(\n * await createHighlighterCore({\n * themes: [githubDark],\n * langs: [ts],\n * engine: createJavaScriptRegexEngine(),\n * }),\n * );\n * ```\n *\n * Your highlighter's grammars are fixed, so a language it does not carry renders as\n * plain text rather than throwing — same degradation as the convenience entry, minus\n * the on-demand load it cannot do.\n *\n * Framework-agnostic, and needs no `AparteClient`. Call once at startup.\n */\nexport function setupShikiProviderFromHighlighter(\n highlighter: ShikiHighlighterLike,\n options: ShikiCoreProviderOptions = {},\n config: AparteConfig = aparteGlobalConfig,\n): void {\n const theme = options.theme ?? 'github-dark';\n if (typeof theme !== 'string') ensureThemePairStyles();\n let loaded: Set<string> | null = null;\n\n config.setHighlightProvider((code, lang) => {\n const requested = (lang || 'text').toLowerCase();\n // Cached after the first call: the grammar set of a core highlighter is fixed.\n loaded ??= new Set(highlighter.getLoadedLanguages());\n const language = PLAINTEXT.has(requested) || !loaded.has(requested) ? 'text' : requested;\n return highlighter.codeToHtml(code, renderOptions(theme, language));\n });\n}\n"],"names":[],"mappings":";AAoCO,MAAM,gCAAgB,IAAI,CAAC,QAAQ,aAAa,OAAO,QAAQ,EAAE,CAAC;AAGlE,SAAS,cAAc,OAAgC,MAAkC;AAC5F,SAAO,OAAO,UAAU,WAAW,EAAE,MAAM,MAAA,IAAU,EAAE,MAAM,QAAQ,OAAO,cAAc,MAAA;AAC9F;AAEA,MAAM,gBAAgB;AAYf,SAAS,wBAA8B;AAC1C,MAAI,OAAO,aAAa,eAAe,SAAS,eAAe,aAAa,EAAG;AAC/E,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cACF;AAIJ,WAAS,KAAK,YAAY,KAAK;AACnC;AA0CO,SAAS,kCACZ,aACA,UAAoC,CAAA,GACpC,SAAuB,oBACnB;AACJ,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,OAAO,UAAU,SAAU,uBAAA;AAC/B,MAAI,SAA6B;AAEjC,SAAO,qBAAqB,CAAC,MAAM,SAAS;AACxC,UAAM,aAAa,QAAQ,QAAQ,YAAA;AAEnC,eAAW,IAAI,IAAI,YAAY,mBAAA,CAAoB;AACnD,UAAM,WAAW,UAAU,IAAI,SAAS,KAAK,CAAC,OAAO,IAAI,SAAS,IAAI,SAAS;AAC/E,WAAO,YAAY,WAAW,MAAM,cAAc,OAAO,QAAQ,CAAC;AAAA,EACtE,CAAC;AACL;"}
package/dist/index.d.ts CHANGED
@@ -2,13 +2,15 @@ import { type AparteConfig } from '@aparte/core';
2
2
  import { type BundledTheme } from 'shiki';
3
3
  export interface ShikiProviderOptions {
4
4
  /**
5
- * Theme to render with — a bundled theme name (e.g. `'github-dark'`) or a
6
- * loaded custom theme name. Default `'github-dark'`.
5
+ * Theme to render with — a bundled theme name (e.g. `'github-dark'`) or a loaded
6
+ * custom theme name — or a `{ light, dark }` pair of them, which follows core's
7
+ * `[data-aparte-theme="dark"]` switch. Default `'github-dark'`.
7
8
  */
8
- theme?: BundledTheme | string;
9
+ theme?: BundledTheme | string | ShikiThemePair;
9
10
  }
11
+ import { type ShikiThemePair } from './core.js';
10
12
  export { setupShikiProviderFromHighlighter } from './core.js';
11
- export type { ShikiCoreProviderOptions, ShikiHighlighterLike } from './core.js';
13
+ export type { ShikiCoreProviderOptions, ShikiHighlighterLike, ShikiThemePair, ShikiRenderOptions } from './core.js';
12
14
  /**
13
15
  * Register [shiki](https://shiki.style) as aparté's syntax-highlight provider.
14
16
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,cAAc,CAAC;AACrE,OAAO,EAIH,KAAK,YAAY,EACpB,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;CACjC;AAID,OAAO,EAAE,iCAAiC,EAAE,MAAM,WAAW,CAAC;AAC9D,YAAY,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,EAAE,MAAM,GAAE,YAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CrI"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,cAAc,CAAC;AACrE,OAAO,EAIH,KAAK,YAAY,EACpB,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,cAAc,CAAC;CAClD;AAED,OAAO,EAAmD,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjG,OAAO,EAAE,iCAAiC,EAAE,MAAM,WAAW,CAAC;AAC9D,YAAY,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEpH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,EAAE,MAAM,GAAE,YAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CrI"}
package/dist/index.js CHANGED
@@ -1,16 +1,18 @@
1
1
  import { aparteGlobalConfig } from "@aparte/core";
2
2
  import { createHighlighter } from "shiki";
3
- import { PLAINTEXT } from "./core.js";
3
+ import { ensureThemePairStyles, PLAINTEXT, renderOptions } from "./core.js";
4
4
  import { setupShikiProviderFromHighlighter } from "./core.js";
5
5
  async function setupShikiProvider(options = {}, config = aparteGlobalConfig) {
6
6
  const theme = options.theme ?? "github-dark";
7
+ const themes = typeof theme === "string" ? [theme] : [theme.light, theme.dark];
8
+ if (typeof theme !== "string") ensureThemePairStyles();
7
9
  let highlighter = null;
8
10
  let creating = null;
9
11
  const loadedLangs = /* @__PURE__ */ new Set();
10
12
  const getHighlighter = () => {
11
13
  if (highlighter) return Promise.resolve(highlighter);
12
14
  if (!creating) {
13
- creating = createHighlighter({ themes: [theme], langs: [] }).then((h) => {
15
+ creating = createHighlighter({ themes, langs: [] }).then((h) => {
14
16
  highlighter = h;
15
17
  return h;
16
18
  }).catch((err) => {
@@ -31,7 +33,7 @@ async function setupShikiProvider(options = {}, config = aparteGlobalConfig) {
31
33
  language = "text";
32
34
  }
33
35
  }
34
- return hl.codeToHtml(code, { lang: language, theme });
36
+ return hl.codeToHtml(code, renderOptions(theme, language));
35
37
  });
36
38
  }
37
39
  export {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { aparteGlobalConfig , type AparteConfig} from '@aparte/core';\nimport {\n createHighlighter,\n type Highlighter,\n type BundledLanguage,\n type BundledTheme,\n} from 'shiki';\n\nexport interface ShikiProviderOptions {\n /**\n * Theme to render with — a bundled theme name (e.g. `'github-dark'`) or a\n * loaded custom theme name. Default `'github-dark'`.\n */\n theme?: BundledTheme | string;\n}\n\nimport { PLAINTEXT } from './core.js';\n\nexport { setupShikiProviderFromHighlighter } from './core.js';\nexport type { ShikiCoreProviderOptions, ShikiHighlighterLike } from './core.js';\n\n/**\n * Register [shiki](https://shiki.style) as aparté's syntax-highlight provider.\n *\n * Backed by a **single, lazily-created** `createHighlighter` instance: the\n * highlighter is built once on first use with only the requested theme, and each\n * language grammar is loaded on demand and cached. This is deliberately NOT the\n * `codeToHtml` shorthand, which can re-initialise a highlighter per call — the\n * cost we control here is why the plugin exists.\n *\n * **Bundle note — runtime cost and distribution cost are not the same thing.**\n * At runtime nothing is eager: a grammar is fetched the first time a language is\n * seen, so you only *execute* what you render. But this entry imports `shiki`,\n * whose bundle maps every known language to a dynamic import, so your bundler\n * **emits one chunk per grammar** — measured on a build whose only import was this\n * function: **302 files, 11 MB** (`emacs-lisp` alone is 780 kB). Restricting the\n * language list would not help; a static import is a static import.\n *\n * If the size of what you ship matters — an app delivered by `npx`, a desktop\n * bundle — use {@link setupShikiProviderFromHighlighter} from\n * `@aparte/plugin-shiki/core` and build the highlighter yourself: same plugin,\n * same behaviour, **1 file / 560 kB** for three grammars.\n *\n * Framework-agnostic — vanilla, no framework imports. Call once at startup.\n */\nexport async function setupShikiProvider(options: ShikiProviderOptions = {}, config: AparteConfig = aparteGlobalConfig): Promise<void> {\n const theme = (options.theme ?? 'github-dark') as BundledTheme;\n\n let highlighter: Highlighter | null = null;\n let creating: Promise<Highlighter> | null = null;\n const loadedLangs = new Set<string>();\n\n const getHighlighter = (): Promise<Highlighter> => {\n if (highlighter) return Promise.resolve(highlighter);\n // De-dupe concurrent first calls so we only ever create one instance.\n if (!creating) {\n creating = createHighlighter({ themes: [theme], langs: [] })\n .then((h) => {\n highlighter = h;\n return h;\n })\n .catch((err) => {\n // A transient failure (network/CSP on the dynamic theme import,\n // wasm engine hiccup) must NOT poison the singleton: clear the\n // cached promise so the next code block retries creation instead\n // of re-returning a permanently-rejected promise. Re-throw so this\n // block still degrades to plaintext via the provider's caller.\n creating = null;\n throw err;\n });\n }\n return creating;\n };\n\n config.setHighlightProvider(async (code, lang) => {\n const hl = await getHighlighter();\n let language = (lang || 'text').toLowerCase();\n\n if (!PLAINTEXT.has(language) && !loadedLangs.has(language)) {\n try {\n await hl.loadLanguage(language as BundledLanguage);\n loadedLangs.add(language);\n } catch {\n language = 'text'; // unknown grammar → render as plain text\n }\n }\n\n return hl.codeToHtml(code, { lang: language, theme });\n });\n}\n"],"names":[],"mappings":";;;;AA6CA,eAAsB,mBAAmB,UAAgC,IAAI,SAAuB,oBAAmC;AACnI,QAAM,QAAS,QAAQ,SAAS;AAEhC,MAAI,cAAkC;AACtC,MAAI,WAAwC;AAC5C,QAAM,kCAAkB,IAAA;AAExB,QAAM,iBAAiB,MAA4B;AAC/C,QAAI,YAAa,QAAO,QAAQ,QAAQ,WAAW;AAEnD,QAAI,CAAC,UAAU;AACX,iBAAW,kBAAkB,EAAE,QAAQ,CAAC,KAAK,GAAG,OAAO,GAAC,CAAG,EACtD,KAAK,CAAC,MAAM;AACT,sBAAc;AACd,eAAO;AAAA,MACX,CAAC,EACA,MAAM,CAAC,QAAQ;AAMZ,mBAAW;AACX,cAAM;AAAA,MACV,CAAC;AAAA,IACT;AACA,WAAO;AAAA,EACX;AAEA,SAAO,qBAAqB,OAAO,MAAM,SAAS;AAC9C,UAAM,KAAK,MAAM,eAAA;AACjB,QAAI,YAAY,QAAQ,QAAQ,YAAA;AAEhC,QAAI,CAAC,UAAU,IAAI,QAAQ,KAAK,CAAC,YAAY,IAAI,QAAQ,GAAG;AACxD,UAAI;AACA,cAAM,GAAG,aAAa,QAA2B;AACjD,oBAAY,IAAI,QAAQ;AAAA,MAC5B,QAAQ;AACJ,mBAAW;AAAA,MACf;AAAA,IACJ;AAEA,WAAO,GAAG,WAAW,MAAM,EAAE,MAAM,UAAU,OAAO;AAAA,EACxD,CAAC;AACL;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { aparteGlobalConfig , type AparteConfig} from '@aparte/core';\nimport {\n createHighlighter,\n type Highlighter,\n type BundledLanguage,\n type BundledTheme,\n} from 'shiki';\n\nexport interface ShikiProviderOptions {\n /**\n * Theme to render with — a bundled theme name (e.g. `'github-dark'`) or a loaded\n * custom theme name — or a `{ light, dark }` pair of them, which follows core's\n * `[data-aparte-theme=\"dark\"]` switch. Default `'github-dark'`.\n */\n theme?: BundledTheme | string | ShikiThemePair;\n}\n\nimport { PLAINTEXT, renderOptions, ensureThemePairStyles, type ShikiThemePair } from './core.js';\n\nexport { setupShikiProviderFromHighlighter } from './core.js';\nexport type { ShikiCoreProviderOptions, ShikiHighlighterLike, ShikiThemePair, ShikiRenderOptions } from './core.js';\n\n/**\n * Register [shiki](https://shiki.style) as aparté's syntax-highlight provider.\n *\n * Backed by a **single, lazily-created** `createHighlighter` instance: the\n * highlighter is built once on first use with only the requested theme, and each\n * language grammar is loaded on demand and cached. This is deliberately NOT the\n * `codeToHtml` shorthand, which can re-initialise a highlighter per call — the\n * cost we control here is why the plugin exists.\n *\n * **Bundle note — runtime cost and distribution cost are not the same thing.**\n * At runtime nothing is eager: a grammar is fetched the first time a language is\n * seen, so you only *execute* what you render. But this entry imports `shiki`,\n * whose bundle maps every known language to a dynamic import, so your bundler\n * **emits one chunk per grammar** — measured on a build whose only import was this\n * function: **302 files, 11 MB** (`emacs-lisp` alone is 780 kB). Restricting the\n * language list would not help; a static import is a static import.\n *\n * If the size of what you ship matters — an app delivered by `npx`, a desktop\n * bundle — use {@link setupShikiProviderFromHighlighter} from\n * `@aparte/plugin-shiki/core` and build the highlighter yourself: same plugin,\n * same behaviour, **1 file / 560 kB** for three grammars.\n *\n * Framework-agnostic — vanilla, no framework imports. Call once at startup.\n */\nexport async function setupShikiProvider(options: ShikiProviderOptions = {}, config: AparteConfig = aparteGlobalConfig): Promise<void> {\n const theme = options.theme ?? 'github-dark';\n const themes = (typeof theme === 'string' ? [theme] : [theme.light, theme.dark]) as BundledTheme[];\n if (typeof theme !== 'string') ensureThemePairStyles();\n\n let highlighter: Highlighter | null = null;\n let creating: Promise<Highlighter> | null = null;\n const loadedLangs = new Set<string>();\n\n const getHighlighter = (): Promise<Highlighter> => {\n if (highlighter) return Promise.resolve(highlighter);\n // De-dupe concurrent first calls so we only ever create one instance.\n if (!creating) {\n creating = createHighlighter({ themes, langs: [] })\n .then((h) => {\n highlighter = h;\n return h;\n })\n .catch((err) => {\n // A transient failure (network/CSP on the dynamic theme import,\n // wasm engine hiccup) must NOT poison the singleton: clear the\n // cached promise so the next code block retries creation instead\n // of re-returning a permanently-rejected promise. Re-throw so this\n // block still degrades to plaintext via the provider's caller.\n creating = null;\n throw err;\n });\n }\n return creating;\n };\n\n config.setHighlightProvider(async (code, lang) => {\n const hl = await getHighlighter();\n let language = (lang || 'text').toLowerCase();\n\n if (!PLAINTEXT.has(language) && !loadedLangs.has(language)) {\n try {\n await hl.loadLanguage(language as BundledLanguage);\n loadedLangs.add(language);\n } catch {\n language = 'text'; // unknown grammar → render as plain text\n }\n }\n\n return hl.codeToHtml(code, renderOptions(theme, language));\n });\n}\n"],"names":[],"mappings":";;;;AA8CA,eAAsB,mBAAmB,UAAgC,IAAI,SAAuB,oBAAmC;AACnI,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAU,OAAO,UAAU,WAAW,CAAC,KAAK,IAAI,CAAC,MAAM,OAAO,MAAM,IAAI;AAC9E,MAAI,OAAO,UAAU,SAAU,uBAAA;AAE/B,MAAI,cAAkC;AACtC,MAAI,WAAwC;AAC5C,QAAM,kCAAkB,IAAA;AAExB,QAAM,iBAAiB,MAA4B;AAC/C,QAAI,YAAa,QAAO,QAAQ,QAAQ,WAAW;AAEnD,QAAI,CAAC,UAAU;AACX,iBAAW,kBAAkB,EAAE,QAAQ,OAAO,CAAA,GAAI,EAC7C,KAAK,CAAC,MAAM;AACT,sBAAc;AACd,eAAO;AAAA,MACX,CAAC,EACA,MAAM,CAAC,QAAQ;AAMZ,mBAAW;AACX,cAAM;AAAA,MACV,CAAC;AAAA,IACT;AACA,WAAO;AAAA,EACX;AAEA,SAAO,qBAAqB,OAAO,MAAM,SAAS;AAC9C,UAAM,KAAK,MAAM,eAAA;AACjB,QAAI,YAAY,QAAQ,QAAQ,YAAA;AAEhC,QAAI,CAAC,UAAU,IAAI,QAAQ,KAAK,CAAC,YAAY,IAAI,QAAQ,GAAG;AACxD,UAAI;AACA,cAAM,GAAG,aAAa,QAA2B;AACjD,oBAAY,IAAI,QAAQ;AAAA,MAC5B,QAAQ;AACJ,mBAAW;AAAA,MACf;AAAA,IACJ;AAEA,WAAO,GAAG,WAAW,MAAM,cAAc,OAAO,QAAQ,CAAC;AAAA,EAC7D,CAAC;AACL;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aparte/plugin-shiki",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Syntax highlighting for aparté via shiki — register it as the highlight provider (single lazily-created highlighter).",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -30,7 +30,7 @@
30
30
  "node": ">=18"
31
31
  },
32
32
  "peerDependencies": {
33
- "@aparte/core": ">=0.13.0 <1.0.0",
33
+ "@aparte/core": ">=0.14.0 <1.0.0",
34
34
  "shiki": "^4.0.0"
35
35
  },
36
36
  "devDependencies": {
@@ -39,7 +39,7 @@
39
39
  "shiki": "^4.0.0",
40
40
  "typescript": "^5.4.0",
41
41
  "vite": "^6.0.0",
42
- "@aparte/core": "0.13.0"
42
+ "@aparte/core": "0.14.0"
43
43
  },
44
44
  "keywords": [
45
45
  "aparte",