Skip to content

Commit a8e566c

Browse files
committed
add metadata rule
1 parent ecca118 commit a8e566c

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

packages/remark-lint/src/api.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import basePreset from './index.mjs';
99
import duplicateStabilityNodes from './rules/duplicate-stability-nodes.mjs';
1010
import hashedSelfReference from './rules/hashed-self-reference.mjs';
1111
import orderedReferences from './rules/ordered-references.mjs';
12+
import requiredMetadata from './rules/required-metadata.mjs';
1213
import yamlComments from './rules/yaml/index.mjs';
1314

1415
export default {
@@ -23,6 +24,7 @@ export default {
2324
yamlComments,
2425
hashedSelfReference,
2526
orderedReferences,
27+
requiredMetadata,
2628
remarkLintNoUnusedDefinitions,
2729
[remarkLintFencedCodeFlag, { allowEmpty: false }],
2830
[remarkLintMaximumLineLength, 120],
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, it } from 'node:test';
2+
3+
import dedent from 'dedent';
4+
5+
import requiredMetadata from '../required-metadata.mjs';
6+
import { testRule } from './utils.mjs';
7+
8+
const testCases = [
9+
{
10+
name: 'both metadata comments present',
11+
input: dedent`
12+
<!-- introduced_in=v20.0.0 -->
13+
<!-- llm_description=This explains something. -->
14+
`,
15+
expected: [],
16+
},
17+
{
18+
name: 'missing introduced_in',
19+
input: '<!-- llm_description=This explains something. -->',
20+
expected: ['Missing "introduced_in" metadata'],
21+
},
22+
{
23+
name: 'missing llm_description, but paragraph exists',
24+
input: dedent`
25+
<!-- introduced_in=v20.0.0 -->
26+
27+
This is a short description for LLMs.
28+
`,
29+
expected: [],
30+
},
31+
{
32+
name: 'missing both metadata entries',
33+
input: '',
34+
expected: [
35+
'Missing "introduced_in" metadata',
36+
'Missing "llm_description" metadata',
37+
],
38+
},
39+
{
40+
name: 'only paragraph, no comments at all',
41+
input: 'This is just a paragraph, nothing else.',
42+
expected: ['Missing "introduced_in" metadata'],
43+
},
44+
];
45+
46+
describe('duplicate-stability-nodes', () => {
47+
for (const { name, input, expected } of testCases) {
48+
it(name, () => testRule(requiredMetadata, input, expected));
49+
}
50+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { lintRule } from 'unified-lint-rule';
2+
import { visit } from 'unist-util-visit';
3+
4+
const requiredKeys = ['introduced_in', 'llm_description'];
5+
const checks = requiredKeys.map(key => new RegExp(`<!--\\s*${key}=.*?-->`));
6+
7+
const hasRequiredMetadata = (tree, vfile) => {
8+
const found = new Set();
9+
let paragraphFound = false;
10+
11+
visit(tree, ['html', 'paragraph'], node => {
12+
if (node.type === 'html') {
13+
checks.forEach((check, i) => {
14+
if (new RegExp(check).test(node.value)) {
15+
found.add(requiredKeys[i]);
16+
}
17+
});
18+
} else if (node.type === 'paragraph') {
19+
paragraphFound = true;
20+
} else {
21+
// Continue searching
22+
return false;
23+
}
24+
});
25+
26+
requiredKeys
27+
.filter(key => !found.has(key))
28+
.forEach(missingKey => {
29+
// The first paragraph can also be the LLM description.
30+
if (missingKey === 'llm_description' && paragraphFound) {
31+
return;
32+
}
33+
34+
vfile.message(`Missing "${missingKey}" metadata`, tree);
35+
});
36+
};
37+
38+
export default lintRule('node-core:required-metadata', hasRequiredMetadata);

0 commit comments

Comments
 (0)