react-doctor 0.7.7-dev.ed241c3 → 0.7.7-dev.fbc804e

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.
Files changed (4) hide show
  1. package/dist/cli.js +69 -427
  2. package/dist/index.js +66 -424
  3. package/dist/lsp.js +66 -424
  4. package/package.json +2 -2
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]="4096a0e2-9712-5e2f-b602-5ff55d3994a3")}catch(e){}}();
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]="1872748b-acb2-5e9a-a748-43bfd023eb85")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.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";
@@ -37182,45 +37182,18 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
37182
37182
  }
37183
37183
  return diagnostics;
37184
37184
  };
37185
- const getTypescriptScriptKind = (fileName) => {
37186
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
37187
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
37188
- if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
37189
- return ts.ScriptKind.JS;
37190
- };
37191
- const unwrapTypescriptExpression = (expression) => {
37192
- let currentExpression = expression;
37193
- while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
37194
- return currentExpression;
37195
- };
37196
- const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
37197
- isAnimationFunction: false,
37198
- isMotionComponent: false,
37199
- isMotionComponentFactory: false,
37200
- isMotionComponentNamespace: false,
37201
- isMotionConfig: false,
37202
- isMotionNamespace: false,
37203
- isReducedMotionHook: false
37204
- };
37205
- const MOTION_COMPONENT_FACTORY_EXPORT_NAMES = new Set(["m", "motion"]);
37206
- const MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES = new Set(["Reorder"]);
37207
- const MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES = new Set(["Item"]);
37208
- const MOTION_ANIMATION_FUNCTION_EXPORT_NAMES = new Set(["animate"]);
37209
- const REDUCED_MOTION_HOOK_EXPORT_NAME = "useReducedMotion";
37210
- const MOTION_CONFIG_EXPORT_NAME = "MotionConfig";
37211
- const REDUCED_MOTION_PROP_NAME = "reducedMotion";
37212
- const REDUCED_MOTION_CONFIG_VALUES = new Set(["always", "user"]);
37213
- const SCRIPT_MODULE_EXTENSIONS = [
37185
+ const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
37186
+ const REDUCED_MOTION_PATTERN = new RegExp(REDUCED_MOTION_GREP_PATTERN);
37187
+ const REDUCED_MOTION_FILE_EXTENSIONS = new Set([
37214
37188
  ".ts",
37215
37189
  ".tsx",
37216
37190
  ".js",
37217
- ".jsx"
37218
- ];
37219
- const SCRIPT_FILE_EXTENSIONS = new Set(SCRIPT_MODULE_EXTENSIONS);
37220
- const STYLE_FILE_EXTENSIONS = new Set([".css", ".scss"]);
37221
- const MOTION_SOURCE_PREFILTER = /framer-motion|["']motion(?:\/[A-Za-z0-9_./-]+)?["']|MotionConfig|useReducedMotion/;
37222
- const REDUCED_MOTION_MEDIA_QUERY_PATTERN = /@media\s+([^{}]*\(\s*prefers-reduced-motion\s*:\s*reduce\s*\)[^{}]*)\{/gi;
37223
- const CSS_DECLARATION_PATTERN = /(?:^|[;{])\s*[-_A-Za-z][-_A-Za-z0-9]*\s*:/;
37191
+ ".jsx",
37192
+ ".css",
37193
+ ".scss"
37194
+ ]);
37195
+ const REDUCED_MOTION_FILE_GLOBS = [...REDUCED_MOTION_FILE_EXTENSIONS].map((extension) => `*${extension}`);
37196
+ const GIT_GREP_NO_MATCH_STATUS = 1;
37224
37197
  const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
37225
37198
  filePath: "package.json",
37226
37199
  plugin: "react-doctor",
@@ -37232,406 +37205,52 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
37232
37205
  column: 0,
37233
37206
  category: "Accessibility"
37234
37207
  };
37235
- const isMotionModuleSource = (moduleSource) => {
37236
- for (const packageName of MOTION_LIBRARY_PACKAGES) if (moduleSource === packageName || moduleSource.startsWith(`${packageName}/`)) return true;
37237
- return false;
37238
- };
37239
- const classifyMotionExport = (exportName) => ({
37240
- isAnimationFunction: MOTION_ANIMATION_FUNCTION_EXPORT_NAMES.has(exportName),
37241
- isMotionComponent: false,
37242
- isMotionComponentFactory: MOTION_COMPONENT_FACTORY_EXPORT_NAMES.has(exportName),
37243
- isMotionComponentNamespace: MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES.has(exportName),
37244
- isMotionConfig: exportName === MOTION_CONFIG_EXPORT_NAME,
37245
- isMotionNamespace: false,
37246
- isReducedMotionHook: exportName === REDUCED_MOTION_HOOK_EXPORT_NAME
37247
- });
37248
- const getImportModuleSource = (node) => {
37249
- let currentNode = node;
37250
- while (currentNode) {
37251
- if (ts.isImportDeclaration(currentNode) && ts.isStringLiteral(currentNode.moduleSpecifier)) return currentNode.moduleSpecifier.text;
37252
- currentNode = currentNode.parent;
37253
- }
37254
- return null;
37255
- };
37256
- const getImportedBindingEvidence = (declaration, typeChecker, program, visitedSymbols) => {
37257
- const moduleSource = getImportModuleSource(declaration);
37258
- if (!moduleSource) return null;
37259
- if (isMotionModuleSource(moduleSource) && ts.isNamespaceImport(declaration)) return {
37260
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37261
- isMotionNamespace: true
37262
- };
37263
- if (isMotionModuleSource(moduleSource) && ts.isImportSpecifier(declaration)) {
37264
- if (declaration.isTypeOnly) return null;
37265
- return classifyMotionExport(declaration.propertyName?.text ?? declaration.name.text);
37266
- }
37267
- if (isMotionModuleSource(moduleSource) && ts.isImportClause(declaration)) {
37268
- if (declaration.isTypeOnly) return null;
37269
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37270
- }
37271
- if (ts.isImportSpecifier(declaration) && !declaration.isTypeOnly || ts.isImportClause(declaration) && !declaration.isTypeOnly && declaration.name) {
37272
- const importedName = ts.isImportSpecifier(declaration) ? declaration.propertyName?.text ?? declaration.name.text : "default";
37273
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, declaration.getSourceFile().fileName, program);
37274
- if (!moduleSourceFile) return null;
37275
- return resolveModuleExportEvidence(moduleSourceFile, importedName, typeChecker, program, visitedSymbols);
37276
- }
37277
- return null;
37278
- };
37279
- const getLocalModuleSourceFile = (moduleSource, importingFileName, program) => {
37280
- if (!moduleSource.startsWith(".")) return null;
37281
- const moduleBasePath = Path.resolve(Path.dirname(importingFileName), moduleSource);
37282
- const moduleBaseExtension = Path.extname(moduleBasePath);
37283
- const typescriptModuleBasePath = moduleBaseExtension === ".js" || moduleBaseExtension === ".jsx" ? moduleBasePath.slice(0, -moduleBaseExtension.length) : null;
37284
- const candidates = [
37285
- moduleBasePath,
37286
- ...typescriptModuleBasePath ? [
37287
- `${typescriptModuleBasePath}.ts`,
37288
- `${typescriptModuleBasePath}.tsx`,
37289
- Path.join(typescriptModuleBasePath, "index.ts"),
37290
- Path.join(typescriptModuleBasePath, "index.tsx")
37291
- ] : [],
37292
- ...SCRIPT_MODULE_EXTENSIONS.map((extension) => `${moduleBasePath}${extension}`),
37293
- ...SCRIPT_MODULE_EXTENSIONS.map((extension) => Path.join(moduleBasePath, `index${extension}`))
37294
- ];
37295
- for (const candidate of candidates) {
37296
- const sourceFile = program.getSourceFile(candidate);
37297
- if (sourceFile) return sourceFile;
37298
- }
37299
- return null;
37300
- };
37301
- const resolveModuleExportEvidence = (sourceFile, exportName, typeChecker, program, visitedSymbols) => {
37302
- const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
37303
- if (moduleSymbol && visitedSymbols.has(moduleSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37304
- const nextVisitedSymbols = new Set(visitedSymbols);
37305
- if (moduleSymbol) nextVisitedSymbols.add(moduleSymbol);
37306
- for (const statement of sourceFile.statements) {
37307
- if (exportName === "default" && ts.isExportAssignment(statement) && !statement.isExportEquals) {
37308
- const evidence = resolveMotionExpressionEvidence(statement.expression, typeChecker, program, nextVisitedSymbols);
37309
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37310
- continue;
37311
- }
37312
- if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) continue;
37313
- if (statement.isTypeOnly) continue;
37314
- if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
37315
- const moduleSource = statement.moduleSpecifier.text;
37316
- if (!statement.exportClause) {
37317
- if (isMotionModuleSource(moduleSource)) {
37318
- const evidence = classifyMotionExport(exportName);
37319
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37320
- continue;
37321
- }
37322
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
37323
- if (moduleSourceFile) {
37324
- const evidence = resolveModuleExportEvidence(moduleSourceFile, exportName, typeChecker, program, nextVisitedSymbols);
37325
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37326
- }
37327
- continue;
37328
- }
37329
- if (!ts.isNamedExports(statement.exportClause)) continue;
37330
- for (const exportSpecifier of statement.exportClause.elements) {
37331
- if (exportSpecifier.name.text !== exportName || exportSpecifier.isTypeOnly) continue;
37332
- const sourceExportName = exportSpecifier.propertyName?.text ?? exportSpecifier.name.text;
37333
- if (isMotionModuleSource(moduleSource)) return classifyMotionExport(sourceExportName);
37334
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
37335
- if (!moduleSourceFile) continue;
37336
- return resolveModuleExportEvidence(moduleSourceFile, sourceExportName, typeChecker, program, nextVisitedSymbols);
37337
- }
37338
- }
37339
- if (!moduleSymbol) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37340
- const exportedSymbol = typeChecker.getExportsOfModule(moduleSymbol).find((candidateSymbol) => candidateSymbol.name === exportName);
37341
- if (!exportedSymbol || nextVisitedSymbols.has(exportedSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37342
- nextVisitedSymbols.add(exportedSymbol);
37343
- let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
37344
- for (const declaration of exportedSymbol.declarations ?? []) {
37345
- if (ts.isExportSpecifier(declaration)) {
37346
- const targetSymbol = typeChecker.getExportSpecifierLocalTargetSymbol(declaration);
37347
- for (const targetDeclaration of targetSymbol?.declarations ?? []) {
37348
- const importedEvidence = getImportedBindingEvidence(targetDeclaration, typeChecker, program, nextVisitedSymbols);
37349
- if (importedEvidence) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
37350
- if (ts.isVariableDeclaration(targetDeclaration) && targetDeclaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(targetDeclaration.initializer, typeChecker, program, nextVisitedSymbols));
37351
- }
37352
- }
37353
- if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
37354
- }
37355
- return resolvedEvidence;
37356
- };
37357
- const mergeMotionExpressionEvidence = (leftEvidence, rightEvidence) => ({
37358
- isAnimationFunction: leftEvidence.isAnimationFunction || rightEvidence.isAnimationFunction,
37359
- isMotionComponent: leftEvidence.isMotionComponent || rightEvidence.isMotionComponent,
37360
- isMotionComponentFactory: leftEvidence.isMotionComponentFactory || rightEvidence.isMotionComponentFactory,
37361
- isMotionComponentNamespace: leftEvidence.isMotionComponentNamespace || rightEvidence.isMotionComponentNamespace,
37362
- isMotionConfig: leftEvidence.isMotionConfig || rightEvidence.isMotionConfig,
37363
- isMotionNamespace: leftEvidence.isMotionNamespace || rightEvidence.isMotionNamespace,
37364
- isReducedMotionHook: leftEvidence.isReducedMotionHook || rightEvidence.isReducedMotionHook
37365
- });
37366
- const hasMotionExpressionEvidence = (evidence) => evidence.isAnimationFunction || evidence.isMotionComponent || evidence.isMotionComponentFactory || evidence.isMotionComponentNamespace || evidence.isMotionConfig || evidence.isMotionNamespace || evidence.isReducedMotionHook;
37367
- const classifyMotionMember = (receiverEvidence, memberName) => {
37368
- if (receiverEvidence.isMotionNamespace) return classifyMotionExport(memberName);
37369
- if (receiverEvidence.isMotionComponentFactory) return {
37370
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37371
- isMotionComponent: true
37372
- };
37373
- if (receiverEvidence.isMotionComponentNamespace && MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES.has(memberName)) return {
37374
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37375
- isMotionComponent: true
37376
- };
37377
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37378
- };
37379
- const resolveMotionExpressionEvidence = (expression, typeChecker, program, visitedSymbols = /* @__PURE__ */ new Set()) => {
37380
- const unwrappedExpression = unwrapTypescriptExpression(expression);
37381
- if (ts.isIdentifier(unwrappedExpression)) {
37382
- const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
37383
- if (!symbol || visitedSymbols.has(symbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37384
- const nextVisitedSymbols = new Set(visitedSymbols);
37385
- nextVisitedSymbols.add(symbol);
37386
- let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
37387
- for (const declaration of symbol.declarations ?? []) {
37388
- const importedEvidence = getImportedBindingEvidence(declaration, typeChecker, program, nextVisitedSymbols);
37389
- if (importedEvidence) {
37390
- resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
37391
- continue;
37392
- }
37393
- if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
37394
- if (ts.isBindingElement(declaration) && ts.isObjectBindingPattern(declaration.parent) && ts.isVariableDeclaration(declaration.parent.parent) && declaration.parent.parent.initializer) {
37395
- const propertyName = declaration.propertyName ?? declaration.name;
37396
- if (ts.isIdentifier(propertyName) || ts.isStringLiteral(propertyName)) {
37397
- const receiverEvidence = resolveMotionExpressionEvidence(declaration.parent.parent.initializer, typeChecker, program, nextVisitedSymbols);
37398
- resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, classifyMotionMember(receiverEvidence, propertyName.text));
37399
- }
37400
- }
37401
- }
37402
- return resolvedEvidence;
37403
- }
37404
- if (ts.isPropertyAccessExpression(unwrappedExpression)) return classifyMotionMember(resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols), unwrappedExpression.name.text);
37405
- 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);
37406
- if (ts.isCallExpression(unwrappedExpression)) {
37407
- 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 {
37408
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37409
- isMotionNamespace: true
37410
- };
37411
- if (ts.isPropertyAccessExpression(unwrappedExpression.expression) && unwrappedExpression.expression.name.text === "bind") {
37412
- const boundReceiverEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression.expression, typeChecker, program, visitedSymbols);
37413
- if (boundReceiverEvidence.isAnimationFunction) return boundReceiverEvidence;
37414
- }
37415
- const calleeEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols);
37416
- if (calleeEvidence.isMotionComponentFactory) return {
37417
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37418
- isMotionComponent: true
37419
- };
37420
- return calleeEvidence;
37421
- }
37422
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37423
- };
37424
- const getStaticStringExpressionValue = (expression, typeChecker, visitedSymbols = /* @__PURE__ */ new Set()) => {
37425
- const unwrappedExpression = unwrapTypescriptExpression(expression);
37426
- if (ts.isStringLiteral(unwrappedExpression) || ts.isNoSubstitutionTemplateLiteral(unwrappedExpression)) return unwrappedExpression.text;
37427
- if (!ts.isIdentifier(unwrappedExpression)) return null;
37428
- const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
37429
- if (!symbol || visitedSymbols.has(symbol)) return null;
37430
- const nextVisitedSymbols = new Set(visitedSymbols);
37431
- nextVisitedSymbols.add(symbol);
37432
- for (const declaration of symbol.declarations ?? []) {
37433
- if (!ts.isVariableDeclaration(declaration) || !declaration.initializer) continue;
37434
- if (!ts.isVariableDeclarationList(declaration.parent)) continue;
37435
- if (!(declaration.parent.flags & ts.NodeFlags.Const)) continue;
37436
- const value = getStaticStringExpressionValue(declaration.initializer, typeChecker, nextVisitedSymbols);
37437
- if (value !== null) return value;
37438
- }
37439
- return null;
37440
- };
37441
- const getStaticJsxAttributeValue = (attribute, typeChecker) => {
37442
- if (!attribute.initializer) return null;
37443
- if (ts.isStringLiteral(attribute.initializer)) return attribute.initializer.text;
37444
- if (!ts.isJsxExpression(attribute.initializer) || !attribute.initializer.expression) return null;
37445
- return getStaticStringExpressionValue(attribute.initializer.expression, typeChecker);
37446
- };
37447
- const isExpressionValueConsumed = (expression) => {
37448
- let currentExpression = expression;
37449
- while (ts.isParenthesizedExpression(currentExpression.parent) || ts.isAsExpression(currentExpression.parent) || ts.isSatisfiesExpression(currentExpression.parent) || ts.isNonNullExpression(currentExpression.parent) || ts.isTypeAssertionExpression(currentExpression.parent)) currentExpression = currentExpression.parent;
37450
- return !ts.isExpressionStatement(currentExpression.parent) && !ts.isVoidExpression(currentExpression.parent);
37451
- };
37452
- 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) ?? ""));
37453
- const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
37454
- const evidence = {
37455
- hasMotionUse: false,
37456
- hasReducedMotionHandling: false
37457
- };
37458
- const visitNode = (node) => {
37459
- if (ts.isCallExpression(node)) {
37460
- const calleeEvidence = resolveMotionExpressionEvidence(node.expression, typeChecker, program);
37461
- 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;
37462
- if (calleeEvidence.isAnimationFunction || isAnimationFunctionCallViaCallOrApply) evidence.hasMotionUse = true;
37463
- if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
37464
- }
37465
- if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
37466
- const tagEvidence = ts.isJsxNamespacedName(node.tagName) ? EMPTY_MOTION_EXPRESSION_EVIDENCE : resolveMotionExpressionEvidence(node.tagName, typeChecker, program);
37467
- if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
37468
- if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
37469
- }
37470
- ts.forEachChild(node, visitNode);
37471
- };
37472
- visitNode(sourceFile);
37473
- return evidence;
37474
- };
37475
- const analyzeReducedMotionSources = (sources) => {
37476
- if (!sources.some(({ sourceText }) => MOTION_SOURCE_PREFILTER.test(sourceText))) return {
37477
- hasMotionUse: false,
37478
- hasReducedMotionHandling: false
37479
- };
37480
- const compilerOptions = {
37481
- allowJs: true,
37482
- checkJs: false,
37483
- jsx: ts.JsxEmit.Preserve,
37484
- module: ts.ModuleKind.ESNext,
37485
- moduleResolution: ts.ModuleResolutionKind.Bundler,
37486
- noEmit: true,
37487
- noLib: true,
37488
- noResolve: true,
37489
- skipLibCheck: true,
37490
- target: ts.ScriptTarget.Latest
37491
- };
37492
- const sourceTextByFileName = new Map(sources.map(({ fileName: sourceFileName, sourceText: content }) => [Path.resolve(sourceFileName), content]));
37493
- const compilerHost = ts.createCompilerHost(compilerOptions);
37494
- const getDefaultSourceFile = compilerHost.getSourceFile.bind(compilerHost);
37495
- 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);
37496
- const program = ts.createProgram([...sourceTextByFileName.keys()], compilerOptions, compilerHost);
37497
- const typeChecker = program.getTypeChecker();
37498
- const evidence = {
37499
- hasMotionUse: false,
37500
- hasReducedMotionHandling: false
37501
- };
37502
- for (const sourceFileName of sourceTextByFileName.keys()) {
37503
- const sourceFile = program.getSourceFile(sourceFileName);
37504
- if (!sourceFile) continue;
37505
- const fileEvidence = collectScriptMotionEvidence(sourceFile, typeChecker, program);
37506
- evidence.hasMotionUse ||= fileEvidence.hasMotionUse;
37507
- evidence.hasReducedMotionHandling ||= fileEvidence.hasReducedMotionHandling;
37508
- }
37509
- return evidence;
37510
- };
37511
- const removeCssCommentsAndStrings = (content) => {
37512
- let sanitizedContent = "";
37513
- let quote = null;
37514
- let isInsideComment = false;
37515
- let isInsideLineComment = false;
37516
- for (let characterIndex = 0; characterIndex < content.length; characterIndex += 1) {
37517
- const character = content[characterIndex];
37518
- const nextCharacter = content[characterIndex + 1];
37519
- if (isInsideLineComment) {
37520
- sanitizedContent += character === "\n" ? "\n" : " ";
37521
- if (character === "\n") isInsideLineComment = false;
37522
- continue;
37523
- }
37524
- if (isInsideComment) {
37525
- if (character === "*" && nextCharacter === "/") {
37526
- sanitizedContent += " ";
37527
- characterIndex += 1;
37528
- isInsideComment = false;
37529
- } else sanitizedContent += character === "\n" ? "\n" : " ";
37530
- continue;
37531
- }
37532
- if (quote) {
37533
- if (character === "\\") {
37534
- sanitizedContent += " ";
37535
- characterIndex += 1;
37536
- continue;
37537
- }
37538
- if (character === quote) quote = null;
37539
- sanitizedContent += character === "\n" ? "\n" : " ";
37540
- continue;
37541
- }
37542
- if (character === "/" && nextCharacter === "*") {
37543
- sanitizedContent += " ";
37544
- characterIndex += 1;
37545
- isInsideComment = true;
37546
- continue;
37547
- }
37548
- if (character === "/" && nextCharacter === "/") {
37549
- sanitizedContent += " ";
37550
- characterIndex += 1;
37551
- isInsideLineComment = true;
37552
- continue;
37553
- }
37554
- if (character === "\"" || character === "'") {
37555
- sanitizedContent += " ";
37556
- quote = character;
37557
- continue;
37558
- }
37559
- sanitizedContent += character;
37560
- }
37561
- return sanitizedContent;
37562
- };
37563
- const findMatchingClosingBrace = (content, openingBraceIndex) => {
37564
- let depth = 0;
37565
- for (let characterIndex = openingBraceIndex; characterIndex < content.length; characterIndex += 1) {
37566
- if (content[characterIndex] === "{") depth += 1;
37567
- if (content[characterIndex] !== "}") continue;
37568
- depth -= 1;
37569
- if (depth === 0) return characterIndex;
37570
- }
37571
- return -1;
37572
- };
37573
- const hasReducedMotionMediaQuery = (content) => {
37574
- const sanitizedContent = removeCssCommentsAndStrings(content);
37575
- REDUCED_MOTION_MEDIA_QUERY_PATTERN.lastIndex = 0;
37576
- for (const match of sanitizedContent.matchAll(REDUCED_MOTION_MEDIA_QUERY_PATTERN)) {
37577
- const mediaPrelude = match[1];
37578
- if (/\bnot\b/i.test(mediaPrelude)) continue;
37579
- const openingBraceIndex = (match.index ?? 0) + match[0].lastIndexOf("{");
37580
- const closingBraceIndex = findMatchingClosingBrace(sanitizedContent, openingBraceIndex);
37581
- if (closingBraceIndex === -1) continue;
37582
- const body = sanitizedContent.slice(openingBraceIndex + 1, closingBraceIndex);
37583
- if (CSS_DECLARATION_PATTERN.test(body)) return true;
37584
- }
37585
- return false;
37586
- };
37587
- const collectProjectMotionEvidence = (rootDirectory) => {
37588
- const scriptSources = [];
37589
- let hasReducedMotionHandling = false;
37208
+ const hasReducedMotionHandlingViaFilesystem = (rootDirectory) => {
37590
37209
  for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
37591
- const extension = Path.extname(name);
37592
- if (!SCRIPT_FILE_EXTENSIONS.has(extension) && !STYLE_FILE_EXTENSIONS.has(extension)) continue;
37210
+ if (!REDUCED_MOTION_FILE_EXTENSIONS.has(Path.extname(name))) continue;
37593
37211
  let content;
37594
37212
  try {
37595
37213
  content = NFS.readFileSync(absolutePath, "utf-8");
37596
37214
  } catch {
37597
37215
  continue;
37598
37216
  }
37599
- if (STYLE_FILE_EXTENSIONS.has(extension)) {
37600
- if (hasReducedMotionMediaQuery(content)) hasReducedMotionHandling = true;
37601
- continue;
37602
- }
37603
- scriptSources.push({
37604
- fileName: absolutePath,
37605
- sourceText: content
37606
- });
37217
+ if (REDUCED_MOTION_PATTERN.test(content)) return true;
37607
37218
  }
37608
- if (scriptSources.length === 0) return {
37609
- hasMotionUse: false,
37610
- hasReducedMotionHandling
37611
- };
37612
- const scriptEvidence = analyzeReducedMotionSources(scriptSources);
37613
- const hasMotionUse = scriptEvidence.hasMotionUse;
37614
- hasReducedMotionHandling ||= scriptEvidence.hasReducedMotionHandling;
37615
- return {
37616
- hasMotionUse,
37617
- hasReducedMotionHandling
37618
- };
37219
+ return false;
37619
37220
  };
37620
37221
  const checkReducedMotion = (rootDirectory) => {
37621
37222
  const packageJsonPath = Path.join(rootDirectory, "package.json");
37622
37223
  if (!isFile(packageJsonPath)) return [];
37224
+ let hasMotionLibrary = false;
37623
37225
  try {
37624
37226
  const packageJson = readPackageJson(packageJsonPath);
37625
37227
  const allDependencies = {
37626
37228
  ...packageJson.dependencies,
37627
37229
  ...packageJson.devDependencies
37628
37230
  };
37629
- if (!Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName))) return [];
37231
+ hasMotionLibrary = Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName));
37630
37232
  } catch {
37631
37233
  return [];
37632
37234
  }
37633
- const evidence = collectProjectMotionEvidence(rootDirectory);
37634
- return evidence.hasMotionUse && !evidence.hasReducedMotionHandling ? [MISSING_REDUCED_MOTION_DIAGNOSTIC] : [];
37235
+ if (!hasMotionLibrary) return [];
37236
+ const result = spawnSync("git", [
37237
+ "grep",
37238
+ "--untracked",
37239
+ "-lE",
37240
+ REDUCED_MOTION_GREP_PATTERN,
37241
+ "--",
37242
+ ...REDUCED_MOTION_FILE_GLOBS
37243
+ ], {
37244
+ cwd: rootDirectory,
37245
+ encoding: "utf-8",
37246
+ stdio: [
37247
+ "ignore",
37248
+ "pipe",
37249
+ "pipe"
37250
+ ]
37251
+ });
37252
+ if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
37253
+ return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
37635
37254
  };
37636
37255
  const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
37637
37256
  filePath: relativePath,
@@ -40234,12 +39853,23 @@ const REACT_USE_BINDING_RESOLUTION = {
40234
39853
  isReactUseBinding: true,
40235
39854
  isReactNamespaceBinding: false
40236
39855
  };
39856
+ const getScriptKind$2 = (filename) => {
39857
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
39858
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
39859
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
39860
+ return ts.ScriptKind.JS;
39861
+ };
40237
39862
  const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
39863
+ const unwrapExpression = (expression) => {
39864
+ let currentExpression = expression;
39865
+ while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
39866
+ return currentExpression;
39867
+ };
40238
39868
  const getStaticPropertyName = (node) => {
40239
39869
  if (!node) return null;
40240
39870
  if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
40241
39871
  if (ts.isComputedPropertyName(node)) {
40242
- const expression = unwrapTypescriptExpression(node.expression);
39872
+ const expression = unwrapExpression(node.expression);
40243
39873
  if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) return expression.text;
40244
39874
  }
40245
39875
  return null;
@@ -40268,7 +39898,7 @@ const isReactUseObjectBindingElement = (bindingElement) => {
40268
39898
  return getStaticPropertyName(bindingElement.propertyName) === USE_IDENTIFIER;
40269
39899
  };
40270
39900
  const isReactRequireCall = (expression) => {
40271
- const unwrappedExpression = unwrapTypescriptExpression(expression);
39901
+ const unwrappedExpression = unwrapExpression(expression);
40272
39902
  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;
40273
39903
  };
40274
39904
  const getModuleSource = (node) => {
@@ -40370,7 +40000,7 @@ const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
40370
40000
  return false;
40371
40001
  };
40372
40002
  const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
40373
- const unwrappedExpression = unwrapTypescriptExpression(expression);
40003
+ const unwrappedExpression = unwrapExpression(expression);
40374
40004
  if (isReactRequireCall(unwrappedExpression)) return true;
40375
40005
  if (!ts.isIdentifier(unwrappedExpression)) return false;
40376
40006
  if (reactImportBindings.namespaceNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
@@ -40378,7 +40008,7 @@ const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile,
40378
40008
  };
40379
40009
  const isReactUseExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
40380
40010
  if (!expression) return false;
40381
- const unwrappedExpression = unwrapTypescriptExpression(expression);
40011
+ const unwrappedExpression = unwrapExpression(expression);
40382
40012
  if (ts.isIdentifier(unwrappedExpression)) {
40383
40013
  if (reactImportBindings.useImportNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
40384
40014
  if (unwrappedExpression.text === USE_IDENTIFIER) return false;
@@ -40478,7 +40108,7 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
40478
40108
  return matchedIdentifier;
40479
40109
  };
40480
40110
  const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
40481
- const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filename));
40111
+ const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$2(filename));
40482
40112
  const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
40483
40113
  if (!useIdentifier) return null;
40484
40114
  return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
@@ -40514,6 +40144,12 @@ const WORKLET_ACCEPTING_CALLEES = new Set([
40514
40144
  "executeOnUIRuntimeSync"
40515
40145
  ]);
40516
40146
  const GESTURE_ROOT_IDENTIFIER = "Gesture";
40147
+ const getScriptKind$1 = (filename) => {
40148
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
40149
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
40150
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
40151
+ return ts.ScriptKind.JS;
40152
+ };
40517
40153
  const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
40518
40154
  const hasWorkletDirective = (node) => {
40519
40155
  if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
@@ -40585,11 +40221,17 @@ const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirecto
40585
40221
  } catch {
40586
40222
  return false;
40587
40223
  }
40588
- return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
40224
+ return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
40589
40225
  };
40590
40226
  const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
40591
40227
  const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
40592
40228
  const REACT_COMPILER_PLUGIN = "react-hooks-js";
40229
+ const getScriptKind = (filename) => {
40230
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
40231
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
40232
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
40233
+ return ts.ScriptKind.JS;
40234
+ };
40593
40235
  const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
40594
40236
  const nodeLineRange = (sourceFile, node) => ({
40595
40237
  startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
@@ -40651,7 +40293,7 @@ const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) =>
40651
40293
  try {
40652
40294
  const buffer = NFS.readFileSync(absolutePath);
40653
40295
  parsed = {
40654
- sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)),
40296
+ sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
40655
40297
  buffer
40656
40298
  };
40657
40299
  } catch {
@@ -43636,4 +43278,4 @@ const toJsonReport = (result, options) => buildJsonReport({
43636
43278
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
43637
43279
 
43638
43280
  //# sourceMappingURL=index.js.map
43639
- //# debugId=4096a0e2-9712-5e2f-b602-5ff55d3994a3
43281
+ //# debugId=1872748b-acb2-5e9a-a748-43bfd023eb85