@shikijs/core 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,10 +2,9 @@ import { ShikiError as ShikiError$1 } from "@shikijs/types";
2
2
  import { ShikiError, applyColorReplacements, codeToTokensBase as codeToTokensBase$1, codeToTokensWithThemes, codeToTokensWithThemes as codeToTokensWithThemes$1, createShikiInternal, createShikiInternalSync, createShikiPrimitive, createShikiPrimitive as createShikiPrimitive$1, createShikiPrimitiveAsync, createShikiPrimitiveAsync as createShikiPrimitiveAsync$1, getLastGrammarState, getLastGrammarStateFromMap, isNoneTheme, isPlainLang, isSpecialLang, isSpecialTheme, normalizeGetter, normalizeTheme, resolveColorReplacements, setLastGrammarStateToMap, splitLines, splitLines as splitLines$1, toArray, tokenizeWithTheme } from "@shikijs/primitive";
3
3
  import { FontStyle } from "@shikijs/vscode-textmate";
4
4
  import { toHtml } from "hast-util-to-html";
5
-
6
- export * from "@shikijs/types"
7
-
5
+ export * from "@shikijs/types";
8
6
  //#region src/utils/hast.ts
7
+ const RE_WHITESPACE = /\s+/g;
9
8
  /**
10
9
  * Utility to append class to a hast node
11
10
  *
@@ -15,15 +14,18 @@ function addClassToHast(node, className) {
15
14
  if (!className) return node;
16
15
  node.properties ||= {};
17
16
  node.properties.class ||= [];
18
- if (typeof node.properties.class === "string") node.properties.class = node.properties.class.split(/\s+/g);
17
+ if (typeof node.properties.class === "string") node.properties.class = node.properties.class.split(RE_WHITESPACE);
19
18
  if (!Array.isArray(node.properties.class)) node.properties.class = [];
20
- const targets = Array.isArray(className) ? className : className.split(/\s+/g);
19
+ const targets = Array.isArray(className) ? className : className.split(RE_WHITESPACE);
21
20
  for (const c of targets) if (c && !node.properties.class.includes(c)) node.properties.class.push(c);
22
21
  return node;
23
22
  }
24
-
25
23
  //#endregion
26
24
  //#region src/utils/strings.ts
25
+ const RE_LANG_ATTR = /:?lang=["']([^"']+)["']/g;
26
+ const RE_CODE_FENCE = /(?:```|~~~)([\w-]+)/g;
27
+ const RE_LATEX_BEGIN = /\\begin\{([\w-]+)\}/g;
28
+ const RE_SCRIPT_LANG = /<script\s+(?:type|lang)=["']([^"']+)["']/gi;
27
29
  /**
28
30
  * Creates a converter between index and position in a code block.
29
31
  *
@@ -34,7 +36,7 @@ function createPositionConverter(code) {
34
36
  function indexToPos(index) {
35
37
  if (index === code.length) return {
36
38
  line: lines.length - 1,
37
- character: lines[lines.length - 1].length
39
+ character: lines.at(-1).length
38
40
  };
39
41
  let character = index;
40
42
  let line = 0;
@@ -81,33 +83,28 @@ function createPositionConverter(code) {
81
83
  */
82
84
  function guessEmbeddedLanguages(code, _lang, highlighter) {
83
85
  const langs = /* @__PURE__ */ new Set();
84
- for (const match of code.matchAll(/:?lang=["']([^"']+)["']/g)) {
86
+ for (const match of code.matchAll(RE_LANG_ATTR)) {
85
87
  const lang = match[1].toLowerCase().trim();
86
88
  if (lang) langs.add(lang);
87
89
  }
88
- for (const match of code.matchAll(/(?:```|~~~)([\w-]+)/g)) {
90
+ for (const match of code.matchAll(RE_CODE_FENCE)) {
89
91
  const lang = match[1].toLowerCase().trim();
90
92
  if (lang) langs.add(lang);
91
93
  }
92
- for (const match of code.matchAll(/\\begin\{([\w-]+)\}/g)) {
94
+ for (const match of code.matchAll(RE_LATEX_BEGIN)) {
93
95
  const lang = match[1].toLowerCase().trim();
94
96
  if (lang) langs.add(lang);
95
97
  }
96
- for (const match of code.matchAll(/<script\s+(?:type|lang)=["']([^"']+)["']/gi)) {
98
+ for (const match of code.matchAll(RE_SCRIPT_LANG)) {
97
99
  const fullType = match[1].toLowerCase().trim();
98
100
  const lang = fullType.includes("/") ? fullType.split("/").pop() : fullType;
99
101
  if (lang) langs.add(lang);
100
102
  }
101
- if (!highlighter) return Array.from(langs);
103
+ if (!highlighter) return [...langs];
102
104
  const bundle = highlighter.getBundledLanguages();
103
- return Array.from(langs).filter((l) => l && bundle[l]);
105
+ return [...langs].filter((l) => l && bundle[l]);
104
106
  }
105
-
106
- //#endregion
107
- //#region src/utils/constants.ts
108
- const DEFAULT_COLOR_LIGHT_DARK = "light-dark()";
109
107
  const COLOR_KEYS = ["color", "background-color"];
110
-
111
108
  //#endregion
112
109
  //#region src/utils/tokens.ts
113
110
  /**
@@ -137,7 +134,7 @@ function splitToken(token, offsets) {
137
134
  * Split 2D tokens array by given breakpoints.
138
135
  */
139
136
  function splitTokens(tokens, breakpoints) {
140
- const sorted = Array.from(breakpoints instanceof Set ? breakpoints : new Set(breakpoints)).sort((a, b) => a - b);
137
+ const sorted = [...breakpoints instanceof Set ? breakpoints : new Set(breakpoints)].sort((a, b) => a - b);
141
138
  if (!sorted.length) return tokens;
142
139
  return tokens.map((line) => {
143
140
  return line.flatMap((token) => {
@@ -163,7 +160,7 @@ function flatTokenVariants(merged, variantsOrder, cssVariablePrefix, defaultColo
163
160
  styles.forEach((cur, idx) => {
164
161
  for (const key of styleKeys) {
165
162
  const value = cur[key] || "inherit";
166
- if (idx === 0 && defaultColor && COLOR_KEYS.includes(key)) if (defaultColor === DEFAULT_COLOR_LIGHT_DARK && styles.length > 1) {
163
+ if (idx === 0 && defaultColor && COLOR_KEYS.includes(key)) if (defaultColor === "light-dark()" && styles.length > 1) {
167
164
  const lightIndex = variantsOrder.findIndex((t) => t === "light");
168
165
  const darkIndex = variantsOrder.findIndex((t) => t === "dark");
169
166
  if (lightIndex === -1 || darkIndex === -1) throw new ShikiError$1("When using `defaultColor: \"light-dark()\"`, you must provide both `light` and `dark` themes");
@@ -194,7 +191,6 @@ function stringifyTokenStyle(token) {
194
191
  if (typeof token === "string") return token;
195
192
  return Object.entries(token).map(([key, value]) => `${key}:${value}`).join(";");
196
193
  }
197
-
198
194
  //#endregion
199
195
  //#region src/transformer-decorations.ts
200
196
  /**
@@ -248,7 +244,7 @@ function transformerDecorations() {
248
244
  code(codeEl) {
249
245
  if (!this.options.decorations?.length) return;
250
246
  const ctx = getContext(this);
251
- const lines = Array.from(codeEl.children).filter((i) => i.type === "element" && i.tagName === "span");
247
+ const lines = [...codeEl.children].filter((i) => i.type === "element" && i.tagName === "span");
252
248
  if (lines.length !== ctx.converter.lines.length) throw new ShikiError$1(`Number of lines in code element (${lines.length}) does not match the number of lines in the source (${ctx.converter.lines.length}). Failed to apply decorations.`);
253
249
  function applyLineSection(line, start, end, decoration) {
254
250
  const lineEl = lines[line];
@@ -335,7 +331,6 @@ function stringify(el) {
335
331
  if (el.type === "element") return el.children.map(stringify).join("");
336
332
  return "";
337
333
  }
338
-
339
334
  //#endregion
340
335
  //#region src/highlight/_get-transformers.ts
341
336
  const builtInTransformers = [/* @__PURE__ */ transformerDecorations()];
@@ -367,7 +362,6 @@ function sortTransformersByEnforcement(transformers) {
367
362
  normal
368
363
  };
369
364
  }
370
-
371
365
  //#endregion
372
366
  //#region ../../node_modules/.pnpm/ansi-sequence-parser@1.1.3/node_modules/ansi-sequence-parser/dist/index.js
373
367
  var namedColors = [
@@ -603,9 +597,10 @@ function createColorPalette(namedColorsMap = defaultNamedColorsMap) {
603
597
  }
604
598
  return { value };
605
599
  }
606
-
607
600
  //#endregion
608
601
  //#region src/highlight/code-to-tokens-ansi.ts
602
+ const RE_HEX_COLOR = /#([0-9a-f]{3,8})/i;
603
+ const RE_CSS_VAR_ANSI = /var\((--[\w-]+-ansi-[\w-]+)\)/;
609
604
  /**
610
605
  * Default ANSI palette (VSCode compatible fallbacks)
611
606
  * Used when the theme does not define terminal.ansi* colors.
@@ -667,7 +662,7 @@ function tokenizeAnsiWithTheme(theme, fileContents, options) {
667
662
  * Adds 50% alpha to a hex color string or the "-dim" postfix to a CSS variable
668
663
  */
669
664
  function dimColor(color) {
670
- const hexMatch = color.match(/#([0-9a-f]{3,8})/i);
665
+ const hexMatch = color.match(RE_HEX_COLOR);
671
666
  if (hexMatch) {
672
667
  const hex = hexMatch[1];
673
668
  if (hex.length === 8) {
@@ -687,11 +682,10 @@ function dimColor(color) {
687
682
  return `#${r}${r}${g}${g}${b}${b}80`;
688
683
  }
689
684
  }
690
- const cssVarMatch = color.match(/var\((--[\w-]+-ansi-[\w-]+)\)/);
685
+ const cssVarMatch = color.match(RE_CSS_VAR_ANSI);
691
686
  if (cssVarMatch) return `var(${cssVarMatch[1]}-dim)`;
692
687
  return color;
693
688
  }
694
-
695
689
  //#endregion
696
690
  //#region src/highlight/code-to-tokens-base.ts
697
691
  /**
@@ -707,7 +701,6 @@ function codeToTokensBase(primitive, code, options = {}) {
707
701
  }
708
702
  return codeToTokensBase$1(primitive, code, options);
709
703
  }
710
-
711
704
  //#endregion
712
705
  //#region src/highlight/code-to-tokens.ts
713
706
  /**
@@ -729,9 +722,9 @@ function codeToTokens(primitive, code, options) {
729
722
  theme: i[1]
730
723
  })).sort((a, b) => a.color === defaultColor ? -1 : b.color === defaultColor ? 1 : 0);
731
724
  if (themes.length === 0) throw new ShikiError$1("`themes` option must not be empty");
732
- const themeTokens = codeToTokensWithThemes$1(primitive, code, options);
725
+ const themeTokens = codeToTokensWithThemes$1(primitive, code, options, codeToTokensBase);
733
726
  grammarState = getLastGrammarStateFromMap(themeTokens);
734
- if (defaultColor && DEFAULT_COLOR_LIGHT_DARK !== defaultColor && !themes.find((t) => t.color === defaultColor)) throw new ShikiError$1(`\`themes\` option must contain the defaultColor key \`${defaultColor}\``);
727
+ if (defaultColor && "light-dark()" !== defaultColor && !themes.some((t) => t.color === defaultColor)) throw new ShikiError$1(`\`themes\` option must contain the defaultColor key \`${defaultColor}\``);
735
728
  const themeRegs = themes.map((t) => primitive.getTheme(t.theme));
736
729
  const themesOrder = themes.map((t) => t.color);
737
730
  tokens = themeTokens.map((line) => line.map((token) => flatTokenVariants(token, themesOrder, cssVariablePrefix, defaultColor, colorsRendering)));
@@ -764,7 +757,7 @@ function mapThemeColors(themes, themeRegs, themeColorReplacements, cssVariablePr
764
757
  const value = applyColorReplacements(themeRegs[idx][property], themeColorReplacements[idx]) || "inherit";
765
758
  const cssVar = `${cssVariablePrefix + t.color}${property === "bg" ? "-bg" : ""}:${value}`;
766
759
  if (idx === 0 && defaultColor) {
767
- if (defaultColor === DEFAULT_COLOR_LIGHT_DARK && themes.length > 1) {
760
+ if (defaultColor === "light-dark()" && themes.length > 1) {
768
761
  const lightIndex = themes.findIndex((t) => t.color === "light");
769
762
  const darkIndex = themes.findIndex((t) => t.color === "dark");
770
763
  if (lightIndex === -1 || darkIndex === -1) throw new ShikiError$1("When using `defaultColor: \"light-dark()\"`, you must provide both `light` and `dark` themes");
@@ -776,9 +769,10 @@ function mapThemeColors(themes, themeRegs, themeColorReplacements, cssVariablePr
776
769
  return null;
777
770
  }).filter((i) => !!i).join(";");
778
771
  }
779
-
780
772
  //#endregion
781
773
  //#region src/highlight/code-to-hast.ts
774
+ const RE_WHITESPACE_ONLY = /^\s+$/;
775
+ const RE_LEADING_TRAILING_WHITESPACE = /^(\s*)(.*?)(\s*)$/;
782
776
  function codeToHast(primitive, code, options, transformerContext = {
783
777
  meta: {},
784
778
  options,
@@ -957,7 +951,7 @@ function mergeWhitespaceTokens(tokens) {
957
951
  let firstOffset;
958
952
  line.forEach((token, idx) => {
959
953
  const couldMerge = !(token.fontStyle && (token.fontStyle & FontStyle.Underline || token.fontStyle & FontStyle.Strikethrough));
960
- if (couldMerge && token.content.match(/^\s+$/) && line[idx + 1]) {
954
+ if (couldMerge && RE_WHITESPACE_ONLY.test(token.content) && line[idx + 1]) {
961
955
  if (firstOffset === void 0) firstOffset = token.offset;
962
956
  carryOnContent += token.content;
963
957
  } else if (carryOnContent) {
@@ -980,8 +974,8 @@ function mergeWhitespaceTokens(tokens) {
980
974
  function splitWhitespaceTokens(tokens) {
981
975
  return tokens.map((line) => {
982
976
  return line.flatMap((token) => {
983
- if (token.content.match(/^\s+$/)) return token;
984
- const match = token.content.match(/^(\s*)(.*?)(\s*)$/);
977
+ if (RE_WHITESPACE_ONLY.test(token.content)) return token;
978
+ const match = token.content.match(RE_LEADING_TRAILING_WHITESPACE);
985
979
  if (!match) return token;
986
980
  const [, leading, content, trailing] = match;
987
981
  if (!leading && !trailing) return token;
@@ -1010,7 +1004,7 @@ function mergeAdjacentStyledTokens(tokens) {
1010
1004
  newLine.push({ ...token });
1011
1005
  continue;
1012
1006
  }
1013
- const prevToken = newLine[newLine.length - 1];
1007
+ const prevToken = newLine.at(-1);
1014
1008
  const prevStyle = stringifyTokenStyle(prevToken.htmlStyle || getTokenStyleObject(prevToken));
1015
1009
  const currentStyle = stringifyTokenStyle(token.htmlStyle || getTokenStyleObject(token));
1016
1010
  const isPrevDecorated = prevToken.fontStyle && (prevToken.fontStyle & FontStyle.Underline || prevToken.fontStyle & FontStyle.Strikethrough);
@@ -1021,7 +1015,6 @@ function mergeAdjacentStyledTokens(tokens) {
1021
1015
  return newLine;
1022
1016
  });
1023
1017
  }
1024
-
1025
1018
  //#endregion
1026
1019
  //#region src/highlight/code-to-html.ts
1027
1020
  const hastToHtml = toHtml;
@@ -1039,7 +1032,6 @@ function codeToHtml(primitive, code, options) {
1039
1032
  for (const transformer of getTransformers(options)) result = transformer.postprocess?.call(context, result, options) || result;
1040
1033
  return result;
1041
1034
  }
1042
-
1043
1035
  //#endregion
1044
1036
  //#region src/constructors/highlighter.ts
1045
1037
  /**
@@ -1105,7 +1097,6 @@ function makeSingletonHighlighterCore(createHighlighter) {
1105
1097
  return getSingletonHighlighterCore;
1106
1098
  }
1107
1099
  const getSingletonHighlighterCore = /* @__PURE__ */ makeSingletonHighlighterCore(createHighlighterCore);
1108
-
1109
1100
  //#endregion
1110
1101
  //#region src/constructors/bundle-factory.ts
1111
1102
  function createBundledHighlighter(options) {
@@ -1216,7 +1207,6 @@ function createSingletonShorthands(createHighlighter, config) {
1216
1207
  }
1217
1208
  };
1218
1209
  }
1219
-
1220
1210
  //#endregion
1221
1211
  //#region src/theme-css-variables.ts
1222
1212
  /**
@@ -1412,6 +1402,5 @@ function createCssVariablesTheme(options = {}) {
1412
1402
  });
1413
1403
  return theme;
1414
1404
  }
1415
-
1416
1405
  //#endregion
1417
- export { ShikiError, addClassToHast, applyColorReplacements, codeToHast, codeToHtml, codeToTokens, codeToTokensBase, codeToTokensWithThemes, createBundledHighlighter, createCssVariablesTheme, createHighlighterCore, createHighlighterCoreSync, createPositionConverter, createShikiInternal, createShikiInternalSync, createShikiPrimitive, createShikiPrimitiveAsync, createSingletonShorthands, flatTokenVariants, getLastGrammarState, getSingletonHighlighterCore, getTokenStyleObject, guessEmbeddedLanguages, hastToHtml, isNoneTheme, isPlainLang, isSpecialLang, isSpecialTheme, makeSingletonHighlighter, makeSingletonHighlighterCore, normalizeGetter, normalizeTheme, resolveColorReplacements, splitLines, splitToken, splitTokens, stringifyTokenStyle, toArray, tokenizeAnsiWithTheme, tokenizeWithTheme, tokensToHast, transformerDecorations };
1406
+ export { ShikiError, addClassToHast, applyColorReplacements, codeToHast, codeToHtml, codeToTokens, codeToTokensBase, codeToTokensWithThemes, createBundledHighlighter, createCssVariablesTheme, createHighlighterCore, createHighlighterCoreSync, createPositionConverter, createShikiInternal, createShikiInternalSync, createShikiPrimitive, createShikiPrimitiveAsync, createSingletonShorthands, flatTokenVariants, getLastGrammarState, getSingletonHighlighterCore, getTokenStyleObject, guessEmbeddedLanguages, hastToHtml, isNoneTheme, isPlainLang, isSpecialLang, isSpecialTheme, makeSingletonHighlighter, makeSingletonHighlighterCore, normalizeGetter, normalizeTheme, resolveColorReplacements, splitLines, splitToken, splitTokens, stringifyTokenStyle, toArray, tokenizeAnsiWithTheme, tokenizeWithTheme, tokensToHast, transformerDecorations };
package/dist/textmate.mjs CHANGED
@@ -1,3 +1,2 @@
1
- export * from "@shikijs/vscode-textmate"
2
-
3
- export { };
1
+ export * from "@shikijs/vscode-textmate";
2
+ export {};
package/dist/types.mjs CHANGED
@@ -1,3 +1,2 @@
1
- export * from "@shikijs/types"
2
-
3
- export { };
1
+ export * from "@shikijs/types";
2
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shikijs/core",
3
3
  "type": "module",
4
- "version": "4.0.1",
4
+ "version": "4.1.0",
5
5
  "description": "Core of Shiki",
6
6
  "author": "Pine Wu <octref@gmail.com>; Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -49,8 +49,8 @@
49
49
  "@shikijs/vscode-textmate": "^10.0.2",
50
50
  "@types/hast": "^3.0.4",
51
51
  "hast-util-to-html": "^9.0.5",
52
- "@shikijs/primitive": "4.0.1",
53
- "@shikijs/types": "4.0.1"
52
+ "@shikijs/primitive": "4.1.0",
53
+ "@shikijs/types": "4.1.0"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsdown",