@vortexlabs/kirari-rehype 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @vortexlabs/kirari-rehype
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ef4462a: Publish under unscoped names: `kirari`, `kirari-react`, and `kirari-rehype`. The `@vortexlabs/kirari`, `@vortexlabs/kirari-react`, and `@vortexlabs/kirari-rehype` packages now re-export them, so existing imports keep working.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [ef4462a]
12
+ - kirari@1.2.0
13
+ - kirari-rehype@1.1.0
14
+
15
+ ## 1.0.2
16
+
17
+ ### Patch Changes
18
+
19
+ - 52ad674: Stop line wrappers from adding an empty last line after a code block's trailing newline. Markdown code blocks always end with a newline, so every block rendered with `lines` or fence line metadata such as `{2,4-6}` showed an extra empty line, and an extra number when line numbers come from CSS. The newline still follows the last wrapper, so the source text is unchanged, and blank lines before it keep their wrappers.
20
+
3
21
  ## 1.0.1
4
22
 
5
23
  ### Patch Changes
package/README.md CHANGED
@@ -1,176 +1,13 @@
1
1
  # @vortexlabs/kirari-rehype
2
2
 
3
- Highlight code blocks in rendered Markdown and MDX with Kirari. This optional
4
- package converts Kirari tokens directly into structured HTML syntax tree nodes.
5
- It does not generate raw HTML or require a DOM.
3
+ This package has been renamed to [`kirari-rehype`](https://www.npmjs.com/package/kirari-rehype).
6
4
 
7
- Run it while building your content to send highlighted HTML and CSS to the
8
- browser, with no client-side highlighter. The integration is separate from the
9
- core package and does not change its bundle.
10
-
11
- [Documentation](https://kirari.sh/docs/) · [Playground](https://kirari.sh/playground/)
12
-
13
- ## Install
5
+ `@vortexlabs/kirari-rehype` re-exports `kirari-rehype` on every entry point, so existing imports keep working and keep receiving updates. New projects should install `kirari-rehype` directly:
14
6
 
15
7
  ```sh
16
- npm install @vortexlabs/kirari @vortexlabs/kirari-rehype
17
- ```
18
-
19
- The plugin uses your application's Kirari through a peer dependency.
20
-
21
- ## Markdown to HTML
22
-
23
- Install `unified`, `remark-parse`, `remark-rehype`, and `rehype-stringify` in your
24
- application if it does not already have a Markdown pipeline.
25
-
26
- ```ts
27
- import { unified } from 'unified'
28
- import remarkParse from 'remark-parse'
29
- import remarkRehype from 'remark-rehype'
30
- import rehypeStringify from 'rehype-stringify'
31
- import rehypeKirari from '@vortexlabs/kirari-rehype'
32
- import { javascript } from '@vortexlabs/kirari/languages/javascript'
33
- import { typescript } from '@vortexlabs/kirari/languages/typescript'
34
-
35
- const processor = unified()
36
- .use(remarkParse)
37
- .use(remarkRehype)
38
- .use(rehypeKirari, { languages: [javascript, typescript] })
39
- .use(rehypeStringify)
40
-
41
- const source = '```js\nconst answer = 42\n```'
42
- const html = String(await processor.process(source))
43
- ```
44
-
45
- Import the optional theme once from your application's stylesheet entry point:
46
-
47
- ```ts
48
- import '@vortexlabs/kirari/theme.css'
8
+ npm install kirari-rehype
49
9
  ```
50
10
 
51
- The plugin adds `.kirari` to the enclosing `<pre>`, preserves existing properties
52
- and the code element's `language-*` class, and adds `.kirari-*` syntax spans.
53
- Customize colors with variables such as `--kirari-keyword` and `--kirari-variable`.
54
-
55
- ## MDX
56
-
57
- Pass the same plugin to the MDX compiler's `rehypePlugins` option:
58
-
59
- ```ts
60
- import { compile } from '@mdx-js/mdx'
61
- import rehypeKirari from '@vortexlabs/kirari-rehype'
62
- import { javascript } from '@vortexlabs/kirari/languages/javascript'
63
-
64
- const markdown = '```js\nconst answer = 42\n```'
65
- const compiled = await compile(markdown, {
66
- rehypePlugins: [[rehypeKirari, { languages: [javascript] }]],
67
- })
68
- ```
69
-
70
- Frameworks that configure MDX expose this option in different places. Keep the
71
- theme in the application entry point; it does not belong in compiler options.
72
- Custom MDX `pre` and `code` components should forward the generated properties
73
- and children.
74
-
75
- ## Register only what you need
76
-
77
- No languages are registered by default. Import each definition and pass it in
78
- `languages`. Labels resolve through [Kirari's language registry](https://github.com/vortexkit/kirari/blob/main/docs/language-integration.md),
79
- so `js` selects registered JavaScript and `ts` selects registered TypeScript.
80
- There is no language detection or automatic grammar loading. JSX and TSX need
81
- their own definitions.
82
-
83
- The plugin only highlights labelled, registered `<code>` elements directly
84
- inside `<pre>` whose children are all text nodes. It leaves unlabelled code,
85
- unknown languages, inline code, and code with existing element children
86
- untouched. A `no-highlight` or `nohighlight` class on either `pre` or `code` also
87
- skips the block. Ordinary Markdown serialization still escapes their text.
88
-
89
- `@vortexlabs/kirari/markdown` serves a different purpose: it colors Markdown source itself,
90
- including the fence markers. Use `@vortexlabs/kirari-rehype` when rendering a Markdown
91
- document into HTML or MDX.
92
-
93
- ## Lines and fence metadata
94
-
95
- Add a standalone brace group to a fence's metadata to highlight lines:
96
-
97
- ````md
98
- ```js {2,4-6}
99
- const count = 1
100
- const doubled = count * 2
101
-
102
- console.log(count)
103
- console.log(doubled)
104
- console.log('done')
105
- ```
106
- ````
107
-
108
- Line numbers are one-based. Valid metadata enables `.kirari-line` wrappers with
109
- `data-line` attributes and adds `.kirari-line-highlighted` to selected lines.
110
- Malformed groups and groups inside quoted metadata values are ignored.
111
- Metadata is read from `code.data.meta`, as supplied by `remark-rehype` and MDX.
112
-
113
- Provide your own line highlight styling:
114
-
115
- ```css
116
- .kirari-line-highlighted {
117
- background: var(--kirari-line-background, #7452bd18);
118
- }
119
- ```
120
-
121
- Use `lines: true` to wrap every highlighted block's lines even without metadata.
122
- The `lineClassName` callback can append your own classes:
123
-
124
- ```ts
125
- import type { Options } from '@vortexlabs/kirari-rehype'
126
- import { javascript } from '@vortexlabs/kirari/languages/javascript'
127
-
128
- const options = {
129
- languages: [javascript],
130
- lines: true,
131
- lineClassName: ({ number }) => number === 1 ? 'opening-line' : undefined,
132
- } satisfies Options
133
- ```
134
-
135
- Line endings remain outside the wrappers. Empty and trailing lines are
136
- preserved. Callback offsets are UTF-16 code units into the code block's source;
137
- `start` is inclusive and `end` is exclusive and excludes the line ending.
138
- Token class callbacks receive each original token once, even when a token spans
139
- multiple lines.
140
-
141
- ## Options
142
-
143
- The default export is the plugin. `Options` is its exported TypeScript type.
144
-
145
- | Option | Default | Behavior |
146
- | --- | --- | --- |
147
- | `languages` | `[]` | Explicit readonly list of Kirari language definitions. |
148
- | `classes` | None | Token category to extra CSS class map, as in Kirari's `RenderOptions`. |
149
- | `className(token)` | None | Extra CSS classes for each original token. |
150
- | `lines` | `false` | Wrap every line of a highlighted block. Valid line metadata also enables wrappers. |
151
- | `lineClassName({ number, start, end })` | None | Extra classes for a line; called only when line wrappers are enabled. |
152
-
153
- Custom classes append to the standard Kirari classes. Plain `text` tokens remain
154
- text nodes unless they receive a custom class. Source strings become text node
155
- values, and classes become element properties; the downstream serializer or
156
- MDX compiler handles escaping.
157
-
158
- ## Pipeline order
159
-
160
- Run the plugin after `remark-rehype`, when the document has become an HTML tree,
161
- and before `rehype-stringify`. In MDX, use `rehypePlugins`, not `remarkPlugins`.
162
- Configure one highlighter for a block; this plugin skips existing markup rather
163
- than removing another plugin's spans.
164
-
165
- If your pipeline sanitizes HTML, run `rehype-sanitize` before `rehypeKirari` and
166
- preserve the `language-*` classes needed for lookup. If sanitization runs after
167
- highlighting instead, its schema must allow Kirari's span classes, `.kirari` on
168
- `pre`, and `data-line` when lines are enabled.
169
-
170
- This plugin does not sanitize the surrounding document or change how your
171
- pipeline handles raw HTML or MDX expressions. Language definitions and callbacks
172
- remain trusted application code.
173
-
174
- ## License
11
+ To switch an existing project, replace `@vortexlabs/kirari-rehype` with `kirari-rehype` in `package.json` and in import paths. Nothing else changes.
175
12
 
176
- [MIT](https://github.com/vortexkit/kirari/blob/main/packages/rehype/LICENSE) © 2026 [Ugi](https://ugi.dev)
13
+ Documentation: [kirari.sh](https://kirari.sh)
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from 'kirari-rehype'
2
+ export { default } from 'kirari-rehype'
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from 'kirari-rehype'
2
+ export { default } from 'kirari-rehype'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vortexlabs/kirari-rehype",
3
- "version": "1.0.1",
4
- "description": "Kirari syntax highlighting for Rehype and MDX.",
3
+ "version": "1.1.0",
4
+ "description": "Renamed to kirari-rehype. This package re-exports kirari-rehype so existing imports keep working.",
5
5
  "keywords": [
6
6
  "rehype",
7
7
  "rehype-plugin",
@@ -14,47 +14,31 @@
14
14
  "repository": {
15
15
  "type": "git",
16
16
  "url": "git+https://github.com/vortexkit/kirari.git",
17
- "directory": "packages/rehype"
17
+ "directory": "packages/compat-rehype"
18
18
  },
19
19
  "type": "module",
20
20
  "sideEffects": false,
21
21
  "files": [
22
- "dist",
22
+ "index.js",
23
+ "index.d.ts",
23
24
  "README.md",
24
25
  "LICENSE",
25
26
  "CHANGELOG.md"
26
27
  ],
27
28
  "exports": {
28
29
  ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js"
30
+ "types": "./index.d.ts",
31
+ "import": "./index.js"
31
32
  }
32
33
  },
33
34
  "scripts": {
34
- "build": "tsc -p tsconfig.build.json",
35
35
  "test": "bun test ./test",
36
36
  "typecheck": "tsc --noEmit",
37
- "check": "bun run typecheck && bun run test && bun run build && bun test ./checks",
38
- "prepublishOnly": "bun run build"
37
+ "check": "bun run typecheck && bun test ./test"
39
38
  },
40
39
  "dependencies": {
41
- "@types/hast": "^3.0.5"
42
- },
43
- "peerDependencies": {
44
- "@vortexlabs/kirari": "^1.0.0"
45
- },
46
- "devDependencies": {
47
- "@mdx-js/mdx": "^3.1.1",
48
- "@types/react": "^19.3.0",
49
- "@types/react-dom": "^19.3.0",
50
- "@vortexlabs/kirari": "1.0.1",
51
- "react": "^19.3.1",
52
- "react-dom": "^19.3.1",
53
- "rehype-sanitize": "^6.0.0",
54
- "rehype-stringify": "^10.0.1",
55
- "remark-parse": "^11.0.0",
56
- "remark-rehype": "^11.1.2",
57
- "unified": "^11.0.5"
40
+ "kirari": "^1.2.0",
41
+ "kirari-rehype": "^1.1.0"
58
42
  },
59
43
  "publishConfig": {
60
44
  "access": "public"
package/dist/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { Root } from 'hast';
2
- import type { Language, RenderOptions } from '@vortexlabs/kirari/core';
3
- export interface Options extends RenderOptions {
4
- readonly languages?: readonly Language[];
5
- readonly lines?: boolean;
6
- readonly lineClassName?: (line: {
7
- readonly number: number;
8
- readonly start: number;
9
- readonly end: number;
10
- }) => string | undefined;
11
- }
12
- declare const rehypeKirari: (options?: Options) => (tree: Root) => undefined;
13
- export default rehypeKirari;
package/dist/index.js DELETED
@@ -1,149 +0,0 @@
1
- import { tokenize } from '@vortexlabs/kirari/core';
2
- import { createRegistry } from '@vortexlabs/kirari/registry';
3
- const classes = (value) => {
4
- return (Array.isArray(value) ? value : [value])
5
- .filter((item) => typeof item === 'string' || typeof item === 'number')
6
- .flatMap(item => String(item).split(/[\t\n\f\r ]+/).filter(Boolean));
7
- };
8
- const lineRanges = (meta) => {
9
- if (typeof meta !== 'string')
10
- return [];
11
- const ranges = [];
12
- let quote = '';
13
- let start = -1;
14
- let depth = 0;
15
- for (let index = 0; index < meta.length; index++) {
16
- const char = meta[index];
17
- if (char === '\\') {
18
- index++;
19
- start = -1;
20
- }
21
- else if (quote) {
22
- if (char === quote)
23
- quote = '';
24
- }
25
- else if (char === '"' || char === "'") {
26
- quote = char;
27
- start = -1;
28
- }
29
- else if (char === '{') {
30
- depth++;
31
- start = depth === 1 && (index === 0 || /\s/.test(meta[index - 1])) ? index + 1 : -1;
32
- }
33
- else if (char === '}') {
34
- depth = Math.max(0, depth - 1);
35
- if (depth !== 0 || start < 0)
36
- continue;
37
- const body = meta.slice(start, index);
38
- if ((index + 1 === meta.length || /\s/.test(meta[index + 1]))
39
- && /^\s*[1-9]\d*(?:\s*-\s*[1-9]\d*)?(?:\s*,\s*[1-9]\d*(?:\s*-\s*[1-9]\d*)?)*\s*$/.test(body)) {
40
- const parsed = body.split(',').map(part => {
41
- const [first, last = first] = part.split('-').map(Number);
42
- return [first, last];
43
- });
44
- if (parsed.every(([first, last]) => Number.isSafeInteger(first) && Number.isSafeInteger(last) && first <= last)) {
45
- for (const range of parsed)
46
- ranges.push(range);
47
- }
48
- }
49
- start = -1;
50
- }
51
- else if (start >= 0 && !/[\d\s,-]/.test(char)) {
52
- start = -1;
53
- }
54
- }
55
- return ranges.sort((left, right) => left[0] - right[0]);
56
- };
57
- const tokenNodes = (source, tokens, options, ranges) => {
58
- let offset = 0;
59
- for (const token of tokens) {
60
- if (token.start !== offset || token.end !== offset + token.value.length
61
- || token.value !== source.slice(token.start, token.end)) {
62
- throw new RangeError('Kirari: tokens must cover the original code contiguously');
63
- }
64
- offset = token.end;
65
- }
66
- if (offset !== source.length)
67
- throw new RangeError('Kirari: tokens must cover the original code contiguously');
68
- // Resolve callbacks once before any multiline token is split into line spans.
69
- const tokenClasses = tokens.map(token => {
70
- const mapped = options.classes && Object.hasOwn(options.classes, token.type) ? options.classes[token.type] : undefined;
71
- const extra = [mapped, options.className?.(token)].filter(Boolean).join(' ');
72
- return token.type === 'text' && !extra ? [] : classes(`kirari-${token.type}${extra ? ` ${extra}` : ''}`);
73
- });
74
- let tokenIndex = 0;
75
- const rangeNodes = (start, end) => {
76
- const nodes = [];
77
- while (start < end) {
78
- while (tokens[tokenIndex].end <= start)
79
- tokenIndex++;
80
- const token = tokens[tokenIndex];
81
- const stop = Math.min(end, token.end);
82
- const text = { type: 'text', value: source.slice(start, stop) };
83
- const className = tokenClasses[tokenIndex];
84
- nodes.push(className.length ? { type: 'element', tagName: 'span', properties: { className }, children: [text] } : text);
85
- start = stop;
86
- }
87
- return nodes;
88
- };
89
- if (!options.lines && ranges.length === 0)
90
- return rangeNodes(0, source.length);
91
- const nodes = [];
92
- const newline = /\r\n|\r|\n/g;
93
- let start = 0;
94
- let number = 1;
95
- let rangeIndex = 0;
96
- do {
97
- const match = newline.exec(source);
98
- const end = match?.index ?? source.length;
99
- const className = ['kirari-line', ...classes(options.lineClassName?.({ number, start, end }))];
100
- while (ranges[rangeIndex] && ranges[rangeIndex][1] < number)
101
- rangeIndex++;
102
- if (ranges[rangeIndex] && ranges[rangeIndex][0] <= number)
103
- className.push('kirari-line-highlighted');
104
- nodes.push({ type: 'element', tagName: 'span', properties: { className, dataLine: number }, children: rangeNodes(start, end) });
105
- if (!match)
106
- break;
107
- nodes.push({ type: 'text', value: match[0] });
108
- start = end + match[0].length;
109
- number++;
110
- } while (true);
111
- return nodes;
112
- };
113
- const rehypeKirari = (options = {}) => {
114
- const registry = createRegistry(options.languages ?? []);
115
- return tree => {
116
- const parents = [tree];
117
- while (parents.length) {
118
- const parent = parents.pop();
119
- for (const child of parent.children) {
120
- if (child.type === 'element' && child.tagName === 'pre') {
121
- const preClasses = classes(child.properties.className);
122
- if (!preClasses.some(name => name === 'no-highlight' || name === 'nohighlight')) {
123
- for (const code of child.children) {
124
- if (code.type !== 'element' || code.tagName !== 'code' || code.children.some(node => node.type !== 'text'))
125
- continue;
126
- const codeClasses = classes(code.properties.className);
127
- if (codeClasses.some(name => name === 'no-highlight' || name === 'nohighlight'))
128
- continue;
129
- const language = codeClasses.filter(name => name.startsWith('language-')).map(name => registry.resolve(name.slice(9))).find(Boolean);
130
- if (!language)
131
- continue;
132
- const source = code.children.map(node => node.type === 'text' ? node.value : '').join('');
133
- const meta = code.data && 'meta' in code.data ? code.data.meta : undefined;
134
- const children = tokenNodes(source, tokenize(source, language), options, lineRanges(meta));
135
- code.children = children;
136
- child.properties.className = [...new Set([...preClasses, 'kirari'])];
137
- }
138
- }
139
- continue;
140
- }
141
- // MDX JSX nodes can contain normal HAST children without being elements themselves.
142
- if ('children' in child && Array.isArray(child.children))
143
- parents.push(child);
144
- }
145
- }
146
- return undefined;
147
- };
148
- };
149
- export default rehypeKirari;