tree-sitter-ts-highlight-react 0.1.2
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 +85 -0
- package/dist/index.cjs +342 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +310 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# tree-sitter-ts-highlight-react
|
|
2
|
+
|
|
3
|
+
React components and hooks for [`tree-sitter-ts-highlight`](https://www.npmjs.com/package/tree-sitter-ts-highlight), with React-element rendering (no `dangerouslySetInnerHTML` required).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install tree-sitter-ts-highlight-react tree-sitter-ts-highlight tree-sitter-ts
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Peer dependencies
|
|
12
|
+
|
|
13
|
+
This package expects these to be installed in your app:
|
|
14
|
+
|
|
15
|
+
- `react` (`^18` or `^19`)
|
|
16
|
+
- `tree-sitter-ts`
|
|
17
|
+
- `tree-sitter-ts-highlight`
|
|
18
|
+
|
|
19
|
+
## Theme CSS
|
|
20
|
+
|
|
21
|
+
Import a theme from `tree-sitter-ts-highlight` once in your app entry:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import "tree-sitter-ts-highlight/themes/github-dark.css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Basic usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Highlight, HighlightDiff } from "tree-sitter-ts-highlight-react";
|
|
31
|
+
|
|
32
|
+
export function Example() {
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<Highlight
|
|
36
|
+
code={`const n: number = 42;`}
|
|
37
|
+
language="typescript"
|
|
38
|
+
options={{ lineNumbers: true }}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<HighlightDiff
|
|
42
|
+
oldCode={`const n = 1;`}
|
|
43
|
+
newCode={`const n = 2;`}
|
|
44
|
+
language="typescript"
|
|
45
|
+
options={{ view: "side-by-side" }}
|
|
46
|
+
/>
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### Components
|
|
55
|
+
|
|
56
|
+
- `Highlight`
|
|
57
|
+
- Props: `code`, `language`, `options?`, `preProps?`, `codeProps?`
|
|
58
|
+
- Renders highlighted output as `<pre><code>` React nodes
|
|
59
|
+
- `HighlightDiff`
|
|
60
|
+
- Props: `oldCode`, `newCode`, `language`, `options?`, `containerProps?`
|
|
61
|
+
- Renders highlighted diffs (inline or side-by-side)
|
|
62
|
+
|
|
63
|
+
### Hooks
|
|
64
|
+
|
|
65
|
+
- `useHighlightedHtml({ code, language, options? })`
|
|
66
|
+
- Returns highlighted HTML without wrapping `<pre>`
|
|
67
|
+
- `useHighlightedDiffHtml({ oldCode, newCode, language, options? })`
|
|
68
|
+
- Returns highlighted diff HTML
|
|
69
|
+
|
|
70
|
+
### Re-exports
|
|
71
|
+
|
|
72
|
+
- Runtime exports from `tree-sitter-ts-highlight` are re-exported by this package.
|
|
73
|
+
- Type exports from both `tree-sitter-ts-highlight` and `tree-sitter-ts` are re-exported.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import type { HighlightOptions } from "tree-sitter-ts-highlight-react";
|
|
77
|
+
import type * as TsTypes from "tree-sitter-ts-highlight-react";
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run build
|
|
84
|
+
npm test
|
|
85
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
Highlight: () => Highlight,
|
|
25
|
+
HighlightDiff: () => HighlightDiff,
|
|
26
|
+
useHighlightedDiffHtml: () => useHighlightedDiffHtml,
|
|
27
|
+
useHighlightedHtml: () => useHighlightedHtml
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/components/Highlight.tsx
|
|
32
|
+
var import_tree_sitter_ts_highlight2 = require("tree-sitter-ts-highlight");
|
|
33
|
+
var import_tree_sitter_ts = require("tree-sitter-ts");
|
|
34
|
+
var import_react = require("react");
|
|
35
|
+
|
|
36
|
+
// src/react-renderer.tsx
|
|
37
|
+
var import_tree_sitter_ts_highlight = require("tree-sitter-ts-highlight");
|
|
38
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
function renderTokenNodes(tokens, options = {}) {
|
|
40
|
+
const prefix = options.classPrefix ?? "hlts-";
|
|
41
|
+
const decorated = options.decorations && options.decorations.length > 0 ? (0, import_tree_sitter_ts_highlight.applyDecorations)(tokens, options.decorations) : tokens.map((token) => ({
|
|
42
|
+
token,
|
|
43
|
+
extraClasses: [],
|
|
44
|
+
extraAttrs: {},
|
|
45
|
+
extraStyle: void 0
|
|
46
|
+
}));
|
|
47
|
+
return decorated.map(({ token, extraClasses, extraAttrs, extraStyle }, index) => {
|
|
48
|
+
if (token.category === "whitespace" || token.category === "newline") {
|
|
49
|
+
return token.value;
|
|
50
|
+
}
|
|
51
|
+
const key = `${options.keyPrefix ?? "tok"}-${index}`;
|
|
52
|
+
const dataAttrs = Object.fromEntries(
|
|
53
|
+
Object.entries(extraAttrs).map(([k, v]) => [`data-${k}`, v])
|
|
54
|
+
);
|
|
55
|
+
if (options.theme) {
|
|
56
|
+
const baseStyle = options.theme.styles[token.category] ?? "";
|
|
57
|
+
const style = cssTextToReactStyle([baseStyle, extraStyle].filter(Boolean).join(";"));
|
|
58
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style, ...dataAttrs, children: token.value }, key);
|
|
59
|
+
}
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
61
|
+
"span",
|
|
62
|
+
{
|
|
63
|
+
className: [prefix + token.category, ...extraClasses].join(" "),
|
|
64
|
+
...dataAttrs,
|
|
65
|
+
children: token.value
|
|
66
|
+
},
|
|
67
|
+
key
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function renderLineTable(tokens, options = {}) {
|
|
72
|
+
const groups = (0, import_tree_sitter_ts_highlight.groupTokensByLine)(tokens);
|
|
73
|
+
const startLine = options.startLine ?? 1;
|
|
74
|
+
const withDataLine = options.dataLineAttributes ?? true;
|
|
75
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className: "hlts-table", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: groups.map((group, index) => {
|
|
76
|
+
const displayLine = group.lineNumber - groups[0].lineNumber + startLine;
|
|
77
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
78
|
+
"tr",
|
|
79
|
+
{
|
|
80
|
+
...withDataLine ? { "data-line": displayLine } : {},
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "hlts-line-number", children: displayLine }),
|
|
83
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "hlts-line-content", children: renderTokenNodes(group.tokens, {
|
|
84
|
+
classPrefix: options.classPrefix,
|
|
85
|
+
theme: options.theme,
|
|
86
|
+
decorations: options.decorations,
|
|
87
|
+
keyPrefix: `line-${group.lineNumber}`
|
|
88
|
+
}) })
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
`line-${group.lineNumber}-${index}`
|
|
92
|
+
);
|
|
93
|
+
}) }) });
|
|
94
|
+
}
|
|
95
|
+
function renderDiffTable(options) {
|
|
96
|
+
const prefix = options.classPrefix ?? "hlts-";
|
|
97
|
+
const view = options.view ?? "side-by-side";
|
|
98
|
+
if (view === "inline") {
|
|
99
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className: `${prefix}diff ${prefix}diff-inline`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: options.rows.flatMap((row, index) => {
|
|
100
|
+
if (row.changeType === "context") {
|
|
101
|
+
return [
|
|
102
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: `${prefix}diff-row ${prefix}diff-context`, children: [
|
|
103
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber ?? "" }),
|
|
104
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-sign`, children: " " }),
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
106
|
+
lineNumber: row.newLineNumber,
|
|
107
|
+
tokenMap: options.newLineTokens,
|
|
108
|
+
fallbackText: row.newText,
|
|
109
|
+
classPrefix: options.classPrefix,
|
|
110
|
+
theme: options.theme,
|
|
111
|
+
decorations: options.decorations,
|
|
112
|
+
keyPrefix: `inline-context-${index}`
|
|
113
|
+
}) })
|
|
114
|
+
] }, `ctx-${index}`)
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
const nodes = [];
|
|
118
|
+
if (row.oldLineNumber !== null) {
|
|
119
|
+
nodes.push(
|
|
120
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: `${prefix}diff-row ${prefix}diff-removed`, children: [
|
|
121
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-gutter`, children: row.oldLineNumber }),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-sign`, children: "-" }),
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
124
|
+
lineNumber: row.oldLineNumber,
|
|
125
|
+
tokenMap: options.oldLineTokens,
|
|
126
|
+
fallbackText: row.oldText,
|
|
127
|
+
classPrefix: options.classPrefix,
|
|
128
|
+
theme: options.theme,
|
|
129
|
+
decorations: options.decorations,
|
|
130
|
+
keyPrefix: `inline-old-${index}`
|
|
131
|
+
}) })
|
|
132
|
+
] }, `old-${index}`)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (row.newLineNumber !== null) {
|
|
136
|
+
nodes.push(
|
|
137
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: `${prefix}diff-row ${prefix}diff-added`, children: [
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber }),
|
|
139
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-sign`, children: "+" }),
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
141
|
+
lineNumber: row.newLineNumber,
|
|
142
|
+
tokenMap: options.newLineTokens,
|
|
143
|
+
fallbackText: row.newText,
|
|
144
|
+
classPrefix: options.classPrefix,
|
|
145
|
+
theme: options.theme,
|
|
146
|
+
decorations: options.decorations,
|
|
147
|
+
keyPrefix: `inline-new-${index}`
|
|
148
|
+
}) })
|
|
149
|
+
] }, `new-${index}`)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return nodes;
|
|
153
|
+
}) }) });
|
|
154
|
+
}
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className: `${prefix}diff ${prefix}diff-side-by-side`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
|
|
156
|
+
(options.showHeader ?? true) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: `${prefix}diff-header`, children: [
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: `${prefix}diff-label`, colSpan: 2, children: options.oldLabel }),
|
|
158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: `${prefix}diff-label`, colSpan: 2, children: options.newLabel })
|
|
159
|
+
] }),
|
|
160
|
+
options.rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
161
|
+
"tr",
|
|
162
|
+
{
|
|
163
|
+
className: `${prefix}diff-row ${prefix}diff-${row.changeType}`,
|
|
164
|
+
children: [
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-gutter`, children: row.oldLineNumber ?? "" }),
|
|
166
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
167
|
+
lineNumber: row.oldLineNumber,
|
|
168
|
+
tokenMap: options.oldLineTokens,
|
|
169
|
+
fallbackText: row.oldText,
|
|
170
|
+
classPrefix: options.classPrefix,
|
|
171
|
+
theme: options.theme,
|
|
172
|
+
decorations: options.decorations,
|
|
173
|
+
keyPrefix: `side-old-${index}`
|
|
174
|
+
}) }),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber ?? "" }),
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
177
|
+
lineNumber: row.newLineNumber,
|
|
178
|
+
tokenMap: options.newLineTokens,
|
|
179
|
+
fallbackText: row.newText,
|
|
180
|
+
classPrefix: options.classPrefix,
|
|
181
|
+
theme: options.theme,
|
|
182
|
+
decorations: options.decorations,
|
|
183
|
+
keyPrefix: `side-new-${index}`
|
|
184
|
+
}) })
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
`row-${index}`
|
|
188
|
+
))
|
|
189
|
+
] }) });
|
|
190
|
+
}
|
|
191
|
+
function renderDiffLineContent(params) {
|
|
192
|
+
if (params.lineNumber === null) {
|
|
193
|
+
return "";
|
|
194
|
+
}
|
|
195
|
+
const tokens = params.tokenMap.get(params.lineNumber);
|
|
196
|
+
if (!tokens || tokens.length === 0) {
|
|
197
|
+
return params.fallbackText;
|
|
198
|
+
}
|
|
199
|
+
return renderTokenNodes(tokens, {
|
|
200
|
+
classPrefix: params.classPrefix,
|
|
201
|
+
theme: params.theme,
|
|
202
|
+
decorations: params.decorations,
|
|
203
|
+
keyPrefix: params.keyPrefix
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function cssTextToReactStyle(cssText) {
|
|
207
|
+
const style = {};
|
|
208
|
+
for (const declaration of cssText.split(";")) {
|
|
209
|
+
const trimmed = declaration.trim();
|
|
210
|
+
if (!trimmed) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
214
|
+
if (separatorIndex === -1) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const property = trimmed.slice(0, separatorIndex).trim();
|
|
218
|
+
const value = trimmed.slice(separatorIndex + 1).trim();
|
|
219
|
+
if (!property || !value) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
style[kebabToCamel(property)] = value;
|
|
223
|
+
}
|
|
224
|
+
return style;
|
|
225
|
+
}
|
|
226
|
+
function kebabToCamel(property) {
|
|
227
|
+
return property.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/components/Highlight.tsx
|
|
231
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
232
|
+
function Highlight({
|
|
233
|
+
code,
|
|
234
|
+
language,
|
|
235
|
+
options,
|
|
236
|
+
preProps,
|
|
237
|
+
codeProps
|
|
238
|
+
}) {
|
|
239
|
+
const tokens = (0, import_react.useMemo)(() => {
|
|
240
|
+
const rawTokens = (0, import_tree_sitter_ts.tokenize)(code, language);
|
|
241
|
+
return options?.semanticHighlighting ? (0, import_tree_sitter_ts_highlight2.enhanceSemantics)(rawTokens) : rawTokens;
|
|
242
|
+
}, [code, language, options?.semanticHighlighting]);
|
|
243
|
+
const { children: _ignoredChildren, ...safeCodeProps } = codeProps ?? {};
|
|
244
|
+
const mergedPreStyle = {
|
|
245
|
+
...options?.theme?.background ? { background: options.theme.background } : {},
|
|
246
|
+
...options?.theme?.foreground ? { color: options.theme.foreground } : {},
|
|
247
|
+
...preProps?.style
|
|
248
|
+
};
|
|
249
|
+
const classPrefix = options?.classPrefix;
|
|
250
|
+
const content = options?.lineNumbers ? renderLineTable(tokens, {
|
|
251
|
+
classPrefix,
|
|
252
|
+
theme: options.theme,
|
|
253
|
+
decorations: options.decorations,
|
|
254
|
+
startLine: options.startLine,
|
|
255
|
+
dataLineAttributes: options.dataLineAttributes
|
|
256
|
+
}) : renderTokenNodes(tokens, {
|
|
257
|
+
classPrefix,
|
|
258
|
+
theme: options?.theme,
|
|
259
|
+
decorations: options?.decorations
|
|
260
|
+
});
|
|
261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
262
|
+
"pre",
|
|
263
|
+
{
|
|
264
|
+
...preProps,
|
|
265
|
+
className: ["hlts", `hlts-lang-${language}`, preProps?.className].filter(Boolean).join(" "),
|
|
266
|
+
style: mergedPreStyle,
|
|
267
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { ...safeCodeProps, children: content })
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/components/HighlightDiff.tsx
|
|
273
|
+
var import_tree_sitter_ts_highlight3 = require("tree-sitter-ts-highlight");
|
|
274
|
+
var import_react2 = require("react");
|
|
275
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
276
|
+
function HighlightDiff({
|
|
277
|
+
oldCode,
|
|
278
|
+
newCode,
|
|
279
|
+
language,
|
|
280
|
+
options,
|
|
281
|
+
containerProps
|
|
282
|
+
}) {
|
|
283
|
+
const diff = (0, import_react2.useMemo)(
|
|
284
|
+
() => (0, import_tree_sitter_ts_highlight3.createDiffModelWithTokens)(oldCode, newCode, language, options),
|
|
285
|
+
[oldCode, newCode, language, options]
|
|
286
|
+
);
|
|
287
|
+
const { children: _ignoredChildren, ...safeContainerProps } = containerProps ?? {};
|
|
288
|
+
const table = renderDiffTable({
|
|
289
|
+
view: options?.view,
|
|
290
|
+
showHeader: options?.showHeader,
|
|
291
|
+
classPrefix: options?.classPrefix,
|
|
292
|
+
decorations: options?.decorations,
|
|
293
|
+
theme: options?.theme,
|
|
294
|
+
oldLabel: diff.model.oldLabel,
|
|
295
|
+
newLabel: diff.model.newLabel,
|
|
296
|
+
rows: diff.model.rows,
|
|
297
|
+
oldLineTokens: diff.oldLineTokens,
|
|
298
|
+
newLineTokens: diff.newLineTokens
|
|
299
|
+
});
|
|
300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...safeContainerProps, children: table });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/hooks/useHighlightedHtml.ts
|
|
304
|
+
var import_tree_sitter_ts_highlight4 = require("tree-sitter-ts-highlight");
|
|
305
|
+
var import_react3 = require("react");
|
|
306
|
+
function useHighlightedHtml({
|
|
307
|
+
code,
|
|
308
|
+
language,
|
|
309
|
+
options
|
|
310
|
+
}) {
|
|
311
|
+
return (0, import_react3.useMemo)(
|
|
312
|
+
() => (0, import_tree_sitter_ts_highlight4.highlight)(code, language, {
|
|
313
|
+
...options,
|
|
314
|
+
wrapInPre: false,
|
|
315
|
+
language
|
|
316
|
+
}),
|
|
317
|
+
[code, language, options]
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
function useHighlightedDiffHtml({
|
|
321
|
+
oldCode,
|
|
322
|
+
newCode,
|
|
323
|
+
language,
|
|
324
|
+
options
|
|
325
|
+
}) {
|
|
326
|
+
return (0, import_react3.useMemo)(
|
|
327
|
+
() => (0, import_tree_sitter_ts_highlight4.highlightDiff)(oldCode, newCode, language, options),
|
|
328
|
+
[oldCode, newCode, language, options]
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/index.ts
|
|
333
|
+
__reExport(index_exports, require("tree-sitter-ts-highlight"), module.exports);
|
|
334
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
335
|
+
0 && (module.exports = {
|
|
336
|
+
Highlight,
|
|
337
|
+
HighlightDiff,
|
|
338
|
+
useHighlightedDiffHtml,
|
|
339
|
+
useHighlightedHtml,
|
|
340
|
+
...require("tree-sitter-ts-highlight")
|
|
341
|
+
});
|
|
342
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/Highlight.tsx","../src/react-renderer.tsx","../src/components/HighlightDiff.tsx","../src/hooks/useHighlightedHtml.ts"],"sourcesContent":["export { Highlight } from \"./components/Highlight\";\r\nexport type { HighlightProps } from \"./components/Highlight\";\r\n\r\nexport { HighlightDiff } from \"./components/HighlightDiff\";\r\nexport type { HighlightDiffProps } from \"./components/HighlightDiff\";\r\n\r\nexport {\r\n useHighlightedHtml,\r\n useHighlightedDiffHtml,\r\n} from \"./hooks/useHighlightedHtml\";\r\nexport type {\r\n UseHighlightedHtmlParams,\r\n UseHighlightedDiffHtmlParams,\r\n} from \"./hooks/useHighlightedHtml\";\r\n\r\nexport type * from \"tree-sitter-ts\";\r\nexport type * from \"tree-sitter-ts-highlight\";\r\nexport * from \"tree-sitter-ts-highlight\";\r\n","import { enhanceSemantics } from \"tree-sitter-ts-highlight\";\r\nimport type { HighlightOptions } from \"tree-sitter-ts-highlight\";\r\nimport { tokenize } from \"tree-sitter-ts\";\r\nimport { useMemo } from \"react\";\r\nimport type { HTMLAttributes, ReactElement } from \"react\";\r\nimport { renderLineTable, renderTokenNodes } from \"../react-renderer\";\r\n\r\nexport interface HighlightProps {\r\n code: string;\r\n language: string;\r\n options?: Omit<HighlightOptions, \"wrapInPre\" | \"language\">;\r\n preProps?: HTMLAttributes<HTMLPreElement>;\r\n codeProps?: HTMLAttributes<HTMLElement>;\r\n}\r\n\r\nexport function Highlight({\r\n code,\r\n language,\r\n options,\r\n preProps,\r\n codeProps,\r\n}: HighlightProps): ReactElement {\r\n const tokens = useMemo(() => {\r\n const rawTokens = tokenize(code, language);\r\n return options?.semanticHighlighting ? enhanceSemantics(rawTokens) : rawTokens;\r\n }, [code, language, options?.semanticHighlighting]);\r\n\r\n const { children: _ignoredChildren, ...safeCodeProps } = codeProps ?? {};\r\n\r\n const mergedPreStyle = {\r\n ...(options?.theme?.background ? { background: options.theme.background } : {}),\r\n ...(options?.theme?.foreground ? { color: options.theme.foreground } : {}),\r\n ...preProps?.style,\r\n };\r\n\r\n const classPrefix = options?.classPrefix;\r\n const content = options?.lineNumbers\r\n ? renderLineTable(tokens, {\r\n classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n startLine: options.startLine,\r\n dataLineAttributes: options.dataLineAttributes,\r\n })\r\n : renderTokenNodes(tokens, {\r\n classPrefix,\r\n theme: options?.theme,\r\n decorations: options?.decorations,\r\n });\r\n\r\n return (\r\n <pre\r\n {...preProps}\r\n className={[\"hlts\", `hlts-lang-${language}`, preProps?.className].filter(Boolean).join(\" \")}\r\n style={mergedPreStyle}\r\n >\r\n <code {...safeCodeProps}>{content}</code>\r\n </pre>\r\n );\r\n}\r\n","import { applyDecorations, groupTokensByLine } from \"tree-sitter-ts-highlight\";\r\nimport type {\r\n Decoration,\r\n DiffOptions,\r\n HtmlTheme,\r\n} from \"tree-sitter-ts-highlight\";\r\nimport type { Token } from \"tree-sitter-ts\";\r\nimport type { CSSProperties, ReactNode } from \"react\";\r\n\r\ninterface RenderTokenNodesOptions {\r\n classPrefix?: string;\r\n theme?: HtmlTheme;\r\n decorations?: Decoration[];\r\n keyPrefix?: string;\r\n}\r\n\r\ninterface RenderLineTableOptions extends RenderTokenNodesOptions {\r\n startLine?: number;\r\n dataLineAttributes?: boolean;\r\n}\r\n\r\ninterface RenderDiffTableOptions extends RenderTokenNodesOptions {\r\n view?: DiffOptions[\"view\"];\r\n showHeader?: boolean;\r\n oldLabel: string;\r\n newLabel: string;\r\n rows: Array<{\r\n changeType: \"context\" | \"added\" | \"removed\" | \"modified\";\r\n oldLineNumber: number | null;\r\n newLineNumber: number | null;\r\n oldText: string;\r\n newText: string;\r\n }>;\r\n oldLineTokens: Map<number, Token[]>;\r\n newLineTokens: Map<number, Token[]>;\r\n}\r\n\r\nexport function renderTokenNodes(\r\n tokens: Token[],\r\n options: RenderTokenNodesOptions = {},\r\n): ReactNode[] {\r\n const prefix = options.classPrefix ?? \"hlts-\";\r\n const decorated =\r\n options.decorations && options.decorations.length > 0\r\n ? applyDecorations(tokens, options.decorations)\r\n : tokens.map((token) => ({\r\n token,\r\n extraClasses: [],\r\n extraAttrs: {},\r\n extraStyle: undefined,\r\n }));\r\n\r\n return decorated.map(({ token, extraClasses, extraAttrs, extraStyle }, index) => {\r\n if (token.category === \"whitespace\" || token.category === \"newline\") {\r\n return token.value;\r\n }\r\n\r\n const key = `${options.keyPrefix ?? \"tok\"}-${index}`;\r\n const dataAttrs = Object.fromEntries(\r\n Object.entries(extraAttrs).map(([k, v]) => [`data-${k}`, v]),\r\n );\r\n\r\n if (options.theme) {\r\n const baseStyle = options.theme.styles[token.category] ?? \"\";\r\n const style = cssTextToReactStyle([baseStyle, extraStyle].filter(Boolean).join(\";\"));\r\n\r\n return (\r\n <span key={key} style={style} {...dataAttrs}>\r\n {token.value}\r\n </span>\r\n );\r\n }\r\n\r\n return (\r\n <span\r\n key={key}\r\n className={[prefix + token.category, ...extraClasses].join(\" \")}\r\n {...dataAttrs}\r\n >\r\n {token.value}\r\n </span>\r\n );\r\n });\r\n}\r\n\r\nexport function renderLineTable(\r\n tokens: Token[],\r\n options: RenderLineTableOptions = {},\r\n): ReactNode {\r\n const groups = groupTokensByLine(tokens);\r\n const startLine = options.startLine ?? 1;\r\n const withDataLine = options.dataLineAttributes ?? true;\r\n\r\n return (\r\n <table className=\"hlts-table\">\r\n <tbody>\r\n {groups.map((group, index) => {\r\n const displayLine = group.lineNumber - groups[0].lineNumber + startLine;\r\n return (\r\n <tr\r\n key={`line-${group.lineNumber}-${index}`}\r\n {...(withDataLine ? { \"data-line\": displayLine } : {})}\r\n >\r\n <td className=\"hlts-line-number\">{displayLine}</td>\r\n <td className=\"hlts-line-content\">\r\n {renderTokenNodes(group.tokens, {\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `line-${group.lineNumber}`,\r\n })}\r\n </td>\r\n </tr>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n );\r\n}\r\n\r\nexport function renderDiffTable(options: RenderDiffTableOptions): ReactNode {\r\n const prefix = options.classPrefix ?? \"hlts-\";\r\n const view = options.view ?? \"side-by-side\";\r\n\r\n if (view === \"inline\") {\r\n return (\r\n <table className={`${prefix}diff ${prefix}diff-inline`}>\r\n <tbody>\r\n {options.rows.flatMap((row, index) => {\r\n if (row.changeType === \"context\") {\r\n return [\r\n <tr key={`ctx-${index}`} className={`${prefix}diff-row ${prefix}diff-context`}>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-sign`}> </td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-context-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n ];\r\n }\r\n\r\n const nodes: ReactNode[] = [];\r\n\r\n if (row.oldLineNumber !== null) {\r\n nodes.push(\r\n <tr key={`old-${index}`} className={`${prefix}diff-row ${prefix}diff-removed`}>\r\n <td className={`${prefix}diff-gutter`}>{row.oldLineNumber}</td>\r\n <td className={`${prefix}diff-sign`}>-</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.oldLineNumber,\r\n tokenMap: options.oldLineTokens,\r\n fallbackText: row.oldText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-old-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n );\r\n }\r\n\r\n if (row.newLineNumber !== null) {\r\n nodes.push(\r\n <tr key={`new-${index}`} className={`${prefix}diff-row ${prefix}diff-added`}>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber}</td>\r\n <td className={`${prefix}diff-sign`}>+</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-new-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n );\r\n }\r\n\r\n return nodes;\r\n })}\r\n </tbody>\r\n </table>\r\n );\r\n }\r\n\r\n return (\r\n <table className={`${prefix}diff ${prefix}diff-side-by-side`}>\r\n <tbody>\r\n {(options.showHeader ?? true) && (\r\n <tr className={`${prefix}diff-header`}>\r\n <th className={`${prefix}diff-label`} colSpan={2}>\r\n {options.oldLabel}\r\n </th>\r\n <th className={`${prefix}diff-label`} colSpan={2}>\r\n {options.newLabel}\r\n </th>\r\n </tr>\r\n )}\r\n\r\n {options.rows.map((row, index) => (\r\n <tr\r\n key={`row-${index}`}\r\n className={`${prefix}diff-row ${prefix}diff-${row.changeType}`}\r\n >\r\n <td className={`${prefix}diff-gutter`}>{row.oldLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.oldLineNumber,\r\n tokenMap: options.oldLineTokens,\r\n fallbackText: row.oldText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `side-old-${index}`,\r\n })}\r\n </td>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `side-new-${index}`,\r\n })}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n );\r\n}\r\n\r\nfunction renderDiffLineContent(params: {\r\n lineNumber: number | null;\r\n tokenMap: Map<number, Token[]>;\r\n fallbackText: string;\r\n classPrefix?: string;\r\n theme?: HtmlTheme;\r\n decorations?: Decoration[];\r\n keyPrefix: string;\r\n}): ReactNode {\r\n if (params.lineNumber === null) {\r\n return \"\";\r\n }\r\n\r\n const tokens = params.tokenMap.get(params.lineNumber);\r\n if (!tokens || tokens.length === 0) {\r\n return params.fallbackText;\r\n }\r\n\r\n return renderTokenNodes(tokens, {\r\n classPrefix: params.classPrefix,\r\n theme: params.theme,\r\n decorations: params.decorations,\r\n keyPrefix: params.keyPrefix,\r\n });\r\n}\r\n\r\nfunction cssTextToReactStyle(cssText: string): CSSProperties {\r\n const style: Record<string, string> = {};\r\n\r\n for (const declaration of cssText.split(\";\")) {\r\n const trimmed = declaration.trim();\r\n if (!trimmed) {\r\n continue;\r\n }\r\n\r\n const separatorIndex = trimmed.indexOf(\":\");\r\n if (separatorIndex === -1) {\r\n continue;\r\n }\r\n\r\n const property = trimmed.slice(0, separatorIndex).trim();\r\n const value = trimmed.slice(separatorIndex + 1).trim();\r\n if (!property || !value) {\r\n continue;\r\n }\r\n\r\n style[kebabToCamel(property)] = value;\r\n }\r\n\r\n return style as CSSProperties;\r\n}\r\n\r\nfunction kebabToCamel(property: string): string {\r\n return property.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());\r\n}\r\n","import { createDiffModelWithTokens } from \"tree-sitter-ts-highlight\";\r\nimport type { DiffOptions } from \"tree-sitter-ts-highlight\";\r\nimport { useMemo } from \"react\";\r\nimport type { HTMLAttributes, ReactElement } from \"react\";\r\nimport { renderDiffTable } from \"../react-renderer\";\r\n\r\ntype DataAttributes = {\r\n [key: `data-${string}`]: string | number | boolean | undefined;\r\n};\r\n\r\nexport interface HighlightDiffProps {\r\n oldCode: string;\r\n newCode: string;\r\n language: string;\r\n options?: DiffOptions;\r\n containerProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\r\n}\r\n\r\nexport function HighlightDiff({\r\n oldCode,\r\n newCode,\r\n language,\r\n options,\r\n containerProps,\r\n}: HighlightDiffProps): ReactElement {\r\n const diff = useMemo(\r\n () => createDiffModelWithTokens(oldCode, newCode, language, options),\r\n [oldCode, newCode, language, options],\r\n );\r\n\r\n const { children: _ignoredChildren, ...safeContainerProps } = containerProps ?? {};\r\n\r\n const table = renderDiffTable({\r\n view: options?.view,\r\n showHeader: options?.showHeader,\r\n classPrefix: options?.classPrefix,\r\n decorations: options?.decorations,\r\n theme: options?.theme,\r\n oldLabel: diff.model.oldLabel,\r\n newLabel: diff.model.newLabel,\r\n rows: diff.model.rows,\r\n oldLineTokens: diff.oldLineTokens,\r\n newLineTokens: diff.newLineTokens,\r\n });\r\n\r\n return <div {...safeContainerProps}>{table}</div>;\r\n}\r\n","import { highlight, highlightDiff } from \"tree-sitter-ts-highlight\";\r\nimport type { DiffOptions, HighlightOptions } from \"tree-sitter-ts-highlight\";\r\nimport { useMemo } from \"react\";\r\n\r\nexport interface UseHighlightedHtmlParams {\r\n code: string;\r\n language: string;\r\n options?: Omit<HighlightOptions, \"wrapInPre\" | \"language\">;\r\n}\r\n\r\nexport function useHighlightedHtml({\r\n code,\r\n language,\r\n options,\r\n}: UseHighlightedHtmlParams): string {\r\n return useMemo(\r\n () =>\r\n highlight(code, language, {\r\n ...options,\r\n wrapInPre: false,\r\n language,\r\n }),\r\n [code, language, options],\r\n );\r\n}\r\n\r\nexport interface UseHighlightedDiffHtmlParams {\r\n oldCode: string;\r\n newCode: string;\r\n language: string;\r\n options?: DiffOptions;\r\n}\r\n\r\nexport function useHighlightedDiffHtml({\r\n oldCode,\r\n newCode,\r\n language,\r\n options,\r\n}: UseHighlightedDiffHtmlParams): string {\r\n return useMemo(\r\n () => highlightDiff(oldCode, newCode, language, options),\r\n [oldCode, newCode, language, options],\r\n );\r\n}\r\n"],"mappings":"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","names":["import_tree_sitter_ts_highlight","import_jsx_runtime","import_tree_sitter_ts_highlight","import_react","import_jsx_runtime","import_tree_sitter_ts_highlight","import_react"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { HighlightOptions, DiffOptions } from 'tree-sitter-ts-highlight';
|
|
2
|
+
export * from 'tree-sitter-ts-highlight';
|
|
3
|
+
import { HTMLAttributes, ReactElement } from 'react';
|
|
4
|
+
export * from 'tree-sitter-ts';
|
|
5
|
+
|
|
6
|
+
interface HighlightProps {
|
|
7
|
+
code: string;
|
|
8
|
+
language: string;
|
|
9
|
+
options?: Omit<HighlightOptions, "wrapInPre" | "language">;
|
|
10
|
+
preProps?: HTMLAttributes<HTMLPreElement>;
|
|
11
|
+
codeProps?: HTMLAttributes<HTMLElement>;
|
|
12
|
+
}
|
|
13
|
+
declare function Highlight({ code, language, options, preProps, codeProps, }: HighlightProps): ReactElement;
|
|
14
|
+
|
|
15
|
+
type DataAttributes = {
|
|
16
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
17
|
+
};
|
|
18
|
+
interface HighlightDiffProps {
|
|
19
|
+
oldCode: string;
|
|
20
|
+
newCode: string;
|
|
21
|
+
language: string;
|
|
22
|
+
options?: DiffOptions;
|
|
23
|
+
containerProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
24
|
+
}
|
|
25
|
+
declare function HighlightDiff({ oldCode, newCode, language, options, containerProps, }: HighlightDiffProps): ReactElement;
|
|
26
|
+
|
|
27
|
+
interface UseHighlightedHtmlParams {
|
|
28
|
+
code: string;
|
|
29
|
+
language: string;
|
|
30
|
+
options?: Omit<HighlightOptions, "wrapInPre" | "language">;
|
|
31
|
+
}
|
|
32
|
+
declare function useHighlightedHtml({ code, language, options, }: UseHighlightedHtmlParams): string;
|
|
33
|
+
interface UseHighlightedDiffHtmlParams {
|
|
34
|
+
oldCode: string;
|
|
35
|
+
newCode: string;
|
|
36
|
+
language: string;
|
|
37
|
+
options?: DiffOptions;
|
|
38
|
+
}
|
|
39
|
+
declare function useHighlightedDiffHtml({ oldCode, newCode, language, options, }: UseHighlightedDiffHtmlParams): string;
|
|
40
|
+
|
|
41
|
+
export { Highlight, HighlightDiff, type HighlightDiffProps, type HighlightProps, type UseHighlightedDiffHtmlParams, type UseHighlightedHtmlParams, useHighlightedDiffHtml, useHighlightedHtml };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { HighlightOptions, DiffOptions } from 'tree-sitter-ts-highlight';
|
|
2
|
+
export * from 'tree-sitter-ts-highlight';
|
|
3
|
+
import { HTMLAttributes, ReactElement } from 'react';
|
|
4
|
+
export * from 'tree-sitter-ts';
|
|
5
|
+
|
|
6
|
+
interface HighlightProps {
|
|
7
|
+
code: string;
|
|
8
|
+
language: string;
|
|
9
|
+
options?: Omit<HighlightOptions, "wrapInPre" | "language">;
|
|
10
|
+
preProps?: HTMLAttributes<HTMLPreElement>;
|
|
11
|
+
codeProps?: HTMLAttributes<HTMLElement>;
|
|
12
|
+
}
|
|
13
|
+
declare function Highlight({ code, language, options, preProps, codeProps, }: HighlightProps): ReactElement;
|
|
14
|
+
|
|
15
|
+
type DataAttributes = {
|
|
16
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
17
|
+
};
|
|
18
|
+
interface HighlightDiffProps {
|
|
19
|
+
oldCode: string;
|
|
20
|
+
newCode: string;
|
|
21
|
+
language: string;
|
|
22
|
+
options?: DiffOptions;
|
|
23
|
+
containerProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
24
|
+
}
|
|
25
|
+
declare function HighlightDiff({ oldCode, newCode, language, options, containerProps, }: HighlightDiffProps): ReactElement;
|
|
26
|
+
|
|
27
|
+
interface UseHighlightedHtmlParams {
|
|
28
|
+
code: string;
|
|
29
|
+
language: string;
|
|
30
|
+
options?: Omit<HighlightOptions, "wrapInPre" | "language">;
|
|
31
|
+
}
|
|
32
|
+
declare function useHighlightedHtml({ code, language, options, }: UseHighlightedHtmlParams): string;
|
|
33
|
+
interface UseHighlightedDiffHtmlParams {
|
|
34
|
+
oldCode: string;
|
|
35
|
+
newCode: string;
|
|
36
|
+
language: string;
|
|
37
|
+
options?: DiffOptions;
|
|
38
|
+
}
|
|
39
|
+
declare function useHighlightedDiffHtml({ oldCode, newCode, language, options, }: UseHighlightedDiffHtmlParams): string;
|
|
40
|
+
|
|
41
|
+
export { Highlight, HighlightDiff, type HighlightDiffProps, type HighlightProps, type UseHighlightedDiffHtmlParams, type UseHighlightedHtmlParams, useHighlightedDiffHtml, useHighlightedHtml };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// src/components/Highlight.tsx
|
|
2
|
+
import { enhanceSemantics } from "tree-sitter-ts-highlight";
|
|
3
|
+
import { tokenize } from "tree-sitter-ts";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
// src/react-renderer.tsx
|
|
7
|
+
import { applyDecorations, groupTokensByLine } from "tree-sitter-ts-highlight";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function renderTokenNodes(tokens, options = {}) {
|
|
10
|
+
const prefix = options.classPrefix ?? "hlts-";
|
|
11
|
+
const decorated = options.decorations && options.decorations.length > 0 ? applyDecorations(tokens, options.decorations) : tokens.map((token) => ({
|
|
12
|
+
token,
|
|
13
|
+
extraClasses: [],
|
|
14
|
+
extraAttrs: {},
|
|
15
|
+
extraStyle: void 0
|
|
16
|
+
}));
|
|
17
|
+
return decorated.map(({ token, extraClasses, extraAttrs, extraStyle }, index) => {
|
|
18
|
+
if (token.category === "whitespace" || token.category === "newline") {
|
|
19
|
+
return token.value;
|
|
20
|
+
}
|
|
21
|
+
const key = `${options.keyPrefix ?? "tok"}-${index}`;
|
|
22
|
+
const dataAttrs = Object.fromEntries(
|
|
23
|
+
Object.entries(extraAttrs).map(([k, v]) => [`data-${k}`, v])
|
|
24
|
+
);
|
|
25
|
+
if (options.theme) {
|
|
26
|
+
const baseStyle = options.theme.styles[token.category] ?? "";
|
|
27
|
+
const style = cssTextToReactStyle([baseStyle, extraStyle].filter(Boolean).join(";"));
|
|
28
|
+
return /* @__PURE__ */ jsx("span", { style, ...dataAttrs, children: token.value }, key);
|
|
29
|
+
}
|
|
30
|
+
return /* @__PURE__ */ jsx(
|
|
31
|
+
"span",
|
|
32
|
+
{
|
|
33
|
+
className: [prefix + token.category, ...extraClasses].join(" "),
|
|
34
|
+
...dataAttrs,
|
|
35
|
+
children: token.value
|
|
36
|
+
},
|
|
37
|
+
key
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function renderLineTable(tokens, options = {}) {
|
|
42
|
+
const groups = groupTokensByLine(tokens);
|
|
43
|
+
const startLine = options.startLine ?? 1;
|
|
44
|
+
const withDataLine = options.dataLineAttributes ?? true;
|
|
45
|
+
return /* @__PURE__ */ jsx("table", { className: "hlts-table", children: /* @__PURE__ */ jsx("tbody", { children: groups.map((group, index) => {
|
|
46
|
+
const displayLine = group.lineNumber - groups[0].lineNumber + startLine;
|
|
47
|
+
return /* @__PURE__ */ jsxs(
|
|
48
|
+
"tr",
|
|
49
|
+
{
|
|
50
|
+
...withDataLine ? { "data-line": displayLine } : {},
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsx("td", { className: "hlts-line-number", children: displayLine }),
|
|
53
|
+
/* @__PURE__ */ jsx("td", { className: "hlts-line-content", children: renderTokenNodes(group.tokens, {
|
|
54
|
+
classPrefix: options.classPrefix,
|
|
55
|
+
theme: options.theme,
|
|
56
|
+
decorations: options.decorations,
|
|
57
|
+
keyPrefix: `line-${group.lineNumber}`
|
|
58
|
+
}) })
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
`line-${group.lineNumber}-${index}`
|
|
62
|
+
);
|
|
63
|
+
}) }) });
|
|
64
|
+
}
|
|
65
|
+
function renderDiffTable(options) {
|
|
66
|
+
const prefix = options.classPrefix ?? "hlts-";
|
|
67
|
+
const view = options.view ?? "side-by-side";
|
|
68
|
+
if (view === "inline") {
|
|
69
|
+
return /* @__PURE__ */ jsx("table", { className: `${prefix}diff ${prefix}diff-inline`, children: /* @__PURE__ */ jsx("tbody", { children: options.rows.flatMap((row, index) => {
|
|
70
|
+
if (row.changeType === "context") {
|
|
71
|
+
return [
|
|
72
|
+
/* @__PURE__ */ jsxs("tr", { className: `${prefix}diff-row ${prefix}diff-context`, children: [
|
|
73
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber ?? "" }),
|
|
74
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-sign`, children: " " }),
|
|
75
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
76
|
+
lineNumber: row.newLineNumber,
|
|
77
|
+
tokenMap: options.newLineTokens,
|
|
78
|
+
fallbackText: row.newText,
|
|
79
|
+
classPrefix: options.classPrefix,
|
|
80
|
+
theme: options.theme,
|
|
81
|
+
decorations: options.decorations,
|
|
82
|
+
keyPrefix: `inline-context-${index}`
|
|
83
|
+
}) })
|
|
84
|
+
] }, `ctx-${index}`)
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
const nodes = [];
|
|
88
|
+
if (row.oldLineNumber !== null) {
|
|
89
|
+
nodes.push(
|
|
90
|
+
/* @__PURE__ */ jsxs("tr", { className: `${prefix}diff-row ${prefix}diff-removed`, children: [
|
|
91
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-gutter`, children: row.oldLineNumber }),
|
|
92
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-sign`, children: "-" }),
|
|
93
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
94
|
+
lineNumber: row.oldLineNumber,
|
|
95
|
+
tokenMap: options.oldLineTokens,
|
|
96
|
+
fallbackText: row.oldText,
|
|
97
|
+
classPrefix: options.classPrefix,
|
|
98
|
+
theme: options.theme,
|
|
99
|
+
decorations: options.decorations,
|
|
100
|
+
keyPrefix: `inline-old-${index}`
|
|
101
|
+
}) })
|
|
102
|
+
] }, `old-${index}`)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (row.newLineNumber !== null) {
|
|
106
|
+
nodes.push(
|
|
107
|
+
/* @__PURE__ */ jsxs("tr", { className: `${prefix}diff-row ${prefix}diff-added`, children: [
|
|
108
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber }),
|
|
109
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-sign`, children: "+" }),
|
|
110
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
111
|
+
lineNumber: row.newLineNumber,
|
|
112
|
+
tokenMap: options.newLineTokens,
|
|
113
|
+
fallbackText: row.newText,
|
|
114
|
+
classPrefix: options.classPrefix,
|
|
115
|
+
theme: options.theme,
|
|
116
|
+
decorations: options.decorations,
|
|
117
|
+
keyPrefix: `inline-new-${index}`
|
|
118
|
+
}) })
|
|
119
|
+
] }, `new-${index}`)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return nodes;
|
|
123
|
+
}) }) });
|
|
124
|
+
}
|
|
125
|
+
return /* @__PURE__ */ jsx("table", { className: `${prefix}diff ${prefix}diff-side-by-side`, children: /* @__PURE__ */ jsxs("tbody", { children: [
|
|
126
|
+
(options.showHeader ?? true) && /* @__PURE__ */ jsxs("tr", { className: `${prefix}diff-header`, children: [
|
|
127
|
+
/* @__PURE__ */ jsx("th", { className: `${prefix}diff-label`, colSpan: 2, children: options.oldLabel }),
|
|
128
|
+
/* @__PURE__ */ jsx("th", { className: `${prefix}diff-label`, colSpan: 2, children: options.newLabel })
|
|
129
|
+
] }),
|
|
130
|
+
options.rows.map((row, index) => /* @__PURE__ */ jsxs(
|
|
131
|
+
"tr",
|
|
132
|
+
{
|
|
133
|
+
className: `${prefix}diff-row ${prefix}diff-${row.changeType}`,
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-gutter`, children: row.oldLineNumber ?? "" }),
|
|
136
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
137
|
+
lineNumber: row.oldLineNumber,
|
|
138
|
+
tokenMap: options.oldLineTokens,
|
|
139
|
+
fallbackText: row.oldText,
|
|
140
|
+
classPrefix: options.classPrefix,
|
|
141
|
+
theme: options.theme,
|
|
142
|
+
decorations: options.decorations,
|
|
143
|
+
keyPrefix: `side-old-${index}`
|
|
144
|
+
}) }),
|
|
145
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-gutter`, children: row.newLineNumber ?? "" }),
|
|
146
|
+
/* @__PURE__ */ jsx("td", { className: `${prefix}diff-content`, children: renderDiffLineContent({
|
|
147
|
+
lineNumber: row.newLineNumber,
|
|
148
|
+
tokenMap: options.newLineTokens,
|
|
149
|
+
fallbackText: row.newText,
|
|
150
|
+
classPrefix: options.classPrefix,
|
|
151
|
+
theme: options.theme,
|
|
152
|
+
decorations: options.decorations,
|
|
153
|
+
keyPrefix: `side-new-${index}`
|
|
154
|
+
}) })
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
`row-${index}`
|
|
158
|
+
))
|
|
159
|
+
] }) });
|
|
160
|
+
}
|
|
161
|
+
function renderDiffLineContent(params) {
|
|
162
|
+
if (params.lineNumber === null) {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
const tokens = params.tokenMap.get(params.lineNumber);
|
|
166
|
+
if (!tokens || tokens.length === 0) {
|
|
167
|
+
return params.fallbackText;
|
|
168
|
+
}
|
|
169
|
+
return renderTokenNodes(tokens, {
|
|
170
|
+
classPrefix: params.classPrefix,
|
|
171
|
+
theme: params.theme,
|
|
172
|
+
decorations: params.decorations,
|
|
173
|
+
keyPrefix: params.keyPrefix
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function cssTextToReactStyle(cssText) {
|
|
177
|
+
const style = {};
|
|
178
|
+
for (const declaration of cssText.split(";")) {
|
|
179
|
+
const trimmed = declaration.trim();
|
|
180
|
+
if (!trimmed) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
184
|
+
if (separatorIndex === -1) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const property = trimmed.slice(0, separatorIndex).trim();
|
|
188
|
+
const value = trimmed.slice(separatorIndex + 1).trim();
|
|
189
|
+
if (!property || !value) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
style[kebabToCamel(property)] = value;
|
|
193
|
+
}
|
|
194
|
+
return style;
|
|
195
|
+
}
|
|
196
|
+
function kebabToCamel(property) {
|
|
197
|
+
return property.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/components/Highlight.tsx
|
|
201
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
202
|
+
function Highlight({
|
|
203
|
+
code,
|
|
204
|
+
language,
|
|
205
|
+
options,
|
|
206
|
+
preProps,
|
|
207
|
+
codeProps
|
|
208
|
+
}) {
|
|
209
|
+
const tokens = useMemo(() => {
|
|
210
|
+
const rawTokens = tokenize(code, language);
|
|
211
|
+
return options?.semanticHighlighting ? enhanceSemantics(rawTokens) : rawTokens;
|
|
212
|
+
}, [code, language, options?.semanticHighlighting]);
|
|
213
|
+
const { children: _ignoredChildren, ...safeCodeProps } = codeProps ?? {};
|
|
214
|
+
const mergedPreStyle = {
|
|
215
|
+
...options?.theme?.background ? { background: options.theme.background } : {},
|
|
216
|
+
...options?.theme?.foreground ? { color: options.theme.foreground } : {},
|
|
217
|
+
...preProps?.style
|
|
218
|
+
};
|
|
219
|
+
const classPrefix = options?.classPrefix;
|
|
220
|
+
const content = options?.lineNumbers ? renderLineTable(tokens, {
|
|
221
|
+
classPrefix,
|
|
222
|
+
theme: options.theme,
|
|
223
|
+
decorations: options.decorations,
|
|
224
|
+
startLine: options.startLine,
|
|
225
|
+
dataLineAttributes: options.dataLineAttributes
|
|
226
|
+
}) : renderTokenNodes(tokens, {
|
|
227
|
+
classPrefix,
|
|
228
|
+
theme: options?.theme,
|
|
229
|
+
decorations: options?.decorations
|
|
230
|
+
});
|
|
231
|
+
return /* @__PURE__ */ jsx2(
|
|
232
|
+
"pre",
|
|
233
|
+
{
|
|
234
|
+
...preProps,
|
|
235
|
+
className: ["hlts", `hlts-lang-${language}`, preProps?.className].filter(Boolean).join(" "),
|
|
236
|
+
style: mergedPreStyle,
|
|
237
|
+
children: /* @__PURE__ */ jsx2("code", { ...safeCodeProps, children: content })
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/components/HighlightDiff.tsx
|
|
243
|
+
import { createDiffModelWithTokens } from "tree-sitter-ts-highlight";
|
|
244
|
+
import { useMemo as useMemo2 } from "react";
|
|
245
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
246
|
+
function HighlightDiff({
|
|
247
|
+
oldCode,
|
|
248
|
+
newCode,
|
|
249
|
+
language,
|
|
250
|
+
options,
|
|
251
|
+
containerProps
|
|
252
|
+
}) {
|
|
253
|
+
const diff = useMemo2(
|
|
254
|
+
() => createDiffModelWithTokens(oldCode, newCode, language, options),
|
|
255
|
+
[oldCode, newCode, language, options]
|
|
256
|
+
);
|
|
257
|
+
const { children: _ignoredChildren, ...safeContainerProps } = containerProps ?? {};
|
|
258
|
+
const table = renderDiffTable({
|
|
259
|
+
view: options?.view,
|
|
260
|
+
showHeader: options?.showHeader,
|
|
261
|
+
classPrefix: options?.classPrefix,
|
|
262
|
+
decorations: options?.decorations,
|
|
263
|
+
theme: options?.theme,
|
|
264
|
+
oldLabel: diff.model.oldLabel,
|
|
265
|
+
newLabel: diff.model.newLabel,
|
|
266
|
+
rows: diff.model.rows,
|
|
267
|
+
oldLineTokens: diff.oldLineTokens,
|
|
268
|
+
newLineTokens: diff.newLineTokens
|
|
269
|
+
});
|
|
270
|
+
return /* @__PURE__ */ jsx3("div", { ...safeContainerProps, children: table });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/hooks/useHighlightedHtml.ts
|
|
274
|
+
import { highlight, highlightDiff } from "tree-sitter-ts-highlight";
|
|
275
|
+
import { useMemo as useMemo3 } from "react";
|
|
276
|
+
function useHighlightedHtml({
|
|
277
|
+
code,
|
|
278
|
+
language,
|
|
279
|
+
options
|
|
280
|
+
}) {
|
|
281
|
+
return useMemo3(
|
|
282
|
+
() => highlight(code, language, {
|
|
283
|
+
...options,
|
|
284
|
+
wrapInPre: false,
|
|
285
|
+
language
|
|
286
|
+
}),
|
|
287
|
+
[code, language, options]
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
function useHighlightedDiffHtml({
|
|
291
|
+
oldCode,
|
|
292
|
+
newCode,
|
|
293
|
+
language,
|
|
294
|
+
options
|
|
295
|
+
}) {
|
|
296
|
+
return useMemo3(
|
|
297
|
+
() => highlightDiff(oldCode, newCode, language, options),
|
|
298
|
+
[oldCode, newCode, language, options]
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/index.ts
|
|
303
|
+
export * from "tree-sitter-ts-highlight";
|
|
304
|
+
export {
|
|
305
|
+
Highlight,
|
|
306
|
+
HighlightDiff,
|
|
307
|
+
useHighlightedDiffHtml,
|
|
308
|
+
useHighlightedHtml
|
|
309
|
+
};
|
|
310
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Highlight.tsx","../src/react-renderer.tsx","../src/components/HighlightDiff.tsx","../src/hooks/useHighlightedHtml.ts","../src/index.ts"],"sourcesContent":["import { enhanceSemantics } from \"tree-sitter-ts-highlight\";\r\nimport type { HighlightOptions } from \"tree-sitter-ts-highlight\";\r\nimport { tokenize } from \"tree-sitter-ts\";\r\nimport { useMemo } from \"react\";\r\nimport type { HTMLAttributes, ReactElement } from \"react\";\r\nimport { renderLineTable, renderTokenNodes } from \"../react-renderer\";\r\n\r\nexport interface HighlightProps {\r\n code: string;\r\n language: string;\r\n options?: Omit<HighlightOptions, \"wrapInPre\" | \"language\">;\r\n preProps?: HTMLAttributes<HTMLPreElement>;\r\n codeProps?: HTMLAttributes<HTMLElement>;\r\n}\r\n\r\nexport function Highlight({\r\n code,\r\n language,\r\n options,\r\n preProps,\r\n codeProps,\r\n}: HighlightProps): ReactElement {\r\n const tokens = useMemo(() => {\r\n const rawTokens = tokenize(code, language);\r\n return options?.semanticHighlighting ? enhanceSemantics(rawTokens) : rawTokens;\r\n }, [code, language, options?.semanticHighlighting]);\r\n\r\n const { children: _ignoredChildren, ...safeCodeProps } = codeProps ?? {};\r\n\r\n const mergedPreStyle = {\r\n ...(options?.theme?.background ? { background: options.theme.background } : {}),\r\n ...(options?.theme?.foreground ? { color: options.theme.foreground } : {}),\r\n ...preProps?.style,\r\n };\r\n\r\n const classPrefix = options?.classPrefix;\r\n const content = options?.lineNumbers\r\n ? renderLineTable(tokens, {\r\n classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n startLine: options.startLine,\r\n dataLineAttributes: options.dataLineAttributes,\r\n })\r\n : renderTokenNodes(tokens, {\r\n classPrefix,\r\n theme: options?.theme,\r\n decorations: options?.decorations,\r\n });\r\n\r\n return (\r\n <pre\r\n {...preProps}\r\n className={[\"hlts\", `hlts-lang-${language}`, preProps?.className].filter(Boolean).join(\" \")}\r\n style={mergedPreStyle}\r\n >\r\n <code {...safeCodeProps}>{content}</code>\r\n </pre>\r\n );\r\n}\r\n","import { applyDecorations, groupTokensByLine } from \"tree-sitter-ts-highlight\";\r\nimport type {\r\n Decoration,\r\n DiffOptions,\r\n HtmlTheme,\r\n} from \"tree-sitter-ts-highlight\";\r\nimport type { Token } from \"tree-sitter-ts\";\r\nimport type { CSSProperties, ReactNode } from \"react\";\r\n\r\ninterface RenderTokenNodesOptions {\r\n classPrefix?: string;\r\n theme?: HtmlTheme;\r\n decorations?: Decoration[];\r\n keyPrefix?: string;\r\n}\r\n\r\ninterface RenderLineTableOptions extends RenderTokenNodesOptions {\r\n startLine?: number;\r\n dataLineAttributes?: boolean;\r\n}\r\n\r\ninterface RenderDiffTableOptions extends RenderTokenNodesOptions {\r\n view?: DiffOptions[\"view\"];\r\n showHeader?: boolean;\r\n oldLabel: string;\r\n newLabel: string;\r\n rows: Array<{\r\n changeType: \"context\" | \"added\" | \"removed\" | \"modified\";\r\n oldLineNumber: number | null;\r\n newLineNumber: number | null;\r\n oldText: string;\r\n newText: string;\r\n }>;\r\n oldLineTokens: Map<number, Token[]>;\r\n newLineTokens: Map<number, Token[]>;\r\n}\r\n\r\nexport function renderTokenNodes(\r\n tokens: Token[],\r\n options: RenderTokenNodesOptions = {},\r\n): ReactNode[] {\r\n const prefix = options.classPrefix ?? \"hlts-\";\r\n const decorated =\r\n options.decorations && options.decorations.length > 0\r\n ? applyDecorations(tokens, options.decorations)\r\n : tokens.map((token) => ({\r\n token,\r\n extraClasses: [],\r\n extraAttrs: {},\r\n extraStyle: undefined,\r\n }));\r\n\r\n return decorated.map(({ token, extraClasses, extraAttrs, extraStyle }, index) => {\r\n if (token.category === \"whitespace\" || token.category === \"newline\") {\r\n return token.value;\r\n }\r\n\r\n const key = `${options.keyPrefix ?? \"tok\"}-${index}`;\r\n const dataAttrs = Object.fromEntries(\r\n Object.entries(extraAttrs).map(([k, v]) => [`data-${k}`, v]),\r\n );\r\n\r\n if (options.theme) {\r\n const baseStyle = options.theme.styles[token.category] ?? \"\";\r\n const style = cssTextToReactStyle([baseStyle, extraStyle].filter(Boolean).join(\";\"));\r\n\r\n return (\r\n <span key={key} style={style} {...dataAttrs}>\r\n {token.value}\r\n </span>\r\n );\r\n }\r\n\r\n return (\r\n <span\r\n key={key}\r\n className={[prefix + token.category, ...extraClasses].join(\" \")}\r\n {...dataAttrs}\r\n >\r\n {token.value}\r\n </span>\r\n );\r\n });\r\n}\r\n\r\nexport function renderLineTable(\r\n tokens: Token[],\r\n options: RenderLineTableOptions = {},\r\n): ReactNode {\r\n const groups = groupTokensByLine(tokens);\r\n const startLine = options.startLine ?? 1;\r\n const withDataLine = options.dataLineAttributes ?? true;\r\n\r\n return (\r\n <table className=\"hlts-table\">\r\n <tbody>\r\n {groups.map((group, index) => {\r\n const displayLine = group.lineNumber - groups[0].lineNumber + startLine;\r\n return (\r\n <tr\r\n key={`line-${group.lineNumber}-${index}`}\r\n {...(withDataLine ? { \"data-line\": displayLine } : {})}\r\n >\r\n <td className=\"hlts-line-number\">{displayLine}</td>\r\n <td className=\"hlts-line-content\">\r\n {renderTokenNodes(group.tokens, {\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `line-${group.lineNumber}`,\r\n })}\r\n </td>\r\n </tr>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n );\r\n}\r\n\r\nexport function renderDiffTable(options: RenderDiffTableOptions): ReactNode {\r\n const prefix = options.classPrefix ?? \"hlts-\";\r\n const view = options.view ?? \"side-by-side\";\r\n\r\n if (view === \"inline\") {\r\n return (\r\n <table className={`${prefix}diff ${prefix}diff-inline`}>\r\n <tbody>\r\n {options.rows.flatMap((row, index) => {\r\n if (row.changeType === \"context\") {\r\n return [\r\n <tr key={`ctx-${index}`} className={`${prefix}diff-row ${prefix}diff-context`}>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-sign`}> </td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-context-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n ];\r\n }\r\n\r\n const nodes: ReactNode[] = [];\r\n\r\n if (row.oldLineNumber !== null) {\r\n nodes.push(\r\n <tr key={`old-${index}`} className={`${prefix}diff-row ${prefix}diff-removed`}>\r\n <td className={`${prefix}diff-gutter`}>{row.oldLineNumber}</td>\r\n <td className={`${prefix}diff-sign`}>-</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.oldLineNumber,\r\n tokenMap: options.oldLineTokens,\r\n fallbackText: row.oldText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-old-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n );\r\n }\r\n\r\n if (row.newLineNumber !== null) {\r\n nodes.push(\r\n <tr key={`new-${index}`} className={`${prefix}diff-row ${prefix}diff-added`}>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber}</td>\r\n <td className={`${prefix}diff-sign`}>+</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `inline-new-${index}`,\r\n })}\r\n </td>\r\n </tr>,\r\n );\r\n }\r\n\r\n return nodes;\r\n })}\r\n </tbody>\r\n </table>\r\n );\r\n }\r\n\r\n return (\r\n <table className={`${prefix}diff ${prefix}diff-side-by-side`}>\r\n <tbody>\r\n {(options.showHeader ?? true) && (\r\n <tr className={`${prefix}diff-header`}>\r\n <th className={`${prefix}diff-label`} colSpan={2}>\r\n {options.oldLabel}\r\n </th>\r\n <th className={`${prefix}diff-label`} colSpan={2}>\r\n {options.newLabel}\r\n </th>\r\n </tr>\r\n )}\r\n\r\n {options.rows.map((row, index) => (\r\n <tr\r\n key={`row-${index}`}\r\n className={`${prefix}diff-row ${prefix}diff-${row.changeType}`}\r\n >\r\n <td className={`${prefix}diff-gutter`}>{row.oldLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.oldLineNumber,\r\n tokenMap: options.oldLineTokens,\r\n fallbackText: row.oldText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `side-old-${index}`,\r\n })}\r\n </td>\r\n <td className={`${prefix}diff-gutter`}>{row.newLineNumber ?? \"\"}</td>\r\n <td className={`${prefix}diff-content`}>\r\n {renderDiffLineContent({\r\n lineNumber: row.newLineNumber,\r\n tokenMap: options.newLineTokens,\r\n fallbackText: row.newText,\r\n classPrefix: options.classPrefix,\r\n theme: options.theme,\r\n decorations: options.decorations,\r\n keyPrefix: `side-new-${index}`,\r\n })}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n );\r\n}\r\n\r\nfunction renderDiffLineContent(params: {\r\n lineNumber: number | null;\r\n tokenMap: Map<number, Token[]>;\r\n fallbackText: string;\r\n classPrefix?: string;\r\n theme?: HtmlTheme;\r\n decorations?: Decoration[];\r\n keyPrefix: string;\r\n}): ReactNode {\r\n if (params.lineNumber === null) {\r\n return \"\";\r\n }\r\n\r\n const tokens = params.tokenMap.get(params.lineNumber);\r\n if (!tokens || tokens.length === 0) {\r\n return params.fallbackText;\r\n }\r\n\r\n return renderTokenNodes(tokens, {\r\n classPrefix: params.classPrefix,\r\n theme: params.theme,\r\n decorations: params.decorations,\r\n keyPrefix: params.keyPrefix,\r\n });\r\n}\r\n\r\nfunction cssTextToReactStyle(cssText: string): CSSProperties {\r\n const style: Record<string, string> = {};\r\n\r\n for (const declaration of cssText.split(\";\")) {\r\n const trimmed = declaration.trim();\r\n if (!trimmed) {\r\n continue;\r\n }\r\n\r\n const separatorIndex = trimmed.indexOf(\":\");\r\n if (separatorIndex === -1) {\r\n continue;\r\n }\r\n\r\n const property = trimmed.slice(0, separatorIndex).trim();\r\n const value = trimmed.slice(separatorIndex + 1).trim();\r\n if (!property || !value) {\r\n continue;\r\n }\r\n\r\n style[kebabToCamel(property)] = value;\r\n }\r\n\r\n return style as CSSProperties;\r\n}\r\n\r\nfunction kebabToCamel(property: string): string {\r\n return property.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());\r\n}\r\n","import { createDiffModelWithTokens } from \"tree-sitter-ts-highlight\";\r\nimport type { DiffOptions } from \"tree-sitter-ts-highlight\";\r\nimport { useMemo } from \"react\";\r\nimport type { HTMLAttributes, ReactElement } from \"react\";\r\nimport { renderDiffTable } from \"../react-renderer\";\r\n\r\ntype DataAttributes = {\r\n [key: `data-${string}`]: string | number | boolean | undefined;\r\n};\r\n\r\nexport interface HighlightDiffProps {\r\n oldCode: string;\r\n newCode: string;\r\n language: string;\r\n options?: DiffOptions;\r\n containerProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\r\n}\r\n\r\nexport function HighlightDiff({\r\n oldCode,\r\n newCode,\r\n language,\r\n options,\r\n containerProps,\r\n}: HighlightDiffProps): ReactElement {\r\n const diff = useMemo(\r\n () => createDiffModelWithTokens(oldCode, newCode, language, options),\r\n [oldCode, newCode, language, options],\r\n );\r\n\r\n const { children: _ignoredChildren, ...safeContainerProps } = containerProps ?? {};\r\n\r\n const table = renderDiffTable({\r\n view: options?.view,\r\n showHeader: options?.showHeader,\r\n classPrefix: options?.classPrefix,\r\n decorations: options?.decorations,\r\n theme: options?.theme,\r\n oldLabel: diff.model.oldLabel,\r\n newLabel: diff.model.newLabel,\r\n rows: diff.model.rows,\r\n oldLineTokens: diff.oldLineTokens,\r\n newLineTokens: diff.newLineTokens,\r\n });\r\n\r\n return <div {...safeContainerProps}>{table}</div>;\r\n}\r\n","import { highlight, highlightDiff } from \"tree-sitter-ts-highlight\";\r\nimport type { DiffOptions, HighlightOptions } from \"tree-sitter-ts-highlight\";\r\nimport { useMemo } from \"react\";\r\n\r\nexport interface UseHighlightedHtmlParams {\r\n code: string;\r\n language: string;\r\n options?: Omit<HighlightOptions, \"wrapInPre\" | \"language\">;\r\n}\r\n\r\nexport function useHighlightedHtml({\r\n code,\r\n language,\r\n options,\r\n}: UseHighlightedHtmlParams): string {\r\n return useMemo(\r\n () =>\r\n highlight(code, language, {\r\n ...options,\r\n wrapInPre: false,\r\n language,\r\n }),\r\n [code, language, options],\r\n );\r\n}\r\n\r\nexport interface UseHighlightedDiffHtmlParams {\r\n oldCode: string;\r\n newCode: string;\r\n language: string;\r\n options?: DiffOptions;\r\n}\r\n\r\nexport function useHighlightedDiffHtml({\r\n oldCode,\r\n newCode,\r\n language,\r\n options,\r\n}: UseHighlightedDiffHtmlParams): string {\r\n return useMemo(\r\n () => highlightDiff(oldCode, newCode, language, options),\r\n [oldCode, newCode, language, options],\r\n );\r\n}\r\n","export { Highlight } from \"./components/Highlight\";\r\nexport type { HighlightProps } from \"./components/Highlight\";\r\n\r\nexport { HighlightDiff } from \"./components/HighlightDiff\";\r\nexport type { HighlightDiffProps } from \"./components/HighlightDiff\";\r\n\r\nexport {\r\n useHighlightedHtml,\r\n useHighlightedDiffHtml,\r\n} from \"./hooks/useHighlightedHtml\";\r\nexport type {\r\n UseHighlightedHtmlParams,\r\n UseHighlightedDiffHtmlParams,\r\n} from \"./hooks/useHighlightedHtml\";\r\n\r\nexport type * from \"tree-sitter-ts\";\r\nexport type * from \"tree-sitter-ts-highlight\";\r\nexport * from \"tree-sitter-ts-highlight\";\r\n"],"mappings":"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","names":["jsx","useMemo","jsx","useMemo","useMemo"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tree-sitter-ts-highlight-react",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "React wrapper components and hooks for tree-sitter-ts-highlight.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "jest",
|
|
28
|
+
"test:watch": "jest --watch"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
32
|
+
"tree-sitter-ts": "^0.1.2",
|
|
33
|
+
"tree-sitter-ts-highlight": "^0.1.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@swc/core": "^1.11.22",
|
|
37
|
+
"@swc/jest": "^0.2.39",
|
|
38
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
39
|
+
"@testing-library/react": "^16.3.0",
|
|
40
|
+
"@types/jest": "^30.0.0",
|
|
41
|
+
"@types/react": "^19.1.10",
|
|
42
|
+
"jest": "^30.0.5",
|
|
43
|
+
"jest-environment-jsdom": "^30.0.5",
|
|
44
|
+
"react": "^19.1.1",
|
|
45
|
+
"react-dom": "^19.1.1",
|
|
46
|
+
"tree-sitter-ts": "^0.1.2",
|
|
47
|
+
"tree-sitter-ts-highlight": "^0.1.2",
|
|
48
|
+
"tsup": "^8.0.0",
|
|
49
|
+
"typescript": "^5.4.0"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"provenance": false
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"react",
|
|
57
|
+
"tree-sitter",
|
|
58
|
+
"syntax-highlighting",
|
|
59
|
+
"typescript",
|
|
60
|
+
"code-highlighting"
|
|
61
|
+
],
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "https://github.com/hieutran512/tree-sitter-ts-highlight-react"
|
|
65
|
+
},
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=18.0.0"
|
|
69
|
+
}
|
|
70
|
+
}
|