Skip to content

Commit 1ddcdc7

Browse files
committed
fix: cleaner code + better error messages
1 parent fcd0340 commit 1ddcdc7

File tree

10 files changed

+94
-56
lines changed

10 files changed

+94
-56
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ jobs:
2626
- run: "npm run lint:markdown"
2727
- run: "npm run lint:eslint"
2828
- run: "npm run lint:prettier"
29+
- run: "npm run lint:javascript"

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
. "$(dirname "$0")/_/husky.sh"
33

44
npm run lint:staged
5+
npm run lint:javascript
56
npm run test

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ With `awesome.md` content:
4343
Running [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) with `markdownlint-rule-relative-links` will output:
4444

4545
```sh
46-
awesome.md:3 relative-links Relative links should be valid [Link "./invalid.txt" should exist in the file system]
46+
awesome.md:3 relative-links Relative links should be valid ["./invalid.txt" should exist in the file system]
4747
```
4848

4949
### Additional features

jsconfig.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"compilerOptions": {
3+
"lib": ["ESNext"],
4+
"target": "ESNext",
5+
"module": "CommonJS",
6+
"moduleResolution": "Node",
7+
"checkJs": true,
8+
"allowJs": true,
9+
"noEmit": true,
10+
"rootDir": ".",
11+
"baseUrl": ".",
12+
"strict": true,
13+
"allowUnusedLabels": false,
14+
"allowUnreachableCode": false,
15+
"noFallthroughCasesInSwitch": true,
16+
"noImplicitAny": true,
17+
"noImplicitOverride": true,
18+
"noImplicitReturns": true,
19+
"noUncheckedIndexedAccess": true,
20+
"noUnusedLocals": true,
21+
"noUnusedParameters": true,
22+
"forceConsistentCasingInFileNames": true
23+
}
24+
}

package-lock.json

Lines changed: 25 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"lint:markdown": "markdownlint-cli2",
3636
"lint:eslint": "eslint . --max-warnings 0 --report-unused-disable-directives --ignore-path .gitignore",
3737
"lint:prettier": "prettier . --check --ignore-path .gitignore",
38+
"lint:javascript": "tsc --project jsconfig.json --noEmit",
3839
"lint:staged": "lint-staged",
3940
"test": "node --test ./test",
4041
"release": "semantic-release",
@@ -48,6 +49,7 @@
4849
"devDependencies": {
4950
"@commitlint/cli": "18.4.4",
5051
"@commitlint/config-conventional": "18.4.4",
52+
"@types/markdown-it": "13.0.7",
5153
"@types/node": "20.10.8",
5254
"editorconfig-checker": "5.1.2",
5355
"eslint": "8.56.0",
@@ -63,6 +65,7 @@
6365
"markdownlint-cli2": "0.11.0",
6466
"pinst": "3.0.0",
6567
"prettier": "3.1.1",
66-
"semantic-release": "22.0.12"
68+
"semantic-release": "22.0.12",
69+
"typescript": "5.3.3"
6770
}
6871
}

src/index.js

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@ const fs = require("node:fs")
55

66
const {
77
filterTokens,
8-
addError,
98
convertHeadingToHTMLFragment,
109
getMarkdownHeadings,
1110
} = require("./utils.js")
1211

12+
/** @typedef {import('markdownlint').Rule} MarkdownLintRule */
13+
14+
/**
15+
* @type {MarkdownLintRule}
16+
*/
1317
const customRule = {
1418
names: ["relative-links"],
1519
description: "Relative links should be valid",
1620
tags: ["links"],
1721
function: (params, onError) => {
1822
filterTokens(params, "inline", (token) => {
19-
for (const child of token.children) {
20-
const { lineNumber, type, attrs } = child
23+
const children = token.children ?? []
24+
for (const child of children) {
25+
const { type, attrs, lineNumber } = child
2126

22-
/** @type {string | null} */
23-
let hrefSrc = null
27+
/** @type {string | undefined} */
28+
let hrefSrc
2429

2530
if (type === "link_open") {
2631
for (const attr of attrs) {
@@ -45,14 +50,13 @@ const customRule = {
4550
const isRelative =
4651
url.protocol === "file:" && !hrefSrc.startsWith("/")
4752
if (isRelative) {
48-
const detail = `Link "${hrefSrc}"`
53+
const detail = `"${hrefSrc}"`
4954

5055
if (!fs.existsSync(url)) {
51-
addError(
52-
onError,
56+
onError({
5357
lineNumber,
54-
`${detail} should exist in the file system`,
55-
)
58+
detail: `${detail} should exist in the file system`,
59+
})
5660
continue
5761
}
5862

@@ -74,11 +78,10 @@ const customRule = {
7478
})
7579

7680
if (!headingsHTMLFragments.includes(url.hash)) {
77-
addError(
78-
onError,
81+
onError({
7982
lineNumber,
80-
`${detail} should have a valid fragment`,
81-
)
83+
detail: `${detail} should have a valid fragment identifier`,
84+
})
8285
}
8386
}
8487
}

src/utils.js

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
const MarkdownIt = require("markdown-it")
22

3+
/** @typedef {import('markdownlint').RuleParams} MarkdownLintRuleParams */
4+
/** @typedef {import('markdownlint').MarkdownItToken} MarkdownItToken */
5+
36
/**
47
* Calls the provided function for each matching token.
58
*
6-
* @param {object} params RuleParams instance.
9+
* @param {MarkdownLintRuleParams} params RuleParams instance.
710
* @param {string} type Token type identifier.
8-
* @param {Function} handler Callback function.
11+
* @param {(token: MarkdownItToken) => void} handler Callback function.
912
* @returns {void}
1013
*/
1114
const filterTokens = (params, type, handler) => {
@@ -16,27 +19,6 @@ const filterTokens = (params, type, handler) => {
1619
}
1720
}
1821

19-
/**
20-
* Adds a generic error object via the onError callback.
21-
*
22-
* @param {object} onError RuleOnError instance.
23-
* @param {number} lineNumber Line number.
24-
* @param {string} [detail] Error details.
25-
* @param {string} [context] Error context.
26-
* @param {number[]} [range] Column and length of error.
27-
* @param {object} [fixInfo] RuleOnErrorFixInfo instance.
28-
* @returns {void}
29-
*/
30-
const addError = (onError, lineNumber, detail, context, range, fixInfo) => {
31-
onError({
32-
lineNumber,
33-
detail,
34-
context,
35-
range,
36-
fixInfo,
37-
})
38-
}
39-
4022
/**
4123
* Converts a Markdown heading into an HTML fragment according to the rules
4224
* used by GitHub.
@@ -98,8 +80,10 @@ const getMarkdownHeadings = (content) => {
9880
continue
9981
}
10082

83+
const children = token.children ?? []
84+
10185
headings.push(
102-
`${token.children
86+
`${children
10387
.map((token) => {
10488
return token.content
10589
})
@@ -112,7 +96,6 @@ const getMarkdownHeadings = (content) => {
11296

11397
module.exports = {
11498
filterTokens,
115-
addError,
11699
convertHeadingToHTMLFragment,
117100
getMarkdownHeadings,
118101
}

test/basic.test.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const test = require("node:test")
1+
const { test } = require("node:test")
22
const assert = require("node:assert/strict")
33

44
const { markdownlint } = require("markdownlint").promises
@@ -14,33 +14,33 @@ test("ensure the rule validate correctly", async () => {
1414
},
1515
customRules: [relativeLinks],
1616
})
17-
assert.equal(lintResults["test/fixtures/Valid.md"].length, 0)
18-
assert.equal(lintResults["test/fixtures/Invalid.md"].length, 3)
17+
assert.equal(lintResults["test/fixtures/Valid.md"]?.length, 0)
18+
assert.equal(lintResults["test/fixtures/Invalid.md"]?.length, 3)
1919

2020
assert.equal(
21-
lintResults["test/fixtures/Invalid.md"][0]?.ruleDescription,
21+
lintResults["test/fixtures/Invalid.md"]?.[0]?.ruleDescription,
2222
"Relative links should be valid",
2323
)
2424
assert.equal(
25-
lintResults["test/fixtures/Invalid.md"][0]?.errorDetail,
26-
'Link "./basic.test.js" should exist in the file system',
25+
lintResults["test/fixtures/Invalid.md"]?.[0]?.errorDetail,
26+
'"./basic.test.js" should exist in the file system',
2727
)
2828

2929
assert.equal(
30-
lintResults["test/fixtures/Invalid.md"][1]?.ruleDescription,
30+
lintResults["test/fixtures/Invalid.md"]?.[1]?.ruleDescription,
3131
"Relative links should be valid",
3232
)
3333
assert.equal(
34-
lintResults["test/fixtures/Invalid.md"][1]?.errorDetail,
35-
'Link "../image.png" should exist in the file system',
34+
lintResults["test/fixtures/Invalid.md"]?.[1]?.errorDetail,
35+
'"../image.png" should exist in the file system',
3636
)
3737

3838
assert.equal(
39-
lintResults["test/fixtures/Invalid.md"][2]?.ruleDescription,
39+
lintResults["test/fixtures/Invalid.md"]?.[2]?.ruleDescription,
4040
"Relative links should be valid",
4141
)
4242
assert.equal(
43-
lintResults["test/fixtures/Invalid.md"][2]?.errorDetail,
44-
'Link "./Valid.md#not-existing-heading" should have a valid fragment',
43+
lintResults["test/fixtures/Invalid.md"]?.[2]?.errorDetail,
44+
'"./Valid.md#not-existing-heading" should have a valid fragment identifier',
4545
)
4646
})

test/utils.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const test = require("node:test")
1+
const { test } = require("node:test")
22
const assert = require("node:assert/strict")
33

44
const {

0 commit comments

Comments
 (0)