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/cli.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]="ea274003-c9c6-5b5c-b839-b1545ccc48fa")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -64994,7 +64994,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
64994
64994
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
64995
64995
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
64996
64996
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
64997
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
64997
|
+
const CORE_PACKAGE_VERSION = "0.9.1";
|
|
64998
64998
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
64999
64999
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
65000
65000
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -65379,6 +65379,7 @@ const frameworkMergeRank = (framework) => {
|
|
|
65379
65379
|
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
65380
65380
|
};
|
|
65381
65381
|
const REACT_COMPILER_LINT_PACKAGES = new Set(["eslint-plugin-react-compiler"]);
|
|
65382
|
+
const REACT_COMPILER_RUNTIME_PACKAGES = new Set(["react-compiler-runtime"]);
|
|
65382
65383
|
const NEXT_CONFIG_FILENAMES = [
|
|
65383
65384
|
"next.config.js",
|
|
65384
65385
|
"next.config.mjs",
|
|
@@ -65481,7 +65482,7 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
|
|
|
65481
65482
|
]).find(isFile) ?? null;
|
|
65482
65483
|
};
|
|
65483
65484
|
const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
65484
|
-
const getStaticPropertyName$
|
|
65485
|
+
const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
|
|
65485
65486
|
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");
|
|
65486
65487
|
const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
65487
65488
|
const getRequireModuleSpecifier = (expression) => {
|
|
@@ -65545,7 +65546,7 @@ const getImportBinding = (sourceFile, bindingName) => {
|
|
|
65545
65546
|
const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
|
|
65546
65547
|
if (bindingElement && moduleSpecifier) return {
|
|
65547
65548
|
moduleSpecifier,
|
|
65548
|
-
exportName: bindingElement.propertyName ? getStaticPropertyName$
|
|
65549
|
+
exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
|
|
65549
65550
|
isNamespace: false
|
|
65550
65551
|
};
|
|
65551
65552
|
}
|
|
@@ -65706,7 +65707,7 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
65706
65707
|
}
|
|
65707
65708
|
if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
|
|
65708
65709
|
for (const property of [...unwrappedExpression.properties].reverse()) {
|
|
65709
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
65710
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
|
|
65710
65711
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
|
|
65711
65712
|
if (ts.isSpreadAssignment(property)) {
|
|
65712
65713
|
const spreadState = getReactCompilerFlagState(property.expression, analysis);
|
|
@@ -65732,8 +65733,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
65732
65733
|
}
|
|
65733
65734
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
65734
65735
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
65735
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
65736
|
-
if (ts.isMethodDeclaration(property) && getStaticPropertyName$
|
|
65736
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === propertyName) return property.initializer;
|
|
65737
|
+
if (ts.isMethodDeclaration(property) && getStaticPropertyName$2(property.name) === propertyName) return property;
|
|
65737
65738
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
65738
65739
|
if (ts.isSpreadAssignment(property)) {
|
|
65739
65740
|
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
@@ -65764,7 +65765,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
|
|
|
65764
65765
|
return resolvedExpression.properties.some((property) => {
|
|
65765
65766
|
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
65766
65767
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
65767
|
-
const staticPropertyName = getStaticPropertyName$
|
|
65768
|
+
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
65768
65769
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
65769
65770
|
}
|
|
65770
65771
|
return false;
|
|
@@ -65783,7 +65784,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
|
|
|
65783
65784
|
continue;
|
|
65784
65785
|
}
|
|
65785
65786
|
if (ts.isExpressionStatement(statement) && isCommonJsConfigExportAssignment(statement.expression, sourceFile) && exportName !== "default" && ts.isObjectLiteralExpression(statement.expression.right)) for (const property of [...statement.expression.right.properties].reverse()) {
|
|
65786
|
-
if (ts.isPropertyAssignment(property) && getStaticPropertyName$
|
|
65787
|
+
if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
|
|
65787
65788
|
exportedNodes.push(property.initializer);
|
|
65788
65789
|
break;
|
|
65789
65790
|
}
|
|
@@ -65942,10 +65943,10 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
65942
65943
|
const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
|
|
65943
65944
|
if (reactCompilerFlagState?.isEnabled) return true;
|
|
65944
65945
|
for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
|
|
65945
|
-
const propertyName = getStaticPropertyName$
|
|
65946
|
+
const propertyName = getStaticPropertyName$2(property.name);
|
|
65946
65947
|
if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
|
|
65947
65948
|
if (propertyName === "reactCompiler") continue;
|
|
65948
|
-
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$
|
|
65949
|
+
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;
|
|
65949
65950
|
const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
|
|
65950
65951
|
if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
|
|
65951
65952
|
const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
|
|
@@ -65955,12 +65956,12 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
|
|
|
65955
65956
|
} else if (ts.isShorthandPropertyAssignment(property)) {
|
|
65956
65957
|
if (excludedPropertyNames?.has(property.name.text)) continue;
|
|
65957
65958
|
if (property.name.text === "reactCompiler") continue;
|
|
65958
|
-
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$
|
|
65959
|
+
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;
|
|
65959
65960
|
const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
|
|
65960
65961
|
if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
|
|
65961
65962
|
} else if (ts.isSpreadAssignment(property)) {
|
|
65962
65963
|
if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
|
|
65963
|
-
if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$
|
|
65964
|
+
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;
|
|
65964
65965
|
if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
|
|
65965
65966
|
}
|
|
65966
65967
|
return false;
|
|
@@ -66085,7 +66086,7 @@ const hasCompilerConfigurationInAncestors = (directory) => {
|
|
|
66085
66086
|
}
|
|
66086
66087
|
return false;
|
|
66087
66088
|
};
|
|
66088
|
-
const detectReactCompiler = (directory, packageJson) => hasCompilerConfiguration(directory, packageJson) || hasCompilerConfigurationInAncestors(directory);
|
|
66089
|
+
const detectReactCompiler = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_RUNTIME_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_RUNTIME_PACKAGES) || hasCompilerConfiguration(directory, packageJson) || hasCompilerConfigurationInAncestors(directory);
|
|
66089
66090
|
const detectReactCompilerLintPlugin = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_LINT_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_LINT_PACKAGES);
|
|
66090
66091
|
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
66091
66092
|
const filePath = Path.join(directory, filename);
|
|
@@ -70201,6 +70202,321 @@ const buildReactNativeDiagnostic = (input) => ({
|
|
|
70201
70202
|
column: input.column ?? 0,
|
|
70202
70203
|
category: input.category ?? "Correctness"
|
|
70203
70204
|
});
|
|
70205
|
+
const ANDROID_APP_BUILD_FILE_NAMES = ["build.gradle", "build.gradle.kts"];
|
|
70206
|
+
const BUILD_TYPES_BLOCK_PATTERN = /\bbuildTypes\s*\{/g;
|
|
70207
|
+
const RELEASE_STRING_MARKER = "__RD_RELEASE__";
|
|
70208
|
+
const RELEASE_BLOCK_PATTERNS = [/\brelease\s*\{/g, /\b(?:getByName|named)\s*\(\s*(?:["']release["']|__RD_RELEASE__)\s*\)\s*\{/g];
|
|
70209
|
+
const MINIFY_ASSIGNMENT_PATTERN = /\b(?:isMinifyEnabled|minifyEnabled)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
70210
|
+
const SHRINK_RESOURCES_ASSIGNMENT_PATTERN = /\b(?:isShrinkResources|shrinkResources)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
|
|
70211
|
+
const stripGradleComments = (contents) => {
|
|
70212
|
+
let result = "";
|
|
70213
|
+
let index = 0;
|
|
70214
|
+
let quote = null;
|
|
70215
|
+
while (index < contents.length) {
|
|
70216
|
+
const character = contents[index];
|
|
70217
|
+
const nextCharacter = contents[index + 1];
|
|
70218
|
+
if (quote !== null) {
|
|
70219
|
+
result += character;
|
|
70220
|
+
if (character === "\\") {
|
|
70221
|
+
result += nextCharacter ?? "";
|
|
70222
|
+
index += 2;
|
|
70223
|
+
continue;
|
|
70224
|
+
}
|
|
70225
|
+
if (character === quote) quote = null;
|
|
70226
|
+
index += 1;
|
|
70227
|
+
continue;
|
|
70228
|
+
}
|
|
70229
|
+
if (character === "\"" || character === "'") {
|
|
70230
|
+
quote = character;
|
|
70231
|
+
result += character;
|
|
70232
|
+
index += 1;
|
|
70233
|
+
continue;
|
|
70234
|
+
}
|
|
70235
|
+
if (character === "/" && nextCharacter === "/") {
|
|
70236
|
+
while (index < contents.length && contents[index] !== "\n") {
|
|
70237
|
+
result += " ";
|
|
70238
|
+
index += 1;
|
|
70239
|
+
}
|
|
70240
|
+
continue;
|
|
70241
|
+
}
|
|
70242
|
+
if (character === "/" && nextCharacter === "*") {
|
|
70243
|
+
result += " ";
|
|
70244
|
+
index += 2;
|
|
70245
|
+
while (index < contents.length && !(contents[index] === "*" && contents[index + 1] === "/")) {
|
|
70246
|
+
result += contents[index] === "\n" ? "\n" : " ";
|
|
70247
|
+
index += 1;
|
|
70248
|
+
}
|
|
70249
|
+
if (index < contents.length) {
|
|
70250
|
+
result += " ";
|
|
70251
|
+
index += 2;
|
|
70252
|
+
}
|
|
70253
|
+
continue;
|
|
70254
|
+
}
|
|
70255
|
+
result += character;
|
|
70256
|
+
index += 1;
|
|
70257
|
+
}
|
|
70258
|
+
return result;
|
|
70259
|
+
};
|
|
70260
|
+
const maskGradleStrings = (contents) => {
|
|
70261
|
+
let result = "";
|
|
70262
|
+
let index = 0;
|
|
70263
|
+
let quote = null;
|
|
70264
|
+
while (index < contents.length) {
|
|
70265
|
+
const character = contents[index];
|
|
70266
|
+
if (quote === null) {
|
|
70267
|
+
if (character === "\"" || character === "'") {
|
|
70268
|
+
quote = character;
|
|
70269
|
+
result += " ";
|
|
70270
|
+
} else result += character;
|
|
70271
|
+
index += 1;
|
|
70272
|
+
continue;
|
|
70273
|
+
}
|
|
70274
|
+
if (character === "\\") {
|
|
70275
|
+
result += " ";
|
|
70276
|
+
index += 2;
|
|
70277
|
+
continue;
|
|
70278
|
+
}
|
|
70279
|
+
if (character === quote) quote = null;
|
|
70280
|
+
result += character === "\n" ? "\n" : " ";
|
|
70281
|
+
index += 1;
|
|
70282
|
+
}
|
|
70283
|
+
return result;
|
|
70284
|
+
};
|
|
70285
|
+
const findBlockContents = (contents, pattern) => {
|
|
70286
|
+
const blocks = [];
|
|
70287
|
+
pattern.lastIndex = 0;
|
|
70288
|
+
let match = pattern.exec(contents);
|
|
70289
|
+
while (match !== null) {
|
|
70290
|
+
const openingBraceIndex = pattern.lastIndex - 1;
|
|
70291
|
+
let depth = 1;
|
|
70292
|
+
let index = openingBraceIndex + 1;
|
|
70293
|
+
let quote = null;
|
|
70294
|
+
while (index < contents.length && depth > 0) {
|
|
70295
|
+
const character = contents[index];
|
|
70296
|
+
if (quote !== null) {
|
|
70297
|
+
if (character === "\\") {
|
|
70298
|
+
index += 2;
|
|
70299
|
+
continue;
|
|
70300
|
+
}
|
|
70301
|
+
if (character === quote) quote = null;
|
|
70302
|
+
} else if (character === "\"" || character === "'") quote = character;
|
|
70303
|
+
else if (character === "{") depth += 1;
|
|
70304
|
+
else if (character === "}") depth -= 1;
|
|
70305
|
+
index += 1;
|
|
70306
|
+
}
|
|
70307
|
+
if (depth === 0) blocks.push(contents.slice(openingBraceIndex + 1, index - 1));
|
|
70308
|
+
match = pattern.exec(contents);
|
|
70309
|
+
}
|
|
70310
|
+
return blocks;
|
|
70311
|
+
};
|
|
70312
|
+
const getFinalStaticBoolean = (contents, pattern) => {
|
|
70313
|
+
pattern.lastIndex = 0;
|
|
70314
|
+
let finalValue = null;
|
|
70315
|
+
let match = pattern.exec(contents);
|
|
70316
|
+
while (match !== null) {
|
|
70317
|
+
const staticValue = match[1]?.trim().replace(/\)$/, "").trim();
|
|
70318
|
+
if (staticValue === "true") finalValue = true;
|
|
70319
|
+
else if (staticValue === "false") finalValue = false;
|
|
70320
|
+
else finalValue = null;
|
|
70321
|
+
match = pattern.exec(contents);
|
|
70322
|
+
}
|
|
70323
|
+
return finalValue;
|
|
70324
|
+
};
|
|
70325
|
+
const getReleaseBlocks = (contents) => findBlockContents(contents, BUILD_TYPES_BLOCK_PATTERN).flatMap((buildTypesBlock) => RELEASE_BLOCK_PATTERNS.flatMap((pattern) => findBlockContents(buildTypesBlock, pattern)));
|
|
70326
|
+
const readAndroidReleaseShrinking = (rootDirectory) => {
|
|
70327
|
+
for (const buildFileName of ANDROID_APP_BUILD_FILE_NAMES) {
|
|
70328
|
+
const filePath = Path.posix.join("android", "app", buildFileName);
|
|
70329
|
+
const absoluteFilePath = Path.join(rootDirectory, filePath);
|
|
70330
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
70331
|
+
let contents;
|
|
70332
|
+
try {
|
|
70333
|
+
contents = stripGradleComments(NFS.readFileSync(absoluteFilePath, "utf-8"));
|
|
70334
|
+
} catch {
|
|
70335
|
+
return null;
|
|
70336
|
+
}
|
|
70337
|
+
const releaseBlocks = getReleaseBlocks(maskGradleStrings(contents.replace(/(["'])release\1/g, RELEASE_STRING_MARKER)));
|
|
70338
|
+
const [releaseBlock] = releaseBlocks;
|
|
70339
|
+
if (releaseBlock === void 0 || releaseBlocks.length !== 1) return null;
|
|
70340
|
+
const hasDisabledMinification = getFinalStaticBoolean(releaseBlock, MINIFY_ASSIGNMENT_PATTERN) === false;
|
|
70341
|
+
const hasDisabledResourceShrinking = getFinalStaticBoolean(releaseBlock, SHRINK_RESOURCES_ASSIGNMENT_PATTERN) === false;
|
|
70342
|
+
if (!hasDisabledMinification && !hasDisabledResourceShrinking) return null;
|
|
70343
|
+
return {
|
|
70344
|
+
filePath,
|
|
70345
|
+
hasDisabledMinification,
|
|
70346
|
+
hasDisabledResourceShrinking
|
|
70347
|
+
};
|
|
70348
|
+
}
|
|
70349
|
+
return null;
|
|
70350
|
+
};
|
|
70351
|
+
const checkReactNativeAndroidReleaseShrinking = (rootDirectory) => {
|
|
70352
|
+
const releaseShrinking = readAndroidReleaseShrinking(rootDirectory);
|
|
70353
|
+
if (releaseShrinking === null) return [];
|
|
70354
|
+
const disabledFeatures = [releaseShrinking.hasDisabledMinification ? "code minification" : null, releaseShrinking.hasDisabledResourceShrinking ? "resource shrinking" : null].filter((feature) => feature !== null);
|
|
70355
|
+
return [buildReactNativeDiagnostic({
|
|
70356
|
+
filePath: releaseShrinking.filePath,
|
|
70357
|
+
rule: "rn-android-release-shrinking-disabled",
|
|
70358
|
+
category: "Performance",
|
|
70359
|
+
message: `The Android release build explicitly disables ${disabledFeatures.join(" and ")}, leaving unreachable code or resources in the shipped app.`,
|
|
70360
|
+
help: "Enable release code minification and resource shrinking, then test the optimized build and add keep rules for libraries that require them."
|
|
70361
|
+
})];
|
|
70362
|
+
};
|
|
70363
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
70364
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
70365
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
70366
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
70367
|
+
return ts.ScriptKind.JS;
|
|
70368
|
+
};
|
|
70369
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
70370
|
+
let currentExpression = expression;
|
|
70371
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
70372
|
+
return currentExpression;
|
|
70373
|
+
};
|
|
70374
|
+
const BABEL_CONFIG_FILE_NAMES$1 = [
|
|
70375
|
+
"babel.config.js",
|
|
70376
|
+
"babel.config.cjs",
|
|
70377
|
+
"babel.config.mjs",
|
|
70378
|
+
"babel.config.ts",
|
|
70379
|
+
"babel.config.cts",
|
|
70380
|
+
"babel.config.json",
|
|
70381
|
+
".babelrc",
|
|
70382
|
+
".babelrc.js",
|
|
70383
|
+
".babelrc.cjs",
|
|
70384
|
+
".babelrc.mjs",
|
|
70385
|
+
".babelrc.cts",
|
|
70386
|
+
".babelrc.json"
|
|
70387
|
+
];
|
|
70388
|
+
const readUnknownPluginNames = (value) => {
|
|
70389
|
+
if (!Array.isArray(value)) return null;
|
|
70390
|
+
const pluginNames = [];
|
|
70391
|
+
for (const entry of value) {
|
|
70392
|
+
if (typeof entry === "string") {
|
|
70393
|
+
pluginNames.push(entry);
|
|
70394
|
+
continue;
|
|
70395
|
+
}
|
|
70396
|
+
if (Array.isArray(entry) && typeof entry[0] === "string") {
|
|
70397
|
+
pluginNames.push(entry[0]);
|
|
70398
|
+
continue;
|
|
70399
|
+
}
|
|
70400
|
+
return null;
|
|
70401
|
+
}
|
|
70402
|
+
return pluginNames;
|
|
70403
|
+
};
|
|
70404
|
+
const getStaticPropertyName$1 = (propertyName) => {
|
|
70405
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) return propertyName.text;
|
|
70406
|
+
return null;
|
|
70407
|
+
};
|
|
70408
|
+
const getStaticPluginName = (expression) => {
|
|
70409
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
70410
|
+
if (ts.isStringLiteralLike(unwrappedExpression)) return unwrappedExpression.text;
|
|
70411
|
+
if (!ts.isArrayLiteralExpression(unwrappedExpression)) return null;
|
|
70412
|
+
const pluginExpression = unwrappedExpression.elements[0];
|
|
70413
|
+
return pluginExpression && ts.isStringLiteralLike(pluginExpression) ? pluginExpression.text : null;
|
|
70414
|
+
};
|
|
70415
|
+
const getPluginNamesFromObject = (objectExpression) => {
|
|
70416
|
+
if (objectExpression.properties.some((property) => ts.isSpreadAssignment(property))) return null;
|
|
70417
|
+
const pluginProperties = objectExpression.properties.filter((property) => ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "plugins");
|
|
70418
|
+
if (pluginProperties.length !== 1) return pluginProperties.length === 0 ? [] : null;
|
|
70419
|
+
const pluginListExpression = unwrapTypescriptExpression(pluginProperties[0].initializer);
|
|
70420
|
+
if (!ts.isArrayLiteralExpression(pluginListExpression)) return null;
|
|
70421
|
+
const pluginNames = [];
|
|
70422
|
+
for (const element of pluginListExpression.elements) {
|
|
70423
|
+
if (ts.isSpreadElement(element)) return null;
|
|
70424
|
+
const pluginName = getStaticPluginName(element);
|
|
70425
|
+
if (pluginName === null) return null;
|
|
70426
|
+
pluginNames.push(pluginName);
|
|
70427
|
+
}
|
|
70428
|
+
return pluginNames;
|
|
70429
|
+
};
|
|
70430
|
+
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";
|
|
70431
|
+
const getDirectExportedObject = (sourceFile) => {
|
|
70432
|
+
const exportedExpressions = [];
|
|
70433
|
+
for (const statement of sourceFile.statements) {
|
|
70434
|
+
if (ts.isExportAssignment(statement)) {
|
|
70435
|
+
exportedExpressions.push(statement.expression);
|
|
70436
|
+
continue;
|
|
70437
|
+
}
|
|
70438
|
+
if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && isModuleExports(statement.expression.left)) exportedExpressions.push(statement.expression.right);
|
|
70439
|
+
}
|
|
70440
|
+
if (exportedExpressions.length !== 1) return null;
|
|
70441
|
+
const exportedExpression = unwrapTypescriptExpression(exportedExpressions[0]);
|
|
70442
|
+
return ts.isObjectLiteralExpression(exportedExpression) ? exportedExpression : null;
|
|
70443
|
+
};
|
|
70444
|
+
const parseConfigPluginNames = (filePath, contents) => {
|
|
70445
|
+
try {
|
|
70446
|
+
const parsedJson = JSON.parse(contents);
|
|
70447
|
+
return isPlainObject(parsedJson) ? readUnknownPluginNames(parsedJson.plugins) : null;
|
|
70448
|
+
} catch {
|
|
70449
|
+
if (ts.transpileModule(contents, {
|
|
70450
|
+
fileName: filePath,
|
|
70451
|
+
reportDiagnostics: true,
|
|
70452
|
+
compilerOptions: { allowJs: true }
|
|
70453
|
+
}).diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) return null;
|
|
70454
|
+
const exportedObject = getDirectExportedObject(ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filePath)));
|
|
70455
|
+
return exportedObject === null ? null : getPluginNamesFromObject(exportedObject);
|
|
70456
|
+
}
|
|
70457
|
+
};
|
|
70458
|
+
const readStaticBabelPluginNames = (rootDirectory) => {
|
|
70459
|
+
for (const fileName of BABEL_CONFIG_FILE_NAMES$1) {
|
|
70460
|
+
const absoluteFilePath = Path.join(rootDirectory, fileName);
|
|
70461
|
+
if (!isFile(absoluteFilePath)) continue;
|
|
70462
|
+
try {
|
|
70463
|
+
return {
|
|
70464
|
+
filePath: fileName,
|
|
70465
|
+
pluginNames: parseConfigPluginNames(fileName, NFS.readFileSync(absoluteFilePath, "utf-8"))
|
|
70466
|
+
};
|
|
70467
|
+
} catch {
|
|
70468
|
+
return {
|
|
70469
|
+
filePath: fileName,
|
|
70470
|
+
pluginNames: null
|
|
70471
|
+
};
|
|
70472
|
+
}
|
|
70473
|
+
}
|
|
70474
|
+
const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
|
|
70475
|
+
if (!isPlainObject(packageJson.babel)) return null;
|
|
70476
|
+
return {
|
|
70477
|
+
filePath: "package.json",
|
|
70478
|
+
pluginNames: readUnknownPluginNames(packageJson.babel.plugins)
|
|
70479
|
+
};
|
|
70480
|
+
};
|
|
70481
|
+
const REACT_COMPILER_PLUGIN_NAMES = new Set(["babel-plugin-react-compiler", "react-compiler"]);
|
|
70482
|
+
const REANIMATED_PLUGIN_NAME = "react-native-reanimated/plugin";
|
|
70483
|
+
const WORKLETS_PLUGIN_NAME = "react-native-worklets/plugin";
|
|
70484
|
+
const checkReactCompilerPluginOrder = (filePath, pluginNames) => {
|
|
70485
|
+
if (pluginNames.findIndex((pluginName) => REACT_COMPILER_PLUGIN_NAMES.has(pluginName)) <= 0) return [];
|
|
70486
|
+
return [buildReactNativeDiagnostic({
|
|
70487
|
+
filePath,
|
|
70488
|
+
rule: "rn-react-compiler-plugin-first",
|
|
70489
|
+
category: "Performance",
|
|
70490
|
+
message: "The React Compiler Babel plugin is not first, so an earlier transform can change component or hook code before the compiler analyzes it.",
|
|
70491
|
+
help: "Move `babel-plugin-react-compiler` to the first entry in the Babel `plugins` array."
|
|
70492
|
+
})];
|
|
70493
|
+
};
|
|
70494
|
+
const checkReanimatedPluginOrder = (project, filePath, pluginNames) => {
|
|
70495
|
+
const reanimatedMajorVersion = project.reanimatedVersion === null || project.reanimatedVersion === void 0 ? null : getLowestDependencyMajor(project.reanimatedVersion);
|
|
70496
|
+
if (reanimatedMajorVersion === null || reanimatedMajorVersion < 4) return [];
|
|
70497
|
+
const diagnostics = [];
|
|
70498
|
+
if (pluginNames.includes(REANIMATED_PLUGIN_NAME)) diagnostics.push(buildReactNativeDiagnostic({
|
|
70499
|
+
filePath,
|
|
70500
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
70501
|
+
category: "Performance",
|
|
70502
|
+
message: "This Reanimated 4 project still uses `react-native-reanimated/plugin`, which was renamed to the Worklets Babel plugin.",
|
|
70503
|
+
help: "Replace it with `react-native-worklets/plugin` and keep that plugin last in the Babel `plugins` array."
|
|
70504
|
+
}));
|
|
70505
|
+
const workletsPluginIndex = pluginNames.lastIndexOf(WORKLETS_PLUGIN_NAME);
|
|
70506
|
+
if (workletsPluginIndex !== -1 && workletsPluginIndex !== pluginNames.length - 1) diagnostics.push(buildReactNativeDiagnostic({
|
|
70507
|
+
filePath,
|
|
70508
|
+
rule: "rn-reanimated-worklets-plugin-last",
|
|
70509
|
+
category: "Performance",
|
|
70510
|
+
message: "`react-native-worklets/plugin` is not last in the Babel `plugins` array, so later transforms can prevent worklets from being compiled correctly.",
|
|
70511
|
+
help: "Move `react-native-worklets/plugin` to the final entry in the Babel `plugins` array."
|
|
70512
|
+
}));
|
|
70513
|
+
return diagnostics;
|
|
70514
|
+
};
|
|
70515
|
+
const checkReactNativeBabelPluginOrder = (rootDirectory, project) => {
|
|
70516
|
+
const babelPlugins = readStaticBabelPluginNames(rootDirectory);
|
|
70517
|
+
if (babelPlugins === null || babelPlugins.pluginNames === null) return [];
|
|
70518
|
+
return [...checkReactCompilerPluginOrder(babelPlugins.filePath, babelPlugins.pluginNames), ...checkReanimatedPluginOrder(project, babelPlugins.filePath, babelPlugins.pluginNames)];
|
|
70519
|
+
};
|
|
70204
70520
|
const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
|
|
70205
70521
|
const isBuilderBobLibrary = (packageJson) => {
|
|
70206
70522
|
const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
|
|
@@ -70277,7 +70593,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
70277
70593
|
const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
|
|
70278
70594
|
const checkReactNativeProject = (rootDirectory, project) => {
|
|
70279
70595
|
if (!isReactNativeProject(project)) return [];
|
|
70280
|
-
return [
|
|
70596
|
+
return [
|
|
70597
|
+
...checkReactNativeMetroBabelPreset(rootDirectory),
|
|
70598
|
+
...checkReactNativeBabelPluginOrder(rootDirectory, project),
|
|
70599
|
+
...checkReactNativeAndroidReleaseShrinking(rootDirectory),
|
|
70600
|
+
...checkReactNativeLibraryDependencies(rootDirectory)
|
|
70601
|
+
];
|
|
70281
70602
|
};
|
|
70282
70603
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
70283
70604
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
@@ -70428,17 +70749,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
70428
70749
|
}
|
|
70429
70750
|
return diagnostics;
|
|
70430
70751
|
};
|
|
70431
|
-
const getTypescriptScriptKind = (fileName) => {
|
|
70432
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
70433
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
70434
|
-
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
70435
|
-
return ts.ScriptKind.JS;
|
|
70436
|
-
};
|
|
70437
|
-
const unwrapTypescriptExpression = (expression) => {
|
|
70438
|
-
let currentExpression = expression;
|
|
70439
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
70440
|
-
return currentExpression;
|
|
70441
|
-
};
|
|
70442
70752
|
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
70443
70753
|
isAnimationFunction: false,
|
|
70444
70754
|
isMotionComponent: false,
|
|
@@ -70994,7 +71304,11 @@ const buildCapabilities = (project) => {
|
|
|
70994
71304
|
capabilities.add(project.framework);
|
|
70995
71305
|
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
70996
71306
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
70997
|
-
if (project.expoVersion !== null)
|
|
71307
|
+
if (project.expoVersion !== null) {
|
|
71308
|
+
capabilities.add("expo");
|
|
71309
|
+
const expoSdkMajorVersion = getLowestDependencyMajor(project.expoVersion);
|
|
71310
|
+
if (expoSdkMajorVersion !== null && expoSdkMajorVersion >= 54) capabilities.add("expo:54");
|
|
71311
|
+
}
|
|
70998
71312
|
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
70999
71313
|
if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
|
|
71000
71314
|
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
@@ -71058,6 +71372,11 @@ const buildCapabilities = (project) => {
|
|
|
71058
71372
|
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
71059
71373
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
71060
71374
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
71375
|
+
if (project.reanimatedVersion !== void 0 && project.reanimatedVersion !== null) {
|
|
71376
|
+
capabilities.add("reanimated");
|
|
71377
|
+
const reanimatedMajorVersion = getLowestDependencyMajor(project.reanimatedVersion);
|
|
71378
|
+
if (reanimatedMajorVersion !== null && reanimatedMajorVersion >= 4) capabilities.add("reanimated:4");
|
|
71379
|
+
}
|
|
71061
71380
|
if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
|
|
71062
71381
|
if (project.styledComponentsVersion) {
|
|
71063
71382
|
capabilities.add("styled-components");
|
|
@@ -77409,7 +77728,7 @@ const makeNoopConsole = () => ({
|
|
|
77409
77728
|
});
|
|
77410
77729
|
//#endregion
|
|
77411
77730
|
//#region src/cli/utils/version.ts
|
|
77412
|
-
const VERSION = "0.9.
|
|
77731
|
+
const VERSION = "0.9.1-dev.5d2f66d";
|
|
77413
77732
|
//#endregion
|
|
77414
77733
|
//#region src/cli/utils/json-mode.ts
|
|
77415
77734
|
let context = null;
|
|
@@ -77767,13 +78086,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
77767
78086
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
77768
78087
|
* standard `SENTRY_RELEASE` override.
|
|
77769
78088
|
*/
|
|
77770
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.
|
|
78089
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.1-dev.5d2f66d`;
|
|
77771
78090
|
/**
|
|
77772
78091
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
77773
78092
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
77774
78093
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
77775
78094
|
*/
|
|
77776
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.
|
|
78095
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.1-dev.5d2f66d") ? "development" : "production");
|
|
77777
78096
|
/**
|
|
77778
78097
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
77779
78098
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -96468,7 +96787,7 @@ ${formatExampleLines([
|
|
|
96468
96787
|
])}
|
|
96469
96788
|
|
|
96470
96789
|
${highlighter.dim("Scope:")}
|
|
96471
|
-
Runs
|
|
96790
|
+
Runs every rule tagged ${highlighter.info("design")}; all design rules stay opt-in during a general health scan.
|
|
96472
96791
|
Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
|
|
96473
96792
|
Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
|
|
96474
96793
|
`;
|
|
@@ -96571,4 +96890,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
96571
96890
|
export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, succeed$3 as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, getCategoryImpact as z };
|
|
96572
96891
|
|
|
96573
96892
|
//# sourceMappingURL=cli.js.map
|
|
96574
|
-
//# debugId=
|
|
96893
|
+
//# debugId=ea274003-c9c6-5b5c-b839-b1545ccc48fa
|