@mirrordown/mdit-attrs 0.0.0 → 0.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/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @mirrordown/mdit-attrs
2
+
3
+ > Part of [Mirrordown](https://github.com/mirrordown/mirrordown) — a suite of markdown syntax extensions for the unified and markdown-it ecosystems.
4
+
5
+ A markdown-it plugin for the `attrs` syntax extension.
6
+
7
+ ## Overview
8
+
9
+ The `attrs` plugin lets you attach HTML attributes (classes, IDs, data attributes, etc.) to almost any Markdown element using `{...}` syntax. It supports headings, paragraphs, code fences, inline elements, lists, tables, and horizontal rules.
10
+
11
+ ```html
12
+ <h2 id="my-section" class="highlight">My Section</h2>
13
+ ```
14
+
15
+ ## Syntax
16
+
17
+ Attributes are written in curly braces using CSS-like shorthand:
18
+
19
+ - `#id` → sets `id`
20
+ - `.class` → adds a class
21
+ - `key=value` → sets any attribute
22
+
23
+ ### Headings
24
+
25
+ ```markdown
26
+ ## My Section {#my-section .highlight}
27
+ ```
28
+
29
+ ### Code fences
30
+
31
+ ````markdown
32
+ ```ts {.language-typescript data-filename="example.ts"}
33
+ const x = 1;
34
+ ```
35
+ ````
36
+
37
+ ### Paragraphs
38
+
39
+ ```markdown
40
+ A paragraph with a custom class.
41
+ {.note}
42
+ ```
43
+
44
+ ### Inline elements
45
+
46
+ ```markdown
47
+ This is **important**{.warning} text.
48
+ ```
49
+
50
+ ### After lists and tables
51
+
52
+ ```markdown
53
+ - Item one
54
+ - Item two
55
+ {.checklist}
56
+ ```
57
+
58
+ ## Install
59
+
60
+ ```sh
61
+ npm install @mirrordown/mdit-attrs
62
+ ```
63
+
64
+ ### Standalone
65
+
66
+ ```ts
67
+ import MarkdownIt from "markdown-it";
68
+ import { attrs } from "@mirrordown/mdit-attrs";
69
+
70
+ const md = new MarkdownIt().use(attrs);
71
+ ```
72
+
73
+ ### VitePress
74
+
75
+ ```ts
76
+ // .vitepress/config.ts
77
+ import { defineConfig } from "vitepress";
78
+ import { attrs } from "@mirrordown/mdit-attrs";
79
+
80
+ export default defineConfig({
81
+ markdown: {
82
+ config: (md) => md.use(attrs)
83
+ }
84
+ });
85
+ ```
86
+
87
+ ## Documentation
88
+
89
+ Full documentation, more examples, and configuration options: [github.com/mirrordown/mirrordown](https://github.com/mirrordown/mirrordown) (dedicated docs site coming soon).
90
+
91
+ ## License
92
+
93
+ MIT © [Drake Costa](https://saeris.gg)
@@ -0,0 +1,25 @@
1
+ import { PluginWithOptions } from "markdown-it";
2
+
3
+ //#region src/types.d.ts
4
+ type AttrRuleName = "fence" | "inline" | "table" | "list" | "heading" | "hr" | "softbreak" | "block";
5
+ interface DelimiterConfig {
6
+ left: string;
7
+ right: string;
8
+ allowed: Array<string | RegExp>;
9
+ }
10
+ interface AttrsOptions extends Partial<DelimiterConfig> {
11
+ /**
12
+ * Which rules to enable. "all" enables everything (default). false or []
13
+ * disables all rules. An array of rule names enables only those rules.
14
+ */
15
+ rule?: "all" | boolean | AttrRuleName[];
16
+ }
17
+ type Attr = [key: string, value: string];
18
+ /** [startIndex, endIndex] within the token content string, exclusive of delimiters */
19
+ type DelimiterRange = [start: number, end: number];
20
+ //#endregion
21
+ //#region src/plugin.d.ts
22
+ declare const attrs: PluginWithOptions<AttrsOptions>;
23
+ //#endregion
24
+ export { type Attr, type AttrRuleName, type AttrsOptions, type DelimiterConfig, type DelimiterRange, attrs };
25
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/plugin.ts"],"mappings":";;;KAAY,YAAA;AAAA,UAUK,eAAA;EACf,IAAA;EACA,KAAA;EACA,OAAA,EAAS,KAAA,UAAe,MAAA;AAAA;AAAA,UAGT,YAAA,SAAqB,OAAA,CAAQ,eAAA;EAhBtB;AAUxB;;;EAWE,IAAA,qBAAyB,YAAA;AAAA;AAAA,KAGf,IAAA,IAAQ,GAAA,UAAa,KAAA;;KAGrB,cAAA,IAAkB,KAAA,UAAe,GAAA;;;cCtBhC,KAAA,EAAO,iBAAA,CAAkB,YAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,875 @@
1
+ //#region src/helper/getAttrs.ts
2
+ const CLASS_MARKER = 46;
3
+ const ID_MARKER = 35;
4
+ const PAIR_SEPARATOR = 32;
5
+ const KEY_SEPARATOR = 61;
6
+ const QUOTE_MARKER = 34;
7
+ const DISALLOWED_KEY_CHARS = new Set([
8
+ 9,
9
+ 10,
10
+ 12,
11
+ 32,
12
+ 47,
13
+ 62,
14
+ 34,
15
+ 39,
16
+ 61
17
+ ]);
18
+ const getAttrs = (str, range, allowed) => {
19
+ let key = "";
20
+ let value = "";
21
+ let parsingKey = true;
22
+ let valueInsideQuotes = false;
23
+ const attrs = [];
24
+ for (let i = range[0]; i < range[1]; i++) {
25
+ const code = str.charCodeAt(i);
26
+ if (code === KEY_SEPARATOR && parsingKey) {
27
+ parsingKey = false;
28
+ continue;
29
+ }
30
+ if (code === CLASS_MARKER && key === "") {
31
+ key = str.charCodeAt(i + 1) === CLASS_MARKER ? (i++, "css-module") : "class";
32
+ parsingKey = false;
33
+ continue;
34
+ }
35
+ if (code === ID_MARKER && key === "") {
36
+ key = "id";
37
+ parsingKey = false;
38
+ continue;
39
+ }
40
+ if (code === QUOTE_MARKER) {
41
+ if (value === "" && !valueInsideQuotes) valueInsideQuotes = true;
42
+ else if (valueInsideQuotes) valueInsideQuotes = false;
43
+ continue;
44
+ }
45
+ if (code === PAIR_SEPARATOR && !valueInsideQuotes) {
46
+ if (key !== "") {
47
+ attrs.push([key, value]);
48
+ key = "";
49
+ value = "";
50
+ parsingKey = true;
51
+ }
52
+ continue;
53
+ }
54
+ if (parsingKey) {
55
+ if (!DISALLOWED_KEY_CHARS.has(code)) key += str[i];
56
+ } else value += str[i];
57
+ }
58
+ if (key !== "") attrs.push([key, value]);
59
+ if (allowed.length === 0) return attrs;
60
+ return attrs.filter(([attr]) => allowed.some((item) => item instanceof RegExp ? item.test(attr) : item === attr));
61
+ };
62
+ //#endregion
63
+ //#region src/helper/addAttrs.ts
64
+ const addAttrs = (token, content, range, allowed) => {
65
+ for (const [key, value] of getAttrs(content, range, allowed)) if (key === "class" || key === "css-module") token.attrJoin(key, value);
66
+ else token.attrPush([key, value]);
67
+ };
68
+ //#endregion
69
+ //#region src/helper/getDelimiterChecker.ts
70
+ /**
71
+ * Returns a function that checks whether `str` contains a valid attribute
72
+ * block at the given position ("start", "end", or "only") and returns the
73
+ * [start, end] range of the content inside the delimiters, or null if no
74
+ * match. The returned range is exclusive of the delimiter characters.
75
+ *
76
+ * A valid block must contain at least one character (i.e. `{}` is invalid),
77
+ * and the outer delimiters must not be repeated (i.e. `{{}}` is invalid).
78
+ */
79
+ const getDelimiterChecker = (left, right) => {
80
+ const ll = left.length;
81
+ const rl = right.length;
82
+ const minLen = ll + 1 + rl;
83
+ const doubleLeft = left + left;
84
+ const doubleRight = right + right;
85
+ return (str, pos) => {
86
+ if (str.length < minLen) return null;
87
+ if (pos === "start" || pos === "only") {
88
+ if (!str.startsWith(left)) return null;
89
+ if (str.startsWith(doubleLeft)) return null;
90
+ }
91
+ if (pos === "end" || pos === "only") {
92
+ if (!str.endsWith(right)) return null;
93
+ if (str.endsWith(doubleRight)) return null;
94
+ }
95
+ if (pos === "only") {
96
+ const contentEnd = str.length - rl;
97
+ if (contentEnd <= ll) return null;
98
+ return [ll, contentEnd];
99
+ }
100
+ if (pos === "start") {
101
+ const end = str.indexOf(right, ll + 1);
102
+ if (end === -1) return null;
103
+ return [ll, end];
104
+ }
105
+ const start = str.lastIndexOf(left, str.length - rl - 1);
106
+ if (start === -1) return null;
107
+ if (str.startsWith(left, start - ll)) return null;
108
+ return [start + ll, str.length - rl];
109
+ };
110
+ };
111
+ //#endregion
112
+ //#region src/helper/testRule.ts
113
+ const matches = (test, actual) => {
114
+ if (typeof test === "function") return test(actual);
115
+ return test === actual;
116
+ };
117
+ const testChild = (child, childTest) => {
118
+ let range = null;
119
+ if (childTest.type !== void 0 && !matches(childTest.type, child.type)) return {
120
+ match: false,
121
+ range: null
122
+ };
123
+ if (childTest.nesting !== void 0 && !matches(childTest.nesting, child.nesting)) return {
124
+ match: false,
125
+ range: null
126
+ };
127
+ if (childTest.tag !== void 0 && child.tag !== childTest.tag) return {
128
+ match: false,
129
+ range: null
130
+ };
131
+ if (childTest.content !== void 0) {
132
+ if (!matches(childTest.content, child.content)) return {
133
+ match: false,
134
+ range: null
135
+ };
136
+ }
137
+ if (childTest.attrChecker) {
138
+ range = childTest.attrChecker(child.content, childTest.attrPos ?? "only");
139
+ if (!range) return {
140
+ match: false,
141
+ range: null
142
+ };
143
+ }
144
+ return {
145
+ match: true,
146
+ range
147
+ };
148
+ };
149
+ const testRule = (tokens, index, test) => {
150
+ let tokenIndex;
151
+ if (test.position !== void 0) tokenIndex = test.position < 0 ? tokens.length + test.position : test.position;
152
+ else tokenIndex = index + (test.shift ?? 0);
153
+ const token = tokens[tokenIndex];
154
+ if (!token) return {
155
+ match: false,
156
+ position: null,
157
+ range: null
158
+ };
159
+ let range = null;
160
+ if (test.type !== void 0 && !matches(test.type, token.type)) return {
161
+ match: false,
162
+ position: null,
163
+ range: null
164
+ };
165
+ if (test.nesting !== void 0 && !matches(test.nesting, token.nesting)) return {
166
+ match: false,
167
+ position: null,
168
+ range: null
169
+ };
170
+ if (test.tag !== void 0 && token.tag !== test.tag) return {
171
+ match: false,
172
+ position: null,
173
+ range: null
174
+ };
175
+ if (test.content !== void 0 && !matches(test.content, token.content)) return {
176
+ match: false,
177
+ position: null,
178
+ range: null
179
+ };
180
+ if (test.meta !== void 0 && !test.meta(token.meta ?? null)) return {
181
+ match: false,
182
+ position: null,
183
+ range: null
184
+ };
185
+ if (test.attrChecker) {
186
+ range = test.attrChecker(token.content, test.attrPos ?? "only");
187
+ if (!range) return {
188
+ match: false,
189
+ position: null,
190
+ range: null
191
+ };
192
+ }
193
+ if (test.children) {
194
+ const children = token.children ?? [];
195
+ for (const childTest of test.children) {
196
+ const idx = childTest.index !== void 0 ? childTest.index < 0 ? children.length + childTest.index : childTest.index : -1;
197
+ if (idx >= 0) {
198
+ const child = children[idx];
199
+ if (!child) return {
200
+ match: false,
201
+ position: null,
202
+ range: null
203
+ };
204
+ const result = testChild(child, childTest);
205
+ if (!result.match) return {
206
+ match: false,
207
+ position: null,
208
+ range: null
209
+ };
210
+ if (result.range) range = result.range;
211
+ } else if (!children.some((child) => {
212
+ const result = testChild(child, childTest);
213
+ if (result.range) range = result.range;
214
+ return result.match;
215
+ })) return {
216
+ match: false,
217
+ position: null,
218
+ range: null
219
+ };
220
+ }
221
+ }
222
+ return {
223
+ match: true,
224
+ position: tokenIndex,
225
+ range
226
+ };
227
+ };
228
+ //#endregion
229
+ //#region src/helper/getMatchingOpeningToken.ts
230
+ /**
231
+ * Given a closing or self-closing token at `index`, returns its matching
232
+ * opening token by walking backwards to find the token of the same type at
233
+ * the same nesting level. Returns the token itself for nesting === 0.
234
+ */
235
+ const getMatchingOpeningToken = (tokens, index) => {
236
+ const token = tokens[index];
237
+ if (!token) return null;
238
+ if (token.type === "softbreak") return null;
239
+ if (token.nesting === 0) return token;
240
+ const target = token.type.replace(/_close$/, "_open");
241
+ let depth = 0;
242
+ for (let i = index; i >= 0; i--) {
243
+ const t = tokens[i];
244
+ if (t.type === token.type) depth++;
245
+ else if (t.type === target) {
246
+ depth--;
247
+ if (depth === 0) return t;
248
+ }
249
+ }
250
+ return null;
251
+ };
252
+ //#endregion
253
+ //#region src/rules/block.ts
254
+ const SKIP_TYPES = new Set([
255
+ "code_inline",
256
+ "math_inline",
257
+ "fence",
258
+ "code_block"
259
+ ]);
260
+ const createBlockRule = (options) => {
261
+ const check = getDelimiterChecker(options.left, options.right);
262
+ return {
263
+ name: "block attributes",
264
+ tests: [{
265
+ shift: 0,
266
+ type: "inline",
267
+ children: [{
268
+ index: -1,
269
+ type: (t) => !SKIP_TYPES.has(t),
270
+ content: (content) => check(content.trim(), "end") !== null
271
+ }]
272
+ }],
273
+ transform(tokens, index) {
274
+ const children = tokens[index].children;
275
+ const lastChild = children[children.length - 1];
276
+ const content = lastChild.content.trimEnd();
277
+ const range = check(content, "end");
278
+ let openToken = null;
279
+ let closeIdx = index + 1;
280
+ while (closeIdx < tokens.length && tokens[closeIdx].nesting !== -1) closeIdx++;
281
+ if (closeIdx < tokens.length) openToken = getMatchingOpeningToken(tokens, closeIdx);
282
+ if (!openToken) return;
283
+ if (SKIP_TYPES.has(openToken.type)) return;
284
+ addAttrs(openToken, content, range, options.allowed);
285
+ const attrStart = content.lastIndexOf(options.left, range[0] - 1);
286
+ lastChild.content = content.slice(0, attrStart).trimEnd();
287
+ if (lastChild.content === "" && children.length > 1) children.pop();
288
+ }
289
+ };
290
+ };
291
+ //#endregion
292
+ //#region src/rules/inline.ts
293
+ const createInlineRules = (options) => {
294
+ const check = getDelimiterChecker(options.left, options.right);
295
+ return [{
296
+ name: "inline nesting self-close",
297
+ tests: [{
298
+ shift: 0,
299
+ type: "inline"
300
+ }],
301
+ transform(tokens, index) {
302
+ const children = tokens[index].children;
303
+ let modified = false;
304
+ for (let i = children.length - 1; i >= 0; i--) {
305
+ const child = children[i];
306
+ if (child.type !== "text") continue;
307
+ const range = check(child.content, "only");
308
+ if (!range) continue;
309
+ const prev = children[i - 1];
310
+ if (prev?.nesting !== 0 || prev.type === "softbreak") continue;
311
+ addAttrs(prev, child.content, range, options.allowed);
312
+ children.splice(i, 1);
313
+ modified = true;
314
+ }
315
+ return modified;
316
+ }
317
+ }, {
318
+ name: "inline attributes",
319
+ tests: [{
320
+ shift: 0,
321
+ type: "inline"
322
+ }],
323
+ transform(tokens, index) {
324
+ const children = tokens[index].children;
325
+ let modified = false;
326
+ for (let i = children.length - 1; i >= 0; i--) {
327
+ const child = children[i];
328
+ if (child.type !== "text") continue;
329
+ const range = check(child.content, "only");
330
+ if (!range) continue;
331
+ const prev = children[i - 1];
332
+ if (!prev || prev.nesting !== -1) continue;
333
+ const openType = prev.type.replace(/_close$/, "_open");
334
+ let openIdx = -1;
335
+ for (let j = i - 2; j >= 0; j--) if (children[j].type === openType && children[j].level === prev.level) {
336
+ openIdx = j;
337
+ break;
338
+ }
339
+ if (openIdx === -1) continue;
340
+ addAttrs(children[openIdx], child.content, range, options.allowed);
341
+ children.splice(i, 1);
342
+ modified = true;
343
+ }
344
+ return modified;
345
+ }
346
+ }];
347
+ };
348
+ //#endregion
349
+ //#region src/rules/fence.ts
350
+ const createFenceRule = (options) => {
351
+ const check = getDelimiterChecker(options.left, options.right);
352
+ return {
353
+ name: "fence attributes",
354
+ tests: [{
355
+ shift: 0,
356
+ type: "fence"
357
+ }],
358
+ transform(tokens, index) {
359
+ const token = tokens[index];
360
+ const info = token.info.trim();
361
+ const range = check(info, "end") ?? check(info, "only");
362
+ if (!range) return;
363
+ addAttrs(token, info, range, options.allowed);
364
+ token.info = info.slice(0, info.lastIndexOf(options.left, range[0] - 1)).trimEnd();
365
+ }
366
+ };
367
+ };
368
+ //#endregion
369
+ //#region src/rules/heading.ts
370
+ const createHeadingRule = (options) => {
371
+ const check = getDelimiterChecker(options.left, options.right);
372
+ return {
373
+ name: "heading attributes",
374
+ tests: [{
375
+ shift: 0,
376
+ type: "heading_open"
377
+ }, {
378
+ shift: 1,
379
+ type: "inline",
380
+ children: [{
381
+ index: -1,
382
+ type: (t) => t !== "code_inline" && t !== "math_inline",
383
+ content: (content) => check(content.trim(), "end") !== null
384
+ }]
385
+ }],
386
+ transform(tokens, index) {
387
+ const openToken = tokens[index];
388
+ const children = tokens[index + 1].children;
389
+ const lastChild = children[children.length - 1];
390
+ const trimmed = lastChild.content.trimEnd();
391
+ const range = check(trimmed, "end");
392
+ addAttrs(openToken, trimmed, range, options.allowed);
393
+ lastChild.content = trimmed.slice(0, trimmed.lastIndexOf(options.left, range[0] - 1)).trimEnd();
394
+ if (lastChild.content === "" && children.length > 1) children.pop();
395
+ }
396
+ };
397
+ };
398
+ //#endregion
399
+ //#region src/rules/hr.ts
400
+ const createHrRule = (options) => {
401
+ const check = getDelimiterChecker(options.left, options.right);
402
+ return {
403
+ name: "hr attributes",
404
+ tests: [{
405
+ shift: 0,
406
+ type: "inline",
407
+ children: [{
408
+ index: 0,
409
+ type: "text",
410
+ content: (content) => check(content.trim(), "only") !== null
411
+ }]
412
+ }, {
413
+ shift: -2,
414
+ type: "hr"
415
+ }],
416
+ transform(tokens, index) {
417
+ const content = tokens[index].children[0].content.trim();
418
+ const range = check(content, "only");
419
+ const hr = tokens[index - 2];
420
+ addAttrs(hr, content, range, options.allowed);
421
+ tokens.splice(index - 1, 3);
422
+ }
423
+ };
424
+ };
425
+ //#endregion
426
+ //#region src/rules/list.ts
427
+ const createListRules = (options) => {
428
+ const check = getDelimiterChecker(options.left, options.right);
429
+ return [
430
+ {
431
+ name: "list item end",
432
+ tests: [
433
+ {
434
+ shift: 0,
435
+ type: "inline",
436
+ children: [{
437
+ index: -1,
438
+ type: (t) => t !== "code_inline",
439
+ content: (content) => check(content.trim(), "end") !== null
440
+ }]
441
+ },
442
+ {
443
+ shift: -1,
444
+ type: "paragraph_open"
445
+ },
446
+ {
447
+ shift: -2,
448
+ type: "list_item_open"
449
+ }
450
+ ],
451
+ transform(tokens, index) {
452
+ const children = tokens[index].children;
453
+ const lastChild = children[children.length - 1];
454
+ const content = lastChild.content.trimEnd();
455
+ const range = check(content, "end");
456
+ const listItemOpen = tokens[index - 2];
457
+ addAttrs(listItemOpen, content, range, options.allowed);
458
+ const attrStart = content.lastIndexOf(options.left, range[0] - 1);
459
+ lastChild.content = content.slice(0, attrStart).trimEnd();
460
+ if (lastChild.content === "" && children.length > 1) children.pop();
461
+ }
462
+ },
463
+ {
464
+ name: "custom item end",
465
+ tests: [
466
+ {
467
+ shift: 0,
468
+ type: "inline",
469
+ children: [{
470
+ index: -1,
471
+ type: (t) => t !== "code_inline",
472
+ content: (content) => check(content.trim(), "end") !== null
473
+ }]
474
+ },
475
+ {
476
+ shift: -1,
477
+ meta: (m) => m?.attrsItemTitle === true
478
+ },
479
+ {
480
+ shift: -2,
481
+ meta: (m) => m?.attrsRole === "containerItem"
482
+ }
483
+ ],
484
+ transform(tokens, index) {
485
+ const children = tokens[index].children;
486
+ const lastChild = children[children.length - 1];
487
+ const content = lastChild.content.trimEnd();
488
+ const range = check(content, "end");
489
+ const itemOpen = tokens[index - 2];
490
+ addAttrs(itemOpen, content, range, options.allowed);
491
+ const attrStart = content.lastIndexOf(options.left, range[0] - 1);
492
+ lastChild.content = content.slice(0, attrStart).trimEnd();
493
+ if (lastChild.content === "" && children.length > 1) children.pop();
494
+ }
495
+ },
496
+ {
497
+ name: "custom item end direct",
498
+ tests: [{
499
+ shift: 0,
500
+ type: "inline",
501
+ children: [{
502
+ index: -1,
503
+ type: (t) => t !== "code_inline",
504
+ content: (content) => check(content.trim(), "end") !== null
505
+ }]
506
+ }, {
507
+ shift: -1,
508
+ meta: (m) => m?.attrsItemTitle === true && m.attrsRole === "containerItem"
509
+ }],
510
+ transform(tokens, index) {
511
+ const children = tokens[index].children;
512
+ const lastChild = children[children.length - 1];
513
+ const content = lastChild.content.trimEnd();
514
+ const range = check(content, "end");
515
+ const itemOpen = tokens[index - 1];
516
+ addAttrs(itemOpen, content, range, options.allowed);
517
+ const attrStart = content.lastIndexOf(options.left, range[0] - 1);
518
+ lastChild.content = content.slice(0, attrStart).trimEnd();
519
+ if (lastChild.content === "" && children.length > 1) children.pop();
520
+ }
521
+ },
522
+ {
523
+ name: "list attributes",
524
+ tests: [
525
+ {
526
+ shift: 0,
527
+ type: "inline",
528
+ children: [{
529
+ index: 0,
530
+ type: "text",
531
+ content: (content) => check(content.trim(), "only") !== null
532
+ }]
533
+ },
534
+ {
535
+ shift: -1,
536
+ type: "paragraph_open"
537
+ },
538
+ {
539
+ shift: -2,
540
+ type: (t) => t === "bullet_list_close" || t === "ordered_list_close"
541
+ }
542
+ ],
543
+ transform(tokens, index) {
544
+ const content = tokens[index].children[0].content.trim();
545
+ const range = check(content, "only");
546
+ const closeToken = tokens[index - 2];
547
+ const openType = closeToken.type.replace("_close", "_open");
548
+ let depth = 1;
549
+ let listOpenIdx = -1;
550
+ for (let i = index - 3; i >= 0; i--) {
551
+ const t = tokens[i];
552
+ if (t.type === closeToken.type) depth++;
553
+ else if (t.type === openType) {
554
+ depth--;
555
+ if (depth === 0) {
556
+ listOpenIdx = i;
557
+ break;
558
+ }
559
+ }
560
+ }
561
+ if (listOpenIdx === -1) return;
562
+ addAttrs(tokens[listOpenIdx], content, range, options.allowed);
563
+ tokens.splice(index - 1, 3);
564
+ }
565
+ },
566
+ {
567
+ name: "custom list attributes",
568
+ tests: [
569
+ {
570
+ shift: 0,
571
+ type: "inline",
572
+ children: [{
573
+ index: 0,
574
+ type: "text",
575
+ content: (content) => check(content.trim(), "only") !== null
576
+ }]
577
+ },
578
+ {
579
+ shift: -1,
580
+ type: "paragraph_open"
581
+ },
582
+ {
583
+ shift: -2,
584
+ type: (t) => t.endsWith("_close"),
585
+ meta: (m) => m?.attrsRole === "container"
586
+ }
587
+ ],
588
+ transform(tokens, index) {
589
+ const content = tokens[index].children[0].content.trim();
590
+ const range = check(content, "only");
591
+ const closeToken = tokens[index - 2];
592
+ const openType = closeToken.type.replace("_close", "_open");
593
+ let depth = 1;
594
+ let listOpenIdx = -1;
595
+ for (let i = index - 3; i >= 0; i--) {
596
+ const t = tokens[i];
597
+ if (t.type === closeToken.type) depth++;
598
+ else if (t.type === openType) {
599
+ depth--;
600
+ if (depth === 0) {
601
+ listOpenIdx = i;
602
+ break;
603
+ }
604
+ }
605
+ }
606
+ if (listOpenIdx === -1) return;
607
+ addAttrs(tokens[listOpenIdx], content, range, options.allowed);
608
+ tokens.splice(index - 1, 3);
609
+ }
610
+ }
611
+ ];
612
+ };
613
+ //#endregion
614
+ //#region src/rules/softbreak.ts
615
+ const createSoftbreakRule = (options) => {
616
+ const check = getDelimiterChecker(options.left, options.right);
617
+ return {
618
+ name: "softbreak attributes",
619
+ tests: [{
620
+ shift: 0,
621
+ type: "inline",
622
+ children: [{
623
+ index: -2,
624
+ type: "softbreak"
625
+ }, {
626
+ index: -1,
627
+ type: "text",
628
+ content: (content) => check(content.trim(), "only") !== null
629
+ }]
630
+ }],
631
+ transform(tokens, index) {
632
+ const children = tokens[index].children;
633
+ const content = children[children.length - 1].content.trim();
634
+ const range = check(content, "only");
635
+ let target = tokens[index];
636
+ for (let i = index - 1; i >= 0; i--) {
637
+ const t = tokens[i];
638
+ if (t.nesting === 1) {
639
+ target = t;
640
+ break;
641
+ }
642
+ }
643
+ addAttrs(target, content, range, options.allowed);
644
+ children.splice(children.length - 2, 2);
645
+ }
646
+ };
647
+ };
648
+ //#endregion
649
+ //#region src/rules/table.ts
650
+ const createTableRules = (options) => {
651
+ const check = getDelimiterChecker(options.left, options.right);
652
+ return [
653
+ {
654
+ name: "table attributes",
655
+ tests: [
656
+ {
657
+ shift: 0,
658
+ type: "inline",
659
+ children: [{
660
+ index: 0,
661
+ type: "text",
662
+ content: (content) => check(content.trim(), "only") !== null
663
+ }]
664
+ },
665
+ {
666
+ shift: -1,
667
+ type: "paragraph_open"
668
+ },
669
+ {
670
+ shift: -2,
671
+ type: "table_close"
672
+ }
673
+ ],
674
+ transform(tokens, index) {
675
+ const content = tokens[index].children[0].content.trim();
676
+ const range = check(content, "only");
677
+ let tableOpenIdx = -1;
678
+ let depth = 1;
679
+ for (let i = index - 3; i >= 0; i--) {
680
+ const t = tokens[i];
681
+ if (t.type === "table_close") depth++;
682
+ else if (t.type === "table_open") {
683
+ depth--;
684
+ if (depth === 0) {
685
+ tableOpenIdx = i;
686
+ break;
687
+ }
688
+ }
689
+ }
690
+ if (tableOpenIdx === -1) return;
691
+ addAttrs(tokens[tableOpenIdx], content, range, options.allowed);
692
+ tokens.splice(index - 1, 3);
693
+ }
694
+ },
695
+ {
696
+ name: "table cell attributes",
697
+ tests: [{
698
+ shift: 0,
699
+ type: "inline",
700
+ children: [{
701
+ index: -1,
702
+ type: "text",
703
+ content: (content) => check(content.trim(), "end") !== null
704
+ }]
705
+ }, {
706
+ shift: -1,
707
+ type: (t) => t === "td_open" || t === "th_open"
708
+ }],
709
+ transform(tokens, index) {
710
+ const children = tokens[index].children;
711
+ const lastChild = children[children.length - 1];
712
+ const content = lastChild.content.trimEnd();
713
+ const range = check(content, "end");
714
+ const cellOpen = tokens[index - 1];
715
+ addAttrs(cellOpen, content, range, options.allowed);
716
+ const attrStart = content.lastIndexOf(options.left, range[0] - 1);
717
+ lastChild.content = content.slice(0, attrStart).trimEnd();
718
+ if (lastChild.content === "" && children.length > 1) children.pop();
719
+ }
720
+ },
721
+ {
722
+ name: "table column tracker",
723
+ tests: [{
724
+ shift: 0,
725
+ type: "tr_open"
726
+ }],
727
+ transform(tokens, index) {
728
+ let cols = 0;
729
+ for (let i = index + 1; i < tokens.length; i++) {
730
+ const t = tokens[i];
731
+ if (t.type === "tr_close") break;
732
+ if (t.type === "th_open") cols++;
733
+ }
734
+ if (cols === 0) return;
735
+ for (let i = index - 1; i >= 0; i--) {
736
+ const t = tokens[i];
737
+ if (t.type === "thead_open") {
738
+ t.meta = t.meta ?? {};
739
+ t.meta.cols = cols;
740
+ break;
741
+ }
742
+ if (t.type === "table_open") break;
743
+ }
744
+ }
745
+ },
746
+ {
747
+ name: "table span calculator",
748
+ tests: [{
749
+ shift: 0,
750
+ type: "tbody_open"
751
+ }],
752
+ transform(tokens, index) {
753
+ let cols = 0;
754
+ for (let i = index - 1; i >= 0; i--) {
755
+ const t = tokens[i];
756
+ if (t.type === "thead_open") {
757
+ cols = t.meta?.cols ?? 0;
758
+ break;
759
+ }
760
+ if (t.type === "table_open") break;
761
+ }
762
+ if (cols === 0) return;
763
+ let tbodyClose = index + 1;
764
+ let depth = 1;
765
+ while (tbodyClose < tokens.length && depth > 0) {
766
+ const t = tokens[tbodyClose];
767
+ if (t.type === "tbody_open") depth++;
768
+ else if (t.type === "tbody_close") depth--;
769
+ tbodyClose++;
770
+ }
771
+ tbodyClose--;
772
+ const occupied = Array.from({ length: cols }, () => 0);
773
+ let i = index + 1;
774
+ while (i < tbodyClose) {
775
+ if (tokens[i].type === "tr_open") {
776
+ for (let c = 0; c < cols; c++) if (occupied[c] > 0) occupied[c]--;
777
+ const tdIndices = [];
778
+ for (let j = i + 1; j < tbodyClose; j++) {
779
+ if (tokens[j].type === "tr_close") break;
780
+ if (tokens[j].type === "td_open") tdIndices.push(j);
781
+ }
782
+ let col = 0;
783
+ let tdIdx = 0;
784
+ while (col < cols && tdIdx < tdIndices.length) {
785
+ while (col < cols && occupied[col] > 0) col++;
786
+ if (col >= cols) break;
787
+ const tdOpen = tokens[tdIndices[tdIdx]];
788
+ const rowspanAttr = tdOpen.attrGet("rowspan");
789
+ const colspanAttr = tdOpen.attrGet("colspan");
790
+ const rowspan = rowspanAttr ? parseInt(rowspanAttr, 10) : 1;
791
+ let colspan = colspanAttr ? parseInt(colspanAttr, 10) : 1;
792
+ let freeCols = 0;
793
+ for (let c = col; c < cols && occupied[c] === 0; c++) freeCols++;
794
+ if (colspan > freeCols) {
795
+ colspan = freeCols;
796
+ if (colspan === 1) {}
797
+ tdOpen.attrSet("colspan", String(colspan));
798
+ }
799
+ for (let c = col; c < col + colspan && c < cols; c++) if (rowspan > 1) occupied[c] = rowspan - 1;
800
+ col += colspan;
801
+ tdIdx++;
802
+ }
803
+ while (tdIdx < tdIndices.length) {
804
+ const phantomStart = tdIndices[tdIdx];
805
+ let phantomEnd = phantomStart + 1;
806
+ while (phantomEnd < tokens.length && tokens[phantomEnd].type !== "td_close") phantomEnd++;
807
+ tokens.splice(phantomStart, phantomEnd - phantomStart + 1);
808
+ tbodyClose -= phantomEnd - phantomStart + 1;
809
+ }
810
+ }
811
+ i++;
812
+ }
813
+ }
814
+ }
815
+ ];
816
+ };
817
+ //#endregion
818
+ //#region src/rules/rules.ts
819
+ const ALL_RULE_SET = new Set([
820
+ "fence",
821
+ "inline",
822
+ "table",
823
+ "list",
824
+ "heading",
825
+ "hr",
826
+ "softbreak",
827
+ "block"
828
+ ]);
829
+ const createRules = (options) => {
830
+ const { rule = "all" } = options;
831
+ let enabled;
832
+ if (rule === "all" || rule === true) enabled = ALL_RULE_SET;
833
+ else if (!rule || Array.isArray(rule) && rule.length === 0) return [];
834
+ else enabled = new Set(rule.filter((r) => ALL_RULE_SET.has(r)));
835
+ const rules = [];
836
+ if (enabled.has("fence")) rules.push(createFenceRule(options));
837
+ if (enabled.has("inline")) rules.push(...createInlineRules(options));
838
+ if (enabled.has("table")) rules.push(...createTableRules(options));
839
+ if (enabled.has("list")) rules.push(...createListRules(options));
840
+ if (enabled.has("heading")) rules.push(createHeadingRule(options));
841
+ if (enabled.has("hr")) rules.push(createHrRule(options));
842
+ if (enabled.has("softbreak")) rules.push(createSoftbreakRule(options));
843
+ if (enabled.has("block")) rules.push(createBlockRule(options));
844
+ return rules;
845
+ };
846
+ //#endregion
847
+ //#region src/plugin.ts
848
+ const attrs = (md, { left = "{", right = "}", allowed = [], rule = "all" } = {}) => {
849
+ const rules = createRules({
850
+ left,
851
+ right,
852
+ allowed,
853
+ rule
854
+ });
855
+ md.core.ruler.before("linkify", "attrs", (state) => {
856
+ const { tokens } = state;
857
+ for (let index = 0; index < tokens.length; index++) for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex++) {
858
+ const pattern = rules[ruleIndex];
859
+ let position = null;
860
+ let range = null;
861
+ if (pattern.tests.every((test) => {
862
+ const result = testRule(tokens, index, test);
863
+ if (result.position != null) position = result.position;
864
+ if (result.range) range = result.range;
865
+ return result.match;
866
+ })) {
867
+ if (pattern.transform(tokens, index, position, range) !== false && (pattern.name === "inline attributes" || pattern.name === "inline nesting self-close")) ruleIndex--;
868
+ }
869
+ }
870
+ });
871
+ };
872
+ //#endregion
873
+ export { attrs };
874
+
875
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/helper/getAttrs.ts","../src/helper/addAttrs.ts","../src/helper/getDelimiterChecker.ts","../src/helper/testRule.ts","../src/helper/getMatchingOpeningToken.ts","../src/rules/block.ts","../src/rules/inline.ts","../src/rules/fence.ts","../src/rules/heading.ts","../src/rules/hr.ts","../src/rules/list.ts","../src/rules/softbreak.ts","../src/rules/table.ts","../src/rules/rules.ts","../src/plugin.ts"],"sourcesContent":["import type { Attr, DelimiterRange } from \"../types.js\";\n\nconst CLASS_MARKER = 46; // '.'\nconst ID_MARKER = 35; // '#'\nconst PAIR_SEPARATOR = 32; // ' '\nconst KEY_SEPARATOR = 61; // '='\nconst QUOTE_MARKER = 34; // '\"'\n\nconst DISALLOWED_KEY_CHARS = new Set([9, 10, 12, 32, 47, 62, 34, 39, 61]);\n\nexport const getAttrs = (\n str: string,\n range: DelimiterRange,\n allowed: Array<string | RegExp>\n): Attr[] => {\n let key = \"\";\n let value = \"\";\n let parsingKey = true;\n let valueInsideQuotes = false;\n const attrs: Attr[] = [];\n\n for (let i = range[0]; i < range[1]; i++) {\n const code = str.charCodeAt(i);\n\n if (code === KEY_SEPARATOR && parsingKey) {\n parsingKey = false;\n continue;\n }\n\n if (code === CLASS_MARKER && key === \"\") {\n key =\n str.charCodeAt(i + 1) === CLASS_MARKER ? (i++, \"css-module\") : \"class\";\n parsingKey = false;\n continue;\n }\n\n if (code === ID_MARKER && key === \"\") {\n key = \"id\";\n parsingKey = false;\n continue;\n }\n\n if (code === QUOTE_MARKER) {\n if (value === \"\" && !valueInsideQuotes) {\n valueInsideQuotes = true;\n } else if (valueInsideQuotes) {\n valueInsideQuotes = false;\n }\n continue;\n }\n\n if (code === PAIR_SEPARATOR && !valueInsideQuotes) {\n if (key !== \"\") {\n attrs.push([key, value]);\n key = \"\";\n value = \"\";\n parsingKey = true;\n }\n continue;\n }\n\n if (parsingKey) {\n if (!DISALLOWED_KEY_CHARS.has(code)) key += str[i];\n } else {\n value += str[i];\n }\n }\n\n if (key !== \"\") attrs.push([key, value]);\n\n if (allowed.length === 0) return attrs;\n return attrs.filter(([attr]) =>\n allowed.some((item) =>\n item instanceof RegExp ? item.test(attr) : item === attr\n )\n );\n};\n","import type Token from \"markdown-it/lib/token.mjs\";\nimport type { DelimiterRange } from \"../types.js\";\nimport { getAttrs } from \"./getAttrs.js\";\n\nexport const addAttrs = (\n token: Token,\n content: string,\n range: DelimiterRange,\n allowed: Array<string | RegExp>\n): void => {\n for (const [key, value] of getAttrs(content, range, allowed)) {\n if (key === \"class\" || key === \"css-module\") {\n token.attrJoin(key, value);\n } else {\n token.attrPush([key, value]);\n }\n }\n};\n","import type { DelimiterRange } from \"../types.js\";\n\ntype DelimiterPosition = \"start\" | \"end\" | \"only\";\n\nexport type DelimiterChecker = (\n str: string,\n pos: DelimiterPosition\n) => DelimiterRange | null;\n\n/**\n * Returns a function that checks whether `str` contains a valid attribute\n * block at the given position (\"start\", \"end\", or \"only\") and returns the\n * [start, end] range of the content inside the delimiters, or null if no\n * match. The returned range is exclusive of the delimiter characters.\n *\n * A valid block must contain at least one character (i.e. `{}` is invalid),\n * and the outer delimiters must not be repeated (i.e. `{{}}` is invalid).\n */\nexport const getDelimiterChecker = (\n left: string,\n right: string\n): DelimiterChecker => {\n const ll = left.length;\n const rl = right.length;\n const minLen = ll + 1 + rl;\n const doubleLeft = left + left;\n const doubleRight = right + right;\n\n return (str, pos): DelimiterRange | null => {\n if (str.length < minLen) return null;\n\n if (pos === \"start\" || pos === \"only\") {\n if (!str.startsWith(left)) return null;\n if (str.startsWith(doubleLeft)) return null;\n }\n\n if (pos === \"end\" || pos === \"only\") {\n if (!str.endsWith(right)) return null;\n if (str.endsWith(doubleRight)) return null;\n }\n\n if (pos === \"only\") {\n const contentEnd = str.length - rl;\n if (contentEnd <= ll) return null;\n return [ll, contentEnd];\n }\n\n if (pos === \"start\") {\n const end = str.indexOf(right, ll + 1);\n if (end === -1) return null;\n return [ll, end];\n }\n\n // pos === \"end\"\n const start = str.lastIndexOf(left, str.length - rl - 1);\n if (start === -1) return null;\n if (str.startsWith(left, start - ll)) return null;\n return [start + ll, str.length - rl];\n };\n};\n","// Token.meta is typed `any` by markdown-it; casts bridge to our richer rule-test predicates.\n/* oxlint-disable typescript/no-unsafe-type-assertion */\nimport type Token from \"markdown-it/lib/token.mjs\";\nimport type { DelimiterRange } from \"../types.js\";\nimport type { DelimiterChecker } from \"./getDelimiterChecker.js\";\n\nexport interface RuleTest {\n /** Offset from the current token index. Mutually exclusive with `position`. */\n shift?: number;\n /** Absolute token index. Mutually exclusive with `shift`. */\n position?: number;\n /** Token type must equal this string. */\n type?: string | ((t: string) => boolean);\n /** Token tag must equal this string. */\n tag?: string;\n /** Token nesting must equal this value. */\n nesting?: number | ((n: number) => boolean);\n /** Inspect token content; called with (content, token, tokens, index). */\n content?: string | ((c: string) => boolean);\n /** Inspect token.meta; predicate receives the meta object (may be null). */\n meta?: (meta: Record<string, unknown> | null) => boolean;\n /** Inspect a child token at the given index (negative = from end). */\n children?: Array<Omit<RuleTest, \"shift\" | \"position\"> & { index?: number }>;\n /** If set, use this delimiter checker to extract a range from token content. */\n attrChecker?: DelimiterChecker;\n /** Which position argument to pass to attrChecker (\"start\"|\"end\"|\"only\"). */\n attrPos?: \"start\" | \"end\" | \"only\";\n}\n\nexport interface TestResult {\n match: boolean;\n position: number | null;\n range: DelimiterRange | null;\n}\n\nconst matches = (test: unknown, actual: unknown): boolean => {\n if (typeof test === \"function\")\n return (test as (v: unknown) => boolean)(actual);\n return test === actual;\n};\n\nconst testChild = (\n child: Token,\n childTest: Omit<RuleTest, \"shift\" | \"position\">\n): { match: boolean; range: DelimiterRange | null } => {\n let range: DelimiterRange | null = null;\n\n if (childTest.type !== undefined && !matches(childTest.type, child.type)) {\n return { match: false, range: null };\n }\n if (\n childTest.nesting !== undefined &&\n !matches(childTest.nesting, child.nesting)\n ) {\n return { match: false, range: null };\n }\n if (childTest.tag !== undefined && child.tag !== childTest.tag) {\n return { match: false, range: null };\n }\n if (childTest.content !== undefined) {\n if (!matches(childTest.content, child.content))\n return { match: false, range: null };\n }\n if (childTest.attrChecker) {\n range = childTest.attrChecker(child.content, childTest.attrPos ?? \"only\");\n if (!range) return { match: false, range: null };\n }\n\n return { match: true, range };\n};\n\nexport const testRule = (\n tokens: Token[],\n index: number,\n test: RuleTest\n): TestResult => {\n let tokenIndex: number;\n if (test.position !== undefined) {\n tokenIndex =\n test.position < 0 ? tokens.length + test.position : test.position;\n } else {\n tokenIndex = index + (test.shift ?? 0);\n }\n\n const token = tokens[tokenIndex];\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n if (!token) return { match: false, position: null, range: null };\n\n let range: DelimiterRange | null = null;\n\n if (test.type !== undefined && !matches(test.type, token.type)) {\n return { match: false, position: null, range: null };\n }\n if (test.nesting !== undefined && !matches(test.nesting, token.nesting)) {\n return { match: false, position: null, range: null };\n }\n if (test.tag !== undefined && token.tag !== test.tag) {\n return { match: false, position: null, range: null };\n }\n if (test.content !== undefined && !matches(test.content, token.content)) {\n return { match: false, position: null, range: null };\n }\n if (\n test.meta !== undefined &&\n !test.meta((token.meta as Record<string, unknown> | null) ?? null)\n ) {\n return { match: false, position: null, range: null };\n }\n if (test.attrChecker) {\n range = test.attrChecker(token.content, test.attrPos ?? \"only\");\n if (!range) return { match: false, position: null, range: null };\n }\n\n if (test.children) {\n const children = token.children ?? [];\n for (const childTest of test.children) {\n const idx =\n childTest.index !== undefined\n ? childTest.index < 0\n ? children.length + childTest.index\n : childTest.index\n : -1;\n\n if (idx >= 0) {\n const child = children[idx];\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n if (!child) return { match: false, position: null, range: null };\n const result = testChild(child, childTest);\n if (!result.match) return { match: false, position: null, range: null };\n if (result.range) range = result.range;\n } else {\n // No index: find any matching child\n const found = children.some((child) => {\n const result = testChild(child, childTest);\n if (result.range) range = result.range;\n return result.match;\n });\n if (!found) return { match: false, position: null, range: null };\n }\n }\n }\n\n return { match: true, position: tokenIndex, range };\n};\n","import type Token from \"markdown-it/lib/token.mjs\";\n\n/**\n * Given a closing or self-closing token at `index`, returns its matching\n * opening token by walking backwards to find the token of the same type at\n * the same nesting level. Returns the token itself for nesting === 0.\n */\nexport const getMatchingOpeningToken = (\n tokens: Token[],\n index: number\n): Token | null => {\n const token = tokens[index];\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n if (!token) return null;\n if (token.type === \"softbreak\") return null;\n if (token.nesting === 0) return token;\n\n const target = token.type.replace(/_close$/, \"_open\");\n let depth = 0;\n\n for (let i = index; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === token.type) depth++;\n else if (t.type === target) {\n depth--;\n if (depth === 0) return t;\n }\n }\n\n return null;\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport {\n addAttrs,\n getDelimiterChecker,\n getMatchingOpeningToken\n} from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\n// Token types that should not be treated as attr targets\nconst SKIP_TYPES = new Set([\n \"code_inline\",\n \"math_inline\",\n \"fence\",\n \"code_block\"\n]);\n\nexport const createBlockRule = (options: DelimiterConfig): AttrRule => {\n const check = getDelimiterChecker(options.left, options.right);\n\n return {\n name: \"block attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: (t: string) => !SKIP_TYPES.has(t),\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n\n const content = lastChild.content.trimEnd();\n const range = check(content, \"end\")!;\n\n // Find the block-level opening token that contains this inline\n // Walk backwards: skip the paragraph_open immediately before, then\n // look for the outermost open that wraps this inline token.\n let openToken = null;\n\n // First: find the _close token that closes the block containing this inline\n // by walking forward from the inline's position\n let closeIdx = index + 1;\n while (closeIdx < tokens.length && tokens[closeIdx].nesting !== -1) {\n closeIdx++;\n }\n\n if (closeIdx < tokens.length) {\n openToken = getMatchingOpeningToken(tokens, closeIdx);\n }\n\n if (!openToken) return;\n if (SKIP_TYPES.has(openToken.type)) return;\n\n addAttrs(openToken, content, range, options.allowed);\n\n // Strip the attr block from the last child's content\n const attrStart = content.lastIndexOf(options.left, range[0] - 1);\n lastChild.content = content.slice(0, attrStart).trimEnd();\n\n // Drop the child if now empty\n if (lastChild.content === \"\" && children.length > 1) {\n children.pop();\n }\n }\n };\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createInlineRules = (options: DelimiterConfig): AttrRule[] => {\n const check = getDelimiterChecker(options.left, options.right);\n\n // Handles self-closing inline elements: `code`, images (nesting === 0)\n const selfClose: AttrRule = {\n name: \"inline nesting self-close\",\n tests: [{ shift: 0, type: \"inline\" }],\n transform(tokens, index) {\n const token = tokens[index];\n const children = token.children!;\n let modified = false;\n\n for (let i = children.length - 1; i >= 0; i--) {\n const child = children[i];\n if (child.type !== \"text\") continue;\n\n const range = check(child.content, \"only\");\n if (!range) continue;\n\n // The preceding sibling must be nesting === 0 but not a softbreak\n const prev = children[i - 1];\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n if (prev?.nesting !== 0 || prev.type === \"softbreak\") continue;\n\n addAttrs(prev, child.content, range, options.allowed);\n children.splice(i, 1);\n modified = true;\n }\n\n return modified;\n }\n };\n\n // Handles closing inline elements: em, strong, s, etc. (nesting === -1)\n const closing: AttrRule = {\n name: \"inline attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\"\n }\n ],\n transform(tokens, index) {\n const token = tokens[index];\n const children = token.children!;\n let modified = false;\n\n for (let i = children.length - 1; i >= 0; i--) {\n const child = children[i];\n if (child.type !== \"text\") continue;\n\n const range = check(child.content, \"only\");\n if (!range) continue;\n\n // The preceding sibling must be a closing tag (nesting === -1)\n const prev = children[i - 1];\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n if (!prev || prev.nesting !== -1) continue;\n\n // Find the matching opening token among children\n const openType = prev.type.replace(/_close$/, \"_open\");\n let openIdx = -1;\n for (let j = i - 2; j >= 0; j--) {\n if (\n children[j].type === openType &&\n children[j].level === prev.level\n ) {\n openIdx = j;\n break;\n }\n }\n if (openIdx === -1) continue;\n\n addAttrs(children[openIdx], child.content, range, options.allowed);\n children.splice(i, 1);\n modified = true;\n }\n\n return modified;\n }\n };\n\n return [selfClose, closing];\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createFenceRule = (options: DelimiterConfig): AttrRule => {\n const check = getDelimiterChecker(options.left, options.right);\n\n return {\n name: \"fence attributes\",\n tests: [\n {\n shift: 0,\n type: \"fence\"\n }\n ],\n transform(tokens, index) {\n const token = tokens[index];\n const info = token.info.trim();\n const range = check(info, \"end\") ?? check(info, \"only\");\n if (!range) return;\n addAttrs(token, info, range, options.allowed);\n // Strip the attr block from the info string, preserving the language tag\n token.info = info\n .slice(0, info.lastIndexOf(options.left, range[0] - 1))\n .trimEnd();\n }\n };\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createHeadingRule = (options: DelimiterConfig): AttrRule => {\n const check = getDelimiterChecker(options.left, options.right);\n\n return {\n name: \"heading attributes\",\n tests: [\n {\n shift: 0,\n type: \"heading_open\"\n },\n {\n shift: 1,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: (t: string) => t !== \"code_inline\" && t !== \"math_inline\",\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n }\n ],\n transform(tokens, index) {\n const openToken = tokens[index];\n const inline = tokens[index + 1];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n\n const content = lastChild.content;\n const trimmed = content.trimEnd();\n const range = check(trimmed, \"end\")!;\n\n addAttrs(openToken, trimmed, range, options.allowed);\n\n // Strip the attr block from the last child content\n const before = trimmed.slice(\n 0,\n trimmed.lastIndexOf(options.left, range[0] - 1)\n );\n lastChild.content = before.trimEnd();\n\n // Drop the child entirely if it's now empty\n if (lastChild.content === \"\" && children.length > 1) {\n children.pop();\n }\n }\n };\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createHrRule = (options: DelimiterConfig): AttrRule => {\n const check = getDelimiterChecker(options.left, options.right);\n\n return {\n name: \"hr attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: 0,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"only\") !== null\n }\n ]\n },\n {\n shift: -2,\n type: \"hr\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const textChild = inline.children![0];\n const content = textChild.content.trim();\n const range = check(content, \"only\")!;\n\n const hr = tokens[index - 2];\n addAttrs(hr, content, range, options.allowed);\n\n // Remove paragraph_open + inline + paragraph_close\n tokens.splice(index - 1, 3);\n }\n };\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createListRules = (options: DelimiterConfig): AttrRule[] => {\n const check = getDelimiterChecker(options.left, options.right);\n\n // Rule 1: attrs at end of a list item inline \"- item{.red}\"\n // Structure: list_item_open(-2) paragraph_open(-1) inline(0)\n const listItemEnd: AttrRule = {\n name: \"list item end\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: (t: string) => t !== \"code_inline\",\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n },\n {\n shift: -1,\n type: \"paragraph_open\"\n },\n {\n shift: -2,\n type: \"list_item_open\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n const content = lastChild.content.trimEnd();\n const range = check(content, \"end\")!;\n\n const listItemOpen = tokens[index - 2];\n addAttrs(listItemOpen, content, range, options.allowed);\n\n const attrStart = content.lastIndexOf(options.left, range[0] - 1);\n lastChild.content = content.slice(0, attrStart).trimEnd();\n if (lastChild.content === \"\" && children.length > 1) children.pop();\n }\n };\n\n // Rule 1b (generic): attrs at end of a custom item title inline\n // Matches when the open token two before the inline has meta.attrsRole === \"containerItem\"\n // and the token one before has meta.attrsItemTitle === true.\n // Third-party plugins opt in by setting these meta fields on their tokens.\n const customItemEnd: AttrRule = {\n name: \"custom item end\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: (t: string) => t !== \"code_inline\",\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n },\n {\n shift: -1,\n meta: (m) => m?.attrsItemTitle === true\n },\n {\n shift: -2,\n meta: (m) => m?.attrsRole === \"containerItem\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n const content = lastChild.content.trimEnd();\n const range = check(content, \"end\")!;\n\n const itemOpen = tokens[index - 2];\n addAttrs(itemOpen, content, range, options.allowed);\n\n const attrStart = content.lastIndexOf(options.left, range[0] - 1);\n lastChild.content = content.slice(0, attrStart).trimEnd();\n if (lastChild.content === \"\" && children.length > 1) children.pop();\n }\n };\n\n // Rule 1c (direct): attrs at end of a custom item inline where the open token\n // itself carries both attrsRole === \"containerItem\" AND attrsItemTitle === true.\n // Matches dt_open → inline (no intermediate title token).\n const customItemEndDirect: AttrRule = {\n name: \"custom item end direct\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: (t: string) => t !== \"code_inline\",\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n },\n {\n shift: -1,\n meta: (m: Record<string, unknown> | null) =>\n m?.attrsItemTitle === true && m.attrsRole === \"containerItem\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n const content = lastChild.content.trimEnd();\n const range = check(content, \"end\")!;\n\n const itemOpen = tokens[index - 1];\n addAttrs(itemOpen, content, range, options.allowed);\n\n const attrStart = content.lastIndexOf(options.left, range[0] - 1);\n lastChild.content = content.slice(0, attrStart).trimEnd();\n if (lastChild.content === \"\" && children.length > 1) children.pop();\n }\n };\n\n // Rule 2: attrs on own paragraph after bullet/ordered list close → apply to list\n // Structure: bullet_list_close(-2) paragraph_open(-1) inline(0) paragraph_close(+1)\n const listAttr: AttrRule = {\n name: \"list attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: 0,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"only\") !== null\n }\n ]\n },\n {\n shift: -1,\n type: \"paragraph_open\"\n },\n {\n shift: -2,\n type: (t: string) =>\n t === \"bullet_list_close\" || t === \"ordered_list_close\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const textChild = children[0];\n const content = textChild.content.trim();\n const range = check(content, \"only\")!;\n\n // Find the matching list_open for the close token at index-2\n const closeToken = tokens[index - 2];\n const openType = closeToken.type.replace(\"_close\", \"_open\");\n\n let depth = 1;\n let listOpenIdx = -1;\n for (let i = index - 3; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === closeToken.type) depth++;\n else if (t.type === openType) {\n depth--;\n if (depth === 0) {\n listOpenIdx = i;\n break;\n }\n }\n }\n\n if (listOpenIdx === -1) return;\n addAttrs(tokens[listOpenIdx], content, range, options.allowed);\n\n // Remove paragraph_open + inline + paragraph_close\n tokens.splice(index - 1, 3);\n }\n };\n\n // Rule 2b (generic): attrs on own paragraph after a custom container close → apply to container open\n // Matches when the close token two before the paragraph has meta.attrsRole === \"container\".\n // Third-party plugins opt in by setting meta.attrsRole = \"container\" on their open/close tokens.\n const customListAttr: AttrRule = {\n name: \"custom list attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: 0,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"only\") !== null\n }\n ]\n },\n {\n shift: -1,\n type: \"paragraph_open\"\n },\n {\n shift: -2,\n type: (t: string) => t.endsWith(\"_close\"),\n meta: (m) => m?.attrsRole === \"container\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const textChild = children[0];\n const content = textChild.content.trim();\n const range = check(content, \"only\")!;\n\n // Walk back to find the matching open token (same type with _open suffix)\n const closeToken = tokens[index - 2];\n const openType = closeToken.type.replace(\"_close\", \"_open\");\n\n let depth = 1;\n let listOpenIdx = -1;\n for (let i = index - 3; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === closeToken.type) depth++;\n else if (t.type === openType) {\n depth--;\n if (depth === 0) {\n listOpenIdx = i;\n break;\n }\n }\n }\n\n if (listOpenIdx === -1) return;\n addAttrs(tokens[listOpenIdx], content, range, options.allowed);\n\n // Remove paragraph_open + inline + paragraph_close\n tokens.splice(index - 1, 3);\n }\n };\n\n return [\n listItemEnd,\n customItemEnd,\n customItemEndDirect,\n listAttr,\n customListAttr\n ];\n};\n","import type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createSoftbreakRule = (options: DelimiterConfig): AttrRule => {\n const check = getDelimiterChecker(options.left, options.right);\n\n return {\n name: \"softbreak attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n // second-to-last child must be a softbreak\n index: -2,\n type: \"softbreak\"\n },\n {\n // last child must be a text node containing only an attr block\n index: -1,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"only\") !== null\n }\n ]\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const attrChild = children[children.length - 1];\n\n const content = attrChild.content.trim();\n const range = check(content, \"only\")!;\n\n // Walk backward to find the nearest nesting===1 opener that contains this inline\n let target = tokens[index];\n for (let i = index - 1; i >= 0; i--) {\n const t = tokens[i];\n if (t.nesting === 1) {\n target = t;\n break;\n }\n }\n\n addAttrs(target, content, range, options.allowed);\n\n // Remove the attr text child and the preceding softbreak\n children.splice(children.length - 2, 2);\n }\n };\n};\n","// TableMeta lives on Token.meta which markdown-it types as any — casts narrow to the rule-specific shape.\n/* oxlint-disable typescript/no-unsafe-type-assertion */\nimport type { DelimiterConfig } from \"../types.js\";\nimport { addAttrs, getDelimiterChecker } from \"../helper/index.js\";\nimport type { AttrRule } from \"./types.js\";\n\nexport const createTableRules = (options: DelimiterConfig): AttrRule[] => {\n const check = getDelimiterChecker(options.left, options.right);\n\n // Rule 1: table-level attrs — standalone paragraph after the table \"\\n{.class}\"\n const tableAttrs: AttrRule = {\n name: \"table attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: 0,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"only\") !== null\n }\n ]\n },\n {\n shift: -1,\n type: \"paragraph_open\"\n },\n {\n shift: -2,\n type: \"table_close\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const content = inline.children![0].content.trim();\n const range = check(content, \"only\")!;\n\n // Find the matching table_open (start from table_close at index-2)\n let tableOpenIdx = -1;\n let depth = 1;\n for (let i = index - 3; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === \"table_close\") depth++;\n else if (t.type === \"table_open\") {\n depth--;\n if (depth === 0) {\n tableOpenIdx = i;\n break;\n }\n }\n }\n if (tableOpenIdx === -1) return;\n\n addAttrs(tokens[tableOpenIdx], content, range, options.allowed);\n\n // Remove paragraph_open + inline + paragraph_close\n tokens.splice(index - 1, 3);\n }\n };\n\n // Rule 2: table cell attrs — \"cell content {.class}\" inside td/th\n const tableCellAttrs: AttrRule = {\n name: \"table cell attributes\",\n tests: [\n {\n shift: 0,\n type: \"inline\",\n children: [\n {\n index: -1,\n type: \"text\",\n content: (content: string) => check(content.trim(), \"end\") !== null\n }\n ]\n },\n {\n shift: -1,\n type: (t: string) => t === \"td_open\" || t === \"th_open\"\n }\n ],\n transform(tokens, index) {\n const inline = tokens[index];\n const children = inline.children!;\n const lastChild = children[children.length - 1];\n const content = lastChild.content.trimEnd();\n const range = check(content, \"end\")!;\n\n const cellOpen = tokens[index - 1];\n addAttrs(cellOpen, content, range, options.allowed);\n\n const attrStart = content.lastIndexOf(options.left, range[0] - 1);\n lastChild.content = content.slice(0, attrStart).trimEnd();\n if (lastChild.content === \"\" && children.length > 1) children.pop();\n }\n };\n\n // Rule 3: track column count in thead (stored as metadata for rowspan/colspan calc)\n interface TableMeta {\n cols: number;\n rowspan: number[];\n }\n\n const colTracker: AttrRule = {\n name: \"table column tracker\",\n tests: [{ shift: 0, type: \"tr_open\" }],\n transform(tokens, index) {\n // Count th tokens in this row to know column count; stored on thead_open meta\n let cols = 0;\n for (let i = index + 1; i < tokens.length; i++) {\n const t = tokens[i];\n if (t.type === \"tr_close\") break;\n if (t.type === \"th_open\") cols++;\n }\n if (cols === 0) return;\n\n // Find thead_open and store column count\n for (let i = index - 1; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === \"thead_open\") {\n t.meta = t.meta ?? {};\n (t.meta as TableMeta).cols = cols;\n break;\n }\n if (t.type === \"table_open\") break;\n }\n }\n };\n\n // Rule 4: colspan/rowspan calculation — remove phantom cells, adjust span values\n const spanCalc: AttrRule = {\n name: \"table span calculator\",\n tests: [{ shift: 0, type: \"tbody_open\" }],\n transform(tokens, index) {\n // Find matching thead_open to get column count\n let cols = 0;\n for (let i = index - 1; i >= 0; i--) {\n const t = tokens[i];\n if (t.type === \"thead_open\") {\n cols = (t.meta as TableMeta | undefined)?.cols ?? 0;\n break;\n }\n if (t.type === \"table_open\") break;\n }\n if (cols === 0) return;\n\n // Find the tbody_close\n let tbodyClose = index + 1;\n let depth = 1;\n while (tbodyClose < tokens.length && depth > 0) {\n const t = tokens[tbodyClose];\n if (t.type === \"tbody_open\") depth++;\n else if (t.type === \"tbody_close\") depth--;\n tbodyClose++;\n }\n tbodyClose--;\n\n // Process each row within tbody\n const occupied: number[] = Array.from({ length: cols }, () => 0);\n\n let i = index + 1;\n while (i < tbodyClose) {\n const t = tokens[i];\n if (t.type === \"tr_open\") {\n // Decrement all occupied counts\n for (let c = 0; c < cols; c++) {\n if (occupied[c] > 0) occupied[c]--;\n }\n\n // Collect td tokens in this row\n const tdIndices: number[] = [];\n for (let j = i + 1; j < tbodyClose; j++) {\n if (tokens[j].type === \"tr_close\") break;\n if (tokens[j].type === \"td_open\") tdIndices.push(j);\n }\n\n // Assign cells to columns, skipping occupied ones\n let col = 0;\n let tdIdx = 0;\n\n while (col < cols && tdIdx < tdIndices.length) {\n // Skip occupied columns (from rowspan)\n while (col < cols && occupied[col] > 0) col++;\n if (col >= cols) break;\n\n const tdOpen = tokens[tdIndices[tdIdx]];\n const rowspanAttr = tdOpen.attrGet(\"rowspan\");\n const colspanAttr = tdOpen.attrGet(\"colspan\");\n const rowspan = rowspanAttr ? parseInt(rowspanAttr, 10) : 1;\n let colspan = colspanAttr ? parseInt(colspanAttr, 10) : 1;\n\n // Count how many consecutive free columns are available\n let freeCols = 0;\n for (let c = col; c < cols && occupied[c] === 0; c++) freeCols++;\n\n // Clamp colspan to available free columns\n if (colspan > freeCols) {\n colspan = freeCols;\n if (colspan === 1) {\n // Remove colspan attr if it resolved to 1 (keep as-is if explicitly set)\n // We keep it to match reference behavior: colspan=\"1\" is left on token\n }\n tdOpen.attrSet(\"colspan\", String(colspan));\n }\n\n // Mark columns occupied by this cell's rowspan\n for (let c = col; c < col + colspan && c < cols; c++) {\n if (rowspan > 1) occupied[c] = rowspan - 1;\n }\n\n col += colspan;\n tdIdx++;\n }\n\n // Remove phantom cells beyond assigned ones\n // (cells that were merged away by rowspan from a previous row)\n while (tdIdx < tdIndices.length) {\n const phantomStart = tdIndices[tdIdx];\n // Find the matching td_close\n let phantomEnd = phantomStart + 1;\n while (\n phantomEnd < tokens.length &&\n tokens[phantomEnd].type !== \"td_close\"\n ) {\n phantomEnd++;\n }\n tokens.splice(phantomStart, phantomEnd - phantomStart + 1);\n tbodyClose -= phantomEnd - phantomStart + 1;\n // Don't increment tdIdx since we just spliced\n }\n }\n i++;\n }\n }\n };\n\n return [tableAttrs, tableCellAttrs, colTracker, spanCalc];\n};\n","import type { AttrsOptions, AttrRuleName, DelimiterConfig } from \"../types.js\";\nimport type { AttrRule } from \"./types.js\";\nimport { createBlockRule } from \"./block.js\";\nimport { createInlineRules } from \"./inline.js\";\nimport { createFenceRule } from \"./fence.js\";\nimport { createHeadingRule } from \"./heading.js\";\nimport { createHrRule } from \"./hr.js\";\nimport { createListRules } from \"./list.js\";\nimport { createSoftbreakRule } from \"./softbreak.js\";\nimport { createTableRules } from \"./table.js\";\n\nconst ALL_RULE_NAMES: AttrRuleName[] = [\n \"fence\",\n \"inline\",\n \"table\",\n \"list\",\n \"heading\",\n \"hr\",\n \"softbreak\",\n \"block\"\n];\n\nconst ALL_RULE_SET = new Set<string>(ALL_RULE_NAMES);\n\nexport const createRules = (\n options: DelimiterConfig & Pick<AttrsOptions, \"rule\">\n): AttrRule[] => {\n const { rule = \"all\" } = options;\n\n let enabled: Set<string>;\n if (rule === \"all\" || rule === true) {\n enabled = ALL_RULE_SET;\n } else if (!rule || (Array.isArray(rule) && rule.length === 0)) {\n return [];\n } else {\n enabled = new Set(rule.filter((r) => ALL_RULE_SET.has(r)));\n }\n\n const rules: AttrRule[] = [];\n\n if (enabled.has(\"fence\")) rules.push(createFenceRule(options));\n if (enabled.has(\"inline\")) rules.push(...createInlineRules(options));\n if (enabled.has(\"table\")) rules.push(...createTableRules(options));\n if (enabled.has(\"list\")) rules.push(...createListRules(options));\n if (enabled.has(\"heading\")) rules.push(createHeadingRule(options));\n if (enabled.has(\"hr\")) rules.push(createHrRule(options));\n if (enabled.has(\"softbreak\")) rules.push(createSoftbreakRule(options));\n if (enabled.has(\"block\")) rules.push(createBlockRule(options));\n\n return rules;\n};\n","import type { PluginWithOptions } from \"markdown-it\";\nimport type { AttrsOptions, DelimiterRange } from \"./types.js\";\nimport { createRules } from \"./rules/index.js\";\nimport { testRule } from \"./helper/index.js\";\n\nexport const attrs: PluginWithOptions<AttrsOptions> = (\n md,\n { left = \"{\", right = \"}\", allowed = [], rule = \"all\" } = {}\n) => {\n const rules = createRules({ left, right, allowed, rule });\n\n md.core.ruler.before(\"linkify\", \"attrs\", (state) => {\n const { tokens } = state;\n\n for (let index = 0; index < tokens.length; index++) {\n for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex++) {\n const pattern = rules[ruleIndex];\n let position: number | null = null;\n let range: DelimiterRange | null = null;\n\n const match = pattern.tests.every((test) => {\n const result = testRule(tokens, index, test);\n if (result.position != null) position = result.position;\n if (result.range) range = result.range;\n return result.match;\n });\n\n if (match) {\n const modified = pattern.transform(tokens, index, position!, range!);\n\n if (\n modified !== false &&\n (pattern.name === \"inline attributes\" ||\n pattern.name === \"inline nesting self-close\")\n ) {\n ruleIndex--;\n }\n }\n }\n }\n });\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,38 @@
1
1
  {
2
2
  "name": "@mirrordown/mdit-attrs",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "license": "MIT",
5
5
  "author": "Drake Costa <drake@saeris.io> (https://saeris.gg)",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/mirrordown/mirrordown.git"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "development": "./src/index.ts",
17
+ "types": "./dist/index.d.mts",
18
+ "import": "./dist/index.mjs"
19
+ }
20
+ },
21
+ "devDependencies": {
22
+ "@types/markdown-it": "catalog:",
23
+ "markdown-it": "catalog:"
24
+ },
25
+ "peerDependencies": {
26
+ "markdown-it": "^14.0.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=24.0.0"
30
+ },
31
+ "bumpy": {
32
+ "buildCommand": "vp pack",
33
+ "publishCommand": [
34
+ "node ../../scripts/sync-jsr-version.mjs",
35
+ "npm publish --access public"
36
+ ]
9
37
  }
10
38
  }