react-doctor 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -15
- package/dist/cli.js +93 -19
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.js +92 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,21 +160,26 @@ When a suppression isn't working, `--explain <file:line>` reports what the scann
|
|
|
160
160
|
|
|
161
161
|
### Config keys
|
|
162
162
|
|
|
163
|
-
| Key
|
|
164
|
-
|
|
|
165
|
-
| `ignore.rules`
|
|
166
|
-
| `ignore.files`
|
|
167
|
-
| `ignore.overrides`
|
|
168
|
-
| `lint`
|
|
169
|
-
| `deadCode`
|
|
170
|
-
| `verbose`
|
|
171
|
-
| `diff`
|
|
172
|
-
| `failOn`
|
|
173
|
-
| `customRulesOnly`
|
|
174
|
-
| `share`
|
|
175
|
-
| `textComponents`
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
163
|
+
| Key | Type | Default |
|
|
164
|
+
| -------------------------- | -------------------------------- | -------- |
|
|
165
|
+
| `ignore.rules` | `string[]` | `[]` |
|
|
166
|
+
| `ignore.files` | `string[]` | `[]` |
|
|
167
|
+
| `ignore.overrides` | `{ files, rules? }[]` | `[]` |
|
|
168
|
+
| `lint` | `boolean` | `true` |
|
|
169
|
+
| `deadCode` | `boolean` | `true` |
|
|
170
|
+
| `verbose` | `boolean` | `false` |
|
|
171
|
+
| `diff` | `boolean \| string` | |
|
|
172
|
+
| `failOn` | `"error" \| "warning" \| "none"` | `"none"` |
|
|
173
|
+
| `customRulesOnly` | `boolean` | `false` |
|
|
174
|
+
| `share` | `boolean` | `true` |
|
|
175
|
+
| `textComponents` | `string[]` | `[]` |
|
|
176
|
+
| `rawTextWrapperComponents` | `string[]` | `[]` |
|
|
177
|
+
| `respectInlineDisables` | `boolean` | `true` |
|
|
178
|
+
| `adoptExistingLintConfig` | `boolean` | `true` |
|
|
179
|
+
|
|
180
|
+
`textComponents` is the broad escape hatch for `rn-no-raw-text` — list components that themselves behave like React Native's `<Text>` (custom `Typography`, `NativeTabs.Trigger.Label`, etc.) and the rule will treat them as text containers regardless of what their children look like.
|
|
181
|
+
|
|
182
|
+
`rawTextWrapperComponents` is the narrower option for components that are not text elements but safely route string-only children through an internal `<Text>` (e.g. `heroui-native`'s `Button`, which stringifies its children and renders them through a `ButtonLabel`). Listed wrappers suppress `rn-no-raw-text` only when their children are entirely stringifiable. A wrapper with mixed children — e.g. `<Button>Save<Icon /></Button>` — still reports because the wrapper can't safely route raw text alongside a sibling JSX element.
|
|
178
183
|
|
|
179
184
|
## Node.js API
|
|
180
185
|
|
package/dist/cli.js
CHANGED
|
@@ -912,6 +912,8 @@ const isFileIgnoredByPatterns = (filePath, rootDirectory, patterns) => {
|
|
|
912
912
|
//#endregion
|
|
913
913
|
//#region src/utils/filter-diagnostics.ts
|
|
914
914
|
const OPENING_TAG_PATTERN = /<([A-Z][\w.]*)/;
|
|
915
|
+
const JSX_CHILD_OPEN_PATTERN = /<[A-Za-z]/;
|
|
916
|
+
const escapeRegExpSpecials = (rawText) => rawText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
915
917
|
const resolveCandidateReadPath = (rootDirectory, filePath) => {
|
|
916
918
|
const normalizedFile = filePath.replace(/\\/g, "/");
|
|
917
919
|
if (normalizedFile.startsWith("/") || /^[a-zA-Z]:\//.test(normalizedFile) || /^[a-zA-Z]:\\/.test(filePath)) return filePath;
|
|
@@ -937,21 +939,93 @@ const isInsideTextComponent = (lines, diagnosticLine, textComponentNames) => {
|
|
|
937
939
|
}
|
|
938
940
|
return false;
|
|
939
941
|
};
|
|
942
|
+
const findOpenerAtOrAbove = (lines, upperBoundLineIndex) => {
|
|
943
|
+
for (let lineIndex = upperBoundLineIndex; lineIndex >= 0; lineIndex--) {
|
|
944
|
+
const match = lines[lineIndex].match(OPENING_TAG_PATTERN);
|
|
945
|
+
if (!match) continue;
|
|
946
|
+
const fullName = match[1];
|
|
947
|
+
return {
|
|
948
|
+
fullName,
|
|
949
|
+
leafName: fullName.includes(".") ? fullName.split(".").at(-1) ?? fullName : fullName,
|
|
950
|
+
lineIndex
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
return null;
|
|
954
|
+
};
|
|
955
|
+
const resolveJsxRange = (lines, opener) => {
|
|
956
|
+
const closingPattern = new RegExp(`</(?:${escapeRegExpSpecials(opener.fullName)}|${escapeRegExpSpecials(opener.leafName)})\\s*>`);
|
|
957
|
+
let closerLineIndex = -1;
|
|
958
|
+
let closerColumn = -1;
|
|
959
|
+
for (let lineIndex = opener.lineIndex; lineIndex < lines.length; lineIndex++) {
|
|
960
|
+
const match = closingPattern.exec(lines[lineIndex]);
|
|
961
|
+
if (!match) continue;
|
|
962
|
+
closerLineIndex = lineIndex;
|
|
963
|
+
closerColumn = match.index;
|
|
964
|
+
break;
|
|
965
|
+
}
|
|
966
|
+
if (closerLineIndex < 0) return null;
|
|
967
|
+
const openerLine = lines[opener.lineIndex];
|
|
968
|
+
const tagStartIndex = openerLine.indexOf(`<${opener.fullName}`);
|
|
969
|
+
if (tagStartIndex < 0) return null;
|
|
970
|
+
const openerEndIndex = openerLine.indexOf(">", tagStartIndex);
|
|
971
|
+
let bodyText;
|
|
972
|
+
if (opener.lineIndex === closerLineIndex) {
|
|
973
|
+
if (openerEndIndex < 0 || openerEndIndex >= closerColumn) return null;
|
|
974
|
+
bodyText = openerLine.slice(openerEndIndex + 1, closerColumn);
|
|
975
|
+
} else {
|
|
976
|
+
const segments = [];
|
|
977
|
+
if (openerEndIndex >= 0) segments.push(openerLine.slice(openerEndIndex + 1));
|
|
978
|
+
for (let lineIndex = opener.lineIndex + 1; lineIndex < closerLineIndex; lineIndex++) segments.push(lines[lineIndex]);
|
|
979
|
+
segments.push(lines[closerLineIndex].slice(0, closerColumn));
|
|
980
|
+
bodyText = segments.join("\n");
|
|
981
|
+
}
|
|
982
|
+
return {
|
|
983
|
+
closerLineIndex,
|
|
984
|
+
closerColumn,
|
|
985
|
+
bodyText
|
|
986
|
+
};
|
|
987
|
+
};
|
|
988
|
+
const isInsideStringOnlyWrapper = (lines, diagnosticLine, diagnosticColumn, wrapperNames) => {
|
|
989
|
+
const diagnosticLineIndex = diagnosticLine - 1;
|
|
990
|
+
const diagnosticColumnIndex = Math.max(0, diagnosticColumn - 1);
|
|
991
|
+
let upperBoundLineIndex = diagnosticLineIndex;
|
|
992
|
+
while (upperBoundLineIndex >= 0) {
|
|
993
|
+
const opener = findOpenerAtOrAbove(lines, upperBoundLineIndex);
|
|
994
|
+
if (!opener) return false;
|
|
995
|
+
const range = resolveJsxRange(lines, opener);
|
|
996
|
+
if (range === null) {
|
|
997
|
+
upperBoundLineIndex = opener.lineIndex - 1;
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
if (range.closerLineIndex < diagnosticLineIndex || range.closerLineIndex === diagnosticLineIndex && range.closerColumn <= diagnosticColumnIndex) {
|
|
1001
|
+
upperBoundLineIndex = opener.lineIndex - 1;
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (!wrapperNames.has(opener.fullName) && !wrapperNames.has(opener.leafName)) return false;
|
|
1005
|
+
return !JSX_CHILD_OPEN_PATTERN.test(range.bodyText);
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
};
|
|
940
1009
|
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory, readFileLinesSync) => {
|
|
941
1010
|
const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules.filter((rule) => typeof rule === "string") : []);
|
|
942
1011
|
const ignoredFilePatterns = compileIgnoredFilePatterns(config);
|
|
943
1012
|
const compiledOverrides = compileIgnoreOverrides(config);
|
|
944
1013
|
const textComponentNames = new Set(Array.isArray(config.textComponents) ? config.textComponents.filter((name) => typeof name === "string") : []);
|
|
945
1014
|
const hasTextComponents = textComponentNames.size > 0;
|
|
1015
|
+
const rawTextWrapperComponentNames = new Set(Array.isArray(config.rawTextWrapperComponents) ? config.rawTextWrapperComponents.filter((name) => typeof name === "string") : []);
|
|
1016
|
+
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
946
1017
|
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
947
1018
|
return diagnostics.filter((diagnostic) => {
|
|
948
1019
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
949
1020
|
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
950
1021
|
if (isFileIgnoredByPatterns(diagnostic.filePath, rootDirectory, ignoredFilePatterns)) return false;
|
|
951
1022
|
if (isDiagnosticIgnoredByOverrides(diagnostic, rootDirectory, compiledOverrides)) return false;
|
|
952
|
-
if (hasTextComponents && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
1023
|
+
if ((hasTextComponents || hasRawTextWrappers) && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
953
1024
|
const lines = getFileLines(diagnostic.filePath);
|
|
954
|
-
if (lines
|
|
1025
|
+
if (lines) {
|
|
1026
|
+
if (hasTextComponents && isInsideTextComponent(lines, diagnostic.line, textComponentNames)) return false;
|
|
1027
|
+
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return false;
|
|
1028
|
+
}
|
|
955
1029
|
}
|
|
956
1030
|
return true;
|
|
957
1031
|
});
|
|
@@ -2183,22 +2257,22 @@ const TANSTACK_START_RULES = {
|
|
|
2183
2257
|
"react-doctor/tanstack-start-loader-parallel-fetch": "warn"
|
|
2184
2258
|
};
|
|
2185
2259
|
const REACT_COMPILER_RULES = {
|
|
2186
|
-
"react-hooks-js/set-state-in-render": "
|
|
2187
|
-
"react-hooks-js/immutability": "
|
|
2188
|
-
"react-hooks-js/refs": "
|
|
2189
|
-
"react-hooks-js/purity": "
|
|
2190
|
-
"react-hooks-js/hooks": "
|
|
2191
|
-
"react-hooks-js/set-state-in-effect": "
|
|
2192
|
-
"react-hooks-js/globals": "
|
|
2193
|
-
"react-hooks-js/error-boundaries": "
|
|
2194
|
-
"react-hooks-js/preserve-manual-memoization": "
|
|
2195
|
-
"react-hooks-js/unsupported-syntax": "
|
|
2196
|
-
"react-hooks-js/component-hook-factories": "
|
|
2197
|
-
"react-hooks-js/static-components": "
|
|
2198
|
-
"react-hooks-js/use-memo": "
|
|
2199
|
-
"react-hooks-js/void-use-memo": "
|
|
2200
|
-
"react-hooks-js/incompatible-library": "
|
|
2201
|
-
"react-hooks-js/todo": "
|
|
2260
|
+
"react-hooks-js/set-state-in-render": "error",
|
|
2261
|
+
"react-hooks-js/immutability": "error",
|
|
2262
|
+
"react-hooks-js/refs": "error",
|
|
2263
|
+
"react-hooks-js/purity": "error",
|
|
2264
|
+
"react-hooks-js/hooks": "error",
|
|
2265
|
+
"react-hooks-js/set-state-in-effect": "error",
|
|
2266
|
+
"react-hooks-js/globals": "error",
|
|
2267
|
+
"react-hooks-js/error-boundaries": "error",
|
|
2268
|
+
"react-hooks-js/preserve-manual-memoization": "error",
|
|
2269
|
+
"react-hooks-js/unsupported-syntax": "error",
|
|
2270
|
+
"react-hooks-js/component-hook-factories": "error",
|
|
2271
|
+
"react-hooks-js/static-components": "error",
|
|
2272
|
+
"react-hooks-js/use-memo": "error",
|
|
2273
|
+
"react-hooks-js/void-use-memo": "error",
|
|
2274
|
+
"react-hooks-js/incompatible-library": "error",
|
|
2275
|
+
"react-hooks-js/todo": "error"
|
|
2202
2276
|
};
|
|
2203
2277
|
const readPluginRuleNames = (pluginSpecifier) => {
|
|
2204
2278
|
try {
|
|
@@ -4028,7 +4102,7 @@ const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
|
4028
4102
|
};
|
|
4029
4103
|
//#endregion
|
|
4030
4104
|
//#region src/cli.ts
|
|
4031
|
-
const VERSION = "0.1.
|
|
4105
|
+
const VERSION = "0.1.3";
|
|
4032
4106
|
const VALID_FAIL_ON_LEVELS = new Set([
|
|
4033
4107
|
"error",
|
|
4034
4108
|
"warning",
|
package/dist/eslint-plugin.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,23 @@ interface ReactDoctorConfig {
|
|
|
76
76
|
customRulesOnly?: boolean;
|
|
77
77
|
share?: boolean;
|
|
78
78
|
textComponents?: string[];
|
|
79
|
+
/**
|
|
80
|
+
* Names of components that safely route string-only children through a
|
|
81
|
+
* React Native `<Text>` internally (e.g. `heroui-native`'s `Button`,
|
|
82
|
+
* which stringifies its children and renders them through a
|
|
83
|
+
* `ButtonLabel` → `Text`). For listed components, `rn-no-raw-text`
|
|
84
|
+
* is suppressed ONLY when the wrapper's children are entirely
|
|
85
|
+
* stringifiable (no nested JSX elements). A wrapper with mixed
|
|
86
|
+
* children — e.g. `<Button>Save<Icon /></Button>` — still reports,
|
|
87
|
+
* because the wrapper can't safely route raw text alongside a
|
|
88
|
+
* sibling JSX element.
|
|
89
|
+
*
|
|
90
|
+
* Use this instead of `textComponents` when the component is not
|
|
91
|
+
* itself a text element but is known to wrap its string children
|
|
92
|
+
* in one. `textComponents` is the broader escape hatch and
|
|
93
|
+
* suppresses regardless of sibling content.
|
|
94
|
+
*/
|
|
95
|
+
rawTextWrapperComponents?: string[];
|
|
79
96
|
/**
|
|
80
97
|
* Whether to respect inline `// eslint-disable*`, `// oxlint-disable*`,
|
|
81
98
|
* and `// react-doctor-disable*` comments in source files. Default: `true`.
|
package/dist/index.js
CHANGED
|
@@ -1349,6 +1349,8 @@ const isFileIgnoredByPatterns = (filePath, rootDirectory, patterns) => {
|
|
|
1349
1349
|
//#endregion
|
|
1350
1350
|
//#region src/utils/filter-diagnostics.ts
|
|
1351
1351
|
const OPENING_TAG_PATTERN = /<([A-Z][\w.]*)/;
|
|
1352
|
+
const JSX_CHILD_OPEN_PATTERN = /<[A-Za-z]/;
|
|
1353
|
+
const escapeRegExpSpecials = (rawText) => rawText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1352
1354
|
const resolveCandidateReadPath = (rootDirectory, filePath) => {
|
|
1353
1355
|
const normalizedFile = filePath.replace(/\\/g, "/");
|
|
1354
1356
|
if (normalizedFile.startsWith("/") || /^[a-zA-Z]:\//.test(normalizedFile) || /^[a-zA-Z]:\\/.test(filePath)) return filePath;
|
|
@@ -1374,21 +1376,93 @@ const isInsideTextComponent = (lines, diagnosticLine, textComponentNames) => {
|
|
|
1374
1376
|
}
|
|
1375
1377
|
return false;
|
|
1376
1378
|
};
|
|
1379
|
+
const findOpenerAtOrAbove = (lines, upperBoundLineIndex) => {
|
|
1380
|
+
for (let lineIndex = upperBoundLineIndex; lineIndex >= 0; lineIndex--) {
|
|
1381
|
+
const match = lines[lineIndex].match(OPENING_TAG_PATTERN);
|
|
1382
|
+
if (!match) continue;
|
|
1383
|
+
const fullName = match[1];
|
|
1384
|
+
return {
|
|
1385
|
+
fullName,
|
|
1386
|
+
leafName: fullName.includes(".") ? fullName.split(".").at(-1) ?? fullName : fullName,
|
|
1387
|
+
lineIndex
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
return null;
|
|
1391
|
+
};
|
|
1392
|
+
const resolveJsxRange = (lines, opener) => {
|
|
1393
|
+
const closingPattern = new RegExp(`</(?:${escapeRegExpSpecials(opener.fullName)}|${escapeRegExpSpecials(opener.leafName)})\\s*>`);
|
|
1394
|
+
let closerLineIndex = -1;
|
|
1395
|
+
let closerColumn = -1;
|
|
1396
|
+
for (let lineIndex = opener.lineIndex; lineIndex < lines.length; lineIndex++) {
|
|
1397
|
+
const match = closingPattern.exec(lines[lineIndex]);
|
|
1398
|
+
if (!match) continue;
|
|
1399
|
+
closerLineIndex = lineIndex;
|
|
1400
|
+
closerColumn = match.index;
|
|
1401
|
+
break;
|
|
1402
|
+
}
|
|
1403
|
+
if (closerLineIndex < 0) return null;
|
|
1404
|
+
const openerLine = lines[opener.lineIndex];
|
|
1405
|
+
const tagStartIndex = openerLine.indexOf(`<${opener.fullName}`);
|
|
1406
|
+
if (tagStartIndex < 0) return null;
|
|
1407
|
+
const openerEndIndex = openerLine.indexOf(">", tagStartIndex);
|
|
1408
|
+
let bodyText;
|
|
1409
|
+
if (opener.lineIndex === closerLineIndex) {
|
|
1410
|
+
if (openerEndIndex < 0 || openerEndIndex >= closerColumn) return null;
|
|
1411
|
+
bodyText = openerLine.slice(openerEndIndex + 1, closerColumn);
|
|
1412
|
+
} else {
|
|
1413
|
+
const segments = [];
|
|
1414
|
+
if (openerEndIndex >= 0) segments.push(openerLine.slice(openerEndIndex + 1));
|
|
1415
|
+
for (let lineIndex = opener.lineIndex + 1; lineIndex < closerLineIndex; lineIndex++) segments.push(lines[lineIndex]);
|
|
1416
|
+
segments.push(lines[closerLineIndex].slice(0, closerColumn));
|
|
1417
|
+
bodyText = segments.join("\n");
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
closerLineIndex,
|
|
1421
|
+
closerColumn,
|
|
1422
|
+
bodyText
|
|
1423
|
+
};
|
|
1424
|
+
};
|
|
1425
|
+
const isInsideStringOnlyWrapper = (lines, diagnosticLine, diagnosticColumn, wrapperNames) => {
|
|
1426
|
+
const diagnosticLineIndex = diagnosticLine - 1;
|
|
1427
|
+
const diagnosticColumnIndex = Math.max(0, diagnosticColumn - 1);
|
|
1428
|
+
let upperBoundLineIndex = diagnosticLineIndex;
|
|
1429
|
+
while (upperBoundLineIndex >= 0) {
|
|
1430
|
+
const opener = findOpenerAtOrAbove(lines, upperBoundLineIndex);
|
|
1431
|
+
if (!opener) return false;
|
|
1432
|
+
const range = resolveJsxRange(lines, opener);
|
|
1433
|
+
if (range === null) {
|
|
1434
|
+
upperBoundLineIndex = opener.lineIndex - 1;
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
if (range.closerLineIndex < diagnosticLineIndex || range.closerLineIndex === diagnosticLineIndex && range.closerColumn <= diagnosticColumnIndex) {
|
|
1438
|
+
upperBoundLineIndex = opener.lineIndex - 1;
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
if (!wrapperNames.has(opener.fullName) && !wrapperNames.has(opener.leafName)) return false;
|
|
1442
|
+
return !JSX_CHILD_OPEN_PATTERN.test(range.bodyText);
|
|
1443
|
+
}
|
|
1444
|
+
return false;
|
|
1445
|
+
};
|
|
1377
1446
|
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory, readFileLinesSync) => {
|
|
1378
1447
|
const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules.filter((rule) => typeof rule === "string") : []);
|
|
1379
1448
|
const ignoredFilePatterns = compileIgnoredFilePatterns(config);
|
|
1380
1449
|
const compiledOverrides = compileIgnoreOverrides(config);
|
|
1381
1450
|
const textComponentNames = new Set(Array.isArray(config.textComponents) ? config.textComponents.filter((name) => typeof name === "string") : []);
|
|
1382
1451
|
const hasTextComponents = textComponentNames.size > 0;
|
|
1452
|
+
const rawTextWrapperComponentNames = new Set(Array.isArray(config.rawTextWrapperComponents) ? config.rawTextWrapperComponents.filter((name) => typeof name === "string") : []);
|
|
1453
|
+
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
1383
1454
|
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
1384
1455
|
return diagnostics.filter((diagnostic) => {
|
|
1385
1456
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
1386
1457
|
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
1387
1458
|
if (isFileIgnoredByPatterns(diagnostic.filePath, rootDirectory, ignoredFilePatterns)) return false;
|
|
1388
1459
|
if (isDiagnosticIgnoredByOverrides(diagnostic, rootDirectory, compiledOverrides)) return false;
|
|
1389
|
-
if (hasTextComponents && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
1460
|
+
if ((hasTextComponents || hasRawTextWrappers) && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
1390
1461
|
const lines = getFileLines(diagnostic.filePath);
|
|
1391
|
-
if (lines
|
|
1462
|
+
if (lines) {
|
|
1463
|
+
if (hasTextComponents && isInsideTextComponent(lines, diagnostic.line, textComponentNames)) return false;
|
|
1464
|
+
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return false;
|
|
1465
|
+
}
|
|
1392
1466
|
}
|
|
1393
1467
|
return true;
|
|
1394
1468
|
});
|
|
@@ -1850,22 +1924,22 @@ const TANSTACK_START_RULES = {
|
|
|
1850
1924
|
"react-doctor/tanstack-start-loader-parallel-fetch": "warn"
|
|
1851
1925
|
};
|
|
1852
1926
|
const REACT_COMPILER_RULES = {
|
|
1853
|
-
"react-hooks-js/set-state-in-render": "
|
|
1854
|
-
"react-hooks-js/immutability": "
|
|
1855
|
-
"react-hooks-js/refs": "
|
|
1856
|
-
"react-hooks-js/purity": "
|
|
1857
|
-
"react-hooks-js/hooks": "
|
|
1858
|
-
"react-hooks-js/set-state-in-effect": "
|
|
1859
|
-
"react-hooks-js/globals": "
|
|
1860
|
-
"react-hooks-js/error-boundaries": "
|
|
1861
|
-
"react-hooks-js/preserve-manual-memoization": "
|
|
1862
|
-
"react-hooks-js/unsupported-syntax": "
|
|
1863
|
-
"react-hooks-js/component-hook-factories": "
|
|
1864
|
-
"react-hooks-js/static-components": "
|
|
1865
|
-
"react-hooks-js/use-memo": "
|
|
1866
|
-
"react-hooks-js/void-use-memo": "
|
|
1867
|
-
"react-hooks-js/incompatible-library": "
|
|
1868
|
-
"react-hooks-js/todo": "
|
|
1927
|
+
"react-hooks-js/set-state-in-render": "error",
|
|
1928
|
+
"react-hooks-js/immutability": "error",
|
|
1929
|
+
"react-hooks-js/refs": "error",
|
|
1930
|
+
"react-hooks-js/purity": "error",
|
|
1931
|
+
"react-hooks-js/hooks": "error",
|
|
1932
|
+
"react-hooks-js/set-state-in-effect": "error",
|
|
1933
|
+
"react-hooks-js/globals": "error",
|
|
1934
|
+
"react-hooks-js/error-boundaries": "error",
|
|
1935
|
+
"react-hooks-js/preserve-manual-memoization": "error",
|
|
1936
|
+
"react-hooks-js/unsupported-syntax": "error",
|
|
1937
|
+
"react-hooks-js/component-hook-factories": "error",
|
|
1938
|
+
"react-hooks-js/static-components": "error",
|
|
1939
|
+
"react-hooks-js/use-memo": "error",
|
|
1940
|
+
"react-hooks-js/void-use-memo": "error",
|
|
1941
|
+
"react-hooks-js/incompatible-library": "error",
|
|
1942
|
+
"react-hooks-js/todo": "error"
|
|
1869
1943
|
};
|
|
1870
1944
|
const readPluginRuleNames = (pluginSpecifier) => {
|
|
1871
1945
|
try {
|
package/package.json
CHANGED