eslint-plugin-markdown-preferences 0.9.0 → 0.10.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 +9 -3
- package/lib/index.d.ts +16 -1
- package/lib/index.js +350 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ Example **eslint.config.js**:
|
|
|
50
50
|
```js
|
|
51
51
|
import { defineConfig } from "eslint/config";
|
|
52
52
|
// import markdown from "@eslint/markdown";
|
|
53
|
-
import markdownPreferences from
|
|
53
|
+
import markdownPreferences from "eslint-plugin-markdown-preferences";
|
|
54
54
|
export default [
|
|
55
55
|
// add more generic rule sets here, such as:
|
|
56
56
|
// markdown.configs.recommended,
|
|
@@ -59,8 +59,8 @@ export default [
|
|
|
59
59
|
rules: {
|
|
60
60
|
// override/add rules settings here, such as:
|
|
61
61
|
// 'markdown-preferences/prefer-linked-words': 'error'
|
|
62
|
-
}
|
|
63
|
-
}
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
64
|
];
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -86,6 +86,8 @@ The rules with the following star ⭐ are included in the configs.
|
|
|
86
86
|
|
|
87
87
|
<!--RULES_TABLE_START-->
|
|
88
88
|
|
|
89
|
+
<!-- prettier-ignore-start -->
|
|
90
|
+
|
|
89
91
|
### Preference Rules
|
|
90
92
|
|
|
91
93
|
| Rule ID | Description | Fixable | RECOMMENDED |
|
|
@@ -102,10 +104,14 @@ The rules with the following star ⭐ are included in the configs.
|
|
|
102
104
|
| [markdown-preferences/definitions-last](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/definitions-last.html) | require link definitions and footnote definitions to be placed at the end of the document | 🔧 | |
|
|
103
105
|
| [markdown-preferences/hard-linebreak-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/hard-linebreak-style.html) | enforce consistent hard linebreak style. | 🔧 | ⭐ |
|
|
104
106
|
| [markdown-preferences/heading-casing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/heading-casing.html) | enforce consistent casing in headings. | 🔧 | |
|
|
107
|
+
| [markdown-preferences/no-laziness-blockquotes](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html) | disallow laziness in blockquotes | | ⭐ |
|
|
108
|
+
| [markdown-preferences/no-multiple-empty-lines](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html) | disallow multiple empty lines in Markdown files. | 🔧 | |
|
|
105
109
|
| [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | disallow trailing whitespace at the end of lines in Markdown files. | 🔧 | |
|
|
106
110
|
| [markdown-preferences/prefer-link-reference-definitions](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-link-reference-definitions.html) | enforce using link reference definitions instead of inline links | 🔧 | |
|
|
107
111
|
| [markdown-preferences/sort-definitions](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/sort-definitions.html) | enforce a specific order for link definitions and footnote definitions | 🔧 | |
|
|
108
112
|
|
|
113
|
+
<!-- prettier-ignore-end -->
|
|
114
|
+
|
|
109
115
|
<!--RULES_TABLE_END-->
|
|
110
116
|
<!--RULES_SECTION_END-->
|
|
111
117
|
<!--DOCS_IGNORE_START-->
|
package/lib/index.d.ts
CHANGED
|
@@ -30,6 +30,16 @@ interface RuleOptions {
|
|
|
30
30
|
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/heading-casing.html
|
|
31
31
|
*/
|
|
32
32
|
'markdown-preferences/heading-casing'?: Linter.RuleEntry<MarkdownPreferencesHeadingCasing>;
|
|
33
|
+
/**
|
|
34
|
+
* disallow laziness in blockquotes
|
|
35
|
+
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html
|
|
36
|
+
*/
|
|
37
|
+
'markdown-preferences/no-laziness-blockquotes'?: Linter.RuleEntry<[]>;
|
|
38
|
+
/**
|
|
39
|
+
* disallow multiple empty lines in Markdown files.
|
|
40
|
+
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html
|
|
41
|
+
*/
|
|
42
|
+
'markdown-preferences/no-multiple-empty-lines'?: Linter.RuleEntry<MarkdownPreferencesNoMultipleEmptyLines>;
|
|
33
43
|
/**
|
|
34
44
|
* disallow text backslash at the end of a line.
|
|
35
45
|
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-text-backslash-linebreak.html
|
|
@@ -75,6 +85,11 @@ type MarkdownPreferencesHeadingCasing = [] | [{
|
|
|
75
85
|
ignorePatterns?: string[];
|
|
76
86
|
minorWords?: string[];
|
|
77
87
|
}];
|
|
88
|
+
type MarkdownPreferencesNoMultipleEmptyLines = [] | [{
|
|
89
|
+
max?: number;
|
|
90
|
+
maxEOF?: number;
|
|
91
|
+
maxBOF?: number;
|
|
92
|
+
}];
|
|
78
93
|
type MarkdownPreferencesNoTrailingSpaces = [] | [{
|
|
79
94
|
skipBlankLines?: boolean;
|
|
80
95
|
ignoreComments?: boolean;
|
|
@@ -128,7 +143,7 @@ declare namespace meta_d_exports {
|
|
|
128
143
|
export { name, version };
|
|
129
144
|
}
|
|
130
145
|
declare const name: "eslint-plugin-markdown-preferences";
|
|
131
|
-
declare const version: "0.
|
|
146
|
+
declare const version: "0.10.0";
|
|
132
147
|
//#endregion
|
|
133
148
|
//#region src/index.d.ts
|
|
134
149
|
declare const configs: {
|
package/lib/index.js
CHANGED
|
@@ -978,6 +978,333 @@ var heading_casing_default = createRule("heading-casing", {
|
|
|
978
978
|
}
|
|
979
979
|
});
|
|
980
980
|
|
|
981
|
+
//#endregion
|
|
982
|
+
//#region src/utils/lines.ts
|
|
983
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
984
|
+
var ParsedLines = class {
|
|
985
|
+
lines;
|
|
986
|
+
constructor(codeText) {
|
|
987
|
+
let offset = 0;
|
|
988
|
+
this.lines = codeText.split(/(?<=\n)/u).map((lineText, index) => {
|
|
989
|
+
const start = offset;
|
|
990
|
+
offset += lineText.length;
|
|
991
|
+
const range = [start, offset];
|
|
992
|
+
let text = lineText;
|
|
993
|
+
let linebreak = "";
|
|
994
|
+
if (text.at(-1) === "\n") {
|
|
995
|
+
text = text.slice(0, -1);
|
|
996
|
+
linebreak = "\n";
|
|
997
|
+
}
|
|
998
|
+
if (text.at(-1) === "\r") {
|
|
999
|
+
text = text.slice(0, -1);
|
|
1000
|
+
linebreak = `\r${linebreak}`;
|
|
1001
|
+
}
|
|
1002
|
+
return {
|
|
1003
|
+
text,
|
|
1004
|
+
range,
|
|
1005
|
+
line: index + 1,
|
|
1006
|
+
linebreak
|
|
1007
|
+
};
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
[Symbol.iterator]() {
|
|
1011
|
+
return this.lines[Symbol.iterator]();
|
|
1012
|
+
}
|
|
1013
|
+
get length() {
|
|
1014
|
+
return this.lines.length;
|
|
1015
|
+
}
|
|
1016
|
+
get(lineNumber) {
|
|
1017
|
+
return this.lines[lineNumber - 1];
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
/**
|
|
1021
|
+
* Parse the lines of the source code.
|
|
1022
|
+
* @param sourceCode source code to parse
|
|
1023
|
+
* @returns parsed lines
|
|
1024
|
+
*/
|
|
1025
|
+
function parseLines(sourceCode) {
|
|
1026
|
+
const cached = cache.get(sourceCode);
|
|
1027
|
+
if (cached) return cached;
|
|
1028
|
+
const parsedLines = new ParsedLines(sourceCode.text);
|
|
1029
|
+
cache.set(sourceCode, parsedLines);
|
|
1030
|
+
return parsedLines;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
//#endregion
|
|
1034
|
+
//#region src/rules/no-laziness-blockquotes.ts
|
|
1035
|
+
/**
|
|
1036
|
+
* Helper function to get blockquote line information.
|
|
1037
|
+
*/
|
|
1038
|
+
function getBlockquoteLineInfo(line) {
|
|
1039
|
+
const regex = /^\s*(?:>\s*)*/u;
|
|
1040
|
+
const match = regex.exec(line.text);
|
|
1041
|
+
return {
|
|
1042
|
+
line,
|
|
1043
|
+
prefix: match[0],
|
|
1044
|
+
level: (match[0].match(/>/gu) || []).length
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
|
|
1048
|
+
meta: {
|
|
1049
|
+
type: "problem",
|
|
1050
|
+
docs: {
|
|
1051
|
+
description: "disallow laziness in blockquotes",
|
|
1052
|
+
categories: ["recommended"],
|
|
1053
|
+
listCategory: "Stylistic"
|
|
1054
|
+
},
|
|
1055
|
+
fixable: void 0,
|
|
1056
|
+
hasSuggestions: true,
|
|
1057
|
+
schema: [],
|
|
1058
|
+
messages: {
|
|
1059
|
+
lazyBlockquoteLine: "Expected {{level}} '>' marker(s), but found {{actualLevel}}. This line will be interpreted as part of level {{level}} blockquote.",
|
|
1060
|
+
addMarker: "Add {{missingMarkers}} '>' marker(s).",
|
|
1061
|
+
addLineBreak: "Add line break to separate from blockquote."
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
create(context) {
|
|
1065
|
+
const sourceCode = context.sourceCode;
|
|
1066
|
+
const checkedLines = /* @__PURE__ */ new Set();
|
|
1067
|
+
const lines = parseLines(sourceCode);
|
|
1068
|
+
/**
|
|
1069
|
+
* Report invalid blockquote lines.
|
|
1070
|
+
*/
|
|
1071
|
+
function reportInvalidLines(invalidLines, base) {
|
|
1072
|
+
const invalidGroups = [];
|
|
1073
|
+
for (const invalidLine of invalidLines) {
|
|
1074
|
+
const currentGroup = invalidGroups.at(-1);
|
|
1075
|
+
if (!currentGroup || currentGroup.level < invalidLine.level) {
|
|
1076
|
+
invalidGroups.push({
|
|
1077
|
+
level: invalidLine.level,
|
|
1078
|
+
lines: [invalidLine]
|
|
1079
|
+
});
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (currentGroup.level === invalidLine.level) {
|
|
1083
|
+
currentGroup.lines.push(invalidLine);
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
if (invalidLine.level < currentGroup.level) break;
|
|
1087
|
+
}
|
|
1088
|
+
for (const group of invalidGroups) {
|
|
1089
|
+
const first = group.lines[0];
|
|
1090
|
+
const last = group.lines.at(-1);
|
|
1091
|
+
context.report({
|
|
1092
|
+
loc: {
|
|
1093
|
+
start: {
|
|
1094
|
+
line: first.line.line,
|
|
1095
|
+
column: 1
|
|
1096
|
+
},
|
|
1097
|
+
end: {
|
|
1098
|
+
line: last.line.line,
|
|
1099
|
+
column: last.line.text.length + 1
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
messageId: "lazyBlockquoteLine",
|
|
1103
|
+
data: {
|
|
1104
|
+
level: `${base.level}`,
|
|
1105
|
+
actualLevel: `${group.level}`
|
|
1106
|
+
},
|
|
1107
|
+
suggest: [{
|
|
1108
|
+
messageId: "addMarker",
|
|
1109
|
+
data: { missingMarkers: `${base.level - group.level}` },
|
|
1110
|
+
*fix(fixer) {
|
|
1111
|
+
for (const invalidLine of group.lines) yield fixer.replaceTextRange([invalidLine.line.range[0], invalidLine.line.range[0] + invalidLine.prefix.length], base.prefix);
|
|
1112
|
+
}
|
|
1113
|
+
}, {
|
|
1114
|
+
messageId: "addLineBreak",
|
|
1115
|
+
fix: (fixer) => {
|
|
1116
|
+
return fixer.insertTextBeforeRange([first.line.range[0], first.line.range[0]], `${first.prefix.trimEnd()}\n`);
|
|
1117
|
+
}
|
|
1118
|
+
}]
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return { "blockquote:exit"(node) {
|
|
1123
|
+
const loc = sourceCode.getLoc(node);
|
|
1124
|
+
const startLine = loc.start.line;
|
|
1125
|
+
const endLine = loc.end.line;
|
|
1126
|
+
const base = getBlockquoteLineInfo(lines.get(startLine));
|
|
1127
|
+
const invalidLines = [];
|
|
1128
|
+
for (let lineNumber = startLine + 1; lineNumber <= endLine; lineNumber++) {
|
|
1129
|
+
if (checkedLines.has(lineNumber)) {
|
|
1130
|
+
reportInvalidLines(invalidLines, base);
|
|
1131
|
+
invalidLines.length = 0;
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
checkedLines.add(lineNumber);
|
|
1135
|
+
const line = lines.get(lineNumber);
|
|
1136
|
+
const current = getBlockquoteLineInfo(line);
|
|
1137
|
+
if (base.level <= current.level) {
|
|
1138
|
+
reportInvalidLines(invalidLines, base);
|
|
1139
|
+
invalidLines.length = 0;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
invalidLines.push(current);
|
|
1143
|
+
}
|
|
1144
|
+
reportInvalidLines(invalidLines, base);
|
|
1145
|
+
} };
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
//#endregion
|
|
1150
|
+
//#region src/rules/no-multiple-empty-lines.ts
|
|
1151
|
+
var no_multiple_empty_lines_default = createRule("no-multiple-empty-lines", {
|
|
1152
|
+
meta: {
|
|
1153
|
+
type: "layout",
|
|
1154
|
+
docs: {
|
|
1155
|
+
description: "disallow multiple empty lines in Markdown files.",
|
|
1156
|
+
categories: null,
|
|
1157
|
+
listCategory: "Stylistic"
|
|
1158
|
+
},
|
|
1159
|
+
fixable: "whitespace",
|
|
1160
|
+
hasSuggestions: false,
|
|
1161
|
+
schema: [{
|
|
1162
|
+
type: "object",
|
|
1163
|
+
properties: {
|
|
1164
|
+
max: {
|
|
1165
|
+
type: "integer",
|
|
1166
|
+
minimum: 0,
|
|
1167
|
+
default: 1
|
|
1168
|
+
},
|
|
1169
|
+
maxEOF: {
|
|
1170
|
+
type: "integer",
|
|
1171
|
+
minimum: 0,
|
|
1172
|
+
default: 0
|
|
1173
|
+
},
|
|
1174
|
+
maxBOF: {
|
|
1175
|
+
type: "integer",
|
|
1176
|
+
minimum: 0,
|
|
1177
|
+
default: 0
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
additionalProperties: false
|
|
1181
|
+
}],
|
|
1182
|
+
messages: {
|
|
1183
|
+
blankBeginningOfFile: "Too many blank lines at the beginning of file. Max of {{max}} allowed.",
|
|
1184
|
+
blankEndOfFile: "Too many blank lines at the end of file. Max of {{max}} allowed.",
|
|
1185
|
+
consecutiveBlank: "More than {{max}} blank {{pluralizedLines}} not allowed."
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1188
|
+
create(context) {
|
|
1189
|
+
const sourceCode = context.sourceCode;
|
|
1190
|
+
const option = context.options[0] || {};
|
|
1191
|
+
const max = typeof option.max === "number" ? option.max : 1;
|
|
1192
|
+
const maxEOF = typeof option.maxEOF === "number" ? option.maxEOF : 0;
|
|
1193
|
+
const maxBOF = typeof option.maxBOF === "number" ? option.maxBOF : 0;
|
|
1194
|
+
const ignoreLocs = [];
|
|
1195
|
+
/**
|
|
1196
|
+
* Register the range of nodes to ignore (code, html, yaml, toml, json)
|
|
1197
|
+
* @param node mdast node
|
|
1198
|
+
*/
|
|
1199
|
+
function addIgnoreLoc(node) {
|
|
1200
|
+
const loc = sourceCode.getLoc(node);
|
|
1201
|
+
ignoreLocs.push({
|
|
1202
|
+
startLine: loc.start.line,
|
|
1203
|
+
endLine: loc.end.line
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
return {
|
|
1207
|
+
code: addIgnoreLoc,
|
|
1208
|
+
html: addIgnoreLoc,
|
|
1209
|
+
yaml: addIgnoreLoc,
|
|
1210
|
+
toml: addIgnoreLoc,
|
|
1211
|
+
json: addIgnoreLoc,
|
|
1212
|
+
"root:exit"() {
|
|
1213
|
+
const lines = [...parseLines(sourceCode)];
|
|
1214
|
+
const bofEmptyLines = [];
|
|
1215
|
+
while (lines.length) {
|
|
1216
|
+
if (lines[0].text.trim()) break;
|
|
1217
|
+
bofEmptyLines.push(lines.shift());
|
|
1218
|
+
}
|
|
1219
|
+
const invalidBOFEmptyLines = bofEmptyLines.slice(maxBOF);
|
|
1220
|
+
if (invalidBOFEmptyLines.length > 0) {
|
|
1221
|
+
const first = invalidBOFEmptyLines[0];
|
|
1222
|
+
const last = invalidBOFEmptyLines[invalidBOFEmptyLines.length - 1];
|
|
1223
|
+
context.report({
|
|
1224
|
+
loc: {
|
|
1225
|
+
start: {
|
|
1226
|
+
line: first.line,
|
|
1227
|
+
column: 1
|
|
1228
|
+
},
|
|
1229
|
+
end: {
|
|
1230
|
+
line: last.line,
|
|
1231
|
+
column: last.text.length + 1
|
|
1232
|
+
}
|
|
1233
|
+
},
|
|
1234
|
+
messageId: "blankBeginningOfFile",
|
|
1235
|
+
data: { max: maxBOF },
|
|
1236
|
+
fix(fixer) {
|
|
1237
|
+
return fixer.removeRange([first.range[0], last.range[1]]);
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
const eofEmptyLines = [];
|
|
1242
|
+
while (lines.length) {
|
|
1243
|
+
if (lines[lines.length - 1].text.trim()) break;
|
|
1244
|
+
eofEmptyLines.unshift(lines.pop());
|
|
1245
|
+
}
|
|
1246
|
+
const invalidEOFEmptyLines = eofEmptyLines.slice(maxEOF);
|
|
1247
|
+
if (invalidEOFEmptyLines.length > 0) {
|
|
1248
|
+
const first = invalidEOFEmptyLines[0];
|
|
1249
|
+
const last = invalidEOFEmptyLines[invalidEOFEmptyLines.length - 1];
|
|
1250
|
+
context.report({
|
|
1251
|
+
loc: {
|
|
1252
|
+
start: {
|
|
1253
|
+
line: first.line,
|
|
1254
|
+
column: 1
|
|
1255
|
+
},
|
|
1256
|
+
end: {
|
|
1257
|
+
line: last.line,
|
|
1258
|
+
column: last.text.length + 1
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
messageId: "blankEndOfFile",
|
|
1262
|
+
data: { max: maxEOF },
|
|
1263
|
+
fix(fixer) {
|
|
1264
|
+
return fixer.removeRange([first.range[0], last.range[1]]);
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
const emptyLines = [];
|
|
1269
|
+
for (const lineInfo of lines) {
|
|
1270
|
+
if (!lineInfo.text.trim() && !ignoreLocs.some(({ startLine, endLine }) => {
|
|
1271
|
+
return lineInfo.line > startLine && lineInfo.line < endLine;
|
|
1272
|
+
})) {
|
|
1273
|
+
emptyLines.push(lineInfo);
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
const invalidEmptyLines = emptyLines.slice(max);
|
|
1277
|
+
emptyLines.length = 0;
|
|
1278
|
+
if (invalidEmptyLines.length) {
|
|
1279
|
+
const first = invalidEmptyLines[0];
|
|
1280
|
+
const last = invalidEmptyLines[invalidEmptyLines.length - 1];
|
|
1281
|
+
context.report({
|
|
1282
|
+
loc: {
|
|
1283
|
+
start: {
|
|
1284
|
+
line: first.line,
|
|
1285
|
+
column: 1
|
|
1286
|
+
},
|
|
1287
|
+
end: {
|
|
1288
|
+
line: last.line,
|
|
1289
|
+
column: last.text.length + 1
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
messageId: "consecutiveBlank",
|
|
1293
|
+
data: {
|
|
1294
|
+
max,
|
|
1295
|
+
pluralizedLines: max === 1 ? "line" : "lines"
|
|
1296
|
+
},
|
|
1297
|
+
fix(fixer) {
|
|
1298
|
+
return fixer.removeRange([first.range[0], last.range[1]]);
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
|
|
981
1308
|
//#endregion
|
|
982
1309
|
//#region src/rules/no-text-backslash-linebreak.ts
|
|
983
1310
|
var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebreak", {
|
|
@@ -1109,45 +1436,28 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
|
|
|
1109
1436
|
"root:exit"() {
|
|
1110
1437
|
const re = /[^\S\n\r]+$/u;
|
|
1111
1438
|
const skipMatch = /^[^\S\n\r]*$/u;
|
|
1112
|
-
const lines = sourceCode
|
|
1113
|
-
const linebreaks = sourceCode.text.match(/\r?\n/gu);
|
|
1439
|
+
const lines = parseLines(sourceCode);
|
|
1114
1440
|
const commentLineNumbers = getCommentLineNumbers();
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
|
|
1119
|
-
const lineLength = lines[i].length + linebreakLength;
|
|
1120
|
-
const matches = re.exec(lines[i]);
|
|
1121
|
-
if (!matches) {
|
|
1122
|
-
totalLength += lineLength;
|
|
1123
|
-
continue;
|
|
1124
|
-
}
|
|
1441
|
+
for (const lineInfo of lines) {
|
|
1442
|
+
const matches = re.exec(lineInfo.text);
|
|
1443
|
+
if (!matches) continue;
|
|
1125
1444
|
const location = {
|
|
1126
1445
|
start: {
|
|
1127
|
-
line:
|
|
1446
|
+
line: lineInfo.line,
|
|
1128
1447
|
column: matches.index + 1
|
|
1129
1448
|
},
|
|
1130
1449
|
end: {
|
|
1131
|
-
line:
|
|
1132
|
-
column:
|
|
1450
|
+
line: lineInfo.line,
|
|
1451
|
+
column: matches.index + 1 + matches[0].length
|
|
1133
1452
|
}
|
|
1134
1453
|
};
|
|
1135
|
-
const
|
|
1136
|
-
const rangeEnd = totalLength + location.end.column - 1;
|
|
1454
|
+
const range = [lineInfo.range[0] + location.start.column - 1, lineInfo.range[0] + location.end.column - 1];
|
|
1137
1455
|
if (ignoreNodes.some((node) => {
|
|
1138
|
-
const
|
|
1139
|
-
return
|
|
1140
|
-
}))
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
if (skipBlankLines && skipMatch.test(lines[i])) {
|
|
1145
|
-
totalLength += lineLength;
|
|
1146
|
-
continue;
|
|
1147
|
-
}
|
|
1148
|
-
const fixRange = [rangeStart, rangeEnd];
|
|
1149
|
-
if (!ignoreComments || !commentLineNumbers.has(lineNumber)) report(location, fixRange);
|
|
1150
|
-
totalLength += lineLength;
|
|
1456
|
+
const nodeRange = sourceCode.getRange(node);
|
|
1457
|
+
return nodeRange[0] <= range[0] && range[1] <= nodeRange[1];
|
|
1458
|
+
})) continue;
|
|
1459
|
+
if (skipBlankLines && skipMatch.test(lineInfo.text)) continue;
|
|
1460
|
+
if (!ignoreComments || !commentLineNumbers.has(lineInfo.line)) report(location, range);
|
|
1151
1461
|
}
|
|
1152
1462
|
}
|
|
1153
1463
|
};
|
|
@@ -1488,25 +1798,6 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
|
|
|
1488
1798
|
}
|
|
1489
1799
|
});
|
|
1490
1800
|
|
|
1491
|
-
//#endregion
|
|
1492
|
-
//#region src/utils/url.ts
|
|
1493
|
-
/**
|
|
1494
|
-
* Utility function to check if a string is a valid URL.
|
|
1495
|
-
*/
|
|
1496
|
-
function isValidURL(url) {
|
|
1497
|
-
return Boolean(createURLSafe(url));
|
|
1498
|
-
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Utility function to create a URL object safely.
|
|
1501
|
-
*/
|
|
1502
|
-
function createURLSafe(url) {
|
|
1503
|
-
try {
|
|
1504
|
-
return new URL(url);
|
|
1505
|
-
} catch {
|
|
1506
|
-
return null;
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
1801
|
//#endregion
|
|
1511
1802
|
//#region src/rules/prefer-linked-words.ts
|
|
1512
1803
|
var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
@@ -1608,7 +1899,7 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
|
1608
1899
|
* Adjust link to be relative to the file.
|
|
1609
1900
|
*/
|
|
1610
1901
|
function adjustLink(link) {
|
|
1611
|
-
if (
|
|
1902
|
+
if (URL.canParse(link)) return link;
|
|
1612
1903
|
if (link.startsWith("#")) return link;
|
|
1613
1904
|
const absoluteLink = path.isAbsolute(link) || path.posix.isAbsolute(link) ? link : path.join(context.cwd, link);
|
|
1614
1905
|
return `./${path.relative(path.dirname(context.filename), absoluteLink)}`;
|
|
@@ -1789,14 +2080,14 @@ var sort_definitions_default = createRule("sort-definitions", {
|
|
|
1789
2080
|
}
|
|
1790
2081
|
/** Compile order option */
|
|
1791
2082
|
function compileOption(orderOption) {
|
|
1792
|
-
const cache = /* @__PURE__ */ new Map();
|
|
2083
|
+
const cache$1 = /* @__PURE__ */ new Map();
|
|
1793
2084
|
const compiled = compileOptionWithoutCache(orderOption);
|
|
1794
2085
|
return {
|
|
1795
2086
|
match: (node) => {
|
|
1796
|
-
const cached = cache.get(node);
|
|
2087
|
+
const cached = cache$1.get(node);
|
|
1797
2088
|
if (cached != null) return cached;
|
|
1798
2089
|
const result = compiled.match(node);
|
|
1799
|
-
cache.set(node, result);
|
|
2090
|
+
cache$1.set(node, result);
|
|
1800
2091
|
return result;
|
|
1801
2092
|
},
|
|
1802
2093
|
sort: compiled.sort
|
|
@@ -1847,7 +2138,7 @@ var sort_definitions_default = createRule("sort-definitions", {
|
|
|
1847
2138
|
if (node.label === patternStr || node.identifier === patternStr) return true;
|
|
1848
2139
|
if (node.type === "definition") {
|
|
1849
2140
|
if (node.url === patternStr) return true;
|
|
1850
|
-
if (
|
|
2141
|
+
if (URL.canParse(patternStr) && URL.canParse(node.url)) {
|
|
1851
2142
|
const normalizedPattern = normalizedURL(patternStr);
|
|
1852
2143
|
const normalizedUrl = normalizedURL(node.url);
|
|
1853
2144
|
if (normalizedUrl.startsWith(normalizedPattern)) return true;
|
|
@@ -1894,7 +2185,7 @@ function getQuote(text) {
|
|
|
1894
2185
|
* Normalize a URL by ensuring it ends with a slash.
|
|
1895
2186
|
*/
|
|
1896
2187
|
function normalizedURL(url) {
|
|
1897
|
-
const urlObj =
|
|
2188
|
+
const urlObj = new URL(url);
|
|
1898
2189
|
if (!urlObj) return url;
|
|
1899
2190
|
return urlObj.href.endsWith("/") ? urlObj.href : `${urlObj.href}/`;
|
|
1900
2191
|
}
|
|
@@ -1906,6 +2197,8 @@ const rules$1 = [
|
|
|
1906
2197
|
definitions_last_default,
|
|
1907
2198
|
hard_linebreak_style_default,
|
|
1908
2199
|
heading_casing_default,
|
|
2200
|
+
no_laziness_blockquotes_default,
|
|
2201
|
+
no_multiple_empty_lines_default,
|
|
1909
2202
|
no_text_backslash_linebreak_default,
|
|
1910
2203
|
no_trailing_spaces_default,
|
|
1911
2204
|
prefer_inline_code_words_default,
|
|
@@ -1935,6 +2228,7 @@ const plugins = {
|
|
|
1935
2228
|
};
|
|
1936
2229
|
const rules$2 = {
|
|
1937
2230
|
"markdown-preferences/hard-linebreak-style": "error",
|
|
2231
|
+
"markdown-preferences/no-laziness-blockquotes": "error",
|
|
1938
2232
|
"markdown-preferences/no-text-backslash-linebreak": "error"
|
|
1939
2233
|
};
|
|
1940
2234
|
|
|
@@ -1946,7 +2240,7 @@ __export(meta_exports, {
|
|
|
1946
2240
|
version: () => version
|
|
1947
2241
|
});
|
|
1948
2242
|
const name = "eslint-plugin-markdown-preferences";
|
|
1949
|
-
const version = "0.
|
|
2243
|
+
const version = "0.10.0";
|
|
1950
2244
|
|
|
1951
2245
|
//#endregion
|
|
1952
2246
|
//#region src/index.ts
|