react-doctor 0.9.1-dev.54ba416 → 0.9.1-dev.8534d86
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 +2 -0
- package/dist/cli.js +676 -69
- package/dist/index.d.ts +2 -2
- package/dist/index.js +671 -65
- package/dist/lsp.js +673 -66
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2a46b47b-3e8f-5566-a6f0-442aa6df4a39")}catch(e){}}();
|
|
3
3
|
import { i as __toESM$1, r as __require, t as __commonJSMin$1 } from "./chunk-ChKA3wi6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -61448,7 +61448,8 @@ var AmbiguousProjectError = class extends Error {
|
|
|
61448
61448
|
}
|
|
61449
61449
|
};
|
|
61450
61450
|
const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
|
|
61451
|
-
const
|
|
61451
|
+
const HTML_FILE_PATTERN = /\.html$/i;
|
|
61452
|
+
const SOURCE_FILE_PATTERN = /\.(?:tsx?|jsx?|mts|mjs|[hH][tT][mM][lL])$/;
|
|
61452
61453
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
61453
61454
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
61454
61455
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
@@ -62285,7 +62286,7 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
|
|
|
62285
62286
|
]).find(isFile) ?? null;
|
|
62286
62287
|
};
|
|
62287
62288
|
const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
62288
|
-
const getStaticPropertyName$
|
|
62289
|
+
const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
|
|
62289
62290
|
const isCommonJsConfigExportAssignment = (node, sourceFile) => ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && (node.left.getText(sourceFile) === "module.exports" || node.left.getText(sourceFile) === "exports.default");
|
|
62290
62291
|
const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
62291
62292
|
const getRequireModuleSpecifier = (expression) => {
|
|
@@ -62349,7 +62350,7 @@ const getImportBinding = (sourceFile, bindingName) => {
|
|
|
62349
62350
|
const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
|
|
62350
62351
|
if (bindingElement && moduleSpecifier) return {
|
|
62351
62352
|
moduleSpecifier,
|
|
62352
|
-
exportName: bindingElement.propertyName ? getStaticPropertyName$
|
|
62353
|
+
exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
|
|
62353
62354
|
isNamespace: false
|
|
62354
62355
|
};
|
|
62355
62356
|
}
|
|
@@ -62510,7 +62511,7 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
62510
62511
|
}
|
|
62511
62512
|
if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
|
|
62512
62513
|
for (const property of [...unwrappedExpression.properties].reverse()) {
|
|
62513
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62514
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
|
|
62514
62515
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
|
|
62515
62516
|
if (ts.isSpreadAssignment(property)) {
|
|
62516
62517
|
const spreadState = getReactCompilerFlagState(property.expression, analysis);
|
|
@@ -62536,8 +62537,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
62536
62537
|
}
|
|
62537
62538
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
62538
62539
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
62539
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62540
|
-
if (ts.isMethodDeclaration(property) && getStaticPropertyName$
|
|
62540
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === propertyName) return property.initializer;
|
|
62541
|
+
if (ts.isMethodDeclaration(property) && getStaticPropertyName$2(property.name) === propertyName) return property;
|
|
62541
62542
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
62542
62543
|
if (ts.isSpreadAssignment(property)) {
|
|
62543
62544
|
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
@@ -62568,7 +62569,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
|
|
|
62568
62569
|
return resolvedExpression.properties.some((property) => {
|
|
62569
62570
|
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
62570
62571
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
62571
|
-
const staticPropertyName = getStaticPropertyName$
|
|
62572
|
+
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
62572
62573
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
62573
62574
|
}
|
|
62574
62575
|
return false;
|
|
@@ -62587,7 +62588,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
|
|
|
62587
62588
|
continue;
|
|
62588
62589
|
}
|
|
62589
62590
|
if (ts.isExpressionStatement(statement) && isCommonJsConfigExportAssignment(statement.expression, sourceFile) && exportName !== "default" && ts.isObjectLiteralExpression(statement.expression.right)) for (const property of [...statement.expression.right.properties].reverse()) {
|
|
62590
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62591
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
|
|
62591
62592
|
exportedNodes.push(property.initializer);
|
|
62592
62593
|
break;
|
|
62593
62594
|
}
|
|
@@ -62746,10 +62747,10 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62746
62747
|
const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
|
|
62747
62748
|
if (reactCompilerFlagState?.isEnabled) return true;
|
|
62748
62749
|
for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
|
|
62749
|
-
const propertyName = getStaticPropertyName$
|
|
62750
|
+
const propertyName = getStaticPropertyName$2(property.name);
|
|
62750
62751
|
if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
|
|
62751
62752
|
if (propertyName === "reactCompiler") continue;
|
|
62752
|
-
if ((propertyName === "plugins" || propertyName === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, propertyName, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$
|
|
62753
|
+
if ((propertyName === "plugins" || propertyName === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, propertyName, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$2(laterProperty.name) === propertyName)) continue;
|
|
62753
62754
|
const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
|
|
62754
62755
|
if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
|
|
62755
62756
|
const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
|
|
@@ -62759,12 +62760,12 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62759
62760
|
} else if (ts.isShorthandPropertyAssignment(property)) {
|
|
62760
62761
|
if (excludedPropertyNames?.has(property.name.text)) continue;
|
|
62761
62762
|
if (property.name.text === "reactCompiler") continue;
|
|
62762
|
-
if ((property.name.text === "plugins" || property.name.text === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, property.name.text, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$
|
|
62763
|
+
if ((property.name.text === "plugins" || property.name.text === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, property.name.text, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$2(laterProperty.name) === property.name.text)) continue;
|
|
62763
62764
|
const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
|
|
62764
62765
|
if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
|
|
62765
62766
|
} else if (ts.isSpreadAssignment(property)) {
|
|
62766
62767
|
if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
|
|
62767
|
-
if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$
|
|
62768
|
+
if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$2(laterProperty.name) === "plugins" || getStaticPropertyName$2(laterProperty.name) === "presets"))) continue;
|
|
62768
62769
|
if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
|
|
62769
62770
|
}
|
|
62770
62771
|
return false;
|
|
@@ -66977,6 +66978,321 @@ const buildReactNativeDiagnostic = (input) => ({
|
|
|
66977
66978
|
column: input.column ?? 0,
|
|
66978
66979
|
category: input.category ?? "Correctness"
|
|
66979
66980
|
});
|
|
66981
|
+
const ANDROID_APP_BUILD_FILE_NAMES = ["build.gradle", "build.gradle.kts"];
|
|
66982
|
+
const BUILD_TYPES_BLOCK_PATTERN = /\bbuildTypes\s*\{/g;
|
|
66983
|
+
const RELEASE_STRING_MARKER = "__RD_RELEASE__";
|
|
66984
|
+
const RELEASE_BLOCK_PATTERNS = [/\brelease\s*\{/g, /\b(?:getByName|named)\s*\(\s*(?:["']release["']|__RD_RELEASE__)\s*\)\s*\{/g];
|
|
66985
|
+
const MINIFY_ASSIGNMENT_PATTERN = /\b(?:isMinifyEnabled|minifyEnabled)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66986
|
+
const SHRINK_RESOURCES_ASSIGNMENT_PATTERN = /\b(?:isShrinkResources|shrinkResources)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66987
|
+
const stripGradleComments = (contents) => {
|
|
66988
|
+
let result = "";
|
|
66989
|
+
let index = 0;
|
|
66990
|
+
let quote = null;
|
|
66991
|
+
while (index < contents.length) {
|
|
66992
|
+
const character = contents[index];
|
|
66993
|
+
const nextCharacter = contents[index + 1];
|
|
66994
|
+
if (quote !== null) {
|
|
66995
|
+
result += character;
|
|
66996
|
+
if (character === "\\") {
|
|
66997
|
+
result += nextCharacter ?? "";
|
|
66998
|
+
index += 2;
|
|
66999
|
+
continue;
|
|
67000
|
+
}
|
|
67001
|
+
if (character === quote) quote = null;
|
|
67002
|
+
index += 1;
|
|
67003
|
+
continue;
|
|
67004
|
+
}
|
|
67005
|
+
if (character === "\"" || character === "'") {
|
|
67006
|
+
quote = character;
|
|
67007
|
+
result += character;
|
|
67008
|
+
index += 1;
|
|
67009
|
+
continue;
|
|
67010
|
+
}
|
|
67011
|
+
if (character === "/" && nextCharacter === "/") {
|
|
67012
|
+
while (index < contents.length && contents[index] !== "\n") {
|
|
67013
|
+
result += " ";
|
|
67014
|
+
index += 1;
|
|
67015
|
+
}
|
|
67016
|
+
continue;
|
|
67017
|
+
}
|
|
67018
|
+
if (character === "/" && nextCharacter === "*") {
|
|
67019
|
+
result += " ";
|
|
67020
|
+
index += 2;
|
|
67021
|
+
while (index < contents.length && !(contents[index] === "*" && contents[index + 1] === "/")) {
|
|
67022
|
+
result += contents[index] === "\n" ? "\n" : " ";
|
|
67023
|
+
index += 1;
|
|
67024
|
+
}
|
|
67025
|
+
if (index < contents.length) {
|
|
67026
|
+
result += " ";
|
|
67027
|
+
index += 2;
|
|
67028
|
+
}
|
|
67029
|
+
continue;
|
|
67030
|
+
}
|
|
67031
|
+
result += character;
|
|
67032
|
+
index += 1;
|
|
67033
|
+
}
|
|
67034
|
+
return result;
|
|
67035
|
+
};
|
|
67036
|
+
const maskGradleStrings = (contents) => {
|
|
67037
|
+
let result = "";
|
|
67038
|
+
let index = 0;
|
|
67039
|
+
let quote = null;
|
|
67040
|
+
while (index < contents.length) {
|
|
67041
|
+
const character = contents[index];
|
|
67042
|
+
if (quote === null) {
|
|
67043
|
+
if (character === "\"" || character === "'") {
|
|
67044
|
+
quote = character;
|
|
67045
|
+
result += " ";
|
|
67046
|
+
} else result += character;
|
|
67047
|
+
index += 1;
|
|
67048
|
+
continue;
|
|
67049
|
+
}
|
|
67050
|
+
if (character === "\\") {
|
|
67051
|
+
result += " ";
|
|
67052
|
+
index += 2;
|
|
67053
|
+
continue;
|
|
67054
|
+
}
|
|
67055
|
+
if (character === quote) quote = null;
|
|
67056
|
+
result += character === "\n" ? "\n" : " ";
|
|
67057
|
+
index += 1;
|
|
67058
|
+
}
|
|
67059
|
+
return result;
|
|
67060
|
+
};
|
|
67061
|
+
const findBlockContents = (contents, pattern) => {
|
|
67062
|
+
const blocks = [];
|
|
67063
|
+
pattern.lastIndex = 0;
|
|
67064
|
+
let match = pattern.exec(contents);
|
|
67065
|
+
while (match !== null) {
|
|
67066
|
+
const openingBraceIndex = pattern.lastIndex - 1;
|
|
67067
|
+
let depth = 1;
|
|
67068
|
+
let index = openingBraceIndex + 1;
|
|
67069
|
+
let quote = null;
|
|
67070
|
+
while (index < contents.length && depth > 0) {
|
|
67071
|
+
const character = contents[index];
|
|
67072
|
+
if (quote !== null) {
|
|
67073
|
+
if (character === "\\") {
|
|
67074
|
+
index += 2;
|
|
67075
|
+
continue;
|
|
67076
|
+
}
|
|
67077
|
+
if (character === quote) quote = null;
|
|
67078
|
+
} else if (character === "\"" || character === "'") quote = character;
|
|
67079
|
+
else if (character === "{") depth += 1;
|
|
67080
|
+
else if (character === "}") depth -= 1;
|
|
67081
|
+
index += 1;
|
|
67082
|
+
}
|
|
67083
|
+
if (depth === 0) blocks.push(contents.slice(openingBraceIndex + 1, index - 1));
|
|
67084
|
+
match = pattern.exec(contents);
|
|
67085
|
+
}
|
|
67086
|
+
return blocks;
|
|
67087
|
+
};
|
|
67088
|
+
const getFinalStaticBoolean = (contents, pattern) => {
|
|
67089
|
+
pattern.lastIndex = 0;
|
|
67090
|
+
let finalValue = null;
|
|
67091
|
+
let match = pattern.exec(contents);
|
|
67092
|
+
while (match !== null) {
|
|
67093
|
+
const staticValue = match[1]?.trim().replace(/\)$/, "").trim();
|
|
67094
|
+
if (staticValue === "true") finalValue = true;
|
|
67095
|
+
else if (staticValue === "false") finalValue = false;
|
|
67096
|
+
else finalValue = null;
|
|
67097
|
+
match = pattern.exec(contents);
|
|
67098
|
+
}
|
|
67099
|
+
return finalValue;
|
|
67100
|
+
};
|
|
67101
|
+
const getReleaseBlocks = (contents) => findBlockContents(contents, BUILD_TYPES_BLOCK_PATTERN).flatMap((buildTypesBlock) => RELEASE_BLOCK_PATTERNS.flatMap((pattern) => findBlockContents(buildTypesBlock, pattern)));
|
|
67102
|
+
const readAndroidReleaseShrinking = (rootDirectory) => {
|
|
67103
|
+
for (const buildFileName of ANDROID_APP_BUILD_FILE_NAMES) {
|
|
67104
|
+
const filePath = Path.posix.join("android", "app", buildFileName);
|
|
67105
|
+
const absoluteFilePath = Path.join(rootDirectory, filePath);
|
|
67106
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67107
|
+
let contents;
|
|
67108
|
+
try {
|
|
67109
|
+
contents = stripGradleComments(NFS.readFileSync(absoluteFilePath, "utf-8"));
|
|
67110
|
+
} catch {
|
|
67111
|
+
return null;
|
|
67112
|
+
}
|
|
67113
|
+
const releaseBlocks = getReleaseBlocks(maskGradleStrings(contents.replace(/(["'])release\1/g, RELEASE_STRING_MARKER)));
|
|
67114
|
+
const [releaseBlock] = releaseBlocks;
|
|
67115
|
+
if (releaseBlock === void 0 || releaseBlocks.length !== 1) return null;
|
|
67116
|
+
const hasDisabledMinification = getFinalStaticBoolean(releaseBlock, MINIFY_ASSIGNMENT_PATTERN) === false;
|
|
67117
|
+
const hasDisabledResourceShrinking = getFinalStaticBoolean(releaseBlock, SHRINK_RESOURCES_ASSIGNMENT_PATTERN) === false;
|
|
67118
|
+
if (!hasDisabledMinification && !hasDisabledResourceShrinking) return null;
|
|
67119
|
+
return {
|
|
67120
|
+
filePath,
|
|
67121
|
+
hasDisabledMinification,
|
|
67122
|
+
hasDisabledResourceShrinking
|
|
67123
|
+
};
|
|
67124
|
+
}
|
|
67125
|
+
return null;
|
|
67126
|
+
};
|
|
67127
|
+
const checkReactNativeAndroidReleaseShrinking = (rootDirectory) => {
|
|
67128
|
+
const releaseShrinking = readAndroidReleaseShrinking(rootDirectory);
|
|
67129
|
+
if (releaseShrinking === null) return [];
|
|
67130
|
+
const disabledFeatures = [releaseShrinking.hasDisabledMinification ? "code minification" : null, releaseShrinking.hasDisabledResourceShrinking ? "resource shrinking" : null].filter((feature) => feature !== null);
|
|
67131
|
+
return [buildReactNativeDiagnostic({
|
|
67132
|
+
filePath: releaseShrinking.filePath,
|
|
67133
|
+
rule: "rn-android-release-shrinking-disabled",
|
|
67134
|
+
category: "Performance",
|
|
67135
|
+
message: `The Android release build explicitly disables ${disabledFeatures.join(" and ")}, leaving unreachable code or resources in the shipped app.`,
|
|
67136
|
+
help: "Enable release code minification and resource shrinking, then test the optimized build and add keep rules for libraries that require them."
|
|
67137
|
+
})];
|
|
67138
|
+
};
|
|
67139
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
67140
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67141
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67142
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67143
|
+
return ts.ScriptKind.JS;
|
|
67144
|
+
};
|
|
67145
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
67146
|
+
let currentExpression = expression;
|
|
67147
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67148
|
+
return currentExpression;
|
|
67149
|
+
};
|
|
67150
|
+
const BABEL_CONFIG_FILE_NAMES$1 = [
|
|
67151
|
+
"babel.config.js",
|
|
67152
|
+
"babel.config.cjs",
|
|
67153
|
+
"babel.config.mjs",
|
|
67154
|
+
"babel.config.ts",
|
|
67155
|
+
"babel.config.cts",
|
|
67156
|
+
"babel.config.json",
|
|
67157
|
+
".babelrc",
|
|
67158
|
+
".babelrc.js",
|
|
67159
|
+
".babelrc.cjs",
|
|
67160
|
+
".babelrc.mjs",
|
|
67161
|
+
".babelrc.cts",
|
|
67162
|
+
".babelrc.json"
|
|
67163
|
+
];
|
|
67164
|
+
const readUnknownPluginNames = (value) => {
|
|
67165
|
+
if (!Array.isArray(value)) return null;
|
|
67166
|
+
const pluginNames = [];
|
|
67167
|
+
for (const entry of value) {
|
|
67168
|
+
if (typeof entry === "string") {
|
|
67169
|
+
pluginNames.push(entry);
|
|
67170
|
+
continue;
|
|
67171
|
+
}
|
|
67172
|
+
if (Array.isArray(entry) && typeof entry[0] === "string") {
|
|
67173
|
+
pluginNames.push(entry[0]);
|
|
67174
|
+
continue;
|
|
67175
|
+
}
|
|
67176
|
+
return null;
|
|
67177
|
+
}
|
|
67178
|
+
return pluginNames;
|
|
67179
|
+
};
|
|
67180
|
+
const getStaticPropertyName$1 = (propertyName) => {
|
|
67181
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) return propertyName.text;
|
|
67182
|
+
return null;
|
|
67183
|
+
};
|
|
67184
|
+
const getStaticPluginName = (expression) => {
|
|
67185
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
67186
|
+
if (ts.isStringLiteralLike(unwrappedExpression)) return unwrappedExpression.text;
|
|
67187
|
+
if (!ts.isArrayLiteralExpression(unwrappedExpression)) return null;
|
|
67188
|
+
const pluginExpression = unwrappedExpression.elements[0];
|
|
67189
|
+
return pluginExpression && ts.isStringLiteralLike(pluginExpression) ? pluginExpression.text : null;
|
|
67190
|
+
};
|
|
67191
|
+
const getPluginNamesFromObject = (objectExpression) => {
|
|
67192
|
+
if (objectExpression.properties.some((property) => ts.isSpreadAssignment(property))) return null;
|
|
67193
|
+
const pluginProperties = objectExpression.properties.filter((property) => ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "plugins");
|
|
67194
|
+
if (pluginProperties.length !== 1) return pluginProperties.length === 0 ? [] : null;
|
|
67195
|
+
const pluginListExpression = unwrapTypescriptExpression(pluginProperties[0].initializer);
|
|
67196
|
+
if (!ts.isArrayLiteralExpression(pluginListExpression)) return null;
|
|
67197
|
+
const pluginNames = [];
|
|
67198
|
+
for (const element of pluginListExpression.elements) {
|
|
67199
|
+
if (ts.isSpreadElement(element)) return null;
|
|
67200
|
+
const pluginName = getStaticPluginName(element);
|
|
67201
|
+
if (pluginName === null) return null;
|
|
67202
|
+
pluginNames.push(pluginName);
|
|
67203
|
+
}
|
|
67204
|
+
return pluginNames;
|
|
67205
|
+
};
|
|
67206
|
+
const isModuleExports = (expression) => ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "module" && expression.name.text === "exports" || ts.isElementAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "module" && expression.argumentExpression !== void 0 && ts.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === "exports";
|
|
67207
|
+
const getDirectExportedObject = (sourceFile) => {
|
|
67208
|
+
const exportedExpressions = [];
|
|
67209
|
+
for (const statement of sourceFile.statements) {
|
|
67210
|
+
if (ts.isExportAssignment(statement)) {
|
|
67211
|
+
exportedExpressions.push(statement.expression);
|
|
67212
|
+
continue;
|
|
67213
|
+
}
|
|
67214
|
+
if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && isModuleExports(statement.expression.left)) exportedExpressions.push(statement.expression.right);
|
|
67215
|
+
}
|
|
67216
|
+
if (exportedExpressions.length !== 1) return null;
|
|
67217
|
+
const exportedExpression = unwrapTypescriptExpression(exportedExpressions[0]);
|
|
67218
|
+
return ts.isObjectLiteralExpression(exportedExpression) ? exportedExpression : null;
|
|
67219
|
+
};
|
|
67220
|
+
const parseConfigPluginNames = (filePath, contents) => {
|
|
67221
|
+
try {
|
|
67222
|
+
const parsedJson = JSON.parse(contents);
|
|
67223
|
+
return isPlainObject(parsedJson) ? readUnknownPluginNames(parsedJson.plugins) : null;
|
|
67224
|
+
} catch {
|
|
67225
|
+
if (ts.transpileModule(contents, {
|
|
67226
|
+
fileName: filePath,
|
|
67227
|
+
reportDiagnostics: true,
|
|
67228
|
+
compilerOptions: { allowJs: true }
|
|
67229
|
+
}).diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) return null;
|
|
67230
|
+
const exportedObject = getDirectExportedObject(ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filePath)));
|
|
67231
|
+
return exportedObject === null ? null : getPluginNamesFromObject(exportedObject);
|
|
67232
|
+
}
|
|
67233
|
+
};
|
|
67234
|
+
const readStaticBabelPluginNames = (rootDirectory) => {
|
|
67235
|
+
for (const fileName of BABEL_CONFIG_FILE_NAMES$1) {
|
|
67236
|
+
const absoluteFilePath = Path.join(rootDirectory, fileName);
|
|
67237
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67238
|
+
try {
|
|
67239
|
+
return {
|
|
67240
|
+
filePath: fileName,
|
|
67241
|
+
pluginNames: parseConfigPluginNames(fileName, NFS.readFileSync(absoluteFilePath, "utf-8"))
|
|
67242
|
+
};
|
|
67243
|
+
} catch {
|
|
67244
|
+
return {
|
|
67245
|
+
filePath: fileName,
|
|
67246
|
+
pluginNames: null
|
|
67247
|
+
};
|
|
67248
|
+
}
|
|
67249
|
+
}
|
|
67250
|
+
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
67251
|
+
if (!isPlainObject(packageJson.babel)) return null;
|
|
67252
|
+
return {
|
|
67253
|
+
filePath: "package.json",
|
|
67254
|
+
pluginNames: readUnknownPluginNames(packageJson.babel.plugins)
|
|
67255
|
+
};
|
|
67256
|
+
};
|
|
67257
|
+
const REACT_COMPILER_PLUGIN_NAMES = new Set(["babel-plugin-react-compiler", "react-compiler"]);
|
|
67258
|
+
const REANIMATED_PLUGIN_NAME = "react-native-reanimated/plugin";
|
|
67259
|
+
const WORKLETS_PLUGIN_NAME = "react-native-worklets/plugin";
|
|
67260
|
+
const checkReactCompilerPluginOrder = (filePath, pluginNames) => {
|
|
67261
|
+
if (pluginNames.findIndex((pluginName) => REACT_COMPILER_PLUGIN_NAMES.has(pluginName)) <= 0) return [];
|
|
67262
|
+
return [buildReactNativeDiagnostic({
|
|
67263
|
+
filePath,
|
|
67264
|
+
rule: "rn-react-compiler-plugin-first",
|
|
67265
|
+
category: "Performance",
|
|
67266
|
+
message: "The React Compiler Babel plugin is not first, so an earlier transform can change component or hook code before the compiler analyzes it.",
|
|
67267
|
+
help: "Move `babel-plugin-react-compiler` to the first entry in the Babel `plugins` array."
|
|
67268
|
+
})];
|
|
67269
|
+
};
|
|
67270
|
+
const checkReanimatedPluginOrder = (project, filePath, pluginNames) => {
|
|
67271
|
+
const reanimatedMajorVersion = project.reanimatedVersion === null || project.reanimatedVersion === void 0 ? null : getLowestDependencyMajor(project.reanimatedVersion);
|
|
67272
|
+
if (reanimatedMajorVersion === null || reanimatedMajorVersion < 4) return [];
|
|
67273
|
+
const diagnostics = [];
|
|
67274
|
+
if (pluginNames.includes(REANIMATED_PLUGIN_NAME)) diagnostics.push(buildReactNativeDiagnostic({
|
|
67275
|
+
filePath,
|
|
67276
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67277
|
+
category: "Performance",
|
|
67278
|
+
message: "This Reanimated 4 project still uses `react-native-reanimated/plugin`, which was renamed to the Worklets Babel plugin.",
|
|
67279
|
+
help: "Replace it with `react-native-worklets/plugin` and keep that plugin last in the Babel `plugins` array."
|
|
67280
|
+
}));
|
|
67281
|
+
const workletsPluginIndex = pluginNames.lastIndexOf(WORKLETS_PLUGIN_NAME);
|
|
67282
|
+
if (workletsPluginIndex !== -1 && workletsPluginIndex !== pluginNames.length - 1) diagnostics.push(buildReactNativeDiagnostic({
|
|
67283
|
+
filePath,
|
|
67284
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67285
|
+
category: "Performance",
|
|
67286
|
+
message: "`react-native-worklets/plugin` is not last in the Babel `plugins` array, so later transforms can prevent worklets from being compiled correctly.",
|
|
67287
|
+
help: "Move `react-native-worklets/plugin` to the final entry in the Babel `plugins` array."
|
|
67288
|
+
}));
|
|
67289
|
+
return diagnostics;
|
|
67290
|
+
};
|
|
67291
|
+
const checkReactNativeBabelPluginOrder = (rootDirectory, project) => {
|
|
67292
|
+
const babelPlugins = readStaticBabelPluginNames(rootDirectory);
|
|
67293
|
+
if (babelPlugins === null || babelPlugins.pluginNames === null) return [];
|
|
67294
|
+
return [...checkReactCompilerPluginOrder(babelPlugins.filePath, babelPlugins.pluginNames), ...checkReanimatedPluginOrder(project, babelPlugins.filePath, babelPlugins.pluginNames)];
|
|
67295
|
+
};
|
|
66980
67296
|
const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
|
|
66981
67297
|
const isBuilderBobLibrary = (packageJson) => {
|
|
66982
67298
|
const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
|
|
@@ -67053,7 +67369,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
67053
67369
|
const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
|
|
67054
67370
|
const checkReactNativeProject = (rootDirectory, project) => {
|
|
67055
67371
|
if (!isReactNativeProject(project)) return [];
|
|
67056
|
-
return [
|
|
67372
|
+
return [
|
|
67373
|
+
...checkReactNativeMetroBabelPreset(rootDirectory),
|
|
67374
|
+
...checkReactNativeBabelPluginOrder(rootDirectory, project),
|
|
67375
|
+
...checkReactNativeAndroidReleaseShrinking(rootDirectory),
|
|
67376
|
+
...checkReactNativeLibraryDependencies(rootDirectory)
|
|
67377
|
+
];
|
|
67057
67378
|
};
|
|
67058
67379
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
67059
67380
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
@@ -67204,17 +67525,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
67204
67525
|
}
|
|
67205
67526
|
return diagnostics;
|
|
67206
67527
|
};
|
|
67207
|
-
const getTypescriptScriptKind = (fileName) => {
|
|
67208
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67209
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67210
|
-
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67211
|
-
return ts.ScriptKind.JS;
|
|
67212
|
-
};
|
|
67213
|
-
const unwrapTypescriptExpression = (expression) => {
|
|
67214
|
-
let currentExpression = expression;
|
|
67215
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67216
|
-
return currentExpression;
|
|
67217
|
-
};
|
|
67218
67528
|
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
67219
67529
|
isAnimationFunction: false,
|
|
67220
67530
|
isMotionComponent: false,
|
|
@@ -67770,7 +68080,11 @@ const buildCapabilities = (project) => {
|
|
|
67770
68080
|
capabilities.add(project.framework);
|
|
67771
68081
|
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
67772
68082
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
67773
|
-
if (project.expoVersion !== null)
|
|
68083
|
+
if (project.expoVersion !== null) {
|
|
68084
|
+
capabilities.add("expo");
|
|
68085
|
+
const expoSdkMajorVersion = getLowestDependencyMajor(project.expoVersion);
|
|
68086
|
+
if (expoSdkMajorVersion !== null && expoSdkMajorVersion >= 54) capabilities.add("expo:54");
|
|
68087
|
+
}
|
|
67774
68088
|
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
67775
68089
|
if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
|
|
67776
68090
|
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
@@ -67834,6 +68148,11 @@ const buildCapabilities = (project) => {
|
|
|
67834
68148
|
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
67835
68149
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
67836
68150
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
68151
|
+
if (project.reanimatedVersion !== void 0 && project.reanimatedVersion !== null) {
|
|
68152
|
+
capabilities.add("reanimated");
|
|
68153
|
+
const reanimatedMajorVersion = getLowestDependencyMajor(project.reanimatedVersion);
|
|
68154
|
+
if (reanimatedMajorVersion !== null && reanimatedMajorVersion >= 4) capabilities.add("reanimated:4");
|
|
68155
|
+
}
|
|
67837
68156
|
if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
|
|
67838
68157
|
if (project.styledComponentsVersion) {
|
|
67839
68158
|
capabilities.add("styled-components");
|
|
@@ -70276,6 +70595,12 @@ const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
|
70276
70595
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
70277
70596
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
70278
70597
|
};
|
|
70598
|
+
const columnOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
70599
|
+
const boundedOffset = Math.min(Math.max(utf8Offset, 0), sourceBuffer.length);
|
|
70600
|
+
let lineStartOffset = boundedOffset;
|
|
70601
|
+
while (lineStartOffset > 0 && sourceBuffer[lineStartOffset - 1] !== 10) lineStartOffset--;
|
|
70602
|
+
return sourceBuffer.subarray(lineStartOffset, boundedOffset).toString("utf8").length + 1;
|
|
70603
|
+
};
|
|
70279
70604
|
const REDACTED_PLACEHOLDER = "<redacted>";
|
|
70280
70605
|
const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
|
|
70281
70606
|
const KNOWN_SECRET_RULES = [
|
|
@@ -70991,7 +71316,7 @@ const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORI
|
|
|
70991
71316
|
* with empty text still carry a useful jump target, so they're kept
|
|
70992
71317
|
* with a neutral message.
|
|
70993
71318
|
*/
|
|
70994
|
-
const buildRelatedLocations = (labels, filePath) => {
|
|
71319
|
+
const buildRelatedLocations = (labels, filePath, sourceBuffer) => {
|
|
70995
71320
|
if (labels.length <= 1) return [];
|
|
70996
71321
|
const related = [];
|
|
70997
71322
|
for (let labelIndex = 1; labelIndex < labels.length; labelIndex++) {
|
|
@@ -70999,8 +71324,8 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
70999
71324
|
if (!label?.span) continue;
|
|
71000
71325
|
related.push({
|
|
71001
71326
|
filePath,
|
|
71002
|
-
line: label.span.line ?? 0,
|
|
71003
|
-
column: label.span.column ?? 0,
|
|
71327
|
+
line: sourceBuffer === null ? label.span.line ?? 0 : lineOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
71328
|
+
column: sourceBuffer === null ? label.span.column ?? 0 : columnOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
71004
71329
|
offset: label.span.offset,
|
|
71005
71330
|
length: label.span.length,
|
|
71006
71331
|
message: label.label ?? ""
|
|
@@ -71020,7 +71345,7 @@ const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnos
|
|
|
71020
71345
|
* to the first `{`. Raises `OxlintOutputUnparseable` when the
|
|
71021
71346
|
* stdout isn't valid JSON or doesn't carry a `diagnostics` array.
|
|
71022
71347
|
*/
|
|
71023
|
-
const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
71348
|
+
const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath) => {
|
|
71024
71349
|
if (!stdout) return [];
|
|
71025
71350
|
const jsonStart = stdout.indexOf("{");
|
|
71026
71351
|
const sanitizedStdout = jsonStart > 0 ? stdout.slice(jsonStart) : stdout;
|
|
@@ -71037,8 +71362,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71037
71362
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
71038
71363
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
71039
71364
|
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
71365
|
+
const resolveMappedSourceFilename = (filename) => sourcePathByLintPath?.get(Path.normalize(resolveAbsolutePath(filename)));
|
|
71366
|
+
const resolveSourceFilename = (filename) => resolveMappedSourceFilename(filename) ?? filename;
|
|
71040
71367
|
const readSourceBuffer = (filename) => {
|
|
71041
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71368
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71042
71369
|
const cached = sourceBufferCache.get(absolutePath);
|
|
71043
71370
|
if (cached !== void 0) return cached;
|
|
71044
71371
|
let sourceBuffer;
|
|
@@ -71051,7 +71378,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71051
71378
|
return sourceBuffer;
|
|
71052
71379
|
};
|
|
71053
71380
|
const readSourceLines = (filename) => {
|
|
71054
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71381
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71055
71382
|
const cached = sourceLinesCache.get(absolutePath);
|
|
71056
71383
|
if (cached !== void 0) return cached;
|
|
71057
71384
|
const sourceBuffer = readSourceBuffer(filename);
|
|
@@ -71060,27 +71387,30 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71060
71387
|
return sourceLines;
|
|
71061
71388
|
};
|
|
71062
71389
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
71063
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71390
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71064
71391
|
const cached = minifiedFileCache.get(absolutePath);
|
|
71065
71392
|
if (cached !== void 0) return cached;
|
|
71066
71393
|
const minified = isMinifiedSource(absolutePath);
|
|
71067
71394
|
minifiedFileCache.set(absolutePath, minified);
|
|
71068
71395
|
return minified;
|
|
71069
71396
|
};
|
|
71070
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
71397
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(resolveSourceFilename(diagnostic.filename)) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
71071
71398
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
71072
71399
|
const primaryLabel = diagnostic.labels[0];
|
|
71073
71400
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
71074
|
-
const
|
|
71401
|
+
const mappedSourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
71402
|
+
const normalizedFilePath = (mappedSourceFilename ?? diagnostic.filename).replaceAll("\\", "/");
|
|
71075
71403
|
const primarySpan = primaryLabel?.span;
|
|
71076
71404
|
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
71077
|
-
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
71405
|
+
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath, mappedSourceFilename === void 0 ? null : sourceBuffer);
|
|
71078
71406
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
71079
71407
|
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
71080
71408
|
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
71081
|
-
const
|
|
71409
|
+
const primaryLineNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.line ?? 0;
|
|
71410
|
+
const primaryColumnNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? columnOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.column ?? 0;
|
|
71411
|
+
const primaryLineIndex = primarySpan ? primaryLineNumber - 1 : -1;
|
|
71082
71412
|
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
71083
|
-
const primaryColumnIndex = primarySpan ?
|
|
71413
|
+
const primaryColumnIndex = primarySpan ? primaryColumnNumber - 1 : -1;
|
|
71084
71414
|
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
71085
71415
|
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
71086
71416
|
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
@@ -71093,8 +71423,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71093
71423
|
message: cleaned.message,
|
|
71094
71424
|
help: cleaned.help,
|
|
71095
71425
|
url: diagnostic.url,
|
|
71096
|
-
line:
|
|
71097
|
-
column:
|
|
71426
|
+
line: primaryLineNumber,
|
|
71427
|
+
column: primaryColumnNumber,
|
|
71098
71428
|
...primarySpan ? {
|
|
71099
71429
|
offset: primarySpan.offset,
|
|
71100
71430
|
length: primarySpan.length,
|
|
@@ -71282,7 +71612,11 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
71282
71612
|
* loop with a slimmer config in that case.
|
|
71283
71613
|
*/
|
|
71284
71614
|
const spawnLintBatches = async (input) => {
|
|
71285
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
71615
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
71616
|
+
const resolveSourcePath = (filePath) => {
|
|
71617
|
+
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
71618
|
+
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
71619
|
+
};
|
|
71286
71620
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
71287
71621
|
let rescueDeadlineEpochMs;
|
|
71288
71622
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
@@ -71302,7 +71636,7 @@ const spawnLintBatches = async (input) => {
|
|
|
71302
71636
|
}
|
|
71303
71637
|
const batchArgs = [...baseArgs, ...batch];
|
|
71304
71638
|
try {
|
|
71305
|
-
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
71639
|
+
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory, sourcePathByLintPath);
|
|
71306
71640
|
} catch (error) {
|
|
71307
71641
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
71308
71642
|
if (isPastDeadline()) {
|
|
@@ -71408,10 +71742,12 @@ const spawnLintBatches = async (input) => {
|
|
|
71408
71742
|
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
71409
71743
|
};
|
|
71410
71744
|
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
71411
|
-
|
|
71412
|
-
|
|
71745
|
+
const droppedSourceFiles = [...new Set(droppedFiles.map(resolveSourcePath))];
|
|
71746
|
+
const deadlineSkippedSourceFiles = [...new Set(outcome.deadlineSkippedFiles.map(resolveSourcePath))];
|
|
71747
|
+
reportSkippedFiles(droppedSourceFiles, (fileListPreview) => `${droppedSourceFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
71748
|
+
reportSkippedFiles(deadlineSkippedSourceFiles, (fileListPreview) => `${deadlineSkippedSourceFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
71413
71749
|
const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
|
|
71414
|
-
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
|
|
71750
|
+
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)).map(resolveSourcePath))].sort());
|
|
71415
71751
|
return dedupeDiagnostics(diagnostics);
|
|
71416
71752
|
};
|
|
71417
71753
|
let didValidate = false;
|
|
@@ -71510,6 +71846,266 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
71510
71846
|
}
|
|
71511
71847
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
71512
71848
|
};
|
|
71849
|
+
const THREE_MODULE_IMPORT_PATTERN = /\b(?:from\s*|import\s*(?:\(\s*)?|require\s*\(\s*)["']three(?:\/[^"']*)?["']/;
|
|
71850
|
+
const containsThreeModuleImport = (sourceText) => THREE_MODULE_IMPORT_PATTERN.test(sourceText);
|
|
71851
|
+
const COMMENT_OPEN = "<!--";
|
|
71852
|
+
const COMMENT_CLOSE = "-->";
|
|
71853
|
+
const FRONTMATTER_FENCE = "---";
|
|
71854
|
+
const SCRIPT_TAG_NAME = "script";
|
|
71855
|
+
const SCRIPT_OPEN = `<${SCRIPT_TAG_NAME}`;
|
|
71856
|
+
const SCRIPT_CLOSE = `</${SCRIPT_TAG_NAME}`;
|
|
71857
|
+
const TEMPLATE_TAG_NAME = "template";
|
|
71858
|
+
const TEMPLATE_CLOSE = `</${TEMPLATE_TAG_NAME}`;
|
|
71859
|
+
const JAVASCRIPT_MIME_TYPES = new Set([
|
|
71860
|
+
"application/ecmascript",
|
|
71861
|
+
"application/javascript",
|
|
71862
|
+
"application/x-ecmascript",
|
|
71863
|
+
"application/x-javascript",
|
|
71864
|
+
"text/ecmascript",
|
|
71865
|
+
"text/javascript",
|
|
71866
|
+
"text/javascript1.0",
|
|
71867
|
+
"text/javascript1.1",
|
|
71868
|
+
"text/javascript1.2",
|
|
71869
|
+
"text/javascript1.3",
|
|
71870
|
+
"text/javascript1.4",
|
|
71871
|
+
"text/javascript1.5",
|
|
71872
|
+
"text/jscript",
|
|
71873
|
+
"text/livescript",
|
|
71874
|
+
"text/x-ecmascript",
|
|
71875
|
+
"text/x-javascript"
|
|
71876
|
+
]);
|
|
71877
|
+
const RAW_TEXT_TAG_NAMES = new Set([
|
|
71878
|
+
"iframe",
|
|
71879
|
+
"noembed",
|
|
71880
|
+
"noframes",
|
|
71881
|
+
"noscript",
|
|
71882
|
+
"style",
|
|
71883
|
+
"textarea",
|
|
71884
|
+
"title",
|
|
71885
|
+
"xmp"
|
|
71886
|
+
]);
|
|
71887
|
+
const PLAINTEXT_TAG_NAME = "plaintext";
|
|
71888
|
+
const TAG_OPEN_UTF8_BYTE = "<".charCodeAt(0);
|
|
71889
|
+
const TAG_CLOSE_UTF8_BYTE = ">".charCodeAt(0);
|
|
71890
|
+
const SINGLE_QUOTE_UTF8_BYTE = "'".charCodeAt(0);
|
|
71891
|
+
const DOUBLE_QUOTE_UTF8_BYTE = "\"".charCodeAt(0);
|
|
71892
|
+
const ATTRIBUTE_ASSIGNMENT_UTF8_BYTE = "=".charCodeAt(0);
|
|
71893
|
+
const TAG_SELF_CLOSE_UTF8_BYTE = "/".charCodeAt(0);
|
|
71894
|
+
const TAB_UTF8_BYTE = " ".charCodeAt(0);
|
|
71895
|
+
const FORM_FEED_UTF8_BYTE = "\f".charCodeAt(0);
|
|
71896
|
+
const isHtmlWhitespace = (byte) => byte === TAB_UTF8_BYTE || byte === 10 || byte === FORM_FEED_UTF8_BYTE || byte === 13 || byte === 32;
|
|
71897
|
+
const matchesAsciiCaseInsensitive = (sourceBuffer, startByte, expected) => {
|
|
71898
|
+
if (startByte + expected.length > sourceBuffer.length) return false;
|
|
71899
|
+
for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex++) {
|
|
71900
|
+
const sourceByte = sourceBuffer[startByte + expectedIndex];
|
|
71901
|
+
if (sourceByte === void 0 || String.fromCharCode(sourceByte).toLowerCase() !== expected[expectedIndex]) return false;
|
|
71902
|
+
}
|
|
71903
|
+
return true;
|
|
71904
|
+
};
|
|
71905
|
+
const isTagBoundary = (byte) => byte === TAG_CLOSE_UTF8_BYTE || byte === TAG_SELF_CLOSE_UTF8_BYTE || isHtmlWhitespace(byte);
|
|
71906
|
+
const isPotentialTagStart = (byte) => {
|
|
71907
|
+
if (byte === void 0) return false;
|
|
71908
|
+
return /[A-Za-z!/?]/.test(String.fromCharCode(byte));
|
|
71909
|
+
};
|
|
71910
|
+
const findLineEnd = (sourceBuffer, lineStartByte) => {
|
|
71911
|
+
let byteIndex = lineStartByte;
|
|
71912
|
+
while (byteIndex < sourceBuffer.length && sourceBuffer[byteIndex] !== 10 && sourceBuffer[byteIndex] !== 13) byteIndex++;
|
|
71913
|
+
return byteIndex;
|
|
71914
|
+
};
|
|
71915
|
+
const findNextLineStart = (sourceBuffer, lineStartByte) => {
|
|
71916
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
71917
|
+
if (sourceBuffer[lineEndByte] === 13 && sourceBuffer[lineEndByte + 1] === 10) return lineEndByte + 2;
|
|
71918
|
+
return Math.min(lineEndByte + 1, sourceBuffer.length);
|
|
71919
|
+
};
|
|
71920
|
+
const isFrontmatterFenceLine = (sourceBuffer, lineStartByte) => {
|
|
71921
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, lineStartByte, FRONTMATTER_FENCE)) return false;
|
|
71922
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
71923
|
+
for (let byteIndex = lineStartByte + 3; byteIndex < lineEndByte; byteIndex++) if (!isHtmlWhitespace(sourceBuffer[byteIndex])) return false;
|
|
71924
|
+
return true;
|
|
71925
|
+
};
|
|
71926
|
+
const findFrontmatterEnd = (sourceBuffer) => {
|
|
71927
|
+
if (!isFrontmatterFenceLine(sourceBuffer, 0)) return 0;
|
|
71928
|
+
const openingFenceEndByte = findLineEnd(sourceBuffer, 0);
|
|
71929
|
+
let lineStartByte = findNextLineStart(sourceBuffer, 0);
|
|
71930
|
+
while (lineStartByte < sourceBuffer.length) {
|
|
71931
|
+
if (isFrontmatterFenceLine(sourceBuffer, lineStartByte)) return findLineEnd(sourceBuffer, lineStartByte);
|
|
71932
|
+
lineStartByte = findNextLineStart(sourceBuffer, lineStartByte);
|
|
71933
|
+
}
|
|
71934
|
+
return openingFenceEndByte;
|
|
71935
|
+
};
|
|
71936
|
+
const maskByteRange = (maskedBuffer, startByte, endByte) => {
|
|
71937
|
+
for (let byteIndex = startByte; byteIndex < endByte; byteIndex++) {
|
|
71938
|
+
const byte = maskedBuffer[byteIndex];
|
|
71939
|
+
if (byte !== 10 && byte !== 13) maskedBuffer[byteIndex] = 32;
|
|
71940
|
+
}
|
|
71941
|
+
};
|
|
71942
|
+
const readStartTagName = (sourceBuffer, tagStartByte) => {
|
|
71943
|
+
const firstNameByte = sourceBuffer[tagStartByte + 1];
|
|
71944
|
+
if (firstNameByte === void 0 || !/[A-Za-z]/.test(String.fromCharCode(firstNameByte))) return null;
|
|
71945
|
+
let nameEndByte = tagStartByte + 2;
|
|
71946
|
+
while (nameEndByte < sourceBuffer.length && /[A-Za-z0-9:-]/.test(String.fromCharCode(sourceBuffer[nameEndByte] ?? 32))) nameEndByte++;
|
|
71947
|
+
return sourceBuffer.subarray(tagStartByte + 1, nameEndByte).toString("ascii").toLowerCase();
|
|
71948
|
+
};
|
|
71949
|
+
const findTagEnd = (sourceBuffer, startByte) => {
|
|
71950
|
+
let quoteByte = null;
|
|
71951
|
+
for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) {
|
|
71952
|
+
const byte = sourceBuffer[byteIndex];
|
|
71953
|
+
if (quoteByte !== null) {
|
|
71954
|
+
if (byte === quoteByte) quoteByte = null;
|
|
71955
|
+
continue;
|
|
71956
|
+
}
|
|
71957
|
+
if (byte === SINGLE_QUOTE_UTF8_BYTE || byte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
71958
|
+
quoteByte = byte;
|
|
71959
|
+
continue;
|
|
71960
|
+
}
|
|
71961
|
+
if (byte === TAG_CLOSE_UTF8_BYTE) return byteIndex;
|
|
71962
|
+
}
|
|
71963
|
+
return sourceBuffer.length;
|
|
71964
|
+
};
|
|
71965
|
+
const parseScriptAttributes = (sourceBuffer, attributesStartByte, tagEndByte) => {
|
|
71966
|
+
let byteIndex = attributesStartByte;
|
|
71967
|
+
let hasSource = false;
|
|
71968
|
+
let language = null;
|
|
71969
|
+
let scriptType = null;
|
|
71970
|
+
while (byteIndex < tagEndByte) {
|
|
71971
|
+
while (byteIndex < tagEndByte && (isHtmlWhitespace(sourceBuffer[byteIndex]) || sourceBuffer[byteIndex] === TAG_SELF_CLOSE_UTF8_BYTE)) byteIndex++;
|
|
71972
|
+
const attributeNameStartByte = byteIndex;
|
|
71973
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== ATTRIBUTE_ASSIGNMENT_UTF8_BYTE && sourceBuffer[byteIndex] !== TAG_SELF_CLOSE_UTF8_BYTE) byteIndex++;
|
|
71974
|
+
const attributeName = sourceBuffer.subarray(attributeNameStartByte, byteIndex).toString("ascii").toLowerCase();
|
|
71975
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
71976
|
+
let attributeValue = "";
|
|
71977
|
+
if (sourceBuffer[byteIndex] === ATTRIBUTE_ASSIGNMENT_UTF8_BYTE) {
|
|
71978
|
+
byteIndex++;
|
|
71979
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
71980
|
+
const quoteByte = sourceBuffer[byteIndex];
|
|
71981
|
+
if (quoteByte === SINGLE_QUOTE_UTF8_BYTE || quoteByte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
71982
|
+
const valueStartByte = byteIndex + 1;
|
|
71983
|
+
byteIndex = valueStartByte;
|
|
71984
|
+
while (byteIndex < tagEndByte && sourceBuffer[byteIndex] !== quoteByte) byteIndex++;
|
|
71985
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
71986
|
+
if (byteIndex < tagEndByte) byteIndex++;
|
|
71987
|
+
} else {
|
|
71988
|
+
const valueStartByte = byteIndex;
|
|
71989
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== TAG_CLOSE_UTF8_BYTE) byteIndex++;
|
|
71990
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
71991
|
+
}
|
|
71992
|
+
}
|
|
71993
|
+
if (attributeName === "src") hasSource = true;
|
|
71994
|
+
else if (attributeName === "type" && scriptType === null) scriptType = attributeValue;
|
|
71995
|
+
else if (attributeName === "language" && language === null) language = attributeValue;
|
|
71996
|
+
}
|
|
71997
|
+
return {
|
|
71998
|
+
hasSource,
|
|
71999
|
+
language,
|
|
72000
|
+
type: scriptType
|
|
72001
|
+
};
|
|
72002
|
+
};
|
|
72003
|
+
const isExecutableScript = (attributes) => {
|
|
72004
|
+
if (attributes.hasSource) return false;
|
|
72005
|
+
const type = attributes.type?.trim().toLowerCase() ?? "";
|
|
72006
|
+
if (type !== "") {
|
|
72007
|
+
const mimeType = type.split(";")[0]?.trim() ?? "";
|
|
72008
|
+
return mimeType === "module" || JAVASCRIPT_MIME_TYPES.has(mimeType);
|
|
72009
|
+
}
|
|
72010
|
+
const language = attributes.language?.trim().toLowerCase() ?? "";
|
|
72011
|
+
return language === "" || JAVASCRIPT_MIME_TYPES.has(`text/${language}`);
|
|
72012
|
+
};
|
|
72013
|
+
const findElementClose = (sourceBuffer, startByte, tagName) => {
|
|
72014
|
+
const closeTagStart = `</${tagName}`;
|
|
72015
|
+
for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) if (sourceBuffer[byteIndex] === TAG_OPEN_UTF8_BYTE && matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, closeTagStart) && isTagBoundary(sourceBuffer[byteIndex + closeTagStart.length])) return byteIndex;
|
|
72016
|
+
return sourceBuffer.length;
|
|
72017
|
+
};
|
|
72018
|
+
const prepareHtmlScriptSource = (sourceBuffer) => {
|
|
72019
|
+
const frontmatterEndByte = findFrontmatterEnd(sourceBuffer);
|
|
72020
|
+
let maskedBuffer = frontmatterEndByte > 0 ? Buffer.from(sourceBuffer) : null;
|
|
72021
|
+
if (maskedBuffer !== null) maskByteRange(maskedBuffer, 0, frontmatterEndByte);
|
|
72022
|
+
const executableScriptBodies = [];
|
|
72023
|
+
let byteIndex = frontmatterEndByte;
|
|
72024
|
+
let templateDepth = 0;
|
|
72025
|
+
while (byteIndex < sourceBuffer.length) {
|
|
72026
|
+
if (sourceBuffer[byteIndex] !== TAG_OPEN_UTF8_BYTE) {
|
|
72027
|
+
byteIndex++;
|
|
72028
|
+
continue;
|
|
72029
|
+
}
|
|
72030
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, COMMENT_OPEN)) {
|
|
72031
|
+
const commentEndByte = sourceBuffer.indexOf(COMMENT_CLOSE, byteIndex + 4);
|
|
72032
|
+
byteIndex = commentEndByte === -1 ? sourceBuffer.length : commentEndByte + 3;
|
|
72033
|
+
continue;
|
|
72034
|
+
}
|
|
72035
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, TEMPLATE_CLOSE) && isTagBoundary(sourceBuffer[byteIndex + TEMPLATE_CLOSE.length])) {
|
|
72036
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + TEMPLATE_CLOSE.length);
|
|
72037
|
+
if (templateDepth > 0) templateDepth--;
|
|
72038
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
72039
|
+
continue;
|
|
72040
|
+
}
|
|
72041
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, SCRIPT_OPEN) || !isTagBoundary(sourceBuffer[byteIndex + SCRIPT_OPEN.length])) {
|
|
72042
|
+
if (!isPotentialTagStart(sourceBuffer[byteIndex + 1])) {
|
|
72043
|
+
byteIndex++;
|
|
72044
|
+
continue;
|
|
72045
|
+
}
|
|
72046
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + 1);
|
|
72047
|
+
const tagName = readStartTagName(sourceBuffer, byteIndex);
|
|
72048
|
+
if (tagName === PLAINTEXT_TAG_NAME) break;
|
|
72049
|
+
if (tagName === TEMPLATE_TAG_NAME) templateDepth++;
|
|
72050
|
+
if (tagName !== null && RAW_TEXT_TAG_NAMES.has(tagName)) {
|
|
72051
|
+
const closeTagStartByte = findElementClose(sourceBuffer, tagEndByte + 1, tagName);
|
|
72052
|
+
if (closeTagStartByte === sourceBuffer.length) break;
|
|
72053
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, closeTagStartByte + tagName.length + 2);
|
|
72054
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
72055
|
+
continue;
|
|
72056
|
+
}
|
|
72057
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
72058
|
+
continue;
|
|
72059
|
+
}
|
|
72060
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + SCRIPT_OPEN.length);
|
|
72061
|
+
if (tagEndByte === sourceBuffer.length) break;
|
|
72062
|
+
const bodyStartByte = tagEndByte + 1;
|
|
72063
|
+
const bodyEndByte = findElementClose(sourceBuffer, bodyStartByte, SCRIPT_TAG_NAME);
|
|
72064
|
+
const attributes = parseScriptAttributes(sourceBuffer, byteIndex + SCRIPT_OPEN.length, tagEndByte);
|
|
72065
|
+
if (attributes.hasSource || templateDepth > 0) {
|
|
72066
|
+
maskedBuffer ??= Buffer.from(sourceBuffer);
|
|
72067
|
+
maskByteRange(maskedBuffer, bodyStartByte, bodyEndByte);
|
|
72068
|
+
} else if (isExecutableScript(attributes)) executableScriptBodies.push(sourceBuffer.subarray(bodyStartByte, bodyEndByte));
|
|
72069
|
+
if (bodyEndByte === sourceBuffer.length) break;
|
|
72070
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, bodyEndByte + SCRIPT_CLOSE.length);
|
|
72071
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
72072
|
+
}
|
|
72073
|
+
return {
|
|
72074
|
+
executableScriptBodies,
|
|
72075
|
+
lintBuffer: maskedBuffer ?? sourceBuffer
|
|
72076
|
+
};
|
|
72077
|
+
};
|
|
72078
|
+
const prepareHtmlLintSources = (rootDirectory, temporaryDirectory, candidateFiles) => {
|
|
72079
|
+
const lintFiles = [];
|
|
72080
|
+
const sourcePathByLintPath = /* @__PURE__ */ new Map();
|
|
72081
|
+
const sizeByLintPath = /* @__PURE__ */ new Map();
|
|
72082
|
+
const htmlSourcesDirectory = Path.join(temporaryDirectory, "html-sources");
|
|
72083
|
+
let htmlFileIndex = 0;
|
|
72084
|
+
let hasThreeModuleImport = false;
|
|
72085
|
+
for (const candidateFile of candidateFiles) {
|
|
72086
|
+
if (!HTML_FILE_PATTERN.test(candidateFile)) {
|
|
72087
|
+
lintFiles.push(candidateFile);
|
|
72088
|
+
continue;
|
|
72089
|
+
}
|
|
72090
|
+
const absoluteSourcePath = Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile);
|
|
72091
|
+
const sourceBuffer = NFS.readFileSync(absoluteSourcePath);
|
|
72092
|
+
const { executableScriptBodies, lintBuffer } = prepareHtmlScriptSource(sourceBuffer);
|
|
72093
|
+
if (htmlFileIndex === 0) NFS.mkdirSync(htmlSourcesDirectory, { recursive: true });
|
|
72094
|
+
const lintPath = Path.join(htmlSourcesDirectory, `${htmlFileIndex}.astro`);
|
|
72095
|
+
NFS.writeFileSync(lintPath, lintBuffer);
|
|
72096
|
+
lintFiles.push(lintPath);
|
|
72097
|
+
sourcePathByLintPath.set(Path.resolve(lintPath), candidateFile);
|
|
72098
|
+
sizeByLintPath.set(lintPath, sourceBuffer.length);
|
|
72099
|
+
hasThreeModuleImport ||= executableScriptBodies.some((scriptBody) => containsThreeModuleImport(scriptBody.toString("utf8")));
|
|
72100
|
+
htmlFileIndex++;
|
|
72101
|
+
}
|
|
72102
|
+
return {
|
|
72103
|
+
lintFiles,
|
|
72104
|
+
sourcePathByLintPath,
|
|
72105
|
+
sizeByLintPath,
|
|
72106
|
+
hasThreeModuleImport
|
|
72107
|
+
};
|
|
72108
|
+
};
|
|
71513
72109
|
/**
|
|
71514
72110
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
71515
72111
|
* the runner: once for the primary scan, once for the
|
|
@@ -71683,21 +72279,6 @@ const runOxlint = async (options) => {
|
|
|
71683
72279
|
const pluginPath = resolvePluginPath();
|
|
71684
72280
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
71685
72281
|
const userPlugins = includedTags.size > 0 ? [] : resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
71686
|
-
const buildConfig = (overrides) => createOxlintConfig({
|
|
71687
|
-
pluginPath,
|
|
71688
|
-
project,
|
|
71689
|
-
customRulesOnly,
|
|
71690
|
-
extendsPaths: overrides.extendsPaths,
|
|
71691
|
-
ignoredTags,
|
|
71692
|
-
includedTags,
|
|
71693
|
-
includeTagDefaults,
|
|
71694
|
-
serverAuthFunctionNames,
|
|
71695
|
-
severityControls,
|
|
71696
|
-
userPlugins,
|
|
71697
|
-
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
71698
|
-
ruleSelection: overrides.ruleSelection,
|
|
71699
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
71700
|
-
});
|
|
71701
72282
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
71702
72283
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
71703
72284
|
const configPath = Path.join(configDirectory, "oxlintrc.json");
|
|
@@ -71733,7 +72314,30 @@ const runOxlint = async (options) => {
|
|
|
71733
72314
|
await yieldToEventLoop();
|
|
71734
72315
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
71735
72316
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
72317
|
+
const preparedHtmlSources = prepareHtmlLintSources(rootDirectory, configDirectory, candidateFiles);
|
|
72318
|
+
const lintFiles = preparedHtmlSources.lintFiles;
|
|
72319
|
+
const hasHtmlFiles = preparedHtmlSources.sourcePathByLintPath.size > 0;
|
|
72320
|
+
const lintProject = !project.hasThree && preparedHtmlSources.hasThreeModuleImport ? {
|
|
72321
|
+
...project,
|
|
72322
|
+
hasThree: true
|
|
72323
|
+
} : project;
|
|
72324
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
72325
|
+
pluginPath,
|
|
72326
|
+
project: lintProject,
|
|
72327
|
+
customRulesOnly,
|
|
72328
|
+
extendsPaths: overrides.extendsPaths,
|
|
72329
|
+
ignoredTags,
|
|
72330
|
+
includedTags,
|
|
72331
|
+
includeTagDefaults,
|
|
72332
|
+
serverAuthFunctionNames,
|
|
72333
|
+
severityControls,
|
|
72334
|
+
userPlugins,
|
|
72335
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
72336
|
+
ruleSelection: overrides.ruleSelection,
|
|
72337
|
+
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
72338
|
+
});
|
|
71736
72339
|
const sizeByFile = sizedScanFiles !== null && lintBatchOrdering === "cost" ? new Map(sizedScanFiles.map((entry) => [entry.path, entry.sizeBytes])) : null;
|
|
72340
|
+
if (sizeByFile !== null) for (const [lintPath, sizeBytes] of preparedHtmlSources.sizeByLintPath) sizeByFile.set(lintPath, sizeBytes);
|
|
71737
72341
|
const buildFileBatches = (passBaseArgs, passFiles) => sizeByFile !== null ? planLintBatches({
|
|
71738
72342
|
baseArgs: passBaseArgs,
|
|
71739
72343
|
files: passFiles,
|
|
@@ -71762,7 +72366,8 @@ const runOxlint = async (options) => {
|
|
|
71762
72366
|
fileBatches: passFileBatches,
|
|
71763
72367
|
rootDirectory,
|
|
71764
72368
|
nodeBinaryPath,
|
|
71765
|
-
project,
|
|
72369
|
+
project: lintProject,
|
|
72370
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
71766
72371
|
onPartialFailure: reportPartialFailure,
|
|
71767
72372
|
onAnalyzedFiles: (filePaths) => {
|
|
71768
72373
|
analyzedFiles = filePaths;
|
|
@@ -71797,7 +72402,7 @@ const runOxlint = async (options) => {
|
|
|
71797
72402
|
};
|
|
71798
72403
|
}
|
|
71799
72404
|
};
|
|
71800
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
72405
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0 && !hasHtmlFiles) {
|
|
71801
72406
|
const rulesetHash = computeRulesetHash({
|
|
71802
72407
|
config: buildConfig({
|
|
71803
72408
|
extendsPaths: [],
|
|
@@ -71955,14 +72560,15 @@ const runOxlint = async (options) => {
|
|
|
71955
72560
|
]);
|
|
71956
72561
|
}
|
|
71957
72562
|
const baseArgs = makeBaseArgs(configPath);
|
|
71958
|
-
const fileBatches = buildFileBatches(baseArgs,
|
|
72563
|
+
const fileBatches = buildFileBatches(baseArgs, lintFiles);
|
|
71959
72564
|
let analyzedFiles = [];
|
|
71960
72565
|
const runBatches = () => spawnLintBatches({
|
|
71961
72566
|
baseArgs,
|
|
71962
72567
|
fileBatches,
|
|
71963
72568
|
rootDirectory,
|
|
71964
72569
|
nodeBinaryPath,
|
|
71965
|
-
project,
|
|
72570
|
+
project: lintProject,
|
|
72571
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
71966
72572
|
onPartialFailure,
|
|
71967
72573
|
onAnalyzedFiles: (filePaths) => {
|
|
71968
72574
|
analyzedFiles = filePaths;
|
|
@@ -73884,4 +74490,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
73884
74490
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
73885
74491
|
|
|
73886
74492
|
//# sourceMappingURL=index.js.map
|
|
73887
|
-
//# debugId=
|
|
74493
|
+
//# debugId=2a46b47b-3e8f-5566-a6f0-442aa6df4a39
|