@shikijs/twoslash 1.0.0 → 1.1.1

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.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { TwoslashReturn, TwoslashExecuteOptions, TwoslashOptions, NodeError, NodeTag, NodeQuery, NodeCompletion, NodeHover, NodeHighlight } from 'twoslash';
1
+ import { TwoslashReturn, TwoslashExecuteOptions, TwoslashGenericFunction, TwoslashOptions, NodeError, NodeTag, NodeQuery, NodeCompletion, NodeHover, NodeHighlight } from 'twoslash';
2
2
  import { CodeToHastOptions, ShikiTransformerContext, ShikiTransformerContextCommon, ShikiTransformer } from '@shikijs/core';
3
3
  import { ElementContent, Element, Text } from 'hast';
4
4
 
@@ -34,7 +34,7 @@ interface TransformerTwoslashOptions {
34
34
  /**
35
35
  * Custom instance of twoslasher function
36
36
  */
37
- twoslasher?: TwoslashShikiFunction;
37
+ twoslasher?: TwoslashShikiFunction | TwoslashGenericFunction;
38
38
  /**
39
39
  * Options to pass to twoslash
40
40
  */
@@ -48,6 +48,17 @@ interface TransformerTwoslashOptions {
48
48
  * @default true
49
49
  */
50
50
  throws?: boolean;
51
+ /**
52
+ * Custom error handler for twoslash errors
53
+ * When specified, `throws` will be ignored
54
+ * Optionally return a string to replace the code
55
+ */
56
+ onTwoslashError?: (error: unknown, code: string, lang: string, options: CodeToHastOptions) => string | void;
57
+ /**
58
+ * Custom error handler for Shiki errors
59
+ * When specified, `throws` will be ignored
60
+ */
61
+ onShikiError?: (error: unknown) => void;
51
62
  }
52
63
  interface TwoslashRenderer {
53
64
  lineError?: (this: ShikiTransformerContext, error: NodeError) => ElementContent[];
@@ -62,10 +73,6 @@ interface TwoslashRenderer {
62
73
  nodesHighlight?: (this: ShikiTransformerContext, highlight: NodeHighlight, nodes: ElementContent[]) => ElementContent[];
63
74
  }
64
75
 
65
- type CompletionItem = NonNullable<NodeCompletion['completions']>[number];
66
- declare const defaultCompletionIcons: Record<CompletionItem['kind'], Element | undefined>;
67
- declare const defaultCustomTagIcons: Record<string, Element | undefined>;
68
-
69
76
  interface RendererRichOptions {
70
77
  /**
71
78
  * Render JSDoc comments in hover popup.
@@ -80,7 +87,7 @@ interface RendererRichOptions {
80
87
  * If `false`, no icons will be rendered.
81
88
  * @default defaultCompletionIcons
82
89
  */
83
- completionIcons?: Partial<Record<CompletionItem['kind'], ElementContent>> | false;
90
+ completionIcons?: Partial<Record<string, ElementContent>> | false;
84
91
  /**
85
92
  * Custom icons for custom tags lines.
86
93
  * A map from tag name to a HAST node.
@@ -245,6 +252,10 @@ declare function defaultHoverInfoProcessor(type: string): string;
245
252
  */
246
253
  declare function rendererClassic(): TwoslashRenderer;
247
254
 
255
+ type CompletionItem = NonNullable<NodeCompletion['completions']>[number];
256
+ declare const defaultCompletionIcons: Record<string, Element | undefined>;
257
+ declare const defaultCustomTagIcons: Record<string, Element | undefined>;
258
+
248
259
  declare class ShikiTwoslashError extends Error {
249
260
  constructor(message: string);
250
261
  }
@@ -255,6 +266,6 @@ declare class ShikiTwoslashError extends Error {
255
266
  */
256
267
 
257
268
  declare function defaultTwoslashOptions(): TwoslashExecuteOptions;
258
- declare function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer;
269
+ declare function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer;
259
270
 
260
271
  export { type CompletionItem, type HastExtension, type RendererRichOptions, ShikiTwoslashError, type TransformerTwoslashOptions, type TwoslashRenderer, type TwoslashShikiFunction, type TwoslashShikiReturn, createTransformerFactory, defaultCompletionIcons, defaultCustomTagIcons, defaultHoverInfoProcessor, defaultTwoslashOptions, rendererClassic, rendererRich };
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TwoslashReturn, TwoslashExecuteOptions, TwoslashOptions, NodeError, NodeTag, NodeQuery, NodeCompletion, NodeHover, NodeHighlight } from 'twoslash';
1
+ import { TwoslashReturn, TwoslashExecuteOptions, TwoslashGenericFunction, TwoslashOptions, NodeError, NodeTag, NodeQuery, NodeCompletion, NodeHover, NodeHighlight } from 'twoslash';
2
2
  import { CodeToHastOptions, ShikiTransformerContext, ShikiTransformerContextCommon, ShikiTransformer } from '@shikijs/core';
3
3
  import { ElementContent, Element, Text } from 'hast';
4
4
 
@@ -34,7 +34,7 @@ interface TransformerTwoslashOptions {
34
34
  /**
35
35
  * Custom instance of twoslasher function
36
36
  */
37
- twoslasher?: TwoslashShikiFunction;
37
+ twoslasher?: TwoslashShikiFunction | TwoslashGenericFunction;
38
38
  /**
39
39
  * Options to pass to twoslash
40
40
  */
@@ -48,6 +48,17 @@ interface TransformerTwoslashOptions {
48
48
  * @default true
49
49
  */
50
50
  throws?: boolean;
51
+ /**
52
+ * Custom error handler for twoslash errors
53
+ * When specified, `throws` will be ignored
54
+ * Optionally return a string to replace the code
55
+ */
56
+ onTwoslashError?: (error: unknown, code: string, lang: string, options: CodeToHastOptions) => string | void;
57
+ /**
58
+ * Custom error handler for Shiki errors
59
+ * When specified, `throws` will be ignored
60
+ */
61
+ onShikiError?: (error: unknown) => void;
51
62
  }
52
63
  interface TwoslashRenderer {
53
64
  lineError?: (this: ShikiTransformerContext, error: NodeError) => ElementContent[];
@@ -62,10 +73,6 @@ interface TwoslashRenderer {
62
73
  nodesHighlight?: (this: ShikiTransformerContext, highlight: NodeHighlight, nodes: ElementContent[]) => ElementContent[];
63
74
  }
64
75
 
65
- type CompletionItem = NonNullable<NodeCompletion['completions']>[number];
66
- declare const defaultCompletionIcons: Record<CompletionItem['kind'], Element | undefined>;
67
- declare const defaultCustomTagIcons: Record<string, Element | undefined>;
68
-
69
76
  interface RendererRichOptions {
70
77
  /**
71
78
  * Render JSDoc comments in hover popup.
@@ -80,7 +87,7 @@ interface RendererRichOptions {
80
87
  * If `false`, no icons will be rendered.
81
88
  * @default defaultCompletionIcons
82
89
  */
83
- completionIcons?: Partial<Record<CompletionItem['kind'], ElementContent>> | false;
90
+ completionIcons?: Partial<Record<string, ElementContent>> | false;
84
91
  /**
85
92
  * Custom icons for custom tags lines.
86
93
  * A map from tag name to a HAST node.
@@ -245,6 +252,10 @@ declare function defaultHoverInfoProcessor(type: string): string;
245
252
  */
246
253
  declare function rendererClassic(): TwoslashRenderer;
247
254
 
255
+ type CompletionItem = NonNullable<NodeCompletion['completions']>[number];
256
+ declare const defaultCompletionIcons: Record<string, Element | undefined>;
257
+ declare const defaultCustomTagIcons: Record<string, Element | undefined>;
258
+
248
259
  declare class ShikiTwoslashError extends Error {
249
260
  constructor(message: string);
250
261
  }
@@ -255,6 +266,6 @@ declare class ShikiTwoslashError extends Error {
255
266
  */
256
267
 
257
268
  declare function defaultTwoslashOptions(): TwoslashExecuteOptions;
258
- declare function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer;
269
+ declare function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer;
259
270
 
260
271
  export { type CompletionItem, type HastExtension, type RendererRichOptions, ShikiTwoslashError, type TransformerTwoslashOptions, type TwoslashRenderer, type TwoslashShikiFunction, type TwoslashShikiReturn, createTransformerFactory, defaultCompletionIcons, defaultCustomTagIcons, defaultHoverInfoProcessor, defaultTwoslashOptions, rendererClassic, rendererRich };
package/dist/core.mjs CHANGED
@@ -557,52 +557,56 @@ function rendererRich(options = {}) {
557
557
  nodeCompletion(query, node) {
558
558
  if (node.type !== "text")
559
559
  throw new ShikiTwoslashError(`Renderer hook nodeCompletion only works on text nodes, got ${node.type}`);
560
- const items = query.completions.map((i) => ({
561
- type: "element",
562
- tagName: "li",
563
- properties: {},
564
- children: [
565
- ...completionIcons ? [{
566
- type: "element",
567
- tagName: "span",
568
- properties: { class: `twoslash-completions-icon completions-${i.kind.replace(/\s/g, "-")}` },
569
- children: [
570
- completionIcons[i.kind] || completionIcons.property
571
- ].filter(Boolean)
572
- }] : [],
573
- {
574
- type: "element",
575
- tagName: "span",
576
- properties: {
577
- class: i.kindModifiers?.split(",").includes("deprecated") ? "deprecated" : void 0
578
- },
579
- children: [
580
- {
581
- type: "element",
582
- tagName: "span",
583
- properties: { class: "twoslash-completions-matched" },
584
- children: [
585
- {
586
- type: "text",
587
- value: i.name.startsWith(query.completionsPrefix) ? query.completionsPrefix : ""
588
- }
589
- ]
560
+ const items = query.completions.map((i) => {
561
+ const kind = i.kind || "default";
562
+ const isDeprecated = "kindModifiers" in i && typeof i.kindModifiers === "string" && i.kindModifiers?.split(",").includes("deprecated");
563
+ return {
564
+ type: "element",
565
+ tagName: "li",
566
+ properties: {},
567
+ children: [
568
+ ...completionIcons ? [{
569
+ type: "element",
570
+ tagName: "span",
571
+ properties: { class: `twoslash-completions-icon completions-${kind.replace(/\s/g, "-")}` },
572
+ children: [
573
+ completionIcons[kind] || completionIcons.property
574
+ ].filter(Boolean)
575
+ }] : [],
576
+ {
577
+ type: "element",
578
+ tagName: "span",
579
+ properties: {
580
+ class: isDeprecated ? "deprecated" : void 0
590
581
  },
591
- {
592
- type: "element",
593
- tagName: "span",
594
- properties: { class: "twoslash-completions-unmatched" },
595
- children: [
596
- {
597
- type: "text",
598
- value: i.name.startsWith(query.completionsPrefix) ? i.name.slice(query.completionsPrefix.length || 0) : i.name
599
- }
600
- ]
601
- }
602
- ]
603
- }
604
- ]
605
- }));
582
+ children: [
583
+ {
584
+ type: "element",
585
+ tagName: "span",
586
+ properties: { class: "twoslash-completions-matched" },
587
+ children: [
588
+ {
589
+ type: "text",
590
+ value: i.name.startsWith(query.completionsPrefix) ? query.completionsPrefix : ""
591
+ }
592
+ ]
593
+ },
594
+ {
595
+ type: "element",
596
+ tagName: "span",
597
+ properties: { class: "twoslash-completions-unmatched" },
598
+ children: [
599
+ {
600
+ type: "text",
601
+ value: i.name.startsWith(query.completionsPrefix) ? i.name.slice(query.completionsPrefix.length || 0) : i.name
602
+ }
603
+ ]
604
+ }
605
+ ]
606
+ }
607
+ ]
608
+ };
609
+ });
606
610
  const cursor = extend(
607
611
  hast?.completionCursor,
608
612
  {
@@ -778,14 +782,14 @@ function defaultHoverInfoProcessor(type) {
778
782
  }
779
783
  function getErrorLevelClass(error) {
780
784
  switch (error.level) {
781
- case 0:
785
+ case "warning":
782
786
  return "twoslash-error-level-warning";
783
- case 1:
784
- return "";
785
- case 2:
787
+ case "suggestion":
786
788
  return "twoslash-error-level-suggestion";
789
+ case "message":
790
+ return "twoslash-error-level-message";
787
791
  default:
788
- return "twoslash-error-level-info";
792
+ return "";
789
793
  }
790
794
  }
791
795
 
@@ -879,7 +883,7 @@ function rendererClassic() {
879
883
  type: "element",
880
884
  tagName: "li",
881
885
  properties: {
882
- class: i.kindModifiers?.split(",").includes("deprecated") ? "deprecated" : void 0
886
+ class: "kindModifiers" in i && typeof i.kindModifiers === "string" && i.kindModifiers?.split(",").includes("deprecated") ? "deprecated" : void 0
883
887
  },
884
888
  children: [{
885
889
  type: "element",
@@ -986,9 +990,16 @@ function createTransformerFactory(defaultTwoslasher, defaultRenderer) {
986
990
  renderer = defaultRenderer,
987
991
  throws = true
988
992
  } = options;
993
+ const onTwoslashError = options.onTwoslashError || (throws ? (error) => {
994
+ throw error;
995
+ } : () => false);
996
+ const onShikiError = options.onShikiError || (throws ? (error) => {
997
+ throw error;
998
+ } : () => false);
989
999
  const trigger = explicitTrigger instanceof RegExp ? explicitTrigger : /\btwoslash\b/;
990
1000
  if (!renderer)
991
1001
  throw new ShikiTwoslashError("Missing renderer");
1002
+ const map = /* @__PURE__ */ new WeakMap();
992
1003
  const filter = options.filter || ((lang, _, options2) => langs.includes(lang) && (!explicitTrigger || trigger.test(options2.meta?.__raw || "")));
993
1004
  return {
994
1005
  preprocess(code) {
@@ -996,28 +1007,38 @@ function createTransformerFactory(defaultTwoslasher, defaultRenderer) {
996
1007
  if (lang in langAlias)
997
1008
  lang = langAlias[this.options.lang];
998
1009
  if (filter(lang, code, this.options)) {
999
- const twoslash = twoslasher(code, lang, twoslashOptions);
1000
- this.meta.twoslash = twoslash;
1001
- this.options.lang = twoslash.meta?.extension || lang;
1002
- return twoslash.code;
1010
+ try {
1011
+ const twoslash = twoslasher(code, lang, twoslashOptions);
1012
+ map.set(this.meta, twoslash);
1013
+ this.meta.twoslash = twoslash;
1014
+ this.options.lang = twoslash.meta?.extension || lang;
1015
+ return twoslash.code;
1016
+ } catch (error) {
1017
+ const result = onTwoslashError(error, code, lang, this.options);
1018
+ if (typeof result === "string")
1019
+ return code;
1020
+ }
1003
1021
  }
1004
1022
  },
1005
1023
  tokens(tokens) {
1006
- if (!this.meta.twoslash)
1024
+ const twoslash = map.get(this.meta);
1025
+ if (!twoslash)
1007
1026
  return;
1008
1027
  return splitTokens(
1009
1028
  tokens,
1010
- this.meta.twoslash.nodes.flatMap(
1029
+ twoslash.nodes.flatMap(
1011
1030
  (i) => ["hover", "error", "query", "highlight", "completion"].includes(i.type) ? [i.start, i.start + i.length] : []
1012
1031
  )
1013
1032
  );
1014
1033
  },
1015
1034
  pre(pre) {
1016
- if (this.meta.twoslash)
1017
- this.addClassToHast(pre, "twoslash lsp");
1035
+ const twoslash = map.get(this.meta);
1036
+ if (!twoslash)
1037
+ return;
1038
+ this.addClassToHast(pre, "twoslash lsp");
1018
1039
  },
1019
1040
  code(codeEl) {
1020
- const twoslash = this.meta.twoslash;
1041
+ const twoslash = map.get(this.meta);
1021
1042
  if (!twoslash)
1022
1043
  return;
1023
1044
  const insertAfterLine = (line, nodes) => {
@@ -1030,8 +1051,7 @@ function createTransformerFactory(defaultTwoslasher, defaultRenderer) {
1030
1051
  const lineEl = this.lines[line];
1031
1052
  index = codeEl.children.indexOf(lineEl);
1032
1053
  if (index === -1) {
1033
- if (throws)
1034
- throw new ShikiTwoslashError(`Cannot find line ${line} in code element`);
1054
+ onShikiError(new ShikiTwoslashError(`Cannot find line ${line} in code element`));
1035
1055
  return;
1036
1056
  }
1037
1057
  }
@@ -1068,6 +1088,10 @@ function createTransformerFactory(defaultTwoslasher, defaultRenderer) {
1068
1088
  continue;
1069
1089
  }
1070
1090
  const tokens = locateTextTokens(node.line, node.character, node.length);
1091
+ if (!tokens.length && !(node.type === "error" && renderer.nodesError)) {
1092
+ onShikiError(new ShikiTwoslashError(`Cannot find tokens for node: ${JSON.stringify(node)}`));
1093
+ continue;
1094
+ }
1071
1095
  const wrapTokens = (fn) => {
1072
1096
  const line = this.lines[node.line];
1073
1097
  let charIndex = 0;
@@ -1153,8 +1177,7 @@ function createTransformerFactory(defaultTwoslasher, defaultRenderer) {
1153
1177
  break;
1154
1178
  }
1155
1179
  default: {
1156
- if (throws)
1157
- throw new ShikiTwoslashError(`Unknown node type: ${node.type}`);
1180
+ onShikiError(new ShikiTwoslashError(`Unknown node type: ${node?.type}`));
1158
1181
  }
1159
1182
  }
1160
1183
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shikijs/twoslash",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.1.1",
5
5
  "description": "Shiki transformer for twoslash",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -49,11 +49,11 @@
49
49
  "dist"
50
50
  ],
51
51
  "dependencies": {
52
- "twoslash": "^0.1.2",
53
- "@shikijs/core": "1.0.0"
52
+ "twoslash": "^0.2.1",
53
+ "@shikijs/core": "1.1.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@iconify-json/carbon": "^1.1.28",
56
+ "@iconify-json/carbon": "^1.1.29",
57
57
  "@iconify-json/codicon": "^1.1.41",
58
58
  "@shikijs/twoslash": "^3.1.2",
59
59
  "hast-util-from-html": "^2.0.1",