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/lsp.js CHANGED
@@ -6,8 +6,6 @@ import os from "node:os";
6
6
  import * as Path from "node:path";
7
7
  import path from "node:path";
8
8
  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";
9
- import * as Crypto from "node:crypto";
10
- import crypto, { createHash } from "node:crypto";
11
9
  import * as NodeChildProcess from "node:child_process";
12
10
  import { execFileSync, spawn, spawnSync } from "node:child_process";
13
11
  import ts from "typescript";
@@ -15,6 +13,8 @@ import { parseJSON5 } from "confbox";
15
13
  import * as NodeUrl from "node:url";
16
14
  import { fileURLToPath } from "node:url";
17
15
  import { createJiti } from "jiti";
16
+ import * as Crypto from "node:crypto";
17
+ import crypto, { createHash } from "node:crypto";
18
18
  import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
19
19
  import { gzipSync } from "node:zlib";
20
20
  import { CodeActionKind, CodeActionTriggerKind, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FileChangeType, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node.js";
@@ -19365,13 +19365,12 @@ var JsonReportDiagnosticV3 = class extends Class("JsonReportDiagnosticV3")({
19365
19365
  fixGroupId: optional(String$1)
19366
19366
  }) {};
19367
19367
  /**
19368
- * Deterministic identity string for a retained diagnostic occurrence.
19369
- * Severity and message distinguish content variants at one normalized site.
19368
+ * Deterministic identity string for a diagnostic. Same diagnostic
19369
+ * across two scans yields the same identity; lets baselines,
19370
+ * suppression files, content-hash caches, and IDE "ignore this"
19371
+ * actions all key off one shared shape.
19370
19372
  */
19371
- const buildDiagnosticIdentity = (input) => {
19372
- const occurrenceDigest = createHash("sha256").update(JSON.stringify([input.severity, input.message])).digest("hex");
19373
- return `${input.filePath}::${input.line}:${input.column}::${input.plugin}/${input.rule}::${occurrenceDigest}`;
19374
- };
19373
+ const buildDiagnosticIdentity = (input) => `${input.filePath}::${input.line}:${input.column}::${input.plugin}/${input.rule}`;
19375
19374
  const JsonReportMode = Literals([
19376
19375
  "full",
19377
19376
  "diff",
@@ -37161,45 +37160,18 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
37161
37160
  }
37162
37161
  return diagnostics;
37163
37162
  };
37164
- const getTypescriptScriptKind = (fileName) => {
37165
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
37166
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
37167
- if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
37168
- return ts.ScriptKind.JS;
37169
- };
37170
- const unwrapTypescriptExpression = (expression) => {
37171
- let currentExpression = expression;
37172
- while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
37173
- return currentExpression;
37174
- };
37175
- const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
37176
- isAnimationFunction: false,
37177
- isMotionComponent: false,
37178
- isMotionComponentFactory: false,
37179
- isMotionComponentNamespace: false,
37180
- isMotionConfig: false,
37181
- isMotionNamespace: false,
37182
- isReducedMotionHook: false
37183
- };
37184
- const MOTION_COMPONENT_FACTORY_EXPORT_NAMES = new Set(["m", "motion"]);
37185
- const MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES = new Set(["Reorder"]);
37186
- const MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES = new Set(["Item"]);
37187
- const MOTION_ANIMATION_FUNCTION_EXPORT_NAMES = new Set(["animate"]);
37188
- const REDUCED_MOTION_HOOK_EXPORT_NAME = "useReducedMotion";
37189
- const MOTION_CONFIG_EXPORT_NAME = "MotionConfig";
37190
- const REDUCED_MOTION_PROP_NAME = "reducedMotion";
37191
- const REDUCED_MOTION_CONFIG_VALUES = new Set(["always", "user"]);
37192
- const SCRIPT_MODULE_EXTENSIONS = [
37163
+ const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
37164
+ const REDUCED_MOTION_PATTERN = new RegExp(REDUCED_MOTION_GREP_PATTERN);
37165
+ const REDUCED_MOTION_FILE_EXTENSIONS = new Set([
37193
37166
  ".ts",
37194
37167
  ".tsx",
37195
37168
  ".js",
37196
- ".jsx"
37197
- ];
37198
- const SCRIPT_FILE_EXTENSIONS = new Set(SCRIPT_MODULE_EXTENSIONS);
37199
- const STYLE_FILE_EXTENSIONS = new Set([".css", ".scss"]);
37200
- const MOTION_SOURCE_PREFILTER = /framer-motion|["']motion(?:\/[A-Za-z0-9_./-]+)?["']|MotionConfig|useReducedMotion/;
37201
- const REDUCED_MOTION_MEDIA_QUERY_PATTERN = /@media\s+([^{}]*\(\s*prefers-reduced-motion\s*:\s*reduce\s*\)[^{}]*)\{/gi;
37202
- const CSS_DECLARATION_PATTERN = /(?:^|[;{])\s*[-_A-Za-z][-_A-Za-z0-9]*\s*:/;
37169
+ ".jsx",
37170
+ ".css",
37171
+ ".scss"
37172
+ ]);
37173
+ const REDUCED_MOTION_FILE_GLOBS = [...REDUCED_MOTION_FILE_EXTENSIONS].map((extension) => `*${extension}`);
37174
+ const GIT_GREP_NO_MATCH_STATUS = 1;
37203
37175
  const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
37204
37176
  filePath: "package.json",
37205
37177
  plugin: "react-doctor",
@@ -37211,406 +37183,52 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
37211
37183
  column: 0,
37212
37184
  category: "Accessibility"
37213
37185
  };
37214
- const isMotionModuleSource = (moduleSource) => {
37215
- for (const packageName of MOTION_LIBRARY_PACKAGES) if (moduleSource === packageName || moduleSource.startsWith(`${packageName}/`)) return true;
37216
- return false;
37217
- };
37218
- const classifyMotionExport = (exportName) => ({
37219
- isAnimationFunction: MOTION_ANIMATION_FUNCTION_EXPORT_NAMES.has(exportName),
37220
- isMotionComponent: false,
37221
- isMotionComponentFactory: MOTION_COMPONENT_FACTORY_EXPORT_NAMES.has(exportName),
37222
- isMotionComponentNamespace: MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES.has(exportName),
37223
- isMotionConfig: exportName === MOTION_CONFIG_EXPORT_NAME,
37224
- isMotionNamespace: false,
37225
- isReducedMotionHook: exportName === REDUCED_MOTION_HOOK_EXPORT_NAME
37226
- });
37227
- const getImportModuleSource = (node) => {
37228
- let currentNode = node;
37229
- while (currentNode) {
37230
- if (ts.isImportDeclaration(currentNode) && ts.isStringLiteral(currentNode.moduleSpecifier)) return currentNode.moduleSpecifier.text;
37231
- currentNode = currentNode.parent;
37232
- }
37233
- return null;
37234
- };
37235
- const getImportedBindingEvidence = (declaration, typeChecker, program, visitedSymbols) => {
37236
- const moduleSource = getImportModuleSource(declaration);
37237
- if (!moduleSource) return null;
37238
- if (isMotionModuleSource(moduleSource) && ts.isNamespaceImport(declaration)) return {
37239
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37240
- isMotionNamespace: true
37241
- };
37242
- if (isMotionModuleSource(moduleSource) && ts.isImportSpecifier(declaration)) {
37243
- if (declaration.isTypeOnly) return null;
37244
- return classifyMotionExport(declaration.propertyName?.text ?? declaration.name.text);
37245
- }
37246
- if (isMotionModuleSource(moduleSource) && ts.isImportClause(declaration)) {
37247
- if (declaration.isTypeOnly) return null;
37248
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37249
- }
37250
- if (ts.isImportSpecifier(declaration) && !declaration.isTypeOnly || ts.isImportClause(declaration) && !declaration.isTypeOnly && declaration.name) {
37251
- const importedName = ts.isImportSpecifier(declaration) ? declaration.propertyName?.text ?? declaration.name.text : "default";
37252
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, declaration.getSourceFile().fileName, program);
37253
- if (!moduleSourceFile) return null;
37254
- return resolveModuleExportEvidence(moduleSourceFile, importedName, typeChecker, program, visitedSymbols);
37255
- }
37256
- return null;
37257
- };
37258
- const getLocalModuleSourceFile = (moduleSource, importingFileName, program) => {
37259
- if (!moduleSource.startsWith(".")) return null;
37260
- const moduleBasePath = Path.resolve(Path.dirname(importingFileName), moduleSource);
37261
- const moduleBaseExtension = Path.extname(moduleBasePath);
37262
- const typescriptModuleBasePath = moduleBaseExtension === ".js" || moduleBaseExtension === ".jsx" ? moduleBasePath.slice(0, -moduleBaseExtension.length) : null;
37263
- const candidates = [
37264
- moduleBasePath,
37265
- ...typescriptModuleBasePath ? [
37266
- `${typescriptModuleBasePath}.ts`,
37267
- `${typescriptModuleBasePath}.tsx`,
37268
- Path.join(typescriptModuleBasePath, "index.ts"),
37269
- Path.join(typescriptModuleBasePath, "index.tsx")
37270
- ] : [],
37271
- ...SCRIPT_MODULE_EXTENSIONS.map((extension) => `${moduleBasePath}${extension}`),
37272
- ...SCRIPT_MODULE_EXTENSIONS.map((extension) => Path.join(moduleBasePath, `index${extension}`))
37273
- ];
37274
- for (const candidate of candidates) {
37275
- const sourceFile = program.getSourceFile(candidate);
37276
- if (sourceFile) return sourceFile;
37277
- }
37278
- return null;
37279
- };
37280
- const resolveModuleExportEvidence = (sourceFile, exportName, typeChecker, program, visitedSymbols) => {
37281
- const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
37282
- if (moduleSymbol && visitedSymbols.has(moduleSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37283
- const nextVisitedSymbols = new Set(visitedSymbols);
37284
- if (moduleSymbol) nextVisitedSymbols.add(moduleSymbol);
37285
- for (const statement of sourceFile.statements) {
37286
- if (exportName === "default" && ts.isExportAssignment(statement) && !statement.isExportEquals) {
37287
- const evidence = resolveMotionExpressionEvidence(statement.expression, typeChecker, program, nextVisitedSymbols);
37288
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37289
- continue;
37290
- }
37291
- if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) continue;
37292
- if (statement.isTypeOnly) continue;
37293
- if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
37294
- const moduleSource = statement.moduleSpecifier.text;
37295
- if (!statement.exportClause) {
37296
- if (isMotionModuleSource(moduleSource)) {
37297
- const evidence = classifyMotionExport(exportName);
37298
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37299
- continue;
37300
- }
37301
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
37302
- if (moduleSourceFile) {
37303
- const evidence = resolveModuleExportEvidence(moduleSourceFile, exportName, typeChecker, program, nextVisitedSymbols);
37304
- if (hasMotionExpressionEvidence(evidence)) return evidence;
37305
- }
37306
- continue;
37307
- }
37308
- if (!ts.isNamedExports(statement.exportClause)) continue;
37309
- for (const exportSpecifier of statement.exportClause.elements) {
37310
- if (exportSpecifier.name.text !== exportName || exportSpecifier.isTypeOnly) continue;
37311
- const sourceExportName = exportSpecifier.propertyName?.text ?? exportSpecifier.name.text;
37312
- if (isMotionModuleSource(moduleSource)) return classifyMotionExport(sourceExportName);
37313
- const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
37314
- if (!moduleSourceFile) continue;
37315
- return resolveModuleExportEvidence(moduleSourceFile, sourceExportName, typeChecker, program, nextVisitedSymbols);
37316
- }
37317
- }
37318
- if (!moduleSymbol) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37319
- const exportedSymbol = typeChecker.getExportsOfModule(moduleSymbol).find((candidateSymbol) => candidateSymbol.name === exportName);
37320
- if (!exportedSymbol || nextVisitedSymbols.has(exportedSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37321
- nextVisitedSymbols.add(exportedSymbol);
37322
- let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
37323
- for (const declaration of exportedSymbol.declarations ?? []) {
37324
- if (ts.isExportSpecifier(declaration)) {
37325
- const targetSymbol = typeChecker.getExportSpecifierLocalTargetSymbol(declaration);
37326
- for (const targetDeclaration of targetSymbol?.declarations ?? []) {
37327
- const importedEvidence = getImportedBindingEvidence(targetDeclaration, typeChecker, program, nextVisitedSymbols);
37328
- if (importedEvidence) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
37329
- if (ts.isVariableDeclaration(targetDeclaration) && targetDeclaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(targetDeclaration.initializer, typeChecker, program, nextVisitedSymbols));
37330
- }
37331
- }
37332
- if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
37333
- }
37334
- return resolvedEvidence;
37335
- };
37336
- const mergeMotionExpressionEvidence = (leftEvidence, rightEvidence) => ({
37337
- isAnimationFunction: leftEvidence.isAnimationFunction || rightEvidence.isAnimationFunction,
37338
- isMotionComponent: leftEvidence.isMotionComponent || rightEvidence.isMotionComponent,
37339
- isMotionComponentFactory: leftEvidence.isMotionComponentFactory || rightEvidence.isMotionComponentFactory,
37340
- isMotionComponentNamespace: leftEvidence.isMotionComponentNamespace || rightEvidence.isMotionComponentNamespace,
37341
- isMotionConfig: leftEvidence.isMotionConfig || rightEvidence.isMotionConfig,
37342
- isMotionNamespace: leftEvidence.isMotionNamespace || rightEvidence.isMotionNamespace,
37343
- isReducedMotionHook: leftEvidence.isReducedMotionHook || rightEvidence.isReducedMotionHook
37344
- });
37345
- const hasMotionExpressionEvidence = (evidence) => evidence.isAnimationFunction || evidence.isMotionComponent || evidence.isMotionComponentFactory || evidence.isMotionComponentNamespace || evidence.isMotionConfig || evidence.isMotionNamespace || evidence.isReducedMotionHook;
37346
- const classifyMotionMember = (receiverEvidence, memberName) => {
37347
- if (receiverEvidence.isMotionNamespace) return classifyMotionExport(memberName);
37348
- if (receiverEvidence.isMotionComponentFactory) return {
37349
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37350
- isMotionComponent: true
37351
- };
37352
- if (receiverEvidence.isMotionComponentNamespace && MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES.has(memberName)) return {
37353
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37354
- isMotionComponent: true
37355
- };
37356
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37357
- };
37358
- const resolveMotionExpressionEvidence = (expression, typeChecker, program, visitedSymbols = /* @__PURE__ */ new Set()) => {
37359
- const unwrappedExpression = unwrapTypescriptExpression(expression);
37360
- if (ts.isIdentifier(unwrappedExpression)) {
37361
- const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
37362
- if (!symbol || visitedSymbols.has(symbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37363
- const nextVisitedSymbols = new Set(visitedSymbols);
37364
- nextVisitedSymbols.add(symbol);
37365
- let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
37366
- for (const declaration of symbol.declarations ?? []) {
37367
- const importedEvidence = getImportedBindingEvidence(declaration, typeChecker, program, nextVisitedSymbols);
37368
- if (importedEvidence) {
37369
- resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
37370
- continue;
37371
- }
37372
- if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
37373
- if (ts.isBindingElement(declaration) && ts.isObjectBindingPattern(declaration.parent) && ts.isVariableDeclaration(declaration.parent.parent) && declaration.parent.parent.initializer) {
37374
- const propertyName = declaration.propertyName ?? declaration.name;
37375
- if (ts.isIdentifier(propertyName) || ts.isStringLiteral(propertyName)) {
37376
- const receiverEvidence = resolveMotionExpressionEvidence(declaration.parent.parent.initializer, typeChecker, program, nextVisitedSymbols);
37377
- resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, classifyMotionMember(receiverEvidence, propertyName.text));
37378
- }
37379
- }
37380
- }
37381
- return resolvedEvidence;
37382
- }
37383
- if (ts.isPropertyAccessExpression(unwrappedExpression)) return classifyMotionMember(resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols), unwrappedExpression.name.text);
37384
- 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);
37385
- if (ts.isCallExpression(unwrappedExpression)) {
37386
- 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 {
37387
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37388
- isMotionNamespace: true
37389
- };
37390
- if (ts.isPropertyAccessExpression(unwrappedExpression.expression) && unwrappedExpression.expression.name.text === "bind") {
37391
- const boundReceiverEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression.expression, typeChecker, program, visitedSymbols);
37392
- if (boundReceiverEvidence.isAnimationFunction) return boundReceiverEvidence;
37393
- }
37394
- const calleeEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols);
37395
- if (calleeEvidence.isMotionComponentFactory) return {
37396
- ...EMPTY_MOTION_EXPRESSION_EVIDENCE,
37397
- isMotionComponent: true
37398
- };
37399
- return calleeEvidence;
37400
- }
37401
- return EMPTY_MOTION_EXPRESSION_EVIDENCE;
37402
- };
37403
- const getStaticStringExpressionValue = (expression, typeChecker, visitedSymbols = /* @__PURE__ */ new Set()) => {
37404
- const unwrappedExpression = unwrapTypescriptExpression(expression);
37405
- if (ts.isStringLiteral(unwrappedExpression) || ts.isNoSubstitutionTemplateLiteral(unwrappedExpression)) return unwrappedExpression.text;
37406
- if (!ts.isIdentifier(unwrappedExpression)) return null;
37407
- const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
37408
- if (!symbol || visitedSymbols.has(symbol)) return null;
37409
- const nextVisitedSymbols = new Set(visitedSymbols);
37410
- nextVisitedSymbols.add(symbol);
37411
- for (const declaration of symbol.declarations ?? []) {
37412
- if (!ts.isVariableDeclaration(declaration) || !declaration.initializer) continue;
37413
- if (!ts.isVariableDeclarationList(declaration.parent)) continue;
37414
- if (!(declaration.parent.flags & ts.NodeFlags.Const)) continue;
37415
- const value = getStaticStringExpressionValue(declaration.initializer, typeChecker, nextVisitedSymbols);
37416
- if (value !== null) return value;
37417
- }
37418
- return null;
37419
- };
37420
- const getStaticJsxAttributeValue = (attribute, typeChecker) => {
37421
- if (!attribute.initializer) return null;
37422
- if (ts.isStringLiteral(attribute.initializer)) return attribute.initializer.text;
37423
- if (!ts.isJsxExpression(attribute.initializer) || !attribute.initializer.expression) return null;
37424
- return getStaticStringExpressionValue(attribute.initializer.expression, typeChecker);
37425
- };
37426
- const isExpressionValueConsumed = (expression) => {
37427
- let currentExpression = expression;
37428
- while (ts.isParenthesizedExpression(currentExpression.parent) || ts.isAsExpression(currentExpression.parent) || ts.isSatisfiesExpression(currentExpression.parent) || ts.isNonNullExpression(currentExpression.parent) || ts.isTypeAssertionExpression(currentExpression.parent)) currentExpression = currentExpression.parent;
37429
- return !ts.isExpressionStatement(currentExpression.parent) && !ts.isVoidExpression(currentExpression.parent);
37430
- };
37431
- 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) ?? ""));
37432
- const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
37433
- const evidence = {
37434
- hasMotionUse: false,
37435
- hasReducedMotionHandling: false
37436
- };
37437
- const visitNode = (node) => {
37438
- if (ts.isCallExpression(node)) {
37439
- const calleeEvidence = resolveMotionExpressionEvidence(node.expression, typeChecker, program);
37440
- 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;
37441
- if (calleeEvidence.isAnimationFunction || isAnimationFunctionCallViaCallOrApply) evidence.hasMotionUse = true;
37442
- if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
37443
- }
37444
- if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
37445
- const tagEvidence = ts.isJsxNamespacedName(node.tagName) ? EMPTY_MOTION_EXPRESSION_EVIDENCE : resolveMotionExpressionEvidence(node.tagName, typeChecker, program);
37446
- if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
37447
- if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
37448
- }
37449
- ts.forEachChild(node, visitNode);
37450
- };
37451
- visitNode(sourceFile);
37452
- return evidence;
37453
- };
37454
- const analyzeReducedMotionSources = (sources) => {
37455
- if (!sources.some(({ sourceText }) => MOTION_SOURCE_PREFILTER.test(sourceText))) return {
37456
- hasMotionUse: false,
37457
- hasReducedMotionHandling: false
37458
- };
37459
- const compilerOptions = {
37460
- allowJs: true,
37461
- checkJs: false,
37462
- jsx: ts.JsxEmit.Preserve,
37463
- module: ts.ModuleKind.ESNext,
37464
- moduleResolution: ts.ModuleResolutionKind.Bundler,
37465
- noEmit: true,
37466
- noLib: true,
37467
- noResolve: true,
37468
- skipLibCheck: true,
37469
- target: ts.ScriptTarget.Latest
37470
- };
37471
- const sourceTextByFileName = new Map(sources.map(({ fileName: sourceFileName, sourceText: content }) => [Path.resolve(sourceFileName), content]));
37472
- const compilerHost = ts.createCompilerHost(compilerOptions);
37473
- const getDefaultSourceFile = compilerHost.getSourceFile.bind(compilerHost);
37474
- 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);
37475
- const program = ts.createProgram([...sourceTextByFileName.keys()], compilerOptions, compilerHost);
37476
- const typeChecker = program.getTypeChecker();
37477
- const evidence = {
37478
- hasMotionUse: false,
37479
- hasReducedMotionHandling: false
37480
- };
37481
- for (const sourceFileName of sourceTextByFileName.keys()) {
37482
- const sourceFile = program.getSourceFile(sourceFileName);
37483
- if (!sourceFile) continue;
37484
- const fileEvidence = collectScriptMotionEvidence(sourceFile, typeChecker, program);
37485
- evidence.hasMotionUse ||= fileEvidence.hasMotionUse;
37486
- evidence.hasReducedMotionHandling ||= fileEvidence.hasReducedMotionHandling;
37487
- }
37488
- return evidence;
37489
- };
37490
- const removeCssCommentsAndStrings = (content) => {
37491
- let sanitizedContent = "";
37492
- let quote = null;
37493
- let isInsideComment = false;
37494
- let isInsideLineComment = false;
37495
- for (let characterIndex = 0; characterIndex < content.length; characterIndex += 1) {
37496
- const character = content[characterIndex];
37497
- const nextCharacter = content[characterIndex + 1];
37498
- if (isInsideLineComment) {
37499
- sanitizedContent += character === "\n" ? "\n" : " ";
37500
- if (character === "\n") isInsideLineComment = false;
37501
- continue;
37502
- }
37503
- if (isInsideComment) {
37504
- if (character === "*" && nextCharacter === "/") {
37505
- sanitizedContent += " ";
37506
- characterIndex += 1;
37507
- isInsideComment = false;
37508
- } else sanitizedContent += character === "\n" ? "\n" : " ";
37509
- continue;
37510
- }
37511
- if (quote) {
37512
- if (character === "\\") {
37513
- sanitizedContent += " ";
37514
- characterIndex += 1;
37515
- continue;
37516
- }
37517
- if (character === quote) quote = null;
37518
- sanitizedContent += character === "\n" ? "\n" : " ";
37519
- continue;
37520
- }
37521
- if (character === "/" && nextCharacter === "*") {
37522
- sanitizedContent += " ";
37523
- characterIndex += 1;
37524
- isInsideComment = true;
37525
- continue;
37526
- }
37527
- if (character === "/" && nextCharacter === "/") {
37528
- sanitizedContent += " ";
37529
- characterIndex += 1;
37530
- isInsideLineComment = true;
37531
- continue;
37532
- }
37533
- if (character === "\"" || character === "'") {
37534
- sanitizedContent += " ";
37535
- quote = character;
37536
- continue;
37537
- }
37538
- sanitizedContent += character;
37539
- }
37540
- return sanitizedContent;
37541
- };
37542
- const findMatchingClosingBrace = (content, openingBraceIndex) => {
37543
- let depth = 0;
37544
- for (let characterIndex = openingBraceIndex; characterIndex < content.length; characterIndex += 1) {
37545
- if (content[characterIndex] === "{") depth += 1;
37546
- if (content[characterIndex] !== "}") continue;
37547
- depth -= 1;
37548
- if (depth === 0) return characterIndex;
37549
- }
37550
- return -1;
37551
- };
37552
- const hasReducedMotionMediaQuery = (content) => {
37553
- const sanitizedContent = removeCssCommentsAndStrings(content);
37554
- REDUCED_MOTION_MEDIA_QUERY_PATTERN.lastIndex = 0;
37555
- for (const match of sanitizedContent.matchAll(REDUCED_MOTION_MEDIA_QUERY_PATTERN)) {
37556
- const mediaPrelude = match[1];
37557
- if (/\bnot\b/i.test(mediaPrelude)) continue;
37558
- const openingBraceIndex = (match.index ?? 0) + match[0].lastIndexOf("{");
37559
- const closingBraceIndex = findMatchingClosingBrace(sanitizedContent, openingBraceIndex);
37560
- if (closingBraceIndex === -1) continue;
37561
- const body = sanitizedContent.slice(openingBraceIndex + 1, closingBraceIndex);
37562
- if (CSS_DECLARATION_PATTERN.test(body)) return true;
37563
- }
37564
- return false;
37565
- };
37566
- const collectProjectMotionEvidence = (rootDirectory) => {
37567
- const scriptSources = [];
37568
- let hasReducedMotionHandling = false;
37186
+ const hasReducedMotionHandlingViaFilesystem = (rootDirectory) => {
37569
37187
  for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
37570
- const extension = Path.extname(name);
37571
- if (!SCRIPT_FILE_EXTENSIONS.has(extension) && !STYLE_FILE_EXTENSIONS.has(extension)) continue;
37188
+ if (!REDUCED_MOTION_FILE_EXTENSIONS.has(Path.extname(name))) continue;
37572
37189
  let content;
37573
37190
  try {
37574
37191
  content = NFS.readFileSync(absolutePath, "utf-8");
37575
37192
  } catch {
37576
37193
  continue;
37577
37194
  }
37578
- if (STYLE_FILE_EXTENSIONS.has(extension)) {
37579
- if (hasReducedMotionMediaQuery(content)) hasReducedMotionHandling = true;
37580
- continue;
37581
- }
37582
- scriptSources.push({
37583
- fileName: absolutePath,
37584
- sourceText: content
37585
- });
37195
+ if (REDUCED_MOTION_PATTERN.test(content)) return true;
37586
37196
  }
37587
- if (scriptSources.length === 0) return {
37588
- hasMotionUse: false,
37589
- hasReducedMotionHandling
37590
- };
37591
- const scriptEvidence = analyzeReducedMotionSources(scriptSources);
37592
- const hasMotionUse = scriptEvidence.hasMotionUse;
37593
- hasReducedMotionHandling ||= scriptEvidence.hasReducedMotionHandling;
37594
- return {
37595
- hasMotionUse,
37596
- hasReducedMotionHandling
37597
- };
37197
+ return false;
37598
37198
  };
37599
37199
  const checkReducedMotion = (rootDirectory) => {
37600
37200
  const packageJsonPath = Path.join(rootDirectory, "package.json");
37601
37201
  if (!isFile(packageJsonPath)) return [];
37202
+ let hasMotionLibrary = false;
37602
37203
  try {
37603
37204
  const packageJson = readPackageJson(packageJsonPath);
37604
37205
  const allDependencies = {
37605
37206
  ...packageJson.dependencies,
37606
37207
  ...packageJson.devDependencies
37607
37208
  };
37608
- if (!Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName))) return [];
37209
+ hasMotionLibrary = Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName));
37609
37210
  } catch {
37610
37211
  return [];
37611
37212
  }
37612
- const evidence = collectProjectMotionEvidence(rootDirectory);
37613
- return evidence.hasMotionUse && !evidence.hasReducedMotionHandling ? [MISSING_REDUCED_MOTION_DIAGNOSTIC] : [];
37213
+ if (!hasMotionLibrary) return [];
37214
+ const result = spawnSync("git", [
37215
+ "grep",
37216
+ "--untracked",
37217
+ "-lE",
37218
+ REDUCED_MOTION_GREP_PATTERN,
37219
+ "--",
37220
+ ...REDUCED_MOTION_FILE_GLOBS
37221
+ ], {
37222
+ cwd: rootDirectory,
37223
+ encoding: "utf-8",
37224
+ stdio: [
37225
+ "ignore",
37226
+ "pipe",
37227
+ "pipe"
37228
+ ]
37229
+ });
37230
+ if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
37231
+ return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
37614
37232
  };
37615
37233
  const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
37616
37234
  filePath: relativePath,
@@ -40213,12 +39831,23 @@ const REACT_USE_BINDING_RESOLUTION = {
40213
39831
  isReactUseBinding: true,
40214
39832
  isReactNamespaceBinding: false
40215
39833
  };
39834
+ const getScriptKind$2 = (filename) => {
39835
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
39836
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
39837
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
39838
+ return ts.ScriptKind.JS;
39839
+ };
40216
39840
  const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
39841
+ const unwrapExpression = (expression) => {
39842
+ let currentExpression = expression;
39843
+ while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
39844
+ return currentExpression;
39845
+ };
40217
39846
  const getStaticPropertyName = (node) => {
40218
39847
  if (!node) return null;
40219
39848
  if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
40220
39849
  if (ts.isComputedPropertyName(node)) {
40221
- const expression = unwrapTypescriptExpression(node.expression);
39850
+ const expression = unwrapExpression(node.expression);
40222
39851
  if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) return expression.text;
40223
39852
  }
40224
39853
  return null;
@@ -40247,7 +39876,7 @@ const isReactUseObjectBindingElement = (bindingElement) => {
40247
39876
  return getStaticPropertyName(bindingElement.propertyName) === USE_IDENTIFIER;
40248
39877
  };
40249
39878
  const isReactRequireCall = (expression) => {
40250
- const unwrappedExpression = unwrapTypescriptExpression(expression);
39879
+ const unwrappedExpression = unwrapExpression(expression);
40251
39880
  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;
40252
39881
  };
40253
39882
  const getModuleSource = (node) => {
@@ -40349,7 +39978,7 @@ const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
40349
39978
  return false;
40350
39979
  };
40351
39980
  const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
40352
- const unwrappedExpression = unwrapTypescriptExpression(expression);
39981
+ const unwrappedExpression = unwrapExpression(expression);
40353
39982
  if (isReactRequireCall(unwrappedExpression)) return true;
40354
39983
  if (!ts.isIdentifier(unwrappedExpression)) return false;
40355
39984
  if (reactImportBindings.namespaceNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
@@ -40357,7 +39986,7 @@ const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile,
40357
39986
  };
40358
39987
  const isReactUseExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
40359
39988
  if (!expression) return false;
40360
- const unwrappedExpression = unwrapTypescriptExpression(expression);
39989
+ const unwrappedExpression = unwrapExpression(expression);
40361
39990
  if (ts.isIdentifier(unwrappedExpression)) {
40362
39991
  if (reactImportBindings.useImportNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
40363
39992
  if (unwrappedExpression.text === USE_IDENTIFIER) return false;
@@ -40457,7 +40086,7 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
40457
40086
  return matchedIdentifier;
40458
40087
  };
40459
40088
  const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
40460
- const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filename));
40089
+ const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$2(filename));
40461
40090
  const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
40462
40091
  if (!useIdentifier) return null;
40463
40092
  return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
@@ -40493,6 +40122,12 @@ const WORKLET_ACCEPTING_CALLEES = new Set([
40493
40122
  "executeOnUIRuntimeSync"
40494
40123
  ]);
40495
40124
  const GESTURE_ROOT_IDENTIFIER = "Gesture";
40125
+ const getScriptKind$1 = (filename) => {
40126
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
40127
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
40128
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
40129
+ return ts.ScriptKind.JS;
40130
+ };
40496
40131
  const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
40497
40132
  const hasWorkletDirective = (node) => {
40498
40133
  if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
@@ -40564,11 +40199,17 @@ const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirecto
40564
40199
  } catch {
40565
40200
  return false;
40566
40201
  }
40567
- return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
40202
+ return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
40568
40203
  };
40569
40204
  const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
40570
40205
  const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
40571
40206
  const REACT_COMPILER_PLUGIN = "react-hooks-js";
40207
+ const getScriptKind = (filename) => {
40208
+ if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
40209
+ if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
40210
+ if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
40211
+ return ts.ScriptKind.JS;
40212
+ };
40572
40213
  const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
40573
40214
  const nodeLineRange = (sourceFile, node) => ({
40574
40215
  startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
@@ -40630,7 +40271,7 @@ const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) =>
40630
40271
  try {
40631
40272
  const buffer = NFS.readFileSync(absolutePath);
40632
40273
  parsed = {
40633
- sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)),
40274
+ sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
40634
40275
  buffer
40635
40276
  };
40636
40277
  } catch {
@@ -43432,9 +43073,7 @@ const toLspDiagnostic = (input) => {
43432
43073
  line: diagnostic.line,
43433
43074
  column: diagnostic.column,
43434
43075
  plugin: diagnostic.plugin,
43435
- rule: diagnostic.rule,
43436
- severity: diagnostic.severity,
43437
- message: diagnostic.message
43076
+ rule: diagnostic.rule
43438
43077
  }),
43439
43078
  plugin: diagnostic.plugin,
43440
43079
  rule: diagnostic.rule,
@@ -45276,5 +44915,5 @@ const startLanguageServer = () => {
45276
44915
  };
45277
44916
  //#endregion
45278
44917
  export { startLanguageServer };
45279
- !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]="249156e6-4373-541f-bda8-032ea9394ea7")}catch(e){}}();
45280
- //# debugId=249156e6-4373-541f-bda8-032ea9394ea7
44918
+ !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]="64a3133a-c62e-5131-9413-879f051371ef")}catch(e){}}();
44919
+ //# debugId=64a3133a-c62e-5131-9413-879f051371ef