eslint-plugin-markdown-preferences 0.3.3 → 0.4.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 +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +128 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ The rules with the following star ⭐ are included in the configs.
|
|
|
93
93
|
| [markdown-preferences/hard-linebreak-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/hard-linebreak-style.html) | enforce consistent hard linebreak style. | 🔧 | ⭐ |
|
|
94
94
|
| [markdown-preferences/no-text-backslash-linebreak](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-text-backslash-linebreak.html) | disallow text backslash at the end of a line. | | ⭐ |
|
|
95
95
|
| [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | trailing whitespace at the end of lines in Markdown files. | 🔧 | |
|
|
96
|
+
| [markdown-preferences/prefer-inline-code-words](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-inline-code-words.html) | enforce the use of inline code for specific words. | 🔧 | |
|
|
96
97
|
| [markdown-preferences/prefer-linked-words](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-linked-words.html) | enforce the specified word to be a link. | 🔧 | |
|
|
97
98
|
|
|
98
99
|
<!--RULES_TABLE_END-->
|
package/lib/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ declare namespace meta_d_exports {
|
|
|
19
19
|
export { name, version };
|
|
20
20
|
}
|
|
21
21
|
declare const name: "eslint-plugin-markdown-preferences";
|
|
22
|
-
declare const version: "0.
|
|
22
|
+
declare const version: "0.4.0";
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region src/index.d.ts
|
|
25
25
|
declare const configs: {
|
package/lib/index.js
CHANGED
|
@@ -244,8 +244,99 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
|
|
|
244
244
|
});
|
|
245
245
|
|
|
246
246
|
//#endregion
|
|
247
|
-
//#region src/
|
|
247
|
+
//#region src/utils/search-words.ts
|
|
248
248
|
const RE_BOUNDARY = /^[\s\p{Letter_Number}\p{Modifier_Letter}\p{Modifier_Symbol}\p{Nonspacing_Mark}\p{Other_Letter}\p{Other_Symbol}\p{Script=Han}!"#$%&'(),./:;<=>?\\{|}~\u{2ffc}-\u{303d}\u{30a0}-\u{30fb}\u{3192}-\u{32bf}\u{fe10}-\u{fe1f}\u{fe30}-\u{fe6f}\u{ff00}-\u{ffef}\u{2ebf0}-\u{2ee5d}]*$/u;
|
|
249
|
+
/**
|
|
250
|
+
* Iterate through words in a text node that match the specified words.
|
|
251
|
+
*/
|
|
252
|
+
function* iterateSearchWords(sourceCode, node, words) {
|
|
253
|
+
const text = sourceCode.getText(node);
|
|
254
|
+
for (const word of words) {
|
|
255
|
+
let startPosition = 0;
|
|
256
|
+
while (true) {
|
|
257
|
+
const index = text.indexOf(word, startPosition);
|
|
258
|
+
if (index < 0) break;
|
|
259
|
+
startPosition = index + word.length;
|
|
260
|
+
if (!RE_BOUNDARY.test(text[index - 1] || "") || !RE_BOUNDARY.test(text[index + word.length] || "")) continue;
|
|
261
|
+
const loc = sourceCode.getLoc(node);
|
|
262
|
+
const beforeLines = text.slice(0, index).split(/\n/u);
|
|
263
|
+
const line = loc.start.line + beforeLines.length - 1;
|
|
264
|
+
const column = (beforeLines.length === 1 ? loc.start.column : 1) + (beforeLines.at(-1) || "").length;
|
|
265
|
+
const range = sourceCode.getRange(node);
|
|
266
|
+
yield {
|
|
267
|
+
loc: {
|
|
268
|
+
start: {
|
|
269
|
+
line,
|
|
270
|
+
column
|
|
271
|
+
},
|
|
272
|
+
end: {
|
|
273
|
+
line,
|
|
274
|
+
column: column + word.length
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
range: [range[0] + index, range[0] + index + word.length],
|
|
278
|
+
word
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
//#endregion
|
|
285
|
+
//#region src/rules/prefer-inline-code-words.ts
|
|
286
|
+
var prefer_inline_code_words_default = createRule("prefer-inline-code-words", {
|
|
287
|
+
meta: {
|
|
288
|
+
type: "suggestion",
|
|
289
|
+
docs: {
|
|
290
|
+
description: "enforce the use of inline code for specific words.",
|
|
291
|
+
categories: []
|
|
292
|
+
},
|
|
293
|
+
fixable: "code",
|
|
294
|
+
hasSuggestions: false,
|
|
295
|
+
schema: [{
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: { words: {
|
|
298
|
+
type: "array",
|
|
299
|
+
items: { type: "string" }
|
|
300
|
+
} },
|
|
301
|
+
required: ["words"]
|
|
302
|
+
}],
|
|
303
|
+
messages: { requireInlineCode: "The word \"{{name}}\" should be in inline code." }
|
|
304
|
+
},
|
|
305
|
+
create(context) {
|
|
306
|
+
const sourceCode = context.sourceCode;
|
|
307
|
+
const words = context.options[0]?.words || [];
|
|
308
|
+
let shortcutLinkReference = null;
|
|
309
|
+
return {
|
|
310
|
+
linkReference(node) {
|
|
311
|
+
if (node.referenceType !== "shortcut") return;
|
|
312
|
+
if (shortcutLinkReference) return;
|
|
313
|
+
shortcutLinkReference = node;
|
|
314
|
+
},
|
|
315
|
+
"linkReference:exit"(node) {
|
|
316
|
+
if (shortcutLinkReference === node) shortcutLinkReference = null;
|
|
317
|
+
},
|
|
318
|
+
text(node) {
|
|
319
|
+
for (const { word, loc, range } of iterateSearchWords(sourceCode, node, words)) {
|
|
320
|
+
const shortcutLinkReferenceToReport = shortcutLinkReference;
|
|
321
|
+
context.report({
|
|
322
|
+
node,
|
|
323
|
+
loc,
|
|
324
|
+
messageId: "requireInlineCode",
|
|
325
|
+
data: { name: word },
|
|
326
|
+
*fix(fixer) {
|
|
327
|
+
yield fixer.insertTextBeforeRange(range, "`");
|
|
328
|
+
yield fixer.insertTextAfterRange(range, "`");
|
|
329
|
+
if (shortcutLinkReferenceToReport) yield fixer.insertTextAfter(shortcutLinkReferenceToReport, `[${shortcutLinkReferenceToReport.label}]`);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/rules/prefer-linked-words.ts
|
|
249
340
|
var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
250
341
|
meta: {
|
|
251
342
|
type: "suggestion",
|
|
@@ -270,8 +361,18 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
|
270
361
|
},
|
|
271
362
|
create(context) {
|
|
272
363
|
const sourceCode = context.sourceCode;
|
|
273
|
-
const
|
|
274
|
-
const
|
|
364
|
+
const wordsOption = context.options[0]?.words || {};
|
|
365
|
+
const links = Object.create(null);
|
|
366
|
+
const words = [];
|
|
367
|
+
if (Array.isArray(wordsOption)) words.push(...wordsOption);
|
|
368
|
+
else for (const [word, link] of Object.entries(wordsOption)) {
|
|
369
|
+
if (link) {
|
|
370
|
+
const adjustedLink = adjustLink(link);
|
|
371
|
+
if (adjustedLink === `./${path.basename(context.filename)}`) continue;
|
|
372
|
+
links[word] = adjustedLink;
|
|
373
|
+
}
|
|
374
|
+
words.push(word);
|
|
375
|
+
}
|
|
275
376
|
let ignore = null;
|
|
276
377
|
return {
|
|
277
378
|
"link, linkReference, heading, footnoteDefinition"(node) {
|
|
@@ -283,50 +384,32 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
|
283
384
|
},
|
|
284
385
|
text(node) {
|
|
285
386
|
if (ignore) return;
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const column = (beforeLines.length === 1 ? loc.start.column : 1) + (beforeLines.at(-1) || "").length;
|
|
298
|
-
context.report({
|
|
299
|
-
node,
|
|
300
|
-
loc: {
|
|
301
|
-
start: {
|
|
302
|
-
line,
|
|
303
|
-
column
|
|
304
|
-
},
|
|
305
|
-
end: {
|
|
306
|
-
line,
|
|
307
|
-
column: column + word.length
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
messageId: "requireLink",
|
|
311
|
-
data: { name: word },
|
|
312
|
-
fix: link ? (fixer) => {
|
|
313
|
-
const [start] = sourceCode.getRange(node);
|
|
314
|
-
return fixer.replaceTextRange([start + index, start + index + word.length], `[${word}](${link})`);
|
|
315
|
-
} : null
|
|
316
|
-
});
|
|
317
|
-
}
|
|
387
|
+
for (const { word, loc, range } of iterateSearchWords(sourceCode, node, words)) {
|
|
388
|
+
const link = links[word];
|
|
389
|
+
context.report({
|
|
390
|
+
node,
|
|
391
|
+
loc,
|
|
392
|
+
messageId: "requireLink",
|
|
393
|
+
data: { name: word },
|
|
394
|
+
fix: link ? (fixer) => {
|
|
395
|
+
return fixer.replaceTextRange(range, `[${word}](${link})`);
|
|
396
|
+
} : null
|
|
397
|
+
});
|
|
318
398
|
}
|
|
319
399
|
},
|
|
320
400
|
inlineCode(node) {
|
|
321
401
|
if (ignore) return;
|
|
322
|
-
for (const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
402
|
+
for (const word of words) if (node.value === word) {
|
|
403
|
+
const link = links[word];
|
|
404
|
+
context.report({
|
|
405
|
+
node,
|
|
406
|
+
messageId: "requireLink",
|
|
407
|
+
data: { name: word },
|
|
408
|
+
fix: link ? (fixer) => {
|
|
409
|
+
return fixer.replaceText(node, `[\`${word}\`](${link})`);
|
|
410
|
+
} : null
|
|
411
|
+
});
|
|
412
|
+
}
|
|
330
413
|
}
|
|
331
414
|
};
|
|
332
415
|
/**
|
|
@@ -347,6 +430,7 @@ const rules$1 = [
|
|
|
347
430
|
hard_linebreak_style_default,
|
|
348
431
|
no_text_backslash_linebreak_default,
|
|
349
432
|
no_trailing_spaces_default,
|
|
433
|
+
prefer_inline_code_words_default,
|
|
350
434
|
prefer_linked_words_default
|
|
351
435
|
];
|
|
352
436
|
|
|
@@ -382,7 +466,7 @@ __export(meta_exports, {
|
|
|
382
466
|
version: () => version
|
|
383
467
|
});
|
|
384
468
|
const name = "eslint-plugin-markdown-preferences";
|
|
385
|
-
const version = "0.
|
|
469
|
+
const version = "0.4.0";
|
|
386
470
|
|
|
387
471
|
//#endregion
|
|
388
472
|
//#region src/index.ts
|