react-doctor 0.9.1-dev.54ba416 → 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 +347 -29
- package/dist/index.js +344 -26
- package/dist/lsp.js +344 -26
- package/package.json +5 -5
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";
|
|
@@ -62285,7 +62285,7 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
|
|
|
62285
62285
|
]).find(isFile) ?? null;
|
|
62286
62286
|
};
|
|
62287
62287
|
const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
62288
|
-
const getStaticPropertyName$
|
|
62288
|
+
const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
|
|
62289
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");
|
|
62290
62290
|
const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
62291
62291
|
const getRequireModuleSpecifier = (expression) => {
|
|
@@ -62349,7 +62349,7 @@ const getImportBinding = (sourceFile, bindingName) => {
|
|
|
62349
62349
|
const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
|
|
62350
62350
|
if (bindingElement && moduleSpecifier) return {
|
|
62351
62351
|
moduleSpecifier,
|
|
62352
|
-
exportName: bindingElement.propertyName ? getStaticPropertyName$
|
|
62352
|
+
exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
|
|
62353
62353
|
isNamespace: false
|
|
62354
62354
|
};
|
|
62355
62355
|
}
|
|
@@ -62510,7 +62510,7 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
62510
62510
|
}
|
|
62511
62511
|
if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
|
|
62512
62512
|
for (const property of [...unwrappedExpression.properties].reverse()) {
|
|
62513
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62513
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
|
|
62514
62514
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
|
|
62515
62515
|
if (ts.isSpreadAssignment(property)) {
|
|
62516
62516
|
const spreadState = getReactCompilerFlagState(property.expression, analysis);
|
|
@@ -62536,8 +62536,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
62536
62536
|
}
|
|
62537
62537
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
62538
62538
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
62539
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62540
|
-
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;
|
|
62541
62541
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
62542
62542
|
if (ts.isSpreadAssignment(property)) {
|
|
62543
62543
|
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
@@ -62568,7 +62568,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
|
|
|
62568
62568
|
return resolvedExpression.properties.some((property) => {
|
|
62569
62569
|
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
62570
62570
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
62571
|
-
const staticPropertyName = getStaticPropertyName$
|
|
62571
|
+
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
62572
62572
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
62573
62573
|
}
|
|
62574
62574
|
return false;
|
|
@@ -62587,7 +62587,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
|
|
|
62587
62587
|
continue;
|
|
62588
62588
|
}
|
|
62589
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()) {
|
|
62590
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
62590
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
|
|
62591
62591
|
exportedNodes.push(property.initializer);
|
|
62592
62592
|
break;
|
|
62593
62593
|
}
|
|
@@ -62746,10 +62746,10 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62746
62746
|
const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
|
|
62747
62747
|
if (reactCompilerFlagState?.isEnabled) return true;
|
|
62748
62748
|
for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
|
|
62749
|
-
const propertyName = getStaticPropertyName$
|
|
62749
|
+
const propertyName = getStaticPropertyName$2(property.name);
|
|
62750
62750
|
if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
|
|
62751
62751
|
if (propertyName === "reactCompiler") continue;
|
|
62752
|
-
if ((propertyName === "plugins" || propertyName === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, propertyName, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$
|
|
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;
|
|
62753
62753
|
const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
|
|
62754
62754
|
if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
|
|
62755
62755
|
const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
|
|
@@ -62759,12 +62759,12 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
62759
62759
|
} else if (ts.isShorthandPropertyAssignment(property)) {
|
|
62760
62760
|
if (excludedPropertyNames?.has(property.name.text)) continue;
|
|
62761
62761
|
if (property.name.text === "reactCompiler") continue;
|
|
62762
|
-
if ((property.name.text === "plugins" || property.name.text === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, property.name.text, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$
|
|
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;
|
|
62763
62763
|
const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
|
|
62764
62764
|
if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
|
|
62765
62765
|
} else if (ts.isSpreadAssignment(property)) {
|
|
62766
62766
|
if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
|
|
62767
|
-
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;
|
|
62768
62768
|
if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
|
|
62769
62769
|
}
|
|
62770
62770
|
return false;
|
|
@@ -66977,6 +66977,321 @@ const buildReactNativeDiagnostic = (input) => ({
|
|
|
66977
66977
|
column: input.column ?? 0,
|
|
66978
66978
|
category: input.category ?? "Correctness"
|
|
66979
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
|
+
};
|
|
66980
67295
|
const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
|
|
66981
67296
|
const isBuilderBobLibrary = (packageJson) => {
|
|
66982
67297
|
const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
|
|
@@ -67053,7 +67368,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
67053
67368
|
const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
|
|
67054
67369
|
const checkReactNativeProject = (rootDirectory, project) => {
|
|
67055
67370
|
if (!isReactNativeProject(project)) return [];
|
|
67056
|
-
return [
|
|
67371
|
+
return [
|
|
67372
|
+
...checkReactNativeMetroBabelPreset(rootDirectory),
|
|
67373
|
+
...checkReactNativeBabelPluginOrder(rootDirectory, project),
|
|
67374
|
+
...checkReactNativeAndroidReleaseShrinking(rootDirectory),
|
|
67375
|
+
...checkReactNativeLibraryDependencies(rootDirectory)
|
|
67376
|
+
];
|
|
67057
67377
|
};
|
|
67058
67378
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
67059
67379
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
@@ -67204,17 +67524,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
67204
67524
|
}
|
|
67205
67525
|
return diagnostics;
|
|
67206
67526
|
};
|
|
67207
|
-
const getTypescriptScriptKind = (fileName) => {
|
|
67208
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
67209
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
67210
|
-
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
67211
|
-
return ts.ScriptKind.JS;
|
|
67212
|
-
};
|
|
67213
|
-
const unwrapTypescriptExpression = (expression) => {
|
|
67214
|
-
let currentExpression = expression;
|
|
67215
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
67216
|
-
return currentExpression;
|
|
67217
|
-
};
|
|
67218
67527
|
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
67219
67528
|
isAnimationFunction: false,
|
|
67220
67529
|
isMotionComponent: false,
|
|
@@ -67770,7 +68079,11 @@ const buildCapabilities = (project) => {
|
|
|
67770
68079
|
capabilities.add(project.framework);
|
|
67771
68080
|
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
67772
68081
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
67773
|
-
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
|
+
}
|
|
67774
68087
|
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
67775
68088
|
if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
|
|
67776
68089
|
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
@@ -67834,6 +68147,11 @@ const buildCapabilities = (project) => {
|
|
|
67834
68147
|
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
67835
68148
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
67836
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
|
+
}
|
|
67837
68155
|
if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
|
|
67838
68156
|
if (project.styledComponentsVersion) {
|
|
67839
68157
|
capabilities.add("styled-components");
|
|
@@ -73884,4 +74202,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
73884
74202
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
73885
74203
|
|
|
73886
74204
|
//# sourceMappingURL=index.js.map
|
|
73887
|
-
//# debugId=
|
|
74205
|
+
//# debugId=6d9ce7c4-f6fc-597c-8340-3d408ed860e3
|