react-doctor 0.9.0 → 0.9.1-dev.5d2f66d
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/dist/cli.js +351 -32
- package/dist/index.js +347 -28
- package/dist/lsp.js +348 -29
- package/package.json +6 -6
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]="6d9ce7c4-f6fc-597c-8340-3d408ed860e3")}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";
|
|
@@ -61791,7 +61791,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
61791
61791
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
61792
61792
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
61793
61793
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
61794
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
61794
|
+
const CORE_PACKAGE_VERSION = "0.9.1";
|
|
61795
61795
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
61796
61796
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
61797
61797
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -62182,6 +62182,7 @@ const frameworkMergeRank = (framework) => {
|
|
|
62182
62182
|
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
62183
62183
|
};
|
|
62184
62184
|
const REACT_COMPILER_LINT_PACKAGES = new Set(["eslint-plugin-react-compiler"]);
|
|
62185
|
+
const REACT_COMPILER_RUNTIME_PACKAGES = new Set(["react-compiler-runtime"]);
|
|
62185
62186
|
const NEXT_CONFIG_FILENAMES = [
|
|
62186
62187
|
"next.config.js",
|
|
62187
62188
|
"next.config.mjs",
|
|
@@ -62284,7 +62285,7 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
|
|
|
62284
62285
|
]).find(isFile) ?? null;
|
|
62285
62286
|
};
|
|
62286
62287
|
const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
62287
|
-
const getStaticPropertyName$
|
|
62288
|
+
const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
|
|
62288
62289
|
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");
|
|
62289
62290
|
const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
62290
62291
|
const getRequireModuleSpecifier = (expression) => {
|
|
@@ -62348,7 +62349,7 @@ const getImportBinding = (sourceFile, bindingName) => {
|
|
|
62348
62349
|
const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
|
|
62349
62350
|
if (bindingElement && moduleSpecifier) return {
|
|
62350
62351
|
moduleSpecifier,
|
|
62351
|
-
exportName: bindingElement.propertyName ? getStaticPropertyName$
|
|
62352
|
+
exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
|
|
62352
62353
|
isNamespace: false
|
|
62353
62354
|
};
|
|
62354
62355
|
}
|
|
@@ -62509,7 +62510,7 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
62509
62510
|
}
|
|
62510
62511
|
if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
|
|
62511
62512
|
for (const property of [...unwrappedExpression.properties].reverse()) {
|
|
62512
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62513
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
|
|
62513
62514
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
|
|
62514
62515
|
if (ts.isSpreadAssignment(property)) {
|
|
62515
62516
|
const spreadState = getReactCompilerFlagState(property.expression, analysis);
|
|
@@ -62535,8 +62536,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
62535
62536
|
}
|
|
62536
62537
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
62537
62538
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
62538
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62539
|
-
if (ts.isMethodDeclaration(property) && getStaticPropertyName$
|
|
62539
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === propertyName) return property.initializer;
|
|
62540
|
+
if (ts.isMethodDeclaration(property) && getStaticPropertyName$2(property.name) === propertyName) return property;
|
|
62540
62541
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
62541
62542
|
if (ts.isSpreadAssignment(property)) {
|
|
62542
62543
|
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
@@ -62567,7 +62568,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
|
|
|
62567
62568
|
return resolvedExpression.properties.some((property) => {
|
|
62568
62569
|
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
62569
62570
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
62570
|
-
const staticPropertyName = getStaticPropertyName$
|
|
62571
|
+
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
62571
62572
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
62572
62573
|
}
|
|
62573
62574
|
return false;
|
|
@@ -62586,7 +62587,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
|
|
|
62586
62587
|
continue;
|
|
62587
62588
|
}
|
|
62588
62589
|
if (ts.isExpressionStatement(statement) && isCommonJsConfigExportAssignment(statement.expression, sourceFile) && exportName !== "default" && ts.isObjectLiteralExpression(statement.expression.right)) for (const property of [...statement.expression.right.properties].reverse()) {
|
|
62589
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62590
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
|
|
62590
62591
|
exportedNodes.push(property.initializer);
|
|
62591
62592
|
break;
|
|
62592
62593
|
}
|
|
@@ -62745,10 +62746,10 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62745
62746
|
const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
|
|
62746
62747
|
if (reactCompilerFlagState?.isEnabled) return true;
|
|
62747
62748
|
for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
|
|
62748
|
-
const propertyName = getStaticPropertyName$
|
|
62749
|
+
const propertyName = getStaticPropertyName$2(property.name);
|
|
62749
62750
|
if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
|
|
62750
62751
|
if (propertyName === "reactCompiler") continue;
|
|
62751
|
-
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$
|
|
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$2(laterProperty.name) === propertyName)) continue;
|
|
62752
62753
|
const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
|
|
62753
62754
|
if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
|
|
62754
62755
|
const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
|
|
@@ -62758,12 +62759,12 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62758
62759
|
} else if (ts.isShorthandPropertyAssignment(property)) {
|
|
62759
62760
|
if (excludedPropertyNames?.has(property.name.text)) continue;
|
|
62760
62761
|
if (property.name.text === "reactCompiler") continue;
|
|
62761
|
-
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$
|
|
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$2(laterProperty.name) === property.name.text)) continue;
|
|
62762
62763
|
const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
|
|
62763
62764
|
if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
|
|
62764
62765
|
} else if (ts.isSpreadAssignment(property)) {
|
|
62765
62766
|
if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
|
|
62766
|
-
if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$
|
|
62767
|
+
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;
|
|
62767
62768
|
if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
|
|
62768
62769
|
}
|
|
62769
62770
|
return false;
|
|
@@ -62888,7 +62889,7 @@ const hasCompilerConfigurationInAncestors = (directory) => {
|
|
|
62888
62889
|
}
|
|
62889
62890
|
return false;
|
|
62890
62891
|
};
|
|
62891
|
-
const detectReactCompiler = (directory, packageJson) => hasCompilerConfiguration(directory, packageJson) || hasCompilerConfigurationInAncestors(directory);
|
|
62892
|
+
const detectReactCompiler = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_RUNTIME_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_RUNTIME_PACKAGES) || hasCompilerConfiguration(directory, packageJson) || hasCompilerConfigurationInAncestors(directory);
|
|
62892
62893
|
const detectReactCompilerLintPlugin = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_LINT_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_LINT_PACKAGES);
|
|
62893
62894
|
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
62894
62895
|
const filePath = Path.join(directory, filename);
|
|
@@ -66976,6 +66977,321 @@ const buildReactNativeDiagnostic = (input) => ({
|
|
|
66976
66977
|
column: input.column ?? 0,
|
|
66977
66978
|
category: input.category ?? "Correctness"
|
|
66978
66979
|
});
|
|
66980
|
+
const ANDROID_APP_BUILD_FILE_NAMES = ["build.gradle", "build.gradle.kts"];
|
|
66981
|
+
const BUILD_TYPES_BLOCK_PATTERN = /\bbuildTypes\s*\{/g;
|
|
66982
|
+
const RELEASE_STRING_MARKER = "__RD_RELEASE__";
|
|
66983
|
+
const RELEASE_BLOCK_PATTERNS = [/\brelease\s*\{/g, /\b(?:getByName|named)\s*\(\s*(?:["']release["']|__RD_RELEASE__)\s*\)\s*\{/g];
|
|
66984
|
+
const MINIFY_ASSIGNMENT_PATTERN = /\b(?:isMinifyEnabled|minifyEnabled)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66985
|
+
const SHRINK_RESOURCES_ASSIGNMENT_PATTERN = /\b(?:isShrinkResources|shrinkResources)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
66986
|
+
const stripGradleComments = (contents) => {
|
|
66987
|
+
let result = "";
|
|
66988
|
+
let index = 0;
|
|
66989
|
+
let quote = null;
|
|
66990
|
+
while (index < contents.length) {
|
|
66991
|
+
const character = contents[index];
|
|
66992
|
+
const nextCharacter = contents[index + 1];
|
|
66993
|
+
if (quote !== null) {
|
|
66994
|
+
result += character;
|
|
66995
|
+
if (character === "\\") {
|
|
66996
|
+
result += nextCharacter ?? "";
|
|
66997
|
+
index += 2;
|
|
66998
|
+
continue;
|
|
66999
|
+
}
|
|
67000
|
+
if (character === quote) quote = null;
|
|
67001
|
+
index += 1;
|
|
67002
|
+
continue;
|
|
67003
|
+
}
|
|
67004
|
+
if (character === "\"" || character === "'") {
|
|
67005
|
+
quote = character;
|
|
67006
|
+
result += character;
|
|
67007
|
+
index += 1;
|
|
67008
|
+
continue;
|
|
67009
|
+
}
|
|
67010
|
+
if (character === "/" && nextCharacter === "/") {
|
|
67011
|
+
while (index < contents.length && contents[index] !== "\n") {
|
|
67012
|
+
result += " ";
|
|
67013
|
+
index += 1;
|
|
67014
|
+
}
|
|
67015
|
+
continue;
|
|
67016
|
+
}
|
|
67017
|
+
if (character === "/" && nextCharacter === "*") {
|
|
67018
|
+
result += " ";
|
|
67019
|
+
index += 2;
|
|
67020
|
+
while (index < contents.length && !(contents[index] === "*" && contents[index + 1] === "/")) {
|
|
67021
|
+
result += contents[index] === "\n" ? "\n" : " ";
|
|
67022
|
+
index += 1;
|
|
67023
|
+
}
|
|
67024
|
+
if (index < contents.length) {
|
|
67025
|
+
result += " ";
|
|
67026
|
+
index += 2;
|
|
67027
|
+
}
|
|
67028
|
+
continue;
|
|
67029
|
+
}
|
|
67030
|
+
result += character;
|
|
67031
|
+
index += 1;
|
|
67032
|
+
}
|
|
67033
|
+
return result;
|
|
67034
|
+
};
|
|
67035
|
+
const maskGradleStrings = (contents) => {
|
|
67036
|
+
let result = "";
|
|
67037
|
+
let index = 0;
|
|
67038
|
+
let quote = null;
|
|
67039
|
+
while (index < contents.length) {
|
|
67040
|
+
const character = contents[index];
|
|
67041
|
+
if (quote === null) {
|
|
67042
|
+
if (character === "\"" || character === "'") {
|
|
67043
|
+
quote = character;
|
|
67044
|
+
result += " ";
|
|
67045
|
+
} else result += character;
|
|
67046
|
+
index += 1;
|
|
67047
|
+
continue;
|
|
67048
|
+
}
|
|
67049
|
+
if (character === "\\") {
|
|
67050
|
+
result += " ";
|
|
67051
|
+
index += 2;
|
|
67052
|
+
continue;
|
|
67053
|
+
}
|
|
67054
|
+
if (character === quote) quote = null;
|
|
67055
|
+
result += character === "\n" ? "\n" : " ";
|
|
67056
|
+
index += 1;
|
|
67057
|
+
}
|
|
67058
|
+
return result;
|
|
67059
|
+
};
|
|
67060
|
+
const findBlockContents = (contents, pattern) => {
|
|
67061
|
+
const blocks = [];
|
|
67062
|
+
pattern.lastIndex = 0;
|
|
67063
|
+
let match = pattern.exec(contents);
|
|
67064
|
+
while (match !== null) {
|
|
67065
|
+
const openingBraceIndex = pattern.lastIndex - 1;
|
|
67066
|
+
let depth = 1;
|
|
67067
|
+
let index = openingBraceIndex + 1;
|
|
67068
|
+
let quote = null;
|
|
67069
|
+
while (index < contents.length && depth > 0) {
|
|
67070
|
+
const character = contents[index];
|
|
67071
|
+
if (quote !== null) {
|
|
67072
|
+
if (character === "\\") {
|
|
67073
|
+
index += 2;
|
|
67074
|
+
continue;
|
|
67075
|
+
}
|
|
67076
|
+
if (character === quote) quote = null;
|
|
67077
|
+
} else if (character === "\"" || character === "'") quote = character;
|
|
67078
|
+
else if (character === "{") depth += 1;
|
|
67079
|
+
else if (character === "}") depth -= 1;
|
|
67080
|
+
index += 1;
|
|
67081
|
+
}
|
|
67082
|
+
if (depth === 0) blocks.push(contents.slice(openingBraceIndex + 1, index - 1));
|
|
67083
|
+
match = pattern.exec(contents);
|
|
67084
|
+
}
|
|
67085
|
+
return blocks;
|
|
67086
|
+
};
|
|
67087
|
+
const getFinalStaticBoolean = (contents, pattern) => {
|
|
67088
|
+
pattern.lastIndex = 0;
|
|
67089
|
+
let finalValue = null;
|
|
67090
|
+
let match = pattern.exec(contents);
|
|
67091
|
+
while (match !== null) {
|
|
67092
|
+
const staticValue = match[1]?.trim().replace(/\)$/, "").trim();
|
|
67093
|
+
if (staticValue === "true") finalValue = true;
|
|
67094
|
+
else if (staticValue === "false") finalValue = false;
|
|
67095
|
+
else finalValue = null;
|
|
67096
|
+
match = pattern.exec(contents);
|
|
67097
|
+
}
|
|
67098
|
+
return finalValue;
|
|
67099
|
+
};
|
|
67100
|
+
const getReleaseBlocks = (contents) => findBlockContents(contents, BUILD_TYPES_BLOCK_PATTERN).flatMap((buildTypesBlock) => RELEASE_BLOCK_PATTERNS.flatMap((pattern) => findBlockContents(buildTypesBlock, pattern)));
|
|
67101
|
+
const readAndroidReleaseShrinking = (rootDirectory) => {
|
|
67102
|
+
for (const buildFileName of ANDROID_APP_BUILD_FILE_NAMES) {
|
|
67103
|
+
const filePath = Path.posix.join("android", "app", buildFileName);
|
|
67104
|
+
const absoluteFilePath = Path.join(rootDirectory, filePath);
|
|
67105
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67106
|
+
let contents;
|
|
67107
|
+
try {
|
|
67108
|
+
contents = stripGradleComments(NFS.readFileSync(absoluteFilePath, "utf-8"));
|
|
67109
|
+
} catch {
|
|
67110
|
+
return null;
|
|
67111
|
+
}
|
|
67112
|
+
const releaseBlocks = getReleaseBlocks(maskGradleStrings(contents.replace(/(["'])release\1/g, RELEASE_STRING_MARKER)));
|
|
67113
|
+
const [releaseBlock] = releaseBlocks;
|
|
67114
|
+
if (releaseBlock === void 0 || releaseBlocks.length !== 1) return null;
|
|
67115
|
+
const hasDisabledMinification = getFinalStaticBoolean(releaseBlock, MINIFY_ASSIGNMENT_PATTERN) === false;
|
|
67116
|
+
const hasDisabledResourceShrinking = getFinalStaticBoolean(releaseBlock, SHRINK_RESOURCES_ASSIGNMENT_PATTERN) === false;
|
|
67117
|
+
if (!hasDisabledMinification && !hasDisabledResourceShrinking) return null;
|
|
67118
|
+
return {
|
|
67119
|
+
filePath,
|
|
67120
|
+
hasDisabledMinification,
|
|
67121
|
+
hasDisabledResourceShrinking
|
|
67122
|
+
};
|
|
67123
|
+
}
|
|
67124
|
+
return null;
|
|
67125
|
+
};
|
|
67126
|
+
const checkReactNativeAndroidReleaseShrinking = (rootDirectory) => {
|
|
67127
|
+
const releaseShrinking = readAndroidReleaseShrinking(rootDirectory);
|
|
67128
|
+
if (releaseShrinking === null) return [];
|
|
67129
|
+
const disabledFeatures = [releaseShrinking.hasDisabledMinification ? "code minification" : null, releaseShrinking.hasDisabledResourceShrinking ? "resource shrinking" : null].filter((feature) => feature !== null);
|
|
67130
|
+
return [buildReactNativeDiagnostic({
|
|
67131
|
+
filePath: releaseShrinking.filePath,
|
|
67132
|
+
rule: "rn-android-release-shrinking-disabled",
|
|
67133
|
+
category: "Performance",
|
|
67134
|
+
message: `The Android release build explicitly disables ${disabledFeatures.join(" and ")}, leaving unreachable code or resources in the shipped app.`,
|
|
67135
|
+
help: "Enable release code minification and resource shrinking, then test the optimized build and add keep rules for libraries that require them."
|
|
67136
|
+
})];
|
|
67137
|
+
};
|
|
67138
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
67139
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67140
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67141
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67142
|
+
return ts.ScriptKind.JS;
|
|
67143
|
+
};
|
|
67144
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
67145
|
+
let currentExpression = expression;
|
|
67146
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67147
|
+
return currentExpression;
|
|
67148
|
+
};
|
|
67149
|
+
const BABEL_CONFIG_FILE_NAMES$1 = [
|
|
67150
|
+
"babel.config.js",
|
|
67151
|
+
"babel.config.cjs",
|
|
67152
|
+
"babel.config.mjs",
|
|
67153
|
+
"babel.config.ts",
|
|
67154
|
+
"babel.config.cts",
|
|
67155
|
+
"babel.config.json",
|
|
67156
|
+
".babelrc",
|
|
67157
|
+
".babelrc.js",
|
|
67158
|
+
".babelrc.cjs",
|
|
67159
|
+
".babelrc.mjs",
|
|
67160
|
+
".babelrc.cts",
|
|
67161
|
+
".babelrc.json"
|
|
67162
|
+
];
|
|
67163
|
+
const readUnknownPluginNames = (value) => {
|
|
67164
|
+
if (!Array.isArray(value)) return null;
|
|
67165
|
+
const pluginNames = [];
|
|
67166
|
+
for (const entry of value) {
|
|
67167
|
+
if (typeof entry === "string") {
|
|
67168
|
+
pluginNames.push(entry);
|
|
67169
|
+
continue;
|
|
67170
|
+
}
|
|
67171
|
+
if (Array.isArray(entry) && typeof entry[0] === "string") {
|
|
67172
|
+
pluginNames.push(entry[0]);
|
|
67173
|
+
continue;
|
|
67174
|
+
}
|
|
67175
|
+
return null;
|
|
67176
|
+
}
|
|
67177
|
+
return pluginNames;
|
|
67178
|
+
};
|
|
67179
|
+
const getStaticPropertyName$1 = (propertyName) => {
|
|
67180
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) return propertyName.text;
|
|
67181
|
+
return null;
|
|
67182
|
+
};
|
|
67183
|
+
const getStaticPluginName = (expression) => {
|
|
67184
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
67185
|
+
if (ts.isStringLiteralLike(unwrappedExpression)) return unwrappedExpression.text;
|
|
67186
|
+
if (!ts.isArrayLiteralExpression(unwrappedExpression)) return null;
|
|
67187
|
+
const pluginExpression = unwrappedExpression.elements[0];
|
|
67188
|
+
return pluginExpression && ts.isStringLiteralLike(pluginExpression) ? pluginExpression.text : null;
|
|
67189
|
+
};
|
|
67190
|
+
const getPluginNamesFromObject = (objectExpression) => {
|
|
67191
|
+
if (objectExpression.properties.some((property) => ts.isSpreadAssignment(property))) return null;
|
|
67192
|
+
const pluginProperties = objectExpression.properties.filter((property) => ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "plugins");
|
|
67193
|
+
if (pluginProperties.length !== 1) return pluginProperties.length === 0 ? [] : null;
|
|
67194
|
+
const pluginListExpression = unwrapTypescriptExpression(pluginProperties[0].initializer);
|
|
67195
|
+
if (!ts.isArrayLiteralExpression(pluginListExpression)) return null;
|
|
67196
|
+
const pluginNames = [];
|
|
67197
|
+
for (const element of pluginListExpression.elements) {
|
|
67198
|
+
if (ts.isSpreadElement(element)) return null;
|
|
67199
|
+
const pluginName = getStaticPluginName(element);
|
|
67200
|
+
if (pluginName === null) return null;
|
|
67201
|
+
pluginNames.push(pluginName);
|
|
67202
|
+
}
|
|
67203
|
+
return pluginNames;
|
|
67204
|
+
};
|
|
67205
|
+
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";
|
|
67206
|
+
const getDirectExportedObject = (sourceFile) => {
|
|
67207
|
+
const exportedExpressions = [];
|
|
67208
|
+
for (const statement of sourceFile.statements) {
|
|
67209
|
+
if (ts.isExportAssignment(statement)) {
|
|
67210
|
+
exportedExpressions.push(statement.expression);
|
|
67211
|
+
continue;
|
|
67212
|
+
}
|
|
67213
|
+
if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && isModuleExports(statement.expression.left)) exportedExpressions.push(statement.expression.right);
|
|
67214
|
+
}
|
|
67215
|
+
if (exportedExpressions.length !== 1) return null;
|
|
67216
|
+
const exportedExpression = unwrapTypescriptExpression(exportedExpressions[0]);
|
|
67217
|
+
return ts.isObjectLiteralExpression(exportedExpression) ? exportedExpression : null;
|
|
67218
|
+
};
|
|
67219
|
+
const parseConfigPluginNames = (filePath, contents) => {
|
|
67220
|
+
try {
|
|
67221
|
+
const parsedJson = JSON.parse(contents);
|
|
67222
|
+
return isPlainObject(parsedJson) ? readUnknownPluginNames(parsedJson.plugins) : null;
|
|
67223
|
+
} catch {
|
|
67224
|
+
if (ts.transpileModule(contents, {
|
|
67225
|
+
fileName: filePath,
|
|
67226
|
+
reportDiagnostics: true,
|
|
67227
|
+
compilerOptions: { allowJs: true }
|
|
67228
|
+
}).diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) return null;
|
|
67229
|
+
const exportedObject = getDirectExportedObject(ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filePath)));
|
|
67230
|
+
return exportedObject === null ? null : getPluginNamesFromObject(exportedObject);
|
|
67231
|
+
}
|
|
67232
|
+
};
|
|
67233
|
+
const readStaticBabelPluginNames = (rootDirectory) => {
|
|
67234
|
+
for (const fileName of BABEL_CONFIG_FILE_NAMES$1) {
|
|
67235
|
+
const absoluteFilePath = Path.join(rootDirectory, fileName);
|
|
67236
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
67237
|
+
try {
|
|
67238
|
+
return {
|
|
67239
|
+
filePath: fileName,
|
|
67240
|
+
pluginNames: parseConfigPluginNames(fileName, NFS.readFileSync(absoluteFilePath, "utf-8"))
|
|
67241
|
+
};
|
|
67242
|
+
} catch {
|
|
67243
|
+
return {
|
|
67244
|
+
filePath: fileName,
|
|
67245
|
+
pluginNames: null
|
|
67246
|
+
};
|
|
67247
|
+
}
|
|
67248
|
+
}
|
|
67249
|
+
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
67250
|
+
if (!isPlainObject(packageJson.babel)) return null;
|
|
67251
|
+
return {
|
|
67252
|
+
filePath: "package.json",
|
|
67253
|
+
pluginNames: readUnknownPluginNames(packageJson.babel.plugins)
|
|
67254
|
+
};
|
|
67255
|
+
};
|
|
67256
|
+
const REACT_COMPILER_PLUGIN_NAMES = new Set(["babel-plugin-react-compiler", "react-compiler"]);
|
|
67257
|
+
const REANIMATED_PLUGIN_NAME = "react-native-reanimated/plugin";
|
|
67258
|
+
const WORKLETS_PLUGIN_NAME = "react-native-worklets/plugin";
|
|
67259
|
+
const checkReactCompilerPluginOrder = (filePath, pluginNames) => {
|
|
67260
|
+
if (pluginNames.findIndex((pluginName) => REACT_COMPILER_PLUGIN_NAMES.has(pluginName)) <= 0) return [];
|
|
67261
|
+
return [buildReactNativeDiagnostic({
|
|
67262
|
+
filePath,
|
|
67263
|
+
rule: "rn-react-compiler-plugin-first",
|
|
67264
|
+
category: "Performance",
|
|
67265
|
+
message: "The React Compiler Babel plugin is not first, so an earlier transform can change component or hook code before the compiler analyzes it.",
|
|
67266
|
+
help: "Move `babel-plugin-react-compiler` to the first entry in the Babel `plugins` array."
|
|
67267
|
+
})];
|
|
67268
|
+
};
|
|
67269
|
+
const checkReanimatedPluginOrder = (project, filePath, pluginNames) => {
|
|
67270
|
+
const reanimatedMajorVersion = project.reanimatedVersion === null || project.reanimatedVersion === void 0 ? null : getLowestDependencyMajor(project.reanimatedVersion);
|
|
67271
|
+
if (reanimatedMajorVersion === null || reanimatedMajorVersion < 4) return [];
|
|
67272
|
+
const diagnostics = [];
|
|
67273
|
+
if (pluginNames.includes(REANIMATED_PLUGIN_NAME)) diagnostics.push(buildReactNativeDiagnostic({
|
|
67274
|
+
filePath,
|
|
67275
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67276
|
+
category: "Performance",
|
|
67277
|
+
message: "This Reanimated 4 project still uses `react-native-reanimated/plugin`, which was renamed to the Worklets Babel plugin.",
|
|
67278
|
+
help: "Replace it with `react-native-worklets/plugin` and keep that plugin last in the Babel `plugins` array."
|
|
67279
|
+
}));
|
|
67280
|
+
const workletsPluginIndex = pluginNames.lastIndexOf(WORKLETS_PLUGIN_NAME);
|
|
67281
|
+
if (workletsPluginIndex !== -1 && workletsPluginIndex !== pluginNames.length - 1) diagnostics.push(buildReactNativeDiagnostic({
|
|
67282
|
+
filePath,
|
|
67283
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
67284
|
+
category: "Performance",
|
|
67285
|
+
message: "`react-native-worklets/plugin` is not last in the Babel `plugins` array, so later transforms can prevent worklets from being compiled correctly.",
|
|
67286
|
+
help: "Move `react-native-worklets/plugin` to the final entry in the Babel `plugins` array."
|
|
67287
|
+
}));
|
|
67288
|
+
return diagnostics;
|
|
67289
|
+
};
|
|
67290
|
+
const checkReactNativeBabelPluginOrder = (rootDirectory, project) => {
|
|
67291
|
+
const babelPlugins = readStaticBabelPluginNames(rootDirectory);
|
|
67292
|
+
if (babelPlugins === null || babelPlugins.pluginNames === null) return [];
|
|
67293
|
+
return [...checkReactCompilerPluginOrder(babelPlugins.filePath, babelPlugins.pluginNames), ...checkReanimatedPluginOrder(project, babelPlugins.filePath, babelPlugins.pluginNames)];
|
|
67294
|
+
};
|
|
66979
67295
|
const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
|
|
66980
67296
|
const isBuilderBobLibrary = (packageJson) => {
|
|
66981
67297
|
const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
|
|
@@ -67052,7 +67368,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
67052
67368
|
const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
|
|
67053
67369
|
const checkReactNativeProject = (rootDirectory, project) => {
|
|
67054
67370
|
if (!isReactNativeProject(project)) return [];
|
|
67055
|
-
return [
|
|
67371
|
+
return [
|
|
67372
|
+
...checkReactNativeMetroBabelPreset(rootDirectory),
|
|
67373
|
+
...checkReactNativeBabelPluginOrder(rootDirectory, project),
|
|
67374
|
+
...checkReactNativeAndroidReleaseShrinking(rootDirectory),
|
|
67375
|
+
...checkReactNativeLibraryDependencies(rootDirectory)
|
|
67376
|
+
];
|
|
67056
67377
|
};
|
|
67057
67378
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
67058
67379
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
@@ -67203,17 +67524,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
67203
67524
|
}
|
|
67204
67525
|
return diagnostics;
|
|
67205
67526
|
};
|
|
67206
|
-
const getTypescriptScriptKind = (fileName) => {
|
|
67207
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67208
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67209
|
-
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67210
|
-
return ts.ScriptKind.JS;
|
|
67211
|
-
};
|
|
67212
|
-
const unwrapTypescriptExpression = (expression) => {
|
|
67213
|
-
let currentExpression = expression;
|
|
67214
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67215
|
-
return currentExpression;
|
|
67216
|
-
};
|
|
67217
67527
|
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
67218
67528
|
isAnimationFunction: false,
|
|
67219
67529
|
isMotionComponent: false,
|
|
@@ -67769,7 +68079,11 @@ const buildCapabilities = (project) => {
|
|
|
67769
68079
|
capabilities.add(project.framework);
|
|
67770
68080
|
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
67771
68081
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
67772
|
-
if (project.expoVersion !== null)
|
|
68082
|
+
if (project.expoVersion !== null) {
|
|
68083
|
+
capabilities.add("expo");
|
|
68084
|
+
const expoSdkMajorVersion = getLowestDependencyMajor(project.expoVersion);
|
|
68085
|
+
if (expoSdkMajorVersion !== null && expoSdkMajorVersion >= 54) capabilities.add("expo:54");
|
|
68086
|
+
}
|
|
67773
68087
|
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
67774
68088
|
if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
|
|
67775
68089
|
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
@@ -67833,6 +68147,11 @@ const buildCapabilities = (project) => {
|
|
|
67833
68147
|
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
67834
68148
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
67835
68149
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
68150
|
+
if (project.reanimatedVersion !== void 0 && project.reanimatedVersion !== null) {
|
|
68151
|
+
capabilities.add("reanimated");
|
|
68152
|
+
const reanimatedMajorVersion = getLowestDependencyMajor(project.reanimatedVersion);
|
|
68153
|
+
if (reanimatedMajorVersion !== null && reanimatedMajorVersion >= 4) capabilities.add("reanimated:4");
|
|
68154
|
+
}
|
|
67836
68155
|
if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
|
|
67837
68156
|
if (project.styledComponentsVersion) {
|
|
67838
68157
|
capabilities.add("styled-components");
|
|
@@ -73883,4 +74202,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
73883
74202
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
73884
74203
|
|
|
73885
74204
|
//# sourceMappingURL=index.js.map
|
|
73886
|
-
//# debugId=
|
|
74205
|
+
//# debugId=6d9ce7c4-f6fc-597c-8340-3d408ed860e3
|