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/lsp.js
CHANGED
|
@@ -61478,7 +61478,8 @@ var AmbiguousProjectError = class extends Error {
|
|
|
61478
61478
|
}
|
|
61479
61479
|
};
|
|
61480
61480
|
const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
|
|
61481
|
-
const
|
|
61481
|
+
const HTML_FILE_PATTERN = /\.html$/i;
|
|
61482
|
+
const SOURCE_FILE_PATTERN = /\.(?:tsx?|jsx?|mts|mjs|[hH][tT][mM][lL])$/;
|
|
61482
61483
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
61483
61484
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
61484
61485
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
@@ -62338,7 +62339,7 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
|
|
|
62338
62339
|
]).find(isFile) ?? null;
|
|
62339
62340
|
};
|
|
62340
62341
|
const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
62341
|
-
const getStaticPropertyName$
|
|
62342
|
+
const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
|
|
62342
62343
|
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");
|
|
62343
62344
|
const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
62344
62345
|
const getRequireModuleSpecifier = (expression) => {
|
|
@@ -62402,7 +62403,7 @@ const getImportBinding = (sourceFile, bindingName) => {
|
|
|
62402
62403
|
const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
|
|
62403
62404
|
if (bindingElement && moduleSpecifier) return {
|
|
62404
62405
|
moduleSpecifier,
|
|
62405
|
-
exportName: bindingElement.propertyName ? getStaticPropertyName$
|
|
62406
|
+
exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
|
|
62406
62407
|
isNamespace: false
|
|
62407
62408
|
};
|
|
62408
62409
|
}
|
|
@@ -62563,7 +62564,7 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
62563
62564
|
}
|
|
62564
62565
|
if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
|
|
62565
62566
|
for (const property of [...unwrappedExpression.properties].reverse()) {
|
|
62566
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62567
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
|
|
62567
62568
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
|
|
62568
62569
|
if (ts.isSpreadAssignment(property)) {
|
|
62569
62570
|
const spreadState = getReactCompilerFlagState(property.expression, analysis);
|
|
@@ -62589,8 +62590,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
62589
62590
|
}
|
|
62590
62591
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
62591
62592
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
62592
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62593
|
-
if (ts.isMethodDeclaration(property) && getStaticPropertyName$
|
|
62593
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === propertyName) return property.initializer;
|
|
62594
|
+
if (ts.isMethodDeclaration(property) && getStaticPropertyName$2(property.name) === propertyName) return property;
|
|
62594
62595
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
62595
62596
|
if (ts.isSpreadAssignment(property)) {
|
|
62596
62597
|
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
@@ -62621,7 +62622,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
|
|
|
62621
62622
|
return resolvedExpression.properties.some((property) => {
|
|
62622
62623
|
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
62623
62624
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
62624
|
-
const staticPropertyName = getStaticPropertyName$
|
|
62625
|
+
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
62625
62626
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
62626
62627
|
}
|
|
62627
62628
|
return false;
|
|
@@ -62640,7 +62641,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
|
|
|
62640
62641
|
continue;
|
|
62641
62642
|
}
|
|
62642
62643
|
if (ts.isExpressionStatement(statement) && isCommonJsConfigExportAssignment(statement.expression, sourceFile) && exportName !== "default" && ts.isObjectLiteralExpression(statement.expression.right)) for (const property of [...statement.expression.right.properties].reverse()) {
|
|
62643
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62644
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
|
|
62644
62645
|
exportedNodes.push(property.initializer);
|
|
62645
62646
|
break;
|
|
62646
62647
|
}
|
|
@@ -62799,10 +62800,10 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62799
62800
|
const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
|
|
62800
62801
|
if (reactCompilerFlagState?.isEnabled) return true;
|
|
62801
62802
|
for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
|
|
62802
|
-
const propertyName = getStaticPropertyName$
|
|
62803
|
+
const propertyName = getStaticPropertyName$2(property.name);
|
|
62803
62804
|
if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
|
|
62804
62805
|
if (propertyName === "reactCompiler") continue;
|
|
62805
|
-
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$
|
|
62806
|
+
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;
|
|
62806
62807
|
const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
|
|
62807
62808
|
if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
|
|
62808
62809
|
const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
|
|
@@ -62812,12 +62813,12 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62812
62813
|
} else if (ts.isShorthandPropertyAssignment(property)) {
|
|
62813
62814
|
if (excludedPropertyNames?.has(property.name.text)) continue;
|
|
62814
62815
|
if (property.name.text === "reactCompiler") continue;
|
|
62815
|
-
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$
|
|
62816
|
+
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;
|
|
62816
62817
|
const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
|
|
62817
62818
|
if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
|
|
62818
62819
|
} else if (ts.isSpreadAssignment(property)) {
|
|
62819
62820
|
if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
|
|
62820
|
-
if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$
|
|
62821
|
+
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;
|
|
62821
62822
|
if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
|
|
62822
62823
|
}
|
|
62823
62824
|
return false;
|
|
@@ -66956,6 +66957,321 @@ const buildReactNativeDiagnostic = (input) => ({
|
|
|
66956
66957
|
column: input.column ?? 0,
|
|
66957
66958
|
category: input.category ?? "Correctness"
|
|
66958
66959
|
});
|
|
66960
|
+
const ANDROID_APP_BUILD_FILE_NAMES = ["build.gradle", "build.gradle.kts"];
|
|
66961
|
+
const BUILD_TYPES_BLOCK_PATTERN = /\bbuildTypes\s*\{/g;
|
|
66962
|
+
const RELEASE_STRING_MARKER = "__RD_RELEASE__";
|
|
66963
|
+
const RELEASE_BLOCK_PATTERNS = [/\brelease\s*\{/g, /\b(?:getByName|named)\s*\(\s*(?:["']release["']|__RD_RELEASE__)\s*\)\s*\{/g];
|
|
66964
|
+
const MINIFY_ASSIGNMENT_PATTERN = /\b(?:isMinifyEnabled|minifyEnabled)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66965
|
+
const SHRINK_RESOURCES_ASSIGNMENT_PATTERN = /\b(?:isShrinkResources|shrinkResources)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66966
|
+
const stripGradleComments = (contents) => {
|
|
66967
|
+
let result = "";
|
|
66968
|
+
let index = 0;
|
|
66969
|
+
let quote = null;
|
|
66970
|
+
while (index < contents.length) {
|
|
66971
|
+
const character = contents[index];
|
|
66972
|
+
const nextCharacter = contents[index + 1];
|
|
66973
|
+
if (quote !== null) {
|
|
66974
|
+
result += character;
|
|
66975
|
+
if (character === "\\") {
|
|
66976
|
+
result += nextCharacter ?? "";
|
|
66977
|
+
index += 2;
|
|
66978
|
+
continue;
|
|
66979
|
+
}
|
|
66980
|
+
if (character === quote) quote = null;
|
|
66981
|
+
index += 1;
|
|
66982
|
+
continue;
|
|
66983
|
+
}
|
|
66984
|
+
if (character === "\"" || character === "'") {
|
|
66985
|
+
quote = character;
|
|
66986
|
+
result += character;
|
|
66987
|
+
index += 1;
|
|
66988
|
+
continue;
|
|
66989
|
+
}
|
|
66990
|
+
if (character === "/" && nextCharacter === "/") {
|
|
66991
|
+
while (index < contents.length && contents[index] !== "\n") {
|
|
66992
|
+
result += " ";
|
|
66993
|
+
index += 1;
|
|
66994
|
+
}
|
|
66995
|
+
continue;
|
|
66996
|
+
}
|
|
66997
|
+
if (character === "/" && nextCharacter === "*") {
|
|
66998
|
+
result += " ";
|
|
66999
|
+
index += 2;
|
|
67000
|
+
while (index < contents.length && !(contents[index] === "*" && contents[index + 1] === "/")) {
|
|
67001
|
+
result += contents[index] === "\n" ? "\n" : " ";
|
|
67002
|
+
index += 1;
|
|
67003
|
+
}
|
|
67004
|
+
if (index < contents.length) {
|
|
67005
|
+
result += " ";
|
|
67006
|
+
index += 2;
|
|
67007
|
+
}
|
|
67008
|
+
continue;
|
|
67009
|
+
}
|
|
67010
|
+
result += character;
|
|
67011
|
+
index += 1;
|
|
67012
|
+
}
|
|
67013
|
+
return result;
|
|
67014
|
+
};
|
|
67015
|
+
const maskGradleStrings = (contents) => {
|
|
67016
|
+
let result = "";
|
|
67017
|
+
let index = 0;
|
|
67018
|
+
let quote = null;
|
|
67019
|
+
while (index < contents.length) {
|
|
67020
|
+
const character = contents[index];
|
|
67021
|
+
if (quote === null) {
|
|
67022
|
+
if (character === "\"" || character === "'") {
|
|
67023
|
+
quote = character;
|
|
67024
|
+
result += " ";
|
|
67025
|
+
} else result += character;
|
|
67026
|
+
index += 1;
|
|
67027
|
+
continue;
|
|
67028
|
+
}
|
|
67029
|
+
if (character === "\\") {
|
|
67030
|
+
result += " ";
|
|
67031
|
+
index += 2;
|
|
67032
|
+
continue;
|
|
67033
|
+
}
|
|
67034
|
+
if (character === quote) quote = null;
|
|
67035
|
+
result += character === "\n" ? "\n" : " ";
|
|
67036
|
+
index += 1;
|
|
67037
|
+
}
|
|
67038
|
+
return result;
|
|
67039
|
+
};
|
|
67040
|
+
const findBlockContents = (contents, pattern) => {
|
|
67041
|
+
const blocks = [];
|
|
67042
|
+
pattern.lastIndex = 0;
|
|
67043
|
+
let match = pattern.exec(contents);
|
|
67044
|
+
while (match !== null) {
|
|
67045
|
+
const openingBraceIndex = pattern.lastIndex - 1;
|
|
67046
|
+
let depth = 1;
|
|
67047
|
+
let index = openingBraceIndex + 1;
|
|
67048
|
+
let quote = null;
|
|
67049
|
+
while (index < contents.length && depth > 0) {
|
|
67050
|
+
const character = contents[index];
|
|
67051
|
+
if (quote !== null) {
|
|
67052
|
+
if (character === "\\") {
|
|
67053
|
+
index += 2;
|
|
67054
|
+
continue;
|
|
67055
|
+
}
|
|
67056
|
+
if (character === quote) quote = null;
|
|
67057
|
+
} else if (character === "\"" || character === "'") quote = character;
|
|
67058
|
+
else if (character === "{") depth += 1;
|
|
67059
|
+
else if (character === "}") depth -= 1;
|
|
67060
|
+
index += 1;
|
|
67061
|
+
}
|
|
67062
|
+
if (depth === 0) blocks.push(contents.slice(openingBraceIndex + 1, index - 1));
|
|
67063
|
+
match = pattern.exec(contents);
|
|
67064
|
+
}
|
|
67065
|
+
return blocks;
|
|
67066
|
+
};
|
|
67067
|
+
const getFinalStaticBoolean = (contents, pattern) => {
|
|
67068
|
+
pattern.lastIndex = 0;
|
|
67069
|
+
let finalValue = null;
|
|
67070
|
+
let match = pattern.exec(contents);
|
|
67071
|
+
while (match !== null) {
|
|
67072
|
+
const staticValue = match[1]?.trim().replace(/\)$/, "").trim();
|
|
67073
|
+
if (staticValue === "true") finalValue = true;
|
|
67074
|
+
else if (staticValue === "false") finalValue = false;
|
|
67075
|
+
else finalValue = null;
|
|
67076
|
+
match = pattern.exec(contents);
|
|
67077
|
+
}
|
|
67078
|
+
return finalValue;
|
|
67079
|
+
};
|
|
67080
|
+
const getReleaseBlocks = (contents) => findBlockContents(contents, BUILD_TYPES_BLOCK_PATTERN).flatMap((buildTypesBlock) => RELEASE_BLOCK_PATTERNS.flatMap((pattern) => findBlockContents(buildTypesBlock, pattern)));
|
|
67081
|
+
const readAndroidReleaseShrinking = (rootDirectory) => {
|
|
67082
|
+
for (const buildFileName of ANDROID_APP_BUILD_FILE_NAMES) {
|
|
67083
|
+
const filePath = Path.posix.join("android", "app", buildFileName);
|
|
67084
|
+
const absoluteFilePath = Path.join(rootDirectory, filePath);
|
|
67085
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67086
|
+
let contents;
|
|
67087
|
+
try {
|
|
67088
|
+
contents = stripGradleComments(NFS.readFileSync(absoluteFilePath, "utf-8"));
|
|
67089
|
+
} catch {
|
|
67090
|
+
return null;
|
|
67091
|
+
}
|
|
67092
|
+
const releaseBlocks = getReleaseBlocks(maskGradleStrings(contents.replace(/(["'])release\1/g, RELEASE_STRING_MARKER)));
|
|
67093
|
+
const [releaseBlock] = releaseBlocks;
|
|
67094
|
+
if (releaseBlock === void 0 || releaseBlocks.length !== 1) return null;
|
|
67095
|
+
const hasDisabledMinification = getFinalStaticBoolean(releaseBlock, MINIFY_ASSIGNMENT_PATTERN) === false;
|
|
67096
|
+
const hasDisabledResourceShrinking = getFinalStaticBoolean(releaseBlock, SHRINK_RESOURCES_ASSIGNMENT_PATTERN) === false;
|
|
67097
|
+
if (!hasDisabledMinification && !hasDisabledResourceShrinking) return null;
|
|
67098
|
+
return {
|
|
67099
|
+
filePath,
|
|
67100
|
+
hasDisabledMinification,
|
|
67101
|
+
hasDisabledResourceShrinking
|
|
67102
|
+
};
|
|
67103
|
+
}
|
|
67104
|
+
return null;
|
|
67105
|
+
};
|
|
67106
|
+
const checkReactNativeAndroidReleaseShrinking = (rootDirectory) => {
|
|
67107
|
+
const releaseShrinking = readAndroidReleaseShrinking(rootDirectory);
|
|
67108
|
+
if (releaseShrinking === null) return [];
|
|
67109
|
+
const disabledFeatures = [releaseShrinking.hasDisabledMinification ? "code minification" : null, releaseShrinking.hasDisabledResourceShrinking ? "resource shrinking" : null].filter((feature) => feature !== null);
|
|
67110
|
+
return [buildReactNativeDiagnostic({
|
|
67111
|
+
filePath: releaseShrinking.filePath,
|
|
67112
|
+
rule: "rn-android-release-shrinking-disabled",
|
|
67113
|
+
category: "Performance",
|
|
67114
|
+
message: `The Android release build explicitly disables ${disabledFeatures.join(" and ")}, leaving unreachable code or resources in the shipped app.`,
|
|
67115
|
+
help: "Enable release code minification and resource shrinking, then test the optimized build and add keep rules for libraries that require them."
|
|
67116
|
+
})];
|
|
67117
|
+
};
|
|
67118
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
67119
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67120
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67121
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67122
|
+
return ts.ScriptKind.JS;
|
|
67123
|
+
};
|
|
67124
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
67125
|
+
let currentExpression = expression;
|
|
67126
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67127
|
+
return currentExpression;
|
|
67128
|
+
};
|
|
67129
|
+
const BABEL_CONFIG_FILE_NAMES$1 = [
|
|
67130
|
+
"babel.config.js",
|
|
67131
|
+
"babel.config.cjs",
|
|
67132
|
+
"babel.config.mjs",
|
|
67133
|
+
"babel.config.ts",
|
|
67134
|
+
"babel.config.cts",
|
|
67135
|
+
"babel.config.json",
|
|
67136
|
+
".babelrc",
|
|
67137
|
+
".babelrc.js",
|
|
67138
|
+
".babelrc.cjs",
|
|
67139
|
+
".babelrc.mjs",
|
|
67140
|
+
".babelrc.cts",
|
|
67141
|
+
".babelrc.json"
|
|
67142
|
+
];
|
|
67143
|
+
const readUnknownPluginNames = (value) => {
|
|
67144
|
+
if (!Array.isArray(value)) return null;
|
|
67145
|
+
const pluginNames = [];
|
|
67146
|
+
for (const entry of value) {
|
|
67147
|
+
if (typeof entry === "string") {
|
|
67148
|
+
pluginNames.push(entry);
|
|
67149
|
+
continue;
|
|
67150
|
+
}
|
|
67151
|
+
if (Array.isArray(entry) && typeof entry[0] === "string") {
|
|
67152
|
+
pluginNames.push(entry[0]);
|
|
67153
|
+
continue;
|
|
67154
|
+
}
|
|
67155
|
+
return null;
|
|
67156
|
+
}
|
|
67157
|
+
return pluginNames;
|
|
67158
|
+
};
|
|
67159
|
+
const getStaticPropertyName$1 = (propertyName) => {
|
|
67160
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) return propertyName.text;
|
|
67161
|
+
return null;
|
|
67162
|
+
};
|
|
67163
|
+
const getStaticPluginName = (expression) => {
|
|
67164
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
67165
|
+
if (ts.isStringLiteralLike(unwrappedExpression)) return unwrappedExpression.text;
|
|
67166
|
+
if (!ts.isArrayLiteralExpression(unwrappedExpression)) return null;
|
|
67167
|
+
const pluginExpression = unwrappedExpression.elements[0];
|
|
67168
|
+
return pluginExpression && ts.isStringLiteralLike(pluginExpression) ? pluginExpression.text : null;
|
|
67169
|
+
};
|
|
67170
|
+
const getPluginNamesFromObject = (objectExpression) => {
|
|
67171
|
+
if (objectExpression.properties.some((property) => ts.isSpreadAssignment(property))) return null;
|
|
67172
|
+
const pluginProperties = objectExpression.properties.filter((property) => ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "plugins");
|
|
67173
|
+
if (pluginProperties.length !== 1) return pluginProperties.length === 0 ? [] : null;
|
|
67174
|
+
const pluginListExpression = unwrapTypescriptExpression(pluginProperties[0].initializer);
|
|
67175
|
+
if (!ts.isArrayLiteralExpression(pluginListExpression)) return null;
|
|
67176
|
+
const pluginNames = [];
|
|
67177
|
+
for (const element of pluginListExpression.elements) {
|
|
67178
|
+
if (ts.isSpreadElement(element)) return null;
|
|
67179
|
+
const pluginName = getStaticPluginName(element);
|
|
67180
|
+
if (pluginName === null) return null;
|
|
67181
|
+
pluginNames.push(pluginName);
|
|
67182
|
+
}
|
|
67183
|
+
return pluginNames;
|
|
67184
|
+
};
|
|
67185
|
+
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";
|
|
67186
|
+
const getDirectExportedObject = (sourceFile) => {
|
|
67187
|
+
const exportedExpressions = [];
|
|
67188
|
+
for (const statement of sourceFile.statements) {
|
|
67189
|
+
if (ts.isExportAssignment(statement)) {
|
|
67190
|
+
exportedExpressions.push(statement.expression);
|
|
67191
|
+
continue;
|
|
67192
|
+
}
|
|
67193
|
+
if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && isModuleExports(statement.expression.left)) exportedExpressions.push(statement.expression.right);
|
|
67194
|
+
}
|
|
67195
|
+
if (exportedExpressions.length !== 1) return null;
|
|
67196
|
+
const exportedExpression = unwrapTypescriptExpression(exportedExpressions[0]);
|
|
67197
|
+
return ts.isObjectLiteralExpression(exportedExpression) ? exportedExpression : null;
|
|
67198
|
+
};
|
|
67199
|
+
const parseConfigPluginNames = (filePath, contents) => {
|
|
67200
|
+
try {
|
|
67201
|
+
const parsedJson = JSON.parse(contents);
|
|
67202
|
+
return isPlainObject(parsedJson) ? readUnknownPluginNames(parsedJson.plugins) : null;
|
|
67203
|
+
} catch {
|
|
67204
|
+
if (ts.transpileModule(contents, {
|
|
67205
|
+
fileName: filePath,
|
|
67206
|
+
reportDiagnostics: true,
|
|
67207
|
+
compilerOptions: { allowJs: true }
|
|
67208
|
+
}).diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) return null;
|
|
67209
|
+
const exportedObject = getDirectExportedObject(ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filePath)));
|
|
67210
|
+
return exportedObject === null ? null : getPluginNamesFromObject(exportedObject);
|
|
67211
|
+
}
|
|
67212
|
+
};
|
|
67213
|
+
const readStaticBabelPluginNames = (rootDirectory) => {
|
|
67214
|
+
for (const fileName of BABEL_CONFIG_FILE_NAMES$1) {
|
|
67215
|
+
const absoluteFilePath = Path.join(rootDirectory, fileName);
|
|
67216
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67217
|
+
try {
|
|
67218
|
+
return {
|
|
67219
|
+
filePath: fileName,
|
|
67220
|
+
pluginNames: parseConfigPluginNames(fileName, NFS.readFileSync(absoluteFilePath, "utf-8"))
|
|
67221
|
+
};
|
|
67222
|
+
} catch {
|
|
67223
|
+
return {
|
|
67224
|
+
filePath: fileName,
|
|
67225
|
+
pluginNames: null
|
|
67226
|
+
};
|
|
67227
|
+
}
|
|
67228
|
+
}
|
|
67229
|
+
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
67230
|
+
if (!isPlainObject(packageJson.babel)) return null;
|
|
67231
|
+
return {
|
|
67232
|
+
filePath: "package.json",
|
|
67233
|
+
pluginNames: readUnknownPluginNames(packageJson.babel.plugins)
|
|
67234
|
+
};
|
|
67235
|
+
};
|
|
67236
|
+
const REACT_COMPILER_PLUGIN_NAMES = new Set(["babel-plugin-react-compiler", "react-compiler"]);
|
|
67237
|
+
const REANIMATED_PLUGIN_NAME = "react-native-reanimated/plugin";
|
|
67238
|
+
const WORKLETS_PLUGIN_NAME = "react-native-worklets/plugin";
|
|
67239
|
+
const checkReactCompilerPluginOrder = (filePath, pluginNames) => {
|
|
67240
|
+
if (pluginNames.findIndex((pluginName) => REACT_COMPILER_PLUGIN_NAMES.has(pluginName)) <= 0) return [];
|
|
67241
|
+
return [buildReactNativeDiagnostic({
|
|
67242
|
+
filePath,
|
|
67243
|
+
rule: "rn-react-compiler-plugin-first",
|
|
67244
|
+
category: "Performance",
|
|
67245
|
+
message: "The React Compiler Babel plugin is not first, so an earlier transform can change component or hook code before the compiler analyzes it.",
|
|
67246
|
+
help: "Move `babel-plugin-react-compiler` to the first entry in the Babel `plugins` array."
|
|
67247
|
+
})];
|
|
67248
|
+
};
|
|
67249
|
+
const checkReanimatedPluginOrder = (project, filePath, pluginNames) => {
|
|
67250
|
+
const reanimatedMajorVersion = project.reanimatedVersion === null || project.reanimatedVersion === void 0 ? null : getLowestDependencyMajor(project.reanimatedVersion);
|
|
67251
|
+
if (reanimatedMajorVersion === null || reanimatedMajorVersion < 4) return [];
|
|
67252
|
+
const diagnostics = [];
|
|
67253
|
+
if (pluginNames.includes(REANIMATED_PLUGIN_NAME)) diagnostics.push(buildReactNativeDiagnostic({
|
|
67254
|
+
filePath,
|
|
67255
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67256
|
+
category: "Performance",
|
|
67257
|
+
message: "This Reanimated 4 project still uses `react-native-reanimated/plugin`, which was renamed to the Worklets Babel plugin.",
|
|
67258
|
+
help: "Replace it with `react-native-worklets/plugin` and keep that plugin last in the Babel `plugins` array."
|
|
67259
|
+
}));
|
|
67260
|
+
const workletsPluginIndex = pluginNames.lastIndexOf(WORKLETS_PLUGIN_NAME);
|
|
67261
|
+
if (workletsPluginIndex !== -1 && workletsPluginIndex !== pluginNames.length - 1) diagnostics.push(buildReactNativeDiagnostic({
|
|
67262
|
+
filePath,
|
|
67263
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67264
|
+
category: "Performance",
|
|
67265
|
+
message: "`react-native-worklets/plugin` is not last in the Babel `plugins` array, so later transforms can prevent worklets from being compiled correctly.",
|
|
67266
|
+
help: "Move `react-native-worklets/plugin` to the final entry in the Babel `plugins` array."
|
|
67267
|
+
}));
|
|
67268
|
+
return diagnostics;
|
|
67269
|
+
};
|
|
67270
|
+
const checkReactNativeBabelPluginOrder = (rootDirectory, project) => {
|
|
67271
|
+
const babelPlugins = readStaticBabelPluginNames(rootDirectory);
|
|
67272
|
+
if (babelPlugins === null || babelPlugins.pluginNames === null) return [];
|
|
67273
|
+
return [...checkReactCompilerPluginOrder(babelPlugins.filePath, babelPlugins.pluginNames), ...checkReanimatedPluginOrder(project, babelPlugins.filePath, babelPlugins.pluginNames)];
|
|
67274
|
+
};
|
|
66959
67275
|
const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
|
|
66960
67276
|
const isBuilderBobLibrary = (packageJson) => {
|
|
66961
67277
|
const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
|
|
@@ -67032,7 +67348,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
67032
67348
|
const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
|
|
67033
67349
|
const checkReactNativeProject = (rootDirectory, project) => {
|
|
67034
67350
|
if (!isReactNativeProject(project)) return [];
|
|
67035
|
-
return [
|
|
67351
|
+
return [
|
|
67352
|
+
...checkReactNativeMetroBabelPreset(rootDirectory),
|
|
67353
|
+
...checkReactNativeBabelPluginOrder(rootDirectory, project),
|
|
67354
|
+
...checkReactNativeAndroidReleaseShrinking(rootDirectory),
|
|
67355
|
+
...checkReactNativeLibraryDependencies(rootDirectory)
|
|
67356
|
+
];
|
|
67036
67357
|
};
|
|
67037
67358
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
67038
67359
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
@@ -67183,17 +67504,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
67183
67504
|
}
|
|
67184
67505
|
return diagnostics;
|
|
67185
67506
|
};
|
|
67186
|
-
const getTypescriptScriptKind = (fileName) => {
|
|
67187
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67188
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67189
|
-
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67190
|
-
return ts.ScriptKind.JS;
|
|
67191
|
-
};
|
|
67192
|
-
const unwrapTypescriptExpression = (expression) => {
|
|
67193
|
-
let currentExpression = expression;
|
|
67194
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67195
|
-
return currentExpression;
|
|
67196
|
-
};
|
|
67197
67507
|
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
67198
67508
|
isAnimationFunction: false,
|
|
67199
67509
|
isMotionComponent: false,
|
|
@@ -67749,7 +68059,11 @@ const buildCapabilities = (project) => {
|
|
|
67749
68059
|
capabilities.add(project.framework);
|
|
67750
68060
|
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
67751
68061
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
67752
|
-
if (project.expoVersion !== null)
|
|
68062
|
+
if (project.expoVersion !== null) {
|
|
68063
|
+
capabilities.add("expo");
|
|
68064
|
+
const expoSdkMajorVersion = getLowestDependencyMajor(project.expoVersion);
|
|
68065
|
+
if (expoSdkMajorVersion !== null && expoSdkMajorVersion >= 54) capabilities.add("expo:54");
|
|
68066
|
+
}
|
|
67753
68067
|
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
67754
68068
|
if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
|
|
67755
68069
|
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
@@ -67813,6 +68127,11 @@ const buildCapabilities = (project) => {
|
|
|
67813
68127
|
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
67814
68128
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
67815
68129
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
68130
|
+
if (project.reanimatedVersion !== void 0 && project.reanimatedVersion !== null) {
|
|
68131
|
+
capabilities.add("reanimated");
|
|
68132
|
+
const reanimatedMajorVersion = getLowestDependencyMajor(project.reanimatedVersion);
|
|
68133
|
+
if (reanimatedMajorVersion !== null && reanimatedMajorVersion >= 4) capabilities.add("reanimated:4");
|
|
68134
|
+
}
|
|
67816
68135
|
if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
|
|
67817
68136
|
if (project.styledComponentsVersion) {
|
|
67818
68137
|
capabilities.add("styled-components");
|
|
@@ -70255,6 +70574,12 @@ const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
|
70255
70574
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
70256
70575
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
70257
70576
|
};
|
|
70577
|
+
const columnOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
70578
|
+
const boundedOffset = Math.min(Math.max(utf8Offset, 0), sourceBuffer.length);
|
|
70579
|
+
let lineStartOffset = boundedOffset;
|
|
70580
|
+
while (lineStartOffset > 0 && sourceBuffer[lineStartOffset - 1] !== 10) lineStartOffset--;
|
|
70581
|
+
return sourceBuffer.subarray(lineStartOffset, boundedOffset).toString("utf8").length + 1;
|
|
70582
|
+
};
|
|
70258
70583
|
const REDACTED_PLACEHOLDER = "<redacted>";
|
|
70259
70584
|
const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
|
|
70260
70585
|
const KNOWN_SECRET_RULES = [
|
|
@@ -70970,7 +71295,7 @@ const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORI
|
|
|
70970
71295
|
* with empty text still carry a useful jump target, so they're kept
|
|
70971
71296
|
* with a neutral message.
|
|
70972
71297
|
*/
|
|
70973
|
-
const buildRelatedLocations = (labels, filePath) => {
|
|
71298
|
+
const buildRelatedLocations = (labels, filePath, sourceBuffer) => {
|
|
70974
71299
|
if (labels.length <= 1) return [];
|
|
70975
71300
|
const related = [];
|
|
70976
71301
|
for (let labelIndex = 1; labelIndex < labels.length; labelIndex++) {
|
|
@@ -70978,8 +71303,8 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
70978
71303
|
if (!label?.span) continue;
|
|
70979
71304
|
related.push({
|
|
70980
71305
|
filePath,
|
|
70981
|
-
line: label.span.line ?? 0,
|
|
70982
|
-
column: label.span.column ?? 0,
|
|
71306
|
+
line: sourceBuffer === null ? label.span.line ?? 0 : lineOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
71307
|
+
column: sourceBuffer === null ? label.span.column ?? 0 : columnOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
70983
71308
|
offset: label.span.offset,
|
|
70984
71309
|
length: label.span.length,
|
|
70985
71310
|
message: label.label ?? ""
|
|
@@ -70999,7 +71324,7 @@ const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnos
|
|
|
70999
71324
|
* to the first `{`. Raises `OxlintOutputUnparseable` when the
|
|
71000
71325
|
* stdout isn't valid JSON or doesn't carry a `diagnostics` array.
|
|
71001
71326
|
*/
|
|
71002
|
-
const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
71327
|
+
const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath) => {
|
|
71003
71328
|
if (!stdout) return [];
|
|
71004
71329
|
const jsonStart = stdout.indexOf("{");
|
|
71005
71330
|
const sanitizedStdout = jsonStart > 0 ? stdout.slice(jsonStart) : stdout;
|
|
@@ -71016,8 +71341,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71016
71341
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
71017
71342
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
71018
71343
|
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
71344
|
+
const resolveMappedSourceFilename = (filename) => sourcePathByLintPath?.get(Path.normalize(resolveAbsolutePath(filename)));
|
|
71345
|
+
const resolveSourceFilename = (filename) => resolveMappedSourceFilename(filename) ?? filename;
|
|
71019
71346
|
const readSourceBuffer = (filename) => {
|
|
71020
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71347
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71021
71348
|
const cached = sourceBufferCache.get(absolutePath);
|
|
71022
71349
|
if (cached !== void 0) return cached;
|
|
71023
71350
|
let sourceBuffer;
|
|
@@ -71030,7 +71357,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71030
71357
|
return sourceBuffer;
|
|
71031
71358
|
};
|
|
71032
71359
|
const readSourceLines = (filename) => {
|
|
71033
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71360
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71034
71361
|
const cached = sourceLinesCache.get(absolutePath);
|
|
71035
71362
|
if (cached !== void 0) return cached;
|
|
71036
71363
|
const sourceBuffer = readSourceBuffer(filename);
|
|
@@ -71039,27 +71366,30 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71039
71366
|
return sourceLines;
|
|
71040
71367
|
};
|
|
71041
71368
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
71042
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
71369
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
71043
71370
|
const cached = minifiedFileCache.get(absolutePath);
|
|
71044
71371
|
if (cached !== void 0) return cached;
|
|
71045
71372
|
const minified = isMinifiedSource(absolutePath);
|
|
71046
71373
|
minifiedFileCache.set(absolutePath, minified);
|
|
71047
71374
|
return minified;
|
|
71048
71375
|
};
|
|
71049
|
-
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) => {
|
|
71376
|
+
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) => {
|
|
71050
71377
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
71051
71378
|
const primaryLabel = diagnostic.labels[0];
|
|
71052
71379
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
71053
|
-
const
|
|
71380
|
+
const mappedSourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
71381
|
+
const normalizedFilePath = (mappedSourceFilename ?? diagnostic.filename).replaceAll("\\", "/");
|
|
71054
71382
|
const primarySpan = primaryLabel?.span;
|
|
71055
71383
|
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
71056
|
-
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
71384
|
+
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath, mappedSourceFilename === void 0 ? null : sourceBuffer);
|
|
71057
71385
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
71058
71386
|
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
71059
71387
|
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
71060
|
-
const
|
|
71388
|
+
const primaryLineNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.line ?? 0;
|
|
71389
|
+
const primaryColumnNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? columnOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.column ?? 0;
|
|
71390
|
+
const primaryLineIndex = primarySpan ? primaryLineNumber - 1 : -1;
|
|
71061
71391
|
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
71062
|
-
const primaryColumnIndex = primarySpan ?
|
|
71392
|
+
const primaryColumnIndex = primarySpan ? primaryColumnNumber - 1 : -1;
|
|
71063
71393
|
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
71064
71394
|
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
71065
71395
|
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
@@ -71072,8 +71402,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
71072
71402
|
message: cleaned.message,
|
|
71073
71403
|
help: cleaned.help,
|
|
71074
71404
|
url: diagnostic.url,
|
|
71075
|
-
line:
|
|
71076
|
-
column:
|
|
71405
|
+
line: primaryLineNumber,
|
|
71406
|
+
column: primaryColumnNumber,
|
|
71077
71407
|
...primarySpan ? {
|
|
71078
71408
|
offset: primarySpan.offset,
|
|
71079
71409
|
length: primarySpan.length,
|
|
@@ -71261,7 +71591,11 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
71261
71591
|
* loop with a slimmer config in that case.
|
|
71262
71592
|
*/
|
|
71263
71593
|
const spawnLintBatches = async (input) => {
|
|
71264
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
71594
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
71595
|
+
const resolveSourcePath = (filePath) => {
|
|
71596
|
+
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
71597
|
+
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
71598
|
+
};
|
|
71265
71599
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
71266
71600
|
let rescueDeadlineEpochMs;
|
|
71267
71601
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
@@ -71281,7 +71615,7 @@ const spawnLintBatches = async (input) => {
|
|
|
71281
71615
|
}
|
|
71282
71616
|
const batchArgs = [...baseArgs, ...batch];
|
|
71283
71617
|
try {
|
|
71284
|
-
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
71618
|
+
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory, sourcePathByLintPath);
|
|
71285
71619
|
} catch (error) {
|
|
71286
71620
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
71287
71621
|
if (isPastDeadline()) {
|
|
@@ -71387,10 +71721,12 @@ const spawnLintBatches = async (input) => {
|
|
|
71387
71721
|
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
71388
71722
|
};
|
|
71389
71723
|
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
71390
|
-
|
|
71391
|
-
|
|
71724
|
+
const droppedSourceFiles = [...new Set(droppedFiles.map(resolveSourcePath))];
|
|
71725
|
+
const deadlineSkippedSourceFiles = [...new Set(outcome.deadlineSkippedFiles.map(resolveSourcePath))];
|
|
71726
|
+
reportSkippedFiles(droppedSourceFiles, (fileListPreview) => `${droppedSourceFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
71727
|
+
reportSkippedFiles(deadlineSkippedSourceFiles, (fileListPreview) => `${deadlineSkippedSourceFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
71392
71728
|
const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
|
|
71393
|
-
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
|
|
71729
|
+
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)).map(resolveSourcePath))].sort());
|
|
71394
71730
|
return dedupeDiagnostics(diagnostics);
|
|
71395
71731
|
};
|
|
71396
71732
|
let didValidate = false;
|
|
@@ -71489,6 +71825,266 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
71489
71825
|
}
|
|
71490
71826
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
71491
71827
|
};
|
|
71828
|
+
const THREE_MODULE_IMPORT_PATTERN = /\b(?:from\s*|import\s*(?:\(\s*)?|require\s*\(\s*)["']three(?:\/[^"']*)?["']/;
|
|
71829
|
+
const containsThreeModuleImport = (sourceText) => THREE_MODULE_IMPORT_PATTERN.test(sourceText);
|
|
71830
|
+
const COMMENT_OPEN = "<!--";
|
|
71831
|
+
const COMMENT_CLOSE = "-->";
|
|
71832
|
+
const FRONTMATTER_FENCE = "---";
|
|
71833
|
+
const SCRIPT_TAG_NAME = "script";
|
|
71834
|
+
const SCRIPT_OPEN = `<${SCRIPT_TAG_NAME}`;
|
|
71835
|
+
const SCRIPT_CLOSE = `</${SCRIPT_TAG_NAME}`;
|
|
71836
|
+
const TEMPLATE_TAG_NAME = "template";
|
|
71837
|
+
const TEMPLATE_CLOSE = `</${TEMPLATE_TAG_NAME}`;
|
|
71838
|
+
const JAVASCRIPT_MIME_TYPES = new Set([
|
|
71839
|
+
"application/ecmascript",
|
|
71840
|
+
"application/javascript",
|
|
71841
|
+
"application/x-ecmascript",
|
|
71842
|
+
"application/x-javascript",
|
|
71843
|
+
"text/ecmascript",
|
|
71844
|
+
"text/javascript",
|
|
71845
|
+
"text/javascript1.0",
|
|
71846
|
+
"text/javascript1.1",
|
|
71847
|
+
"text/javascript1.2",
|
|
71848
|
+
"text/javascript1.3",
|
|
71849
|
+
"text/javascript1.4",
|
|
71850
|
+
"text/javascript1.5",
|
|
71851
|
+
"text/jscript",
|
|
71852
|
+
"text/livescript",
|
|
71853
|
+
"text/x-ecmascript",
|
|
71854
|
+
"text/x-javascript"
|
|
71855
|
+
]);
|
|
71856
|
+
const RAW_TEXT_TAG_NAMES = new Set([
|
|
71857
|
+
"iframe",
|
|
71858
|
+
"noembed",
|
|
71859
|
+
"noframes",
|
|
71860
|
+
"noscript",
|
|
71861
|
+
"style",
|
|
71862
|
+
"textarea",
|
|
71863
|
+
"title",
|
|
71864
|
+
"xmp"
|
|
71865
|
+
]);
|
|
71866
|
+
const PLAINTEXT_TAG_NAME = "plaintext";
|
|
71867
|
+
const TAG_OPEN_UTF8_BYTE = "<".charCodeAt(0);
|
|
71868
|
+
const TAG_CLOSE_UTF8_BYTE = ">".charCodeAt(0);
|
|
71869
|
+
const SINGLE_QUOTE_UTF8_BYTE = "'".charCodeAt(0);
|
|
71870
|
+
const DOUBLE_QUOTE_UTF8_BYTE = "\"".charCodeAt(0);
|
|
71871
|
+
const ATTRIBUTE_ASSIGNMENT_UTF8_BYTE = "=".charCodeAt(0);
|
|
71872
|
+
const TAG_SELF_CLOSE_UTF8_BYTE = "/".charCodeAt(0);
|
|
71873
|
+
const TAB_UTF8_BYTE = " ".charCodeAt(0);
|
|
71874
|
+
const FORM_FEED_UTF8_BYTE = "\f".charCodeAt(0);
|
|
71875
|
+
const isHtmlWhitespace = (byte) => byte === TAB_UTF8_BYTE || byte === 10 || byte === FORM_FEED_UTF8_BYTE || byte === 13 || byte === 32;
|
|
71876
|
+
const matchesAsciiCaseInsensitive = (sourceBuffer, startByte, expected) => {
|
|
71877
|
+
if (startByte + expected.length > sourceBuffer.length) return false;
|
|
71878
|
+
for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex++) {
|
|
71879
|
+
const sourceByte = sourceBuffer[startByte + expectedIndex];
|
|
71880
|
+
if (sourceByte === void 0 || String.fromCharCode(sourceByte).toLowerCase() !== expected[expectedIndex]) return false;
|
|
71881
|
+
}
|
|
71882
|
+
return true;
|
|
71883
|
+
};
|
|
71884
|
+
const isTagBoundary = (byte) => byte === TAG_CLOSE_UTF8_BYTE || byte === TAG_SELF_CLOSE_UTF8_BYTE || isHtmlWhitespace(byte);
|
|
71885
|
+
const isPotentialTagStart = (byte) => {
|
|
71886
|
+
if (byte === void 0) return false;
|
|
71887
|
+
return /[A-Za-z!/?]/.test(String.fromCharCode(byte));
|
|
71888
|
+
};
|
|
71889
|
+
const findLineEnd = (sourceBuffer, lineStartByte) => {
|
|
71890
|
+
let byteIndex = lineStartByte;
|
|
71891
|
+
while (byteIndex < sourceBuffer.length && sourceBuffer[byteIndex] !== 10 && sourceBuffer[byteIndex] !== 13) byteIndex++;
|
|
71892
|
+
return byteIndex;
|
|
71893
|
+
};
|
|
71894
|
+
const findNextLineStart = (sourceBuffer, lineStartByte) => {
|
|
71895
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
71896
|
+
if (sourceBuffer[lineEndByte] === 13 && sourceBuffer[lineEndByte + 1] === 10) return lineEndByte + 2;
|
|
71897
|
+
return Math.min(lineEndByte + 1, sourceBuffer.length);
|
|
71898
|
+
};
|
|
71899
|
+
const isFrontmatterFenceLine = (sourceBuffer, lineStartByte) => {
|
|
71900
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, lineStartByte, FRONTMATTER_FENCE)) return false;
|
|
71901
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
71902
|
+
for (let byteIndex = lineStartByte + 3; byteIndex < lineEndByte; byteIndex++) if (!isHtmlWhitespace(sourceBuffer[byteIndex])) return false;
|
|
71903
|
+
return true;
|
|
71904
|
+
};
|
|
71905
|
+
const findFrontmatterEnd = (sourceBuffer) => {
|
|
71906
|
+
if (!isFrontmatterFenceLine(sourceBuffer, 0)) return 0;
|
|
71907
|
+
const openingFenceEndByte = findLineEnd(sourceBuffer, 0);
|
|
71908
|
+
let lineStartByte = findNextLineStart(sourceBuffer, 0);
|
|
71909
|
+
while (lineStartByte < sourceBuffer.length) {
|
|
71910
|
+
if (isFrontmatterFenceLine(sourceBuffer, lineStartByte)) return findLineEnd(sourceBuffer, lineStartByte);
|
|
71911
|
+
lineStartByte = findNextLineStart(sourceBuffer, lineStartByte);
|
|
71912
|
+
}
|
|
71913
|
+
return openingFenceEndByte;
|
|
71914
|
+
};
|
|
71915
|
+
const maskByteRange = (maskedBuffer, startByte, endByte) => {
|
|
71916
|
+
for (let byteIndex = startByte; byteIndex < endByte; byteIndex++) {
|
|
71917
|
+
const byte = maskedBuffer[byteIndex];
|
|
71918
|
+
if (byte !== 10 && byte !== 13) maskedBuffer[byteIndex] = 32;
|
|
71919
|
+
}
|
|
71920
|
+
};
|
|
71921
|
+
const readStartTagName = (sourceBuffer, tagStartByte) => {
|
|
71922
|
+
const firstNameByte = sourceBuffer[tagStartByte + 1];
|
|
71923
|
+
if (firstNameByte === void 0 || !/[A-Za-z]/.test(String.fromCharCode(firstNameByte))) return null;
|
|
71924
|
+
let nameEndByte = tagStartByte + 2;
|
|
71925
|
+
while (nameEndByte < sourceBuffer.length && /[A-Za-z0-9:-]/.test(String.fromCharCode(sourceBuffer[nameEndByte] ?? 32))) nameEndByte++;
|
|
71926
|
+
return sourceBuffer.subarray(tagStartByte + 1, nameEndByte).toString("ascii").toLowerCase();
|
|
71927
|
+
};
|
|
71928
|
+
const findTagEnd = (sourceBuffer, startByte) => {
|
|
71929
|
+
let quoteByte = null;
|
|
71930
|
+
for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) {
|
|
71931
|
+
const byte = sourceBuffer[byteIndex];
|
|
71932
|
+
if (quoteByte !== null) {
|
|
71933
|
+
if (byte === quoteByte) quoteByte = null;
|
|
71934
|
+
continue;
|
|
71935
|
+
}
|
|
71936
|
+
if (byte === SINGLE_QUOTE_UTF8_BYTE || byte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
71937
|
+
quoteByte = byte;
|
|
71938
|
+
continue;
|
|
71939
|
+
}
|
|
71940
|
+
if (byte === TAG_CLOSE_UTF8_BYTE) return byteIndex;
|
|
71941
|
+
}
|
|
71942
|
+
return sourceBuffer.length;
|
|
71943
|
+
};
|
|
71944
|
+
const parseScriptAttributes = (sourceBuffer, attributesStartByte, tagEndByte) => {
|
|
71945
|
+
let byteIndex = attributesStartByte;
|
|
71946
|
+
let hasSource = false;
|
|
71947
|
+
let language = null;
|
|
71948
|
+
let scriptType = null;
|
|
71949
|
+
while (byteIndex < tagEndByte) {
|
|
71950
|
+
while (byteIndex < tagEndByte && (isHtmlWhitespace(sourceBuffer[byteIndex]) || sourceBuffer[byteIndex] === TAG_SELF_CLOSE_UTF8_BYTE)) byteIndex++;
|
|
71951
|
+
const attributeNameStartByte = byteIndex;
|
|
71952
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== ATTRIBUTE_ASSIGNMENT_UTF8_BYTE && sourceBuffer[byteIndex] !== TAG_SELF_CLOSE_UTF8_BYTE) byteIndex++;
|
|
71953
|
+
const attributeName = sourceBuffer.subarray(attributeNameStartByte, byteIndex).toString("ascii").toLowerCase();
|
|
71954
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
71955
|
+
let attributeValue = "";
|
|
71956
|
+
if (sourceBuffer[byteIndex] === ATTRIBUTE_ASSIGNMENT_UTF8_BYTE) {
|
|
71957
|
+
byteIndex++;
|
|
71958
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
71959
|
+
const quoteByte = sourceBuffer[byteIndex];
|
|
71960
|
+
if (quoteByte === SINGLE_QUOTE_UTF8_BYTE || quoteByte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
71961
|
+
const valueStartByte = byteIndex + 1;
|
|
71962
|
+
byteIndex = valueStartByte;
|
|
71963
|
+
while (byteIndex < tagEndByte && sourceBuffer[byteIndex] !== quoteByte) byteIndex++;
|
|
71964
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
71965
|
+
if (byteIndex < tagEndByte) byteIndex++;
|
|
71966
|
+
} else {
|
|
71967
|
+
const valueStartByte = byteIndex;
|
|
71968
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== TAG_CLOSE_UTF8_BYTE) byteIndex++;
|
|
71969
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
71970
|
+
}
|
|
71971
|
+
}
|
|
71972
|
+
if (attributeName === "src") hasSource = true;
|
|
71973
|
+
else if (attributeName === "type" && scriptType === null) scriptType = attributeValue;
|
|
71974
|
+
else if (attributeName === "language" && language === null) language = attributeValue;
|
|
71975
|
+
}
|
|
71976
|
+
return {
|
|
71977
|
+
hasSource,
|
|
71978
|
+
language,
|
|
71979
|
+
type: scriptType
|
|
71980
|
+
};
|
|
71981
|
+
};
|
|
71982
|
+
const isExecutableScript = (attributes) => {
|
|
71983
|
+
if (attributes.hasSource) return false;
|
|
71984
|
+
const type = attributes.type?.trim().toLowerCase() ?? "";
|
|
71985
|
+
if (type !== "") {
|
|
71986
|
+
const mimeType = type.split(";")[0]?.trim() ?? "";
|
|
71987
|
+
return mimeType === "module" || JAVASCRIPT_MIME_TYPES.has(mimeType);
|
|
71988
|
+
}
|
|
71989
|
+
const language = attributes.language?.trim().toLowerCase() ?? "";
|
|
71990
|
+
return language === "" || JAVASCRIPT_MIME_TYPES.has(`text/${language}`);
|
|
71991
|
+
};
|
|
71992
|
+
const findElementClose = (sourceBuffer, startByte, tagName) => {
|
|
71993
|
+
const closeTagStart = `</${tagName}`;
|
|
71994
|
+
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;
|
|
71995
|
+
return sourceBuffer.length;
|
|
71996
|
+
};
|
|
71997
|
+
const prepareHtmlScriptSource = (sourceBuffer) => {
|
|
71998
|
+
const frontmatterEndByte = findFrontmatterEnd(sourceBuffer);
|
|
71999
|
+
let maskedBuffer = frontmatterEndByte > 0 ? Buffer.from(sourceBuffer) : null;
|
|
72000
|
+
if (maskedBuffer !== null) maskByteRange(maskedBuffer, 0, frontmatterEndByte);
|
|
72001
|
+
const executableScriptBodies = [];
|
|
72002
|
+
let byteIndex = frontmatterEndByte;
|
|
72003
|
+
let templateDepth = 0;
|
|
72004
|
+
while (byteIndex < sourceBuffer.length) {
|
|
72005
|
+
if (sourceBuffer[byteIndex] !== TAG_OPEN_UTF8_BYTE) {
|
|
72006
|
+
byteIndex++;
|
|
72007
|
+
continue;
|
|
72008
|
+
}
|
|
72009
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, COMMENT_OPEN)) {
|
|
72010
|
+
const commentEndByte = sourceBuffer.indexOf(COMMENT_CLOSE, byteIndex + 4);
|
|
72011
|
+
byteIndex = commentEndByte === -1 ? sourceBuffer.length : commentEndByte + 3;
|
|
72012
|
+
continue;
|
|
72013
|
+
}
|
|
72014
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, TEMPLATE_CLOSE) && isTagBoundary(sourceBuffer[byteIndex + TEMPLATE_CLOSE.length])) {
|
|
72015
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + TEMPLATE_CLOSE.length);
|
|
72016
|
+
if (templateDepth > 0) templateDepth--;
|
|
72017
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
72018
|
+
continue;
|
|
72019
|
+
}
|
|
72020
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, SCRIPT_OPEN) || !isTagBoundary(sourceBuffer[byteIndex + SCRIPT_OPEN.length])) {
|
|
72021
|
+
if (!isPotentialTagStart(sourceBuffer[byteIndex + 1])) {
|
|
72022
|
+
byteIndex++;
|
|
72023
|
+
continue;
|
|
72024
|
+
}
|
|
72025
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + 1);
|
|
72026
|
+
const tagName = readStartTagName(sourceBuffer, byteIndex);
|
|
72027
|
+
if (tagName === PLAINTEXT_TAG_NAME) break;
|
|
72028
|
+
if (tagName === TEMPLATE_TAG_NAME) templateDepth++;
|
|
72029
|
+
if (tagName !== null && RAW_TEXT_TAG_NAMES.has(tagName)) {
|
|
72030
|
+
const closeTagStartByte = findElementClose(sourceBuffer, tagEndByte + 1, tagName);
|
|
72031
|
+
if (closeTagStartByte === sourceBuffer.length) break;
|
|
72032
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, closeTagStartByte + tagName.length + 2);
|
|
72033
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
72034
|
+
continue;
|
|
72035
|
+
}
|
|
72036
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
72037
|
+
continue;
|
|
72038
|
+
}
|
|
72039
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + SCRIPT_OPEN.length);
|
|
72040
|
+
if (tagEndByte === sourceBuffer.length) break;
|
|
72041
|
+
const bodyStartByte = tagEndByte + 1;
|
|
72042
|
+
const bodyEndByte = findElementClose(sourceBuffer, bodyStartByte, SCRIPT_TAG_NAME);
|
|
72043
|
+
const attributes = parseScriptAttributes(sourceBuffer, byteIndex + SCRIPT_OPEN.length, tagEndByte);
|
|
72044
|
+
if (attributes.hasSource || templateDepth > 0) {
|
|
72045
|
+
maskedBuffer ??= Buffer.from(sourceBuffer);
|
|
72046
|
+
maskByteRange(maskedBuffer, bodyStartByte, bodyEndByte);
|
|
72047
|
+
} else if (isExecutableScript(attributes)) executableScriptBodies.push(sourceBuffer.subarray(bodyStartByte, bodyEndByte));
|
|
72048
|
+
if (bodyEndByte === sourceBuffer.length) break;
|
|
72049
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, bodyEndByte + SCRIPT_CLOSE.length);
|
|
72050
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
72051
|
+
}
|
|
72052
|
+
return {
|
|
72053
|
+
executableScriptBodies,
|
|
72054
|
+
lintBuffer: maskedBuffer ?? sourceBuffer
|
|
72055
|
+
};
|
|
72056
|
+
};
|
|
72057
|
+
const prepareHtmlLintSources = (rootDirectory, temporaryDirectory, candidateFiles) => {
|
|
72058
|
+
const lintFiles = [];
|
|
72059
|
+
const sourcePathByLintPath = /* @__PURE__ */ new Map();
|
|
72060
|
+
const sizeByLintPath = /* @__PURE__ */ new Map();
|
|
72061
|
+
const htmlSourcesDirectory = Path.join(temporaryDirectory, "html-sources");
|
|
72062
|
+
let htmlFileIndex = 0;
|
|
72063
|
+
let hasThreeModuleImport = false;
|
|
72064
|
+
for (const candidateFile of candidateFiles) {
|
|
72065
|
+
if (!HTML_FILE_PATTERN.test(candidateFile)) {
|
|
72066
|
+
lintFiles.push(candidateFile);
|
|
72067
|
+
continue;
|
|
72068
|
+
}
|
|
72069
|
+
const absoluteSourcePath = Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile);
|
|
72070
|
+
const sourceBuffer = NFS.readFileSync(absoluteSourcePath);
|
|
72071
|
+
const { executableScriptBodies, lintBuffer } = prepareHtmlScriptSource(sourceBuffer);
|
|
72072
|
+
if (htmlFileIndex === 0) NFS.mkdirSync(htmlSourcesDirectory, { recursive: true });
|
|
72073
|
+
const lintPath = Path.join(htmlSourcesDirectory, `${htmlFileIndex}.astro`);
|
|
72074
|
+
NFS.writeFileSync(lintPath, lintBuffer);
|
|
72075
|
+
lintFiles.push(lintPath);
|
|
72076
|
+
sourcePathByLintPath.set(Path.resolve(lintPath), candidateFile);
|
|
72077
|
+
sizeByLintPath.set(lintPath, sourceBuffer.length);
|
|
72078
|
+
hasThreeModuleImport ||= executableScriptBodies.some((scriptBody) => containsThreeModuleImport(scriptBody.toString("utf8")));
|
|
72079
|
+
htmlFileIndex++;
|
|
72080
|
+
}
|
|
72081
|
+
return {
|
|
72082
|
+
lintFiles,
|
|
72083
|
+
sourcePathByLintPath,
|
|
72084
|
+
sizeByLintPath,
|
|
72085
|
+
hasThreeModuleImport
|
|
72086
|
+
};
|
|
72087
|
+
};
|
|
71492
72088
|
/**
|
|
71493
72089
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
71494
72090
|
* the runner: once for the primary scan, once for the
|
|
@@ -71662,21 +72258,6 @@ const runOxlint = async (options) => {
|
|
|
71662
72258
|
const pluginPath = resolvePluginPath();
|
|
71663
72259
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
71664
72260
|
const userPlugins = includedTags.size > 0 ? [] : resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
71665
|
-
const buildConfig = (overrides) => createOxlintConfig({
|
|
71666
|
-
pluginPath,
|
|
71667
|
-
project,
|
|
71668
|
-
customRulesOnly,
|
|
71669
|
-
extendsPaths: overrides.extendsPaths,
|
|
71670
|
-
ignoredTags,
|
|
71671
|
-
includedTags,
|
|
71672
|
-
includeTagDefaults,
|
|
71673
|
-
serverAuthFunctionNames,
|
|
71674
|
-
severityControls,
|
|
71675
|
-
userPlugins,
|
|
71676
|
-
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
71677
|
-
ruleSelection: overrides.ruleSelection,
|
|
71678
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
71679
|
-
});
|
|
71680
72261
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
71681
72262
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
71682
72263
|
const configPath = Path.join(configDirectory, "oxlintrc.json");
|
|
@@ -71712,7 +72293,30 @@ const runOxlint = async (options) => {
|
|
|
71712
72293
|
await yieldToEventLoop();
|
|
71713
72294
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
71714
72295
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
72296
|
+
const preparedHtmlSources = prepareHtmlLintSources(rootDirectory, configDirectory, candidateFiles);
|
|
72297
|
+
const lintFiles = preparedHtmlSources.lintFiles;
|
|
72298
|
+
const hasHtmlFiles = preparedHtmlSources.sourcePathByLintPath.size > 0;
|
|
72299
|
+
const lintProject = !project.hasThree && preparedHtmlSources.hasThreeModuleImport ? {
|
|
72300
|
+
...project,
|
|
72301
|
+
hasThree: true
|
|
72302
|
+
} : project;
|
|
72303
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
72304
|
+
pluginPath,
|
|
72305
|
+
project: lintProject,
|
|
72306
|
+
customRulesOnly,
|
|
72307
|
+
extendsPaths: overrides.extendsPaths,
|
|
72308
|
+
ignoredTags,
|
|
72309
|
+
includedTags,
|
|
72310
|
+
includeTagDefaults,
|
|
72311
|
+
serverAuthFunctionNames,
|
|
72312
|
+
severityControls,
|
|
72313
|
+
userPlugins,
|
|
72314
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
72315
|
+
ruleSelection: overrides.ruleSelection,
|
|
72316
|
+
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
72317
|
+
});
|
|
71715
72318
|
const sizeByFile = sizedScanFiles !== null && lintBatchOrdering === "cost" ? new Map(sizedScanFiles.map((entry) => [entry.path, entry.sizeBytes])) : null;
|
|
72319
|
+
if (sizeByFile !== null) for (const [lintPath, sizeBytes] of preparedHtmlSources.sizeByLintPath) sizeByFile.set(lintPath, sizeBytes);
|
|
71716
72320
|
const buildFileBatches = (passBaseArgs, passFiles) => sizeByFile !== null ? planLintBatches({
|
|
71717
72321
|
baseArgs: passBaseArgs,
|
|
71718
72322
|
files: passFiles,
|
|
@@ -71741,7 +72345,8 @@ const runOxlint = async (options) => {
|
|
|
71741
72345
|
fileBatches: passFileBatches,
|
|
71742
72346
|
rootDirectory,
|
|
71743
72347
|
nodeBinaryPath,
|
|
71744
|
-
project,
|
|
72348
|
+
project: lintProject,
|
|
72349
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
71745
72350
|
onPartialFailure: reportPartialFailure,
|
|
71746
72351
|
onAnalyzedFiles: (filePaths) => {
|
|
71747
72352
|
analyzedFiles = filePaths;
|
|
@@ -71776,7 +72381,7 @@ const runOxlint = async (options) => {
|
|
|
71776
72381
|
};
|
|
71777
72382
|
}
|
|
71778
72383
|
};
|
|
71779
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
72384
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0 && !hasHtmlFiles) {
|
|
71780
72385
|
const rulesetHash = computeRulesetHash({
|
|
71781
72386
|
config: buildConfig({
|
|
71782
72387
|
extendsPaths: [],
|
|
@@ -71934,14 +72539,15 @@ const runOxlint = async (options) => {
|
|
|
71934
72539
|
]);
|
|
71935
72540
|
}
|
|
71936
72541
|
const baseArgs = makeBaseArgs(configPath);
|
|
71937
|
-
const fileBatches = buildFileBatches(baseArgs,
|
|
72542
|
+
const fileBatches = buildFileBatches(baseArgs, lintFiles);
|
|
71938
72543
|
let analyzedFiles = [];
|
|
71939
72544
|
const runBatches = () => spawnLintBatches({
|
|
71940
72545
|
baseArgs,
|
|
71941
72546
|
fileBatches,
|
|
71942
72547
|
rootDirectory,
|
|
71943
72548
|
nodeBinaryPath,
|
|
71944
|
-
project,
|
|
72549
|
+
project: lintProject,
|
|
72550
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
71945
72551
|
onPartialFailure,
|
|
71946
72552
|
onAnalyzedFiles: (filePaths) => {
|
|
71947
72553
|
analyzedFiles = filePaths;
|
|
@@ -73517,7 +74123,8 @@ const SCANNABLE_EXTENSIONS = [
|
|
|
73517
74123
|
".js",
|
|
73518
74124
|
".jsx",
|
|
73519
74125
|
".mts",
|
|
73520
|
-
".mjs"
|
|
74126
|
+
".mjs",
|
|
74127
|
+
".html"
|
|
73521
74128
|
];
|
|
73522
74129
|
const SILENT_LOGGER = {
|
|
73523
74130
|
info: () => {},
|
|
@@ -75505,5 +76112,5 @@ const startLanguageServer = () => {
|
|
|
75505
76112
|
};
|
|
75506
76113
|
//#endregion
|
|
75507
76114
|
export { startLanguageServer };
|
|
75508
|
-
!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]="
|
|
75509
|
-
//# debugId=
|
|
76115
|
+
!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]="8f4550d9-ab09-5b95-8e16-28b534d81552")}catch(e){}}();
|
|
76116
|
+
//# debugId=8f4550d9-ab09-5b95-8e16-28b534d81552
|