react-doctor 0.7.7-dev.e632f8a → 0.7.7-dev.ec144d9

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