Skip to content

Commit b207f7e

Browse files
merging all conflicts
2 parents c617453 + 8efce78 commit b207f7e

41 files changed

Lines changed: 9536 additions & 7366 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```js [[1, 1, "submitAction"]]
2+
function UpdateName() {}
3+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```js [[1, 3, "submitAction"]]
2+
function submitAction() {}
3+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```js [[1, 1, "submitAction"]]
2+
function submitAction() {}
3+
```
4+
5+
```js [[1, 1, "\\"hidden\\""]]
6+
const mode = "hidden";
7+
```

eslint-local-rules/__tests__/lint-markdown-code-blocks.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ async function run() {
110110
malformedFixed.output.includes("{expectedErrors: {'react-compiler': [4]}}"),
111111
'expected malformed metadata to be replaced with canonical form'
112112
);
113+
114+
const validHighlightResult = await lintFixture('valid-highlight.md');
115+
assert.strictEqual(
116+
validHighlightResult.messages.length,
117+
0,
118+
'expected a valid inline highlight to pass'
119+
);
120+
121+
const missingHighlightResult = await lintFixture('missing-highlight.md');
122+
assert.strictEqual(
123+
missingHighlightResult.messages.length,
124+
1,
125+
'expected a highlight with text missing from its line to fail'
126+
);
127+
assert.strictEqual(
128+
missingHighlightResult.messages[0].message,
129+
"Could not find 'submitAction' on highlighted line 1"
130+
);
131+
132+
const outOfBoundsHighlightResult = await lintFixture(
133+
'out-of-bounds-highlight.md'
134+
);
135+
assert.strictEqual(
136+
outOfBoundsHighlightResult.messages.length,
137+
1,
138+
'expected an out-of-bounds highlight line to fail'
139+
);
140+
assert.strictEqual(
141+
outOfBoundsHighlightResult.messages[0].message,
142+
'Code highlight line 3 is outside this code block'
143+
);
113144
}
114145

115146
run().catch((error) => {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
const INLINE_HIGHLIGHT_REGEX = /(\[\[.*\]\])/;
9+
10+
function validateInlineHighlights(meta, code) {
11+
const match = INLINE_HIGHLIGHT_REGEX.exec(meta);
12+
if (!match) {
13+
return [];
14+
}
15+
16+
let highlights;
17+
try {
18+
highlights = JSON.parse(match[1]);
19+
} catch (error) {
20+
return ['Code highlight metadata must be valid JSON'];
21+
}
22+
23+
if (!Array.isArray(highlights)) {
24+
return ['Code highlight metadata must be an array'];
25+
}
26+
27+
const lines = code.split('\n');
28+
const errors = [];
29+
30+
for (const highlight of highlights) {
31+
if (!Array.isArray(highlight) || highlight.length < 3) {
32+
errors.push('Each code highlight must specify a step, line, and text');
33+
continue;
34+
}
35+
36+
const [, lineNo, text, fromIndex] = highlight;
37+
if (!Number.isInteger(lineNo) || lineNo < 1 || lineNo > lines.length) {
38+
errors.push(`Code highlight line ${lineNo} is outside this code block`);
39+
continue;
40+
}
41+
if (typeof text !== 'string') {
42+
errors.push(`Code highlight text on line ${lineNo} must be a string`);
43+
continue;
44+
}
45+
46+
const line = lines[lineNo - 1];
47+
let index = line.indexOf(text);
48+
const lastIndex = line.lastIndexOf(text);
49+
if (index !== lastIndex) {
50+
if (fromIndex === undefined) {
51+
errors.push(
52+
`Found '${text}' twice on highlighted line ${lineNo}; specify fromIndex`
53+
);
54+
continue;
55+
}
56+
index = line.indexOf(text, fromIndex);
57+
}
58+
if (index === -1) {
59+
errors.push(`Could not find '${text}' on highlighted line ${lineNo}`);
60+
}
61+
}
62+
63+
return errors;
64+
}
65+
66+
module.exports = {validateInlineHighlights};

eslint-local-rules/rules/lint-markdown-code-blocks.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ const {
1616
setCompilerExpectedLines,
1717
} = require('./metadata');
1818
const {normalizeDiagnostics} = require('./diagnostics');
19+
const {validateInlineHighlights} = require('./inline-highlights');
1920
const {parseMarkdownFile} = require('./markdown');
2021
const {runReactCompiler} = require('./react-compiler');
2122

2223
module.exports = {
2324
meta: {
2425
type: 'problem',
2526
docs: {
26-
description: 'Run React Compiler on markdown code blocks',
27+
description: 'Validate and compile markdown code blocks',
2728
category: 'Possible Errors',
2829
},
2930
fixable: 'code',
@@ -43,6 +44,17 @@ module.exports = {
4344
const {blocks} = parseMarkdownFile(sourceCode.text, filename);
4445
// For each supported code block, run the compiler and reconcile metadata.
4546
for (const block of blocks) {
47+
for (const message of validateInlineHighlights(
48+
block.meta,
49+
block.code
50+
)) {
51+
context.report({
52+
node,
53+
loc: block.position,
54+
message,
55+
});
56+
}
57+
4658
const compilerResult = runReactCompiler(
4759
block.code,
4860
`${filename}#codeblock`

eslint-local-rules/rules/markdown.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {parseFenceMetadata} = require('./metadata');
1616
* @property {{lineIndex: number, rawText: string, metaText: string, range: [number, number]}} fence
1717
* @property {string} filePath
1818
* @property {string} lang
19+
* @property {string} meta
1920
* @property {import('./metadata').FenceMetadata} metadata
2021
*/
2122

@@ -76,6 +77,7 @@ function parseMarkdownFile(content, filePath) {
7677

7778
blocks.push({
7879
lang: rawLang || normalizedLang,
80+
meta: node.meta || metaText.trim(),
7981
metadata,
8082
filePath,
8183
code: node.value || '',

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"prettier:diff": "yarn nit:source",
1717
"lint-heading-ids": "node scripts/headingIdLinter.js",
1818
"fix-headings": "node scripts/headingIdLinter.js --fix",
19-
"ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss deadlinks",
19+
"ci-check": "npm-run-all prettier:diff --parallel lint test:eslint-local-rules tsc lint-heading-ids rss deadlinks",
2020
"tsc": "tsc --noEmit",
2121
"start": "next start",
2222
"postinstall": "yarn --cwd eslint-local-rules install && is-ci || husky install .husky",
@@ -93,7 +93,11 @@
9393
"postcss-flexbugs-fixes": "4.2.1",
9494
"postcss-preset-env": "^6.7.0",
9595
"prettier": "^2.5.1",
96+
<<<<<<< HEAD
9697
"react-server-dom-webpack": "^19.2.5",
98+
=======
99+
"react-server-dom-webpack": "^19.3.0",
100+
>>>>>>> 8efce7853d0fc59e615ed1c253799cf1798b8428
97101
"reading-time": "^1.2.0",
98102
"remark": "^12.0.1",
99103
"remark-external-links": "^7.0.0",

0 commit comments

Comments
 (0)