mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Compare commits
480 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
224987d727 | ||
![]() |
8cb8f6c695 | ||
![]() |
a1506e71f3 | ||
![]() |
b3935080e9 | ||
![]() |
738f4aa59a | ||
![]() |
979419c7a0 | ||
![]() |
4d8e79d33e | ||
![]() |
6adfaf68b5 | ||
![]() |
a4b7ffa8ce | ||
![]() |
f34f4dde69 | ||
![]() |
2775534144 | ||
![]() |
006b6af736 | ||
![]() |
328506e6c8 | ||
![]() |
c413ac9a88 | ||
![]() |
9849e2871f | ||
![]() |
2a85b3c3f1 | ||
![]() |
b611eba684 | ||
![]() |
faf96d513b | ||
![]() |
318b9ff6b4 | ||
![]() |
e93b9854dc | ||
![]() |
f6087f0e6a | ||
![]() |
1dc4974191 | ||
![]() |
647dac621f | ||
![]() |
fb8b562c94 | ||
![]() |
91eba5470a | ||
![]() |
4c6672d24d | ||
![]() |
92cd6d51cb | ||
![]() |
759c31760e | ||
![]() |
00fef38119 | ||
![]() |
e40ed300d0 | ||
![]() |
2ab8b49b48 | ||
![]() |
75003eb741 | ||
![]() |
c17bfc5c17 | ||
![]() |
40a252d028 | ||
![]() |
02478d24cf | ||
![]() |
45932c7837 | ||
![]() |
3145b68bed | ||
![]() |
a84f7c5b18 | ||
![]() |
a9417fdf67 | ||
![]() |
b4204f197e | ||
![]() |
465fc448eb | ||
![]() |
918f15945b | ||
![]() |
c4a6ef42a6 | ||
![]() |
32d660c34b | ||
![]() |
61fab7ba24 | ||
![]() |
41357b551f | ||
![]() |
62dc79864d | ||
![]() |
c061888937 | ||
![]() |
031c7be4db | ||
![]() |
0916d178e7 | ||
![]() |
9b46692b8d | ||
![]() |
95f82760ce | ||
![]() |
8baa843ce3 | ||
![]() |
5fc91f6977 | ||
![]() |
cb39972d9b | ||
![]() |
50300fa7af | ||
![]() |
5749c8dcf7 | ||
![]() |
06b60b7372 | ||
![]() |
a3db3dba2d | ||
![]() |
917c5a5f1d | ||
![]() |
61febde9c1 | ||
![]() |
d4a8a3bdfa | ||
![]() |
5cc88a7759 | ||
![]() |
d4a638101a | ||
![]() |
087df4480e | ||
![]() |
7b4c958e48 | ||
![]() |
d5cb5c8546 | ||
![]() |
d36a57d680 | ||
![]() |
f0b1afe884 | ||
![]() |
435c55f72a | ||
![]() |
d02090db2c | ||
![]() |
dbf828c493 | ||
![]() |
965b642109 | ||
![]() |
14cf2454ae | ||
![]() |
a5e2134147 | ||
![]() |
63147ee1eb | ||
![]() |
1f237e6c54 | ||
![]() |
c4d15e0d2a | ||
![]() |
27d70632cd | ||
![]() |
55941a5627 | ||
![]() |
1c4f69a7fc | ||
![]() |
5d3d07d345 | ||
![]() |
909b9a75ae | ||
![]() |
eaa1c74a11 | ||
![]() |
d918f61c77 | ||
![]() |
14524283cc | ||
![]() |
6645868510 | ||
![]() |
a2d569b012 | ||
![]() |
d53e3ff46c | ||
![]() |
264946920a | ||
![]() |
c30b59ae81 | ||
![]() |
a36f2dcf41 | ||
![]() |
4b3626b5f2 | ||
![]() |
715020de46 | ||
![]() |
241fe5b51c | ||
![]() |
31e480ad05 | ||
![]() |
41654fbb60 | ||
![]() |
c39a2dd516 | ||
![]() |
571c2353ea | ||
![]() |
b8374ec5d2 | ||
![]() |
4117394250 | ||
![]() |
6f7307ae7b | ||
![]() |
737ea4ec6d | ||
![]() |
bae6f9c63e | ||
![]() |
aa4f080b3f | ||
![]() |
f950936baa | ||
![]() |
44c302fe0b | ||
![]() |
d4b981bcb3 | ||
![]() |
3cbe1cb6c5 | ||
![]() |
b23fc96ab2 | ||
![]() |
656254e64f | ||
![]() |
c71e298d4b | ||
![]() |
90cf515ff0 | ||
![]() |
2d2fafc58d | ||
![]() |
f2a0bfe651 | ||
![]() |
37f96413dd | ||
![]() |
b0a267bf47 | ||
![]() |
9d2b4cb1cc | ||
![]() |
cb00a41ff3 | ||
![]() |
079aba9012 | ||
![]() |
feca49e4f8 | ||
![]() |
eb8a0a2b7c | ||
![]() |
8dbfe965e9 | ||
![]() |
4d4aab6bd4 | ||
![]() |
a4c553a45a | ||
![]() |
53ff5c43f1 | ||
![]() |
a1da464618 | ||
![]() |
e6ee4cc0b1 | ||
![]() |
e272b10a90 | ||
![]() |
48336b7ed3 | ||
![]() |
a6cc4cc860 | ||
![]() |
4ad6628dcb | ||
![]() |
637975f517 | ||
![]() |
d4352c7a44 | ||
![]() |
74b1c195e3 | ||
![]() |
ef70472df7 | ||
![]() |
795eac22de | ||
![]() |
df33933fe5 | ||
![]() |
8e5f699bb2 | ||
![]() |
92eb4254fc | ||
![]() |
1587927660 | ||
![]() |
a1081ec450 | ||
![]() |
5fad452106 | ||
![]() |
baf4944f5f | ||
![]() |
8ad4698536 | ||
![]() |
599b687849 | ||
![]() |
e959c1e522 | ||
![]() |
af086995ed | ||
![]() |
dc26c50688 | ||
![]() |
5056da0f93 | ||
![]() |
a46b587900 | ||
![]() |
faa9c6cbb4 | ||
![]() |
c9d32c33b0 | ||
![]() |
a009407088 | ||
![]() |
65eeb4c8d9 | ||
![]() |
8b26129d55 | ||
![]() |
6737ace933 | ||
![]() |
259f19a558 | ||
![]() |
14d8db70f6 | ||
![]() |
ed3cd80bab | ||
![]() |
898c573f29 | ||
![]() |
015b34ff09 | ||
![]() |
62073a8fde | ||
![]() |
6e52643f8c | ||
![]() |
2449163ffb | ||
![]() |
fd7fb925a9 | ||
![]() |
ce7efbd9e1 | ||
![]() |
b404c8bf16 | ||
![]() |
8da43dd246 | ||
![]() |
e41f034bef | ||
![]() |
853c142291 | ||
![]() |
69242b3aa6 | ||
![]() |
4965ee84e4 | ||
![]() |
8f66aafe28 | ||
![]() |
789190962c | ||
![]() |
3599f694ba | ||
![]() |
1e71f6f44e | ||
![]() |
191226f070 | ||
![]() |
41dda9e041 | ||
![]() |
07ef2bf804 | ||
![]() |
e815e1a458 | ||
![]() |
9fe9f801dc | ||
![]() |
770bbce5d4 | ||
![]() |
587b830cb2 | ||
![]() |
3812cba029 | ||
![]() |
6f76cd105d | ||
![]() |
d536543344 | ||
![]() |
4853cf4c31 | ||
![]() |
a25af76bbb | ||
![]() |
a22b432d8a | ||
![]() |
fe45e0110a | ||
![]() |
1ace77f0b0 | ||
![]() |
0092c5f189 | ||
![]() |
e07c66ae68 | ||
![]() |
f29ca16c38 | ||
![]() |
1737a6c672 | ||
![]() |
132ccbea03 | ||
![]() |
3850a6dc60 | ||
![]() |
b91dfb539f | ||
![]() |
a418847399 | ||
![]() |
975e25fcf0 | ||
![]() |
2beebd6fc5 | ||
![]() |
7ba8160e8c | ||
![]() |
db409190ed | ||
![]() |
727518d1cf | ||
![]() |
589ecbf720 | ||
![]() |
04b597ab90 | ||
![]() |
b6dda50e58 | ||
![]() |
886e00a8d5 | ||
![]() |
0eedda0a6c | ||
![]() |
368cb1be5f | ||
![]() |
a7a50eaa39 | ||
![]() |
3b07268aeb | ||
![]() |
d96bc9c97f | ||
![]() |
e708143a28 | ||
![]() |
b4697d6354 | ||
![]() |
14a2756470 | ||
![]() |
16512bba25 | ||
![]() |
e6e799d145 | ||
![]() |
d94b78e6bf | ||
![]() |
38b4ec0c2f | ||
![]() |
1d9bd4725d | ||
![]() |
36aaebbf44 | ||
![]() |
fea5d886b7 | ||
![]() |
17cca9e8a1 | ||
![]() |
effab8d513 | ||
![]() |
eae26a2af8 | ||
![]() |
fc204577ed | ||
![]() |
36ff47a3fc | ||
![]() |
b92ebcfc59 | ||
![]() |
1be3273b79 | ||
![]() |
5e1b269fa5 | ||
![]() |
a63972a666 | ||
![]() |
d22c1f19ef | ||
![]() |
2fa7730a6b | ||
![]() |
8b1ed92466 | ||
![]() |
04c693b00b | ||
![]() |
74aa96d19d | ||
![]() |
5faa6f4235 | ||
![]() |
61a9c2427e | ||
![]() |
4f6b17934a | ||
![]() |
c5d4a3297f | ||
![]() |
df70ea5989 | ||
![]() |
5367a9aeec | ||
![]() |
4e30462216 | ||
![]() |
e0219411c6 | ||
![]() |
9b1840a5a4 | ||
![]() |
5182911acc | ||
![]() |
29ebb28f10 | ||
![]() |
30bb7e4f8e | ||
![]() |
5cc40c54b7 | ||
![]() |
264da24dae | ||
![]() |
790cd1353e | ||
![]() |
d3819c4e75 | ||
![]() |
ee50519e00 | ||
![]() |
33631a5984 | ||
![]() |
5701d0bf52 | ||
![]() |
97effd921e | ||
![]() |
124b7e2276 | ||
![]() |
5e568d0da9 | ||
![]() |
cfcb5fbd57 | ||
![]() |
63744b4047 | ||
![]() |
073c376fa6 | ||
![]() |
fa24329f89 | ||
![]() |
8cbbed8e79 | ||
![]() |
0f210d5c1a | ||
![]() |
ab4f5b8bb1 | ||
![]() |
a73990cdc5 | ||
![]() |
d3f814fc1f | ||
![]() |
0dffe940b5 | ||
![]() |
92a786c5e9 | ||
![]() |
57cd055fd1 | ||
![]() |
f4aae0b7a3 | ||
![]() |
2c56a08277 | ||
![]() |
87daa5b06c | ||
![]() |
2ea3f95fd1 | ||
![]() |
b749d9fbb4 | ||
![]() |
1c5927fd26 | ||
![]() |
98ff66209d | ||
![]() |
a65e05bff2 | ||
![]() |
bdd5cdcafe | ||
![]() |
d279c4e424 | ||
![]() |
5e7c3b7420 | ||
![]() |
4893b55d22 | ||
![]() |
7a8e2c95e0 | ||
![]() |
2e2d719669 | ||
![]() |
d269f2a2a6 | ||
![]() |
338ce755c9 | ||
![]() |
087be0dd8a | ||
![]() |
e50cc161b8 | ||
![]() |
164bbac651 | ||
![]() |
fe02674ce0 | ||
![]() |
6d097cce3f | ||
![]() |
9c8e7156e1 | ||
![]() |
bbca3ad209 | ||
![]() |
0291179e93 | ||
![]() |
05a467494b | ||
![]() |
dfcb4529f3 | ||
![]() |
85e704f32a | ||
![]() |
6870a48829 | ||
![]() |
0d769607af | ||
![]() |
ea8d596a9b | ||
![]() |
697780b3b6 | ||
![]() |
60d00bc944 | ||
![]() |
b79b519c34 | ||
![]() |
7aac5b0553 | ||
![]() |
55729cfcf7 | ||
![]() |
2b8369ae39 | ||
![]() |
c8fd9eb4b3 | ||
![]() |
7efc2605b1 | ||
![]() |
c5f4a93cc7 | ||
![]() |
3101c94cf0 | ||
![]() |
3b581a7f6d | ||
![]() |
4072cf7417 | ||
![]() |
37ab4a0faf | ||
![]() |
9161692c4b | ||
![]() |
d8601edef2 | ||
![]() |
c5abbafb5e | ||
![]() |
ff8c1bf9b2 | ||
![]() |
e528b2998a | ||
![]() |
c35e8ce40d | ||
![]() |
a8da6c5586 | ||
![]() |
470fe89684 | ||
![]() |
a63e6ed9b7 | ||
![]() |
d3178a7865 | ||
![]() |
a6cf08dfc6 | ||
![]() |
daa155d5a1 | ||
![]() |
92a19b6f36 | ||
![]() |
f07de29a92 | ||
![]() |
a1d8f4e022 | ||
![]() |
0eaedb80ed | ||
![]() |
ba4b224bf4 | ||
![]() |
d7431657cd | ||
![]() |
57ee2fe322 | ||
![]() |
5bc11228c4 | ||
![]() |
7d73e48c79 | ||
![]() |
5fc3399bce | ||
![]() |
8889706bfe | ||
![]() |
f5d732eeaf | ||
![]() |
1bac9445f2 | ||
![]() |
4d002e268e | ||
![]() |
6c8d0fdef8 | ||
![]() |
a80387a0a0 | ||
![]() |
29adb6d843 | ||
![]() |
85ce6b1e8d | ||
![]() |
0fe5fddbba | ||
![]() |
7eda0894e4 | ||
![]() |
b9ce9b2285 | ||
![]() |
fb52c5dc66 | ||
![]() |
f08249a613 | ||
![]() |
0f9556f4ec | ||
![]() |
4fd1b05021 | ||
![]() |
6912c12847 | ||
![]() |
2dd3ea7d18 | ||
![]() |
54f5faafac | ||
![]() |
e2a2f9ddc5 | ||
![]() |
0f4beae0f3 | ||
![]() |
7e488a112f | ||
![]() |
4522a44453 | ||
![]() |
984cf2d46d | ||
![]() |
ac0b0bb11c | ||
![]() |
d9467eca21 | ||
![]() |
0d2af0c26a | ||
![]() |
ef03a3a839 | ||
![]() |
ca53b93e64 | ||
![]() |
c162ddcdc3 | ||
![]() |
f6f9e41d11 | ||
![]() |
964e4c80ab | ||
![]() |
7377b75383 | ||
![]() |
cfa76c8c71 | ||
![]() |
053be3fab6 | ||
![]() |
9d023ae07c | ||
![]() |
25b12eab56 | ||
![]() |
adb388b120 | ||
![]() |
347302169c | ||
![]() |
ec957029a5 | ||
![]() |
1513e3803b | ||
![]() |
d9c5d24e20 | ||
![]() |
e4a90136f0 | ||
![]() |
96354678dc | ||
![]() |
b1b16dabec | ||
![]() |
fc9eaca653 | ||
![]() |
3b9a7fb2dd | ||
![]() |
26a0084515 | ||
![]() |
8d55bceaee | ||
![]() |
07bdeda466 | ||
![]() |
2ea03a928d | ||
![]() |
157e888532 | ||
![]() |
0b165c1566 | ||
![]() |
e3ca9b1755 | ||
![]() |
c3f351e6bd | ||
![]() |
e8ecc3f69f | ||
![]() |
4ff3a27fa2 | ||
![]() |
3003d244f9 | ||
![]() |
5280fc4d68 | ||
![]() |
23a712ccb8 | ||
![]() |
de14950965 | ||
![]() |
106ca0bc90 | ||
![]() |
12502f6571 | ||
![]() |
ea9659841e | ||
![]() |
6daaa43410 | ||
![]() |
e447db33c9 | ||
![]() |
446fe901c3 | ||
![]() |
610d82352e | ||
![]() |
f032849081 | ||
![]() |
39afe0a775 | ||
![]() |
de6382ce05 | ||
![]() |
e4b5ecf07d | ||
![]() |
56a7fc82d7 | ||
![]() |
78400582df | ||
![]() |
e9e9c5ff4f | ||
![]() |
26466108e9 | ||
![]() |
5ecdb045a5 | ||
![]() |
ce525005d8 | ||
![]() |
4a5af4b448 | ||
![]() |
da5b4dca7a | ||
![]() |
c06492cfc6 | ||
![]() |
399e79052d | ||
![]() |
65dfdd504a | ||
![]() |
71bef68c11 | ||
![]() |
189d58d88e | ||
![]() |
81dacdae0d | ||
![]() |
ebded850b5 | ||
![]() |
2c88c520f3 | ||
![]() |
6120106f40 | ||
![]() |
e754cb2973 | ||
![]() |
428bb0023f | ||
![]() |
b0272506b0 | ||
![]() |
90b572f292 | ||
![]() |
38cbbc9669 | ||
![]() |
823002ccba | ||
![]() |
cb1d38434e | ||
![]() |
e2bcb29257 | ||
![]() |
ed8296a55f | ||
![]() |
cc34704ffa | ||
![]() |
607c3af776 | ||
![]() |
815ac10d12 | ||
![]() |
e12f4d4562 | ||
![]() |
22f1f064fd | ||
![]() |
4a1b3550d3 | ||
![]() |
d293854714 | ||
![]() |
7bbf99948e | ||
![]() |
95561108e7 | ||
![]() |
c58c706f52 | ||
![]() |
2946d5af09 | ||
![]() |
5ef6f7ceed | ||
![]() |
c96f06f3c5 | ||
![]() |
0cb91affd0 | ||
![]() |
fa78d69634 | ||
![]() |
ae913184db | ||
![]() |
aa3981190f | ||
![]() |
6d908d610d | ||
![]() |
ed1bb6d88e | ||
![]() |
3c9e5f2342 | ||
![]() |
b657990bac | ||
![]() |
c3ecf9bdad | ||
![]() |
0f55b9aec8 | ||
![]() |
6c7784fbbe | ||
![]() |
ef98d74dd6 | ||
![]() |
c6716f9bac | ||
![]() |
36844eefef | ||
![]() |
5fc311f824 | ||
![]() |
42e2c52c61 | ||
![]() |
c0719025c4 | ||
![]() |
5a9a30af65 | ||
![]() |
22c56b91b9 | ||
![]() |
992f65703a | ||
![]() |
74a9f2a155 | ||
![]() |
b9618df069 | ||
![]() |
ce24356f17 | ||
![]() |
285cbb7a47 | ||
![]() |
06824f201d | ||
![]() |
dfbb7ffcf6 | ||
![]() |
02afc89443 | ||
![]() |
9b78582e86 | ||
![]() |
9aa6ff4fd5 | ||
![]() |
0dbbedb2f7 | ||
![]() |
b7ed1e6540 | ||
![]() |
fb671b6cd1 | ||
![]() |
51bb751155 |
354 changed files with 26925 additions and 18498 deletions
140
.eslintrc.json
140
.eslintrc.json
|
@ -1,140 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"es2021": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:all",
|
|
||||||
"plugin:jsdoc/recommended",
|
|
||||||
"plugin:n/recommended",
|
|
||||||
"plugin:regexp/recommended",
|
|
||||||
"plugin:unicorn/all"
|
|
||||||
],
|
|
||||||
"ignorePatterns": [
|
|
||||||
"demo/markdown-it.min.js",
|
|
||||||
"demo/markdownlint-browser.js",
|
|
||||||
"demo/markdownlint-browser.min.js",
|
|
||||||
"demo/micromark-browser.js",
|
|
||||||
"demo/micromark-html-browser.js",
|
|
||||||
"example/typescript/type-check.js",
|
|
||||||
"micromark/micromark.cjs",
|
|
||||||
"micromark/micromark.dev.cjs",
|
|
||||||
"micromark/micromark-browser.js",
|
|
||||||
"micromark/micromark-browser.dev.js",
|
|
||||||
"test-repos/"
|
|
||||||
],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"demo/*.js"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"jsdoc/require-jsdoc": "off",
|
|
||||||
"unicorn/prefer-query-selector": "off",
|
|
||||||
"unicorn/prefer-add-event-listener": "off",
|
|
||||||
"no-console": "off",
|
|
||||||
"no-invalid-this": "off",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"no-var": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"example/*.js"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"n/no-extraneous-require": "off",
|
|
||||||
"n/no-missing-require": "off",
|
|
||||||
"no-console": "off",
|
|
||||||
"no-invalid-this": "off",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"object-property-newline": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"test/rules/**/*.js"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"jsdoc/valid-types": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"**/*.mjs"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2022,
|
|
||||||
"sourceType": "module"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2021,
|
|
||||||
"sourceType": "script"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"jsdoc",
|
|
||||||
"n",
|
|
||||||
"regexp",
|
|
||||||
"unicorn"
|
|
||||||
],
|
|
||||||
"reportUnusedDisableDirectives": true,
|
|
||||||
"rules": {
|
|
||||||
"capitalized-comments": "off",
|
|
||||||
"complexity": "off",
|
|
||||||
"func-style": "off",
|
|
||||||
"id-length": "off",
|
|
||||||
"jsdoc/tag-lines": ["error", "never", {"startLines":1}],
|
|
||||||
"logical-assignment-operators": "off",
|
|
||||||
"max-depth": "off",
|
|
||||||
"max-lines-per-function": "off",
|
|
||||||
"max-lines": "off",
|
|
||||||
"max-params": "off",
|
|
||||||
"max-statements": "off",
|
|
||||||
"multiline-comment-style": ["error", "separate-lines"],
|
|
||||||
"no-empty-function": "off",
|
|
||||||
"no-implicit-coercion": "off",
|
|
||||||
"no-magic-numbers": "off",
|
|
||||||
"no-param-reassign": "off",
|
|
||||||
"no-plusplus": "off",
|
|
||||||
"no-ternary": "off",
|
|
||||||
"no-undefined": "off",
|
|
||||||
"object-shorthand": "off",
|
|
||||||
"one-var": "off",
|
|
||||||
"prefer-arrow-callback": "off",
|
|
||||||
"prefer-destructuring": "off",
|
|
||||||
"prefer-named-capture-group": "off",
|
|
||||||
"prefer-template": "off",
|
|
||||||
"regexp/no-super-linear-backtracking": "off",
|
|
||||||
"require-unicode-regexp": "off",
|
|
||||||
"sort-imports": "off",
|
|
||||||
"sort-keys": "off",
|
|
||||||
"unicorn/better-regex": "off",
|
|
||||||
"unicorn/consistent-function-scoping": "off",
|
|
||||||
"unicorn/filename-case": "off",
|
|
||||||
"unicorn/no-array-callback-reference": "off",
|
|
||||||
"unicorn/no-keyword-prefix": "off",
|
|
||||||
"unicorn/no-new-array": "off",
|
|
||||||
"unicorn/no-null": "off",
|
|
||||||
"unicorn/no-useless-undefined": "off",
|
|
||||||
"unicorn/prefer-at": "off",
|
|
||||||
"unicorn/prefer-module": "off",
|
|
||||||
"unicorn/prefer-string-replace-all": "off",
|
|
||||||
"unicorn/prefer-string-slice": "off",
|
|
||||||
"unicorn/prefer-switch": "off",
|
|
||||||
"unicorn/prevent-abbreviations": "off",
|
|
||||||
"unicorn/switch-case-braces": ["error", "avoid"],
|
|
||||||
"vars-on-top": "off"
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"jsdoc": {
|
|
||||||
"preferredTypes": {
|
|
||||||
"object": "Object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
blank_issues_enabled: false
|
40
.github/ISSUE_TEMPLATE/markdownlint-issue-template.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/markdownlint-issue-template.md
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
name: markdownlint issue template
|
||||||
|
about: This template helps report issues with the markdownlint family of tools.
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Thank you for taking the time to report an issue!
|
||||||
|
|
||||||
|
When deciding where to open an issue, please note there are multiple projects under the markdownlint umbrella:
|
||||||
|
|
||||||
|
- https://github.com/DavidAnson/markdownlint : This is the core JavaScript/Node.js library and is used by other tools. Most issues with implementation and rule behavior belong here.
|
||||||
|
- https://github.com/igorshubovych/markdownlint-cli : This is the original CLI for markdownlint. Issues specific to CLI belong here.
|
||||||
|
- https://github.com/DavidAnson/markdownlint-cli2 : This is a newer CLI for markdownlint and is used by other tools. Issues specific to CLI2 belong here.
|
||||||
|
- https://github.com/DavidAnson/vscode-markdownlint : This is the Visual Studio Code extension for markdownlint. Issues specific to VS Code belong here.
|
||||||
|
- https://github.com/DavidAnson/markdownlint-cli2-action : This is a GitHub Action for markdownlint. Issues specific to the Action belong here.
|
||||||
|
- https://github.com/markdownlint/markdownlint : This is the original markdownlint implementation for Ruby. All Ruby-related issues belong here.
|
||||||
|
|
||||||
|
Before creating an issue, it's a good practice to search existing issues for something similar. If your issue has already been reported, please update the existing one with any new information. It's also good to review the documentation for any relevant details.
|
||||||
|
|
||||||
|
When describing an issue, the following information is helpful:
|
||||||
|
|
||||||
|
- What did you do?
|
||||||
|
- What did you expect to happen?
|
||||||
|
- What actually happened?
|
||||||
|
- What messages or errors were there?
|
||||||
|
- How can the issue be reproduced?
|
||||||
|
- What version were you using?
|
||||||
|
- What operating system were you using?
|
||||||
|
|
||||||
|
The simplest demonstration of a problem is the most helpful. Small examples can be pasted into the issue description. (Be sure to paste as code so GitHub doesn't render the example in Markdown.) For larger examples, linking to a repository or file is more appropriate.
|
||||||
|
|
||||||
|
Before proposing a new rule, please review the existing suggestions: https://github.com/DavidAnson/markdownlint/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+rule%22
|
||||||
|
|
||||||
|
Thank you!
|
||||||
|
|
||||||
|
-->
|
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
@ -11,9 +11,3 @@ updates:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
target-branch: "next"
|
target-branch: "next"
|
||||||
versioning-strategy: "increase"
|
versioning-strategy: "increase"
|
||||||
- package-ecosystem: "npm"
|
|
||||||
directory: "/micromark"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
target-branch: "next"
|
|
||||||
versioning-strategy: "increase"
|
|
||||||
|
|
5
.github/dictionary.txt
vendored
5
.github/dictionary.txt
vendored
|
@ -16,6 +16,7 @@ blockquotes
|
||||||
Boostnote
|
Boostnote
|
||||||
br
|
br
|
||||||
br_spaces
|
br_spaces
|
||||||
|
bundler
|
||||||
changelog
|
changelog
|
||||||
Changelog
|
Changelog
|
||||||
changelogs
|
changelogs
|
||||||
|
@ -30,14 +31,17 @@ Config
|
||||||
config.
|
config.
|
||||||
CVE-\d+-\d+
|
CVE-\d+-\d+
|
||||||
docs-util
|
docs-util
|
||||||
|
ECMAScript
|
||||||
ES2015
|
ES2015
|
||||||
ES6
|
ES6
|
||||||
ESLint
|
ESLint
|
||||||
eslint-plugin-markdownlint
|
eslint-plugin-markdownlint
|
||||||
first-line-h1
|
first-line-h1
|
||||||
|
flymake-markdownlint-cli2
|
||||||
formatter
|
formatter
|
||||||
fs
|
fs
|
||||||
GFM
|
GFM
|
||||||
|
github
|
||||||
globbing
|
globbing
|
||||||
grunt-markdownlint
|
grunt-markdownlint
|
||||||
h1
|
h1
|
||||||
|
@ -93,6 +97,7 @@ setext_with_atx_closed
|
||||||
setext-style
|
setext-style
|
||||||
single-h1
|
single-h1
|
||||||
sublist
|
sublist
|
||||||
|
subpath
|
||||||
Super-Linter
|
Super-Linter
|
||||||
TestCafe
|
TestCafe
|
||||||
TOML
|
TOML
|
||||||
|
|
4
.github/workflows/checkers.yml
vendored
4
.github/workflows/checkers.yml
vendored
|
@ -13,9 +13,9 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: JustinBeckwith/linkinator-action@v1.10.4
|
- uses: JustinBeckwith/linkinator-action@v1.11.0
|
||||||
with:
|
with:
|
||||||
linksToSkip: '^https://github.com/.*/search\?q= ^https://opensource.org/ ^https://unix.stackexchange.com/'
|
linksToSkip: '^https://github.com/ ^https://opensource.org/ ^https://unix.stackexchange.com/'
|
||||||
paths: '*.md doc/*.md helpers/*.md'
|
paths: '*.md doc/*.md helpers/*.md'
|
||||||
timeout: 60000
|
timeout: 60000
|
||||||
|
|
||||||
|
|
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
|
@ -11,14 +11,13 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
node-version: [ 18, 20 ]
|
node-version: [ 20, 22, 23 ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -30,11 +29,21 @@ jobs:
|
||||||
run: npm install --no-package-lock
|
run: npm install --no-package-lock
|
||||||
- name: Run CI Tests
|
- name: Run CI Tests
|
||||||
run: npm run ci
|
run: npm run ci
|
||||||
- name: Install markdownlint-micromark Dependencies
|
|
||||||
run: npm run install-micromark
|
pnpm:
|
||||||
- name: Build markdownlint-micromark
|
runs-on: ubuntu-latest
|
||||||
run: npm run build-micromark
|
|
||||||
- name: Install local markdownlint-micromark
|
steps:
|
||||||
run: npm install --no-package-lock ./micromark
|
- uses: actions/checkout@v4
|
||||||
- name: Test with local markdownlint-micromark
|
- name: Use pnpm latest
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
- name: Use Node.js latest
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: latest
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
- name: Run CI Tests
|
||||||
run: npm test
|
run: npm test
|
||||||
|
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,16 +1,10 @@
|
||||||
coverage
|
coverage
|
||||||
demo/markdown-it.min.js
|
demo/markdown-it.min.js
|
||||||
|
demo/markdownlint-browser.js
|
||||||
demo/markdownlint-browser.min.js
|
demo/markdownlint-browser.min.js
|
||||||
demo/micromark-browser.js
|
|
||||||
demo/micromark-html-browser.js
|
|
||||||
micromark/micromark.cjs
|
|
||||||
micromark/micromark.dev.cjs
|
|
||||||
micromark/micromark-browser.js
|
|
||||||
micromark/micromark-browser.dev.js
|
|
||||||
micromark/micromark-html-browser.js
|
|
||||||
micromark/micromark-html-browser.dev.js
|
|
||||||
node_modules
|
node_modules
|
||||||
!test/node_modules
|
!test/node_modules
|
||||||
|
!test/rules/node_modules
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
test-repos
|
test-repos
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
"emphasis-style": {
|
"emphasis-style": {
|
||||||
"style": "asterisk"
|
"style": "asterisk"
|
||||||
},
|
},
|
||||||
|
"extended-ascii": {
|
||||||
|
"ascii-only": true
|
||||||
|
},
|
||||||
"fenced-code-language": {
|
"fenced-code-language": {
|
||||||
"allowed_languages": [
|
"allowed_languages": [
|
||||||
"bash",
|
"bash",
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
.eslintignore
|
.eslintignore
|
||||||
.eslintrc.json
|
|
||||||
.github
|
.github
|
||||||
.markdownlint.json
|
.markdownlint.json
|
||||||
.npmrc
|
.npmrc
|
||||||
.vscode
|
.vscode
|
||||||
coverage
|
coverage
|
||||||
demo/*
|
demo/*
|
||||||
!demo/markdownlint-browser.js
|
|
||||||
doc-build
|
doc-build
|
||||||
|
eslint.config.mjs
|
||||||
example
|
example
|
||||||
micromark
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
schema/*.js
|
schema/*.mjs
|
||||||
scripts
|
scripts
|
||||||
test
|
test
|
||||||
test-repos
|
test-repos
|
||||||
|
|
1
.npmrc
1
.npmrc
|
@ -1,2 +1,3 @@
|
||||||
|
engine-strict=true
|
||||||
ignore-scripts=true
|
ignore-scripts=true
|
||||||
package-lock=false
|
package-lock=false
|
||||||
|
|
59
CHANGELOG.md
59
CHANGELOG.md
|
@ -1,5 +1,64 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.38.0
|
||||||
|
|
||||||
|
- Add MD059/descriptive-link-text
|
||||||
|
- Improve MD025/MD027/MD036/MD038/MD041/MD043/MD045/MD051/MD052
|
||||||
|
- `markdown-it` parser no longer a production dependency (breaking change)
|
||||||
|
- Add `markdownItFactory` option, remove `markdownItPlugins` option
|
||||||
|
- Remove support for end-of-life Node version 18
|
||||||
|
- Improve performance
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
|
## 0.37.4
|
||||||
|
|
||||||
|
- Stop using `module.createRequire`, export `resolveModule`
|
||||||
|
|
||||||
|
## 0.37.3
|
||||||
|
|
||||||
|
- Tweak `package.json` dependencies to work with `pnpm`
|
||||||
|
|
||||||
|
## 0.37.2
|
||||||
|
|
||||||
|
- Add subpath imports for overriding default bundler behavior
|
||||||
|
- Improve MD032
|
||||||
|
|
||||||
|
## 0.37.1
|
||||||
|
|
||||||
|
- Add support for "browser" condition (as used by webpack)
|
||||||
|
|
||||||
|
## 0.37.0
|
||||||
|
|
||||||
|
- Convert module to ECMAScript (breaking change)
|
||||||
|
- <https://nodejs.org/docs/latest/api/esm.html>
|
||||||
|
- <https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c>
|
||||||
|
- Convert module to named exports (breaking change)
|
||||||
|
|
||||||
|
## 0.36.1
|
||||||
|
|
||||||
|
- Fix behavior of MD054
|
||||||
|
|
||||||
|
## 0.36.0
|
||||||
|
|
||||||
|
- Improve MD051
|
||||||
|
- Move `applyFix` and `applyFixes` from helpers to core
|
||||||
|
- Make `micromark` parser available to custom rules
|
||||||
|
- Introduce `./micromark` helpers exports
|
||||||
|
- Update custom/rule documentation
|
||||||
|
- Improve performance
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
|
## 0.35.0
|
||||||
|
|
||||||
|
- Add MD058/blanks-around-tables
|
||||||
|
- Use `micromark` in MD001/MD003/MD009/MD010/MD013/MD014/MD019/MD021/MD023/
|
||||||
|
MD024/MD025/MD039/MD042/MD043
|
||||||
|
- Improve MD018/MD020/MD031/MD034/MD044
|
||||||
|
- `markdown-it` parser no longer invoked by default
|
||||||
|
- Add strict version of JSON schema
|
||||||
|
- Improve performance
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
## 0.34.0
|
## 0.34.0
|
||||||
|
|
||||||
- Use `micromark` in MD027/MD028/MD036/MD040/MD041/MD046/MD048
|
- Use `micromark` in MD027/MD028/MD036/MD040/MD041/MD046/MD048
|
||||||
|
|
|
@ -14,8 +14,8 @@ Match the coding style of the files you edit. Although everyone has their own
|
||||||
preferences and opinions, a pull request is not the right forum to debate them.
|
preferences and opinions, a pull request is not the right forum to debate them.
|
||||||
|
|
||||||
Do not add new [`dependencies` to `package.json`][dependencies]. The Markdown
|
Do not add new [`dependencies` to `package.json`][dependencies]. The Markdown
|
||||||
parsers [`markdown-it`][markdown-it] and [`micromark`][micromark] are the
|
parser [`micromark`][micromark] (and its extensions) is this project's only
|
||||||
project's only dependencies.
|
dependency.
|
||||||
|
|
||||||
Package versions for `dependencies` and `devDependencies` should be specified
|
Package versions for `dependencies` and `devDependencies` should be specified
|
||||||
exactly (also known as "pinning"). The short explanation is that doing otherwise
|
exactly (also known as "pinning"). The short explanation is that doing otherwise
|
||||||
|
@ -85,7 +85,6 @@ Thank you!
|
||||||
[custom-rules]: doc/CustomRules.md
|
[custom-rules]: doc/CustomRules.md
|
||||||
[dependencies]: https://docs.npmjs.com/files/package.json#dependencies
|
[dependencies]: https://docs.npmjs.com/files/package.json#dependencies
|
||||||
[example-com]: https://en.wikipedia.org/wiki/Example.com
|
[example-com]: https://en.wikipedia.org/wiki/Example.com
|
||||||
[markdown-it]: https://www.npmjs.com/package/markdown-it
|
|
||||||
[micromark]: https://www.npmjs.com/package/micromark
|
[micromark]: https://www.npmjs.com/package/micromark
|
||||||
[new-rule]: https://github.com/DavidAnson/markdownlint/labels/new%20rule
|
[new-rule]: https://github.com/DavidAnson/markdownlint/labels/new%20rule
|
||||||
[npm-scripts]: https://docs.npmjs.com/misc/scripts
|
[npm-scripts]: https://docs.npmjs.com/misc/scripts
|
||||||
|
|
373
README.md
373
README.md
|
@ -13,22 +13,31 @@ npm install markdownlint --save-dev
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The [Markdown](https://en.wikipedia.org/wiki/Markdown) markup language
|
The [Markdown][markdown] markup language is designed to be easy to read, write,
|
||||||
is designed to be easy to read, write, and understand. It succeeds -
|
and understand. It succeeds - and its flexibility is both a benefit and a
|
||||||
and its flexibility is both a benefit and a drawback. Many styles are
|
drawback. Many styles are possible, so formatting can be inconsistent; some
|
||||||
possible, so formatting can be inconsistent. Some constructs don't
|
constructs don't work well in all parsers and should be avoided.
|
||||||
work well in all parsers and should be avoided. The
|
|
||||||
[CommonMark](https://commonmark.org/) specification standardizes
|
|
||||||
parsers - but not authors.
|
|
||||||
|
|
||||||
`markdownlint` is a
|
`markdownlint` is a [static analysis][static-analysis] tool for
|
||||||
[static analysis](https://en.wikipedia.org/wiki/Static_program_analysis)
|
[Node.js][nodejs] with a library of rules to enforce standards and consistency
|
||||||
tool for [Node.js](https://nodejs.org/) with a library of rules
|
for Markdown files. It was inspired by - and heavily influenced by - Mark
|
||||||
to enforce standards and consistency for Markdown files. It was
|
Harrison's [markdownlint][markdownlint-ruby] for Ruby. The initial rules, rule
|
||||||
inspired by - and heavily influenced by - Mark Harrison's
|
documentation, and test cases came from that project.
|
||||||
[markdownlint](https://github.com/markdownlint/markdownlint) for
|
|
||||||
[Ruby](https://www.ruby-lang.org/). The initial rules, rule documentation,
|
`markdownlint` uses the [`micromark` parser][micromark] and honors the
|
||||||
and test cases came directly from that project.
|
[CommonMark][commonmark] specification for Markdown. It additionally supports
|
||||||
|
popular [GitHub Flavored Markdown (GFM)][gfm] syntax like autolinks and tables
|
||||||
|
as well as directives, footnotes, and math syntax - all implemented by
|
||||||
|
[`micromark` extensions][micromark-extensions].
|
||||||
|
|
||||||
|
[commonmark]: https://commonmark.org/
|
||||||
|
[gfm]: https://github.github.com/gfm/
|
||||||
|
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
||||||
|
[markdownlint-ruby]: https://github.com/markdownlint/markdownlint
|
||||||
|
[micromark]: https://github.com/micromark/micromark
|
||||||
|
[micromark-extensions]: https://github.com/micromark/micromark?tab=readme-ov-file#list-of-extensions
|
||||||
|
[nodejs]: https://nodejs.org/
|
||||||
|
[static-analysis]: https://en.wikipedia.org/wiki/Static_program_analysis
|
||||||
|
|
||||||
### Related
|
### Related
|
||||||
|
|
||||||
|
@ -46,6 +55,7 @@ and test cases came directly from that project.
|
||||||
- [vscode-markdownlint extension for VS Code][vscode-markdownlint]
|
- [vscode-markdownlint extension for VS Code][vscode-markdownlint]
|
||||||
- [Sublime Text markdownlint for Sublime Text][sublimelinter]
|
- [Sublime Text markdownlint for Sublime Text][sublimelinter]
|
||||||
- [coc-markdownlint extension for Vim/Neovim][coc]
|
- [coc-markdownlint extension for Vim/Neovim][coc]
|
||||||
|
- [flymake-markdownlint-cli2 extension for Emacs][emacs-flymake]
|
||||||
- Tooling
|
- Tooling
|
||||||
- [eslint-plugin-markdownlint for the ESLint analyzer][eslint-plugin]
|
- [eslint-plugin-markdownlint for the ESLint analyzer][eslint-plugin]
|
||||||
- [grunt-markdownlint for the Grunt task runner][grunt-markdownlint]
|
- [grunt-markdownlint for the Grunt task runner][grunt-markdownlint]
|
||||||
|
@ -56,6 +66,7 @@ and test cases came directly from that project.
|
||||||
|
|
||||||
[cake]: https://github.com/cake-contrib/Cake.Markdownlint
|
[cake]: https://github.com/cake-contrib/Cake.Markdownlint
|
||||||
[coc]: https://github.com/fannheyward/coc-markdownlint
|
[coc]: https://github.com/fannheyward/coc-markdownlint
|
||||||
|
[emacs-flymake]: https://github.com/ewilderj/flymake-markdownlint-cli2
|
||||||
[eslint-plugin]: https://github.com/paweldrozd/eslint-plugin-markdownlint
|
[eslint-plugin]: https://github.com/paweldrozd/eslint-plugin-markdownlint
|
||||||
[grunt-markdownlint]: https://github.com/sagiegurari/grunt-markdownlint
|
[grunt-markdownlint]: https://github.com/sagiegurari/grunt-markdownlint
|
||||||
[markdownlint-cli]: https://github.com/igorshubovych/markdownlint-cli
|
[markdownlint-cli]: https://github.com/igorshubovych/markdownlint-cli
|
||||||
|
@ -66,7 +77,7 @@ and test cases came directly from that project.
|
||||||
[markdownlint-problem-matcher]: https://github.com/xt0rted/markdownlint-problem-matcher
|
[markdownlint-problem-matcher]: https://github.com/xt0rted/markdownlint-problem-matcher
|
||||||
[nodejs-extensions]: https://github.com/Lombiq/NodeJs-Extensions
|
[nodejs-extensions]: https://github.com/Lombiq/NodeJs-Extensions
|
||||||
[rubygems-mdl]: https://rubygems.org/gems/mdl
|
[rubygems-mdl]: https://rubygems.org/gems/mdl
|
||||||
[sublimelinter]: https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint
|
[sublimelinter]: https://github.com/jonlabelle/SublimeLinter-contrib-markdownlint
|
||||||
[super-linter]: https://github.com/super-linter/super-linter
|
[super-linter]: https://github.com/super-linter/super-linter
|
||||||
[vscode-markdownlint]: https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint
|
[vscode-markdownlint]: https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint
|
||||||
|
|
||||||
|
@ -136,6 +147,8 @@ playground for learning and exploring.
|
||||||
- **[MD054](doc/md054.md)** *link-image-style* - Link and image style
|
- **[MD054](doc/md054.md)** *link-image-style* - Link and image style
|
||||||
- **[MD055](doc/md055.md)** *table-pipe-style* - Table pipe style
|
- **[MD055](doc/md055.md)** *table-pipe-style* - Table pipe style
|
||||||
- **[MD056](doc/md056.md)** *table-column-count* - Table column count
|
- **[MD056](doc/md056.md)** *table-column-count* - Table column count
|
||||||
|
- **[MD058](doc/md058.md)** *blanks-around-tables* - Tables should be surrounded by blank lines
|
||||||
|
- **[MD059](doc/md059.md)** *descriptive-link-text* - Link text should be descriptive
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
<!-- markdownlint-restore -->
|
||||||
|
|
||||||
|
@ -155,7 +168,7 @@ To implement your own rules, refer to [CustomRules.md](doc/CustomRules.md).
|
||||||
Tags group related rules and can be used to enable/disable multiple
|
Tags group related rules and can be used to enable/disable multiple
|
||||||
rules at once.
|
rules at once.
|
||||||
|
|
||||||
- **`accessibility`** - `MD045`
|
- **`accessibility`** - `MD045`, `MD059`
|
||||||
- **`atx`** - `MD018`, `MD019`
|
- **`atx`** - `MD018`, `MD019`
|
||||||
- **`atx_closed`** - `MD020`, `MD021`
|
- **`atx_closed`** - `MD020`, `MD021`
|
||||||
- **`blank_lines`** - `MD012`, `MD022`, `MD031`, `MD032`, `MD047`
|
- **`blank_lines`** - `MD012`, `MD022`, `MD031`, `MD032`, `MD047`
|
||||||
|
@ -173,11 +186,11 @@ rules at once.
|
||||||
- **`language`** - `MD040`
|
- **`language`** - `MD040`
|
||||||
- **`line_length`** - `MD013`
|
- **`line_length`** - `MD013`
|
||||||
- **`links`** - `MD011`, `MD034`, `MD039`, `MD042`, `MD051`, `MD052`, `MD053`,
|
- **`links`** - `MD011`, `MD034`, `MD039`, `MD042`, `MD051`, `MD052`, `MD053`,
|
||||||
`MD054`
|
`MD054`, `MD059`
|
||||||
- **`ol`** - `MD029`, `MD030`, `MD032`
|
- **`ol`** - `MD029`, `MD030`, `MD032`
|
||||||
- **`spaces`** - `MD018`, `MD019`, `MD020`, `MD021`, `MD023`
|
- **`spaces`** - `MD018`, `MD019`, `MD020`, `MD021`, `MD023`
|
||||||
- **`spelling`** - `MD044`
|
- **`spelling`** - `MD044`
|
||||||
- **`table`** - `MD055`, `MD056`
|
- **`table`** - `MD055`, `MD056`, `MD058`
|
||||||
- **`ul`** - `MD004`, `MD005`, `MD007`, `MD030`, `MD032`
|
- **`ul`** - `MD004`, `MD005`, `MD007`, `MD030`, `MD032`
|
||||||
- **`url`** - `MD034`
|
- **`url`** - `MD034`
|
||||||
- **`whitespace`** - `MD009`, `MD010`, `MD012`, `MD027`, `MD028`, `MD030`,
|
- **`whitespace`** - `MD009`, `MD010`, `MD012`, `MD027`, `MD028`, `MD030`,
|
||||||
|
@ -302,42 +315,41 @@ alternate formats.
|
||||||
|
|
||||||
### Linting
|
### Linting
|
||||||
|
|
||||||
Standard asynchronous API:
|
Asynchronous API via `import { lint } from "markdownlint/async"`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/**
|
/**
|
||||||
* Lint specified Markdown files.
|
* Lint specified Markdown files.
|
||||||
*
|
*
|
||||||
* @param {Options} options Configuration options.
|
* @param {Options | null} options Configuration options.
|
||||||
* @param {LintCallback} callback Callback (err, result) function.
|
* @param {LintCallback} callback Callback (err, result) function.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function markdownlint(options, callback) { ... }
|
function lint(options, callback) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
Synchronous API (for build scripts, etc.):
|
Synchronous API via `import { lint } from "markdownlint/sync"`:
|
||||||
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* Lint specified Markdown files synchronously.
|
|
||||||
*
|
|
||||||
* @param {Options} options Configuration options.
|
|
||||||
* @returns {LintResults} Results object.
|
|
||||||
*/
|
|
||||||
function markdownlint.sync(options) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
Promise API (in the `promises` namespace like Node.js's
|
|
||||||
[`fs` Promises API](https://nodejs.org/api/fs.html#fs_fs_promises_api)):
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/**
|
/**
|
||||||
* Lint specified Markdown files.
|
* Lint specified Markdown files.
|
||||||
*
|
*
|
||||||
* @param {Options} options Configuration options.
|
* @param {Options | null} options Configuration options.
|
||||||
|
* @returns {LintResults} Results object.
|
||||||
|
*/
|
||||||
|
function lint(options) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Promise API via `import { lint } from "markdownlint/promise"`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/**
|
||||||
|
* Lint specified Markdown files.
|
||||||
|
*
|
||||||
|
* @param {Options | null} options Configuration options.
|
||||||
* @returns {Promise<LintResults>} Results object.
|
* @returns {Promise<LintResults>} Results object.
|
||||||
*/
|
*/
|
||||||
function markdownlint(options) { ... }
|
function lint(options) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
#### options
|
#### options
|
||||||
|
@ -353,16 +365,16 @@ Type: `Object` mapping `String` to `Boolean | Object`
|
||||||
|
|
||||||
Configures the rules to use.
|
Configures the rules to use.
|
||||||
|
|
||||||
Object keys are rule names or aliases and values are the rule's configuration.
|
Object keys are rule names/aliases; object values are the rule's configuration.
|
||||||
The value `false` disables a rule, `true` enables its default configuration,
|
The value `false` disables a rule, `true` enables its default configuration,
|
||||||
and passing an object customizes its settings. Setting the special `default`
|
and passing an object value customizes that rule. Setting the special `default`
|
||||||
rule to `true` or `false` includes/excludes all rules by default. Enabling
|
rule to `true` or `false` includes/excludes all rules by default. In the absence
|
||||||
or disabling a tag name (ex: `whitespace`) affects all rules having that
|
of a configuration object, all rules are enabled. Enabling or disabling a tag
|
||||||
tag.
|
name (ex: `whitespace`) affects all rules having that tag.
|
||||||
|
|
||||||
The `default` rule is applied first, then keys are processed in order
|
The `default` rule is applied first, then keys are processed in order from top
|
||||||
from top to bottom with later values overriding earlier ones. Keys
|
to bottom with later values overriding earlier ones. Keys (including rule names,
|
||||||
(including rule names, aliases, tags, and `default`) are not case-sensitive.
|
aliases, tags, and `default`) are not case-sensitive.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -409,9 +421,9 @@ object.
|
||||||
See [ValidatingConfiguration.md](schema/ValidatingConfiguration.md) for ways to
|
See [ValidatingConfiguration.md](schema/ValidatingConfiguration.md) for ways to
|
||||||
use the JSON Schema to validate configuration.
|
use the JSON Schema to validate configuration.
|
||||||
|
|
||||||
For more advanced scenarios, styles can reference and extend other styles.
|
For more advanced scenarios, styles can reference and build upon other styles
|
||||||
The `readConfig` and `readConfigSync` functions can be used to read such
|
via the `extends` keyword and a file path or (installed) package name. The
|
||||||
styles.
|
`readConfig` function can be used to read such aggregate styles from code.
|
||||||
|
|
||||||
For example, assuming a `base.json` configuration file:
|
For example, assuming a `base.json` configuration file:
|
||||||
|
|
||||||
|
@ -515,7 +527,7 @@ specify a custom `RegExp` or use the value `null` to disable the feature.
|
||||||
The default value:
|
The default value:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/((^---\s*$[\s\S]+?^---\s*)|(^\+\+\+\s*$[\s\S]+?^(\+\+\+|\.\.\.)\s*)|(^\{\s*$[\s\S]+?^\}\s*))(\r\n|\r|\n|$)/m
|
/((^---[^\S\r\n\u2028\u2029]*$[\s\S]+?^---\s*)|(^\+\+\+[^\S\r\n\u2028\u2029]*$[\s\S]+?^(\+\+\+|\.\.\.)\s*)|(^\{[^\S\r\n\u2028\u2029]*$[\s\S]+?^\}\s*))(\r\n|\r|\n|$)/m
|
||||||
```
|
```
|
||||||
|
|
||||||
Ignores [YAML](https://en.wikipedia.org/wiki/YAML),
|
Ignores [YAML](https://en.wikipedia.org/wiki/YAML),
|
||||||
|
@ -537,7 +549,7 @@ Type: `Object` implementing the [file system API][node-fs-api]
|
||||||
|
|
||||||
In advanced scenarios, it may be desirable to bypass the default file system
|
In advanced scenarios, it may be desirable to bypass the default file system
|
||||||
API. If a custom file system implementation is provided, `markdownlint` will use
|
API. If a custom file system implementation is provided, `markdownlint` will use
|
||||||
that instead of invoking `require("fs")`.
|
that instead of using `node:fs`.
|
||||||
|
|
||||||
Note: The only methods called are `readFile` and `readFileSync`.
|
Note: The only methods called are `readFile` and `readFileSync`.
|
||||||
|
|
||||||
|
@ -558,22 +570,44 @@ This setting can be useful in the presence of (custom) rules that encounter
|
||||||
unexpected syntax and fail. By enabling this option, the linting process
|
unexpected syntax and fail. By enabling this option, the linting process
|
||||||
is allowed to continue and report any violations that were found.
|
is allowed to continue and report any violations that were found.
|
||||||
|
|
||||||
##### options.markdownItPlugins
|
##### options.markdownItFactory
|
||||||
|
|
||||||
Type: `Array` of `Array` of `Function` and plugin parameters
|
Type: `Function` returning an instance of a [`markdown-it` parser][markdown-it]
|
||||||
|
|
||||||
Specifies additional [markdown-it plugins][markdown-it-plugin] to use when
|
Provides a factory function for creating instances of the `markdown-it` parser.
|
||||||
parsing input. Plugins can be used to support additional syntax and features for
|
|
||||||
advanced scenarios.
|
|
||||||
|
|
||||||
[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin
|
Previous versions of the `markdownlint` library declared `markdown-it` as a
|
||||||
|
direct dependency. This function makes it possible to avoid that dependency
|
||||||
|
entirely. In cases where `markdown-it` is needed, the caller is responsible for
|
||||||
|
declaring the dependency and returning an instance from this factory. If any
|
||||||
|
[`markdown-it` plugins][markdown-it-plugin] are needed, they should be `use`d by
|
||||||
|
the caller before returning the `markdown-it` instance.
|
||||||
|
|
||||||
Each item in the top-level `Array` should be of the form:
|
For compatibility with previous versions of `markdownlint`, this function should
|
||||||
|
be similar to:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
[ require("markdown-it-plugin"), plugin_param_0, plugin_param_1, ... ]
|
import markdownIt from "markdown-it";
|
||||||
|
const markdownItFactory = () => markdownIt({ "html": true });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When an asynchronous implementation of `lint` is being invoked (e.g., via
|
||||||
|
`markdownlint/async` or `markdownlint/promise`), this function can return a
|
||||||
|
`Promise` in order to defer the import of `markdown-it`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const markdownItFactory = () => import("markdown-it").then((module) => module.default({ "html": true }));
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note that this function is only invoked when a `markdown-it` parser is
|
||||||
|
> needed. None of the built-in rules use the `markdown-it` parser, so it is only
|
||||||
|
> invoked when one or more [custom rules][custom-rules] are present that use the
|
||||||
|
> `markdown-it` parser.
|
||||||
|
|
||||||
|
[custom-rules]: #custom-rules
|
||||||
|
[markdown-it]: https://github.com/markdown-it/markdown-it
|
||||||
|
[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin
|
||||||
|
|
||||||
##### options.noInlineConfig
|
##### options.noInlineConfig
|
||||||
|
|
||||||
Type: `Boolean`
|
Type: `Boolean`
|
||||||
|
@ -593,23 +627,20 @@ Specifies which version of the `result` object to return (see the "Usage"
|
||||||
section below for examples).
|
section below for examples).
|
||||||
|
|
||||||
Passing a `resultVersion` of `0` corresponds to the original, simple format
|
Passing a `resultVersion` of `0` corresponds to the original, simple format
|
||||||
where each error is identified by rule name and line number. *This is
|
where each error is identified by rule name and line number. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `1` corresponds to a detailed format where each
|
Passing a `resultVersion` of `1` corresponds to a detailed format where each
|
||||||
error includes information about the line number, rule name, alias, description,
|
error includes information about the line number, rule name, alias, description,
|
||||||
as well as any additional detail or context that is available. *This is
|
as well as any additional detail or context that is available. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `2` corresponds to a detailed format where each
|
Passing a `resultVersion` of `2` corresponds to a detailed format where each
|
||||||
error includes information about the line number, rule names, description, as
|
error includes information about the line number, rule names, description, as
|
||||||
well as any additional detail or context that is available. *This is
|
well as any additional detail or context that is available. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `3` corresponds to the detailed version `2` format
|
Passing a `resultVersion` of `3` corresponds to the detailed version `2` format
|
||||||
with additional information about how to fix automatically-fixable errors. In
|
with additional information about how to fix automatically-fixable errors. In
|
||||||
this mode, all errors that occur on each line are reported (other versions
|
this mode, all errors that occur on each line are reported (other versions
|
||||||
report only the first error for each rule). *This is the default.*
|
report only the first error for each rule). This is the default behavior.
|
||||||
|
|
||||||
##### options.strings
|
##### options.strings
|
||||||
|
|
||||||
|
@ -647,22 +678,22 @@ uses rule aliases (ex: `no-hard-tabs`) instead of names (ex: `MD010`).
|
||||||
### Config
|
### Config
|
||||||
|
|
||||||
The `options.config` configuration object is simple and can be stored in a file
|
The `options.config` configuration object is simple and can be stored in a file
|
||||||
for readability and easy reuse. The `readConfig` and `readConfigSync` functions
|
for readability and easy reuse. The `readConfig` function loads configuration
|
||||||
load configuration settings and support the `extends` keyword for referencing
|
settings and supports the `extends` keyword for referencing files or packages
|
||||||
other files (see above).
|
(see above).
|
||||||
|
|
||||||
By default, configuration files are parsed as JSON (and named
|
By default, configuration files are parsed as JSON (and named
|
||||||
`.markdownlint.json`). Custom parsers can be provided to handle other formats
|
`.markdownlint.json`). Custom parsers can be provided to handle other formats
|
||||||
like JSONC, YAML, and TOML.
|
like JSONC, YAML, and TOML.
|
||||||
|
|
||||||
Asynchronous API:
|
Asynchronous API via `import { readConfig } from "markdownlint/async"`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/**
|
/**
|
||||||
* Read specified configuration file.
|
* Read specified configuration file.
|
||||||
*
|
*
|
||||||
* @param {string} file Configuration file name.
|
* @param {string} file Configuration file name.
|
||||||
* @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing function.
|
* @param {ConfigurationParser[] | ReadConfigCallback} [parsers] Parsing function(s).
|
||||||
* @param {Object} [fs] File system implementation.
|
* @param {Object} [fs] File system implementation.
|
||||||
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
|
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
@ -670,22 +701,21 @@ Asynchronous API:
|
||||||
function readConfig(file, parsers, fs, callback) { ... }
|
function readConfig(file, parsers, fs, callback) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
Synchronous API:
|
Synchronous API via `import { readConfig } from "markdownlint/sync"`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/**
|
/**
|
||||||
* Read specified configuration file synchronously.
|
* Read specified configuration file.
|
||||||
*
|
*
|
||||||
* @param {string} file Configuration file name.
|
* @param {string} file Configuration file name.
|
||||||
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
||||||
* @param {Object} [fs] File system implementation.
|
* @param {Object} [fs] File system implementation.
|
||||||
* @returns {Configuration} Configuration object.
|
* @returns {Configuration} Configuration object.
|
||||||
*/
|
*/
|
||||||
function readConfigSync(file, parsers, fs) { ... }
|
function readConfig(file, parsers, fs) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
Promise API (in the `promises` namespace like Node.js's
|
Promise API via `import { readConfig } from "markdownlint/promise"`:
|
||||||
[`fs` Promises API](https://nodejs.org/api/fs.html#fs_promises_api)):
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/**
|
/**
|
||||||
|
@ -737,7 +767,7 @@ Type: *Optional* `Object` implementing the [file system API][file-system-api]
|
||||||
|
|
||||||
In advanced scenarios, it may be desirable to bypass the default file system
|
In advanced scenarios, it may be desirable to bypass the default file system
|
||||||
API. If a custom file system implementation is provided, `markdownlint` will use
|
API. If a custom file system implementation is provided, `markdownlint` will use
|
||||||
that instead of invoking `require("fs")`.
|
that instead of invoking `node:fs`.
|
||||||
|
|
||||||
Note: The only methods called are `readFile`, `readFileSync`, `access`, and
|
Note: The only methods called are `readFile`, `readFileSync`, `access`, and
|
||||||
`accessSync`.
|
`accessSync`.
|
||||||
|
@ -754,12 +784,75 @@ Type: `Object`
|
||||||
|
|
||||||
Configuration object.
|
Configuration object.
|
||||||
|
|
||||||
## Usage
|
### Fixing
|
||||||
|
|
||||||
Invoke `markdownlint` and use the `result` object's `toString` method:
|
Rules that can be fixed automatically include a `fixInfo` property which is
|
||||||
|
outlined in the [documentation for custom rules](doc/CustomRules.md#authoring).
|
||||||
|
To apply fixes consistently, the `applyFix`/`applyFixes` methods may be used via
|
||||||
|
`import { applyFix, applyFixes } from "markdownlint"`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const markdownlint = require("markdownlint");
|
/**
|
||||||
|
* Applies the specified fix to a Markdown content line.
|
||||||
|
*
|
||||||
|
* @param {string} line Line of Markdown content.
|
||||||
|
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance.
|
||||||
|
* @param {string} [lineEnding] Line ending to use.
|
||||||
|
* @returns {string | null} Fixed content or null if deleted.
|
||||||
|
*/
|
||||||
|
function applyFix(line, fixInfo, lineEnding = "\n") { ... }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies as many of the specified fixes as possible to Markdown content.
|
||||||
|
*
|
||||||
|
* @param {string} input Lines of Markdown content.
|
||||||
|
* @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances.
|
||||||
|
* @returns {string} Fixed content.
|
||||||
|
*/
|
||||||
|
function applyFixes(input, errors) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Invoking `applyFixes` with the results of a call to lint can be done like so:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { applyFixes } from "markdownlint";
|
||||||
|
import { lint as lintSync } from "markdownlint/sync";
|
||||||
|
|
||||||
|
const results = lintSync({ "strings": { "content": original } });
|
||||||
|
const fixed = applyFixes(original, results.content);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
To get the [semantic version][semver] of the library, the `getVersion` method
|
||||||
|
can be used:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/**
|
||||||
|
* Gets the (semantic) version of the library.
|
||||||
|
*
|
||||||
|
* @returns {string} SemVer string.
|
||||||
|
*/
|
||||||
|
function getVersion() { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Invoking `getVersion` is simple:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { getVersion } from "markdownlint";
|
||||||
|
|
||||||
|
// Displays the library version
|
||||||
|
console.log(getVersion());
|
||||||
|
```
|
||||||
|
|
||||||
|
[semver]: https://semver.org
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Invoke `lint` and use the `result` object's `toString` method:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { lint as lintAsync } from "markdownlint/async";
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
"files": [ "good.md", "bad.md" ],
|
"files": [ "good.md", "bad.md" ],
|
||||||
|
@ -769,9 +862,9 @@ const options = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
markdownlint(options, function callback(err, result) {
|
lintAsync(options, function callback(error, results) {
|
||||||
if (!err) {
|
if (!error && results) {
|
||||||
console.log(result.toString());
|
console.log(results.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
@ -789,21 +882,22 @@ bad.md: 3: MD018/no-missing-space-atx No space after hash on atx style heading [
|
||||||
bad.md: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.md"]
|
bad.md: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.md"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Or invoke `markdownlint.sync` for a synchronous call:
|
Or as a synchronous call:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const result = markdownlint.sync(options);
|
import { lint as lintSync } from "markdownlint/sync";
|
||||||
console.log(result.toString());
|
|
||||||
|
const results = lintSync(options);
|
||||||
|
console.log(results.toString());
|
||||||
```
|
```
|
||||||
|
|
||||||
To examine the `result` object directly:
|
To examine the `result` object directly via a `Promise`-based call:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
markdownlint(options, function callback(err, result) {
|
import { lint as lintPromise } from "markdownlint/promise";
|
||||||
if (!err) {
|
|
||||||
console.dir(result, { "colors": true, "depth": null });
|
const results = await lintPromise(options);
|
||||||
}
|
console.dir(results, { "colors": true, "depth": null });
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
@ -845,88 +939,10 @@ Output:
|
||||||
```
|
```
|
||||||
|
|
||||||
Integration with the [gulp](https://gulpjs.com/) build system is
|
Integration with the [gulp](https://gulpjs.com/) build system is
|
||||||
straightforward:
|
straightforward: [`gulpfile.cjs`](example/gulpfile.cjs).
|
||||||
|
|
||||||
```javascript
|
|
||||||
const gulp = require("gulp");
|
|
||||||
const through2 = require("through2");
|
|
||||||
const markdownlint = require("markdownlint");
|
|
||||||
|
|
||||||
gulp.task("markdownlint", function task() {
|
|
||||||
return gulp.src("*.md", { "read": false })
|
|
||||||
.pipe(through2.obj(function obj(file, enc, next) {
|
|
||||||
markdownlint(
|
|
||||||
{ "files": [ file.relative ] },
|
|
||||||
function callback(err, result) {
|
|
||||||
const resultString = (result || "").toString();
|
|
||||||
if (resultString) {
|
|
||||||
console.log(resultString);
|
|
||||||
}
|
|
||||||
next(err, file);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```text
|
|
||||||
[00:00:00] Starting 'markdownlint'...
|
|
||||||
bad.md: 3: MD010/no-hard-tabs Hard tabs [Column: 17]
|
|
||||||
bad.md: 1: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#bad.md"]
|
|
||||||
bad.md: 3: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#This file fails some rules."]
|
|
||||||
bad.md: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.md"]
|
|
||||||
[00:00:00] Finished 'markdownlint' after 10 ms
|
|
||||||
```
|
|
||||||
|
|
||||||
Integration with the [Grunt](https://gruntjs.com/) build system is similar:
|
Integration with the [Grunt](https://gruntjs.com/) build system is similar:
|
||||||
|
[`Gruntfile.cjs`](example/Gruntfile.cjs).
|
||||||
```javascript
|
|
||||||
const markdownlint = require("markdownlint");
|
|
||||||
|
|
||||||
module.exports = function wrapper(grunt) {
|
|
||||||
grunt.initConfig({
|
|
||||||
"markdownlint": {
|
|
||||||
"example": {
|
|
||||||
"src": [ "*.md" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
grunt.registerMultiTask("markdownlint", function task() {
|
|
||||||
const done = this.async();
|
|
||||||
markdownlint(
|
|
||||||
{ "files": this.filesSrc },
|
|
||||||
function callback(err, result) {
|
|
||||||
const resultString = err || ((result || "").toString());
|
|
||||||
if (resultString) {
|
|
||||||
grunt.fail.warn("\n" + resultString + "\n");
|
|
||||||
}
|
|
||||||
done(!err || !resultString);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Running "markdownlint:example" (markdownlint) task
|
|
||||||
Warning:
|
|
||||||
bad.md: 3: MD010/no-hard-tabs Hard tabs [Column: 17]
|
|
||||||
bad.md: 1: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#bad.md"]
|
|
||||||
bad.md: 3: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#This file fails some rules."]
|
|
||||||
bad.md: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.md"]
|
|
||||||
Use --force to continue.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fixing
|
|
||||||
|
|
||||||
Rules that can be fixed automatically include a `fixInfo` property which is
|
|
||||||
outlined in the [documentation for custom rules](doc/CustomRules.md#authoring).
|
|
||||||
To apply those fixes more easily, the `applyFixes` method in
|
|
||||||
[markdownlint-rule-helpers](helpers/README.md#applying-recommended-fixes) may
|
|
||||||
be used.
|
|
||||||
|
|
||||||
## Browser
|
## Browser
|
||||||
|
|
||||||
|
@ -938,10 +954,9 @@ Generate normal and minified scripts with:
|
||||||
npm run build-demo
|
npm run build-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
Then reference `markdown-it` and `markdownlint`:
|
Then reference the `markdownlint-browser` script:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="demo/markdown-it.min.js"></script>
|
|
||||||
<script src="demo/markdownlint-browser.min.js"></script>
|
<script src="demo/markdownlint-browser.min.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -953,7 +968,8 @@ const options = {
|
||||||
"content": "Some Markdown to lint."
|
"content": "Some Markdown to lint."
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const results = window.markdownlint.sync(options).toString();
|
|
||||||
|
const results = globalThis.markdownlint.lintSync(options).toString();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
@ -979,6 +995,11 @@ following projects or one of the tools in the [Related section](#related):
|
||||||
- [webpack][webpack] ([Search repository][webpack-search])
|
- [webpack][webpack] ([Search repository][webpack-search])
|
||||||
- [WordPress][wordpress] ([Search repository][wordpress-search])
|
- [WordPress][wordpress] ([Search repository][wordpress-search])
|
||||||
|
|
||||||
|
For more advanced integration scenarios:
|
||||||
|
|
||||||
|
- [GitHub Docs content linter][content-linter]
|
||||||
|
- [GitHub's `markdownlint-github` repository][markdownlint-github]
|
||||||
|
|
||||||
[ally-js]: https://allyjs.io/
|
[ally-js]: https://allyjs.io/
|
||||||
[ally-js-search]: https://github.com/medialize/ally.js/search?q=markdownlint
|
[ally-js-search]: https://github.com/medialize/ally.js/search?q=markdownlint
|
||||||
[airflow]: https://airflow.apache.org
|
[airflow]: https://airflow.apache.org
|
||||||
|
@ -987,6 +1008,7 @@ following projects or one of the tools in the [Related section](#related):
|
||||||
[boostnote-search]: https://github.com/BoostIO/Boostnote/search?q=markdownlint
|
[boostnote-search]: https://github.com/BoostIO/Boostnote/search?q=markdownlint
|
||||||
[codimd]: https://github.com/hackmdio/codimd
|
[codimd]: https://github.com/hackmdio/codimd
|
||||||
[codimd-search]: https://github.com/hackmdio/codimd/search?q=markdownlint
|
[codimd-search]: https://github.com/hackmdio/codimd/search?q=markdownlint
|
||||||
|
[content-linter]: https://docs.github.com/en/contributing/collaborating-on-github-docs/using-the-content-linter
|
||||||
[dot-net-doc]: https://docs.microsoft.com/en-us/dotnet/
|
[dot-net-doc]: https://docs.microsoft.com/en-us/dotnet/
|
||||||
[dot-net-doc-search]: https://github.com/dotnet/docs/search?q=markdownlint
|
[dot-net-doc-search]: https://github.com/dotnet/docs/search?q=markdownlint
|
||||||
[electron]: https://www.electronjs.org
|
[electron]: https://www.electronjs.org
|
||||||
|
@ -995,6 +1017,7 @@ following projects or one of the tools in the [Related section](#related):
|
||||||
[eslint-search]: https://github.com/eslint/eslint/search?q=markdownlint
|
[eslint-search]: https://github.com/eslint/eslint/search?q=markdownlint
|
||||||
[garden]: https://zendeskgarden.github.io/react-components/
|
[garden]: https://zendeskgarden.github.io/react-components/
|
||||||
[garden-search]: https://github.com/zendeskgarden/react-components/search?q=markdownlint
|
[garden-search]: https://github.com/zendeskgarden/react-components/search?q=markdownlint
|
||||||
|
[markdownlint-github]: https://github.com/github/markdownlint-github
|
||||||
[mdn]: https://developer.mozilla.org/
|
[mdn]: https://developer.mozilla.org/
|
||||||
[mdn-search]: https://github.com/mdn/content/search?q=markdownlint
|
[mdn-search]: https://github.com/mdn/content/search?q=markdownlint
|
||||||
[mkdocs]: https://www.mkdocs.org/
|
[mkdocs]: https://www.mkdocs.org/
|
||||||
|
|
10
demo/browser-exports.mjs
Normal file
10
demo/browser-exports.mjs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { applyFixes, getVersion } from "markdownlint";
|
||||||
|
export { lint as lintSync } from "markdownlint/sync";
|
||||||
|
export { compile, parse, postprocess, preprocess } from "micromark";
|
||||||
|
export { directive, directiveHtml } from "micromark-extension-directive";
|
||||||
|
export { gfmAutolinkLiteral, gfmAutolinkLiteralHtml } from "micromark-extension-gfm-autolink-literal";
|
||||||
|
export { gfmFootnote, gfmFootnoteHtml } from "micromark-extension-gfm-footnote";
|
||||||
|
export { gfmTable, gfmTableHtml } from "micromark-extension-gfm-table";
|
||||||
|
export { math, mathHtml } from "micromark-extension-math";
|
|
@ -34,8 +34,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="markdown-it.min.js"></script>
|
<script src="markdown-it.min.js"></script>
|
||||||
<script src="micromark-browser.js"></script>
|
|
||||||
<script src="micromark-html-browser.js"></script>
|
|
||||||
<script src="markdownlint-browser.min.js"></script>
|
<script src="markdownlint-browser.min.js"></script>
|
||||||
<script src="default.js"></script>
|
<script src="default.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
(function main() {
|
(function main() {
|
||||||
// Dependencies
|
// Dependencies
|
||||||
var markdownit = window.markdownit;
|
var markdownit = globalThis.markdownit;
|
||||||
var markdownlint = window.markdownlint.library;
|
var markdownlint = globalThis.markdownlint;
|
||||||
var helpers = window.markdownlint.helpers;
|
var micromark = markdownlint;
|
||||||
var micromark = window.micromarkBrowser;
|
|
||||||
var micromarkHtml = window.micromarkHtmlBrowser;
|
|
||||||
|
|
||||||
// DOM elements
|
// DOM elements
|
||||||
var markdown = document.getElementById("markdown");
|
var markdown = document.getElementById("markdown");
|
||||||
|
@ -51,7 +49,7 @@
|
||||||
|
|
||||||
// Renders Markdown to HTML
|
// Renders Markdown to HTML
|
||||||
function render(markdown) {
|
function render(markdown) {
|
||||||
const match = /^\?renderer=([a-z-]+)$/.exec(window.location.search);
|
const match = /^\?renderer=([a-z-]+)$/.exec(globalThis.location.search);
|
||||||
const renderer = match ? match[1] : "micromark";
|
const renderer = match ? match[1] : "micromark";
|
||||||
if (renderer === "markdown-it") {
|
if (renderer === "markdown-it") {
|
||||||
return markdownit({ "html": true }).render(markdown);
|
return markdownit({ "html": true }).render(markdown);
|
||||||
|
@ -71,15 +69,15 @@
|
||||||
const compileOptions = {
|
const compileOptions = {
|
||||||
"allowDangerousHtml": true,
|
"allowDangerousHtml": true,
|
||||||
"htmlExtensions": [
|
"htmlExtensions": [
|
||||||
micromarkHtml.directiveHtml({ "*": handleDirective }),
|
micromark.directiveHtml({ "*": handleDirective }),
|
||||||
micromarkHtml.gfmAutolinkLiteralHtml(),
|
micromark.gfmAutolinkLiteralHtml(),
|
||||||
micromarkHtml.gfmFootnoteHtml(),
|
micromark.gfmFootnoteHtml(),
|
||||||
micromarkHtml.gfmTableHtml(),
|
micromark.gfmTableHtml(),
|
||||||
micromarkHtml.mathHtml()
|
micromark.mathHtml()
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
return micromarkHtml.compile(compileOptions)(events);
|
return micromark.compile(compileOptions)(events);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return `[Exception: "${error}"]`;
|
return `[Exception: "${error}"]`;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +112,7 @@
|
||||||
},
|
},
|
||||||
"handleRuleFailures": true
|
"handleRuleFailures": true
|
||||||
};
|
};
|
||||||
allLintErrors = markdownlint.sync(options).content;
|
allLintErrors = markdownlint.lintSync(options).content;
|
||||||
violations.innerHTML = allLintErrors.map(function mapResult(result) {
|
violations.innerHTML = allLintErrors.map(function mapResult(result) {
|
||||||
var ruleName = result.ruleNames.slice(0, 2).join(" / ");
|
var ruleName = result.ruleNames.slice(0, 2).join(" / ");
|
||||||
return "<em><a href='#line' target='" + result.lineNumber + "'>" +
|
return "<em><a href='#line' target='" + result.lineNumber + "'>" +
|
||||||
|
@ -132,9 +130,9 @@
|
||||||
"\"</span>]" :
|
"\"</span>]" :
|
||||||
"") +
|
"") +
|
||||||
(result.fixInfo ?
|
(result.fixInfo ?
|
||||||
" [<a href='#fix' target='" +
|
" [<a href='#fix' target=\"" +
|
||||||
encodeURIComponent(JSON.stringify(result)) +
|
encodeURIComponent(JSON.stringify(result)) +
|
||||||
"' class='detail'>Fix</a>]" :
|
"\" class='detail'>Fix</a>]" :
|
||||||
"");
|
"");
|
||||||
}).join("<br/>");
|
}).join("<br/>");
|
||||||
}
|
}
|
||||||
|
@ -184,7 +182,7 @@
|
||||||
var errors = e.shiftKey ?
|
var errors = e.shiftKey ?
|
||||||
allLintErrors :
|
allLintErrors :
|
||||||
[ JSON.parse(decodeURIComponent(e.target.target)) ];
|
[ JSON.parse(decodeURIComponent(e.target.target)) ];
|
||||||
var fixed = helpers.applyFixes(markdown.value, errors);
|
var fixed = markdownlint.applyFixes(markdown.value, errors);
|
||||||
markdown.value = fixed;
|
markdown.value = fixed;
|
||||||
onMarkdownInput();
|
onMarkdownInput();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -211,9 +209,9 @@
|
||||||
|
|
||||||
// Updates the URL hash and copies the URL to the clipboard
|
// Updates the URL hash and copies the URL to the clipboard
|
||||||
function onCopyLinkClick(e) {
|
function onCopyLinkClick(e) {
|
||||||
window.location.hash = encodeURIComponent(hashPrefix + markdown.value);
|
globalThis.location.hash = encodeURIComponent(hashPrefix + markdown.value);
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
navigator.clipboard.writeText(window.location).then(noop, noop);
|
navigator.clipboard.writeText(globalThis.location).then(noop, noop);
|
||||||
} else {
|
} else {
|
||||||
/* eslint-disable-next-line no-alert */
|
/* eslint-disable-next-line no-alert */
|
||||||
alert("Document URL updated, select and copy it now.");
|
alert("Document URL updated, select and copy it now.");
|
||||||
|
@ -261,14 +259,13 @@
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
// Update Markdown from hash (if present)
|
// Update Markdown from hash (if present)
|
||||||
if (window.location.hash) {
|
if (globalThis.location.hash) {
|
||||||
try {
|
try {
|
||||||
var decodedHash = decodeURIComponent(window.location.hash.substring(1));
|
var decodedHash = decodeURIComponent(globalThis.location.hash.substring(1));
|
||||||
if (hashPrefix === decodedHash.substring(0, hashPrefix.length)) {
|
if (hashPrefix === decodedHash.substring(0, hashPrefix.length)) {
|
||||||
markdown.value = decodedHash.substring(hashPrefix.length);
|
markdown.value = decodedHash.substring(hashPrefix.length);
|
||||||
}
|
}
|
||||||
/* eslint-disable-next-line unicorn/prefer-optional-catch-binding */
|
} catch {
|
||||||
} catch (error) {
|
|
||||||
// Invalid
|
// Invalid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,8 +274,7 @@
|
||||||
try {
|
try {
|
||||||
/* eslint-disable-next-line no-new */
|
/* eslint-disable-next-line no-new */
|
||||||
new URL("https://example.com/");
|
new URL("https://example.com/");
|
||||||
/* eslint-disable-next-line unicorn/prefer-optional-catch-binding */
|
} catch {
|
||||||
} catch (error) {
|
|
||||||
markdown.value = [
|
markdown.value = [
|
||||||
"# Sorry",
|
"# Sorry",
|
||||||
"",
|
"",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
"library": require(".."),
|
|
||||||
"helpers": require("../helpers")
|
|
||||||
};
|
|
|
@ -1,108 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const webpack = require("webpack");
|
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
|
||||||
const nodeModulePrefixRe = /^node:/u;
|
|
||||||
|
|
||||||
function config(options) {
|
|
||||||
const { entry, filename, mode, optimization, packageJson } = options;
|
|
||||||
const { name, version, homepage, license } = packageJson;
|
|
||||||
return {
|
|
||||||
"devtool": false,
|
|
||||||
"entry": entry,
|
|
||||||
"externals": {
|
|
||||||
"markdown-it": "markdownit",
|
|
||||||
"markdownlint-micromark": "micromarkBrowser"
|
|
||||||
},
|
|
||||||
"mode": mode,
|
|
||||||
"module": {
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"test": /\.[cm]?js$/,
|
|
||||||
"exclude": /node_modules/
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"name": name,
|
|
||||||
"optimization": optimization,
|
|
||||||
"output": {
|
|
||||||
"filename": filename,
|
|
||||||
"library": {
|
|
||||||
"name": name.replace(/(-\w)/g, (m) => m.slice(1).toUpperCase()),
|
|
||||||
"type": "var"
|
|
||||||
},
|
|
||||||
"path": __dirname
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
|
||||||
nodeModulePrefixRe,
|
|
||||||
(resource) => {
|
|
||||||
const module = resource.request.replace(nodeModulePrefixRe, "");
|
|
||||||
resource.request = module;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
new webpack.BannerPlugin({
|
|
||||||
"banner": `${name} ${version} ${homepage} @license ${license}`
|
|
||||||
})
|
|
||||||
],
|
|
||||||
"resolve": {
|
|
||||||
"fallback": {
|
|
||||||
"fs": false,
|
|
||||||
"os": false,
|
|
||||||
"path": false,
|
|
||||||
"util": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const modeDevelopment = {
|
|
||||||
"mode": "development"
|
|
||||||
};
|
|
||||||
const modeProduction = {
|
|
||||||
"mode": "production",
|
|
||||||
"optimization": {
|
|
||||||
"minimizer": [
|
|
||||||
new TerserPlugin({
|
|
||||||
"extractComments": false,
|
|
||||||
"terserOptions": {
|
|
||||||
"compress": {
|
|
||||||
"passes": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const entryLibrary = {
|
|
||||||
"entry": "./markdownlint-exports.js",
|
|
||||||
"packageJson": require("../package.json")
|
|
||||||
};
|
|
||||||
// const entryHelpers = {
|
|
||||||
// "entry": "../helpers/helpers.js",
|
|
||||||
// "packageJson": require("../helpers/package.json")
|
|
||||||
// };
|
|
||||||
module.exports = [
|
|
||||||
config({
|
|
||||||
...entryLibrary,
|
|
||||||
...modeDevelopment,
|
|
||||||
"filename": "markdownlint-browser.js"
|
|
||||||
}),
|
|
||||||
config({
|
|
||||||
...entryLibrary,
|
|
||||||
...modeProduction,
|
|
||||||
"filename": "markdownlint-browser.min.js"
|
|
||||||
})
|
|
||||||
// config({
|
|
||||||
// ...entryHelpers,
|
|
||||||
// ...modeDevelopment,
|
|
||||||
// "filename": "markdownlint-rule-helpers-browser.js"
|
|
||||||
// }),
|
|
||||||
// config({
|
|
||||||
// ...entryHelpers,
|
|
||||||
// ...modeProduction,
|
|
||||||
// "filename": "markdownlint-rule-helpers-browser.min.js"
|
|
||||||
// })
|
|
||||||
];
|
|
81
demo/webpack.config.mjs
Normal file
81
demo/webpack.config.mjs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import webpack from "webpack";
|
||||||
|
import TerserPlugin from "terser-webpack-plugin";
|
||||||
|
import { __dirname, importWithTypeJson } from "../test/esm-helpers.mjs";
|
||||||
|
const libraryPackageJson = await importWithTypeJson(import.meta, "../package.json");
|
||||||
|
|
||||||
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||||
|
function config(options) {
|
||||||
|
const { entry, filename, mode, optimization, packageJson } = options;
|
||||||
|
const { name, version, homepage, license } = packageJson;
|
||||||
|
return {
|
||||||
|
"devtool": false,
|
||||||
|
"entry": entry,
|
||||||
|
"externals": {
|
||||||
|
"markdown-it": "markdownit"
|
||||||
|
},
|
||||||
|
"mode": mode,
|
||||||
|
"name": name,
|
||||||
|
"optimization": optimization,
|
||||||
|
"output": {
|
||||||
|
"filename": filename,
|
||||||
|
"library": {
|
||||||
|
"name": name.replace(/(-\w)/g, (m) => m.slice(1).toUpperCase()),
|
||||||
|
"type": "var"
|
||||||
|
},
|
||||||
|
"path": __dirname(import.meta)
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
new webpack.BannerPlugin({
|
||||||
|
"banner": `${name} ${version} ${homepage} @license ${license}`
|
||||||
|
})
|
||||||
|
],
|
||||||
|
"ignoreWarnings": [
|
||||||
|
{
|
||||||
|
"message": /(asset|entrypoint) size limit/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": /dependencies cannot be statically extracted/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": /lazy load some parts of your application/
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeDevelopment = {
|
||||||
|
"mode": "development"
|
||||||
|
};
|
||||||
|
const modeProduction = {
|
||||||
|
"mode": "production",
|
||||||
|
"optimization": {
|
||||||
|
"minimizer": [
|
||||||
|
new TerserPlugin({
|
||||||
|
"extractComments": false,
|
||||||
|
"terserOptions": {
|
||||||
|
"compress": {
|
||||||
|
"passes": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const entryLibrary = {
|
||||||
|
"entry": "./browser-exports.mjs",
|
||||||
|
"packageJson": libraryPackageJson
|
||||||
|
};
|
||||||
|
export default [
|
||||||
|
config({
|
||||||
|
...entryLibrary,
|
||||||
|
...modeDevelopment,
|
||||||
|
"filename": "markdownlint-browser.js"
|
||||||
|
}),
|
||||||
|
config({
|
||||||
|
...entryLibrary,
|
||||||
|
...modeProduction,
|
||||||
|
"filename": "markdownlint-browser.min.js"
|
||||||
|
})
|
||||||
|
];
|
|
@ -1,8 +1,8 @@
|
||||||
import { readFile, writeFile } from "node:fs/promises";
|
import { readFile, writeFile } from "node:fs/promises";
|
||||||
import { EOL } from "node:os";
|
import { EOL } from "node:os";
|
||||||
import { default as rules } from "../lib/rules.js";
|
import rules from "../lib/rules.mjs";
|
||||||
import { newLineRe } from "../helpers/helpers.js";
|
import { newLineRe } from "../helpers/helpers.cjs";
|
||||||
import { deprecatedRuleNames, fixableRuleNames } from "../lib/constants.js";
|
import { deprecatedRuleNames, fixableRuleNames } from "../lib/constants.mjs";
|
||||||
|
|
||||||
const maxLineLength = 80;
|
const maxLineLength = 80;
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@ Setext style H2
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The configured heading style can be a specific style to require (`atx`,
|
Note: The configured heading style can be a specific style to require (`atx`,
|
||||||
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or may
|
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or can
|
||||||
just require that usage is consistent within the document via `consistent`.
|
require that all heading styles match the first heading style via `consistent`.
|
||||||
|
|
||||||
Note: The placement of a horizontal rule directly below a line of text can
|
Note: The placement of a horizontal rule directly below a line of text can
|
||||||
trigger this rule by turning that text into a level 2 setext-style heading:
|
trigger this rule by turning that text into a level 2 setext-style heading:
|
||||||
|
|
|
@ -16,9 +16,10 @@ document:
|
||||||
* Item 3
|
* Item 3
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (asterisk, plus,
|
The configured list style can ensure all list styling is a specific symbol
|
||||||
dash), to ensure that all list styling is consistent, or to ensure that each
|
(`asterisk`, `plus`, `dash`), ensure each sublist has a consistent symbol that
|
||||||
sublist has a consistent symbol that differs from its parent list.
|
differs from its parent list (`sublist`), or ensure all list styles match the
|
||||||
|
first list style (`consistent`).
|
||||||
|
|
||||||
For example, the following is valid for the `sublist` style because the
|
For example, the following is valid for the `sublist` style because the
|
||||||
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
||||||
|
|
|
@ -13,4 +13,8 @@ To fix, remove any extraneous space:
|
||||||
> indentation.
|
> indentation.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Inferring intended list indentation within a blockquote can be challenging;
|
||||||
|
setting the `list_items` parameter to `false` disables this rule for ordered
|
||||||
|
and unordered list items.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -11,6 +11,11 @@ To fix this, add angle brackets around the URL or email address:
|
||||||
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a URL or email address contains non-ASCII characters, it may be not be
|
||||||
|
handled as intended even when angle brackets are present. In such cases,
|
||||||
|
[percent-encoding](https://en.m.wikipedia.org/wiki/Percent-encoding) can be used
|
||||||
|
to comply with the required syntax for URL and email.
|
||||||
|
|
||||||
Note: To include a bare URL or email without it being converted into a link,
|
Note: To include a bare URL or email without it being converted into a link,
|
||||||
wrap it in a code span:
|
wrap it in a code span:
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@ in the document:
|
||||||
****
|
****
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, ensure any horizontal rules used in the document are consistent,
|
To fix this, use the same horizontal rule everywhere:
|
||||||
or match the given style if the rule is so configured:
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
---
|
---
|
||||||
|
@ -22,11 +21,7 @@ or match the given style if the rule is so configured:
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: by default, this rule is configured to just require that all horizontal
|
The configured style can ensure all horizontal rules use a specific string or it
|
||||||
rules in the document are the same and will trigger if any of the horizontal
|
can ensure all horizontal rules match the first horizontal rule (`consistent`).
|
||||||
rules are different than the first one encountered in the document. If you
|
|
||||||
want to configure the rule to match a specific style, the parameter given to
|
|
||||||
the 'style' option is a string containing the exact horizontal rule text that
|
|
||||||
is allowed.
|
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -1,32 +1,44 @@
|
||||||
This rule is triggered for code span elements that have spaces adjacent to the
|
This rule is triggered for code spans containing content with unnecessary space
|
||||||
backticks:
|
next to the beginning or ending backticks:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text `
|
`some text `
|
||||||
|
|
||||||
` some text`
|
` some text`
|
||||||
|
|
||||||
|
` some text `
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, remove any spaces adjacent to the backticks:
|
To fix this, remove the extra space characters from the beginning and ending:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text`
|
`some text`
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading and trailing space is allowed by the specification and
|
Note: A single leading *and* trailing space is allowed by the specification and
|
||||||
automatically trimmed (in order to allow for code spans that embed backticks):
|
trimmed by the parser to support code spans that begin or end with a backtick:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` `backticks` ``
|
`` `backticks` ``
|
||||||
|
|
||||||
|
`` backtick` ``
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading or trailing space is allowed if used to separate code
|
Note: When single-space padding is present in the input, it will be preserved
|
||||||
span markers from an embedded backtick (though the space is not trimmed):
|
(even if unnecessary):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` ` embedded backtick``
|
` code `
|
||||||
```
|
```
|
||||||
|
|
||||||
Rationale: Violations of this rule are usually unintentional and may lead to
|
Note: Code spans containing only spaces are allowed by the specification and are
|
||||||
improperly-rendered content. If spaces beside backticks are intentional, this
|
also preserved:
|
||||||
rule can be disabled for that line or file.
|
|
||||||
|
```markdown
|
||||||
|
` `
|
||||||
|
|
||||||
|
` `
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: Violations of this rule are usually unintentional and can lead to
|
||||||
|
improperly-rendered content.
|
||||||
|
|
|
@ -1,37 +1,50 @@
|
||||||
This rule is intended to ensure documents have a title and is triggered when
|
This rule is intended to ensure documents have a title and is triggered when
|
||||||
the first line in the file isn't a top-level (h1) heading:
|
the first line in a document is not a top-level ([HTML][HTML] `h1`) heading:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
This is a file without a heading
|
This is a document without a heading
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, add a top-level heading to the beginning of the file:
|
To fix this, add a top-level heading to the beginning of the document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# File with heading
|
# Document Heading
|
||||||
|
|
||||||
This is a file with a top-level heading
|
This is a document with a top-level heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Because it is common for projects on GitHub to use an image for the heading of
|
Because it is common for projects on GitHub to use an image for the heading of
|
||||||
`README.md` and that is not well-supported by Markdown, HTML headings are also
|
`README.md` and that pattern is not well-supported by Markdown, HTML headings
|
||||||
permitted by this rule. For example:
|
are also permitted by this rule. For example:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
||||||
|
|
||||||
This is a file with a top-level HTML heading
|
This is a document with a top-level HTML heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The `level` parameter can be used to change the top-level (ex: to h2) in
|
In some cases, a document's title heading may be preceded by text like a table
|
||||||
cases where an h1 is added externally.
|
of contents. This is not ideal for accessibility, but can be allowed by setting
|
||||||
|
the `allow_preamble` parameter to `true`.
|
||||||
|
|
||||||
If [YAML](https://en.wikipedia.org/wiki/YAML) front matter is present and
|
```markdown
|
||||||
contains a `title` property (commonly used with blog posts), this rule will not
|
This is a document with preamble text
|
||||||
report a violation. To use a different property name in the front matter,
|
|
||||||
specify the text of a regular expression via the `front_matter_title` parameter.
|
# Document Heading
|
||||||
To disable the use of front matter by this rule, specify `""` for
|
```
|
||||||
`front_matter_title`.
|
|
||||||
|
If [YAML][YAML] front matter is present and contains a `title` property
|
||||||
|
(commonly used with blog posts), this rule will not report a violation. To use a
|
||||||
|
different property name in the front matter, specify the text of a [regular
|
||||||
|
expression][RegExp] via the `front_matter_title` parameter. To disable the use
|
||||||
|
of front matter by this rule, specify `""` for `front_matter_title`.
|
||||||
|
|
||||||
|
The `level` parameter can be used to change the top-level heading (ex: to `h2`)
|
||||||
|
in cases where an `h1` is added externally.
|
||||||
|
|
||||||
Rationale: The top-level heading often acts as the title of a document. More
|
Rationale: The top-level heading often acts as the title of a document. More
|
||||||
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
||||||
|
|
||||||
|
[HTML]: https://en.wikipedia.org/wiki/HTML
|
||||||
|
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
[YAML]: https://en.wikipedia.org/wiki/YAML
|
||||||
|
|
|
@ -5,7 +5,7 @@ structure for a set of files.
|
||||||
To require exactly the following structure:
|
To require exactly the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail
|
### Detail
|
||||||
```
|
```
|
||||||
|
@ -14,7 +14,7 @@ Set the `headings` parameter to:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"### Detail"
|
"### Detail"
|
||||||
]
|
]
|
||||||
|
@ -23,7 +23,7 @@ Set the `headings` parameter to:
|
||||||
To allow optional headings as with the following structure:
|
To allow optional headings as with the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail (optional)
|
### Detail (optional)
|
||||||
## Foot
|
## Foot
|
||||||
|
@ -36,7 +36,7 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"*",
|
"*",
|
||||||
"## Foot",
|
"## Foot",
|
||||||
|
@ -44,6 +44,24 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To allow a single required heading to vary as with a project name:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Project Name
|
||||||
|
## Description
|
||||||
|
## Examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the special value `"?"` meaning "exactly one unspecified heading":
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"?",
|
||||||
|
"## Description",
|
||||||
|
"## Examples"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
When an error is detected, this rule outputs the line number of the first
|
When an error is detected, this rule outputs the line number of the first
|
||||||
problematic heading (otherwise, it outputs the last line number of the file).
|
problematic heading (otherwise, it outputs the last line number of the file).
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,16 @@ enforce the proper capitalization, specify the desired letter case in the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sometimes a proper name is capitalized differently in certain contexts. In such
|
||||||
|
cases, add both forms to the `names` array:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"GitHub",
|
||||||
|
"github.com"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
||||||
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
||||||
for HTML elements and attributes (such as when using a proper name as part of
|
for HTML elements and attributes (such as when using a proper name as part of
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
This rule is triggered when an image is missing alternate text (alt text)
|
This rule reports a violation when an image is missing alternate text (alt text)
|
||||||
information.
|
information.
|
||||||
|
|
||||||
Alternate text is commonly specified inline as:
|
Alternate text is commonly specified inline as:
|
||||||
|
@ -23,12 +23,20 @@ Or with HTML as:
|
||||||
<img src="image.jpg" alt="Alternate text" />
|
<img src="image.jpg" alt="Alternate text" />
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: If the [HTML `aria-hidden` attribute][aria-hidden] is used to hide the
|
||||||
|
image from assistive technology, this rule does not report a violation:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<img src="image.jpg" aria-hidden="true" />
|
||||||
|
```
|
||||||
|
|
||||||
Guidance for writing alternate text is available from the [W3C][w3c],
|
Guidance for writing alternate text is available from the [W3C][w3c],
|
||||||
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
||||||
|
|
||||||
Rationale: Alternate text is important for accessibility and describes the
|
Rationale: Alternate text is important for accessibility and describes the
|
||||||
content of an image for people who may not be able to see it.
|
content of an image for people who may not be able to see it.
|
||||||
|
|
||||||
|
[aria-hidden]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden
|
||||||
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
||||||
[w3c]: https://www.w3.org/WAI/alt/
|
[w3c]: https://www.w3.org/WAI/alt/
|
||||||
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
||||||
|
|
|
@ -23,7 +23,7 @@ document:
|
||||||
To fix violations of this rule, use a consistent style (either indenting or code
|
To fix violations of this rule, use a consistent style (either indenting or code
|
||||||
fences).
|
fences).
|
||||||
|
|
||||||
The specified style can be specific (`fenced`, `indented`) or simply require
|
The configured code block style can be specific (`fenced`, `indented`) or can
|
||||||
that usage be consistent within the document (`consistent`).
|
require all code blocks match the first code block (`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -24,7 +24,8 @@ document:
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (backtick, tilde), or
|
The configured code fence style can be a specific symbol to use (`backtick`,
|
||||||
can require that usage be consistent within the document.
|
`tilde`) or it can require all code fences match the first code fence
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -13,10 +13,11 @@ To fix this issue, use the configured emphasis style throughout the document:
|
||||||
*Text*
|
*Text*
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured emphasis style can be a specific symbol to use ("asterisk",
|
The configured emphasis style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all emphasis matches the first emphasis
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like_this_one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -13,10 +13,10 @@ To fix this issue, use the configured strong style throughout the document:
|
||||||
**Text**
|
**Text**
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured strong style can be a specific symbol to use ("asterisk",
|
The configured strong style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all strong matches the first strong (`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like__this__one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -16,9 +16,9 @@ generated name (see below):
|
||||||
[Link](#heading-name)
|
[Link](#heading-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Link fragments may be handled case-sensitively, so this rule requires fragments
|
For consistency, this rule requires fragments to exactly match the [GitHub
|
||||||
to exactly match the [GitHub heading algorithm][github-heading-algorithm].
|
heading algorithm][github-heading-algorithm] which converts letters to
|
||||||
Therefore, the following example is reported as a violation:
|
lowercase. Therefore, the following example is reported as a violation:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Heading Name
|
# Heading Name
|
||||||
|
@ -26,6 +26,10 @@ Therefore, the following example is reported as a violation:
|
||||||
[Link](#Heading-Name)
|
[Link](#Heading-Name)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To ignore case when comparing fragments with heading names, the `ignore_case`
|
||||||
|
parameter can be set to `true`. In this configuration, the previous example is
|
||||||
|
not reported as a violation.
|
||||||
|
|
||||||
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
||||||
within a heading to provide a specific name (consisting of only lower-case
|
within a heading to provide a specific name (consisting of only lower-case
|
||||||
letters, numbers, `-`, and `_`):
|
letters, numbers, `-`, and `_`):
|
||||||
|
@ -48,6 +52,13 @@ attribute can be used to define a fragment:
|
||||||
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
||||||
control over the text of the fragment identifier.
|
control over the text of the fragment identifier.
|
||||||
|
|
||||||
|
[HTML links to `#top` scroll to the top of a document][html-top-fragment]. This
|
||||||
|
rule allows that syntax (using lower-case for consistency):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[Link](#top)
|
||||||
|
```
|
||||||
|
|
||||||
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
||||||
[specific content in a document][github-linking-to-content].
|
[specific content in a document][github-linking-to-content].
|
||||||
|
|
||||||
|
@ -63,6 +74,12 @@ And this link to content starting within line 19 running into line 21:
|
||||||
[Link](#L19C5-L21C11)
|
[Link](#L19C5-L21C11)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Some Markdown generators dynamically create and insert headings when building
|
||||||
|
documents, for example by combining a fixed prefix like `figure-` and an
|
||||||
|
incrementing numeric counter. To ignore such generated fragments, set the
|
||||||
|
`ignored_pattern` [regular expression][RegEx] parameter to a pattern that
|
||||||
|
matches (e.g., `^figure-`).
|
||||||
|
|
||||||
Rationale: [GitHub section links][github-section-links] are created
|
Rationale: [GitHub section links][github-section-links] are created
|
||||||
automatically for every heading when Markdown content is displayed on GitHub.
|
automatically for every heading when Markdown content is displayed on GitHub.
|
||||||
This makes it easy to link directly to different sections within a document.
|
This makes it easy to link directly to different sections within a document.
|
||||||
|
@ -77,3 +94,5 @@ append an incrementing integer as needed for uniqueness.
|
||||||
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
||||||
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
||||||
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
||||||
|
[html-top-fragment]: https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
||||||
|
[RegEx]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
|
|
@ -28,3 +28,14 @@ so "shortcut" syntax is ignored by default. To include "shortcut" syntax, set
|
||||||
the `include_shortcut` parameter to `true`. Note that doing so produces warnings
|
the `include_shortcut` parameter to `true`. Note that doing so produces warnings
|
||||||
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
||||||
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
||||||
|
|
||||||
|
If there are link labels that are deliberately unreferenced, they can be ignored
|
||||||
|
by setting the `ignored_labels` parameter to the list of strings to ignore. The
|
||||||
|
default value of this parameter ignores the checkbox syntax used by
|
||||||
|
[GitHub Flavored Markdown task list items][gfm-tasklist]:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- [x] Checked task list item
|
||||||
|
```
|
||||||
|
|
||||||
|
[gfm-tasklist]: https://github.github.com/gfm/#task-list-items-extension-
|
||||||
|
|
|
@ -17,9 +17,9 @@ reference has the corresponding label. The "full", "collapsed", and "shortcut"
|
||||||
formats are all supported.
|
formats are all supported.
|
||||||
|
|
||||||
If there are reference definitions that are deliberately unreferenced, they can
|
If there are reference definitions that are deliberately unreferenced, they can
|
||||||
be ignored by setting the `ignored_definitions` parameter. The default value of
|
be ignored by setting the `ignored_definitions` parameter to the list of strings
|
||||||
this parameter ignores the following convention for adding non-HTML comments to
|
to ignore. The default value of this parameter ignores the following convention
|
||||||
Markdown:
|
for adding non-HTML comments to Markdown:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[//]: # (This behaves like a comment)
|
[//]: # (This behaves like a comment)
|
||||||
|
|
|
@ -2,8 +2,9 @@ This rule is triggered when a [GitHub Flavored Markdown table][gfm-table-055]
|
||||||
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
||||||
|
|
||||||
By default (`consistent` style), the header row of the first table in a document
|
By default (`consistent` style), the header row of the first table in a document
|
||||||
is used to determine the style that is enforced for all tables in that document.
|
is used to determine the style that is enforced for every table in the document.
|
||||||
A specific style can be required by setting the `style` parameter accordingly.
|
A specific style can be used instead (`leading_and_trailing`, `leading_only`,
|
||||||
|
`no_leading_or_trailing`, `trailing_only`).
|
||||||
|
|
||||||
This table's header row has leading and trailing pipes, but its delimiter row is
|
This table's header row has leading and trailing pipes, but its delimiter row is
|
||||||
missing the trailing pipe and its first row of cells is missing the leading
|
missing the trailing pipe and its first row of cells is missing the leading
|
||||||
|
|
40
doc-build/md058.md
Normal file
40
doc-build/md058.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
This rule is triggered when tables are either not preceded or not followed by a
|
||||||
|
blank line:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
To fix violations of this rule, ensure that all tables have a blank line both
|
||||||
|
before and after (except when the table is at the very beginning or end of the
|
||||||
|
document):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that text immediately following a table (i.e., not separated by an empty
|
||||||
|
line) is treated as part of the table (per the specification) and will not
|
||||||
|
trigger this rule:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
This text is part of the table and the next line is blank
|
||||||
|
|
||||||
|
Some text
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: In addition to aesthetic reasons, some parsers will incorrectly parse
|
||||||
|
tables that don't have blank lines before and after them.
|
19
doc-build/md059.md
Normal file
19
doc-build/md059.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
This rule is triggered when a link has generic text like `[click here](...)` or
|
||||||
|
`[link](...)`.
|
||||||
|
|
||||||
|
Link text should be descriptive and communicate the purpose of the link (e.g.,
|
||||||
|
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
|
||||||
|
This is especially important for screen readers which sometimes present links
|
||||||
|
without context.
|
||||||
|
|
||||||
|
By default, this rule prohibits a small number of common English words/phrases.
|
||||||
|
To customize that list of words/phrases, set the `prohibited_texts` parameter to
|
||||||
|
an `Array` of `string`s.
|
||||||
|
|
||||||
|
Note: For languages other than English, use the `prohibited_texts` parameter to
|
||||||
|
customize the list for that language. It is *not* a goal for this rule to have
|
||||||
|
translations for every language.
|
||||||
|
|
||||||
|
Note: This rule checks Markdown links; HTML links are ignored.
|
||||||
|
|
||||||
|
More information: <https://webaim.org/techniques/hypertext/>
|
|
@ -1,20 +1,21 @@
|
||||||
# Custom Rules
|
# Custom Rules
|
||||||
|
|
||||||
In addition to its built-in rules, `markdownlint` lets you enhance the linting
|
In addition to its built-in rules, `markdownlint` lets you enhance the linting
|
||||||
experience by passing a list of custom rules using the [`options.customRules`
|
experience by passing an array of custom rules using the [`options.customRules`
|
||||||
property][options-custom-rules]. Custom rules can do everything the built-in
|
property][options-custom-rules]. Custom rules can do everything the built-in
|
||||||
rules can and are defined inline or imported from another package ([keyword
|
rules can and are defined inline or imported from another package ([keyword
|
||||||
`markdownlint-rule` on npm][markdownlint-rule]). Custom rules can be disabled,
|
`markdownlint-rule` on npm][markdownlint-rule]). When defined by a file or
|
||||||
enabled, and customized using the same syntax as built-in rules.
|
package, the export can be a single rule object (see below) or an array of them.
|
||||||
|
Custom rules can be disabled, enabled, and customized using the same syntax as
|
||||||
|
built-in rules.
|
||||||
|
|
||||||
## Implementing Simple Rules
|
## Implementing Simple Rules
|
||||||
|
|
||||||
For simple requirements like disallowing certain characters or patterns,
|
For simple requirements like disallowing certain characters or patterns,
|
||||||
the community-developed
|
the community-developed
|
||||||
[markdownlint-rule-search-replace][markdownlint-rule-search-replace]
|
[markdownlint-rule-search-replace][markdownlint-rule-search-replace]
|
||||||
plug-in can be used.
|
plug-in can be used. This plug-in allows anyone to create a set of simple
|
||||||
This plug-in allows anyone to create a set of simple text-replacement rules in
|
text-replacement rules without needing to write code.
|
||||||
JSON without needing to write any code.
|
|
||||||
|
|
||||||
[markdownlint-rule-search-replace]: https://www.npmjs.com/package/markdownlint-rule-search-replace
|
[markdownlint-rule-search-replace]: https://www.npmjs.com/package/markdownlint-rule-search-replace
|
||||||
|
|
||||||
|
@ -25,29 +26,62 @@ to more information, one or more tags, and a function that implements the rule's
|
||||||
behavior. That function is called once for each file/string input and is passed
|
behavior. That function is called once for each file/string input and is passed
|
||||||
the parsed input and a function to log any violations.
|
the parsed input and a function to log any violations.
|
||||||
|
|
||||||
A simple rule implementation looks like:
|
Custom rules can (should) operate on a structured set of tokens based on the
|
||||||
|
[`micromark`][micromark] `parser` (this is preferred). Alternatively, custom
|
||||||
|
rules can operate on a structured set of tokens based on the
|
||||||
|
[`markdown-it`][markdown-it] `parser` (legacy support). Finally, custom rules
|
||||||
|
can operate directly on text with the `none` `parser`.
|
||||||
|
|
||||||
|
A simple rule implementation using the `micromark` parser to report a violation
|
||||||
|
for any use of blockquotes might look like:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/** @type import("markdownlint").Rule */
|
/** @type {import("markdownlint").Rule} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": [ "any-blockquote" ],
|
"names": [ "any-blockquote-micromark" ],
|
||||||
|
"description": "Rule that reports an error for any blockquote",
|
||||||
|
"information": new URL("https://example.com/rules/any-blockquote"),
|
||||||
|
"tags": [ "test" ],
|
||||||
|
"parser": "micromark",
|
||||||
|
"function": (params, onError) => {
|
||||||
|
const blockquotes = params.parsers.micromark.tokens
|
||||||
|
.filter((token) => token.type === "blockQuote");
|
||||||
|
for (const blockquote of blockquotes) {
|
||||||
|
const lines = blockquote.endLine - blockquote.startLine + 1;
|
||||||
|
onError({
|
||||||
|
"lineNumber": blockquote.startLine,
|
||||||
|
"detail": "Blockquote spans " + lines + " line(s).",
|
||||||
|
"context": params.lines[blockquote.startLine - 1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That same rule implemented using the `markdown-it` parser might look like:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @type {import("markdownlint").Rule} */
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "any-blockquote-markdown-it" ],
|
||||||
"description": "Rule that reports an error for any blockquote",
|
"description": "Rule that reports an error for any blockquote",
|
||||||
"information": new URL("https://example.com/rules/any-blockquote"),
|
"information": new URL("https://example.com/rules/any-blockquote"),
|
||||||
"tags": [ "test" ],
|
"tags": [ "test" ],
|
||||||
"parser": "markdownit",
|
"parser": "markdownit",
|
||||||
"function": function rule(params, onError) {
|
"function": (params, onError) => {
|
||||||
params.parsers.markdownit.tokens.filter(function filterToken(token) {
|
const blockquotes = params.parsers.markdownit.tokens
|
||||||
return token.type === "blockquote_open";
|
.filter((token) => token.type === "blockquote_open");
|
||||||
}).forEach(function forToken(blockquote) {
|
for (const blockquote of blockquotes) {
|
||||||
var lines = blockquote.map[1] - blockquote.map[0];
|
const [ startIndex, endIndex ] = blockquote.map;
|
||||||
|
const lines = endIndex - startIndex;
|
||||||
onError({
|
onError({
|
||||||
"lineNumber": blockquote.lineNumber,
|
"lineNumber": blockquote.lineNumber,
|
||||||
"detail": "Blockquote spans " + lines + " line(s).",
|
"detail": "Blockquote spans " + lines + " line(s).",
|
||||||
"context": blockquote.line.substr(0, 7)
|
"context": blockquote.line
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A rule is implemented as an `Object`:
|
A rule is implemented as an `Object`:
|
||||||
|
@ -60,9 +94,8 @@ A rule is implemented as an `Object`:
|
||||||
about the rule.
|
about the rule.
|
||||||
- `tags` is a required `Array` of `String` values that groups related rules for
|
- `tags` is a required `Array` of `String` values that groups related rules for
|
||||||
easier customization.
|
easier customization.
|
||||||
- `parser` is a required `String` value `"markdownit" | "none"` that specifies
|
- `parser` is a required `String` value `"markdownit" | "micromark" | "none"`
|
||||||
the parser data used via `params.parsers` (see below).
|
that specifies the parser data used via `params.parsers` (see below).
|
||||||
- Note: The value `"micromark"` is valid but is NOT currently supported.
|
|
||||||
- `asynchronous` is an optional `Boolean` value that indicates whether the rule
|
- `asynchronous` is an optional `Boolean` value that indicates whether the rule
|
||||||
returns a `Promise` and runs asynchronously.
|
returns a `Promise` and runs asynchronously.
|
||||||
- `function` is a required `Function` that implements the rule and is passed two
|
- `function` is a required `Function` that implements the rule and is passed two
|
||||||
|
@ -77,12 +110,17 @@ A rule is implemented as an `Object`:
|
||||||
- `tokens` is an `Array` of [`markdown-it` `Token`s][markdown-it-token]
|
- `tokens` is an `Array` of [`markdown-it` `Token`s][markdown-it-token]
|
||||||
with added `line` and `lineNumber` properties. (This property was
|
with added `line` and `lineNumber` properties. (This property was
|
||||||
previously on the `params` object.)
|
previously on the `params` object.)
|
||||||
|
- `micromark` is an `Object` that provides access to output from the
|
||||||
|
[`micromark`][micromark] parser.
|
||||||
|
- `tokens` is an `Array` of [`MicromarkToken`][micromark-token] objects.
|
||||||
|
- Samples for both `tokens` are available via [test snapshots][tokens].
|
||||||
- `lines` is an `Array` of `String` values corresponding to the lines of the
|
- `lines` is an `Array` of `String` values corresponding to the lines of the
|
||||||
input file/string.
|
input file/string.
|
||||||
- `frontMatterLines` is an `Array` of `String` values corresponding to any
|
- `frontMatterLines` is an `Array` of `String` values corresponding to any
|
||||||
front matter (not present in `lines`).
|
front matter (not present in `lines`).
|
||||||
- `config` is an `Object` corresponding to the rule's entry in
|
- `config` is an `Object` corresponding to the rule's entry in
|
||||||
`options.config` (if present).
|
`options.config` (if present).
|
||||||
|
- `version` is a `String` that corresponds to the version of `markdownlint`
|
||||||
- `onError` is a function that takes a single `Object` parameter with one
|
- `onError` is a function that takes a single `Object` parameter with one
|
||||||
required and four optional properties:
|
required and four optional properties:
|
||||||
- `lineNumber` is a required `Number` specifying the 1-based line number of
|
- `lineNumber` is a required `Number` specifying the 1-based line number of
|
||||||
|
@ -122,15 +160,16 @@ implementation that is resolved when the rule completes. (The value passed to
|
||||||
reported via the `onError` function just like for synchronous rules.
|
reported via the `onError` function just like for synchronous rules.
|
||||||
|
|
||||||
**Note**: Asynchronous rules cannot be referenced in a synchronous calling
|
**Note**: Asynchronous rules cannot be referenced in a synchronous calling
|
||||||
context (i.e., `markdownlint.sync(...)`). Attempting to do so throws an
|
context (i.e., `import { lint } from "markdownlint/sync"`). Attempting to do so
|
||||||
exception.
|
throws an exception.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
- [Simple rules used by the project's test cases][test-rules]
|
- [Simple rules used by the project's test cases][test-rules]
|
||||||
- [Code for all `markdownlint` built-in rules][lib]
|
- [Code for all `markdownlint` built-in rules][lib]
|
||||||
- [Package configuration for publishing to npm][test-rules-npm]
|
- [Complete example rule including npm configuration][extended-ascii]
|
||||||
- Packages should export a single rule object or an `Array` of rule objects
|
- [Custom rules from the github/docs repository][github-docs]
|
||||||
|
- [Custom rules from the electron/lint-roller repository][electron]
|
||||||
- [Custom rules from the webhintio/hint repository][hint]
|
- [Custom rules from the webhintio/hint repository][hint]
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
@ -138,246 +177,18 @@ exception.
|
||||||
- [CommonMark documentation and specification][commonmark]
|
- [CommonMark documentation and specification][commonmark]
|
||||||
- [`markdown-it` Markdown parser project page][markdown-it]
|
- [`markdown-it` Markdown parser project page][markdown-it]
|
||||||
|
|
||||||
## Params
|
|
||||||
|
|
||||||
The Markdown document:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Title
|
|
||||||
|
|
||||||
Text *text* text.
|
|
||||||
```
|
|
||||||
|
|
||||||
Yields the `params` object:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "doc/example.md",
|
|
||||||
"parsers.markdownit.tokens": [
|
|
||||||
{
|
|
||||||
"type": "heading_open",
|
|
||||||
"tag": "h1",
|
|
||||||
"attrs": null,
|
|
||||||
"map": [ 0, 1 ],
|
|
||||||
"nesting": 1,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "#",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false,
|
|
||||||
"line": "# Title",
|
|
||||||
"lineNumber": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "inline",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": [ 0, 1 ],
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 1,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "Title",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 1,
|
|
||||||
"line": "# Title"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"content": "Title",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false,
|
|
||||||
"line": "# Title",
|
|
||||||
"lineNumber": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "heading_close",
|
|
||||||
"tag": "h1",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": -1,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "#",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paragraph_open",
|
|
||||||
"tag": "p",
|
|
||||||
"attrs": null,
|
|
||||||
"map": [ 2, 3 ],
|
|
||||||
"nesting": 1,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false,
|
|
||||||
"line": "Text *text* text.",
|
|
||||||
"lineNumber": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "inline",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": [ 2, 3 ],
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 1,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "Text ",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 3,
|
|
||||||
"line": "Text *text* text."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "em_open",
|
|
||||||
"tag": "em",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": 1,
|
|
||||||
"level": 1,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "*",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 3,
|
|
||||||
"line": "Text *text* text."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 1,
|
|
||||||
"children": null,
|
|
||||||
"content": "text",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 3,
|
|
||||||
"line": "Text *text* text."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "em_close",
|
|
||||||
"tag": "em",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": -1,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "*",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 3,
|
|
||||||
"line": "Text *text* text."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"tag": "",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": 0,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": " text.",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": false,
|
|
||||||
"hidden": false,
|
|
||||||
"lineNumber": 3,
|
|
||||||
"line": "Text *text* text."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"content": "Text *text* text.",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false,
|
|
||||||
"line": "Text *text* text.",
|
|
||||||
"lineNumber": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paragraph_close",
|
|
||||||
"tag": "p",
|
|
||||||
"attrs": null,
|
|
||||||
"map": null,
|
|
||||||
"nesting": -1,
|
|
||||||
"level": 0,
|
|
||||||
"children": null,
|
|
||||||
"content": "",
|
|
||||||
"markup": "",
|
|
||||||
"info": "",
|
|
||||||
"meta": null,
|
|
||||||
"block": true,
|
|
||||||
"hidden": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lines": [
|
|
||||||
"# Title",
|
|
||||||
"",
|
|
||||||
"Text *text* text.",
|
|
||||||
""
|
|
||||||
],
|
|
||||||
"frontMatterLines": [],
|
|
||||||
"config": {
|
|
||||||
"customValue1": "abc",
|
|
||||||
"customValue2": 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[commonmark]: https://commonmark.org/
|
[commonmark]: https://commonmark.org/
|
||||||
|
[electron]: https://github.com/electron/lint-roller/tree/main/markdownlint-rules
|
||||||
|
[extended-ascii]: https://github.com/DavidAnson/markdownlint-rule-extended-ascii
|
||||||
|
[github-docs]: https://github.com/github/docs/tree/main/src/content-linter/lib/linting-rules
|
||||||
[hint]: https://github.com/webhintio/hint/blob/main/scripts/lint-markdown.js
|
[hint]: https://github.com/webhintio/hint/blob/main/scripts/lint-markdown.js
|
||||||
[lib]: ../lib
|
[lib]: ../lib
|
||||||
[markdown-it]: https://github.com/markdown-it/markdown-it
|
[markdown-it]: https://github.com/markdown-it/markdown-it
|
||||||
[markdown-it-token]: https://markdown-it.github.io/markdown-it/#Token
|
[markdown-it-token]: https://markdown-it.github.io/markdown-it/#Token
|
||||||
[markdownlint-rule]: https://www.npmjs.com/search?q=keywords:markdownlint-rule
|
[markdownlint-rule]: https://www.npmjs.com/search?q=keywords:markdownlint-rule
|
||||||
|
[micromark]: https://github.com/micromark/micromark
|
||||||
|
[micromark-token]: ../lib/markdownlint.d.mts
|
||||||
[rule-helpers]: https://www.npmjs.com/package/markdownlint-rule-helpers
|
[rule-helpers]: https://www.npmjs.com/package/markdownlint-rule-helpers
|
||||||
[options-custom-rules]: ../README.md#optionscustomrules
|
[options-custom-rules]: ../README.md#optionscustomrules
|
||||||
[test-rules]: ../test/rules
|
[test-rules]: ../test/rules
|
||||||
[test-rules-npm]: ../test/rules/npm
|
[tokens]: ../test/snapshots/markdownlint-test-custom-rules.mjs.md
|
||||||
|
|
324
doc/Rules.md
324
doc/Rules.md
|
@ -93,8 +93,8 @@ Setext style H2
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The configured heading style can be a specific style to require (`atx`,
|
Note: The configured heading style can be a specific style to require (`atx`,
|
||||||
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or may
|
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or can
|
||||||
just require that usage is consistent within the document via `consistent`.
|
require that all heading styles match the first heading style via `consistent`.
|
||||||
|
|
||||||
Note: The placement of a horizontal rule directly below a line of text can
|
Note: The placement of a horizontal rule directly below a line of text can
|
||||||
trigger this rule by turning that text into a level 2 setext-style heading:
|
trigger this rule by turning that text into a level 2 setext-style heading:
|
||||||
|
@ -139,9 +139,10 @@ document:
|
||||||
* Item 3
|
* Item 3
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (asterisk, plus,
|
The configured list style can ensure all list styling is a specific symbol
|
||||||
dash), to ensure that all list styling is consistent, or to ensure that each
|
(`asterisk`, `plus`, `dash`), ensure each sublist has a consistent symbol that
|
||||||
sublist has a consistent symbol that differs from its parent list.
|
differs from its parent list (`sublist`), or ensure all list styles match the
|
||||||
|
first list style (`consistent`).
|
||||||
|
|
||||||
For example, the following is valid for the `sublist` style because the
|
For example, the following is valid for the `sublist` style because the
|
||||||
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
||||||
|
@ -942,6 +943,10 @@ Tags: `blockquote`, `indentation`, `whitespace`
|
||||||
|
|
||||||
Aliases: `no-multiple-space-blockquote`
|
Aliases: `no-multiple-space-blockquote`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `list_items`: Include list items (`boolean`, default `true`)
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered when blockquotes have more than one space after the
|
This rule is triggered when blockquotes have more than one space after the
|
||||||
|
@ -959,6 +964,10 @@ To fix, remove any extraneous space:
|
||||||
> indentation.
|
> indentation.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Inferring intended list indentation within a blockquote can be challenging;
|
||||||
|
setting the `list_items` parameter to `false` disables this rule for ordered
|
||||||
|
and unordered list items.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
||||||
<a name="md028"></a>
|
<a name="md028"></a>
|
||||||
|
@ -1354,6 +1363,11 @@ To fix this, add angle brackets around the URL or email address:
|
||||||
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a URL or email address contains non-ASCII characters, it may be not be
|
||||||
|
handled as intended even when angle brackets are present. In such cases,
|
||||||
|
[percent-encoding](https://en.m.wikipedia.org/wiki/Percent-encoding) can be used
|
||||||
|
to comply with the required syntax for URL and email.
|
||||||
|
|
||||||
Note: To include a bare URL or email without it being converted into a link,
|
Note: To include a bare URL or email without it being converted into a link,
|
||||||
wrap it in a code span:
|
wrap it in a code span:
|
||||||
|
|
||||||
|
@ -1411,8 +1425,7 @@ in the document:
|
||||||
****
|
****
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, ensure any horizontal rules used in the document are consistent,
|
To fix this, use the same horizontal rule everywhere:
|
||||||
or match the given style if the rule is so configured:
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
---
|
---
|
||||||
|
@ -1420,12 +1433,8 @@ or match the given style if the rule is so configured:
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: by default, this rule is configured to just require that all horizontal
|
The configured style can ensure all horizontal rules use a specific string or it
|
||||||
rules in the document are the same and will trigger if any of the horizontal
|
can ensure all horizontal rules match the first horizontal rule (`consistent`).
|
||||||
rules are different than the first one encountered in the document. If you
|
|
||||||
want to configure the rule to match a specific style, the parameter given to
|
|
||||||
the 'style' option is a string containing the exact horizontal rule text that
|
|
||||||
is allowed.
|
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
||||||
|
@ -1527,38 +1536,50 @@ Aliases: `no-space-in-code`
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered for code span elements that have spaces adjacent to the
|
This rule is triggered for code spans containing content with unnecessary space
|
||||||
backticks:
|
next to the beginning or ending backticks:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text `
|
`some text `
|
||||||
|
|
||||||
` some text`
|
` some text`
|
||||||
|
|
||||||
|
` some text `
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, remove any spaces adjacent to the backticks:
|
To fix this, remove the extra space characters from the beginning and ending:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text`
|
`some text`
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading and trailing space is allowed by the specification and
|
Note: A single leading *and* trailing space is allowed by the specification and
|
||||||
automatically trimmed (in order to allow for code spans that embed backticks):
|
trimmed by the parser to support code spans that begin or end with a backtick:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` `backticks` ``
|
`` `backticks` ``
|
||||||
|
|
||||||
|
`` backtick` ``
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading or trailing space is allowed if used to separate code
|
Note: When single-space padding is present in the input, it will be preserved
|
||||||
span markers from an embedded backtick (though the space is not trimmed):
|
(even if unnecessary):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` ` embedded backtick``
|
` code `
|
||||||
```
|
```
|
||||||
|
|
||||||
Rationale: Violations of this rule are usually unintentional and may lead to
|
Note: Code spans containing only spaces are allowed by the specification and are
|
||||||
improperly-rendered content. If spaces beside backticks are intentional, this
|
also preserved:
|
||||||
rule can be disabled for that line or file.
|
|
||||||
|
```markdown
|
||||||
|
` `
|
||||||
|
|
||||||
|
` `
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: Violations of this rule are usually unintentional and can lead to
|
||||||
|
improperly-rendered content.
|
||||||
|
|
||||||
<a name="md039"></a>
|
<a name="md039"></a>
|
||||||
|
|
||||||
|
@ -1649,48 +1670,63 @@ Aliases: `first-line-h1`, `first-line-heading`
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
|
- `allow_preamble`: Allow content before first heading (`boolean`, default
|
||||||
|
`false`)
|
||||||
- `front_matter_title`: RegExp for matching title in front matter (`string`,
|
- `front_matter_title`: RegExp for matching title in front matter (`string`,
|
||||||
default `^\s*title\s*[:=]`)
|
default `^\s*title\s*[:=]`)
|
||||||
- `level`: Heading level (`integer`, default `1`)
|
- `level`: Heading level (`integer`, default `1`)
|
||||||
|
|
||||||
This rule is intended to ensure documents have a title and is triggered when
|
This rule is intended to ensure documents have a title and is triggered when
|
||||||
the first line in the file isn't a top-level (h1) heading:
|
the first line in a document is not a top-level ([HTML][HTML] `h1`) heading:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
This is a file without a heading
|
This is a document without a heading
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, add a top-level heading to the beginning of the file:
|
To fix this, add a top-level heading to the beginning of the document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# File with heading
|
# Document Heading
|
||||||
|
|
||||||
This is a file with a top-level heading
|
This is a document with a top-level heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Because it is common for projects on GitHub to use an image for the heading of
|
Because it is common for projects on GitHub to use an image for the heading of
|
||||||
`README.md` and that is not well-supported by Markdown, HTML headings are also
|
`README.md` and that pattern is not well-supported by Markdown, HTML headings
|
||||||
permitted by this rule. For example:
|
are also permitted by this rule. For example:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
||||||
|
|
||||||
This is a file with a top-level HTML heading
|
This is a document with a top-level HTML heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The `level` parameter can be used to change the top-level (ex: to h2) in
|
In some cases, a document's title heading may be preceded by text like a table
|
||||||
cases where an h1 is added externally.
|
of contents. This is not ideal for accessibility, but can be allowed by setting
|
||||||
|
the `allow_preamble` parameter to `true`.
|
||||||
|
|
||||||
If [YAML](https://en.wikipedia.org/wiki/YAML) front matter is present and
|
```markdown
|
||||||
contains a `title` property (commonly used with blog posts), this rule will not
|
This is a document with preamble text
|
||||||
report a violation. To use a different property name in the front matter,
|
|
||||||
specify the text of a regular expression via the `front_matter_title` parameter.
|
# Document Heading
|
||||||
To disable the use of front matter by this rule, specify `""` for
|
```
|
||||||
`front_matter_title`.
|
|
||||||
|
If [YAML][YAML] front matter is present and contains a `title` property
|
||||||
|
(commonly used with blog posts), this rule will not report a violation. To use a
|
||||||
|
different property name in the front matter, specify the text of a [regular
|
||||||
|
expression][RegExp] via the `front_matter_title` parameter. To disable the use
|
||||||
|
of front matter by this rule, specify `""` for `front_matter_title`.
|
||||||
|
|
||||||
|
The `level` parameter can be used to change the top-level heading (ex: to `h2`)
|
||||||
|
in cases where an `h1` is added externally.
|
||||||
|
|
||||||
Rationale: The top-level heading often acts as the title of a document. More
|
Rationale: The top-level heading often acts as the title of a document. More
|
||||||
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
||||||
|
|
||||||
|
[HTML]: https://en.wikipedia.org/wiki/HTML
|
||||||
|
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
[YAML]: https://en.wikipedia.org/wiki/YAML
|
||||||
|
|
||||||
<a name="md042"></a>
|
<a name="md042"></a>
|
||||||
|
|
||||||
## `MD042` - No empty links
|
## `MD042` - No empty links
|
||||||
|
@ -1746,7 +1782,7 @@ structure for a set of files.
|
||||||
To require exactly the following structure:
|
To require exactly the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail
|
### Detail
|
||||||
```
|
```
|
||||||
|
@ -1755,7 +1791,7 @@ Set the `headings` parameter to:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"### Detail"
|
"### Detail"
|
||||||
]
|
]
|
||||||
|
@ -1764,7 +1800,7 @@ Set the `headings` parameter to:
|
||||||
To allow optional headings as with the following structure:
|
To allow optional headings as with the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail (optional)
|
### Detail (optional)
|
||||||
## Foot
|
## Foot
|
||||||
|
@ -1777,7 +1813,7 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"*",
|
"*",
|
||||||
"## Foot",
|
"## Foot",
|
||||||
|
@ -1785,6 +1821,24 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To allow a single required heading to vary as with a project name:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Project Name
|
||||||
|
## Description
|
||||||
|
## Examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the special value `"?"` meaning "exactly one unspecified heading":
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"?",
|
||||||
|
"## Description",
|
||||||
|
"## Examples"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
When an error is detected, this rule outputs the line number of the first
|
When an error is detected, this rule outputs the line number of the first
|
||||||
problematic heading (otherwise, it outputs the last line number of the file).
|
problematic heading (otherwise, it outputs the last line number of the file).
|
||||||
|
|
||||||
|
@ -1829,6 +1883,16 @@ enforce the proper capitalization, specify the desired letter case in the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sometimes a proper name is capitalized differently in certain contexts. In such
|
||||||
|
cases, add both forms to the `names` array:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"GitHub",
|
||||||
|
"github.com"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
||||||
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
||||||
for HTML elements and attributes (such as when using a proper name as part of
|
for HTML elements and attributes (such as when using a proper name as part of
|
||||||
|
@ -1844,7 +1908,7 @@ Tags: `accessibility`, `images`
|
||||||
|
|
||||||
Aliases: `no-alt-text`
|
Aliases: `no-alt-text`
|
||||||
|
|
||||||
This rule is triggered when an image is missing alternate text (alt text)
|
This rule reports a violation when an image is missing alternate text (alt text)
|
||||||
information.
|
information.
|
||||||
|
|
||||||
Alternate text is commonly specified inline as:
|
Alternate text is commonly specified inline as:
|
||||||
|
@ -1869,12 +1933,20 @@ Or with HTML as:
|
||||||
<img src="image.jpg" alt="Alternate text" />
|
<img src="image.jpg" alt="Alternate text" />
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: If the [HTML `aria-hidden` attribute][aria-hidden] is used to hide the
|
||||||
|
image from assistive technology, this rule does not report a violation:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<img src="image.jpg" aria-hidden="true" />
|
||||||
|
```
|
||||||
|
|
||||||
Guidance for writing alternate text is available from the [W3C][w3c],
|
Guidance for writing alternate text is available from the [W3C][w3c],
|
||||||
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
||||||
|
|
||||||
Rationale: Alternate text is important for accessibility and describes the
|
Rationale: Alternate text is important for accessibility and describes the
|
||||||
content of an image for people who may not be able to see it.
|
content of an image for people who may not be able to see it.
|
||||||
|
|
||||||
|
[aria-hidden]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden
|
||||||
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
||||||
[w3c]: https://www.w3.org/WAI/alt/
|
[w3c]: https://www.w3.org/WAI/alt/
|
||||||
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
||||||
|
@ -1917,8 +1989,8 @@ document:
|
||||||
To fix violations of this rule, use a consistent style (either indenting or code
|
To fix violations of this rule, use a consistent style (either indenting or code
|
||||||
fences).
|
fences).
|
||||||
|
|
||||||
The specified style can be specific (`fenced`, `indented`) or simply require
|
The configured code block style can be specific (`fenced`, `indented`) or can
|
||||||
that usage be consistent within the document (`consistent`).
|
require all code blocks match the first code block (`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
||||||
|
@ -1998,8 +2070,9 @@ document:
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (backtick, tilde), or
|
The configured code fence style can be a specific symbol to use (`backtick`,
|
||||||
can require that usage be consistent within the document.
|
`tilde`) or it can require all code fences match the first code fence
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
||||||
|
@ -2033,10 +2106,11 @@ To fix this issue, use the configured emphasis style throughout the document:
|
||||||
*Text*
|
*Text*
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured emphasis style can be a specific symbol to use ("asterisk",
|
The configured emphasis style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all emphasis matches the first emphasis
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like_this_one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
@ -2071,11 +2145,11 @@ To fix this issue, use the configured strong style throughout the document:
|
||||||
**Text**
|
**Text**
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured strong style can be a specific symbol to use ("asterisk",
|
The configured strong style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all strong matches the first strong (`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like__this__one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
||||||
|
@ -2087,6 +2161,12 @@ Tags: `links`
|
||||||
|
|
||||||
Aliases: `link-fragments`
|
Aliases: `link-fragments`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `ignore_case`: Ignore case of fragments (`boolean`, default `false`)
|
||||||
|
- `ignored_pattern`: Pattern for ignoring additional fragments (`string`,
|
||||||
|
default ``)
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered when a link fragment does not match any of the fragments
|
This rule is triggered when a link fragment does not match any of the fragments
|
||||||
|
@ -2107,9 +2187,9 @@ generated name (see below):
|
||||||
[Link](#heading-name)
|
[Link](#heading-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Link fragments may be handled case-sensitively, so this rule requires fragments
|
For consistency, this rule requires fragments to exactly match the [GitHub
|
||||||
to exactly match the [GitHub heading algorithm][github-heading-algorithm].
|
heading algorithm][github-heading-algorithm] which converts letters to
|
||||||
Therefore, the following example is reported as a violation:
|
lowercase. Therefore, the following example is reported as a violation:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Heading Name
|
# Heading Name
|
||||||
|
@ -2117,6 +2197,10 @@ Therefore, the following example is reported as a violation:
|
||||||
[Link](#Heading-Name)
|
[Link](#Heading-Name)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To ignore case when comparing fragments with heading names, the `ignore_case`
|
||||||
|
parameter can be set to `true`. In this configuration, the previous example is
|
||||||
|
not reported as a violation.
|
||||||
|
|
||||||
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
||||||
within a heading to provide a specific name (consisting of only lower-case
|
within a heading to provide a specific name (consisting of only lower-case
|
||||||
letters, numbers, `-`, and `_`):
|
letters, numbers, `-`, and `_`):
|
||||||
|
@ -2139,6 +2223,13 @@ attribute can be used to define a fragment:
|
||||||
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
||||||
control over the text of the fragment identifier.
|
control over the text of the fragment identifier.
|
||||||
|
|
||||||
|
[HTML links to `#top` scroll to the top of a document][html-top-fragment]. This
|
||||||
|
rule allows that syntax (using lower-case for consistency):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[Link](#top)
|
||||||
|
```
|
||||||
|
|
||||||
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
||||||
[specific content in a document][github-linking-to-content].
|
[specific content in a document][github-linking-to-content].
|
||||||
|
|
||||||
|
@ -2154,6 +2245,12 @@ And this link to content starting within line 19 running into line 21:
|
||||||
[Link](#L19C5-L21C11)
|
[Link](#L19C5-L21C11)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Some Markdown generators dynamically create and insert headings when building
|
||||||
|
documents, for example by combining a fixed prefix like `figure-` and an
|
||||||
|
incrementing numeric counter. To ignore such generated fragments, set the
|
||||||
|
`ignored_pattern` [regular expression][RegEx] parameter to a pattern that
|
||||||
|
matches (e.g., `^figure-`).
|
||||||
|
|
||||||
Rationale: [GitHub section links][github-section-links] are created
|
Rationale: [GitHub section links][github-section-links] are created
|
||||||
automatically for every heading when Markdown content is displayed on GitHub.
|
automatically for every heading when Markdown content is displayed on GitHub.
|
||||||
This makes it easy to link directly to different sections within a document.
|
This makes it easy to link directly to different sections within a document.
|
||||||
|
@ -2168,6 +2265,8 @@ append an incrementing integer as needed for uniqueness.
|
||||||
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
||||||
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
||||||
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
||||||
|
[html-top-fragment]: https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
||||||
|
[RegEx]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
|
||||||
<a name="md052"></a>
|
<a name="md052"></a>
|
||||||
|
|
||||||
|
@ -2179,6 +2278,7 @@ Aliases: `reference-links-images`
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
|
- `ignored_labels`: Ignored link labels (`string[]`, default `["x"]`)
|
||||||
- `shortcut_syntax`: Include shortcut syntax (`boolean`, default `false`)
|
- `shortcut_syntax`: Include shortcut syntax (`boolean`, default `false`)
|
||||||
|
|
||||||
Links and images in Markdown can provide the link destination or image source
|
Links and images in Markdown can provide the link destination or image source
|
||||||
|
@ -2212,6 +2312,17 @@ the `include_shortcut` parameter to `true`. Note that doing so produces warnings
|
||||||
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
||||||
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
||||||
|
|
||||||
|
If there are link labels that are deliberately unreferenced, they can be ignored
|
||||||
|
by setting the `ignored_labels` parameter to the list of strings to ignore. The
|
||||||
|
default value of this parameter ignores the checkbox syntax used by
|
||||||
|
[GitHub Flavored Markdown task list items][gfm-tasklist]:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- [x] Checked task list item
|
||||||
|
```
|
||||||
|
|
||||||
|
[gfm-tasklist]: https://github.github.com/gfm/#task-list-items-extension-
|
||||||
|
|
||||||
<a name="md053"></a>
|
<a name="md053"></a>
|
||||||
|
|
||||||
## `MD053` - Link and image reference definitions should be needed
|
## `MD053` - Link and image reference definitions should be needed
|
||||||
|
@ -2245,9 +2356,9 @@ reference has the corresponding label. The "full", "collapsed", and "shortcut"
|
||||||
formats are all supported.
|
formats are all supported.
|
||||||
|
|
||||||
If there are reference definitions that are deliberately unreferenced, they can
|
If there are reference definitions that are deliberately unreferenced, they can
|
||||||
be ignored by setting the `ignored_definitions` parameter. The default value of
|
be ignored by setting the `ignored_definitions` parameter to the list of strings
|
||||||
this parameter ignores the following convention for adding non-HTML comments to
|
to ignore. The default value of this parameter ignores the following convention
|
||||||
Markdown:
|
for adding non-HTML comments to Markdown:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[//]: # (This behaves like a comment)
|
[//]: # (This behaves like a comment)
|
||||||
|
@ -2374,8 +2485,9 @@ This rule is triggered when a [GitHub Flavored Markdown table][gfm-table-055]
|
||||||
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
||||||
|
|
||||||
By default (`consistent` style), the header row of the first table in a document
|
By default (`consistent` style), the header row of the first table in a document
|
||||||
is used to determine the style that is enforced for all tables in that document.
|
is used to determine the style that is enforced for every table in the document.
|
||||||
A specific style can be required by setting the `style` parameter accordingly.
|
A specific style can be used instead (`leading_and_trailing`, `leading_only`,
|
||||||
|
`no_leading_or_trailing`, `trailing_only`).
|
||||||
|
|
||||||
This table's header row has leading and trailing pipes, but its delimiter row is
|
This table's header row has leading and trailing pipes, but its delimiter row is
|
||||||
missing the trailing pipe and its first row of cells is missing the leading
|
missing the trailing pipe and its first row of cells is missing the leading
|
||||||
|
@ -2453,6 +2565,90 @@ Missing cells in a row create holes in the table and suggest an omission.
|
||||||
|
|
||||||
[gfm-table-056]: https://github.github.com/gfm/#tables-extension-
|
[gfm-table-056]: https://github.github.com/gfm/#tables-extension-
|
||||||
|
|
||||||
|
<a name="md058"></a>
|
||||||
|
|
||||||
|
## `MD058` - Tables should be surrounded by blank lines
|
||||||
|
|
||||||
|
Tags: `table`
|
||||||
|
|
||||||
|
Aliases: `blanks-around-tables`
|
||||||
|
|
||||||
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
|
This rule is triggered when tables are either not preceded or not followed by a
|
||||||
|
blank line:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
To fix violations of this rule, ensure that all tables have a blank line both
|
||||||
|
before and after (except when the table is at the very beginning or end of the
|
||||||
|
document):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that text immediately following a table (i.e., not separated by an empty
|
||||||
|
line) is treated as part of the table (per the specification) and will not
|
||||||
|
trigger this rule:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
This text is part of the table and the next line is blank
|
||||||
|
|
||||||
|
Some text
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: In addition to aesthetic reasons, some parsers will incorrectly parse
|
||||||
|
tables that don't have blank lines before and after them.
|
||||||
|
|
||||||
|
<a name="md059"></a>
|
||||||
|
|
||||||
|
## `MD059` - Link text should be descriptive
|
||||||
|
|
||||||
|
Tags: `accessibility`, `links`
|
||||||
|
|
||||||
|
Aliases: `descriptive-link-text`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `prohibited_texts`: Prohibited link texts (`string[]`, default `["click
|
||||||
|
here","here","link","more"]`)
|
||||||
|
|
||||||
|
This rule is triggered when a link has generic text like `[click here](...)` or
|
||||||
|
`[link](...)`.
|
||||||
|
|
||||||
|
Link text should be descriptive and communicate the purpose of the link (e.g.,
|
||||||
|
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
|
||||||
|
This is especially important for screen readers which sometimes present links
|
||||||
|
without context.
|
||||||
|
|
||||||
|
By default, this rule prohibits a small number of common English words/phrases.
|
||||||
|
To customize that list of words/phrases, set the `prohibited_texts` parameter to
|
||||||
|
an `Array` of `string`s.
|
||||||
|
|
||||||
|
Note: For languages other than English, use the `prohibited_texts` parameter to
|
||||||
|
customize the list for that language. It is *not* a goal for this rule to have
|
||||||
|
translations for every language.
|
||||||
|
|
||||||
|
Note: This rule checks Markdown links; HTML links are ignored.
|
||||||
|
|
||||||
|
More information: <https://webaim.org/techniques/hypertext/>
|
||||||
|
|
||||||
<!-- markdownlint-configure-file {
|
<!-- markdownlint-configure-file {
|
||||||
"no-inline-html": {
|
"no-inline-html": {
|
||||||
"allowed_elements": [
|
"allowed_elements": [
|
||||||
|
|
|
@ -45,8 +45,8 @@ Setext style H2
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The configured heading style can be a specific style to require (`atx`,
|
Note: The configured heading style can be a specific style to require (`atx`,
|
||||||
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or may
|
`atx_closed`, `setext`, `setext_with_atx`, `setext_with_atx_closed`), or can
|
||||||
just require that usage is consistent within the document via `consistent`.
|
require that all heading styles match the first heading style via `consistent`.
|
||||||
|
|
||||||
Note: The placement of a horizontal rule directly below a line of text can
|
Note: The placement of a horizontal rule directly below a line of text can
|
||||||
trigger this rule by turning that text into a level 2 setext-style heading:
|
trigger this rule by turning that text into a level 2 setext-style heading:
|
||||||
|
|
|
@ -29,9 +29,10 @@ document:
|
||||||
* Item 3
|
* Item 3
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (asterisk, plus,
|
The configured list style can ensure all list styling is a specific symbol
|
||||||
dash), to ensure that all list styling is consistent, or to ensure that each
|
(`asterisk`, `plus`, `dash`), ensure each sublist has a consistent symbol that
|
||||||
sublist has a consistent symbol that differs from its parent list.
|
differs from its parent list (`sublist`), or ensure all list styles match the
|
||||||
|
first list style (`consistent`).
|
||||||
|
|
||||||
For example, the following is valid for the `sublist` style because the
|
For example, the following is valid for the `sublist` style because the
|
||||||
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
outer-most indent uses asterisk, the middle indent uses plus, and the inner-most
|
||||||
|
|
|
@ -4,6 +4,10 @@ Tags: `blockquote`, `indentation`, `whitespace`
|
||||||
|
|
||||||
Aliases: `no-multiple-space-blockquote`
|
Aliases: `no-multiple-space-blockquote`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `list_items`: Include list items (`boolean`, default `true`)
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered when blockquotes have more than one space after the
|
This rule is triggered when blockquotes have more than one space after the
|
||||||
|
@ -21,4 +25,8 @@ To fix, remove any extraneous space:
|
||||||
> indentation.
|
> indentation.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Inferring intended list indentation within a blockquote can be challenging;
|
||||||
|
setting the `list_items` parameter to `false` disables this rule for ordered
|
||||||
|
and unordered list items.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -19,6 +19,11 @@ To fix this, add angle brackets around the URL or email address:
|
||||||
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
For more info, visit <https://www.example.com/> or email <user@example.com>.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a URL or email address contains non-ASCII characters, it may be not be
|
||||||
|
handled as intended even when angle brackets are present. In such cases,
|
||||||
|
[percent-encoding](https://en.m.wikipedia.org/wiki/Percent-encoding) can be used
|
||||||
|
to comply with the required syntax for URL and email.
|
||||||
|
|
||||||
Note: To include a bare URL or email without it being converted into a link,
|
Note: To include a bare URL or email without it being converted into a link,
|
||||||
wrap it in a code span:
|
wrap it in a code span:
|
||||||
|
|
||||||
|
|
11
doc/md035.md
11
doc/md035.md
|
@ -23,8 +23,7 @@ in the document:
|
||||||
****
|
****
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, ensure any horizontal rules used in the document are consistent,
|
To fix this, use the same horizontal rule everywhere:
|
||||||
or match the given style if the rule is so configured:
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
---
|
---
|
||||||
|
@ -32,11 +31,7 @@ or match the given style if the rule is so configured:
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: by default, this rule is configured to just require that all horizontal
|
The configured style can ensure all horizontal rules use a specific string or it
|
||||||
rules in the document are the same and will trigger if any of the horizontal
|
can ensure all horizontal rules match the first horizontal rule (`consistent`).
|
||||||
rules are different than the first one encountered in the document. If you
|
|
||||||
want to configure the rule to match a specific style, the parameter given to
|
|
||||||
the 'style' option is a string containing the exact horizontal rule text that
|
|
||||||
is allowed.
|
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
34
doc/md038.md
34
doc/md038.md
|
@ -6,35 +6,47 @@ Aliases: `no-space-in-code`
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered for code span elements that have spaces adjacent to the
|
This rule is triggered for code spans containing content with unnecessary space
|
||||||
backticks:
|
next to the beginning or ending backticks:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text `
|
`some text `
|
||||||
|
|
||||||
` some text`
|
` some text`
|
||||||
|
|
||||||
|
` some text `
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, remove any spaces adjacent to the backticks:
|
To fix this, remove the extra space characters from the beginning and ending:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`some text`
|
`some text`
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading and trailing space is allowed by the specification and
|
Note: A single leading *and* trailing space is allowed by the specification and
|
||||||
automatically trimmed (in order to allow for code spans that embed backticks):
|
trimmed by the parser to support code spans that begin or end with a backtick:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` `backticks` ``
|
`` `backticks` ``
|
||||||
|
|
||||||
|
`` backtick` ``
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A single leading or trailing space is allowed if used to separate code
|
Note: When single-space padding is present in the input, it will be preserved
|
||||||
span markers from an embedded backtick (though the space is not trimmed):
|
(even if unnecessary):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
`` ` embedded backtick``
|
` code `
|
||||||
```
|
```
|
||||||
|
|
||||||
Rationale: Violations of this rule are usually unintentional and may lead to
|
Note: Code spans containing only spaces are allowed by the specification and are
|
||||||
improperly-rendered content. If spaces beside backticks are intentional, this
|
also preserved:
|
||||||
rule can be disabled for that line or file.
|
|
||||||
|
```markdown
|
||||||
|
` `
|
||||||
|
|
||||||
|
` `
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: Violations of this rule are usually unintentional and can lead to
|
||||||
|
improperly-rendered content.
|
||||||
|
|
47
doc/md041.md
47
doc/md041.md
|
@ -6,44 +6,59 @@ Aliases: `first-line-h1`, `first-line-heading`
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
|
- `allow_preamble`: Allow content before first heading (`boolean`, default
|
||||||
|
`false`)
|
||||||
- `front_matter_title`: RegExp for matching title in front matter (`string`,
|
- `front_matter_title`: RegExp for matching title in front matter (`string`,
|
||||||
default `^\s*title\s*[:=]`)
|
default `^\s*title\s*[:=]`)
|
||||||
- `level`: Heading level (`integer`, default `1`)
|
- `level`: Heading level (`integer`, default `1`)
|
||||||
|
|
||||||
This rule is intended to ensure documents have a title and is triggered when
|
This rule is intended to ensure documents have a title and is triggered when
|
||||||
the first line in the file isn't a top-level (h1) heading:
|
the first line in a document is not a top-level ([HTML][HTML] `h1`) heading:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
This is a file without a heading
|
This is a document without a heading
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this, add a top-level heading to the beginning of the file:
|
To fix this, add a top-level heading to the beginning of the document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# File with heading
|
# Document Heading
|
||||||
|
|
||||||
This is a file with a top-level heading
|
This is a document with a top-level heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Because it is common for projects on GitHub to use an image for the heading of
|
Because it is common for projects on GitHub to use an image for the heading of
|
||||||
`README.md` and that is not well-supported by Markdown, HTML headings are also
|
`README.md` and that pattern is not well-supported by Markdown, HTML headings
|
||||||
permitted by this rule. For example:
|
are also permitted by this rule. For example:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
<h1 align="center"><img src="https://placekitten.com/300/150"/></h1>
|
||||||
|
|
||||||
This is a file with a top-level HTML heading
|
This is a document with a top-level HTML heading
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The `level` parameter can be used to change the top-level (ex: to h2) in
|
In some cases, a document's title heading may be preceded by text like a table
|
||||||
cases where an h1 is added externally.
|
of contents. This is not ideal for accessibility, but can be allowed by setting
|
||||||
|
the `allow_preamble` parameter to `true`.
|
||||||
|
|
||||||
If [YAML](https://en.wikipedia.org/wiki/YAML) front matter is present and
|
```markdown
|
||||||
contains a `title` property (commonly used with blog posts), this rule will not
|
This is a document with preamble text
|
||||||
report a violation. To use a different property name in the front matter,
|
|
||||||
specify the text of a regular expression via the `front_matter_title` parameter.
|
# Document Heading
|
||||||
To disable the use of front matter by this rule, specify `""` for
|
```
|
||||||
`front_matter_title`.
|
|
||||||
|
If [YAML][YAML] front matter is present and contains a `title` property
|
||||||
|
(commonly used with blog posts), this rule will not report a violation. To use a
|
||||||
|
different property name in the front matter, specify the text of a [regular
|
||||||
|
expression][RegExp] via the `front_matter_title` parameter. To disable the use
|
||||||
|
of front matter by this rule, specify `""` for `front_matter_title`.
|
||||||
|
|
||||||
|
The `level` parameter can be used to change the top-level heading (ex: to `h2`)
|
||||||
|
in cases where an `h1` is added externally.
|
||||||
|
|
||||||
Rationale: The top-level heading often acts as the title of a document. More
|
Rationale: The top-level heading often acts as the title of a document. More
|
||||||
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
information: <https://cirosantilli.com/markdown-style-guide#top-level-header>.
|
||||||
|
|
||||||
|
[HTML]: https://en.wikipedia.org/wiki/HTML
|
||||||
|
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
[YAML]: https://en.wikipedia.org/wiki/YAML
|
||||||
|
|
26
doc/md043.md
26
doc/md043.md
|
@ -16,7 +16,7 @@ structure for a set of files.
|
||||||
To require exactly the following structure:
|
To require exactly the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail
|
### Detail
|
||||||
```
|
```
|
||||||
|
@ -25,7 +25,7 @@ Set the `headings` parameter to:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"### Detail"
|
"### Detail"
|
||||||
]
|
]
|
||||||
|
@ -34,7 +34,7 @@ Set the `headings` parameter to:
|
||||||
To allow optional headings as with the following structure:
|
To allow optional headings as with the following structure:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Head
|
# Heading
|
||||||
## Item
|
## Item
|
||||||
### Detail (optional)
|
### Detail (optional)
|
||||||
## Foot
|
## Foot
|
||||||
|
@ -47,7 +47,7 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
"# Head",
|
"# Heading",
|
||||||
"## Item",
|
"## Item",
|
||||||
"*",
|
"*",
|
||||||
"## Foot",
|
"## Foot",
|
||||||
|
@ -55,6 +55,24 @@ special value `"+"` meaning "one or more unspecified headings" and set the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To allow a single required heading to vary as with a project name:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Project Name
|
||||||
|
## Description
|
||||||
|
## Examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the special value `"?"` meaning "exactly one unspecified heading":
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"?",
|
||||||
|
"## Description",
|
||||||
|
"## Examples"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
When an error is detected, this rule outputs the line number of the first
|
When an error is detected, this rule outputs the line number of the first
|
||||||
problematic heading (otherwise, it outputs the last line number of the file).
|
problematic heading (otherwise, it outputs the last line number of the file).
|
||||||
|
|
||||||
|
|
10
doc/md044.md
10
doc/md044.md
|
@ -27,6 +27,16 @@ enforce the proper capitalization, specify the desired letter case in the
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sometimes a proper name is capitalized differently in certain contexts. In such
|
||||||
|
cases, add both forms to the `names` array:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"GitHub",
|
||||||
|
"github.com"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
Set the `code_blocks` parameter to `false` to disable this rule for code blocks
|
||||||
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
and spans. Set the `html_elements` parameter to `false` to disable this rule
|
||||||
for HTML elements and attributes (such as when using a proper name as part of
|
for HTML elements and attributes (such as when using a proper name as part of
|
||||||
|
|
10
doc/md045.md
10
doc/md045.md
|
@ -4,7 +4,7 @@ Tags: `accessibility`, `images`
|
||||||
|
|
||||||
Aliases: `no-alt-text`
|
Aliases: `no-alt-text`
|
||||||
|
|
||||||
This rule is triggered when an image is missing alternate text (alt text)
|
This rule reports a violation when an image is missing alternate text (alt text)
|
||||||
information.
|
information.
|
||||||
|
|
||||||
Alternate text is commonly specified inline as:
|
Alternate text is commonly specified inline as:
|
||||||
|
@ -29,12 +29,20 @@ Or with HTML as:
|
||||||
<img src="image.jpg" alt="Alternate text" />
|
<img src="image.jpg" alt="Alternate text" />
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: If the [HTML `aria-hidden` attribute][aria-hidden] is used to hide the
|
||||||
|
image from assistive technology, this rule does not report a violation:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<img src="image.jpg" aria-hidden="true" />
|
||||||
|
```
|
||||||
|
|
||||||
Guidance for writing alternate text is available from the [W3C][w3c],
|
Guidance for writing alternate text is available from the [W3C][w3c],
|
||||||
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
[Wikipedia][wikipedia], and [other locations][phase2technology].
|
||||||
|
|
||||||
Rationale: Alternate text is important for accessibility and describes the
|
Rationale: Alternate text is important for accessibility and describes the
|
||||||
content of an image for people who may not be able to see it.
|
content of an image for people who may not be able to see it.
|
||||||
|
|
||||||
|
[aria-hidden]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden
|
||||||
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
[phase2technology]: https://www.phase2technology.com/blog/no-more-excuses
|
||||||
[w3c]: https://www.w3.org/WAI/alt/
|
[w3c]: https://www.w3.org/WAI/alt/
|
||||||
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
[wikipedia]: https://en.wikipedia.org/wiki/Alt_attribute
|
||||||
|
|
|
@ -34,7 +34,7 @@ document:
|
||||||
To fix violations of this rule, use a consistent style (either indenting or code
|
To fix violations of this rule, use a consistent style (either indenting or code
|
||||||
fences).
|
fences).
|
||||||
|
|
||||||
The specified style can be specific (`fenced`, `indented`) or simply require
|
The configured code block style can be specific (`fenced`, `indented`) or can
|
||||||
that usage be consistent within the document (`consistent`).
|
require all code blocks match the first code block (`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -35,7 +35,8 @@ document:
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
The configured list style can be a specific symbol to use (backtick, tilde), or
|
The configured code fence style can be a specific symbol to use (`backtick`,
|
||||||
can require that usage be consistent within the document.
|
`tilde`) or it can require all code fences match the first code fence
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -26,10 +26,11 @@ To fix this issue, use the configured emphasis style throughout the document:
|
||||||
*Text*
|
*Text*
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured emphasis style can be a specific symbol to use ("asterisk",
|
The configured emphasis style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all emphasis matches the first emphasis
|
||||||
|
(`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like_this_one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
|
@ -26,10 +26,10 @@ To fix this issue, use the configured strong style throughout the document:
|
||||||
**Text**
|
**Text**
|
||||||
```
|
```
|
||||||
|
|
||||||
The configured strong style can be a specific symbol to use ("asterisk",
|
The configured strong style can be a specific symbol to use (`asterisk`,
|
||||||
"underscore"), or can require that usage be consistent within the document.
|
`underscore`) or can require all strong matches the first strong (`consistent`).
|
||||||
|
|
||||||
Note: Emphasis within a word is restricted to "asterisk" in order to avoid
|
Note: Emphasis within a word is restricted to `asterisk` in order to avoid
|
||||||
unwanted emphasis for words containing internal underscores like_this_one.
|
unwanted emphasis for words containing internal underscores like__this__one.
|
||||||
|
|
||||||
Rationale: Consistent formatting makes it easier to understand a document.
|
Rationale: Consistent formatting makes it easier to understand a document.
|
||||||
|
|
31
doc/md051.md
31
doc/md051.md
|
@ -4,6 +4,12 @@ Tags: `links`
|
||||||
|
|
||||||
Aliases: `link-fragments`
|
Aliases: `link-fragments`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `ignore_case`: Ignore case of fragments (`boolean`, default `false`)
|
||||||
|
- `ignored_pattern`: Pattern for ignoring additional fragments (`string`,
|
||||||
|
default ``)
|
||||||
|
|
||||||
Fixable: Some violations can be fixed by tooling
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
This rule is triggered when a link fragment does not match any of the fragments
|
This rule is triggered when a link fragment does not match any of the fragments
|
||||||
|
@ -24,9 +30,9 @@ generated name (see below):
|
||||||
[Link](#heading-name)
|
[Link](#heading-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Link fragments may be handled case-sensitively, so this rule requires fragments
|
For consistency, this rule requires fragments to exactly match the [GitHub
|
||||||
to exactly match the [GitHub heading algorithm][github-heading-algorithm].
|
heading algorithm][github-heading-algorithm] which converts letters to
|
||||||
Therefore, the following example is reported as a violation:
|
lowercase. Therefore, the following example is reported as a violation:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Heading Name
|
# Heading Name
|
||||||
|
@ -34,6 +40,10 @@ Therefore, the following example is reported as a violation:
|
||||||
[Link](#Heading-Name)
|
[Link](#Heading-Name)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To ignore case when comparing fragments with heading names, the `ignore_case`
|
||||||
|
parameter can be set to `true`. In this configuration, the previous example is
|
||||||
|
not reported as a violation.
|
||||||
|
|
||||||
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
||||||
within a heading to provide a specific name (consisting of only lower-case
|
within a heading to provide a specific name (consisting of only lower-case
|
||||||
letters, numbers, `-`, and `_`):
|
letters, numbers, `-`, and `_`):
|
||||||
|
@ -56,6 +66,13 @@ attribute can be used to define a fragment:
|
||||||
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
||||||
control over the text of the fragment identifier.
|
control over the text of the fragment identifier.
|
||||||
|
|
||||||
|
[HTML links to `#top` scroll to the top of a document][html-top-fragment]. This
|
||||||
|
rule allows that syntax (using lower-case for consistency):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[Link](#top)
|
||||||
|
```
|
||||||
|
|
||||||
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
This rule also recognizes the custom fragment syntax used by GitHub to highlight
|
||||||
[specific content in a document][github-linking-to-content].
|
[specific content in a document][github-linking-to-content].
|
||||||
|
|
||||||
|
@ -71,6 +88,12 @@ And this link to content starting within line 19 running into line 21:
|
||||||
[Link](#L19C5-L21C11)
|
[Link](#L19C5-L21C11)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Some Markdown generators dynamically create and insert headings when building
|
||||||
|
documents, for example by combining a fixed prefix like `figure-` and an
|
||||||
|
incrementing numeric counter. To ignore such generated fragments, set the
|
||||||
|
`ignored_pattern` [regular expression][RegEx] parameter to a pattern that
|
||||||
|
matches (e.g., `^figure-`).
|
||||||
|
|
||||||
Rationale: [GitHub section links][github-section-links] are created
|
Rationale: [GitHub section links][github-section-links] are created
|
||||||
automatically for every heading when Markdown content is displayed on GitHub.
|
automatically for every heading when Markdown content is displayed on GitHub.
|
||||||
This makes it easy to link directly to different sections within a document.
|
This makes it easy to link directly to different sections within a document.
|
||||||
|
@ -85,3 +108,5 @@ append an incrementing integer as needed for uniqueness.
|
||||||
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
||||||
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb
|
||||||
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
[github-linking-to-content]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet#linking-to-markdown#linking-to-markdown
|
||||||
|
[html-top-fragment]: https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
||||||
|
[RegEx]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
||||||
|
|
12
doc/md052.md
12
doc/md052.md
|
@ -6,6 +6,7 @@ Aliases: `reference-links-images`
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
|
- `ignored_labels`: Ignored link labels (`string[]`, default `["x"]`)
|
||||||
- `shortcut_syntax`: Include shortcut syntax (`boolean`, default `false`)
|
- `shortcut_syntax`: Include shortcut syntax (`boolean`, default `false`)
|
||||||
|
|
||||||
Links and images in Markdown can provide the link destination or image source
|
Links and images in Markdown can provide the link destination or image source
|
||||||
|
@ -38,3 +39,14 @@ so "shortcut" syntax is ignored by default. To include "shortcut" syntax, set
|
||||||
the `include_shortcut` parameter to `true`. Note that doing so produces warnings
|
the `include_shortcut` parameter to `true`. Note that doing so produces warnings
|
||||||
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
for *all* text in the document that *could* be a shortcut. If bracketed text is
|
||||||
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
intentional, brackets can be escaped with the `\` character: `\[example\]`.
|
||||||
|
|
||||||
|
If there are link labels that are deliberately unreferenced, they can be ignored
|
||||||
|
by setting the `ignored_labels` parameter to the list of strings to ignore. The
|
||||||
|
default value of this parameter ignores the checkbox syntax used by
|
||||||
|
[GitHub Flavored Markdown task list items][gfm-tasklist]:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- [x] Checked task list item
|
||||||
|
```
|
||||||
|
|
||||||
|
[gfm-tasklist]: https://github.github.com/gfm/#task-list-items-extension-
|
||||||
|
|
|
@ -29,9 +29,9 @@ reference has the corresponding label. The "full", "collapsed", and "shortcut"
|
||||||
formats are all supported.
|
formats are all supported.
|
||||||
|
|
||||||
If there are reference definitions that are deliberately unreferenced, they can
|
If there are reference definitions that are deliberately unreferenced, they can
|
||||||
be ignored by setting the `ignored_definitions` parameter. The default value of
|
be ignored by setting the `ignored_definitions` parameter to the list of strings
|
||||||
this parameter ignores the following convention for adding non-HTML comments to
|
to ignore. The default value of this parameter ignores the following convention
|
||||||
Markdown:
|
for adding non-HTML comments to Markdown:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[//]: # (This behaves like a comment)
|
[//]: # (This behaves like a comment)
|
||||||
|
|
|
@ -14,8 +14,9 @@ This rule is triggered when a [GitHub Flavored Markdown table][gfm-table-055]
|
||||||
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
is inconsistent about its use of leading and trailing pipe characters (`|`).
|
||||||
|
|
||||||
By default (`consistent` style), the header row of the first table in a document
|
By default (`consistent` style), the header row of the first table in a document
|
||||||
is used to determine the style that is enforced for all tables in that document.
|
is used to determine the style that is enforced for every table in the document.
|
||||||
A specific style can be required by setting the `style` parameter accordingly.
|
A specific style can be used instead (`leading_and_trailing`, `leading_only`,
|
||||||
|
`no_leading_or_trailing`, `trailing_only`).
|
||||||
|
|
||||||
This table's header row has leading and trailing pipes, but its delimiter row is
|
This table's header row has leading and trailing pipes, but its delimiter row is
|
||||||
missing the trailing pipe and its first row of cells is missing the leading
|
missing the trailing pipe and its first row of cells is missing the leading
|
||||||
|
|
48
doc/md058.md
Normal file
48
doc/md058.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# `MD058` - Tables should be surrounded by blank lines
|
||||||
|
|
||||||
|
Tags: `table`
|
||||||
|
|
||||||
|
Aliases: `blanks-around-tables`
|
||||||
|
|
||||||
|
Fixable: Some violations can be fixed by tooling
|
||||||
|
|
||||||
|
This rule is triggered when tables are either not preceded or not followed by a
|
||||||
|
blank line:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
To fix violations of this rule, ensure that all tables have a blank line both
|
||||||
|
before and after (except when the table is at the very beginning or end of the
|
||||||
|
document):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Some text
|
||||||
|
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
|
||||||
|
> Blockquote
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that text immediately following a table (i.e., not separated by an empty
|
||||||
|
line) is treated as part of the table (per the specification) and will not
|
||||||
|
trigger this rule:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
| Header | Header |
|
||||||
|
| ------ | ------ |
|
||||||
|
| Cell | Cell |
|
||||||
|
This text is part of the table and the next line is blank
|
||||||
|
|
||||||
|
Some text
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale: In addition to aesthetic reasons, some parsers will incorrectly parse
|
||||||
|
tables that don't have blank lines before and after them.
|
30
doc/md059.md
Normal file
30
doc/md059.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# `MD059` - Link text should be descriptive
|
||||||
|
|
||||||
|
Tags: `accessibility`, `links`
|
||||||
|
|
||||||
|
Aliases: `descriptive-link-text`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `prohibited_texts`: Prohibited link texts (`string[]`, default `["click
|
||||||
|
here","here","link","more"]`)
|
||||||
|
|
||||||
|
This rule is triggered when a link has generic text like `[click here](...)` or
|
||||||
|
`[link](...)`.
|
||||||
|
|
||||||
|
Link text should be descriptive and communicate the purpose of the link (e.g.,
|
||||||
|
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
|
||||||
|
This is especially important for screen readers which sometimes present links
|
||||||
|
without context.
|
||||||
|
|
||||||
|
By default, this rule prohibits a small number of common English words/phrases.
|
||||||
|
To customize that list of words/phrases, set the `prohibited_texts` parameter to
|
||||||
|
an `Array` of `string`s.
|
||||||
|
|
||||||
|
Note: For languages other than English, use the `prohibited_texts` parameter to
|
||||||
|
customize the list for that language. It is *not* a goal for this rule to have
|
||||||
|
translations for every language.
|
||||||
|
|
||||||
|
Note: This rule checks Markdown links; HTML links are ignored.
|
||||||
|
|
||||||
|
More information: <https://webaim.org/techniques/hypertext/>
|
167
eslint.config.mjs
Normal file
167
eslint.config.mjs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import js from "@eslint/js";
|
||||||
|
import eslintPluginJsdoc from "eslint-plugin-jsdoc";
|
||||||
|
import eslintPluginNode from "eslint-plugin-n";
|
||||||
|
import eslintPluginRegexp from "eslint-plugin-regexp";
|
||||||
|
import eslintPluginStylistic from "@stylistic/eslint-plugin";
|
||||||
|
import eslintPluginUnicorn from "eslint-plugin-unicorn";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.all,
|
||||||
|
eslintPluginJsdoc.configs["flat/recommended"],
|
||||||
|
eslintPluginNode.configs["flat/recommended"],
|
||||||
|
eslintPluginRegexp.configs["flat/recommended"],
|
||||||
|
eslintPluginStylistic.configs.customize({
|
||||||
|
"arrowParens": true,
|
||||||
|
"braceStyle": "1tbs",
|
||||||
|
"commaDangle": "never",
|
||||||
|
"jsx": false,
|
||||||
|
"quoteProps": "always",
|
||||||
|
"quotes": "double",
|
||||||
|
"semi": true
|
||||||
|
}),
|
||||||
|
eslintPluginUnicorn.configs["flat/all"],
|
||||||
|
{
|
||||||
|
"ignores": [
|
||||||
|
"demo/markdown-it.min.js",
|
||||||
|
"demo/markdownlint-browser.js",
|
||||||
|
"demo/markdownlint-browser.min.js",
|
||||||
|
"example/typescript/type-check-*",
|
||||||
|
"test-repos/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linterOptions": {
|
||||||
|
"reportUnusedDisableDirectives": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@stylistic/array-bracket-spacing": [ "error", "always" ],
|
||||||
|
"@stylistic/indent": [ "error", 2 ],
|
||||||
|
"@stylistic/indent-binary-ops": [ "off" ],
|
||||||
|
"@stylistic/operator-linebreak": [ "error", "after" ],
|
||||||
|
"@stylistic/padded-blocks": "off",
|
||||||
|
"@stylistic/space-before-function-paren": [ "error", "never" ],
|
||||||
|
"capitalized-comments": "off",
|
||||||
|
"complexity": "off",
|
||||||
|
"func-style": "off",
|
||||||
|
"id-length": "off",
|
||||||
|
"jsdoc/tag-lines": [ "error", "never", { "startLines": 1 } ],
|
||||||
|
"logical-assignment-operators": "off",
|
||||||
|
"max-depth": "off",
|
||||||
|
"max-lines-per-function": "off",
|
||||||
|
"max-lines": "off",
|
||||||
|
"max-params": "off",
|
||||||
|
"max-statements": "off",
|
||||||
|
"multiline-comment-style": [ "error", "separate-lines" ],
|
||||||
|
"no-empty-function": "off",
|
||||||
|
"no-implicit-coercion": "off",
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"no-plusplus": "off",
|
||||||
|
"no-ternary": "off",
|
||||||
|
"no-undef-init": "off",
|
||||||
|
"no-undefined": "off",
|
||||||
|
"no-useless-assignment": "off",
|
||||||
|
"object-shorthand": "off",
|
||||||
|
"one-var": "off",
|
||||||
|
"prefer-arrow-callback": "off",
|
||||||
|
"prefer-destructuring": "off",
|
||||||
|
"prefer-named-capture-group": "off",
|
||||||
|
"prefer-template": "off",
|
||||||
|
"require-unicode-regexp": "off",
|
||||||
|
"sort-imports": "off",
|
||||||
|
"sort-keys": "off",
|
||||||
|
"unicorn/better-regex": "off",
|
||||||
|
"unicorn/consistent-function-scoping": "off",
|
||||||
|
"unicorn/filename-case": "off",
|
||||||
|
"unicorn/no-array-callback-reference": "off",
|
||||||
|
"unicorn/no-keyword-prefix": "off",
|
||||||
|
"unicorn/no-new-array": "off",
|
||||||
|
"unicorn/no-null": "off",
|
||||||
|
"unicorn/no-useless-undefined": "off",
|
||||||
|
"unicorn/prefer-at": "off",
|
||||||
|
"unicorn/prefer-string-raw": "off",
|
||||||
|
"unicorn/prefer-string-replace-all": "off",
|
||||||
|
"unicorn/prefer-string-slice": "off",
|
||||||
|
"unicorn/prefer-switch": "off",
|
||||||
|
"unicorn/prevent-abbreviations": "off",
|
||||||
|
"unicorn/switch-case-braces": [ "error", "avoid" ],
|
||||||
|
"vars-on-top": "off"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"jsdoc": {
|
||||||
|
"preferredTypes": {
|
||||||
|
"object": "Object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.cjs"
|
||||||
|
],
|
||||||
|
"languageOptions": {
|
||||||
|
"sourceType": "commonjs",
|
||||||
|
"globals": {
|
||||||
|
"module": "readonly",
|
||||||
|
"require": "readonly"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"demo/default.js"
|
||||||
|
],
|
||||||
|
"languageOptions": {
|
||||||
|
"globals": {
|
||||||
|
"alert": "readonly",
|
||||||
|
"document": "readonly",
|
||||||
|
"navigator": "readonly",
|
||||||
|
"window": "readonly"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"jsdoc/require-jsdoc": "off",
|
||||||
|
"n/no-unsupported-features/node-builtins": "off",
|
||||||
|
"no-invalid-this": "off",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-var": "off",
|
||||||
|
"unicorn/prefer-module": "off",
|
||||||
|
"unicorn/prefer-query-selector": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"example/*.cjs"
|
||||||
|
],
|
||||||
|
"languageOptions": {
|
||||||
|
"sourceType": "commonjs"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"n/no-missing-require": "off",
|
||||||
|
"no-console": "off",
|
||||||
|
"no-invalid-this": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"example/standalone.mjs"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off",
|
||||||
|
"no-constant-condition": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"test/rules/**/*.js",
|
||||||
|
"test/rules/**/*.cjs"
|
||||||
|
],
|
||||||
|
"languageOptions": {
|
||||||
|
"sourceType": "commonjs"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"unicorn/prefer-module": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
28
example/Gruntfile.cjs
Normal file
28
example/Gruntfile.cjs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = function wrapper(grunt) {
|
||||||
|
grunt.initConfig({
|
||||||
|
"markdownlint": {
|
||||||
|
"example": {
|
||||||
|
"src": [ "*.md" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerMultiTask("markdownlint", function task() {
|
||||||
|
const done = this.async();
|
||||||
|
import("markdownlint/async").then(({ lint }) => {
|
||||||
|
lint(
|
||||||
|
{ "files": this.filesSrc },
|
||||||
|
function callback(err, result) {
|
||||||
|
const resultString = err || ((result || "").toString());
|
||||||
|
if (resultString) {
|
||||||
|
grunt.fail.warn("\n" + resultString + "\n");
|
||||||
|
}
|
||||||
|
done(!err || !resultString);
|
||||||
|
});
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,28 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const markdownlint = require("../lib/markdownlint");
|
|
||||||
|
|
||||||
module.exports = function wrapper(grunt) {
|
|
||||||
grunt.initConfig({
|
|
||||||
"markdownlint": {
|
|
||||||
"example": {
|
|
||||||
"src": [ "*.md" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
grunt.registerMultiTask("markdownlint", function task() {
|
|
||||||
const done = this.async();
|
|
||||||
markdownlint(
|
|
||||||
{ "files": this.filesSrc },
|
|
||||||
function callback(err, result) {
|
|
||||||
const resultString = err || ((result || "").toString());
|
|
||||||
if (resultString) {
|
|
||||||
grunt.fail.warn("\n" + resultString + "\n");
|
|
||||||
}
|
|
||||||
done(!err || !resultString);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
24
example/gulpfile.cjs
Normal file
24
example/gulpfile.cjs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const gulp = require("gulp");
|
||||||
|
const through2 = require("through2");
|
||||||
|
|
||||||
|
// Simple task wrapper
|
||||||
|
gulp.task("markdownlint", function task() {
|
||||||
|
return gulp.src("*.md", { "read": false })
|
||||||
|
.pipe(through2.obj(function obj(file, enc, next) {
|
||||||
|
import("markdownlint/async").then(({ lint }) => {
|
||||||
|
lint(
|
||||||
|
{ "files": [ file.relative ] },
|
||||||
|
function callback(err, result) {
|
||||||
|
const resultString = (result || "").toString();
|
||||||
|
if (resultString) {
|
||||||
|
console.log(resultString);
|
||||||
|
}
|
||||||
|
next(err, file);
|
||||||
|
});
|
||||||
|
}).catch(next);
|
||||||
|
}));
|
||||||
|
});
|
|
@ -1,23 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const gulp = require("gulp");
|
|
||||||
const through2 = require("through2");
|
|
||||||
const markdownlint = require("../lib/markdownlint");
|
|
||||||
|
|
||||||
// Simple task wrapper
|
|
||||||
gulp.task("markdownlint", function task() {
|
|
||||||
return gulp.src("*.md", { "read": false })
|
|
||||||
.pipe(through2.obj(function obj(file, enc, next) {
|
|
||||||
markdownlint(
|
|
||||||
{ "files": [ file.relative ] },
|
|
||||||
function callback(err, result) {
|
|
||||||
const resultString = (result || "").toString();
|
|
||||||
if (resultString) {
|
|
||||||
console.log(resultString);
|
|
||||||
}
|
|
||||||
next(err, file);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const markdownlint = require("../lib/markdownlint");
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
"files": [ "good.md", "bad.md" ],
|
|
||||||
"strings": {
|
|
||||||
"good.string": "# good.string\n\nThis string passes all rules.\n",
|
|
||||||
"bad.string": "#bad.string\n\n#This string fails\tsome rules.\n"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Makes a synchronous call, using result.toString for pretty formatting
|
|
||||||
const result = markdownlint.sync(options);
|
|
||||||
console.log(result.toString());
|
|
||||||
|
|
||||||
// Makes an asynchronous call
|
|
||||||
markdownlint(options, function callback(err, result) {
|
|
||||||
if (!err) {
|
|
||||||
console.log(result.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Displays the result object directly
|
|
||||||
markdownlint(options, function callback(err, result) {
|
|
||||||
if (!err) {
|
|
||||||
console.dir(result, { "colors": true, "depth": null });
|
|
||||||
}
|
|
||||||
});
|
|
54
example/standalone.mjs
Normal file
54
example/standalone.mjs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import { applyFixes, getVersion } from "markdownlint";
|
||||||
|
import { lint as lintAsync } from "markdownlint/async";
|
||||||
|
import { lint as lintPromise } from "markdownlint/promise";
|
||||||
|
import { lint as lintSync } from "markdownlint/sync";
|
||||||
|
|
||||||
|
// Displays the library version
|
||||||
|
console.log(getVersion());
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
"files": [ "good.md", "bad.md" ],
|
||||||
|
"strings": {
|
||||||
|
"good.string": "# good.string\n\nThis string passes all rules.\n",
|
||||||
|
"bad.string": "#bad.string\n\n#This string fails\tsome rules.\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
|
||||||
|
// Makes a synchronous call, uses result.toString for pretty formatting
|
||||||
|
const results = lintSync(options);
|
||||||
|
console.log(results.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
|
||||||
|
// Makes an asynchronous call, uses result.toString for pretty formatting
|
||||||
|
lintAsync(options, function callback(error, results) {
|
||||||
|
if (!error && results) {
|
||||||
|
console.log(results.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
|
||||||
|
// Makes a Promise-based asynchronous call, displays the result object directly
|
||||||
|
const results = await lintPromise(options);
|
||||||
|
console.dir(results, { "colors": true, "depth": null });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
|
||||||
|
// Fixes all supported violations in Markdown content
|
||||||
|
const original = "# Heading";
|
||||||
|
const results = lintSync({ "strings": { "content": original } });
|
||||||
|
const fixed = applyFixes(original, results.content);
|
||||||
|
console.log(fixed);
|
||||||
|
|
||||||
|
}
|
2
example/typescript/.gitignore
vendored
2
example/typescript/.gitignore
vendored
|
@ -1 +1 @@
|
||||||
type-check.js
|
type-check-*
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
"esModuleInterop": true,
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"noFallthroughCasesInSwitch": true,
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"noImplicitAny": true,
|
||||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
"noImplicitOverride": true,
|
||||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
"noImplicitReturns": true,
|
||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"noImplicitThis": true,
|
||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"strict": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
// Attempt to validate all the type declarations in markdownlint.d.ts
|
// Attempt to validate important type declarations
|
||||||
|
|
||||||
import markdownlint from "../..";
|
import { Configuration, ConfigurationStrict, LintResults, Options, Rule, RuleParams, RuleOnError, RuleOnErrorInfo } from "../../lib/exports.mjs";
|
||||||
|
import { applyFix, applyFixes, getVersion } from "../../lib/exports.mjs";
|
||||||
|
import { lint as lintAsync, readConfig as readConfigAsync } from "../../lib/exports-async.mjs";
|
||||||
|
import { lint as lintPromise, readConfig as readConfigPromise } from "../../lib/exports-promise.mjs";
|
||||||
|
import { lint as lintSync, readConfig as readConfigSync } from "../../lib/exports-sync.mjs";
|
||||||
|
|
||||||
const assert = require("assert");
|
import assert from "assert";
|
||||||
|
import markdownIt from "markdown-it";
|
||||||
const markdownlintJsonPath = "../../.markdownlint.json";
|
const markdownlintJsonPath = "../../.markdownlint.json";
|
||||||
|
|
||||||
const version: string = markdownlint.getVersion();
|
const version: string = getVersion();
|
||||||
assert(/^\d+\.\d+\.\d+$/.test(version));
|
assert(/^\d+\.\d+\.\d+$/.test(version));
|
||||||
|
|
||||||
function assertConfiguration(config: markdownlint.Configuration) {
|
function assertConfiguration(config: Configuration) {
|
||||||
assert(!!config);
|
assert(!!config);
|
||||||
assert.deepEqual(config["line-length"], { "strict": true, "code_blocks": false });
|
assert.deepEqual(config["line-length"], { "strict": true, "code_blocks": false });
|
||||||
// config assignment is covered by markdownlint.Options
|
// config assignment is covered by markdownlint.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertConfigurationCallback(err: Error | null, config?: markdownlint.Configuration) {
|
function assertConfigurationCallback(err: Error | null, config?: Configuration) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
config && assertConfiguration(config);
|
config && assertConfiguration(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertLintResults(results: markdownlint.LintResults) {
|
function assertLintResults(results: LintResults) {
|
||||||
assert(!!results);
|
assert(!!results);
|
||||||
assert.equal(results["string"].length, 1);
|
assert.equal(results["string"].length, 1);
|
||||||
assert.equal(results["string"][0].lineNumber, 1);
|
assert.equal(results["string"][0].lineNumber, 1);
|
||||||
|
@ -60,23 +65,23 @@ function assertLintResults(results: markdownlint.LintResults) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertLintResultsCallback(err: Error | null, results?: markdownlint.LintResults) {
|
function assertLintResultsCallback(err: Error | null, results?: LintResults) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
results && assertLintResults(results);
|
results && assertLintResults(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertConfiguration(markdownlint.readConfigSync(markdownlintJsonPath));
|
assertConfiguration(readConfigSync(markdownlintJsonPath));
|
||||||
assertConfiguration(markdownlint.readConfigSync(markdownlintJsonPath, [ JSON.parse ]));
|
assertConfiguration(readConfigSync(markdownlintJsonPath, [ JSON.parse ]));
|
||||||
|
|
||||||
markdownlint.readConfig(markdownlintJsonPath, assertConfigurationCallback);
|
readConfigAsync(markdownlintJsonPath, assertConfigurationCallback);
|
||||||
markdownlint.readConfig(markdownlintJsonPath, [ JSON.parse ], assertConfigurationCallback);
|
readConfigAsync(markdownlintJsonPath, [ JSON.parse ], assertConfigurationCallback);
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
assertConfigurationCallback(null, await markdownlint.promises.readConfig(markdownlintJsonPath));
|
assertConfigurationCallback(null, await readConfigPromise(markdownlintJsonPath));
|
||||||
assertConfigurationCallback(null, await markdownlint.promises.readConfig(markdownlintJsonPath, [ JSON.parse ]))
|
assertConfigurationCallback(null, await readConfigPromise(markdownlintJsonPath, [ JSON.parse ]))
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let options: markdownlint.Options;
|
let options: Options;
|
||||||
options = {
|
options = {
|
||||||
"files": [ "../bad.md" ],
|
"files": [ "../bad.md" ],
|
||||||
"strings": {
|
"strings": {
|
||||||
|
@ -93,32 +98,32 @@ options = {
|
||||||
"frontMatter": /---/,
|
"frontMatter": /---/,
|
||||||
"handleRuleFailures": false,
|
"handleRuleFailures": false,
|
||||||
"noInlineConfig": false,
|
"noInlineConfig": false,
|
||||||
"markdownItPlugins": [ [ require("markdown-it-sub") ] ]
|
"markdownItFactory": () => markdownIt()
|
||||||
};
|
};
|
||||||
|
|
||||||
assertLintResults(markdownlint.sync(options));
|
assertLintResults(lintSync(options));
|
||||||
markdownlint(options, assertLintResultsCallback);
|
lintAsync(options, assertLintResultsCallback);
|
||||||
(async () => {
|
(async () => {
|
||||||
assertLintResultsCallback(null, await markdownlint.promises.markdownlint(options));
|
assertLintResultsCallback(null, await lintPromise(options));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
options.files = "../bad.md";
|
options.files = "../bad.md";
|
||||||
assertLintResults(markdownlint.sync(options));
|
assertLintResults(lintSync(options));
|
||||||
markdownlint(options, assertLintResultsCallback);
|
lintAsync(options, assertLintResultsCallback);
|
||||||
(async () => {
|
(async () => {
|
||||||
assertLintResultsCallback(null, await markdownlint.promises.markdownlint(options));
|
assertLintResultsCallback(null, await lintPromise(options));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const testRule: markdownlint.Rule = {
|
const testRule: Rule = {
|
||||||
"names": [ "test-rule" ],
|
"names": [ "test-rule" ],
|
||||||
"description": "Test rule",
|
"description": "Test rule",
|
||||||
"information": new URL("https://example.com/rule-information"),
|
"information": new URL("https://example.com/rule-information"),
|
||||||
"tags": [ "test-tag" ],
|
"tags": [ "test-tag" ],
|
||||||
"parser": "none",
|
"parser": "none",
|
||||||
"function": function rule(params: markdownlint.RuleParams, onError: markdownlint.RuleOnError) {
|
"function": function rule(params: RuleParams, onError: RuleOnError) {
|
||||||
assert(!!params);
|
assert(!!params);
|
||||||
assert(!!onError);
|
assert(!!onError);
|
||||||
let ruleParams: markdownlint.RuleParams;
|
let ruleParams: RuleParams;
|
||||||
ruleParams = {
|
ruleParams = {
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"parsers": {
|
"parsers": {
|
||||||
|
@ -136,10 +141,11 @@ const testRule: markdownlint.Rule = {
|
||||||
"frontMatterLines": [
|
"frontMatterLines": [
|
||||||
"three"
|
"three"
|
||||||
],
|
],
|
||||||
"config": options.config
|
"config": options.config,
|
||||||
|
"version": "1.2.3"
|
||||||
};
|
};
|
||||||
assert(ruleParams);
|
assert(ruleParams);
|
||||||
let ruleOnErrorInfo: markdownlint.RuleOnErrorInfo;
|
let ruleOnErrorInfo: RuleOnErrorInfo;
|
||||||
ruleOnErrorInfo = {
|
ruleOnErrorInfo = {
|
||||||
"lineNumber": 1,
|
"lineNumber": 1,
|
||||||
"detail": "detail",
|
"detail": "detail",
|
||||||
|
@ -159,8 +165,55 @@ const testRule: markdownlint.Rule = {
|
||||||
};
|
};
|
||||||
|
|
||||||
options.customRules = [ testRule ];
|
options.customRules = [ testRule ];
|
||||||
assertLintResults(markdownlint.sync(options));
|
assertLintResults(lintSync(options));
|
||||||
markdownlint(options, assertLintResultsCallback);
|
lintAsync(options, assertLintResultsCallback);
|
||||||
(async () => {
|
(async () => {
|
||||||
assertLintResultsCallback(null, await markdownlint.promises.markdownlint(options));
|
assertLintResultsCallback(null, await lintPromise(options));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
applyFix(
|
||||||
|
"# Fixing\n",
|
||||||
|
{
|
||||||
|
"insertText": "Head",
|
||||||
|
"editColumn": 3,
|
||||||
|
"deleteCount": 3
|
||||||
|
},
|
||||||
|
"\n"
|
||||||
|
),
|
||||||
|
"# Heading\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
applyFixes(
|
||||||
|
"# Fixing\n",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
|
"fixInfo": {
|
||||||
|
"insertText": "Head",
|
||||||
|
"editColumn": 3,
|
||||||
|
"deleteCount": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"# Heading\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
const configuration: Configuration = {
|
||||||
|
"custom-rule": true,
|
||||||
|
"no-hard-tabs": false,
|
||||||
|
"heading-style": {
|
||||||
|
"style": "consistent"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert(configuration);
|
||||||
|
const configurationStrict: ConfigurationStrict = {
|
||||||
|
// "custom-rule": true,
|
||||||
|
"no-hard-tabs": false,
|
||||||
|
"heading-style": {
|
||||||
|
"style": "consistent"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert(configurationStrict);
|
||||||
|
|
1
helpers/.npmignore
Normal file
1
helpers/.npmignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
test.cjs
|
|
@ -17,56 +17,13 @@ change from release to release. There are brief descriptive comments above each
|
||||||
function, but no [JSDoc][jsdoc] annotations. That said, some of what's here will
|
function, but no [JSDoc][jsdoc] annotations. That said, some of what's here will
|
||||||
be useful to custom rule authors and may avoid duplicating code.
|
be useful to custom rule authors and may avoid duplicating code.
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Using Helpers from a Custom Rule
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { forEachLine, getLineMetadata } = require("markdownlint-rule-helpers");
|
|
||||||
|
|
||||||
/** @type import("markdownlint").Rule */
|
|
||||||
module.exports = {
|
|
||||||
"names": [ "every-n-lines" ],
|
|
||||||
"description": "Rule that reports an error every N lines",
|
|
||||||
"tags": [ "test" ],
|
|
||||||
"parser": "none",
|
|
||||||
"function": (params, onError) => {
|
|
||||||
const n = params.config.n || 2;
|
|
||||||
forEachLine(getLineMetadata(params), (line, lineIndex) => {
|
|
||||||
const lineNumber = lineIndex + 1;
|
|
||||||
if ((lineNumber % n) === 0) {
|
|
||||||
onError({
|
|
||||||
"lineNumber": lineNumber,
|
|
||||||
"detail": "Line number " + lineNumber
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Applying Recommended Fixes
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { "sync": markdownlintSync } = require("markdownlint");
|
|
||||||
const markdownlintRuleHelpers = require("markdownlint-rule-helpers");
|
|
||||||
|
|
||||||
function fixMarkdownlintViolations(content) {
|
|
||||||
const fixResults = markdownlintSync({ strings: { content } });
|
|
||||||
return markdownlintRuleHelpers.applyFixes(content, fixResults.content);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See also: [`markdownlint` built-in rule implementations][lib].
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
*None* - The entire body of code is tested to 100% coverage by the core
|
*None* - The entire body of code is tested to 100% coverage by the core
|
||||||
`markdownlint` project, so there are no additional tests here.
|
`markdownlint` project, so there are no additional tests here.
|
||||||
|
|
||||||
[custom-rules]: https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/CustomRules.md
|
[custom-rules]: https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/CustomRules.md
|
||||||
[jsdoc]: https://en.m.wikipedia.org/wiki/JSDoc
|
[jsdoc]: https://en.m.wikipedia.org/wiki/JSDoc
|
||||||
[lib]: https://github.com/DavidAnson/markdownlint/tree/v0.34.0/lib
|
|
||||||
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
||||||
[markdownlint]: https://github.com/DavidAnson/markdownlint
|
[markdownlint]: https://github.com/DavidAnson/markdownlint
|
||||||
[rules]: https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/Rules.md
|
[rules]: https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/Rules.md
|
||||||
|
|
540
helpers/helpers.cjs
Normal file
540
helpers/helpers.cjs
Normal file
|
@ -0,0 +1,540 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const micromark = require("./micromark-helpers.cjs");
|
||||||
|
|
||||||
|
const { newLineRe, nextLinesRe } = require("./shared.cjs");
|
||||||
|
|
||||||
|
module.exports.newLineRe = newLineRe;
|
||||||
|
module.exports.nextLinesRe = nextLinesRe;
|
||||||
|
|
||||||
|
/** @typedef {import("../lib/exports.mjs").RuleOnError} RuleOnError */
|
||||||
|
/** @typedef {import("../lib/exports.mjs").RuleOnErrorFixInfo} RuleOnErrorFixInfo */
|
||||||
|
/** @typedef {import("../lib/exports.mjs").MicromarkToken} MicromarkToken */
|
||||||
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
|
/** @typedef {import("micromark-extension-gfm-footnote", { with: { "resolution-mode": "import" } })} */
|
||||||
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
|
/** @typedef {import("../lib/micromark-types.d.mts", { with: { "resolution-mode": "import" } })} */
|
||||||
|
|
||||||
|
// Regular expression for matching common front matter (YAML and TOML)
|
||||||
|
// @ts-ignore
|
||||||
|
module.exports.frontMatterRe =
|
||||||
|
/((^---[^\S\r\n\u2028\u2029]*$[\s\S]+?^---\s*)|(^\+\+\+[^\S\r\n\u2028\u2029]*$[\s\S]+?^(\+\+\+|\.\.\.)\s*)|(^\{[^\S\r\n\u2028\u2029]*$[\s\S]+?^\}\s*))(\r\n|\r|\n|$)/m;
|
||||||
|
|
||||||
|
// Regular expression for matching the start of inline disable/enable comments
|
||||||
|
const inlineCommentStartRe =
|
||||||
|
/(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
|
||||||
|
module.exports.inlineCommentStartRe = inlineCommentStartRe;
|
||||||
|
|
||||||
|
// Regular expression for identifying an HTML entity at the end of a line
|
||||||
|
module.exports.endOfLineHtmlEntityRe =
|
||||||
|
/&(?:#\d+|#[xX][\da-fA-F]+|[a-zA-Z]{2,31}|blk\d{2}|emsp1[34]|frac\d{2}|sup\d|there4);$/;
|
||||||
|
|
||||||
|
// Regular expression for identifying a GitHub emoji code at the end of a line
|
||||||
|
module.exports.endOfLineGemojiCodeRe =
|
||||||
|
/:(?:[abmovx]|[-+]1|100|1234|(?:1st|2nd|3rd)_place_medal|8ball|clock\d{1,4}|e-mail|non-potable_water|o2|t-rex|u5272|u5408|u55b6|u6307|u6708|u6709|u6e80|u7121|u7533|u7981|u7a7a|[a-z]{2,15}2?|[a-z]{1,14}(?:_[a-z\d]{1,16})+):$/;
|
||||||
|
|
||||||
|
// All punctuation characters (normal and full-width)
|
||||||
|
const allPunctuation = ".,;:!?。,;:!?";
|
||||||
|
module.exports.allPunctuation = allPunctuation;
|
||||||
|
|
||||||
|
// All punctuation characters without question mark (normal and full-width)
|
||||||
|
module.exports.allPunctuationNoQuestion = allPunctuation.replace(/[??]/gu, "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input is a Number.
|
||||||
|
*
|
||||||
|
* @param {Object} obj Object of unknown type.
|
||||||
|
* @returns {boolean} True iff obj is a Number.
|
||||||
|
*/
|
||||||
|
function isNumber(obj) {
|
||||||
|
return typeof obj === "number";
|
||||||
|
}
|
||||||
|
module.exports.isNumber = isNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input is a String.
|
||||||
|
*
|
||||||
|
* @param {Object} obj Object of unknown type.
|
||||||
|
* @returns {boolean} True iff obj is a String.
|
||||||
|
*/
|
||||||
|
function isString(obj) {
|
||||||
|
return typeof obj === "string";
|
||||||
|
}
|
||||||
|
module.exports.isString = isString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input String is empty.
|
||||||
|
*
|
||||||
|
* @param {string} str String of unknown length.
|
||||||
|
* @returns {boolean} True iff the input String is empty.
|
||||||
|
*/
|
||||||
|
function isEmptyString(str) {
|
||||||
|
return str.length === 0;
|
||||||
|
}
|
||||||
|
module.exports.isEmptyString = isEmptyString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input is an Object.
|
||||||
|
*
|
||||||
|
* @param {Object} obj Object of unknown type.
|
||||||
|
* @returns {boolean} True iff obj is an Object.
|
||||||
|
*/
|
||||||
|
function isObject(obj) {
|
||||||
|
return !!obj && (typeof obj === "object") && !Array.isArray(obj);
|
||||||
|
}
|
||||||
|
module.exports.isObject = isObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input is a URL.
|
||||||
|
*
|
||||||
|
* @param {Object} obj Object of unknown type.
|
||||||
|
* @returns {boolean} True iff obj is a URL.
|
||||||
|
*/
|
||||||
|
function isUrl(obj) {
|
||||||
|
return !!obj && (Object.getPrototypeOf(obj) === URL.prototype);
|
||||||
|
}
|
||||||
|
module.exports.isUrl = isUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the input if it is an Array.
|
||||||
|
*
|
||||||
|
* @param {Object} arr Object of unknown type.
|
||||||
|
* @returns {Object} Clone of obj iff obj is an Array.
|
||||||
|
*/
|
||||||
|
function cloneIfArray(arr) {
|
||||||
|
return Array.isArray(arr) ? [ ...arr ] : arr;
|
||||||
|
}
|
||||||
|
module.exports.cloneIfArray = cloneIfArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the input if it is a URL.
|
||||||
|
*
|
||||||
|
* @param {Object} url Object of unknown type.
|
||||||
|
* @returns {Object} Clone of obj iff obj is a URL.
|
||||||
|
*/
|
||||||
|
function cloneIfUrl(url) {
|
||||||
|
return isUrl(url) ? new URL(url) : url;
|
||||||
|
}
|
||||||
|
module.exports.cloneIfUrl = cloneIfUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Regular Expression for matching the specified HTML attribute.
|
||||||
|
*
|
||||||
|
* @param {string} name HTML attribute name.
|
||||||
|
* @returns {RegExp} Regular Expression for matching.
|
||||||
|
*/
|
||||||
|
module.exports.getHtmlAttributeRe = function getHtmlAttributeRe(name) {
|
||||||
|
return new RegExp(`\\s${name}\\s*=\\s*['"]?([^'"\\s>]*)`, "iu");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the input line is blank (contains nothing, whitespace, or
|
||||||
|
* comments (unclosed start/end comments allowed)).
|
||||||
|
*
|
||||||
|
* @param {string} line Input line.
|
||||||
|
* @returns {boolean} True iff line is blank.
|
||||||
|
*/
|
||||||
|
function isBlankLine(line) {
|
||||||
|
const startComment = "<!--";
|
||||||
|
const endComment = "-->";
|
||||||
|
const removeComments = (s) => {
|
||||||
|
while (true) {
|
||||||
|
const start = s.indexOf(startComment);
|
||||||
|
const end = s.indexOf(endComment);
|
||||||
|
if ((end !== -1) && ((start === -1) || (end < start))) {
|
||||||
|
// Unmatched end comment is first
|
||||||
|
s = s.slice(end + endComment.length);
|
||||||
|
} else if ((start !== -1) && (end !== -1)) {
|
||||||
|
// Start comment is before end comment
|
||||||
|
s = s.slice(0, start) + s.slice(end + endComment.length);
|
||||||
|
} else if ((start !== -1) && (end === -1)) {
|
||||||
|
// Unmatched start comment is last
|
||||||
|
s = s.slice(0, start);
|
||||||
|
} else {
|
||||||
|
// No more comments to remove
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
!line ||
|
||||||
|
!line.trim() ||
|
||||||
|
!removeComments(line).replace(/>/g, "").trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
module.exports.isBlankLine = isBlankLine;
|
||||||
|
|
||||||
|
// Replaces the content of properly-formatted CommonMark comments with "."
|
||||||
|
// This preserves the line/column information for the rest of the document
|
||||||
|
// https://spec.commonmark.org/0.29/#html-blocks
|
||||||
|
// https://spec.commonmark.org/0.29/#html-comment
|
||||||
|
const htmlCommentBegin = "<!--";
|
||||||
|
const htmlCommentEnd = "-->";
|
||||||
|
const safeCommentCharacter = ".";
|
||||||
|
const startsWithPipeRe = /^ *\|/;
|
||||||
|
const notCrLfRe = /[^\r\n]/g;
|
||||||
|
const notSpaceCrLfRe = /[^ \r\n]/g;
|
||||||
|
const trailingSpaceRe = / +[\r\n]/g;
|
||||||
|
const replaceTrailingSpace = (s) => s.replace(notCrLfRe, safeCommentCharacter);
|
||||||
|
module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
||||||
|
let i = 0;
|
||||||
|
while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
|
||||||
|
const j = text.indexOf(htmlCommentEnd, i + 2);
|
||||||
|
if (j === -1) {
|
||||||
|
// Un-terminated comments are treated as text
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If the comment has content...
|
||||||
|
if (j > i + htmlCommentBegin.length) {
|
||||||
|
const content = text.slice(i + htmlCommentBegin.length, j);
|
||||||
|
const lastLf = text.lastIndexOf("\n", i) + 1;
|
||||||
|
const preText = text.slice(lastLf, i);
|
||||||
|
const isBlock = preText.trim().length === 0;
|
||||||
|
const couldBeTable = startsWithPipeRe.test(preText);
|
||||||
|
const spansTableCells = couldBeTable && content.includes("\n");
|
||||||
|
const isValid =
|
||||||
|
isBlock ||
|
||||||
|
!(
|
||||||
|
spansTableCells ||
|
||||||
|
content.startsWith(">") ||
|
||||||
|
content.startsWith("->") ||
|
||||||
|
content.endsWith("-") ||
|
||||||
|
content.includes("--")
|
||||||
|
);
|
||||||
|
// If a valid block/inline comment...
|
||||||
|
if (isValid) {
|
||||||
|
const clearedContent = content
|
||||||
|
.replace(notSpaceCrLfRe, safeCommentCharacter)
|
||||||
|
.replace(trailingSpaceRe, replaceTrailingSpace);
|
||||||
|
text =
|
||||||
|
text.slice(0, i + htmlCommentBegin.length) +
|
||||||
|
clearedContent +
|
||||||
|
text.slice(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = j + htmlCommentEnd.length;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Escapes a string for use in a RegExp
|
||||||
|
module.exports.escapeForRegExp = function escapeForRegExp(str) {
|
||||||
|
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds ellipsis to the left/right/middle of the specified text.
|
||||||
|
*
|
||||||
|
* @param {string} text Text to ellipsify.
|
||||||
|
* @param {boolean} [start] True iff the start of the text is important.
|
||||||
|
* @param {boolean} [end] True iff the end of the text is important.
|
||||||
|
* @returns {string} Ellipsified text.
|
||||||
|
*/
|
||||||
|
function ellipsify(text, start, end) {
|
||||||
|
if (text.length <= 30) {
|
||||||
|
// Nothing to do
|
||||||
|
} else if (start && end) {
|
||||||
|
text = text.slice(0, 15) + "..." + text.slice(-15);
|
||||||
|
} else if (end) {
|
||||||
|
text = "..." + text.slice(-30);
|
||||||
|
} else {
|
||||||
|
text = text.slice(0, 30) + "...";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
module.exports.ellipsify = ellipsify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a generic error object via the onError callback.
|
||||||
|
*
|
||||||
|
* @param {RuleOnError} onError RuleOnError instance.
|
||||||
|
* @param {number} lineNumber Line number.
|
||||||
|
* @param {string} [detail] Error details.
|
||||||
|
* @param {string} [context] Error context.
|
||||||
|
* @param {number[]} [range] Column and length of error.
|
||||||
|
* @param {RuleOnErrorFixInfo} [fixInfo] RuleOnErrorFixInfo instance.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function addError(onError, lineNumber, detail, context, range, fixInfo) {
|
||||||
|
onError({
|
||||||
|
lineNumber,
|
||||||
|
detail,
|
||||||
|
context,
|
||||||
|
range,
|
||||||
|
fixInfo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
module.exports.addError = addError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error object with details conditionally via the onError callback.
|
||||||
|
*
|
||||||
|
* @param {RuleOnError} onError RuleOnError instance.
|
||||||
|
* @param {number} lineNumber Line number.
|
||||||
|
* @param {Object} expected Expected value.
|
||||||
|
* @param {Object} actual Actual value.
|
||||||
|
* @param {string} [detail] Error details.
|
||||||
|
* @param {string} [context] Error context.
|
||||||
|
* @param {number[]} [range] Column and length of error.
|
||||||
|
* @param {RuleOnErrorFixInfo} [fixInfo] RuleOnErrorFixInfo instance.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function addErrorDetailIf(
|
||||||
|
onError, lineNumber, expected, actual, detail, context, range, fixInfo) {
|
||||||
|
if (expected !== actual) {
|
||||||
|
addError(
|
||||||
|
onError,
|
||||||
|
lineNumber,
|
||||||
|
"Expected: " + expected + "; Actual: " + actual +
|
||||||
|
(detail ? "; " + detail : ""),
|
||||||
|
context,
|
||||||
|
range,
|
||||||
|
fixInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports.addErrorDetailIf = addErrorDetailIf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error object with context via the onError callback.
|
||||||
|
*
|
||||||
|
* @param {RuleOnError} onError RuleOnError instance.
|
||||||
|
* @param {number} lineNumber Line number.
|
||||||
|
* @param {string} context Error context.
|
||||||
|
* @param {boolean} [start] True iff the start of the text is important.
|
||||||
|
* @param {boolean} [end] True iff the end of the text is important.
|
||||||
|
* @param {number[]} [range] Column and length of error.
|
||||||
|
* @param {RuleOnErrorFixInfo} [fixInfo] RuleOnErrorFixInfo instance.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function addErrorContext(
|
||||||
|
onError, lineNumber, context, start, end, range, fixInfo) {
|
||||||
|
context = ellipsify(context.replace(newLineRe, "\n"), start, end);
|
||||||
|
addError(onError, lineNumber, undefined, context, range, fixInfo);
|
||||||
|
}
|
||||||
|
module.exports.addErrorContext = addErrorContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a range within a file (start line/column to end line/column, subset of MicromarkToken).
|
||||||
|
*
|
||||||
|
* @typedef {Object} FileRange
|
||||||
|
* @property {number} startLine Start line (1-based).
|
||||||
|
* @property {number} startColumn Start column (1-based).
|
||||||
|
* @property {number} endLine End line (1-based).
|
||||||
|
* @property {number} endColumn End column (1-based).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether line/column A is less than or equal to line/column B.
|
||||||
|
*
|
||||||
|
* @param {number} lineA Line A.
|
||||||
|
* @param {number} columnA Column A.
|
||||||
|
* @param {number} lineB Line B.
|
||||||
|
* @param {number} columnB Column B.
|
||||||
|
* @returns {boolean} True iff A is less than or equal to B.
|
||||||
|
*/
|
||||||
|
const positionLessThanOrEqual = (lineA, columnA, lineB, columnB) => (
|
||||||
|
(lineA < lineB) ||
|
||||||
|
((lineA === lineB) && (columnA <= columnB))
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether two ranges (or MicromarkTokens) overlap anywhere.
|
||||||
|
*
|
||||||
|
* @param {FileRange|MicromarkToken} rangeA Range A.
|
||||||
|
* @param {FileRange|MicromarkToken} rangeB Range B.
|
||||||
|
* @returns {boolean} True iff the two ranges overlap.
|
||||||
|
*/
|
||||||
|
module.exports.hasOverlap = function hasOverlap(rangeA, rangeB) {
|
||||||
|
const lte = positionLessThanOrEqual(rangeA.startLine, rangeA.startColumn, rangeB.startLine, rangeB.startColumn);
|
||||||
|
const first = lte ? rangeA : rangeB;
|
||||||
|
const second = lte ? rangeB : rangeA;
|
||||||
|
return positionLessThanOrEqual(second.startLine, second.startColumn, first.endLine, first.endColumn);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines if the front matter includes a title
|
||||||
|
module.exports.frontMatterHasTitle =
|
||||||
|
function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
|
||||||
|
const ignoreFrontMatter =
|
||||||
|
(frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
|
||||||
|
const frontMatterTitleRe =
|
||||||
|
new RegExp(
|
||||||
|
String(frontMatterTitlePattern || "^\\s*\"?title\"?\\s*[:=]"),
|
||||||
|
"i"
|
||||||
|
);
|
||||||
|
return !ignoreFrontMatter &&
|
||||||
|
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with information about reference links and images.
|
||||||
|
*
|
||||||
|
* @param {MicromarkToken[]} tokens Micromark tokens.
|
||||||
|
* @returns {Object} Reference link/image data.
|
||||||
|
*/
|
||||||
|
function getReferenceLinkImageData(tokens) {
|
||||||
|
const normalizeReference = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
|
||||||
|
const getText = (t) => t?.children.filter((c) => c.type !== "blockQuotePrefix").map((c) => c.text).join("");
|
||||||
|
const references = new Map();
|
||||||
|
const shortcuts = new Map();
|
||||||
|
const addReferenceToDictionary = (token, label, isShortcut) => {
|
||||||
|
const referenceDatum = [
|
||||||
|
token.startLine - 1,
|
||||||
|
token.startColumn - 1,
|
||||||
|
token.text.length
|
||||||
|
];
|
||||||
|
const reference = normalizeReference(label);
|
||||||
|
const dictionary = isShortcut ? shortcuts : references;
|
||||||
|
const referenceData = dictionary.get(reference) || [];
|
||||||
|
referenceData.push(referenceDatum);
|
||||||
|
dictionary.set(reference, referenceData);
|
||||||
|
};
|
||||||
|
const definitions = new Map();
|
||||||
|
const definitionLineIndices = [];
|
||||||
|
const duplicateDefinitions = [];
|
||||||
|
const filteredTokens =
|
||||||
|
micromark.filterByTypes(
|
||||||
|
tokens,
|
||||||
|
[
|
||||||
|
// definitionLineIndices
|
||||||
|
"definition", "gfmFootnoteDefinition",
|
||||||
|
// definitions and definitionLineIndices
|
||||||
|
"definitionLabelString", "gfmFootnoteDefinitionLabelString",
|
||||||
|
// references and shortcuts
|
||||||
|
"gfmFootnoteCall", "image", "link",
|
||||||
|
// undefined link labels
|
||||||
|
"undefinedReferenceCollapsed", "undefinedReferenceFull", "undefinedReferenceShortcut"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
for (const token of filteredTokens) {
|
||||||
|
let labelPrefix = "";
|
||||||
|
// eslint-disable-next-line default-case
|
||||||
|
switch (token.type) {
|
||||||
|
case "definition":
|
||||||
|
case "gfmFootnoteDefinition":
|
||||||
|
// definitionLineIndices
|
||||||
|
for (let i = token.startLine; i <= token.endLine; i++) {
|
||||||
|
definitionLineIndices.push(i - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "gfmFootnoteDefinitionLabelString":
|
||||||
|
labelPrefix = "^";
|
||||||
|
case "definitionLabelString": // eslint-disable-line no-fallthrough
|
||||||
|
{
|
||||||
|
// definitions and definitionLineIndices
|
||||||
|
const reference = normalizeReference(`${labelPrefix}${token.text}`);
|
||||||
|
if (definitions.has(reference)) {
|
||||||
|
duplicateDefinitions.push([ reference, token.startLine - 1 ]);
|
||||||
|
} else {
|
||||||
|
const parent =
|
||||||
|
micromark.getParentOfType(token, [ "definition" ]);
|
||||||
|
const destinationString = parent &&
|
||||||
|
micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
|
||||||
|
definitions.set(
|
||||||
|
reference,
|
||||||
|
[ token.startLine - 1, destinationString ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "gfmFootnoteCall":
|
||||||
|
case "image":
|
||||||
|
case "link":
|
||||||
|
{
|
||||||
|
// Identify if shortcut or full/collapsed
|
||||||
|
let isShortcut = (token.children.length === 1);
|
||||||
|
const isFullOrCollapsed = (token.children.length === 2) && !token.children.some((t) => t.type === "resource");
|
||||||
|
const [ labelText ] = micromark.getDescendantsByType(token, [ "label", "labelText" ]);
|
||||||
|
const [ referenceString ] = micromark.getDescendantsByType(token, [ "reference", "referenceString" ]);
|
||||||
|
let label = getText(labelText);
|
||||||
|
// Identify if footnote
|
||||||
|
if (!isShortcut && !isFullOrCollapsed) {
|
||||||
|
const [ footnoteCallMarker, footnoteCallString ] = token.children.filter(
|
||||||
|
(t) => [ "gfmFootnoteCallMarker", "gfmFootnoteCallString" ].includes(t.type)
|
||||||
|
);
|
||||||
|
if (footnoteCallMarker && footnoteCallString) {
|
||||||
|
label = `${footnoteCallMarker.text}${footnoteCallString.text}`;
|
||||||
|
isShortcut = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Track link (handle shortcuts separately due to ambiguity in "text [text] text")
|
||||||
|
if (isShortcut || isFullOrCollapsed) {
|
||||||
|
addReferenceToDictionary(token, getText(referenceString) || label, isShortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "undefinedReferenceCollapsed":
|
||||||
|
case "undefinedReferenceFull":
|
||||||
|
case "undefinedReferenceShortcut":
|
||||||
|
{
|
||||||
|
const undefinedReference = micromark.getDescendantsByType(token, [ "undefinedReference" ])[0];
|
||||||
|
const label = undefinedReference.children.map((t) => t.text).join("");
|
||||||
|
const isShortcut = (token.type === "undefinedReferenceShortcut");
|
||||||
|
addReferenceToDictionary(token, label, isShortcut);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
references,
|
||||||
|
shortcuts,
|
||||||
|
definitions,
|
||||||
|
duplicateDefinitions,
|
||||||
|
definitionLineIndices
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports.getReferenceLinkImageData = getReferenceLinkImageData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the most common line ending, falling back to the platform default.
|
||||||
|
*
|
||||||
|
* @param {string} input Markdown content to analyze.
|
||||||
|
* @param {Object} [os] Node.js "os" module.
|
||||||
|
* @returns {string} Preferred line ending.
|
||||||
|
*/
|
||||||
|
function getPreferredLineEnding(input, os) {
|
||||||
|
let cr = 0;
|
||||||
|
let lf = 0;
|
||||||
|
let crlf = 0;
|
||||||
|
const endings = input.match(newLineRe) || [];
|
||||||
|
for (const ending of endings) {
|
||||||
|
// eslint-disable-next-line default-case
|
||||||
|
switch (ending) {
|
||||||
|
case "\r":
|
||||||
|
cr++;
|
||||||
|
break;
|
||||||
|
case "\n":
|
||||||
|
lf++;
|
||||||
|
break;
|
||||||
|
case "\r\n":
|
||||||
|
crlf++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let preferredLineEnding = null;
|
||||||
|
if (!cr && !lf && !crlf) {
|
||||||
|
preferredLineEnding = (os && os.EOL) || "\n";
|
||||||
|
} else if ((lf >= crlf) && (lf >= cr)) {
|
||||||
|
preferredLineEnding = "\n";
|
||||||
|
} else if (crlf >= cr) {
|
||||||
|
preferredLineEnding = "\r\n";
|
||||||
|
} else {
|
||||||
|
preferredLineEnding = "\r";
|
||||||
|
}
|
||||||
|
return preferredLineEnding;
|
||||||
|
}
|
||||||
|
module.exports.getPreferredLineEnding = getPreferredLineEnding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands a path with a tilde to an absolute path.
|
||||||
|
*
|
||||||
|
* @param {string} file Path that may begin with a tilde.
|
||||||
|
* @param {Object} os Node.js "os" module.
|
||||||
|
* @returns {string} Absolute path (or original path).
|
||||||
|
*/
|
||||||
|
function expandTildePath(file, os) {
|
||||||
|
const homedir = os && os.homedir && os.homedir();
|
||||||
|
return homedir ? file.replace(/^~($|\/|\\)/, `${homedir}$1`) : file;
|
||||||
|
}
|
||||||
|
module.exports.expandTildePath = expandTildePath;
|
1025
helpers/helpers.js
1025
helpers/helpers.js
File diff suppressed because it is too large
Load diff
327
helpers/micromark-helpers.cjs
Normal file
327
helpers/micromark-helpers.cjs
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { flatTokensSymbol, htmlFlowSymbol } = require("./shared.cjs");
|
||||||
|
|
||||||
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
|
/** @typedef {import("micromark-util-types", { with: { "resolution-mode": "import" } }).TokenType} TokenType */
|
||||||
|
/** @typedef {import("../lib/exports.mjs").MicromarkToken} Token */
|
||||||
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
|
/** @typedef {import("../lib/micromark-types.d.mts", { with: { "resolution-mode": "import" } })} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a Micromark token is within an htmlFlow type.
|
||||||
|
*
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @returns {boolean} True iff the token is within an htmlFlow type.
|
||||||
|
*/
|
||||||
|
function inHtmlFlow(token) {
|
||||||
|
return Boolean(token[htmlFlowSymbol]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a token is an htmlFlow type containing an HTML comment.
|
||||||
|
*
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @returns {boolean} True iff token is htmlFlow containing a comment.
|
||||||
|
*/
|
||||||
|
function isHtmlFlowComment(token) {
|
||||||
|
const { text, type } = token;
|
||||||
|
if (
|
||||||
|
(type === "htmlFlow") &&
|
||||||
|
text.startsWith("<!--") &&
|
||||||
|
text.endsWith("-->")
|
||||||
|
) {
|
||||||
|
const comment = text.slice(4, -3);
|
||||||
|
return (
|
||||||
|
!comment.startsWith(">") &&
|
||||||
|
!comment.startsWith("->") &&
|
||||||
|
!comment.endsWith("-")
|
||||||
|
// The following condition from the CommonMark specification is commented
|
||||||
|
// to avoid parsing HTML comments that include "--" because that is NOT a
|
||||||
|
// condition of the HTML specification.
|
||||||
|
// https://spec.commonmark.org/0.30/#raw-html
|
||||||
|
// https://html.spec.whatwg.org/multipage/syntax.html#comments
|
||||||
|
// && !comment.includes("--")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a range of numbers to a set.
|
||||||
|
*
|
||||||
|
* @param {Set<number>} set Set of numbers.
|
||||||
|
* @param {number} start Starting number.
|
||||||
|
* @param {number} end Ending number.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function addRangeToSet(set, start, end) {
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
set.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback AllowedPredicate
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @returns {boolean} True iff allowed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback TransformPredicate
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @returns {Token[]} Child tokens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a list of Micromark tokens by predicate.
|
||||||
|
*
|
||||||
|
* @param {Token[]} tokens Micromark tokens.
|
||||||
|
* @param {AllowedPredicate} allowed Allowed token predicate.
|
||||||
|
* @param {TransformPredicate} [transformChildren] Transform predicate.
|
||||||
|
* @returns {Token[]} Filtered tokens.
|
||||||
|
*/
|
||||||
|
function filterByPredicate(tokens, allowed, transformChildren) {
|
||||||
|
const result = [];
|
||||||
|
const queue = [
|
||||||
|
{
|
||||||
|
"array": tokens,
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const current = queue[queue.length - 1];
|
||||||
|
const { array, index } = current;
|
||||||
|
if (index < array.length) {
|
||||||
|
const token = array[current.index++];
|
||||||
|
if (allowed(token)) {
|
||||||
|
result.push(token);
|
||||||
|
}
|
||||||
|
const { children } = token;
|
||||||
|
if (children.length > 0) {
|
||||||
|
const transformed =
|
||||||
|
transformChildren ? transformChildren(token) : children;
|
||||||
|
queue.push(
|
||||||
|
{
|
||||||
|
"array": transformed,
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a list of Micromark tokens by type.
|
||||||
|
*
|
||||||
|
* @param {Token[]} tokens Micromark tokens.
|
||||||
|
* @param {TokenType[]} types Types to allow.
|
||||||
|
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
|
||||||
|
* @returns {Token[]} Filtered tokens.
|
||||||
|
*/
|
||||||
|
function filterByTypes(tokens, types, htmlFlow) {
|
||||||
|
const predicate = (token) => types.includes(token.type) && (htmlFlow || !inHtmlFlow(token));
|
||||||
|
const flatTokens = tokens[flatTokensSymbol];
|
||||||
|
if (flatTokens) {
|
||||||
|
return flatTokens.filter(predicate);
|
||||||
|
}
|
||||||
|
return filterByPredicate(tokens, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the blockquote prefix text (if any) for the specified line number.
|
||||||
|
*
|
||||||
|
* @param {Token[]} tokens Micromark tokens.
|
||||||
|
* @param {number} lineNumber Line number to examine.
|
||||||
|
* @param {number} [count] Number of times to repeat.
|
||||||
|
* @returns {string} Blockquote prefix text.
|
||||||
|
*/
|
||||||
|
function getBlockQuotePrefixText(tokens, lineNumber, count = 1) {
|
||||||
|
return filterByTypes(tokens, [ "blockQuotePrefix", "linePrefix" ])
|
||||||
|
.filter((prefix) => prefix.startLine === lineNumber)
|
||||||
|
.map((prefix) => prefix.text)
|
||||||
|
.join("")
|
||||||
|
.trimEnd()
|
||||||
|
// eslint-disable-next-line unicorn/prefer-spread
|
||||||
|
.concat("\n")
|
||||||
|
.repeat(count);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of nested Micromark token descendants by type path.
|
||||||
|
*
|
||||||
|
* @param {Token|Token[]} parent Micromark token parent or parents.
|
||||||
|
* @param {(TokenType|TokenType[])[]} typePath Micromark token type path.
|
||||||
|
* @returns {Token[]} Micromark token descendants.
|
||||||
|
*/
|
||||||
|
function getDescendantsByType(parent, typePath) {
|
||||||
|
let tokens = Array.isArray(parent) ? parent : [ parent ];
|
||||||
|
for (const type of typePath) {
|
||||||
|
const predicate = (token) => Array.isArray(type) ? type.includes(token.type) : (type === token.type);
|
||||||
|
tokens = tokens.flatMap((t) => t.children.filter(predicate));
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the heading level of a Micromark heading tokan.
|
||||||
|
*
|
||||||
|
* @param {Token} heading Micromark heading token.
|
||||||
|
* @returns {number} Heading level.
|
||||||
|
*/
|
||||||
|
function getHeadingLevel(heading) {
|
||||||
|
let level = 1;
|
||||||
|
const headingSequence = heading.children.find(
|
||||||
|
(child) => [ "atxHeadingSequence", "setextHeadingLine" ].includes(child.type)
|
||||||
|
);
|
||||||
|
// @ts-ignore
|
||||||
|
const { text } = headingSequence;
|
||||||
|
if (text[0] === "#") {
|
||||||
|
level = Math.min(text.length, 6);
|
||||||
|
} else if (text[0] === "-") {
|
||||||
|
level = 2;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the heading style of a Micromark heading tokan.
|
||||||
|
*
|
||||||
|
* @param {Token} heading Micromark heading token.
|
||||||
|
* @returns {"atx" | "atx_closed" | "setext"} Heading style.
|
||||||
|
*/
|
||||||
|
function getHeadingStyle(heading) {
|
||||||
|
if (heading.type === "setextHeading") {
|
||||||
|
return "setext";
|
||||||
|
}
|
||||||
|
const atxHeadingSequenceLength = heading.children.filter(
|
||||||
|
(child) => child.type === "atxHeadingSequence"
|
||||||
|
).length;
|
||||||
|
if (atxHeadingSequenceLength === 1) {
|
||||||
|
return "atx";
|
||||||
|
}
|
||||||
|
return "atx_closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the heading text of a Micromark heading token.
|
||||||
|
*
|
||||||
|
* @param {Token} heading Micromark heading token.
|
||||||
|
* @returns {string} Heading text.
|
||||||
|
*/
|
||||||
|
function getHeadingText(heading) {
|
||||||
|
const headingText = getDescendantsByType(heading, [ [ "atxHeadingText", "setextHeadingText" ] ])
|
||||||
|
.flatMap((descendant) => descendant.children.filter((child) => child.type !== "htmlText"))
|
||||||
|
.map((data) => data.text)
|
||||||
|
.join("")
|
||||||
|
.replace(/[\r\n]+/g, " ");
|
||||||
|
return headingText || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML tag information.
|
||||||
|
*
|
||||||
|
* @typedef {Object} HtmlTagInfo
|
||||||
|
* @property {boolean} close True iff close tag.
|
||||||
|
* @property {string} name Tag name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the tag in an HTML token.
|
||||||
|
*
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @returns {HtmlTagInfo | null} HTML tag information.
|
||||||
|
*/
|
||||||
|
function getHtmlTagInfo(token) {
|
||||||
|
const htmlTagNameRe = /^<([^!>][^/\s>]*)/;
|
||||||
|
if (token.type === "htmlText") {
|
||||||
|
const match = htmlTagNameRe.exec(token.text);
|
||||||
|
if (match) {
|
||||||
|
const name = match[1];
|
||||||
|
const close = name.startsWith("/");
|
||||||
|
return {
|
||||||
|
close,
|
||||||
|
"name": close ? name.slice(1) : name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the nearest parent of the specified type for a Micromark token.
|
||||||
|
*
|
||||||
|
* @param {Token} token Micromark token.
|
||||||
|
* @param {TokenType[]} types Types to allow.
|
||||||
|
* @returns {Token | null} Parent token.
|
||||||
|
*/
|
||||||
|
function getParentOfType(token, types) {
|
||||||
|
/** @type {Token | null} */
|
||||||
|
let current = token;
|
||||||
|
while ((current = current.parent) && !types.includes(current.type)) {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docfxTabSyntaxRe = /^#tab\//;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the specified Micromark token looks like a Docfx tab.
|
||||||
|
*
|
||||||
|
* @param {Token | null} heading Micromark token.
|
||||||
|
* @returns {boolean} True iff the token looks like a Docfx tab.
|
||||||
|
*/
|
||||||
|
function isDocfxTab(heading) {
|
||||||
|
// See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs
|
||||||
|
if (heading?.type === "atxHeading") {
|
||||||
|
const headingTexts = getDescendantsByType(heading, [ "atxHeadingText" ]);
|
||||||
|
if ((headingTexts.length === 1) && (headingTexts[0].children.length === 1) && (headingTexts[0].children[0].type === "link")) {
|
||||||
|
const resourceDestinationStrings = filterByTypes(headingTexts[0].children[0].children, [ "resourceDestinationString" ]);
|
||||||
|
return (resourceDestinationStrings.length === 1) && docfxTabSyntaxRe.test(resourceDestinationStrings[0].text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set containing token types that do not contain content.
|
||||||
|
*
|
||||||
|
* @type {Set<TokenType>}
|
||||||
|
*/
|
||||||
|
const nonContentTokens = new Set([
|
||||||
|
"blockQuoteMarker",
|
||||||
|
"blockQuotePrefix",
|
||||||
|
"blockQuotePrefixWhitespace",
|
||||||
|
"lineEnding",
|
||||||
|
"lineEndingBlank",
|
||||||
|
"linePrefix",
|
||||||
|
"listItemIndent",
|
||||||
|
"undefinedReference",
|
||||||
|
"undefinedReferenceCollapsed",
|
||||||
|
"undefinedReferenceFull",
|
||||||
|
"undefinedReferenceShortcut"
|
||||||
|
]);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addRangeToSet,
|
||||||
|
filterByPredicate,
|
||||||
|
filterByTypes,
|
||||||
|
getBlockQuotePrefixText,
|
||||||
|
getDescendantsByType,
|
||||||
|
getHeadingLevel,
|
||||||
|
getHeadingStyle,
|
||||||
|
getHeadingText,
|
||||||
|
getHtmlTagInfo,
|
||||||
|
getParentOfType,
|
||||||
|
inHtmlFlow,
|
||||||
|
isDocfxTab,
|
||||||
|
isHtmlFlowComment,
|
||||||
|
nonContentTokens
|
||||||
|
};
|
|
@ -1,445 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const {
|
|
||||||
directive, gfmAutolinkLiteral, gfmFootnote, gfmTable, math,
|
|
||||||
parse, postprocess, preprocess
|
|
||||||
// @ts-ignore
|
|
||||||
} = require("markdownlint-micromark");
|
|
||||||
const { newLineRe } = require("./shared.js");
|
|
||||||
|
|
||||||
const flatTokensSymbol = Symbol("flat-tokens");
|
|
||||||
|
|
||||||
/** @typedef {import("markdownlint-micromark").Event} Event */
|
|
||||||
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
|
|
||||||
/** @typedef {import("markdownlint-micromark").TokenType} TokenType */
|
|
||||||
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a token is an htmlFlow type containing an HTML comment.
|
|
||||||
*
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @returns {boolean} True iff token is htmlFlow containing a comment.
|
|
||||||
*/
|
|
||||||
function isHtmlFlowComment(token) {
|
|
||||||
const { text, type } = token;
|
|
||||||
if (
|
|
||||||
(type === "htmlFlow") &&
|
|
||||||
text.startsWith("<!--") &&
|
|
||||||
text.endsWith("-->")
|
|
||||||
) {
|
|
||||||
const comment = text.slice(4, -3);
|
|
||||||
return (
|
|
||||||
!comment.startsWith(">") &&
|
|
||||||
!comment.startsWith("->") &&
|
|
||||||
!comment.endsWith("-")
|
|
||||||
// The following condition from the CommonMark specification is commented
|
|
||||||
// to avoid parsing HTML comments that include "--" because that is NOT a
|
|
||||||
// condition of the HTML specification.
|
|
||||||
// https://spec.commonmark.org/0.30/#raw-html
|
|
||||||
// https://html.spec.whatwg.org/multipage/syntax.html#comments
|
|
||||||
// && !comment.includes("--")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a Markdown document and returns Micromark events.
|
|
||||||
*
|
|
||||||
* @param {string} markdown Markdown document.
|
|
||||||
* @param {ParseOptions} [micromarkOptions] Options for micromark.
|
|
||||||
* @param {boolean} [referencesDefined] Treat references as defined.
|
|
||||||
* @returns {Event[]} Micromark events.
|
|
||||||
*/
|
|
||||||
function getMicromarkEvents(
|
|
||||||
markdown,
|
|
||||||
micromarkOptions = {},
|
|
||||||
referencesDefined = true
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Customize options object to add useful extensions
|
|
||||||
micromarkOptions.extensions = micromarkOptions.extensions || [];
|
|
||||||
micromarkOptions.extensions.push(
|
|
||||||
directive(),
|
|
||||||
gfmAutolinkLiteral(),
|
|
||||||
gfmFootnote(),
|
|
||||||
gfmTable(),
|
|
||||||
math()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use micromark to parse document into Events
|
|
||||||
const encoding = undefined;
|
|
||||||
const eol = true;
|
|
||||||
const parseContext = parse(micromarkOptions);
|
|
||||||
if (referencesDefined) {
|
|
||||||
// Customize ParseContext to treat all references as defined
|
|
||||||
parseContext.defined.includes = (searchElement) => searchElement.length > 0;
|
|
||||||
}
|
|
||||||
const chunks = preprocess()(markdown, encoding, eol);
|
|
||||||
const events = postprocess(parseContext.document().write(chunks));
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a Markdown document and returns (frozen) tokens.
|
|
||||||
*
|
|
||||||
* @param {string} markdown Markdown document.
|
|
||||||
* @param {ParseOptions} micromarkOptions Options for micromark.
|
|
||||||
* @param {boolean} referencesDefined Treat references as defined.
|
|
||||||
* @param {number} lineDelta Offset to apply to start/end line.
|
|
||||||
* @param {Token} [ancestor] Parent of top-most tokens.
|
|
||||||
* @returns {Token[]} Micromark tokens (frozen).
|
|
||||||
*/
|
|
||||||
function micromarkParseWithOffset(
|
|
||||||
markdown,
|
|
||||||
micromarkOptions,
|
|
||||||
referencesDefined,
|
|
||||||
lineDelta,
|
|
||||||
ancestor
|
|
||||||
) {
|
|
||||||
// Use micromark to parse document into Events
|
|
||||||
const events = getMicromarkEvents(
|
|
||||||
markdown, micromarkOptions, referencesDefined
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create Token objects
|
|
||||||
const document = [];
|
|
||||||
let flatTokens = [];
|
|
||||||
/** @type {Token} */
|
|
||||||
const root = {
|
|
||||||
"type": "data",
|
|
||||||
"startLine": -1,
|
|
||||||
"startColumn": -1,
|
|
||||||
"endLine": -1,
|
|
||||||
"endColumn": -1,
|
|
||||||
"text": "ROOT",
|
|
||||||
"children": document,
|
|
||||||
"parent": null
|
|
||||||
};
|
|
||||||
const history = [ root ];
|
|
||||||
let current = root;
|
|
||||||
// eslint-disable-next-line jsdoc/valid-types
|
|
||||||
/** @type ParseOptions | null */
|
|
||||||
let reparseOptions = null;
|
|
||||||
let lines = null;
|
|
||||||
let skipHtmlFlowChildren = false;
|
|
||||||
for (const event of events) {
|
|
||||||
const [ kind, token, context ] = event;
|
|
||||||
const { type, start, end } = token;
|
|
||||||
const { "column": startColumn, "line": startLine } = start;
|
|
||||||
const { "column": endColumn, "line": endLine } = end;
|
|
||||||
const text = context.sliceSerialize(token);
|
|
||||||
if ((kind === "enter") && !skipHtmlFlowChildren) {
|
|
||||||
const previous = current;
|
|
||||||
history.push(previous);
|
|
||||||
current = {
|
|
||||||
type,
|
|
||||||
"startLine": startLine + lineDelta,
|
|
||||||
startColumn,
|
|
||||||
"endLine": endLine + lineDelta,
|
|
||||||
endColumn,
|
|
||||||
text,
|
|
||||||
"children": [],
|
|
||||||
"parent": ((previous === root) ? (ancestor || null) : previous)
|
|
||||||
};
|
|
||||||
previous.children.push(current);
|
|
||||||
flatTokens.push(current);
|
|
||||||
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
|
|
||||||
skipHtmlFlowChildren = true;
|
|
||||||
if (!reparseOptions || !lines) {
|
|
||||||
reparseOptions = {
|
|
||||||
...micromarkOptions,
|
|
||||||
"extensions": [
|
|
||||||
{
|
|
||||||
"disable": {
|
|
||||||
"null": [ "codeIndented", "htmlFlow" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
lines = markdown.split(newLineRe);
|
|
||||||
}
|
|
||||||
const reparseMarkdown = lines
|
|
||||||
.slice(current.startLine - 1, current.endLine)
|
|
||||||
.join("\n");
|
|
||||||
const tokens = micromarkParseWithOffset(
|
|
||||||
reparseMarkdown,
|
|
||||||
reparseOptions,
|
|
||||||
referencesDefined,
|
|
||||||
current.startLine - 1,
|
|
||||||
current
|
|
||||||
);
|
|
||||||
current.children = tokens;
|
|
||||||
// Avoid stack overflow of Array.push(...spread)
|
|
||||||
// eslint-disable-next-line unicorn/prefer-spread
|
|
||||||
flatTokens = flatTokens.concat(tokens[flatTokensSymbol]);
|
|
||||||
}
|
|
||||||
} else if (kind === "exit") {
|
|
||||||
if (type === "htmlFlow") {
|
|
||||||
skipHtmlFlowChildren = false;
|
|
||||||
}
|
|
||||||
if (!skipHtmlFlowChildren) {
|
|
||||||
Object.freeze(current.children);
|
|
||||||
Object.freeze(current);
|
|
||||||
// @ts-ignore
|
|
||||||
current = history.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return document
|
|
||||||
Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens });
|
|
||||||
Object.freeze(document);
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a Markdown document and returns (frozen) tokens.
|
|
||||||
*
|
|
||||||
* @param {string} markdown Markdown document.
|
|
||||||
* @param {ParseOptions} [micromarkOptions] Options for micromark.
|
|
||||||
* @param {boolean} [referencesDefined] Treat references as defined.
|
|
||||||
* @returns {Token[]} Micromark tokens (frozen).
|
|
||||||
*/
|
|
||||||
function micromarkParse(
|
|
||||||
markdown,
|
|
||||||
micromarkOptions = {},
|
|
||||||
referencesDefined = true
|
|
||||||
) {
|
|
||||||
return micromarkParseWithOffset(
|
|
||||||
markdown,
|
|
||||||
micromarkOptions,
|
|
||||||
referencesDefined,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback AllowedPredicate
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @returns {boolean} True iff allowed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback TransformPredicate
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @returns {Token[]} Child tokens.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter a list of Micromark tokens by predicate.
|
|
||||||
*
|
|
||||||
* @param {Token[]} tokens Micromark tokens.
|
|
||||||
* @param {AllowedPredicate} [allowed] Allowed token predicate.
|
|
||||||
* @param {TransformPredicate} [transformChildren] Transform predicate.
|
|
||||||
* @returns {Token[]} Filtered tokens.
|
|
||||||
*/
|
|
||||||
function filterByPredicate(tokens, allowed, transformChildren) {
|
|
||||||
allowed = allowed || (() => true);
|
|
||||||
const result = [];
|
|
||||||
const queue = [
|
|
||||||
{
|
|
||||||
"array": tokens,
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const current = queue[queue.length - 1];
|
|
||||||
const { array, index } = current;
|
|
||||||
if (index < array.length) {
|
|
||||||
const token = array[current.index++];
|
|
||||||
if (allowed(token)) {
|
|
||||||
result.push(token);
|
|
||||||
}
|
|
||||||
const { children } = token;
|
|
||||||
if (children.length > 0) {
|
|
||||||
const transformed =
|
|
||||||
transformChildren ? transformChildren(token) : children;
|
|
||||||
queue.push(
|
|
||||||
{
|
|
||||||
"array": transformed,
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
queue.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter a list of Micromark tokens by type.
|
|
||||||
*
|
|
||||||
* @param {Token[]} tokens Micromark tokens.
|
|
||||||
* @param {TokenType[]} types Types to allow.
|
|
||||||
* @returns {Token[]} Filtered tokens.
|
|
||||||
*/
|
|
||||||
function filterByTypes(tokens, types) {
|
|
||||||
const predicate = (token) => types.includes(token.type);
|
|
||||||
const flatTokens = tokens[flatTokensSymbol];
|
|
||||||
if (flatTokens) {
|
|
||||||
return flatTokens.filter(predicate);
|
|
||||||
}
|
|
||||||
return filterByPredicate(tokens, predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the heading level of a Micromark heading tokan.
|
|
||||||
*
|
|
||||||
* @param {Token} heading Micromark heading token.
|
|
||||||
* @returns {number} Heading level.
|
|
||||||
*/
|
|
||||||
function getHeadingLevel(heading) {
|
|
||||||
const headingSequence = filterByTypes(
|
|
||||||
heading.children,
|
|
||||||
[ "atxHeadingSequence", "setextHeadingLineSequence" ]
|
|
||||||
);
|
|
||||||
let level = 1;
|
|
||||||
const { text } = headingSequence[0];
|
|
||||||
if (text[0] === "#") {
|
|
||||||
level = Math.min(text.length, 6);
|
|
||||||
} else if (text[0] === "-") {
|
|
||||||
level = 2;
|
|
||||||
}
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTML tag information.
|
|
||||||
*
|
|
||||||
* @typedef {Object} HtmlTagInfo
|
|
||||||
* @property {boolean} close True iff close tag.
|
|
||||||
* @property {string} name Tag name.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets information about the tag in an HTML token.
|
|
||||||
*
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @returns {HtmlTagInfo | null} HTML tag information.
|
|
||||||
*/
|
|
||||||
function getHtmlTagInfo(token) {
|
|
||||||
const htmlTagNameRe = /^<([^!>][^/\s>]*)/;
|
|
||||||
if (token.type === "htmlText") {
|
|
||||||
const match = htmlTagNameRe.exec(token.text);
|
|
||||||
if (match) {
|
|
||||||
const name = match[1];
|
|
||||||
const close = name.startsWith("/");
|
|
||||||
return {
|
|
||||||
close,
|
|
||||||
"name": close ? name.slice(1) : name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the nearest parent of the specified type for a Micromark token.
|
|
||||||
*
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @param {TokenType[]} types Types to allow.
|
|
||||||
* @returns {Token | null} Parent token.
|
|
||||||
*/
|
|
||||||
function getTokenParentOfType(token, types) {
|
|
||||||
/** @type {Token | null} */
|
|
||||||
let current = token;
|
|
||||||
while ((current = current.parent) && !types.includes(current.type)) {
|
|
||||||
// Empty
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text of the first match from a list of Micromark tokens by type.
|
|
||||||
*
|
|
||||||
* @param {Token[]} tokens Micromark tokens.
|
|
||||||
* @param {TokenType} type Type to match.
|
|
||||||
* @returns {string | null} Text of token.
|
|
||||||
*/
|
|
||||||
function getTokenTextByType(tokens, type) {
|
|
||||||
const filtered = tokens.filter((token) => token.type === type);
|
|
||||||
return (filtered.length > 0) ? filtered[0].text : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if a Micromark token has an htmlFlow-type parent.
|
|
||||||
*
|
|
||||||
* @param {Token} token Micromark token.
|
|
||||||
* @returns {boolean} True iff the token has an htmlFlow-type parent.
|
|
||||||
*/
|
|
||||||
function inHtmlFlow(token) {
|
|
||||||
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines a list of Micromark tokens matches and returns a subset.
|
|
||||||
*
|
|
||||||
* @param {Token[]} tokens Micromark tokens.
|
|
||||||
* @param {TokenType[]} matchTypes Types to match.
|
|
||||||
* @param {TokenType[]} [resultTypes] Types to return.
|
|
||||||
* @returns {Token[] | null} Matching tokens.
|
|
||||||
*/
|
|
||||||
function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
|
|
||||||
if (tokens.length !== matchTypes.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
resultTypes = resultTypes || matchTypes;
|
|
||||||
const result = [];
|
|
||||||
// eslint-disable-next-line unicorn/no-for-loop
|
|
||||||
for (let i = 0; i < matchTypes.length; i++) {
|
|
||||||
if (tokens[i].type !== matchTypes[i]) {
|
|
||||||
return null;
|
|
||||||
} else if (resultTypes.includes(matchTypes[i])) {
|
|
||||||
result.push(tokens[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the specified token iff it is of the desired type.
|
|
||||||
*
|
|
||||||
* @param {Token} token Micromark token candidate.
|
|
||||||
* @param {TokenType} type Desired type.
|
|
||||||
* @returns {Token | null} Token instance.
|
|
||||||
*/
|
|
||||||
function tokenIfType(token, type) {
|
|
||||||
return (token && (token.type === type)) ? token : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set containing token types that do not contain content.
|
|
||||||
*
|
|
||||||
* @type {Set<TokenType>}
|
|
||||||
*/
|
|
||||||
const nonContentTokens = new Set([
|
|
||||||
"blockQuoteMarker",
|
|
||||||
"blockQuotePrefix",
|
|
||||||
"blockQuotePrefixWhitespace",
|
|
||||||
"lineEnding",
|
|
||||||
"lineEndingBlank",
|
|
||||||
"linePrefix",
|
|
||||||
"listItemIndent"
|
|
||||||
]);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
"parse": micromarkParse,
|
|
||||||
filterByPredicate,
|
|
||||||
filterByTypes,
|
|
||||||
getHeadingLevel,
|
|
||||||
getHtmlTagInfo,
|
|
||||||
getMicromarkEvents,
|
|
||||||
getTokenParentOfType,
|
|
||||||
getTokenTextByType,
|
|
||||||
inHtmlFlow,
|
|
||||||
isHtmlFlowComment,
|
|
||||||
matchAndGetTokensByType,
|
|
||||||
nonContentTokens,
|
|
||||||
tokenIfType
|
|
||||||
};
|
|
|
@ -1,9 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "markdownlint-rule-helpers",
|
"name": "markdownlint-rule-helpers",
|
||||||
"version": "0.25.0",
|
"version": "0.29.0",
|
||||||
"description": "A collection of markdownlint helper functions for custom rules",
|
"description": "A collection of markdownlint helper functions for custom rules",
|
||||||
"main": "./helpers.js",
|
"main": "./helpers.cjs",
|
||||||
"exports": "./helpers.js",
|
"exports": {
|
||||||
|
".": "./helpers.cjs",
|
||||||
|
"./micromark": "./micromark-helpers.cjs"
|
||||||
|
},
|
||||||
"author": "David Anson (https://dlaa.me/)",
|
"author": "David Anson (https://dlaa.me/)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/DavidAnson/markdownlint",
|
"homepage": "https://github.com/DavidAnson/markdownlint",
|
||||||
|
@ -16,9 +19,6 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"markdownlint-micromark": "0.1.2"
|
|
||||||
},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"markdownlint",
|
"markdownlint",
|
||||||
"markdownlint-rule"
|
"markdownlint-rule"
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// Symbol for identifing the flat tokens array from micromark parse
|
||||||
|
module.exports.flatTokensSymbol = Symbol("flat-tokens");
|
||||||
|
|
||||||
|
// Symbol for identifying the htmlFlow token from micromark parse
|
||||||
|
module.exports.htmlFlowSymbol = Symbol("html-flow");
|
||||||
|
|
||||||
// Regular expression for matching common newline characters
|
// Regular expression for matching common newline characters
|
||||||
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
|
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
|
||||||
module.exports.newLineRe = /\r\n?|\n/g;
|
module.exports.newLineRe = /\r\n?|\n/g;
|
28
helpers/test.cjs
Normal file
28
helpers/test.cjs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// eslint-disable-next-line n/no-extraneous-require
|
||||||
|
const test = require("ava").default;
|
||||||
|
const { "exports": packageExports, name } = require("../helpers/package.json");
|
||||||
|
|
||||||
|
const exportMappings = new Map([
|
||||||
|
[ ".", "../helpers/helpers.cjs" ],
|
||||||
|
[ "./micromark", "../helpers/micromark-helpers.cjs" ]
|
||||||
|
]);
|
||||||
|
|
||||||
|
test("exportMappings", (t) => {
|
||||||
|
t.deepEqual(
|
||||||
|
Object.keys(packageExports),
|
||||||
|
[ ...exportMappings.keys() ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [ exportName, exportPath ] of exportMappings) {
|
||||||
|
test(exportName, (t) => {
|
||||||
|
t.is(
|
||||||
|
require(exportName.replace(/^\./u, name)),
|
||||||
|
require(exportPath)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
21
lib/cache.js
21
lib/cache.js
|
@ -1,21 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const map = new Map();
|
|
||||||
|
|
||||||
module.exports.set = (keyValuePairs) => {
|
|
||||||
for (const [ key, value ] of Object.entries(keyValuePairs)) {
|
|
||||||
map.set(key, value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
module.exports.clear = () => map.clear();
|
|
||||||
|
|
||||||
module.exports.codeBlockAndSpanRanges =
|
|
||||||
() => map.get("codeBlockAndSpanRanges");
|
|
||||||
module.exports.flattenedLists =
|
|
||||||
() => map.get("flattenedLists");
|
|
||||||
module.exports.lineMetadata =
|
|
||||||
() => map.get("lineMetadata");
|
|
||||||
module.exports.referenceLinkImageData =
|
|
||||||
() => map.get("referenceLinkImageData");
|
|
76
lib/cache.mjs
Normal file
76
lib/cache.mjs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import { getReferenceLinkImageData as helpersGetReferenceLinkImageData } from "../helpers/helpers.cjs";
|
||||||
|
import { filterByTypes } from "../helpers/micromark-helpers.cjs";
|
||||||
|
|
||||||
|
/** @typedef {import("markdownlint").RuleParams} RuleParams */
|
||||||
|
/** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */
|
||||||
|
/** @typedef {import("markdownlint").MicromarkTokenType} MicromarkTokenType */
|
||||||
|
|
||||||
|
/** @type {Map<string, object>} */
|
||||||
|
const map = new Map();
|
||||||
|
/** @type {RuleParams | undefined} */
|
||||||
|
let params = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes (resets) the cache.
|
||||||
|
*
|
||||||
|
* @param {RuleParams} [p] Rule parameters object.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function initialize(p) {
|
||||||
|
map.clear();
|
||||||
|
params = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cached Micromark token array (for testing).
|
||||||
|
*
|
||||||
|
* @returns {MicromarkToken[]} Micromark tokens.
|
||||||
|
*/
|
||||||
|
export function micromarkTokens() {
|
||||||
|
return params?.parsers.micromark.tokens || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a cached object value - computes it and caches it.
|
||||||
|
*
|
||||||
|
* @param {string} name Cache object name.
|
||||||
|
* @param {Function} getValue Getter for object value.
|
||||||
|
* @returns {Object} Object value.
|
||||||
|
*/
|
||||||
|
function getCached(name, getValue) {
|
||||||
|
if (map.has(name)) {
|
||||||
|
return map.get(name);
|
||||||
|
}
|
||||||
|
const value = getValue();
|
||||||
|
map.set(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters a list of Micromark tokens by type and caches the result.
|
||||||
|
*
|
||||||
|
* @param {MicromarkTokenType[]} types Types to allow.
|
||||||
|
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
|
||||||
|
* @returns {MicromarkToken[]} Filtered tokens.
|
||||||
|
*/
|
||||||
|
export function filterByTypesCached(types, htmlFlow) {
|
||||||
|
return getCached(
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
JSON.stringify(arguments),
|
||||||
|
() => filterByTypes(micromarkTokens(), types, htmlFlow)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a reference link and image data object.
|
||||||
|
*
|
||||||
|
* @returns {Object} Reference link and image data object.
|
||||||
|
*/
|
||||||
|
export function getReferenceLinkImageData() {
|
||||||
|
return getCached(
|
||||||
|
getReferenceLinkImageData.name,
|
||||||
|
() => helpersGetReferenceLinkImageData(micromarkTokens())
|
||||||
|
);
|
||||||
|
}
|
1263
lib/configuration-strict.d.ts
vendored
Normal file
1263
lib/configuration-strict.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
1176
lib/configuration.d.ts
vendored
1176
lib/configuration.d.ts
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,14 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
module.exports.deprecatedRuleNames = [];
|
|
||||||
module.exports.fixableRuleNames = [
|
|
||||||
"MD004", "MD005", "MD007", "MD009", "MD010", "MD011",
|
|
||||||
"MD012", "MD014", "MD018", "MD019", "MD020", "MD021",
|
|
||||||
"MD022", "MD023", "MD026", "MD027", "MD030", "MD031",
|
|
||||||
"MD032", "MD034", "MD037", "MD038", "MD039", "MD044",
|
|
||||||
"MD047", "MD049", "MD050", "MD051", "MD053", "MD054"
|
|
||||||
];
|
|
||||||
module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
|
|
||||||
module.exports.version = "0.34.0";
|
|
13
lib/constants.mjs
Normal file
13
lib/constants.mjs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export const deprecatedRuleNames = [];
|
||||||
|
export const fixableRuleNames = [
|
||||||
|
"MD004", "MD005", "MD007", "MD009", "MD010", "MD011",
|
||||||
|
"MD012", "MD014", "MD018", "MD019", "MD020", "MD021",
|
||||||
|
"MD022", "MD023", "MD026", "MD027", "MD030", "MD031",
|
||||||
|
"MD032", "MD034", "MD037", "MD038", "MD039", "MD044",
|
||||||
|
"MD047", "MD049", "MD050", "MD051", "MD053", "MD054",
|
||||||
|
"MD058"
|
||||||
|
];
|
||||||
|
export const homepage = "https://github.com/DavidAnson/markdownlint";
|
||||||
|
export const version = "0.38.0";
|
16
lib/defer-require.cjs
Normal file
16
lib/defer-require.cjs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls require for markdownit.cjs. Used to synchronously defer loading because module.createRequire is buggy under webpack (https://github.com/webpack/webpack/issues/16724).
|
||||||
|
*
|
||||||
|
* @returns {any} Exported module content.
|
||||||
|
*/
|
||||||
|
function requireMarkdownItCjs() {
|
||||||
|
return require("./markdownit.cjs");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
requireMarkdownItCjs
|
||||||
|
};
|
1
lib/exports-async.d.mts
Normal file
1
lib/exports-async.d.mts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { lintAsync as lint, readConfigAsync as readConfig } from "./markdownlint.mjs";
|
3
lib/exports-async.mjs
Normal file
3
lib/exports-async.mjs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { lintAsync as lint, readConfigAsync as readConfig } from "./markdownlint.mjs";
|
1
lib/exports-promise.d.mts
Normal file
1
lib/exports-promise.d.mts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { extendConfigPromise as extendConfig, lintPromise as lint, readConfigPromise as readConfig } from "./markdownlint.mjs";
|
3
lib/exports-promise.mjs
Normal file
3
lib/exports-promise.mjs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { extendConfigPromise as extendConfig, lintPromise as lint, readConfigPromise as readConfig } from "./markdownlint.mjs";
|
1
lib/exports-sync.d.mts
Normal file
1
lib/exports-sync.d.mts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { lintSync as lint, readConfigSync as readConfig } from "./markdownlint.mjs";
|
3
lib/exports-sync.mjs
Normal file
3
lib/exports-sync.mjs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { lintSync as lint, readConfigSync as readConfig } from "./markdownlint.mjs";
|
30
lib/exports.d.mts
Normal file
30
lib/exports.d.mts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
export { resolveModule } from "./resolve-module.cjs";
|
||||||
|
export type Configuration = import("./markdownlint.mjs").Configuration;
|
||||||
|
export type ConfigurationParser = import("./markdownlint.mjs").ConfigurationParser;
|
||||||
|
export type ConfigurationStrict = import("./markdownlint.mjs").ConfigurationStrict;
|
||||||
|
export type FixInfo = import("./markdownlint.mjs").FixInfo;
|
||||||
|
export type LintCallback = import("./markdownlint.mjs").LintCallback;
|
||||||
|
export type LintContentCallback = import("./markdownlint.mjs").LintContentCallback;
|
||||||
|
export type LintError = import("./markdownlint.mjs").LintError;
|
||||||
|
export type LintResults = import("./markdownlint.mjs").LintResults;
|
||||||
|
export type MarkdownItFactory = import("./markdownlint.mjs").MarkdownItFactory;
|
||||||
|
export type MarkdownItToken = import("./markdownlint.mjs").MarkdownItToken;
|
||||||
|
export type MarkdownParsers = import("./markdownlint.mjs").MarkdownParsers;
|
||||||
|
export type MicromarkToken = import("./markdownlint.mjs").MicromarkToken;
|
||||||
|
export type MicromarkTokenType = import("./markdownlint.mjs").MicromarkTokenType;
|
||||||
|
export type Options = import("./markdownlint.mjs").Options;
|
||||||
|
export type ParserMarkdownIt = import("./markdownlint.mjs").ParserMarkdownIt;
|
||||||
|
export type ParserMicromark = import("./markdownlint.mjs").ParserMicromark;
|
||||||
|
export type Plugin = import("./markdownlint.mjs").Plugin;
|
||||||
|
export type ReadConfigCallback = import("./markdownlint.mjs").ReadConfigCallback;
|
||||||
|
export type ResolveConfigExtendsCallback = import("./markdownlint.mjs").ResolveConfigExtendsCallback;
|
||||||
|
export type Rule = import("./markdownlint.mjs").Rule;
|
||||||
|
export type RuleConfiguration = import("./markdownlint.mjs").RuleConfiguration;
|
||||||
|
export type RuleFunction = import("./markdownlint.mjs").RuleFunction;
|
||||||
|
export type RuleOnError = import("./markdownlint.mjs").RuleOnError;
|
||||||
|
export type RuleOnErrorFixInfo = import("./markdownlint.mjs").RuleOnErrorFixInfo;
|
||||||
|
export type RuleOnErrorFixInfoNormalized = import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized;
|
||||||
|
export type RuleOnErrorInfo = import("./markdownlint.mjs").RuleOnErrorInfo;
|
||||||
|
export type RuleParams = import("./markdownlint.mjs").RuleParams;
|
||||||
|
export type ToStringCallback = import("./markdownlint.mjs").ToStringCallback;
|
||||||
|
export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs";
|
33
lib/exports.mjs
Normal file
33
lib/exports.mjs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs";
|
||||||
|
export { resolveModule } from "./resolve-module.cjs";
|
||||||
|
|
||||||
|
/** @typedef {import("./markdownlint.mjs").Configuration} Configuration */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ConfigurationParser} ConfigurationParser */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ConfigurationStrict} ConfigurationStrict */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").FixInfo} FixInfo */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").LintCallback} LintCallback */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").LintContentCallback} LintContentCallback */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").LintError} LintError */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").LintResults} LintResults */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").MarkdownItFactory} MarkdownItFactory */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").MarkdownItToken} MarkdownItToken */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").MarkdownParsers} MarkdownParsers */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").MicromarkToken} MicromarkToken */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").MicromarkTokenType} MicromarkTokenType */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").Options} Options */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ParserMarkdownIt} ParserMarkdownIt */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ParserMicromark} ParserMicromark */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").Plugin} Plugin */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ReadConfigCallback} ReadConfigCallback */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ResolveConfigExtendsCallback} ResolveConfigExtendsCallback */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").Rule} Rule */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleConfiguration} RuleConfiguration */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleFunction} RuleFunction */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleOnError} RuleOnError */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfo} RuleOnErrorFixInfo */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized} RuleOnErrorFixInfoNormalized */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleOnErrorInfo} RuleOnErrorInfo */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").RuleParams} RuleParams */
|
||||||
|
/** @typedef {import("./markdownlint.mjs").ToStringCallback} ToStringCallback */
|
169
lib/markdownit.cjs
Normal file
169
lib/markdownit.cjs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { newLineRe } = require("../helpers");
|
||||||
|
|
||||||
|
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/52529
|
||||||
|
/** @typedef {import("markdownlint").MarkdownIt} MarkdownIt */
|
||||||
|
/** @typedef {import("markdownlint").MarkdownItToken} MarkdownItToken */
|
||||||
|
/** @typedef {import("markdownlint").Plugin} Plugin */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback InlineCodeSpanCallback
|
||||||
|
* @param {string} code Code content.
|
||||||
|
* @param {number} lineIndex Line index (0-based).
|
||||||
|
* @param {number} columnIndex Column index (0-based).
|
||||||
|
* @param {number} ticks Count of backticks.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the provided function for each inline code span's content.
|
||||||
|
*
|
||||||
|
* @param {string} input Markdown content.
|
||||||
|
* @param {InlineCodeSpanCallback} handler Callback function taking (code,
|
||||||
|
* lineIndex, columnIndex, ticks).
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function forEachInlineCodeSpan(input, handler) {
|
||||||
|
const backtickRe = /`+/g;
|
||||||
|
let match = null;
|
||||||
|
const backticksLengthAndIndex = [];
|
||||||
|
while ((match = backtickRe.exec(input)) !== null) {
|
||||||
|
backticksLengthAndIndex.push([ match[0].length, match.index ]);
|
||||||
|
}
|
||||||
|
const newLinesIndex = [];
|
||||||
|
while ((match = newLineRe.exec(input)) !== null) {
|
||||||
|
newLinesIndex.push(match.index);
|
||||||
|
}
|
||||||
|
let lineIndex = 0;
|
||||||
|
let lineStartIndex = 0;
|
||||||
|
let k = 0;
|
||||||
|
for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
|
||||||
|
const [ startLength, startIndex ] = backticksLengthAndIndex[i];
|
||||||
|
if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
|
||||||
|
for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
|
||||||
|
const [ endLength, endIndex ] = backticksLengthAndIndex[j];
|
||||||
|
if (startLength === endLength) {
|
||||||
|
for (; k < newLinesIndex.length; k++) {
|
||||||
|
const newLineIndex = newLinesIndex[k];
|
||||||
|
if (startIndex < newLineIndex) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lineIndex++;
|
||||||
|
lineStartIndex = newLineIndex + 1;
|
||||||
|
}
|
||||||
|
const columnIndex = startIndex - lineStartIndex + startLength;
|
||||||
|
handler(
|
||||||
|
input.slice(startIndex + startLength, endIndex),
|
||||||
|
lineIndex,
|
||||||
|
columnIndex,
|
||||||
|
startLength
|
||||||
|
);
|
||||||
|
i = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Freeze all freeze-able members of a token and its children.
|
||||||
|
*
|
||||||
|
* @param {MarkdownItToken} token A markdown-it token.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function freezeToken(token) {
|
||||||
|
if (token.attrs) {
|
||||||
|
for (const attr of token.attrs) {
|
||||||
|
Object.freeze(attr);
|
||||||
|
}
|
||||||
|
Object.freeze(token.attrs);
|
||||||
|
}
|
||||||
|
if (token.children) {
|
||||||
|
for (const child of token.children) {
|
||||||
|
freezeToken(child);
|
||||||
|
}
|
||||||
|
Object.freeze(token.children);
|
||||||
|
}
|
||||||
|
if (token.map) {
|
||||||
|
Object.freeze(token.map);
|
||||||
|
}
|
||||||
|
Object.freeze(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotate tokens with line/lineNumber and freeze them.
|
||||||
|
*
|
||||||
|
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
|
||||||
|
* @param {string[]} lines Lines of Markdown content.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function annotateAndFreezeTokens(tokens, lines) {
|
||||||
|
let trMap = null;
|
||||||
|
/** @type {MarkdownItToken[]} */
|
||||||
|
// @ts-ignore
|
||||||
|
const markdownItTokens = tokens;
|
||||||
|
for (const token of markdownItTokens) {
|
||||||
|
// Provide missing maps for table content
|
||||||
|
if (token.type === "tr_open") {
|
||||||
|
trMap = token.map;
|
||||||
|
} else if (token.type === "tr_close") {
|
||||||
|
trMap = null;
|
||||||
|
}
|
||||||
|
if (!token.map && trMap) {
|
||||||
|
token.map = [ ...trMap ];
|
||||||
|
}
|
||||||
|
// Update token metadata
|
||||||
|
if (token.map) {
|
||||||
|
token.line = lines[token.map[0]];
|
||||||
|
token.lineNumber = token.map[0] + 1;
|
||||||
|
// Trim bottom of token to exclude whitespace lines
|
||||||
|
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
||||||
|
token.map[1]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Annotate children with lineNumber
|
||||||
|
if (token.children) {
|
||||||
|
const codeSpanExtraLines = [];
|
||||||
|
if (token.children.some((child) => child.type === "code_inline")) {
|
||||||
|
forEachInlineCodeSpan(token.content, (code) => {
|
||||||
|
codeSpanExtraLines.push(code.split(newLineRe).length - 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let lineNumber = token.lineNumber;
|
||||||
|
for (const child of token.children) {
|
||||||
|
child.lineNumber = lineNumber;
|
||||||
|
child.line = lines[lineNumber - 1];
|
||||||
|
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
||||||
|
lineNumber++;
|
||||||
|
} else if (child.type === "code_inline") {
|
||||||
|
lineNumber += codeSpanExtraLines.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freezeToken(token);
|
||||||
|
}
|
||||||
|
Object.freeze(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of markdown-it tokens for the input.
|
||||||
|
*
|
||||||
|
* @param {MarkdownIt} markdownIt Instance of the markdown-it parser.
|
||||||
|
* @param {string} content Markdown content.
|
||||||
|
* @param {string[]} lines Lines of Markdown content.
|
||||||
|
* @returns {MarkdownItToken[]} Array of markdown-it tokens.
|
||||||
|
*/
|
||||||
|
function getMarkdownItTokens(markdownIt, content, lines) {
|
||||||
|
const tokens = markdownIt.parse(content, {});
|
||||||
|
annotateAndFreezeTokens(tokens, lines);
|
||||||
|
return tokens;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
forEachInlineCodeSpan,
|
||||||
|
getMarkdownItTokens
|
||||||
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
export = markdownlint;
|
|
||||||
/**
|
/**
|
||||||
* Lint specified Markdown files.
|
* Lint specified Markdown files.
|
||||||
*
|
*
|
||||||
|
@ -6,112 +5,108 @@ export = markdownlint;
|
||||||
* @param {LintCallback} callback Callback (err, result) function.
|
* @param {LintCallback} callback Callback (err, result) function.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
declare function markdownlint(options: Options | null, callback: LintCallback): void;
|
export function lintAsync(options: Options | null, callback: LintCallback): void;
|
||||||
declare namespace markdownlint {
|
|
||||||
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Configuration options.
|
* Lint specified Markdown files.
|
||||||
|
*
|
||||||
|
* @param {Options | null} options Configuration options.
|
||||||
|
* @returns {Promise<LintResults>} Results object.
|
||||||
*/
|
*/
|
||||||
type Options = {
|
export function lintPromise(options: Options | null): Promise<LintResults>;
|
||||||
/**
|
|
||||||
* Configuration object.
|
|
||||||
*/
|
|
||||||
config?: Configuration;
|
|
||||||
/**
|
|
||||||
* Configuration parsers.
|
|
||||||
*/
|
|
||||||
configParsers?: ConfigurationParser[];
|
|
||||||
/**
|
|
||||||
* Custom rules.
|
|
||||||
*/
|
|
||||||
customRules?: Rule[] | Rule;
|
|
||||||
/**
|
|
||||||
* Files to lint.
|
|
||||||
*/
|
|
||||||
files?: string[] | string;
|
|
||||||
/**
|
|
||||||
* Front matter pattern.
|
|
||||||
*/
|
|
||||||
frontMatter?: RegExp | null;
|
|
||||||
/**
|
|
||||||
* File system implementation.
|
|
||||||
*/
|
|
||||||
fs?: any;
|
|
||||||
/**
|
|
||||||
* True to catch exceptions.
|
|
||||||
*/
|
|
||||||
handleRuleFailures?: boolean;
|
|
||||||
/**
|
|
||||||
* Additional plugins.
|
|
||||||
*/
|
|
||||||
markdownItPlugins?: Plugin[];
|
|
||||||
/**
|
|
||||||
* True to ignore HTML directives.
|
|
||||||
*/
|
|
||||||
noInlineConfig?: boolean;
|
|
||||||
/**
|
|
||||||
* Results object version.
|
|
||||||
*/
|
|
||||||
resultVersion?: number;
|
|
||||||
/**
|
|
||||||
* Strings to lint.
|
|
||||||
*/
|
|
||||||
strings?: {
|
|
||||||
[x: string]: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* Called with the result of the lint function.
|
* Lint specified Markdown files.
|
||||||
*/
|
|
||||||
type LintCallback = (error: Error | null, results?: LintResults) => void;
|
|
||||||
/**
|
|
||||||
* Lint specified Markdown files synchronously.
|
|
||||||
*
|
*
|
||||||
* @param {Options | null} options Configuration options.
|
* @param {Options | null} options Configuration options.
|
||||||
* @returns {LintResults} Results object.
|
* @returns {LintResults} Results object.
|
||||||
*/
|
*/
|
||||||
declare function markdownlintSync(options: Options | null): LintResults;
|
export function lintSync(options: Options | null): LintResults;
|
||||||
|
/**
|
||||||
|
* Extend specified configuration object.
|
||||||
|
*
|
||||||
|
* @param {Configuration} config Configuration object.
|
||||||
|
* @param {string} file Configuration file name.
|
||||||
|
* @param {ConfigurationParser[] | undefined} parsers Parsing function(s).
|
||||||
|
* @param {Object} fs File system implementation.
|
||||||
|
* @returns {Promise<Configuration>} Configuration object.
|
||||||
|
*/
|
||||||
|
export function extendConfigPromise(config: Configuration, file: string, parsers: ConfigurationParser[] | undefined, fs: any): Promise<Configuration>;
|
||||||
/**
|
/**
|
||||||
* Read specified configuration file.
|
* Read specified configuration file.
|
||||||
*
|
*
|
||||||
* @param {string} file Configuration file name.
|
* @param {string} file Configuration file name.
|
||||||
* @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
|
* @param {ConfigurationParser[] | ReadConfigCallback} [parsers] Parsing
|
||||||
* function(s).
|
* function(s).
|
||||||
* @param {Object} [fs] File system implementation.
|
* @param {Object} [fs] File system implementation.
|
||||||
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
|
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, fs?: any, callback?: ReadConfigCallback): void;
|
export function readConfigAsync(file: string, parsers?: ConfigurationParser[] | ReadConfigCallback, fs?: any, callback?: ReadConfigCallback): void;
|
||||||
/**
|
/**
|
||||||
* Read specified configuration file synchronously.
|
* Read specified configuration file.
|
||||||
|
*
|
||||||
|
* @param {string} file Configuration file name.
|
||||||
|
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
||||||
|
* @param {Object} [fs] File system implementation.
|
||||||
|
* @returns {Promise<Configuration>} Configuration object.
|
||||||
|
*/
|
||||||
|
export function readConfigPromise(file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;
|
||||||
|
/**
|
||||||
|
* Read specified configuration file.
|
||||||
*
|
*
|
||||||
* @param {string} file Configuration file name.
|
* @param {string} file Configuration file name.
|
||||||
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
||||||
* @param {Object} [fs] File system implementation.
|
* @param {Object} [fs] File system implementation.
|
||||||
* @returns {Configuration} Configuration object.
|
* @returns {Configuration} Configuration object.
|
||||||
* @throws An Error if processing fails.
|
|
||||||
*/
|
*/
|
||||||
declare function readConfigSync(file: string, parsers?: ConfigurationParser[], fs?: any): Configuration;
|
export function readConfigSync(file: string, parsers?: ConfigurationParser[], fs?: any): Configuration;
|
||||||
|
/**
|
||||||
|
* Applies the specified fix to a Markdown content line.
|
||||||
|
*
|
||||||
|
* @param {string} line Line of Markdown content.
|
||||||
|
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance.
|
||||||
|
* @param {string} [lineEnding] Line ending to use.
|
||||||
|
* @returns {string | null} Fixed content or null if deleted.
|
||||||
|
*/
|
||||||
|
export function applyFix(line: string, fixInfo: RuleOnErrorFixInfo, lineEnding?: string): string | null;
|
||||||
|
/**
|
||||||
|
* Applies as many of the specified fixes as possible to Markdown content.
|
||||||
|
*
|
||||||
|
* @param {string} input Lines of Markdown content.
|
||||||
|
* @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances.
|
||||||
|
* @returns {string} Fixed content.
|
||||||
|
*/
|
||||||
|
export function applyFixes(input: string, errors: RuleOnErrorInfo[]): string;
|
||||||
/**
|
/**
|
||||||
* Gets the (semantic) version of the library.
|
* Gets the (semantic) version of the library.
|
||||||
*
|
*
|
||||||
* @returns {string} SemVer string.
|
* @returns {string} SemVer string.
|
||||||
*/
|
*/
|
||||||
declare function getVersion(): string;
|
export function getVersion(): string;
|
||||||
declare namespace promises {
|
/**
|
||||||
export { markdownlintPromise as markdownlint };
|
* Result object for getEnabledRulesPerLineNumber.
|
||||||
export { extendConfigPromise as extendConfig };
|
*/
|
||||||
export { readConfigPromise as readConfig };
|
export type EnabledRulesPerLineNumberResult = {
|
||||||
}
|
/**
|
||||||
|
* Effective configuration.
|
||||||
|
*/
|
||||||
|
effectiveConfig: Configuration;
|
||||||
|
/**
|
||||||
|
* Enabled rules per line number.
|
||||||
|
*/
|
||||||
|
enabledRulesPerLineNumber: any[];
|
||||||
|
/**
|
||||||
|
* Enabled rule list.
|
||||||
|
*/
|
||||||
|
enabledRuleList: Rule[];
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Function to implement rule logic.
|
* Function to implement rule logic.
|
||||||
*/
|
*/
|
||||||
type RuleFunction = (params: RuleParams, onError: RuleOnError) => void;
|
export type RuleFunction = (params: RuleParams, onError: RuleOnError) => void;
|
||||||
/**
|
/**
|
||||||
* Rule parameters.
|
* Rule parameters.
|
||||||
*/
|
*/
|
||||||
type RuleParams = {
|
export type RuleParams = {
|
||||||
/**
|
/**
|
||||||
* File/string name.
|
* File/string name.
|
||||||
*/
|
*/
|
||||||
|
@ -132,11 +127,15 @@ type RuleParams = {
|
||||||
* Rule configuration.
|
* Rule configuration.
|
||||||
*/
|
*/
|
||||||
config: RuleConfiguration;
|
config: RuleConfiguration;
|
||||||
|
/**
|
||||||
|
* Version of the markdownlint library.
|
||||||
|
*/
|
||||||
|
version: string;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Markdown parser data.
|
* Markdown parser data.
|
||||||
*/
|
*/
|
||||||
type MarkdownParsers = {
|
export type MarkdownParsers = {
|
||||||
/**
|
/**
|
||||||
* Markdown parser data from markdown-it (only present when Rule.parser is "markdownit").
|
* Markdown parser data from markdown-it (only present when Rule.parser is "markdownit").
|
||||||
*/
|
*/
|
||||||
|
@ -149,7 +148,7 @@ type MarkdownParsers = {
|
||||||
/**
|
/**
|
||||||
* Markdown parser data from markdown-it.
|
* Markdown parser data from markdown-it.
|
||||||
*/
|
*/
|
||||||
type ParserMarkdownIt = {
|
export type ParserMarkdownIt = {
|
||||||
/**
|
/**
|
||||||
* Token objects from markdown-it.
|
* Token objects from markdown-it.
|
||||||
*/
|
*/
|
||||||
|
@ -158,7 +157,7 @@ type ParserMarkdownIt = {
|
||||||
/**
|
/**
|
||||||
* Markdown parser data from micromark.
|
* Markdown parser data from micromark.
|
||||||
*/
|
*/
|
||||||
type ParserMicromark = {
|
export type ParserMicromark = {
|
||||||
/**
|
/**
|
||||||
* Token objects from micromark.
|
* Token objects from micromark.
|
||||||
*/
|
*/
|
||||||
|
@ -167,7 +166,7 @@ type ParserMicromark = {
|
||||||
/**
|
/**
|
||||||
* markdown-it token.
|
* markdown-it token.
|
||||||
*/
|
*/
|
||||||
type MarkdownItToken = {
|
export type MarkdownItToken = {
|
||||||
/**
|
/**
|
||||||
* HTML attributes.
|
* HTML attributes.
|
||||||
*/
|
*/
|
||||||
|
@ -229,11 +228,11 @@ type MarkdownItToken = {
|
||||||
*/
|
*/
|
||||||
line: string;
|
line: string;
|
||||||
};
|
};
|
||||||
type MicromarkTokenType = import("markdownlint-micromark").TokenType;
|
export type MicromarkTokenType = import("micromark-util-types").TokenType;
|
||||||
/**
|
/**
|
||||||
* micromark token.
|
* micromark token.
|
||||||
*/
|
*/
|
||||||
type MicromarkToken = {
|
export type MicromarkToken = {
|
||||||
/**
|
/**
|
||||||
* Token type.
|
* Token type.
|
||||||
*/
|
*/
|
||||||
|
@ -270,11 +269,11 @@ type MicromarkToken = {
|
||||||
/**
|
/**
|
||||||
* Error-reporting callback.
|
* Error-reporting callback.
|
||||||
*/
|
*/
|
||||||
type RuleOnError = (onErrorInfo: RuleOnErrorInfo) => void;
|
export type RuleOnError = (onErrorInfo: RuleOnErrorInfo) => void;
|
||||||
/**
|
/**
|
||||||
* Fix information for RuleOnError callback.
|
* Fix information for RuleOnError callback.
|
||||||
*/
|
*/
|
||||||
type RuleOnErrorInfo = {
|
export type RuleOnErrorInfo = {
|
||||||
/**
|
/**
|
||||||
* Line number (1-based).
|
* Line number (1-based).
|
||||||
*/
|
*/
|
||||||
|
@ -303,7 +302,7 @@ type RuleOnErrorInfo = {
|
||||||
/**
|
/**
|
||||||
* Fix information for RuleOnErrorInfo.
|
* Fix information for RuleOnErrorInfo.
|
||||||
*/
|
*/
|
||||||
type RuleOnErrorFixInfo = {
|
export type RuleOnErrorFixInfo = {
|
||||||
/**
|
/**
|
||||||
* Line number (1-based).
|
* Line number (1-based).
|
||||||
*/
|
*/
|
||||||
|
@ -321,10 +320,31 @@ type RuleOnErrorFixInfo = {
|
||||||
*/
|
*/
|
||||||
insertText?: string;
|
insertText?: string;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* RuleOnErrorInfo with all optional properties present.
|
||||||
|
*/
|
||||||
|
export type RuleOnErrorFixInfoNormalized = {
|
||||||
|
/**
|
||||||
|
* Line number (1-based).
|
||||||
|
*/
|
||||||
|
lineNumber: number;
|
||||||
|
/**
|
||||||
|
* Column of the fix (1-based).
|
||||||
|
*/
|
||||||
|
editColumn: number;
|
||||||
|
/**
|
||||||
|
* Count of characters to delete.
|
||||||
|
*/
|
||||||
|
deleteCount: number;
|
||||||
|
/**
|
||||||
|
* Text to insert (after deleting).
|
||||||
|
*/
|
||||||
|
insertText: string;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Rule definition.
|
* Rule definition.
|
||||||
*/
|
*/
|
||||||
type Rule = {
|
export type Rule = {
|
||||||
/**
|
/**
|
||||||
* Rule name(s).
|
* Rule name(s).
|
||||||
*/
|
*/
|
||||||
|
@ -354,24 +374,92 @@ type Rule = {
|
||||||
*/
|
*/
|
||||||
function: RuleFunction;
|
function: RuleFunction;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Method used by the markdown-it parser to parse input.
|
||||||
|
*/
|
||||||
|
export type MarkdownItParse = (src: string, env: any) => any[];
|
||||||
|
/**
|
||||||
|
* Instance of the markdown-it parser.
|
||||||
|
*/
|
||||||
|
export type MarkdownIt = {
|
||||||
|
/**
|
||||||
|
* Method to parse input.
|
||||||
|
*/
|
||||||
|
parse: MarkdownItParse;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Gets an instance of the markdown-it parser. Any plugins should already have been loaded.
|
||||||
|
*/
|
||||||
|
export type MarkdownItFactory = () => MarkdownIt | Promise<MarkdownIt>;
|
||||||
|
/**
|
||||||
|
* Configuration options.
|
||||||
|
*/
|
||||||
|
export type Options = {
|
||||||
|
/**
|
||||||
|
* Configuration object.
|
||||||
|
*/
|
||||||
|
config?: Configuration;
|
||||||
|
/**
|
||||||
|
* Configuration parsers.
|
||||||
|
*/
|
||||||
|
configParsers?: ConfigurationParser[];
|
||||||
|
/**
|
||||||
|
* Custom rules.
|
||||||
|
*/
|
||||||
|
customRules?: Rule[] | Rule;
|
||||||
|
/**
|
||||||
|
* Files to lint.
|
||||||
|
*/
|
||||||
|
files?: string[] | string;
|
||||||
|
/**
|
||||||
|
* Front matter pattern.
|
||||||
|
*/
|
||||||
|
frontMatter?: RegExp | null;
|
||||||
|
/**
|
||||||
|
* File system implementation.
|
||||||
|
*/
|
||||||
|
fs?: any;
|
||||||
|
/**
|
||||||
|
* True to catch exceptions.
|
||||||
|
*/
|
||||||
|
handleRuleFailures?: boolean;
|
||||||
|
/**
|
||||||
|
* Function to create a markdown-it parser.
|
||||||
|
*/
|
||||||
|
markdownItFactory?: MarkdownItFactory;
|
||||||
|
/**
|
||||||
|
* True to ignore HTML directives.
|
||||||
|
*/
|
||||||
|
noInlineConfig?: boolean;
|
||||||
|
/**
|
||||||
|
* Results object version.
|
||||||
|
*/
|
||||||
|
resultVersion?: number;
|
||||||
|
/**
|
||||||
|
* Strings to lint.
|
||||||
|
*/
|
||||||
|
strings?: {
|
||||||
|
[x: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* A markdown-it plugin.
|
* A markdown-it plugin.
|
||||||
*/
|
*/
|
||||||
type Plugin = any[];
|
export type Plugin = any[];
|
||||||
/**
|
/**
|
||||||
* Function to pretty-print lint results.
|
* Function to pretty-print lint results.
|
||||||
*/
|
*/
|
||||||
type ToStringCallback = (ruleAliases?: boolean) => string;
|
export type ToStringCallback = (ruleAliases?: boolean) => string;
|
||||||
/**
|
/**
|
||||||
* Lint results (for resultVersion 3).
|
* Lint results (for resultVersion 3).
|
||||||
*/
|
*/
|
||||||
type LintResults = {
|
export type LintResults = {
|
||||||
[x: string]: LintError[];
|
[x: string]: LintError[];
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Lint error.
|
* Lint error.
|
||||||
*/
|
*/
|
||||||
type LintError = {
|
export type LintError = {
|
||||||
/**
|
/**
|
||||||
* Line number (1-based).
|
* Line number (1-based).
|
||||||
*/
|
*/
|
||||||
|
@ -408,7 +496,7 @@ type LintError = {
|
||||||
/**
|
/**
|
||||||
* Fix information.
|
* Fix information.
|
||||||
*/
|
*/
|
||||||
type FixInfo = {
|
export type FixInfo = {
|
||||||
/**
|
/**
|
||||||
* Line number (1-based).
|
* Line number (1-based).
|
||||||
*/
|
*/
|
||||||
|
@ -429,51 +517,34 @@ type FixInfo = {
|
||||||
/**
|
/**
|
||||||
* Called with the result of linting a string or document.
|
* Called with the result of linting a string or document.
|
||||||
*/
|
*/
|
||||||
type LintContentCallback = (error: Error | null, result?: LintError[]) => void;
|
export type LintContentCallback = (error: Error | null, result?: LintError[]) => void;
|
||||||
|
/**
|
||||||
|
* Called with the result of the lint function.
|
||||||
|
*/
|
||||||
|
export type LintCallback = (error: Error | null, results?: LintResults) => void;
|
||||||
/**
|
/**
|
||||||
* Configuration object for linting rules. For the JSON schema, see
|
* Configuration object for linting rules. For the JSON schema, see
|
||||||
* {@link ../schema/markdownlint-config-schema.json}.
|
* {@link ../schema/markdownlint-config-schema.json}.
|
||||||
*/
|
*/
|
||||||
type Configuration = import("./configuration").Configuration;
|
export type Configuration = import("./configuration.d.ts").Configuration;
|
||||||
|
/**
|
||||||
|
* Configuration object for linting rules strictly. For the JSON schema, see
|
||||||
|
* {@link ../schema/markdownlint-config-schema-strict.json}.
|
||||||
|
*/
|
||||||
|
export type ConfigurationStrict = import("./configuration-strict.d.ts").ConfigurationStrict;
|
||||||
/**
|
/**
|
||||||
* Rule configuration.
|
* Rule configuration.
|
||||||
*/
|
*/
|
||||||
type RuleConfiguration = boolean | any;
|
export type RuleConfiguration = boolean | any;
|
||||||
/**
|
/**
|
||||||
* Parses a configuration string and returns a configuration object.
|
* Parses a configuration string and returns a configuration object.
|
||||||
*/
|
*/
|
||||||
type ConfigurationParser = (text: string) => Configuration;
|
export type ConfigurationParser = (text: string) => Configuration;
|
||||||
/**
|
/**
|
||||||
* Called with the result of the readConfig function.
|
* Called with the result of the readConfig function.
|
||||||
*/
|
*/
|
||||||
type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
|
export type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
|
||||||
/**
|
/**
|
||||||
* Called with the result of the resolveConfigExtends function.
|
* Called with the result of the resolveConfigExtends function.
|
||||||
*/
|
*/
|
||||||
type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
|
export type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
|
||||||
/**
|
|
||||||
* Lint specified Markdown files.
|
|
||||||
*
|
|
||||||
* @param {Options} options Configuration options.
|
|
||||||
* @returns {Promise<LintResults>} Results object.
|
|
||||||
*/
|
|
||||||
declare function markdownlintPromise(options: Options): Promise<LintResults>;
|
|
||||||
/**
|
|
||||||
* Extend specified configuration object.
|
|
||||||
*
|
|
||||||
* @param {Configuration} config Configuration object.
|
|
||||||
* @param {string} file Configuration file name.
|
|
||||||
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
|
||||||
* @param {Object} [fs] File system implementation.
|
|
||||||
* @returns {Promise<Configuration>} Configuration object.
|
|
||||||
*/
|
|
||||||
declare function extendConfigPromise(config: Configuration, file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;
|
|
||||||
/**
|
|
||||||
* Read specified configuration file.
|
|
||||||
*
|
|
||||||
* @param {string} file Configuration file name.
|
|
||||||
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
|
||||||
* @param {Object} [fs] File system implementation.
|
|
||||||
* @returns {Promise<Configuration>} Configuration object.
|
|
||||||
*/
|
|
||||||
declare function readConfigPromise(file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue