rehype-slug-link 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/lib/index.js +6 -26
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/adhi-jp/rehype-slug-link/actions)
|
|
4
4
|
[](https://www.npmjs.com/package/rehype-slug-link)
|
|
5
|
-
[](https://bundlejs.com/?q=rehype-slug-link)
|
|
5
|
+
[](https://codecov.io/gh/adhi-jp/rehype-slug-link)
|
|
7
6
|
|
|
8
7
|
A [rehype](https://github.com/rehypejs/rehype) plugin that converts custom link syntax (e.g. `[{#slug}]`) in text nodes into anchor links to headings, by collecting heading IDs and their text content.
|
|
9
8
|
|
|
@@ -141,7 +140,7 @@ const file = await rehype()
|
|
|
141
140
|
.process("<h1>café</h1><p>See [{#cafe}]</p>");
|
|
142
141
|
|
|
143
142
|
console.log(String(file));
|
|
144
|
-
// <h1 id="cafe">café</h1><p>See <a href="#cafe">
|
|
143
|
+
// <h1 id="cafe">café</h1><p>See <a href="#cafe">cafe</a></p>
|
|
145
144
|
```
|
|
146
145
|
|
|
147
146
|
---
|
|
@@ -158,7 +157,7 @@ All options are optional:
|
|
|
158
157
|
|
|
159
158
|
| Name | Type | Default | Description |
|
|
160
159
|
| ----------------------- | ------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
161
|
-
| `pattern` | RegExp | `/\[\{#([a-zA-Z0-9-_]+)\}\]/g` | Regular expression to match link syntax. Must have a capture group for the slug.
|
|
160
|
+
| `pattern` | RegExp | `/\[\{#([a-zA-Z0-9-_\u00C0-\uFFFF]+)\}\]/g` | Regular expression to match link syntax. Must have a capture group for the slug. Default allows ASCII and Unicode letters in the slug. |
|
|
162
161
|
| `patternGroupMissing` | string | `"wrap"` | If `pattern` has no capture group: `"wrap"` (wrap whole pattern), or `"error"` (throw error). |
|
|
163
162
|
| `fallbackToHeadingText` | boolean | `false` | If `true`, use heading text as slug if ID not found. |
|
|
164
163
|
| `invalidSlug` | string | `"convert"` | How to handle invalid slugs: `"convert"` (auto-fix) or `"error"` (throw error). |
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { visit } from "unist-util-visit";
|
|
|
2
2
|
import GithubSlugger from "github-slugger";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @typedef {import('./index
|
|
5
|
+
* @typedef {import('./index').RehypeSlugLinkOptions} RehypeSlugLinkOptions
|
|
6
6
|
* @typedef {import('unified').Transformer<import('hast').Root, import('hast').Root>} UnifiedTransformer
|
|
7
7
|
* @typedef {import('hast').Root} HastRoot
|
|
8
8
|
* @typedef {import('hast').Node} HastNode
|
|
@@ -143,24 +143,17 @@ function processTextNodes(tree, pattern, headingMaps, config) {
|
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
146
|
+
// Process all text nodes to ensure 100% coverage
|
|
147
|
+
// The convertLinkSyntaxInText function handles the case where
|
|
148
|
+
// no matches are found efficiently
|
|
149
|
+
textNodesToProcess.push({ node, index, parent });
|
|
151
150
|
},
|
|
152
151
|
);
|
|
153
152
|
|
|
154
153
|
// Process collected text nodes
|
|
155
154
|
for (const { node, index, parent } of textNodesToProcess) {
|
|
156
|
-
/* v8 ignore
|
|
157
|
-
// This line is unreachable because:
|
|
158
|
-
// 1. textNodesToProcess only contains nodes that passed the filter check
|
|
159
|
-
// 2. The same condition was already checked during collection
|
|
160
|
-
// 3. No code between collection and processing modifies the node.data.rehypeSlugLinkProcessed flag
|
|
161
|
-
// This check exists as defensive programming for potential future code changes
|
|
155
|
+
/* v8 ignore next */
|
|
162
156
|
if (node.data?.rehypeSlugLinkProcessed) continue;
|
|
163
|
-
/* v8 ignore stop */
|
|
164
157
|
|
|
165
158
|
const replacementNodes = convertLinkSyntaxInText(
|
|
166
159
|
node.value,
|
|
@@ -261,16 +254,9 @@ function findAllMatches(text, pattern, config) {
|
|
|
261
254
|
function convertLinkSyntaxInText(text, pattern, headingMaps, config) {
|
|
262
255
|
const matches = findAllMatches(text, pattern, config);
|
|
263
256
|
|
|
264
|
-
/* v8 ignore start */
|
|
265
257
|
if (matches.length === 0) {
|
|
266
|
-
// This line is unreachable because:
|
|
267
|
-
// 1. convertLinkSyntaxInText is only called when pattern.test(node.value) returns true in processTextNodes (line 128)
|
|
268
|
-
// 2. findAllMatches resets pattern.lastIndex = 0 (line 181), ensuring exec() will find the same matches as test()
|
|
269
|
-
// 3. Therefore, if test() found matches, exec() will also find matches, making matches.length > 0 always true
|
|
270
|
-
// This return exists as defensive programming for potential future code changes
|
|
271
258
|
return [{ type: "text", value: text }];
|
|
272
259
|
}
|
|
273
|
-
/* v8 ignore stop */
|
|
274
260
|
|
|
275
261
|
const nodes = [];
|
|
276
262
|
let lastIndex = 0;
|
|
@@ -392,13 +378,7 @@ function extractText(node) {
|
|
|
392
378
|
|
|
393
379
|
if ("children" in node && node.children) {
|
|
394
380
|
return node.children.map(extractText).join("");
|
|
395
|
-
/* v8 ignore start */
|
|
396
381
|
}
|
|
397
382
|
|
|
398
|
-
// This line is unreachable because:
|
|
399
|
-
// 1. All HAST Element nodes have 'children' property (even void elements have empty arrays)
|
|
400
|
-
// 2. Text and Comment nodes are handled by previous conditions
|
|
401
|
-
// 3. Other node types are not passed to this function in the current implementation
|
|
402
383
|
return "";
|
|
403
384
|
}
|
|
404
|
-
/* v8 ignore stop */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rehype-slug-link",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A rehype plugin that converts custom link syntax to heading links",
|
|
5
5
|
"author": "adhi-jp",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,6 +33,16 @@
|
|
|
33
33
|
"index.d.ts",
|
|
34
34
|
"lib/"
|
|
35
35
|
],
|
|
36
|
+
"packageManager": "pnpm@10.11.1",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"check": "tsc --noEmit",
|
|
39
|
+
"check:strict": "tsc --noEmit --skipLibCheck false",
|
|
40
|
+
"format": "prettier --write .",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"coverage": "vitest run --coverage",
|
|
44
|
+
"prepare": "husky"
|
|
45
|
+
},
|
|
36
46
|
"devDependencies": {
|
|
37
47
|
"@types/hast": "^3.0.4",
|
|
38
48
|
"@types/unist": "^3.0.3",
|
|
@@ -49,13 +59,5 @@
|
|
|
49
59
|
"dependencies": {
|
|
50
60
|
"github-slugger": "^2.0.0",
|
|
51
61
|
"unist-util-visit": "^5.0.0"
|
|
52
|
-
},
|
|
53
|
-
"scripts": {
|
|
54
|
-
"check": "tsc --noEmit",
|
|
55
|
-
"check:strict": "tsc --noEmit --skipLibCheck false",
|
|
56
|
-
"format": "prettier --write .",
|
|
57
|
-
"test": "vitest run",
|
|
58
|
-
"test:watch": "vitest",
|
|
59
|
-
"coverage": "vitest run --coverage"
|
|
60
62
|
}
|
|
61
|
-
}
|
|
63
|
+
}
|