react-doctor 0.7.7-dev.e5b0690 → 0.7.7-dev.e632f8a
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 +450 -77
- package/dist/index.js +435 -74
- package/dist/lsp.js +435 -74
- package/package.json +4 -4
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]="81733cc9-f550-5459-9418-8ab3751a3ea3")}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";
|
|
@@ -9,6 +9,8 @@ import * as NFS from "node:fs";
|
|
|
9
9
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
10
10
|
import process$1 from "node:process";
|
|
11
11
|
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";
|
|
12
|
+
import * as Crypto from "node:crypto";
|
|
13
|
+
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
12
14
|
import ts from "typescript";
|
|
13
15
|
import * as OS from "node:os";
|
|
14
16
|
import os, { tmpdir } from "node:os";
|
|
@@ -16,8 +18,6 @@ import { parseJSON5 } from "confbox";
|
|
|
16
18
|
import * as NodeUrl from "node:url";
|
|
17
19
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
18
20
|
import { createJiti } from "jiti";
|
|
19
|
-
import * as Crypto from "node:crypto";
|
|
20
|
-
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
21
21
|
import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
|
|
22
22
|
import { gzipSync } from "node:zlib";
|
|
23
23
|
import * as Sentry from "@sentry/node";
|
|
@@ -22437,12 +22437,13 @@ var JsonReportDiagnosticV3 = class extends Class("JsonReportDiagnosticV3")({
|
|
|
22437
22437
|
fixGroupId: optional(String$1)
|
|
22438
22438
|
}) {};
|
|
22439
22439
|
/**
|
|
22440
|
-
* Deterministic identity string for a diagnostic.
|
|
22441
|
-
*
|
|
22442
|
-
* suppression files, content-hash caches, and IDE "ignore this"
|
|
22443
|
-
* actions all key off one shared shape.
|
|
22440
|
+
* Deterministic identity string for a retained diagnostic occurrence.
|
|
22441
|
+
* Severity and message distinguish content variants at one normalized site.
|
|
22444
22442
|
*/
|
|
22445
|
-
const buildDiagnosticIdentity = (input) =>
|
|
22443
|
+
const buildDiagnosticIdentity = (input) => {
|
|
22444
|
+
const occurrenceDigest = createHash("sha256").update(JSON.stringify([input.severity, input.message])).digest("hex");
|
|
22445
|
+
return `${input.filePath}::${input.line}:${input.column}::${input.plugin}/${input.rule}::${occurrenceDigest}`;
|
|
22446
|
+
};
|
|
22446
22447
|
const JsonReportMode = Literals([
|
|
22447
22448
|
"full",
|
|
22448
22449
|
"diff",
|
|
@@ -40410,18 +40411,45 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
40410
40411
|
}
|
|
40411
40412
|
return diagnostics;
|
|
40412
40413
|
};
|
|
40413
|
-
const
|
|
40414
|
-
|
|
40415
|
-
|
|
40414
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
40415
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
40416
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
40417
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
40418
|
+
return ts.ScriptKind.JS;
|
|
40419
|
+
};
|
|
40420
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
40421
|
+
let currentExpression = expression;
|
|
40422
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
40423
|
+
return currentExpression;
|
|
40424
|
+
};
|
|
40425
|
+
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
40426
|
+
isAnimationFunction: false,
|
|
40427
|
+
isMotionComponent: false,
|
|
40428
|
+
isMotionComponentFactory: false,
|
|
40429
|
+
isMotionComponentNamespace: false,
|
|
40430
|
+
isMotionConfig: false,
|
|
40431
|
+
isMotionNamespace: false,
|
|
40432
|
+
isReducedMotionHook: false
|
|
40433
|
+
};
|
|
40434
|
+
const MOTION_COMPONENT_FACTORY_EXPORT_NAMES = new Set(["m", "motion"]);
|
|
40435
|
+
const MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES = new Set(["Reorder"]);
|
|
40436
|
+
const MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES = new Set(["Item"]);
|
|
40437
|
+
const MOTION_ANIMATION_FUNCTION_EXPORT_NAMES = new Set(["animate"]);
|
|
40438
|
+
const REDUCED_MOTION_HOOK_EXPORT_NAME = "useReducedMotion";
|
|
40439
|
+
const MOTION_CONFIG_EXPORT_NAME = "MotionConfig";
|
|
40440
|
+
const REDUCED_MOTION_PROP_NAME = "reducedMotion";
|
|
40441
|
+
const REDUCED_MOTION_CONFIG_VALUES = new Set(["always", "user"]);
|
|
40442
|
+
const SCRIPT_MODULE_EXTENSIONS = [
|
|
40416
40443
|
".ts",
|
|
40417
40444
|
".tsx",
|
|
40418
40445
|
".js",
|
|
40419
|
-
".jsx"
|
|
40420
|
-
|
|
40421
|
-
|
|
40422
|
-
]);
|
|
40423
|
-
const
|
|
40424
|
-
const
|
|
40446
|
+
".jsx"
|
|
40447
|
+
];
|
|
40448
|
+
const SCRIPT_FILE_EXTENSIONS = new Set(SCRIPT_MODULE_EXTENSIONS);
|
|
40449
|
+
const STYLE_FILE_EXTENSIONS = new Set([".css", ".scss"]);
|
|
40450
|
+
const MOTION_SOURCE_PREFILTER = /framer-motion|["']motion(?:\/[A-Za-z0-9_./-]+)?["']|MotionConfig|useReducedMotion/;
|
|
40451
|
+
const REDUCED_MOTION_MEDIA_QUERY_PATTERN = /@media\s+([^{}]*\(\s*prefers-reduced-motion\s*:\s*reduce\s*\)[^{}]*)\{/gi;
|
|
40452
|
+
const CSS_DECLARATION_PATTERN = /(?:^|[;{])\s*[-_A-Za-z][-_A-Za-z0-9]*\s*:/;
|
|
40425
40453
|
const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
40426
40454
|
filePath: "package.json",
|
|
40427
40455
|
plugin: "react-doctor",
|
|
@@ -40433,52 +40461,406 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
|
40433
40461
|
column: 0,
|
|
40434
40462
|
category: "Accessibility"
|
|
40435
40463
|
};
|
|
40436
|
-
const
|
|
40464
|
+
const isMotionModuleSource = (moduleSource) => {
|
|
40465
|
+
for (const packageName of MOTION_LIBRARY_PACKAGES) if (moduleSource === packageName || moduleSource.startsWith(`${packageName}/`)) return true;
|
|
40466
|
+
return false;
|
|
40467
|
+
};
|
|
40468
|
+
const classifyMotionExport = (exportName) => ({
|
|
40469
|
+
isAnimationFunction: MOTION_ANIMATION_FUNCTION_EXPORT_NAMES.has(exportName),
|
|
40470
|
+
isMotionComponent: false,
|
|
40471
|
+
isMotionComponentFactory: MOTION_COMPONENT_FACTORY_EXPORT_NAMES.has(exportName),
|
|
40472
|
+
isMotionComponentNamespace: MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES.has(exportName),
|
|
40473
|
+
isMotionConfig: exportName === MOTION_CONFIG_EXPORT_NAME,
|
|
40474
|
+
isMotionNamespace: false,
|
|
40475
|
+
isReducedMotionHook: exportName === REDUCED_MOTION_HOOK_EXPORT_NAME
|
|
40476
|
+
});
|
|
40477
|
+
const getImportModuleSource = (node) => {
|
|
40478
|
+
let currentNode = node;
|
|
40479
|
+
while (currentNode) {
|
|
40480
|
+
if (ts.isImportDeclaration(currentNode) && ts.isStringLiteral(currentNode.moduleSpecifier)) return currentNode.moduleSpecifier.text;
|
|
40481
|
+
currentNode = currentNode.parent;
|
|
40482
|
+
}
|
|
40483
|
+
return null;
|
|
40484
|
+
};
|
|
40485
|
+
const getImportedBindingEvidence = (declaration, typeChecker, program, visitedSymbols) => {
|
|
40486
|
+
const moduleSource = getImportModuleSource(declaration);
|
|
40487
|
+
if (!moduleSource) return null;
|
|
40488
|
+
if (isMotionModuleSource(moduleSource) && ts.isNamespaceImport(declaration)) return {
|
|
40489
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
40490
|
+
isMotionNamespace: true
|
|
40491
|
+
};
|
|
40492
|
+
if (isMotionModuleSource(moduleSource) && ts.isImportSpecifier(declaration)) {
|
|
40493
|
+
if (declaration.isTypeOnly) return null;
|
|
40494
|
+
return classifyMotionExport(declaration.propertyName?.text ?? declaration.name.text);
|
|
40495
|
+
}
|
|
40496
|
+
if (isMotionModuleSource(moduleSource) && ts.isImportClause(declaration)) {
|
|
40497
|
+
if (declaration.isTypeOnly) return null;
|
|
40498
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40499
|
+
}
|
|
40500
|
+
if (ts.isImportSpecifier(declaration) && !declaration.isTypeOnly || ts.isImportClause(declaration) && !declaration.isTypeOnly && declaration.name) {
|
|
40501
|
+
const importedName = ts.isImportSpecifier(declaration) ? declaration.propertyName?.text ?? declaration.name.text : "default";
|
|
40502
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, declaration.getSourceFile().fileName, program);
|
|
40503
|
+
if (!moduleSourceFile) return null;
|
|
40504
|
+
return resolveModuleExportEvidence(moduleSourceFile, importedName, typeChecker, program, visitedSymbols);
|
|
40505
|
+
}
|
|
40506
|
+
return null;
|
|
40507
|
+
};
|
|
40508
|
+
const getLocalModuleSourceFile = (moduleSource, importingFileName, program) => {
|
|
40509
|
+
if (!moduleSource.startsWith(".")) return null;
|
|
40510
|
+
const moduleBasePath = Path.resolve(Path.dirname(importingFileName), moduleSource);
|
|
40511
|
+
const moduleBaseExtension = Path.extname(moduleBasePath);
|
|
40512
|
+
const typescriptModuleBasePath = moduleBaseExtension === ".js" || moduleBaseExtension === ".jsx" ? moduleBasePath.slice(0, -moduleBaseExtension.length) : null;
|
|
40513
|
+
const candidates = [
|
|
40514
|
+
moduleBasePath,
|
|
40515
|
+
...typescriptModuleBasePath ? [
|
|
40516
|
+
`${typescriptModuleBasePath}.ts`,
|
|
40517
|
+
`${typescriptModuleBasePath}.tsx`,
|
|
40518
|
+
Path.join(typescriptModuleBasePath, "index.ts"),
|
|
40519
|
+
Path.join(typescriptModuleBasePath, "index.tsx")
|
|
40520
|
+
] : [],
|
|
40521
|
+
...SCRIPT_MODULE_EXTENSIONS.map((extension) => `${moduleBasePath}${extension}`),
|
|
40522
|
+
...SCRIPT_MODULE_EXTENSIONS.map((extension) => Path.join(moduleBasePath, `index${extension}`))
|
|
40523
|
+
];
|
|
40524
|
+
for (const candidate of candidates) {
|
|
40525
|
+
const sourceFile = program.getSourceFile(candidate);
|
|
40526
|
+
if (sourceFile) return sourceFile;
|
|
40527
|
+
}
|
|
40528
|
+
return null;
|
|
40529
|
+
};
|
|
40530
|
+
const resolveModuleExportEvidence = (sourceFile, exportName, typeChecker, program, visitedSymbols) => {
|
|
40531
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
40532
|
+
if (moduleSymbol && visitedSymbols.has(moduleSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40533
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
40534
|
+
if (moduleSymbol) nextVisitedSymbols.add(moduleSymbol);
|
|
40535
|
+
for (const statement of sourceFile.statements) {
|
|
40536
|
+
if (exportName === "default" && ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
40537
|
+
const evidence = resolveMotionExpressionEvidence(statement.expression, typeChecker, program, nextVisitedSymbols);
|
|
40538
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
40539
|
+
continue;
|
|
40540
|
+
}
|
|
40541
|
+
if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) continue;
|
|
40542
|
+
if (statement.isTypeOnly) continue;
|
|
40543
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
40544
|
+
const moduleSource = statement.moduleSpecifier.text;
|
|
40545
|
+
if (!statement.exportClause) {
|
|
40546
|
+
if (isMotionModuleSource(moduleSource)) {
|
|
40547
|
+
const evidence = classifyMotionExport(exportName);
|
|
40548
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
40549
|
+
continue;
|
|
40550
|
+
}
|
|
40551
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
|
|
40552
|
+
if (moduleSourceFile) {
|
|
40553
|
+
const evidence = resolveModuleExportEvidence(moduleSourceFile, exportName, typeChecker, program, nextVisitedSymbols);
|
|
40554
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
40555
|
+
}
|
|
40556
|
+
continue;
|
|
40557
|
+
}
|
|
40558
|
+
if (!ts.isNamedExports(statement.exportClause)) continue;
|
|
40559
|
+
for (const exportSpecifier of statement.exportClause.elements) {
|
|
40560
|
+
if (exportSpecifier.name.text !== exportName || exportSpecifier.isTypeOnly) continue;
|
|
40561
|
+
const sourceExportName = exportSpecifier.propertyName?.text ?? exportSpecifier.name.text;
|
|
40562
|
+
if (isMotionModuleSource(moduleSource)) return classifyMotionExport(sourceExportName);
|
|
40563
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
|
|
40564
|
+
if (!moduleSourceFile) continue;
|
|
40565
|
+
return resolveModuleExportEvidence(moduleSourceFile, sourceExportName, typeChecker, program, nextVisitedSymbols);
|
|
40566
|
+
}
|
|
40567
|
+
}
|
|
40568
|
+
if (!moduleSymbol) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40569
|
+
const exportedSymbol = typeChecker.getExportsOfModule(moduleSymbol).find((candidateSymbol) => candidateSymbol.name === exportName);
|
|
40570
|
+
if (!exportedSymbol || nextVisitedSymbols.has(exportedSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40571
|
+
nextVisitedSymbols.add(exportedSymbol);
|
|
40572
|
+
let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40573
|
+
for (const declaration of exportedSymbol.declarations ?? []) {
|
|
40574
|
+
if (ts.isExportSpecifier(declaration)) {
|
|
40575
|
+
const targetSymbol = typeChecker.getExportSpecifierLocalTargetSymbol(declaration);
|
|
40576
|
+
for (const targetDeclaration of targetSymbol?.declarations ?? []) {
|
|
40577
|
+
const importedEvidence = getImportedBindingEvidence(targetDeclaration, typeChecker, program, nextVisitedSymbols);
|
|
40578
|
+
if (importedEvidence) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
|
|
40579
|
+
if (ts.isVariableDeclaration(targetDeclaration) && targetDeclaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(targetDeclaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
40580
|
+
}
|
|
40581
|
+
}
|
|
40582
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
40583
|
+
}
|
|
40584
|
+
return resolvedEvidence;
|
|
40585
|
+
};
|
|
40586
|
+
const mergeMotionExpressionEvidence = (leftEvidence, rightEvidence) => ({
|
|
40587
|
+
isAnimationFunction: leftEvidence.isAnimationFunction || rightEvidence.isAnimationFunction,
|
|
40588
|
+
isMotionComponent: leftEvidence.isMotionComponent || rightEvidence.isMotionComponent,
|
|
40589
|
+
isMotionComponentFactory: leftEvidence.isMotionComponentFactory || rightEvidence.isMotionComponentFactory,
|
|
40590
|
+
isMotionComponentNamespace: leftEvidence.isMotionComponentNamespace || rightEvidence.isMotionComponentNamespace,
|
|
40591
|
+
isMotionConfig: leftEvidence.isMotionConfig || rightEvidence.isMotionConfig,
|
|
40592
|
+
isMotionNamespace: leftEvidence.isMotionNamespace || rightEvidence.isMotionNamespace,
|
|
40593
|
+
isReducedMotionHook: leftEvidence.isReducedMotionHook || rightEvidence.isReducedMotionHook
|
|
40594
|
+
});
|
|
40595
|
+
const hasMotionExpressionEvidence = (evidence) => evidence.isAnimationFunction || evidence.isMotionComponent || evidence.isMotionComponentFactory || evidence.isMotionComponentNamespace || evidence.isMotionConfig || evidence.isMotionNamespace || evidence.isReducedMotionHook;
|
|
40596
|
+
const classifyMotionMember = (receiverEvidence, memberName) => {
|
|
40597
|
+
if (receiverEvidence.isMotionNamespace) return classifyMotionExport(memberName);
|
|
40598
|
+
if (receiverEvidence.isMotionComponentFactory) return {
|
|
40599
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
40600
|
+
isMotionComponent: true
|
|
40601
|
+
};
|
|
40602
|
+
if (receiverEvidence.isMotionComponentNamespace && MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES.has(memberName)) return {
|
|
40603
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
40604
|
+
isMotionComponent: true
|
|
40605
|
+
};
|
|
40606
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40607
|
+
};
|
|
40608
|
+
const resolveMotionExpressionEvidence = (expression, typeChecker, program, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
40609
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
40610
|
+
if (ts.isIdentifier(unwrappedExpression)) {
|
|
40611
|
+
const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
|
|
40612
|
+
if (!symbol || visitedSymbols.has(symbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40613
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
40614
|
+
nextVisitedSymbols.add(symbol);
|
|
40615
|
+
let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40616
|
+
for (const declaration of symbol.declarations ?? []) {
|
|
40617
|
+
const importedEvidence = getImportedBindingEvidence(declaration, typeChecker, program, nextVisitedSymbols);
|
|
40618
|
+
if (importedEvidence) {
|
|
40619
|
+
resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
|
|
40620
|
+
continue;
|
|
40621
|
+
}
|
|
40622
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
40623
|
+
if (ts.isBindingElement(declaration) && ts.isObjectBindingPattern(declaration.parent) && ts.isVariableDeclaration(declaration.parent.parent) && declaration.parent.parent.initializer) {
|
|
40624
|
+
const propertyName = declaration.propertyName ?? declaration.name;
|
|
40625
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteral(propertyName)) {
|
|
40626
|
+
const receiverEvidence = resolveMotionExpressionEvidence(declaration.parent.parent.initializer, typeChecker, program, nextVisitedSymbols);
|
|
40627
|
+
resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, classifyMotionMember(receiverEvidence, propertyName.text));
|
|
40628
|
+
}
|
|
40629
|
+
}
|
|
40630
|
+
}
|
|
40631
|
+
return resolvedEvidence;
|
|
40632
|
+
}
|
|
40633
|
+
if (ts.isPropertyAccessExpression(unwrappedExpression)) return classifyMotionMember(resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols), unwrappedExpression.name.text);
|
|
40634
|
+
if (ts.isElementAccessExpression(unwrappedExpression) && unwrappedExpression.argumentExpression && (ts.isStringLiteral(unwrappedExpression.argumentExpression) || ts.isNoSubstitutionTemplateLiteral(unwrappedExpression.argumentExpression))) return classifyMotionMember(resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols), unwrappedExpression.argumentExpression.text);
|
|
40635
|
+
if (ts.isCallExpression(unwrappedExpression)) {
|
|
40636
|
+
if (ts.isIdentifier(unwrappedExpression.expression) && unwrappedExpression.expression.text === "require" && !typeChecker.getSymbolAtLocation(unwrappedExpression.expression) && unwrappedExpression.arguments.length === 1 && ts.isStringLiteral(unwrappedExpression.arguments[0]) && isMotionModuleSource(unwrappedExpression.arguments[0].text)) return {
|
|
40637
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
40638
|
+
isMotionNamespace: true
|
|
40639
|
+
};
|
|
40640
|
+
if (ts.isPropertyAccessExpression(unwrappedExpression.expression) && unwrappedExpression.expression.name.text === "bind") {
|
|
40641
|
+
const boundReceiverEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression.expression, typeChecker, program, visitedSymbols);
|
|
40642
|
+
if (boundReceiverEvidence.isAnimationFunction) return boundReceiverEvidence;
|
|
40643
|
+
}
|
|
40644
|
+
const calleeEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols);
|
|
40645
|
+
if (calleeEvidence.isMotionComponentFactory) return {
|
|
40646
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
40647
|
+
isMotionComponent: true
|
|
40648
|
+
};
|
|
40649
|
+
return calleeEvidence;
|
|
40650
|
+
}
|
|
40651
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
40652
|
+
};
|
|
40653
|
+
const getStaticStringExpressionValue = (expression, typeChecker, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
40654
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
40655
|
+
if (ts.isStringLiteral(unwrappedExpression) || ts.isNoSubstitutionTemplateLiteral(unwrappedExpression)) return unwrappedExpression.text;
|
|
40656
|
+
if (!ts.isIdentifier(unwrappedExpression)) return null;
|
|
40657
|
+
const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
|
|
40658
|
+
if (!symbol || visitedSymbols.has(symbol)) return null;
|
|
40659
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
40660
|
+
nextVisitedSymbols.add(symbol);
|
|
40661
|
+
for (const declaration of symbol.declarations ?? []) {
|
|
40662
|
+
if (!ts.isVariableDeclaration(declaration) || !declaration.initializer) continue;
|
|
40663
|
+
if (!ts.isVariableDeclarationList(declaration.parent)) continue;
|
|
40664
|
+
if (!(declaration.parent.flags & ts.NodeFlags.Const)) continue;
|
|
40665
|
+
const value = getStaticStringExpressionValue(declaration.initializer, typeChecker, nextVisitedSymbols);
|
|
40666
|
+
if (value !== null) return value;
|
|
40667
|
+
}
|
|
40668
|
+
return null;
|
|
40669
|
+
};
|
|
40670
|
+
const getStaticJsxAttributeValue = (attribute, typeChecker) => {
|
|
40671
|
+
if (!attribute.initializer) return null;
|
|
40672
|
+
if (ts.isStringLiteral(attribute.initializer)) return attribute.initializer.text;
|
|
40673
|
+
if (!ts.isJsxExpression(attribute.initializer) || !attribute.initializer.expression) return null;
|
|
40674
|
+
return getStaticStringExpressionValue(attribute.initializer.expression, typeChecker);
|
|
40675
|
+
};
|
|
40676
|
+
const isExpressionValueConsumed = (expression) => {
|
|
40677
|
+
let currentExpression = expression;
|
|
40678
|
+
while (ts.isParenthesizedExpression(currentExpression.parent) || ts.isAsExpression(currentExpression.parent) || ts.isSatisfiesExpression(currentExpression.parent) || ts.isNonNullExpression(currentExpression.parent) || ts.isTypeAssertionExpression(currentExpression.parent)) currentExpression = currentExpression.parent;
|
|
40679
|
+
return !ts.isExpressionStatement(currentExpression.parent) && !ts.isVoidExpression(currentExpression.parent);
|
|
40680
|
+
};
|
|
40681
|
+
const jsxElementHasReducedMotionConfiguration = (attributes, typeChecker) => attributes.properties.some((attribute) => ts.isJsxAttribute(attribute) && ts.isIdentifier(attribute.name) && attribute.name.text === REDUCED_MOTION_PROP_NAME && REDUCED_MOTION_CONFIG_VALUES.has(getStaticJsxAttributeValue(attribute, typeChecker) ?? ""));
|
|
40682
|
+
const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
|
|
40683
|
+
const evidence = {
|
|
40684
|
+
hasMotionUse: false,
|
|
40685
|
+
hasReducedMotionHandling: false
|
|
40686
|
+
};
|
|
40687
|
+
const visitNode = (node) => {
|
|
40688
|
+
if (ts.isCallExpression(node)) {
|
|
40689
|
+
const calleeEvidence = resolveMotionExpressionEvidence(node.expression, typeChecker, program);
|
|
40690
|
+
const isAnimationFunctionCallViaCallOrApply = (ts.isPropertyAccessExpression(node.expression) && (node.expression.name.text === "call" || node.expression.name.text === "apply") || ts.isElementAccessExpression(node.expression) && node.expression.argumentExpression && (ts.isStringLiteral(node.expression.argumentExpression) || ts.isNoSubstitutionTemplateLiteral(node.expression.argumentExpression)) && (node.expression.argumentExpression.text === "call" || node.expression.argumentExpression.text === "apply")) && resolveMotionExpressionEvidence(node.expression.expression, typeChecker, program).isAnimationFunction;
|
|
40691
|
+
if (calleeEvidence.isAnimationFunction || isAnimationFunctionCallViaCallOrApply) evidence.hasMotionUse = true;
|
|
40692
|
+
if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
|
|
40693
|
+
}
|
|
40694
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
40695
|
+
const tagEvidence = ts.isJsxNamespacedName(node.tagName) ? EMPTY_MOTION_EXPRESSION_EVIDENCE : resolveMotionExpressionEvidence(node.tagName, typeChecker, program);
|
|
40696
|
+
if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
|
|
40697
|
+
if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
|
|
40698
|
+
}
|
|
40699
|
+
ts.forEachChild(node, visitNode);
|
|
40700
|
+
};
|
|
40701
|
+
visitNode(sourceFile);
|
|
40702
|
+
return evidence;
|
|
40703
|
+
};
|
|
40704
|
+
const analyzeReducedMotionSources = (sources) => {
|
|
40705
|
+
if (!sources.some(({ sourceText }) => MOTION_SOURCE_PREFILTER.test(sourceText))) return {
|
|
40706
|
+
hasMotionUse: false,
|
|
40707
|
+
hasReducedMotionHandling: false
|
|
40708
|
+
};
|
|
40709
|
+
const compilerOptions = {
|
|
40710
|
+
allowJs: true,
|
|
40711
|
+
checkJs: false,
|
|
40712
|
+
jsx: ts.JsxEmit.Preserve,
|
|
40713
|
+
module: ts.ModuleKind.ESNext,
|
|
40714
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
40715
|
+
noEmit: true,
|
|
40716
|
+
noLib: true,
|
|
40717
|
+
noResolve: true,
|
|
40718
|
+
skipLibCheck: true,
|
|
40719
|
+
target: ts.ScriptTarget.Latest
|
|
40720
|
+
};
|
|
40721
|
+
const sourceTextByFileName = new Map(sources.map(({ fileName: sourceFileName, sourceText: content }) => [Path.resolve(sourceFileName), content]));
|
|
40722
|
+
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
40723
|
+
const getDefaultSourceFile = compilerHost.getSourceFile.bind(compilerHost);
|
|
40724
|
+
compilerHost.getSourceFile = (requestedFileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => sourceTextByFileName.has(Path.resolve(requestedFileName)) ? ts.createSourceFile(Path.resolve(requestedFileName), sourceTextByFileName.get(Path.resolve(requestedFileName)) ?? "", languageVersionOrOptions, true, getTypescriptScriptKind(requestedFileName)) : getDefaultSourceFile(requestedFileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
|
|
40725
|
+
const program = ts.createProgram([...sourceTextByFileName.keys()], compilerOptions, compilerHost);
|
|
40726
|
+
const typeChecker = program.getTypeChecker();
|
|
40727
|
+
const evidence = {
|
|
40728
|
+
hasMotionUse: false,
|
|
40729
|
+
hasReducedMotionHandling: false
|
|
40730
|
+
};
|
|
40731
|
+
for (const sourceFileName of sourceTextByFileName.keys()) {
|
|
40732
|
+
const sourceFile = program.getSourceFile(sourceFileName);
|
|
40733
|
+
if (!sourceFile) continue;
|
|
40734
|
+
const fileEvidence = collectScriptMotionEvidence(sourceFile, typeChecker, program);
|
|
40735
|
+
evidence.hasMotionUse ||= fileEvidence.hasMotionUse;
|
|
40736
|
+
evidence.hasReducedMotionHandling ||= fileEvidence.hasReducedMotionHandling;
|
|
40737
|
+
}
|
|
40738
|
+
return evidence;
|
|
40739
|
+
};
|
|
40740
|
+
const removeCssCommentsAndStrings = (content) => {
|
|
40741
|
+
let sanitizedContent = "";
|
|
40742
|
+
let quote = null;
|
|
40743
|
+
let isInsideComment = false;
|
|
40744
|
+
let isInsideLineComment = false;
|
|
40745
|
+
for (let characterIndex = 0; characterIndex < content.length; characterIndex += 1) {
|
|
40746
|
+
const character = content[characterIndex];
|
|
40747
|
+
const nextCharacter = content[characterIndex + 1];
|
|
40748
|
+
if (isInsideLineComment) {
|
|
40749
|
+
sanitizedContent += character === "\n" ? "\n" : " ";
|
|
40750
|
+
if (character === "\n") isInsideLineComment = false;
|
|
40751
|
+
continue;
|
|
40752
|
+
}
|
|
40753
|
+
if (isInsideComment) {
|
|
40754
|
+
if (character === "*" && nextCharacter === "/") {
|
|
40755
|
+
sanitizedContent += " ";
|
|
40756
|
+
characterIndex += 1;
|
|
40757
|
+
isInsideComment = false;
|
|
40758
|
+
} else sanitizedContent += character === "\n" ? "\n" : " ";
|
|
40759
|
+
continue;
|
|
40760
|
+
}
|
|
40761
|
+
if (quote) {
|
|
40762
|
+
if (character === "\\") {
|
|
40763
|
+
sanitizedContent += " ";
|
|
40764
|
+
characterIndex += 1;
|
|
40765
|
+
continue;
|
|
40766
|
+
}
|
|
40767
|
+
if (character === quote) quote = null;
|
|
40768
|
+
sanitizedContent += character === "\n" ? "\n" : " ";
|
|
40769
|
+
continue;
|
|
40770
|
+
}
|
|
40771
|
+
if (character === "/" && nextCharacter === "*") {
|
|
40772
|
+
sanitizedContent += " ";
|
|
40773
|
+
characterIndex += 1;
|
|
40774
|
+
isInsideComment = true;
|
|
40775
|
+
continue;
|
|
40776
|
+
}
|
|
40777
|
+
if (character === "/" && nextCharacter === "/") {
|
|
40778
|
+
sanitizedContent += " ";
|
|
40779
|
+
characterIndex += 1;
|
|
40780
|
+
isInsideLineComment = true;
|
|
40781
|
+
continue;
|
|
40782
|
+
}
|
|
40783
|
+
if (character === "\"" || character === "'") {
|
|
40784
|
+
sanitizedContent += " ";
|
|
40785
|
+
quote = character;
|
|
40786
|
+
continue;
|
|
40787
|
+
}
|
|
40788
|
+
sanitizedContent += character;
|
|
40789
|
+
}
|
|
40790
|
+
return sanitizedContent;
|
|
40791
|
+
};
|
|
40792
|
+
const findMatchingClosingBrace = (content, openingBraceIndex) => {
|
|
40793
|
+
let depth = 0;
|
|
40794
|
+
for (let characterIndex = openingBraceIndex; characterIndex < content.length; characterIndex += 1) {
|
|
40795
|
+
if (content[characterIndex] === "{") depth += 1;
|
|
40796
|
+
if (content[characterIndex] !== "}") continue;
|
|
40797
|
+
depth -= 1;
|
|
40798
|
+
if (depth === 0) return characterIndex;
|
|
40799
|
+
}
|
|
40800
|
+
return -1;
|
|
40801
|
+
};
|
|
40802
|
+
const hasReducedMotionMediaQuery = (content) => {
|
|
40803
|
+
const sanitizedContent = removeCssCommentsAndStrings(content);
|
|
40804
|
+
REDUCED_MOTION_MEDIA_QUERY_PATTERN.lastIndex = 0;
|
|
40805
|
+
for (const match of sanitizedContent.matchAll(REDUCED_MOTION_MEDIA_QUERY_PATTERN)) {
|
|
40806
|
+
const mediaPrelude = match[1];
|
|
40807
|
+
if (/\bnot\b/i.test(mediaPrelude)) continue;
|
|
40808
|
+
const openingBraceIndex = (match.index ?? 0) + match[0].lastIndexOf("{");
|
|
40809
|
+
const closingBraceIndex = findMatchingClosingBrace(sanitizedContent, openingBraceIndex);
|
|
40810
|
+
if (closingBraceIndex === -1) continue;
|
|
40811
|
+
const body = sanitizedContent.slice(openingBraceIndex + 1, closingBraceIndex);
|
|
40812
|
+
if (CSS_DECLARATION_PATTERN.test(body)) return true;
|
|
40813
|
+
}
|
|
40814
|
+
return false;
|
|
40815
|
+
};
|
|
40816
|
+
const collectProjectMotionEvidence = (rootDirectory) => {
|
|
40817
|
+
const scriptSources = [];
|
|
40818
|
+
let hasReducedMotionHandling = false;
|
|
40437
40819
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
40438
|
-
|
|
40820
|
+
const extension = Path.extname(name);
|
|
40821
|
+
if (!SCRIPT_FILE_EXTENSIONS.has(extension) && !STYLE_FILE_EXTENSIONS.has(extension)) continue;
|
|
40439
40822
|
let content;
|
|
40440
40823
|
try {
|
|
40441
40824
|
content = NFS.readFileSync(absolutePath, "utf-8");
|
|
40442
40825
|
} catch {
|
|
40443
40826
|
continue;
|
|
40444
40827
|
}
|
|
40445
|
-
if (
|
|
40828
|
+
if (STYLE_FILE_EXTENSIONS.has(extension)) {
|
|
40829
|
+
if (hasReducedMotionMediaQuery(content)) hasReducedMotionHandling = true;
|
|
40830
|
+
continue;
|
|
40831
|
+
}
|
|
40832
|
+
scriptSources.push({
|
|
40833
|
+
fileName: absolutePath,
|
|
40834
|
+
sourceText: content
|
|
40835
|
+
});
|
|
40446
40836
|
}
|
|
40447
|
-
return
|
|
40837
|
+
if (scriptSources.length === 0) return {
|
|
40838
|
+
hasMotionUse: false,
|
|
40839
|
+
hasReducedMotionHandling
|
|
40840
|
+
};
|
|
40841
|
+
const scriptEvidence = analyzeReducedMotionSources(scriptSources);
|
|
40842
|
+
const hasMotionUse = scriptEvidence.hasMotionUse;
|
|
40843
|
+
hasReducedMotionHandling ||= scriptEvidence.hasReducedMotionHandling;
|
|
40844
|
+
return {
|
|
40845
|
+
hasMotionUse,
|
|
40846
|
+
hasReducedMotionHandling
|
|
40847
|
+
};
|
|
40448
40848
|
};
|
|
40449
40849
|
const checkReducedMotion = (rootDirectory) => {
|
|
40450
40850
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
40451
40851
|
if (!isFile(packageJsonPath)) return [];
|
|
40452
|
-
let hasMotionLibrary = false;
|
|
40453
40852
|
try {
|
|
40454
40853
|
const packageJson = readPackageJson$1(packageJsonPath);
|
|
40455
40854
|
const allDependencies = {
|
|
40456
40855
|
...packageJson.dependencies,
|
|
40457
40856
|
...packageJson.devDependencies
|
|
40458
40857
|
};
|
|
40459
|
-
|
|
40858
|
+
if (!Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName))) return [];
|
|
40460
40859
|
} catch {
|
|
40461
40860
|
return [];
|
|
40462
40861
|
}
|
|
40463
|
-
|
|
40464
|
-
|
|
40465
|
-
"grep",
|
|
40466
|
-
"--untracked",
|
|
40467
|
-
"-lE",
|
|
40468
|
-
REDUCED_MOTION_GREP_PATTERN,
|
|
40469
|
-
"--",
|
|
40470
|
-
...REDUCED_MOTION_FILE_GLOBS
|
|
40471
|
-
], {
|
|
40472
|
-
cwd: rootDirectory,
|
|
40473
|
-
encoding: "utf-8",
|
|
40474
|
-
stdio: [
|
|
40475
|
-
"ignore",
|
|
40476
|
-
"pipe",
|
|
40477
|
-
"pipe"
|
|
40478
|
-
]
|
|
40479
|
-
});
|
|
40480
|
-
if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
40481
|
-
return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
40862
|
+
const evidence = collectProjectMotionEvidence(rootDirectory);
|
|
40863
|
+
return evidence.hasMotionUse && !evidence.hasReducedMotionHandling ? [MISSING_REDUCED_MOTION_DIAGNOSTIC] : [];
|
|
40482
40864
|
};
|
|
40483
40865
|
const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
|
|
40484
40866
|
filePath: relativePath,
|
|
@@ -43097,23 +43479,12 @@ const REACT_USE_BINDING_RESOLUTION = {
|
|
|
43097
43479
|
isReactUseBinding: true,
|
|
43098
43480
|
isReactNamespaceBinding: false
|
|
43099
43481
|
};
|
|
43100
|
-
const getScriptKind$2 = (filename) => {
|
|
43101
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43102
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43103
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43104
|
-
return ts.ScriptKind.JS;
|
|
43105
|
-
};
|
|
43106
43482
|
const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
43107
|
-
const unwrapExpression = (expression) => {
|
|
43108
|
-
let currentExpression = expression;
|
|
43109
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
43110
|
-
return currentExpression;
|
|
43111
|
-
};
|
|
43112
43483
|
const getStaticPropertyName = (node) => {
|
|
43113
43484
|
if (!node) return null;
|
|
43114
43485
|
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
|
|
43115
43486
|
if (ts.isComputedPropertyName(node)) {
|
|
43116
|
-
const expression =
|
|
43487
|
+
const expression = unwrapTypescriptExpression(node.expression);
|
|
43117
43488
|
if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) return expression.text;
|
|
43118
43489
|
}
|
|
43119
43490
|
return null;
|
|
@@ -43142,7 +43513,7 @@ const isReactUseObjectBindingElement = (bindingElement) => {
|
|
|
43142
43513
|
return getStaticPropertyName(bindingElement.propertyName) === USE_IDENTIFIER;
|
|
43143
43514
|
};
|
|
43144
43515
|
const isReactRequireCall = (expression) => {
|
|
43145
|
-
const unwrappedExpression =
|
|
43516
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
43146
43517
|
return ts.isCallExpression(unwrappedExpression) && ts.isIdentifier(unwrappedExpression.expression) && unwrappedExpression.expression.text === REQUIRE_IDENTIFIER && unwrappedExpression.arguments.length === 1 && ts.isStringLiteral(unwrappedExpression.arguments[0]) && unwrappedExpression.arguments[0].text === REACT_MODULE_SOURCE;
|
|
43147
43518
|
};
|
|
43148
43519
|
const getModuleSource = (node) => {
|
|
@@ -43244,7 +43615,7 @@ const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
|
|
|
43244
43615
|
return false;
|
|
43245
43616
|
};
|
|
43246
43617
|
const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
|
|
43247
|
-
const unwrappedExpression =
|
|
43618
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
43248
43619
|
if (isReactRequireCall(unwrappedExpression)) return true;
|
|
43249
43620
|
if (!ts.isIdentifier(unwrappedExpression)) return false;
|
|
43250
43621
|
if (reactImportBindings.namespaceNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
|
|
@@ -43252,7 +43623,7 @@ const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile,
|
|
|
43252
43623
|
};
|
|
43253
43624
|
const isReactUseExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
|
|
43254
43625
|
if (!expression) return false;
|
|
43255
|
-
const unwrappedExpression =
|
|
43626
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
43256
43627
|
if (ts.isIdentifier(unwrappedExpression)) {
|
|
43257
43628
|
if (reactImportBindings.useImportNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
|
|
43258
43629
|
if (unwrappedExpression.text === USE_IDENTIFIER) return false;
|
|
@@ -43352,7 +43723,7 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
|
|
|
43352
43723
|
return matchedIdentifier;
|
|
43353
43724
|
};
|
|
43354
43725
|
const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
|
|
43355
|
-
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true,
|
|
43726
|
+
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filename));
|
|
43356
43727
|
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
|
|
43357
43728
|
if (!useIdentifier) return null;
|
|
43358
43729
|
return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
|
|
@@ -43388,12 +43759,6 @@ const WORKLET_ACCEPTING_CALLEES = new Set([
|
|
|
43388
43759
|
"executeOnUIRuntimeSync"
|
|
43389
43760
|
]);
|
|
43390
43761
|
const GESTURE_ROOT_IDENTIFIER = "Gesture";
|
|
43391
|
-
const getScriptKind$1 = (filename) => {
|
|
43392
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43393
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43394
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43395
|
-
return ts.ScriptKind.JS;
|
|
43396
|
-
};
|
|
43397
43762
|
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
43398
43763
|
const hasWorkletDirective = (node) => {
|
|
43399
43764
|
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
|
|
@@ -43465,17 +43830,11 @@ const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirecto
|
|
|
43465
43830
|
} catch {
|
|
43466
43831
|
return false;
|
|
43467
43832
|
}
|
|
43468
|
-
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true,
|
|
43833
|
+
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
|
|
43469
43834
|
};
|
|
43470
43835
|
const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
|
|
43471
43836
|
const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
|
|
43472
43837
|
const REACT_COMPILER_PLUGIN = "react-hooks-js";
|
|
43473
|
-
const getScriptKind = (filename) => {
|
|
43474
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43475
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43476
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43477
|
-
return ts.ScriptKind.JS;
|
|
43478
|
-
};
|
|
43479
43838
|
const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
|
|
43480
43839
|
const nodeLineRange = (sourceFile, node) => ({
|
|
43481
43840
|
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
|
|
@@ -43537,7 +43896,7 @@ const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) =>
|
|
|
43537
43896
|
try {
|
|
43538
43897
|
const buffer = NFS.readFileSync(absolutePath);
|
|
43539
43898
|
parsed = {
|
|
43540
|
-
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true,
|
|
43899
|
+
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)),
|
|
43541
43900
|
buffer
|
|
43542
43901
|
};
|
|
43543
43902
|
} catch {
|
|
@@ -46152,7 +46511,9 @@ const toJsonReportDiagnostic = (diagnostic, projectRoot, reportRoot) => {
|
|
|
46152
46511
|
line: diagnostic.line,
|
|
46153
46512
|
column: diagnostic.column,
|
|
46154
46513
|
plugin: diagnostic.plugin,
|
|
46155
|
-
rule: diagnostic.rule
|
|
46514
|
+
rule: diagnostic.rule,
|
|
46515
|
+
severity: diagnostic.severity,
|
|
46516
|
+
message: diagnostic.message
|
|
46156
46517
|
}),
|
|
46157
46518
|
normalizedFilePath,
|
|
46158
46519
|
category: ruleIdentity.category,
|
|
@@ -46774,7 +47135,7 @@ const makeNoopConsole = () => ({
|
|
|
46774
47135
|
});
|
|
46775
47136
|
//#endregion
|
|
46776
47137
|
//#region src/cli/utils/version.ts
|
|
46777
|
-
const VERSION = "0.7.7-dev.
|
|
47138
|
+
const VERSION = "0.7.7-dev.e632f8a";
|
|
46778
47139
|
//#endregion
|
|
46779
47140
|
//#region src/cli/utils/json-mode.ts
|
|
46780
47141
|
let context = null;
|
|
@@ -47143,13 +47504,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47143
47504
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47144
47505
|
* standard `SENTRY_RELEASE` override.
|
|
47145
47506
|
*/
|
|
47146
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.7-dev.
|
|
47507
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.7-dev.e632f8a`;
|
|
47147
47508
|
/**
|
|
47148
47509
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47149
47510
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47150
47511
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47151
47512
|
*/
|
|
47152
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.7-dev.
|
|
47513
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.7-dev.e632f8a") ? "development" : "production");
|
|
47153
47514
|
/**
|
|
47154
47515
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47155
47516
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -59666,14 +60027,25 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59666
60027
|
const largestRuleBucket = buildRuleBlastRadii(result.diagnostics)[0] ?? null;
|
|
59667
60028
|
let diagnosticsInTestFiles = 0;
|
|
59668
60029
|
let diagnosticsInStoryFiles = 0;
|
|
60030
|
+
const diagnosticSiteCounts = /* @__PURE__ */ new Map();
|
|
59669
60031
|
const findingsPerFixGroup = /* @__PURE__ */ new Map();
|
|
59670
60032
|
for (const diagnostic of result.diagnostics) {
|
|
59671
60033
|
if (diagnostic.fileContext === "test") diagnosticsInTestFiles += 1;
|
|
59672
60034
|
if (diagnostic.fileContext === "story") diagnosticsInStoryFiles += 1;
|
|
60035
|
+
const diagnosticSite = JSON.stringify([
|
|
60036
|
+
diagnostic.filePath,
|
|
60037
|
+
diagnostic.line,
|
|
60038
|
+
diagnostic.column,
|
|
60039
|
+
diagnostic.plugin,
|
|
60040
|
+
diagnostic.rule
|
|
60041
|
+
]);
|
|
60042
|
+
diagnosticSiteCounts.set(diagnosticSite, (diagnosticSiteCounts.get(diagnosticSite) ?? 0) + 1);
|
|
59673
60043
|
if (diagnostic.fixGroupId) findingsPerFixGroup.set(diagnostic.fixGroupId, (findingsPerFixGroup.get(diagnostic.fixGroupId) ?? 0) + 1);
|
|
59674
60044
|
}
|
|
59675
60045
|
let fixGroupedFindings = 0;
|
|
59676
60046
|
for (const count of findingsPerFixGroup.values()) fixGroupedFindings += count;
|
|
60047
|
+
let sameSiteOccurrences = 0;
|
|
60048
|
+
for (const count of diagnosticSiteCounts.values()) sameSiteOccurrences += Math.max(0, count - 1);
|
|
59677
60049
|
const categoryRollup = {};
|
|
59678
60050
|
for (const [category, count] of countByCategory) categoryRollup[`category.${toCategoryKey(category)}`] = count;
|
|
59679
60051
|
const suppressionRollup = {};
|
|
@@ -59710,6 +60082,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59710
60082
|
inStoryFiles: diagnosticsInStoryFiles,
|
|
59711
60083
|
distinctRules: countByRule.size,
|
|
59712
60084
|
topRule,
|
|
60085
|
+
sameSiteOccurrences,
|
|
59713
60086
|
fixGroups: findingsPerFixGroup.size,
|
|
59714
60087
|
fixGroupedFindings,
|
|
59715
60088
|
...categoryRollup,
|
|
@@ -65415,4 +65788,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65415
65788
|
export {};
|
|
65416
65789
|
|
|
65417
65790
|
//# sourceMappingURL=cli.js.map
|
|
65418
|
-
//# debugId=
|
|
65791
|
+
//# debugId=81733cc9-f550-5459-9418-8ab3751a3ea3
|