prosemirror-highlight 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/README.md CHANGED
@@ -12,10 +12,9 @@ Highlight your [ProseMirror] code blocks with any syntax highlighter you like!
12
12
  <summary>Static loading of a fixed set of languages</summary>
13
13
 
14
14
  ```ts
15
- import { getSingletonHighlighter } from 'shiki'
16
-
17
15
  import { createHighlightPlugin } from 'prosemirror-highlight'
18
16
  import { createParser } from 'prosemirror-highlight/shiki'
17
+ import { getSingletonHighlighter } from 'shiki'
19
18
 
20
19
  const highlighter = await getSingletonHighlighter({
21
20
  themes: ['github-light'],
@@ -31,17 +30,42 @@ export const shikiPlugin = createHighlightPlugin({ parser })
31
30
  <summary>Dynamic loading of arbitrary languages</summary>
32
31
 
33
32
  ```ts
33
+ import { createHighlightPlugin } from 'prosemirror-highlight'
34
+ import { createParser, type Parser } from 'prosemirror-highlight/shiki'
35
+ import type { Decoration } from 'prosemirror-view'
34
36
  import {
35
- getSingletonHighlighter,
37
+ createHighlighter,
36
38
  type BuiltinLanguage,
37
39
  type Highlighter,
38
40
  } from 'shiki'
39
41
 
40
- import { createHighlightPlugin } from 'prosemirror-highlight'
41
- import { createParser, type Parser } from 'prosemirror-highlight/shiki'
42
-
43
42
  let highlighter: Highlighter | undefined
43
+ let highlighterPromise: Promise<void> | undefined
44
44
  let parser: Parser | undefined
45
+ const loadedLanguages = new Set<string>()
46
+
47
+ function loadHighlighter(): Promise<void> {
48
+ if (!highlighterPromise) {
49
+ highlighterPromise = createHighlighter({
50
+ themes: ['github-light', 'github-dark', 'github-dark-dimmed'],
51
+ langs: [],
52
+ }).then((h) => {
53
+ highlighter = h
54
+ })
55
+ }
56
+ return highlighterPromise
57
+ }
58
+
59
+ async function loadLanguage(
60
+ highlighter: Highlighter,
61
+ language: string,
62
+ ): Promise<void> {
63
+ try {
64
+ return await highlighter.loadLanguage(language as BuiltinLanguage)
65
+ } finally {
66
+ loadedLanguages.add(language)
67
+ }
68
+ }
45
69
 
46
70
  /**
47
71
  * Lazy load highlighter and highlighter languages.
@@ -50,23 +74,25 @@ let parser: Parser | undefined
50
74
  * promise that resolves when the highlighter or the language is loaded.
51
75
  * Otherwise, it returns an array of decorations.
52
76
  */
53
- const lazyParser: Parser = (options) => {
77
+ const lazyParser: Parser = (options): Promise<void> | Decoration[] => {
54
78
  if (!highlighter) {
55
- return getSingletonHighlighter({
56
- themes: ['github-light'],
57
- langs: [],
58
- }).then((h) => {
59
- highlighter = h
60
- })
79
+ return loadHighlighter()
61
80
  }
62
81
 
63
- const language = options.language as BuiltinLanguage
64
- if (language && !highlighter.getLoadedLanguages().includes(language)) {
65
- return highlighter.loadLanguage(language)
82
+ const language = options.language
83
+ if (language && !loadedLanguages.has(language)) {
84
+ return loadLanguage(highlighter, language)
66
85
  }
67
86
 
68
87
  if (!parser) {
69
- parser = createParser(highlighter)
88
+ parser = createParser(highlighter, {
89
+ themes: {
90
+ light: 'github-light',
91
+ dark: 'github-dark',
92
+ dim: 'github-dark-dimmed',
93
+ },
94
+ defaultColor: 'dim',
95
+ })
70
96
  }
71
97
 
72
98
  return parser(options)
@@ -105,7 +131,6 @@ You can use these variables to set the background color and text color of the co
105
131
  import 'highlight.js/styles/default.css'
106
132
 
107
133
  import { common, createLowlight } from 'lowlight'
108
-
109
134
  import { createHighlightPlugin } from 'prosemirror-highlight'
110
135
  import { createParser } from 'prosemirror-highlight/lowlight'
111
136
 
@@ -122,10 +147,9 @@ export const lowlightPlugin = createHighlightPlugin({ parser })
122
147
  <summary>Static loading of all languages</summary>
123
148
 
124
149
  ```ts
125
- import { refractor } from 'refractor/all'
126
-
127
150
  import { createHighlightPlugin } from 'prosemirror-highlight'
128
151
  import { createParser } from 'prosemirror-highlight/refractor'
152
+ import { refractor } from 'refractor/all'
129
153
 
130
154
  const parser = createParser(refractor)
131
155
  export const refractorPlugin = createHighlightPlugin({ parser })
@@ -0,0 +1,35 @@
1
+ import { Decoration } from "prosemirror-view";
2
+
3
+ //#region src/hast.ts
4
+ function fillFromRoot(decorations, node, from) {
5
+ for (const child of node.children) from = fillFromRootContent(decorations, child, from);
6
+ }
7
+ function fillFromRootContent(decorations, node, from) {
8
+ if (node.type === "element") {
9
+ const to = from + getElementSize(node);
10
+ const { className, ...rest } = node.properties || {};
11
+ decorations.push(Decoration.inline(from, to, {
12
+ class: className ? Array.isArray(className) ? className.join(" ") : String(className) : void 0,
13
+ ...rest,
14
+ nodeName: node.tagName
15
+ }));
16
+ return to;
17
+ } else if (node.type === "text") return from + node.value.length;
18
+ else return from;
19
+ }
20
+ function getElementSize(node) {
21
+ let size = 0;
22
+ for (const child of node.children) size += getElementContentSize(child);
23
+ return size;
24
+ }
25
+ function getElementContentSize(node) {
26
+ switch (node.type) {
27
+ case "element": return getElementSize(node);
28
+ case "text": return node.value.length;
29
+ default: return 0;
30
+ }
31
+ }
32
+
33
+ //#endregion
34
+ export { fillFromRoot as t };
35
+ //# sourceMappingURL=hast-Cn9tqyqN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hast-Cn9tqyqN.js","names":[],"sources":["../src/hast.ts"],"sourcesContent":["import type { Element, ElementContent, Root, RootContent } from 'hast'\nimport { Decoration } from 'prosemirror-view'\n\nexport function fillFromRoot(\n decorations: Decoration[],\n node: Root,\n from: number,\n) {\n for (const child of node.children) {\n from = fillFromRootContent(decorations, child, from)\n }\n}\n\nfunction fillFromRootContent(\n decorations: Decoration[],\n node: RootContent,\n from: number,\n): number {\n if (node.type === 'element') {\n const to = from + getElementSize(node)\n const { className, ...rest } = node.properties || {}\n decorations.push(\n Decoration.inline(from, to, {\n class: className\n ? Array.isArray(className)\n ? className.join(' ')\n : String(className)\n : undefined,\n ...rest,\n nodeName: node.tagName,\n }),\n )\n return to\n } else if (node.type === 'text') {\n return from + node.value.length\n } else {\n return from\n }\n}\n\nfunction getElementSize(node: Element): number {\n let size = 0\n\n for (const child of node.children) {\n size += getElementContentSize(child)\n }\n\n return size\n}\n\nfunction getElementContentSize(node: ElementContent): number {\n switch (node.type) {\n case 'element':\n return getElementSize(node)\n case 'text':\n return node.value.length\n default:\n return 0\n }\n}\n"],"mappings":";;;AAGA,SAAgB,aACd,aACA,MACA,MACA;AACA,MAAK,MAAM,SAAS,KAAK,SACvB,QAAO,oBAAoB,aAAa,OAAO,KAAK;;AAIxD,SAAS,oBACP,aACA,MACA,MACQ;AACR,KAAI,KAAK,SAAS,WAAW;EAC3B,MAAM,KAAK,OAAO,eAAe,KAAK;EACtC,MAAM,EAAE,WAAW,GAAG,SAAS,KAAK,cAAc,EAAE;AACpD,cAAY,KACV,WAAW,OAAO,MAAM,IAAI;GAC1B,OAAO,YACH,MAAM,QAAQ,UAAU,GACtB,UAAU,KAAK,IAAI,GACnB,OAAO,UAAU,GACnB;GACJ,GAAG;GACH,UAAU,KAAK;GAChB,CAAC,CACH;AACD,SAAO;YACE,KAAK,SAAS,OACvB,QAAO,OAAO,KAAK,MAAM;KAEzB,QAAO;;AAIX,SAAS,eAAe,MAAuB;CAC7C,IAAI,OAAO;AAEX,MAAK,MAAM,SAAS,KAAK,SACvB,SAAQ,sBAAsB,MAAM;AAGtC,QAAO;;AAGT,SAAS,sBAAsB,MAA8B;AAC3D,SAAQ,KAAK,MAAb;EACE,KAAK,UACH,QAAO,eAAe,KAAK;EAC7B,KAAK,OACH,QAAO,KAAK,MAAM;EACpB,QACE,QAAO"}
package/dist/index.d.ts CHANGED
@@ -1,80 +1,87 @@
1
- import { Node } from 'prosemirror-model';
2
- import { Transaction, Plugin } from 'prosemirror-state';
3
- import { Decoration, DecorationSet } from 'prosemirror-view';
4
- import { P as Parser, L as LanguageExtractor } from './types-BIUZQh-P.js';
1
+ import { n as Parser, t as LanguageExtractor } from "./types-DCbyVqHc.js";
2
+ import { Plugin, Transaction } from "prosemirror-state";
3
+ import { Decoration, DecorationSet } from "prosemirror-view";
4
+ import { Node } from "prosemirror-model";
5
5
 
6
+ //#region src/cache.d.ts
6
7
  /**
7
8
  * Represents a cache of doc positions to the node and decorations at that position
8
9
  */
9
10
  declare class DecorationCache {
10
- private cache;
11
- constructor(cache?: Map<number, [node: Node, decorations: Decoration[]]>);
12
- /**
13
- * Gets the cache entry at the given doc position, or null if it doesn't exist
14
- * @param pos The doc position of the node you want the cache for
15
- */
16
- get(pos: number): [node: Node, decorations: Decoration[]] | undefined;
17
- /**
18
- * Sets the cache entry at the given position with the give node/decoration
19
- * values
20
- * @param pos The doc position of the node to set the cache for
21
- * @param node The node to place in cache
22
- * @param decorations The decorations to place in cache
23
- */
24
- set(pos: number, node: Node, decorations: Decoration[]): void;
25
- /**
26
- * Removes the value at the oldPos (if it exists) and sets the new position to
27
- * the given values
28
- * @param oldPos The old node position to overwrite
29
- * @param newPos The new node position to set the cache for
30
- * @param node The new node to place in cache
31
- * @param decorations The new decorations to place in cache
32
- */
33
- private replace;
34
- /**
35
- * Removes the cache entry at the given position
36
- * @param pos The doc position to remove from cache
37
- */
38
- remove(pos: number): void;
39
- /**
40
- * Invalidates the cache by removing all decoration entries on nodes that have
41
- * changed, updating the positions of the nodes that haven't and removing all
42
- * the entries that have been deleted; NOTE: this does not affect the current
43
- * cache, but returns an entirely new one
44
- * @param tr A transaction to map the current cache to
45
- */
46
- invalidate(tr: Transaction): DecorationCache;
11
+ private cache;
12
+ constructor(cache?: Map<number, [node: Node, decorations: Decoration[]]>);
13
+ /**
14
+ * Gets the cache entry at the given doc position, or null if it doesn't exist
15
+ * @param pos The doc position of the node you want the cache for
16
+ */
17
+ get(pos: number): [node: Node, decorations: Decoration[]] | undefined;
18
+ /**
19
+ * Sets the cache entry at the given position with the give node/decoration
20
+ * values
21
+ * @param pos The doc position of the node to set the cache for
22
+ * @param node The node to place in cache
23
+ * @param decorations The decorations to place in cache
24
+ */
25
+ set(pos: number, node: Node, decorations: Decoration[]): void;
26
+ /**
27
+ * Removes the value at the oldPos (if it exists) and sets the new position to
28
+ * the given values
29
+ * @param oldPos The old node position to overwrite
30
+ * @param newPos The new node position to set the cache for
31
+ * @param node The new node to place in cache
32
+ * @param decorations The new decorations to place in cache
33
+ */
34
+ private replace;
35
+ /**
36
+ * Removes the cache entry at the given position
37
+ * @param pos The doc position to remove from cache
38
+ */
39
+ remove(pos: number): void;
40
+ /**
41
+ * Invalidates the cache by removing all decoration entries on nodes that have
42
+ * changed, updating the positions of the nodes that haven't and removing all
43
+ * the entries that have been deleted; NOTE: this does not affect the current
44
+ * cache, but returns an entirely new one
45
+ * @param tr A transaction to map the current cache to
46
+ */
47
+ invalidate(tr: Transaction): DecorationCache;
47
48
  }
48
-
49
+ //#endregion
50
+ //#region src/plugin.d.ts
49
51
  /**
50
52
  * Describes the current state of the highlightPlugin
51
53
  */
52
54
  interface HighlightPluginState {
53
- cache: DecorationCache;
54
- decorations: DecorationSet;
55
- promises: Promise<void>[];
55
+ cache: DecorationCache;
56
+ decorations: DecorationSet | undefined;
57
+ promises: Promise<void>[];
56
58
  }
57
59
  /**
58
60
  * Creates a plugin that highlights the contents of all nodes (via Decorations)
59
61
  * with a type passed in blockTypes
60
62
  */
61
- declare function createHighlightPlugin({ parser, nodeTypes, languageExtractor, }: {
62
- /**
63
- * A function that returns an array of decorations for the given node text
64
- * content, language, and position.
65
- */
66
- parser: Parser;
67
- /**
68
- * An array containing all the node type name to target for highlighting.
69
- *
70
- * @default ['code_block', 'codeBlock']
71
- */
72
- nodeTypes?: string[];
73
- /**
74
- * A function that returns the language string to use when highlighting that
75
- * node. By default, it returns `node.attrs.language`.
76
- */
77
- languageExtractor?: LanguageExtractor;
63
+ declare function createHighlightPlugin({
64
+ parser,
65
+ nodeTypes,
66
+ languageExtractor
67
+ }: {
68
+ /**
69
+ * A function that returns an array of decorations for the given node text
70
+ * content, language, and position.
71
+ */
72
+ parser: Parser;
73
+ /**
74
+ * An array containing all the node type name to target for highlighting.
75
+ *
76
+ * @default ['code_block', 'codeBlock']
77
+ */
78
+ nodeTypes?: string[];
79
+ /**
80
+ * A function that returns the language string to use when highlighting that
81
+ * node. By default, it returns `node.attrs.language`.
82
+ */
83
+ languageExtractor?: LanguageExtractor;
78
84
  }): Plugin<HighlightPluginState>;
79
-
80
- export { DecorationCache, type HighlightPluginState, LanguageExtractor, Parser, createHighlightPlugin };
85
+ //#endregion
86
+ export { DecorationCache, type HighlightPluginState, type LanguageExtractor, type Parser, createHighlightPlugin };
87
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cache.ts","../src/plugin.ts"],"mappings":";;;;;;;;;cAOa,eAAA;EAAA,QACH,KAAA;cAGN,KAAA,GAAQ,GAAA,UAAa,IAAA,EAAM,IAAA,EAAiB,WAAA,EAAa,UAAA;EAA9B;;;;EAS7B,GAAA,CAAI,GAAA,YAAW,IAAA,EAAA,IAAA,EAAA,WAAA,EAAA,UAAA;EAWQ;;;;;;;EAAvB,GAAA,CAAI,GAAA,UAAa,IAAA,EAAM,IAAA,EAAiB,WAAA,EAAa,UAAA;EApB3C;;;;;;;;EAAA,QAoCF,OAAA;EA3BO;;;;EAyCf,MAAA,CAAO,GAAA;EA9BgB;;;;;;;EAyCvB,UAAA,CAAW,EAAA,EAAI,WAAA,GAAc,eAAA;AAAA;;;;;AAjE/B;UCGiB,oBAAA;EACf,KAAA,EAAO,eAAA;EACP,WAAA,EAAa,aAAA;EACb,QAAA,EAAU,OAAA;AAAA;;;;;iBAOI,qBAAA,CAAA;EACd,MAAA;EACA,SAAA;EACA;AAAA;EDiD6B;;;;EC3C7B,MAAA,EAAQ,MAAA;EDlBE;;;;;ECyBV,SAAA;EDhBA;;;;ECsBA,iBAAA,GAAoB,iBAAA;AAAA,IAClB,MAAA,CAAO,oBAAA"}
package/dist/index.js CHANGED
@@ -1,188 +1,186 @@
1
- // src/cache.ts
2
- var DecorationCache = class _DecorationCache {
3
- constructor(cache) {
4
- this.cache = new Map(cache);
5
- }
6
- /**
7
- * Gets the cache entry at the given doc position, or null if it doesn't exist
8
- * @param pos The doc position of the node you want the cache for
9
- */
10
- get(pos) {
11
- return this.cache.get(pos);
12
- }
13
- /**
14
- * Sets the cache entry at the given position with the give node/decoration
15
- * values
16
- * @param pos The doc position of the node to set the cache for
17
- * @param node The node to place in cache
18
- * @param decorations The decorations to place in cache
19
- */
20
- set(pos, node, decorations) {
21
- if (pos < 0) {
22
- return;
23
- }
24
- this.cache.set(pos, [node, decorations]);
25
- }
26
- /**
27
- * Removes the value at the oldPos (if it exists) and sets the new position to
28
- * the given values
29
- * @param oldPos The old node position to overwrite
30
- * @param newPos The new node position to set the cache for
31
- * @param node The new node to place in cache
32
- * @param decorations The new decorations to place in cache
33
- */
34
- replace(oldPos, newPos, node, decorations) {
35
- this.remove(oldPos);
36
- this.set(newPos, node, decorations);
37
- }
38
- /**
39
- * Removes the cache entry at the given position
40
- * @param pos The doc position to remove from cache
41
- */
42
- remove(pos) {
43
- this.cache.delete(pos);
44
- }
45
- /**
46
- * Invalidates the cache by removing all decoration entries on nodes that have
47
- * changed, updating the positions of the nodes that haven't and removing all
48
- * the entries that have been deleted; NOTE: this does not affect the current
49
- * cache, but returns an entirely new one
50
- * @param tr A transaction to map the current cache to
51
- */
52
- invalidate(tr) {
53
- const returnCache = new _DecorationCache(this.cache);
54
- const mapping = tr.mapping;
55
- this.cache.forEach(([node, decorations], pos) => {
56
- if (pos < 0) {
57
- return;
58
- }
59
- const result = mapping.mapResult(pos);
60
- const mappedNode = tr.doc.nodeAt(result.pos);
61
- if (result.deleted || !(mappedNode == null ? void 0 : mappedNode.eq(node))) {
62
- returnCache.remove(pos);
63
- } else if (pos !== result.pos) {
64
- const updatedDecorations = decorations.map((d) => {
65
- return d.map(mapping, 0, 0);
66
- }).filter((d) => d != null);
67
- returnCache.replace(pos, result.pos, mappedNode, updatedDecorations);
68
- }
69
- });
70
- return returnCache;
71
- }
72
- };
73
-
74
- // src/plugin.ts
75
1
  import { Plugin, PluginKey } from "prosemirror-state";
76
2
  import { DecorationSet } from "prosemirror-view";
77
- function createHighlightPlugin({
78
- parser,
79
- nodeTypes = ["code_block", "codeBlock"],
80
- languageExtractor = (node) => node.attrs.language
81
- }) {
82
- const key = new PluginKey("prosemirror-highlight");
83
- return new Plugin({
84
- key,
85
- state: {
86
- init(_, instance) {
87
- const cache = new DecorationCache();
88
- const [decorations, promises] = calculateDecoration(
89
- instance.doc,
90
- parser,
91
- nodeTypes,
92
- languageExtractor,
93
- cache
94
- );
95
- return { cache, decorations, promises };
96
- },
97
- apply: (tr, data) => {
98
- const cache = data.cache.invalidate(tr);
99
- const refresh = !!tr.getMeta("prosemirror-highlight-refresh");
100
- if (!tr.docChanged && !refresh) {
101
- const decorations2 = data.decorations.map(tr.mapping, tr.doc);
102
- const promises2 = data.promises;
103
- return { cache, decorations: decorations2, promises: promises2 };
104
- }
105
- const [decorations, promises] = calculateDecoration(
106
- tr.doc,
107
- parser,
108
- nodeTypes,
109
- languageExtractor,
110
- cache
111
- );
112
- return { cache, decorations, promises };
113
- }
114
- },
115
- view: (view) => {
116
- const promises = /* @__PURE__ */ new Set();
117
- const refresh = () => {
118
- if (promises.size > 0) {
119
- return;
120
- }
121
- const tr = view.state.tr.setMeta("prosemirror-highlight-refresh", true);
122
- view.dispatch(tr);
123
- };
124
- const check = () => {
125
- var _a;
126
- const state = key.getState(view.state);
127
- for (const promise of (_a = state == null ? void 0 : state.promises) != null ? _a : []) {
128
- promises.add(promise);
129
- promise.then(() => {
130
- promises.delete(promise);
131
- refresh();
132
- }).catch(() => {
133
- promises.delete(promise);
134
- });
135
- }
136
- };
137
- check();
138
- return {
139
- update: () => {
140
- check();
141
- }
142
- };
143
- },
144
- props: {
145
- decorations(state) {
146
- var _a;
147
- return (_a = this.getState(state)) == null ? void 0 : _a.decorations;
148
- }
149
- }
150
- });
3
+
4
+ //#region src/cache.ts
5
+ /**
6
+ * Represents a cache of doc positions to the node and decorations at that position
7
+ */
8
+ var DecorationCache = class DecorationCache {
9
+ constructor(cache) {
10
+ this.cache = new Map(cache);
11
+ }
12
+ /**
13
+ * Gets the cache entry at the given doc position, or null if it doesn't exist
14
+ * @param pos The doc position of the node you want the cache for
15
+ */
16
+ get(pos) {
17
+ return this.cache.get(pos);
18
+ }
19
+ /**
20
+ * Sets the cache entry at the given position with the give node/decoration
21
+ * values
22
+ * @param pos The doc position of the node to set the cache for
23
+ * @param node The node to place in cache
24
+ * @param decorations The decorations to place in cache
25
+ */
26
+ set(pos, node, decorations) {
27
+ if (pos < 0) return;
28
+ this.cache.set(pos, [node, decorations]);
29
+ }
30
+ /**
31
+ * Removes the value at the oldPos (if it exists) and sets the new position to
32
+ * the given values
33
+ * @param oldPos The old node position to overwrite
34
+ * @param newPos The new node position to set the cache for
35
+ * @param node The new node to place in cache
36
+ * @param decorations The new decorations to place in cache
37
+ */
38
+ replace(oldPos, newPos, node, decorations) {
39
+ this.remove(oldPos);
40
+ this.set(newPos, node, decorations);
41
+ }
42
+ /**
43
+ * Removes the cache entry at the given position
44
+ * @param pos The doc position to remove from cache
45
+ */
46
+ remove(pos) {
47
+ this.cache.delete(pos);
48
+ }
49
+ /**
50
+ * Invalidates the cache by removing all decoration entries on nodes that have
51
+ * changed, updating the positions of the nodes that haven't and removing all
52
+ * the entries that have been deleted; NOTE: this does not affect the current
53
+ * cache, but returns an entirely new one
54
+ * @param tr A transaction to map the current cache to
55
+ */
56
+ invalidate(tr) {
57
+ const returnCache = new DecorationCache(this.cache);
58
+ const mapping = tr.mapping;
59
+ this.cache.forEach(([node, decorations], pos) => {
60
+ if (pos < 0) return;
61
+ const result = mapping.mapResult(pos);
62
+ const mappedNode = tr.doc.nodeAt(result.pos);
63
+ if (result.deleted || !mappedNode?.eq(node)) returnCache.remove(pos);
64
+ else if (pos !== result.pos) {
65
+ const updatedDecorations = decorations.map((d) => {
66
+ return d.map(mapping, 0, 0);
67
+ }).filter((d) => d != null);
68
+ returnCache.replace(pos, result.pos, mappedNode, updatedDecorations);
69
+ }
70
+ });
71
+ return returnCache;
72
+ }
73
+ };
74
+
75
+ //#endregion
76
+ //#region src/plugin.ts
77
+ /**
78
+ * Creates a plugin that highlights the contents of all nodes (via Decorations)
79
+ * with a type passed in blockTypes
80
+ */
81
+ function createHighlightPlugin({ parser, nodeTypes = ["code_block", "codeBlock"], languageExtractor = (node) => node.attrs.language }) {
82
+ const key = new PluginKey("prosemirror-highlight");
83
+ return new Plugin({
84
+ key,
85
+ state: {
86
+ init(_, instance) {
87
+ const cache = new DecorationCache();
88
+ const [decorations, promises] = calculateDecoration(instance.doc, parser, nodeTypes, languageExtractor, cache);
89
+ return {
90
+ cache,
91
+ decorations,
92
+ promises
93
+ };
94
+ },
95
+ apply: (tr, data) => {
96
+ const cache = data.cache.invalidate(tr);
97
+ const refresh = !!tr.getMeta("prosemirror-highlight-refresh");
98
+ if (!tr.docChanged && !refresh) return {
99
+ cache,
100
+ decorations: data.decorations?.map(tr.mapping, tr.doc),
101
+ promises: data.promises
102
+ };
103
+ const [decorations, promises] = calculateDecoration(tr.doc, parser, nodeTypes, languageExtractor, cache);
104
+ return {
105
+ cache,
106
+ decorations,
107
+ promises
108
+ };
109
+ }
110
+ },
111
+ view: (view) => {
112
+ const promises = /* @__PURE__ */ new Set();
113
+ const refresh = () => {
114
+ if (promises.size > 0) return;
115
+ const tr = view.state.tr.setMeta("prosemirror-highlight-refresh", true);
116
+ view.dispatch(tr);
117
+ };
118
+ const check = () => {
119
+ const state = key.getState(view.state);
120
+ for (const promise of state?.promises ?? []) {
121
+ promises.add(promise);
122
+ promise.then(() => {
123
+ promises.delete(promise);
124
+ refresh();
125
+ }).catch((error) => {
126
+ console.error("[prosemirror-highlight] Error resolving parser:", error);
127
+ promises.delete(promise);
128
+ });
129
+ }
130
+ };
131
+ check();
132
+ return { update: () => {
133
+ check();
134
+ } };
135
+ },
136
+ props: { decorations(state) {
137
+ return this.getState(state)?.decorations;
138
+ } }
139
+ });
151
140
  }
152
141
  function calculateDecoration(doc, parser, nodeTypes, languageExtractor, cache) {
153
- const result = [];
154
- const promises = [];
155
- doc.descendants((node, pos) => {
156
- if (!node.type.isTextblock) {
157
- return true;
158
- }
159
- if (nodeTypes.includes(node.type.name)) {
160
- const language = languageExtractor(node);
161
- const cached = cache.get(pos);
162
- if (cached) {
163
- const [_, decorations] = cached;
164
- result.push(...decorations);
165
- } else {
166
- const decorations = parser({
167
- content: node.textContent,
168
- language: language || void 0,
169
- pos,
170
- size: node.nodeSize
171
- });
172
- if (decorations && Array.isArray(decorations)) {
173
- cache.set(pos, node, decorations);
174
- result.push(...decorations);
175
- } else if (decorations instanceof Promise) {
176
- cache.remove(pos);
177
- promises.push(decorations);
178
- }
179
- }
180
- }
181
- return false;
182
- });
183
- return [DecorationSet.create(doc, result), promises];
142
+ const allDecorations = [];
143
+ const promises = [];
144
+ const nodes = collectCodeBlocks(doc, nodeTypes);
145
+ try {
146
+ for (const [node, pos] of nodes) {
147
+ const language = languageExtractor(node);
148
+ const cached = cache.get(pos);
149
+ if (cached) {
150
+ const [_, decorations] = cached;
151
+ if (decorations.length > 0) allDecorations.push(decorations);
152
+ } else {
153
+ const parsed = parser({
154
+ content: node.textContent,
155
+ language: language || void 0,
156
+ pos,
157
+ size: node.nodeSize
158
+ });
159
+ if (parsed && Array.isArray(parsed)) {
160
+ cache.set(pos, node, parsed);
161
+ if (parsed.length > 0) allDecorations.push(parsed);
162
+ } else if (parsed instanceof Promise) {
163
+ cache.remove(pos);
164
+ promises.push(parsed);
165
+ } else console.error(`[prosemirror-highlight] Invalid parser result:`, parsed);
166
+ }
167
+ }
168
+ } catch (error) {
169
+ console.error(`[prosemirror-highlight] Error parsing code blocks:`, error);
170
+ }
171
+ return [allDecorations.length > 0 ? DecorationSet.create(doc, allDecorations.flat()) : void 0, promises];
184
172
  }
185
- export {
186
- DecorationCache,
187
- createHighlightPlugin
188
- };
173
+ function collectCodeBlocks(doc, nodeTypes) {
174
+ const nodes = [];
175
+ doc.descendants((node, pos) => {
176
+ if (node.type.isTextblock && nodeTypes.includes(node.type.name)) {
177
+ nodes.push([node, pos]);
178
+ return false;
179
+ }
180
+ });
181
+ return nodes;
182
+ }
183
+
184
+ //#endregion
185
+ export { DecorationCache, createHighlightPlugin };
186
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/cache.ts","../src/plugin.ts"],"sourcesContent":["import type { Node as ProseMirrorNode } from 'prosemirror-model'\nimport type { Transaction } from 'prosemirror-state'\nimport type { Decoration } from 'prosemirror-view'\n\n/**\n * Represents a cache of doc positions to the node and decorations at that position\n */\nexport class DecorationCache {\n private cache: Map<number, [node: ProseMirrorNode, decorations: Decoration[]]>\n\n constructor(\n cache?: Map<number, [node: ProseMirrorNode, decorations: Decoration[]]>,\n ) {\n this.cache = new Map(cache)\n }\n\n /**\n * Gets the cache entry at the given doc position, or null if it doesn't exist\n * @param pos The doc position of the node you want the cache for\n */\n get(pos: number) {\n return this.cache.get(pos)\n }\n\n /**\n * Sets the cache entry at the given position with the give node/decoration\n * values\n * @param pos The doc position of the node to set the cache for\n * @param node The node to place in cache\n * @param decorations The decorations to place in cache\n */\n set(pos: number, node: ProseMirrorNode, decorations: Decoration[]): void {\n if (pos < 0) {\n return\n }\n\n this.cache.set(pos, [node, decorations])\n }\n\n /**\n * Removes the value at the oldPos (if it exists) and sets the new position to\n * the given values\n * @param oldPos The old node position to overwrite\n * @param newPos The new node position to set the cache for\n * @param node The new node to place in cache\n * @param decorations The new decorations to place in cache\n */\n private replace(\n oldPos: number,\n newPos: number,\n node: ProseMirrorNode,\n decorations: Decoration[],\n ): void {\n this.remove(oldPos)\n this.set(newPos, node, decorations)\n }\n\n /**\n * Removes the cache entry at the given position\n * @param pos The doc position to remove from cache\n */\n remove(pos: number): void {\n this.cache.delete(pos)\n }\n\n /**\n * Invalidates the cache by removing all decoration entries on nodes that have\n * changed, updating the positions of the nodes that haven't and removing all\n * the entries that have been deleted; NOTE: this does not affect the current\n * cache, but returns an entirely new one\n * @param tr A transaction to map the current cache to\n */\n invalidate(tr: Transaction): DecorationCache {\n const returnCache = new DecorationCache(this.cache)\n const mapping = tr.mapping\n\n this.cache.forEach(([node, decorations], pos) => {\n if (pos < 0) {\n return\n }\n\n const result = mapping.mapResult(pos)\n const mappedNode = tr.doc.nodeAt(result.pos)\n\n if (result.deleted || !mappedNode?.eq(node)) {\n returnCache.remove(pos)\n } else if (pos !== result.pos) {\n // update the decorations' from/to values to match the new node position\n const updatedDecorations = decorations\n .map((d): Decoration | null => {\n // @ts-expect-error: internal api\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n return d.map(mapping, 0, 0) as Decoration | null\n })\n .filter((d): d is Decoration => d != null)\n returnCache.replace(pos, result.pos, mappedNode, updatedDecorations)\n }\n })\n\n return returnCache\n }\n}\n","import type { Node as ProseMirrorNode } from 'prosemirror-model'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport { type Decoration, DecorationSet } from 'prosemirror-view'\n\nimport { DecorationCache } from './cache'\nimport type { LanguageExtractor, Parser } from './types'\n\n/**\n * Describes the current state of the highlightPlugin\n */\nexport interface HighlightPluginState {\n cache: DecorationCache\n decorations: DecorationSet | undefined\n promises: Promise<void>[]\n}\n\n/**\n * Creates a plugin that highlights the contents of all nodes (via Decorations)\n * with a type passed in blockTypes\n */\nexport function createHighlightPlugin({\n parser,\n nodeTypes = ['code_block', 'codeBlock'],\n languageExtractor = (node) => node.attrs.language as string | undefined,\n}: {\n /**\n * A function that returns an array of decorations for the given node text\n * content, language, and position.\n */\n parser: Parser\n\n /**\n * An array containing all the node type name to target for highlighting.\n *\n * @default ['code_block', 'codeBlock']\n */\n nodeTypes?: string[]\n\n /**\n * A function that returns the language string to use when highlighting that\n * node. By default, it returns `node.attrs.language`.\n */\n languageExtractor?: LanguageExtractor\n}): Plugin<HighlightPluginState> {\n const key = new PluginKey<HighlightPluginState>('prosemirror-highlight')\n\n return new Plugin<HighlightPluginState>({\n key,\n state: {\n init(_, instance) {\n const cache = new DecorationCache()\n const [decorations, promises] = calculateDecoration(\n instance.doc,\n parser,\n nodeTypes,\n languageExtractor,\n cache,\n )\n\n return { cache, decorations, promises }\n },\n apply: (tr, data) => {\n const cache = data.cache.invalidate(tr)\n const refresh = !!tr.getMeta('prosemirror-highlight-refresh')\n\n if (!tr.docChanged && !refresh) {\n const decorations = data.decorations?.map(tr.mapping, tr.doc)\n const promises = data.promises\n return { cache, decorations, promises }\n }\n\n const [decorations, promises] = calculateDecoration(\n tr.doc,\n parser,\n nodeTypes,\n languageExtractor,\n cache,\n )\n return { cache, decorations, promises }\n },\n },\n view: (view) => {\n const promises = new Set<Promise<void>>()\n\n // Refresh the decorations when all promises resolve\n const refresh = () => {\n if (promises.size > 0) {\n return\n }\n const tr = view.state.tr.setMeta('prosemirror-highlight-refresh', true)\n view.dispatch(tr)\n }\n\n const check = () => {\n const state = key.getState(view.state)\n\n for (const promise of state?.promises ?? []) {\n promises.add(promise)\n promise\n .then(() => {\n promises.delete(promise)\n refresh()\n })\n .catch((error) => {\n console.error(\n '[prosemirror-highlight] Error resolving parser:',\n error,\n )\n promises.delete(promise)\n })\n }\n }\n\n check()\n\n return {\n update: () => {\n check()\n },\n }\n },\n props: {\n decorations(this, state) {\n return this.getState(state)?.decorations\n },\n },\n })\n}\n\nfunction calculateDecoration(\n doc: ProseMirrorNode,\n parser: Parser,\n nodeTypes: string[],\n languageExtractor: LanguageExtractor,\n cache: DecorationCache,\n): [DecorationSet | undefined, Promise<void>[]] {\n const allDecorations: Decoration[][] = []\n const promises: Promise<void>[] = []\n const nodes = collectCodeBlocks(doc, nodeTypes)\n\n try {\n for (const [node, pos] of nodes) {\n const language = languageExtractor(node)\n const cached = cache.get(pos)\n\n if (cached) {\n const [_, decorations] = cached\n if (decorations.length > 0) {\n allDecorations.push(decorations)\n }\n } else {\n const parsed = parser({\n content: node.textContent,\n language: language || undefined,\n pos,\n size: node.nodeSize,\n })\n if (parsed && Array.isArray(parsed)) {\n cache.set(pos, node, parsed)\n if (parsed.length > 0) {\n allDecorations.push(parsed)\n }\n } else if (parsed instanceof Promise) {\n cache.remove(pos)\n promises.push(parsed)\n } else {\n console.error(\n `[prosemirror-highlight] Invalid parser result:`,\n parsed,\n )\n }\n }\n }\n } catch (error) {\n console.error(`[prosemirror-highlight] Error parsing code blocks:`, error)\n }\n\n const decorationSet =\n allDecorations.length > 0\n ? DecorationSet.create(doc, allDecorations.flat())\n : undefined\n return [decorationSet, promises]\n}\n\nfunction collectCodeBlocks(\n doc: ProseMirrorNode,\n nodeTypes: string[],\n): Array<[node: ProseMirrorNode, pos: number]> {\n const nodes: Array<[node: ProseMirrorNode, pos: number]> = []\n doc.descendants((node, pos) => {\n if (node.type.isTextblock && nodeTypes.includes(node.type.name)) {\n nodes.push([node, pos])\n return false\n }\n })\n return nodes\n}\n"],"mappings":";;;;;;;AAOA,IAAa,kBAAb,MAAa,gBAAgB;CAG3B,YACE,OACA;AACA,OAAK,QAAQ,IAAI,IAAI,MAAM;;;;;;CAO7B,IAAI,KAAa;AACf,SAAO,KAAK,MAAM,IAAI,IAAI;;;;;;;;;CAU5B,IAAI,KAAa,MAAuB,aAAiC;AACvE,MAAI,MAAM,EACR;AAGF,OAAK,MAAM,IAAI,KAAK,CAAC,MAAM,YAAY,CAAC;;;;;;;;;;CAW1C,AAAQ,QACN,QACA,QACA,MACA,aACM;AACN,OAAK,OAAO,OAAO;AACnB,OAAK,IAAI,QAAQ,MAAM,YAAY;;;;;;CAOrC,OAAO,KAAmB;AACxB,OAAK,MAAM,OAAO,IAAI;;;;;;;;;CAUxB,WAAW,IAAkC;EAC3C,MAAM,cAAc,IAAI,gBAAgB,KAAK,MAAM;EACnD,MAAM,UAAU,GAAG;AAEnB,OAAK,MAAM,SAAS,CAAC,MAAM,cAAc,QAAQ;AAC/C,OAAI,MAAM,EACR;GAGF,MAAM,SAAS,QAAQ,UAAU,IAAI;GACrC,MAAM,aAAa,GAAG,IAAI,OAAO,OAAO,IAAI;AAE5C,OAAI,OAAO,WAAW,CAAC,YAAY,GAAG,KAAK,CACzC,aAAY,OAAO,IAAI;YACd,QAAQ,OAAO,KAAK;IAE7B,MAAM,qBAAqB,YACxB,KAAK,MAAyB;AAG7B,YAAO,EAAE,IAAI,SAAS,GAAG,EAAE;MAC3B,CACD,QAAQ,MAAuB,KAAK,KAAK;AAC5C,gBAAY,QAAQ,KAAK,OAAO,KAAK,YAAY,mBAAmB;;IAEtE;AAEF,SAAO;;;;;;;;;;AC/EX,SAAgB,sBAAsB,EACpC,QACA,YAAY,CAAC,cAAc,YAAY,EACvC,qBAAqB,SAAS,KAAK,MAAM,YAoBV;CAC/B,MAAM,MAAM,IAAI,UAAgC,wBAAwB;AAExE,QAAO,IAAI,OAA6B;EACtC;EACA,OAAO;GACL,KAAK,GAAG,UAAU;IAChB,MAAM,QAAQ,IAAI,iBAAiB;IACnC,MAAM,CAAC,aAAa,YAAY,oBAC9B,SAAS,KACT,QACA,WACA,mBACA,MACD;AAED,WAAO;KAAE;KAAO;KAAa;KAAU;;GAEzC,QAAQ,IAAI,SAAS;IACnB,MAAM,QAAQ,KAAK,MAAM,WAAW,GAAG;IACvC,MAAM,UAAU,CAAC,CAAC,GAAG,QAAQ,gCAAgC;AAE7D,QAAI,CAAC,GAAG,cAAc,CAAC,QAGrB,QAAO;KAAE;KAAO,aAFI,KAAK,aAAa,IAAI,GAAG,SAAS,GAAG,IAAI;KAEhC,UADZ,KAAK;KACiB;IAGzC,MAAM,CAAC,aAAa,YAAY,oBAC9B,GAAG,KACH,QACA,WACA,mBACA,MACD;AACD,WAAO;KAAE;KAAO;KAAa;KAAU;;GAE1C;EACD,OAAO,SAAS;GACd,MAAM,2BAAW,IAAI,KAAoB;GAGzC,MAAM,gBAAgB;AACpB,QAAI,SAAS,OAAO,EAClB;IAEF,MAAM,KAAK,KAAK,MAAM,GAAG,QAAQ,iCAAiC,KAAK;AACvE,SAAK,SAAS,GAAG;;GAGnB,MAAM,cAAc;IAClB,MAAM,QAAQ,IAAI,SAAS,KAAK,MAAM;AAEtC,SAAK,MAAM,WAAW,OAAO,YAAY,EAAE,EAAE;AAC3C,cAAS,IAAI,QAAQ;AACrB,aACG,WAAW;AACV,eAAS,OAAO,QAAQ;AACxB,eAAS;OACT,CACD,OAAO,UAAU;AAChB,cAAQ,MACN,mDACA,MACD;AACD,eAAS,OAAO,QAAQ;OACxB;;;AAIR,UAAO;AAEP,UAAO,EACL,cAAc;AACZ,WAAO;MAEV;;EAEH,OAAO,EACL,YAAkB,OAAO;AACvB,UAAO,KAAK,SAAS,MAAM,EAAE;KAEhC;EACF,CAAC;;AAGJ,SAAS,oBACP,KACA,QACA,WACA,mBACA,OAC8C;CAC9C,MAAM,iBAAiC,EAAE;CACzC,MAAM,WAA4B,EAAE;CACpC,MAAM,QAAQ,kBAAkB,KAAK,UAAU;AAE/C,KAAI;AACF,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO;GAC/B,MAAM,WAAW,kBAAkB,KAAK;GACxC,MAAM,SAAS,MAAM,IAAI,IAAI;AAE7B,OAAI,QAAQ;IACV,MAAM,CAAC,GAAG,eAAe;AACzB,QAAI,YAAY,SAAS,EACvB,gBAAe,KAAK,YAAY;UAE7B;IACL,MAAM,SAAS,OAAO;KACpB,SAAS,KAAK;KACd,UAAU,YAAY;KACtB;KACA,MAAM,KAAK;KACZ,CAAC;AACF,QAAI,UAAU,MAAM,QAAQ,OAAO,EAAE;AACnC,WAAM,IAAI,KAAK,MAAM,OAAO;AAC5B,SAAI,OAAO,SAAS,EAClB,gBAAe,KAAK,OAAO;eAEpB,kBAAkB,SAAS;AACpC,WAAM,OAAO,IAAI;AACjB,cAAS,KAAK,OAAO;UAErB,SAAQ,MACN,kDACA,OACD;;;UAIA,OAAO;AACd,UAAQ,MAAM,sDAAsD,MAAM;;AAO5E,QAAO,CAHL,eAAe,SAAS,IACpB,cAAc,OAAO,KAAK,eAAe,MAAM,CAAC,GAChD,QACiB,SAAS;;AAGlC,SAAS,kBACP,KACA,WAC6C;CAC7C,MAAM,QAAqD,EAAE;AAC7D,KAAI,aAAa,MAAM,QAAQ;AAC7B,MAAI,KAAK,KAAK,eAAe,UAAU,SAAS,KAAK,KAAK,KAAK,EAAE;AAC/D,SAAM,KAAK,CAAC,MAAM,IAAI,CAAC;AACvB,UAAO;;GAET;AACF,QAAO"}
@@ -1,12 +1,12 @@
1
- import { Root } from 'hast';
2
- import { P as Parser } from './types-BIUZQh-P.js';
3
- import 'prosemirror-model';
4
- import 'prosemirror-view';
1
+ import { n as Parser } from "./types-DCbyVqHc.js";
2
+ import { Root } from "hast";
5
3
 
4
+ //#region src/lowlight.d.ts
6
5
  type Lowlight = {
7
- highlight: (language: string, value: string) => Root;
8
- highlightAuto: (value: string) => Root;
6
+ highlight: (language: string, value: string) => Root;
7
+ highlightAuto: (value: string) => Root;
9
8
  };
10
9
  declare function createParser(lowlight: Lowlight): Parser;
11
-
12
- export { type Lowlight, Parser, createParser };
10
+ //#endregion
11
+ export { Lowlight, type Parser, createParser };
12
+ //# sourceMappingURL=lowlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lowlight.d.ts","names":[],"sources":["../src/lowlight.ts"],"mappings":";;;;KAQY,QAAA;EACV,SAAA,GAAY,QAAA,UAAkB,KAAA,aAAkB,IAAA;EAChD,aAAA,GAAgB,KAAA,aAAkB,IAAA;AAAA;AAAA,iBAGpB,YAAA,CAAa,QAAA,EAAU,QAAA,GAAW,MAAA"}
package/dist/lowlight.js CHANGED
@@ -1,17 +1,15 @@
1
- import {
2
- fillFromRoot
3
- } from "./chunk-ZZGBRRBM.js";
1
+ import { t as fillFromRoot } from "./hast-Cn9tqyqN.js";
4
2
 
5
- // src/lowlight.ts
3
+ //#region src/lowlight.ts
6
4
  function createParser(lowlight) {
7
- return function highlighter({ content, language, pos }) {
8
- const root = language ? lowlight.highlight(language, content) : lowlight.highlightAuto(content);
9
- const decorations = [];
10
- const from = pos + 1;
11
- fillFromRoot(decorations, root, from);
12
- return decorations;
13
- };
5
+ return function highlighter({ content, language, pos }) {
6
+ const root = language ? lowlight.highlight(language, content) : lowlight.highlightAuto(content);
7
+ const decorations = [];
8
+ fillFromRoot(decorations, root, pos + 1);
9
+ return decorations;
10
+ };
14
11
  }
15
- export {
16
- createParser
17
- };
12
+
13
+ //#endregion
14
+ export { createParser };
15
+ //# sourceMappingURL=lowlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lowlight.js","names":[],"sources":["../src/lowlight.ts"],"sourcesContent":["import type { Root } from 'hast'\nimport type { Decoration } from 'prosemirror-view'\n\nimport { fillFromRoot } from './hast'\nimport type { Parser } from './types'\n\nexport type { Parser }\n\nexport type Lowlight = {\n highlight: (language: string, value: string) => Root\n highlightAuto: (value: string) => Root\n}\n\nexport function createParser(lowlight: Lowlight): Parser {\n return function highlighter({ content, language, pos }) {\n const root = language\n ? lowlight.highlight(language, content)\n : lowlight.highlightAuto(content)\n\n const decorations: Decoration[] = []\n const from = pos + 1\n fillFromRoot(decorations, root, from)\n return decorations\n }\n}\n"],"mappings":";;;AAaA,SAAgB,aAAa,UAA4B;AACvD,QAAO,SAAS,YAAY,EAAE,SAAS,UAAU,OAAO;EACtD,MAAM,OAAO,WACT,SAAS,UAAU,UAAU,QAAQ,GACrC,SAAS,cAAc,QAAQ;EAEnC,MAAM,cAA4B,EAAE;AAEpC,eAAa,aAAa,MADb,MAAM,EACkB;AACrC,SAAO"}
@@ -1,8 +1,8 @@
1
- import { Refractor } from 'refractor/core';
2
- import { P as Parser } from './types-BIUZQh-P.js';
3
- import 'prosemirror-model';
4
- import 'prosemirror-view';
1
+ import { n as Parser } from "./types-DCbyVqHc.js";
2
+ import { Refractor } from "refractor/core";
5
3
 
4
+ //#region src/refractor.d.ts
6
5
  declare function createParser(refractor: Refractor): Parser;
7
-
8
- export { Parser, createParser };
6
+ //#endregion
7
+ export { type Parser, createParser };
8
+ //# sourceMappingURL=refractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refractor.d.ts","names":[],"sources":["../src/refractor.ts"],"mappings":";;;;iBASgB,YAAA,CAAa,SAAA,EAAW,SAAA,GAAY,MAAA"}
package/dist/refractor.js CHANGED
@@ -1,17 +1,15 @@
1
- import {
2
- fillFromRoot
3
- } from "./chunk-ZZGBRRBM.js";
1
+ import { t as fillFromRoot } from "./hast-Cn9tqyqN.js";
4
2
 
5
- // src/refractor.ts
3
+ //#region src/refractor.ts
6
4
  function createParser(refractor) {
7
- return function highlighter({ content, language, pos }) {
8
- const root = refractor.highlight(content, language || "");
9
- const decorations = [];
10
- const from = pos + 1;
11
- fillFromRoot(decorations, root, from);
12
- return decorations;
13
- };
5
+ return function highlighter({ content, language, pos }) {
6
+ const root = refractor.highlight(content, language || "");
7
+ const decorations = [];
8
+ fillFromRoot(decorations, root, pos + 1);
9
+ return decorations;
10
+ };
14
11
  }
15
- export {
16
- createParser
17
- };
12
+
13
+ //#endregion
14
+ export { createParser };
15
+ //# sourceMappingURL=refractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refractor.js","names":[],"sources":["../src/refractor.ts"],"sourcesContent":["import type { Root } from 'hast'\nimport type { Decoration } from 'prosemirror-view'\nimport type { Refractor } from 'refractor/core'\n\nimport { fillFromRoot } from './hast'\nimport type { Parser } from './types'\n\nexport type { Parser }\n\nexport function createParser(refractor: Refractor): Parser {\n return function highlighter({ content, language, pos }) {\n const root: Root = refractor.highlight(content, language || '')\n\n const decorations: Decoration[] = []\n const from = pos + 1\n\n fillFromRoot(decorations, root, from)\n return decorations\n }\n}\n"],"mappings":";;;AASA,SAAgB,aAAa,WAA8B;AACzD,QAAO,SAAS,YAAY,EAAE,SAAS,UAAU,OAAO;EACtD,MAAM,OAAa,UAAU,UAAU,SAAS,YAAY,GAAG;EAE/D,MAAM,cAA4B,EAAE;AAGpC,eAAa,aAAa,MAFb,MAAM,EAEkB;AACrC,SAAO"}
package/dist/shiki.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { HighlighterGeneric, CodeToTokensOptions } from '@shikijs/types';
2
- import { P as Parser } from './types-BIUZQh-P.js';
3
- import 'prosemirror-model';
4
- import 'prosemirror-view';
1
+ import { n as Parser } from "./types-DCbyVqHc.js";
2
+ import { CodeToTokensOptions, HighlighterGeneric } from "@shikijs/types";
5
3
 
4
+ //#region src/shiki.d.ts
6
5
  declare function createParser<Language extends string = string, Theme extends string = string>(highlighter: HighlighterGeneric<Language, Theme>, options?: CodeToTokensOptions<Language, Theme>): Parser;
7
-
8
- export { Parser, createParser };
6
+ //#endregion
7
+ export { type Parser, createParser };
8
+ //# sourceMappingURL=shiki.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.d.ts","names":[],"sources":["../src/shiki.ts"],"mappings":";;;;iBAOgB,YAAA,iEAAA,CAId,WAAA,EAAa,kBAAA,CAAmB,QAAA,EAAU,KAAA,GAC1C,OAAA,GAAU,mBAAA,CAAoB,QAAA,EAAU,KAAA,IACvC,MAAA"}
package/dist/shiki.js CHANGED
@@ -1,44 +1,44 @@
1
- // src/shiki.ts
2
1
  import { Decoration } from "prosemirror-view";
2
+
3
+ //#region src/shiki.ts
3
4
  function createParser(highlighter, options) {
4
- return function parser({ content, language, pos, size }) {
5
- var _a;
6
- const decorations = [];
7
- const { tokens, fg, bg, rootStyle } = highlighter.codeToTokens(content, {
8
- lang: language,
9
- // Use provided options for themes or just use first loaded theme
10
- ...options != null ? options : {
11
- theme: highlighter.getLoadedThemes()[0]
12
- }
13
- });
14
- const style = rootStyle || (fg && bg ? `--prosemirror-highlight:${fg};--prosemirror-highlight-bg:${bg}` : "");
15
- if (style) {
16
- const decoration = Decoration.node(pos, pos + size, { style });
17
- decorations.push(decoration);
18
- }
19
- let from = pos + 1;
20
- for (const line of tokens) {
21
- for (const token of line) {
22
- const to = from + token.content.length;
23
- const decoration = Decoration.inline(from, to, {
24
- // When using `options.themes` the `htmlStyle` field will be set, otherwise `color` will be set
25
- style: stringifyTokenStyle(
26
- (_a = token.htmlStyle) != null ? _a : `color: ${token.color}`
27
- ),
28
- class: "shiki"
29
- });
30
- decorations.push(decoration);
31
- from = to;
32
- }
33
- from += 1;
34
- }
35
- return decorations;
36
- };
5
+ return function parser({ content, language, pos, size }) {
6
+ const decorations = [];
7
+ const { tokens, fg, bg, rootStyle } = highlighter.codeToTokens(content, {
8
+ lang: language,
9
+ ...options ?? { theme: highlighter.getLoadedThemes()[0] }
10
+ });
11
+ const style = rootStyle || (fg && bg ? `--prosemirror-highlight:${fg};--prosemirror-highlight-bg:${bg}` : "");
12
+ if (style) {
13
+ const decoration = Decoration.node(pos, pos + size, { style });
14
+ decorations.push(decoration);
15
+ }
16
+ let from = pos + 1;
17
+ for (const line of tokens) {
18
+ for (const token of line) {
19
+ const to = from + token.content.length;
20
+ const decoration = Decoration.inline(from, to, {
21
+ style: stringifyTokenStyle(token.htmlStyle ?? `color: ${token.color}`),
22
+ class: "shiki"
23
+ });
24
+ decorations.push(decoration);
25
+ from = to;
26
+ }
27
+ from += 1;
28
+ }
29
+ return decorations;
30
+ };
37
31
  }
32
+ /**
33
+ * Copied from https://github.com/shikijs/shiki/blob/f76a371dbc2752cba341023df00ebfe9b66cb3f6/packages/core/src/utils.ts#L213
34
+ *
35
+ * Copy instead of import it from `shiki` to avoid importing the `shiki` package in this file.
36
+ */
38
37
  function stringifyTokenStyle(token) {
39
- if (typeof token === "string") return token;
40
- return Object.entries(token).map(([key, value]) => `${key}:${value}`).join(";");
38
+ if (typeof token === "string") return token;
39
+ return Object.entries(token).map(([key, value]) => `${key}:${value}`).join(";");
41
40
  }
42
- export {
43
- createParser
44
- };
41
+
42
+ //#endregion
43
+ export { createParser };
44
+ //# sourceMappingURL=shiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.js","names":[],"sources":["../src/shiki.ts"],"sourcesContent":["import type { CodeToTokensOptions, HighlighterGeneric } from '@shikijs/types'\nimport { Decoration } from 'prosemirror-view'\n\nimport type { Parser } from './types'\n\nexport type { Parser }\n\nexport function createParser<\n Language extends string = string,\n Theme extends string = string,\n>(\n highlighter: HighlighterGeneric<Language, Theme>,\n options?: CodeToTokensOptions<Language, Theme>,\n): Parser {\n return function parser({ content, language, pos, size }) {\n const decorations: Decoration[] = []\n\n const { tokens, fg, bg, rootStyle } = highlighter.codeToTokens(content, {\n lang: language as Language | undefined,\n\n // Use provided options for themes or just use first loaded theme\n ...(options ?? {\n theme: highlighter.getLoadedThemes()[0],\n }),\n })\n\n const style =\n rootStyle ||\n (fg && bg\n ? `--prosemirror-highlight:${fg};--prosemirror-highlight-bg:${bg}`\n : '')\n\n if (style) {\n const decoration = Decoration.node(pos, pos + size, { style })\n decorations.push(decoration)\n }\n\n let from = pos + 1\n\n for (const line of tokens) {\n for (const token of line) {\n const to = from + token.content.length\n\n const decoration = Decoration.inline(from, to, {\n // When using `options.themes` the `htmlStyle` field will be set, otherwise `color` will be set\n style: stringifyTokenStyle(\n token.htmlStyle ?? `color: ${token.color}`,\n ),\n class: 'shiki',\n })\n\n decorations.push(decoration)\n\n from = to\n }\n\n from += 1\n }\n\n return decorations\n }\n}\n\n/**\n * Copied from https://github.com/shikijs/shiki/blob/f76a371dbc2752cba341023df00ebfe9b66cb3f6/packages/core/src/utils.ts#L213\n *\n * Copy instead of import it from `shiki` to avoid importing the `shiki` package in this file.\n */\nfunction stringifyTokenStyle(token: string | Record<string, string>): string {\n if (typeof token === 'string') return token\n return Object.entries(token)\n .map(([key, value]) => `${key}:${value}`)\n .join(';')\n}\n"],"mappings":";;;AAOA,SAAgB,aAId,aACA,SACQ;AACR,QAAO,SAAS,OAAO,EAAE,SAAS,UAAU,KAAK,QAAQ;EACvD,MAAM,cAA4B,EAAE;EAEpC,MAAM,EAAE,QAAQ,IAAI,IAAI,cAAc,YAAY,aAAa,SAAS;GACtE,MAAM;GAGN,GAAI,WAAW,EACb,OAAO,YAAY,iBAAiB,CAAC,IACtC;GACF,CAAC;EAEF,MAAM,QACJ,cACC,MAAM,KACH,2BAA2B,GAAG,8BAA8B,OAC5D;AAEN,MAAI,OAAO;GACT,MAAM,aAAa,WAAW,KAAK,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAC9D,eAAY,KAAK,WAAW;;EAG9B,IAAI,OAAO,MAAM;AAEjB,OAAK,MAAM,QAAQ,QAAQ;AACzB,QAAK,MAAM,SAAS,MAAM;IACxB,MAAM,KAAK,OAAO,MAAM,QAAQ;IAEhC,MAAM,aAAa,WAAW,OAAO,MAAM,IAAI;KAE7C,OAAO,oBACL,MAAM,aAAa,UAAU,MAAM,QACpC;KACD,OAAO;KACR,CAAC;AAEF,gBAAY,KAAK,WAAW;AAE5B,WAAO;;AAGT,WAAQ;;AAGV,SAAO;;;;;;;;AASX,SAAS,oBAAoB,OAAgD;AAC3E,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAO,OAAO,QAAQ,MAAM,CACzB,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,QAAQ,CACxC,KAAK,IAAI"}
@@ -1,7 +1,7 @@
1
- import { P as Parser } from './types-BIUZQh-P.js';
2
- import 'prosemirror-model';
3
- import 'prosemirror-view';
1
+ import { n as Parser } from "./types-DCbyVqHc.js";
4
2
 
3
+ //#region src/sugar-high.d.ts
5
4
  declare function createParser(): Parser;
6
-
7
- export { Parser, createParser };
5
+ //#endregion
6
+ export { type Parser, createParser };
7
+ //# sourceMappingURL=sugar-high.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sugar-high.d.ts","names":[],"sources":["../src/sugar-high.ts"],"mappings":";;;iBASgB,YAAA,CAAA,GAAgB,MAAA"}
@@ -1,24 +1,26 @@
1
- // src/sugar-high.ts
2
1
  import { Decoration } from "prosemirror-view";
3
- import { tokenize, SugarHigh } from "sugar-high";
4
- var types = SugarHigh.TokenTypes;
2
+ import { SugarHigh, tokenize } from "sugar-high";
3
+
4
+ //#region src/sugar-high.ts
5
+ const types = SugarHigh.TokenTypes;
5
6
  function createParser() {
6
- return function parser({ content, pos }) {
7
- const decorations = [];
8
- const tokens = tokenize(content);
9
- let from = pos + 1;
10
- for (const [type, content2] of tokens) {
11
- const to = from + content2.length;
12
- const decoration = Decoration.inline(from, to, {
13
- class: `sh__token--${types[type]}`,
14
- style: `color: var(--sh-${types[type]})`
15
- });
16
- decorations.push(decoration);
17
- from = to;
18
- }
19
- return decorations;
20
- };
7
+ return function parser({ content, pos }) {
8
+ const decorations = [];
9
+ const tokens = tokenize(content);
10
+ let from = pos + 1;
11
+ for (const [type, content] of tokens) {
12
+ const to = from + content.length;
13
+ const decoration = Decoration.inline(from, to, {
14
+ class: `sh__token--${types[type]}`,
15
+ style: `color: var(--sh-${types[type]})`
16
+ });
17
+ decorations.push(decoration);
18
+ from = to;
19
+ }
20
+ return decorations;
21
+ };
21
22
  }
22
- export {
23
- createParser
24
- };
23
+
24
+ //#endregion
25
+ export { createParser };
26
+ //# sourceMappingURL=sugar-high.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sugar-high.js","names":[],"sources":["../src/sugar-high.ts"],"sourcesContent":["import { Decoration } from 'prosemirror-view'\nimport { tokenize, SugarHigh } from 'sugar-high'\n\nimport type { Parser } from './types'\n\nexport type { Parser }\n\nconst types = SugarHigh.TokenTypes\n\nexport function createParser(): Parser {\n return function parser({ content, pos }) {\n const decorations: Decoration[] = []\n\n const tokens = tokenize(content)\n\n let from = pos + 1\n\n for (const [type, content] of tokens) {\n const to = from + content.length\n\n const decoration = Decoration.inline(from, to, {\n class: `sh__token--${types[type]}`,\n style: `color: var(--sh-${types[type]})`,\n })\n\n decorations.push(decoration)\n\n from = to\n }\n\n return decorations\n }\n}\n"],"mappings":";;;;AAOA,MAAM,QAAQ,UAAU;AAExB,SAAgB,eAAuB;AACrC,QAAO,SAAS,OAAO,EAAE,SAAS,OAAO;EACvC,MAAM,cAA4B,EAAE;EAEpC,MAAM,SAAS,SAAS,QAAQ;EAEhC,IAAI,OAAO,MAAM;AAEjB,OAAK,MAAM,CAAC,MAAM,YAAY,QAAQ;GACpC,MAAM,KAAK,OAAO,QAAQ;GAE1B,MAAM,aAAa,WAAW,OAAO,MAAM,IAAI;IAC7C,OAAO,cAAc,MAAM;IAC3B,OAAO,mBAAmB,MAAM,MAAM;IACvC,CAAC;AAEF,eAAY,KAAK,WAAW;AAE5B,UAAO;;AAGT,SAAO"}
@@ -0,0 +1,35 @@
1
+ import { Decoration } from "prosemirror-view";
2
+ import { Node } from "prosemirror-model";
3
+
4
+ //#region src/types.d.ts
5
+ /**
6
+ * A function that parses the text content of a code block node and returns an
7
+ * array of ProseMirror decorations. If the underlying syntax highlighter is
8
+ * still loading, you can return a promise that will be resolved when the
9
+ * highlighter is ready.
10
+ */
11
+ type Parser = (options: {
12
+ /**
13
+ * The text content of the code block node.
14
+ */
15
+ content: string;
16
+ /**
17
+ * The start position of the code block node.
18
+ */
19
+ pos: number;
20
+ /**
21
+ * The language of the code block node.
22
+ */
23
+ language?: string;
24
+ /**
25
+ * The size of the code block node.
26
+ */
27
+ size: number;
28
+ }) => Decoration[] | Promise<void>;
29
+ /**
30
+ * A function that extracts the language of a code block node.
31
+ */
32
+ type LanguageExtractor = (node: Node) => string | undefined;
33
+ //#endregion
34
+ export { Parser as n, LanguageExtractor as t };
35
+ //# sourceMappingURL=types-DCbyVqHc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-DCbyVqHc.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;AASA;;;;KAAY,MAAA,IAAU,OAAA;EASpB;;;EALA,OAAA;EAgBI;;;EAXJ,GAAA;EAgBU;;;EAXV,QAAA;EAWoD;;;EANpD,IAAA;AAAA,MACI,UAAA,KAAe,OAAA;;;;KAKT,iBAAA,IAAqB,IAAA,EAAM,IAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prosemirror-highlight",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "description": "A ProseMirror plugin to highlight code blocks",
6
6
  "author": "ocavue <ocavue@gmail.com>",
7
7
  "license": "MIT",
@@ -63,6 +63,9 @@
63
63
  "sugar-high": "^0.6.1 || ^0.7.0 || ^0.8.0 || ^0.9.0"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
+ "@shikijs/types": {
67
+ "optional": true
68
+ },
66
69
  "@types/hast": {
67
70
  "optional": true
68
71
  },
@@ -87,53 +90,44 @@
87
90
  "refractor": {
88
91
  "optional": true
89
92
  },
90
- "@shikijs/types": {
91
- "optional": true
92
- },
93
93
  "sugar-high": {
94
94
  "optional": true
95
95
  }
96
96
  },
97
97
  "devDependencies": {
98
- "@antfu/ni": "^23.3.1",
99
- "@ocavue/eslint-config": "^2.12.5",
98
+ "@ocavue/eslint-config": "^3.11.2",
99
+ "@ocavue/tsconfig": "^0.6.3",
100
+ "@shikijs/types": "^3.22.0",
100
101
  "@types/hast": "^3.0.4",
101
- "@types/node": "^20.17.5",
102
- "eslint": "^9.19.0",
102
+ "@types/node": "^24.0.0",
103
+ "eslint": "^9.39.2",
103
104
  "highlight.js": "^11.11.1",
104
- "jsdom": "^25.0.1",
105
+ "jsdom": "^28.0.0",
105
106
  "lowlight": "^3.3.0",
106
- "pkg-pr-new": "^0.0.41",
107
- "prettier": "^3.5.2",
107
+ "pkg-pr-new": "^0.0.63",
108
+ "prettier": "^3.8.1",
108
109
  "prosemirror-example-setup": "^1.2.3",
109
- "prosemirror-model": "^1.24.1",
110
- "prosemirror-schema-basic": "^1.2.3",
111
- "prosemirror-state": "^1.4.3",
112
- "prosemirror-transform": "^1.10.2",
113
- "prosemirror-view": "^1.38.0",
110
+ "prosemirror-model": "^1.25.4",
111
+ "prosemirror-schema-basic": "^1.2.4",
112
+ "prosemirror-state": "^1.4.4",
113
+ "prosemirror-transform": "^1.11.0",
114
+ "prosemirror-view": "^1.41.6",
114
115
  "refractor": "^5.0.0",
115
- "shiki": "^3.2.1",
116
- "sugar-high": "^0.9.3",
117
- "tsup": "^8.4.0",
118
- "typescript": "^5.7.3",
119
- "vite": "^6.2.0",
120
- "vitest": "^3.0.5"
121
- },
122
- "renovate": {
123
- "dependencyDashboard": true,
124
- "extends": [
125
- "github>ocavue/config-renovate"
126
- ]
116
+ "shiki": "^3.22.0",
117
+ "sugar-high": "^0.9.5",
118
+ "tsdown": "^0.20.3",
119
+ "typescript": "^5.9.3",
120
+ "vite": "^7.3.1",
121
+ "vitest": "^4.0.18"
127
122
  },
128
123
  "scripts": {
129
124
  "dev": "vite",
130
- "build": "tsup",
125
+ "build": "tsdown",
131
126
  "build:playground": "vite build",
132
127
  "lint": "eslint .",
133
128
  "fix": "eslint --fix . && prettier --write .",
134
- "start": "esno src/index.ts",
135
129
  "test": "vitest",
136
- "typecheck": "tsc --noEmit"
130
+ "typecheck": "tsc -b"
137
131
  },
138
132
  "typesVersions": {
139
133
  "*": {
@@ -1,46 +0,0 @@
1
- // src/hast.ts
2
- import { Decoration } from "prosemirror-view";
3
- function fillFromRoot(decorations, node, from) {
4
- for (const child of node.children) {
5
- from = fillFromRootContent(decorations, child, from);
6
- }
7
- }
8
- function fillFromRootContent(decorations, node, from) {
9
- if (node.type === "element") {
10
- const to = from + getElementSize(node);
11
- const { className, ...rest } = node.properties || {};
12
- decorations.push(
13
- Decoration.inline(from, to, {
14
- class: className ? Array.isArray(className) ? className.join(" ") : String(className) : void 0,
15
- ...rest,
16
- nodeName: node.tagName
17
- })
18
- );
19
- return to;
20
- } else if (node.type === "text") {
21
- return from + node.value.length;
22
- } else {
23
- return from;
24
- }
25
- }
26
- function getElementSize(node) {
27
- let size = 0;
28
- for (const child of node.children) {
29
- size += getElementContentSize(child);
30
- }
31
- return size;
32
- }
33
- function getElementContentSize(node) {
34
- switch (node.type) {
35
- case "element":
36
- return getElementSize(node);
37
- case "text":
38
- return node.value.length;
39
- default:
40
- return 0;
41
- }
42
- }
43
-
44
- export {
45
- fillFromRoot
46
- };
@@ -1,33 +0,0 @@
1
- import { Node } from 'prosemirror-model';
2
- import { Decoration } from 'prosemirror-view';
3
-
4
- /**
5
- * A function that parses the text content of a code block node and returns an
6
- * array of ProseMirror decorations. If the underlying syntax highlighter is
7
- * still loading, you can return a promise that will be resolved when the
8
- * highlighter is ready.
9
- */
10
- type Parser = (options: {
11
- /**
12
- * The text content of the code block node.
13
- */
14
- content: string;
15
- /**
16
- * The start position of the code block node.
17
- */
18
- pos: number;
19
- /**
20
- * The language of the code block node.
21
- */
22
- language?: string;
23
- /**
24
- * The size of the code block node.
25
- */
26
- size: number;
27
- }) => Decoration[] | Promise<void>;
28
- /**
29
- * A function that extracts the language of a code block node.
30
- */
31
- type LanguageExtractor = (node: Node) => string | undefined;
32
-
33
- export type { LanguageExtractor as L, Parser as P };