Skip to content

Commit 34650bc

Browse files
committed
[WIP] feature-complete linting
1 parent 2c0cf20 commit 34650bc

116 files changed

Lines changed: 8283 additions & 5694 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/remark-lint-rewrite.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@node-core/remark-lint': major
3+
---
4+
5+
Rewrite `@node-core/remark-lint` with new lint rules

‎apps/site/.remarkrc.json‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"plugins": [
33
"remark-frontmatter",
4-
"@node-core/remark-lint",
54
[
6-
"@node-core/remark-lint/rules/validate-links",
5+
"@node-core/remark-lint",
76
{
8-
"ignoreFiles": ["pages/!(en)/**/*.{md,mdx}"],
9-
"ignoreLinks": ["/learn", "/learn/**/*", "/static/**/*", "/feed/*"]
7+
"link-targets": {
8+
"ignoreFiles": ["pages/!(en)/**/*.{md,mdx}"],
9+
"ignoreLinks": ["/learn", "/learn/**/*", "/static/**/*", "/feed/*"]
10+
}
1011
}
1112
]
1213
]

‎packages/remark-lint/README.md‎

Lines changed: 158 additions & 93 deletions
Large diffs are not rendered by default.

‎packages/remark-lint/package.json‎

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
"name": "@node-core/remark-lint",
33
"type": "module",
44
"version": "1.4.1",
5+
"description": "remark-lint presets and rules enforcing the Node.js documentation standards and the doc-kit Markdown specification",
56
"exports": {
67
".": "./src/index.mjs",
78
"./api": "./src/api.mjs",
8-
"./rules/*": "./src/rules/*.mjs"
9+
"./rules": "./src/rules/index.mjs",
10+
"./rules/*": "./src/rules/*.mjs",
11+
"./presets/*": "./src/presets/*.mjs",
12+
"./package.json": "./package.json"
913
},
1014
"repository": {
1115
"type": "git",
@@ -21,46 +25,37 @@
2125
"test:unit": "cross-env NODE_NO_WARNINGS=1 node --experimental-test-coverage --test \"**/*.test.mjs\""
2226
},
2327
"dependencies": {
24-
"@node-core/doc-kit": "^1.2.0",
25-
"mdast-util-to-string": "^4.0.0",
28+
"@doc-kit/core": "^1.1.0",
29+
"github-slugger": "^2.0.0",
2630
"remark-gfm": "^4.0.1",
27-
"remark-lint-blockquote-indentation": "^4.0.1",
28-
"remark-lint-checkbox-character-style": "^5.0.1",
29-
"remark-lint-checkbox-content-indent": "^5.0.1",
30-
"remark-lint-code-block-style": "^4.0.1",
3131
"remark-lint-definition-spacing": "^4.0.1",
3232
"remark-lint-fenced-code-flag": "^4.2.0",
33-
"remark-lint-fenced-code-marker": "^4.0.1",
33+
"remark-lint-file-extension": "^3.0.1",
3434
"remark-lint-final-definition": "^4.0.2",
35-
"remark-lint-heading-style": "^4.0.1",
35+
"remark-lint-first-heading-level": "^4.0.1",
3636
"remark-lint-maximum-line-length": "^4.1.1",
37-
"remark-lint-no-consecutive-blank-lines": "^5.0.1",
37+
"remark-lint-no-duplicate-definitions": "^4.0.1",
38+
"remark-lint-no-file-name-articles": "^3.0.1",
3839
"remark-lint-no-file-name-consecutive-dashes": "^3.0.1",
3940
"remark-lint-no-file-name-outer-dashes": "^3.0.1",
40-
"remark-lint-no-heading-indent": "^5.0.1",
41-
"remark-lint-no-literal-urls": "^4.0.1",
4241
"remark-lint-no-multiple-toplevel-headings": "^4.0.1",
4342
"remark-lint-no-shell-dollars": "^4.0.1",
44-
"remark-lint-no-table-indentation": "^5.0.1",
45-
"remark-lint-no-tabs": "^4.0.1",
46-
"remark-lint-no-trailing-spaces": "^5.0.0",
43+
"remark-lint-no-shortcut-reference-image": "^4.0.1",
44+
"remark-lint-no-shortcut-reference-link": "^4.0.1",
45+
"remark-lint-no-undefined-references": "^5.0.2",
4746
"remark-lint-no-unused-definitions": "^4.0.2",
4847
"remark-lint-prohibited-strings": "^5.0.0",
49-
"remark-lint-rule-style": "^4.0.1",
50-
"remark-lint-strong-marker": "^4.0.1",
51-
"remark-lint-table-cell-padding": "^5.1.1",
52-
"remark-lint-table-pipes": "^5.0.1",
53-
"remark-lint-unordered-list-marker-style": "^4.0.1",
54-
"remark-preset-lint-recommended": "^7.0.1",
55-
"semver": "^7.8.1",
48+
"remark-message-control": "^8.0.0",
49+
"remark-parse": "^11.0.0",
50+
"semver": "^7.8.5",
51+
"unified": "^11.0.5",
5652
"unified-lint-rule": "^3.0.1",
57-
"unist-util-visit": "^5.1.0",
58-
"yaml": "^2.8.4"
53+
"yaml": "^2.9.1"
5954
},
6055
"devDependencies": {
6156
"cross-env": "catalog:",
6257
"dedent": "^1.7.2",
63-
"remark-parse": "^11.0.0",
64-
"unified": "^11.0.5"
58+
"remark-stringify": "^11.0.0",
59+
"vfile": "^6.0.3"
6560
}
6661
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
4+
import dedent from 'dedent';
5+
import remarkGfm from 'remark-gfm';
6+
import remarkParse from 'remark-parse';
7+
import { unified } from 'unified';
8+
import { VFile } from 'vfile';
9+
10+
import { getContext } from '../context.mjs';
11+
import remarkTypeAnnotations from '../syntax.mjs';
12+
13+
const parse = markdown => {
14+
const file = new VFile({ path: 'doc/api/fs.md', value: markdown });
15+
const tree = unified()
16+
.use(remarkParse)
17+
.use(remarkGfm)
18+
.use(remarkTypeAnnotations)
19+
.parse(file);
20+
21+
return getContext(tree, file);
22+
};
23+
24+
describe('context', () => {
25+
it('splits the document into entries, like doc-kit', () => {
26+
const context = parse(dedent`
27+
Preamble.
28+
29+
# File system
30+
31+
Intro.
32+
33+
## Class: \`fs.Dir\`
34+
35+
### \`dir.close()\`
36+
37+
Text.
38+
39+
## \`fs.readFile(path)\`
40+
41+
### Event: \`'close'\`
42+
`);
43+
44+
const [preamble, title, klass, method, fn, event] = context.entries;
45+
46+
assert.equal(preamble.heading, null);
47+
assert.equal(preamble.nodes.length, 1);
48+
assert.equal(title.data.text, 'File system');
49+
assert.equal(title.data.slug, 'file-system');
50+
assert.equal(klass.data.type, 'class');
51+
assert.equal(klass.data.name, 'fs.Dir');
52+
assert.equal(klass.parent, title);
53+
assert.equal(method.data.type, 'method');
54+
assert.equal(method.parent, klass);
55+
assert.equal(method.nodes.length, 1);
56+
assert.equal(fn.parent, title);
57+
assert.deepEqual(klass.children, [method]);
58+
assert.equal(event.data.type, 'event');
59+
assert.equal(event.data.slug, 'event-close');
60+
});
61+
62+
it('classifies comments', () => {
63+
const context = parse(dedent`
64+
# T
65+
66+
<!--introduced_in=v1.0.0-->
67+
68+
<!-- type=misc -->
69+
70+
<!-- YAML
71+
added: v1.0.0
72+
changes:
73+
- version: v2.0.0
74+
-->
75+
76+
<!-- legacy -->
77+
78+
<!--YAML
79+
added: [
80+
-->
81+
82+
<div>not a comment</div>
83+
`);
84+
85+
const [directive, padded, yaml, tag, broken, html] = context.comments;
86+
87+
assert.equal(directive.kind, 'directive');
88+
assert.equal(directive.key, 'introduced_in');
89+
assert.equal(directive.value, 'v1.0.0');
90+
assert.equal(directive.padded, false);
91+
assert.equal(padded.kind, 'directive');
92+
assert.equal(padded.symmetric, true);
93+
assert.equal(padded.padded, true);
94+
assert.equal(yaml.kind, 'yaml');
95+
assert.equal(yaml.padded, 1);
96+
assert.deepEqual(yaml.yaml, {
97+
added: 'v1.0.0',
98+
changes: [{ version: 'v2.0.0' }],
99+
});
100+
assert.equal(tag.kind, 'tag');
101+
assert.equal(tag.inner, ' legacy ');
102+
assert.equal(broken.kind, 'yaml');
103+
assert.equal(broken.padded, 0);
104+
assert.equal(broken.yaml, undefined);
105+
assert.equal(broken.yamlErrors.length, 1);
106+
assert.equal(html.kind, 'html');
107+
assert.equal(context.commentsOf('directive').length, 2);
108+
});
109+
110+
it('recognizes stability indicators and typed lists', () => {
111+
const context = parse(dedent`
112+
# T
113+
114+
> Stability: 1.2 - Release candidate. Text.
115+
116+
> stability: broken
117+
118+
> Not stability.
119+
120+
* \`path\` {string} The path.
121+
* Returns: {Promise}
122+
123+
<!-- separator -->
124+
125+
* Plain list.
126+
`);
127+
128+
assert.equal(context.stability.length, 2);
129+
assert.equal(context.stability[0].valid, true);
130+
assert.equal(context.stability[0].index, '1.2');
131+
assert.equal(context.stability[0].description, 'Release candidate. Text.');
132+
assert.equal(context.stability[1].valid, false);
133+
assert.deepEqual(
134+
context.lists.map(({ typed }) => typed),
135+
[true, false]
136+
);
137+
assert.equal(context.typeAnnotations.length, 2);
138+
assert.equal(context.typeAnnotations[0].node.value, 'string');
139+
assert.equal(context.typeAnnotations[0].node.raw, '{string}');
140+
});
141+
142+
it('is computed once per tree', () => {
143+
const file = new VFile({ value: '# T\n' });
144+
const tree = unified().use(remarkParse).parse(file);
145+
146+
assert.equal(getContext(tree, file), getContext(tree, file));
147+
});
148+
});

0 commit comments

Comments
 (0)