react-doctor 0.7.6-dev.fbd85e3 → 0.7.7-dev.04c7e36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +702 -160
- package/dist/index.d.ts +5 -4
- package/dist/index.js +562 -136
- package/dist/lsp.js +580 -161
- package/dist/skills/improve-react/AUDIT.md +140 -0
- package/dist/skills/improve-react/PLAN-TEMPLATE.md +82 -0
- package/dist/skills/improve-react/SKILL.md +121 -0
- package/package.json +6 -6
package/dist/lsp.js
CHANGED
|
@@ -32959,7 +32959,6 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
32959
32959
|
"out",
|
|
32960
32960
|
"storybook-static"
|
|
32961
32961
|
]);
|
|
32962
|
-
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
32963
32962
|
const MILLISECONDS_PER_SECOND = 1e3;
|
|
32964
32963
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
32965
32964
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -33092,7 +33091,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33092
33091
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33093
33092
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33094
33093
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33095
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
33094
|
+
const CORE_PACKAGE_VERSION = "0.7.7";
|
|
33096
33095
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33097
33096
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33098
33097
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33195,13 +33194,15 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33195
33194
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33196
33195
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33197
33196
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33197
|
+
const UTF8_BOM_CHARACTER = "";
|
|
33198
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
33198
33199
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33199
33200
|
const clearPackageJsonCache = () => {
|
|
33200
33201
|
cachedPackageJsons.clear();
|
|
33201
33202
|
};
|
|
33202
33203
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
33203
33204
|
try {
|
|
33204
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
33205
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
33205
33206
|
} catch (error) {
|
|
33206
33207
|
if (error instanceof SyntaxError) return {};
|
|
33207
33208
|
if (isErrnoException(error)) {
|
|
@@ -36343,6 +36344,49 @@ const assignFixGroups = (diagnostics) => {
|
|
|
36343
36344
|
};
|
|
36344
36345
|
});
|
|
36345
36346
|
};
|
|
36347
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
36348
|
+
["no-initialize-state", 0],
|
|
36349
|
+
["no-adjust-state-on-prop-change", 1],
|
|
36350
|
+
["no-derived-state", 2],
|
|
36351
|
+
["no-derived-state-effect", 3]
|
|
36352
|
+
]);
|
|
36353
|
+
const buildDiagnosticSiteKey = (diagnostic) => `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}`;
|
|
36354
|
+
const isReactDoctorRulesOfHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-doctor" && diagnostic.rule === "rules-of-hooks";
|
|
36355
|
+
const isReactCompilerHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-hooks-js" && diagnostic.rule === "hooks";
|
|
36356
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
36357
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
36358
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
36359
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
36360
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
36361
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
36362
|
+
}
|
|
36363
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
36364
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
36365
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
36366
|
+
};
|
|
36367
|
+
const keepHighestSeverity = (survivingDiagnostic, collapsedDiagnostic) => collapsedDiagnostic.severity === "error" && survivingDiagnostic.severity !== "error" ? {
|
|
36368
|
+
...survivingDiagnostic,
|
|
36369
|
+
severity: "error"
|
|
36370
|
+
} : survivingDiagnostic;
|
|
36371
|
+
const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
36372
|
+
const reactDoctorHookSites = new Set(diagnostics.filter(isReactDoctorRulesOfHooksDiagnostic).map(buildDiagnosticSiteKey));
|
|
36373
|
+
const uniqueDiagnostics = [];
|
|
36374
|
+
for (const diagnostic of diagnostics) {
|
|
36375
|
+
if (isReactCompilerHooksDiagnostic(diagnostic) && reactDoctorHookSites.has(buildDiagnosticSiteKey(diagnostic))) continue;
|
|
36376
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
36377
|
+
if (priority !== void 0) {
|
|
36378
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
36379
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
36380
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
36381
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
36382
|
+
if (existingDiagnostic && existingPriority !== void 0) uniqueDiagnostics[overlappingDiagnosticIndex] = priority < existingPriority ? keepHighestSeverity(diagnostic, existingDiagnostic) : keepHighestSeverity(existingDiagnostic, diagnostic);
|
|
36383
|
+
continue;
|
|
36384
|
+
}
|
|
36385
|
+
}
|
|
36386
|
+
uniqueDiagnostics.push(diagnostic);
|
|
36387
|
+
}
|
|
36388
|
+
return uniqueDiagnostics;
|
|
36389
|
+
};
|
|
36346
36390
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
36347
36391
|
const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
|
|
36348
36392
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -36768,7 +36812,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
36768
36812
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
36769
36813
|
const PACKAGE_JSON_FILE = "package.json";
|
|
36770
36814
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
36771
|
-
const UTF8_BOM_CHAR = "";
|
|
36772
36815
|
const HARDENING_SETTING_KEYS = new Set([
|
|
36773
36816
|
"minimumReleaseAge",
|
|
36774
36817
|
"blockExoticSubdeps",
|
|
@@ -36793,12 +36836,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
36793
36836
|
return rawValue;
|
|
36794
36837
|
};
|
|
36795
36838
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
36796
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
36797
36839
|
const parseHardeningSettings = (content) => {
|
|
36798
36840
|
let minimumReleaseAge = null;
|
|
36799
36841
|
let blockExoticSubdeps = null;
|
|
36800
36842
|
let trustPolicy = null;
|
|
36801
|
-
const lines =
|
|
36843
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
36802
36844
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
36803
36845
|
const lineText = lines[lineIndex];
|
|
36804
36846
|
if (lineText === void 0) continue;
|
|
@@ -36832,7 +36874,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
36832
36874
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
36833
36875
|
if (!isFile(packageJsonPath)) return false;
|
|
36834
36876
|
try {
|
|
36835
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
36877
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36836
36878
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
36837
36879
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
36838
36880
|
} catch {
|
|
@@ -37118,18 +37160,45 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
37118
37160
|
}
|
|
37119
37161
|
return diagnostics;
|
|
37120
37162
|
};
|
|
37121
|
-
const
|
|
37122
|
-
|
|
37123
|
-
|
|
37163
|
+
const getTypescriptScriptKind = (fileName) => {
|
|
37164
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
37165
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
37166
|
+
if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
37167
|
+
return ts.ScriptKind.JS;
|
|
37168
|
+
};
|
|
37169
|
+
const unwrapTypescriptExpression = (expression) => {
|
|
37170
|
+
let currentExpression = expression;
|
|
37171
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
37172
|
+
return currentExpression;
|
|
37173
|
+
};
|
|
37174
|
+
const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
|
|
37175
|
+
isAnimationFunction: false,
|
|
37176
|
+
isMotionComponent: false,
|
|
37177
|
+
isMotionComponentFactory: false,
|
|
37178
|
+
isMotionComponentNamespace: false,
|
|
37179
|
+
isMotionConfig: false,
|
|
37180
|
+
isMotionNamespace: false,
|
|
37181
|
+
isReducedMotionHook: false
|
|
37182
|
+
};
|
|
37183
|
+
const MOTION_COMPONENT_FACTORY_EXPORT_NAMES = new Set(["m", "motion"]);
|
|
37184
|
+
const MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES = new Set(["Reorder"]);
|
|
37185
|
+
const MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES = new Set(["Item"]);
|
|
37186
|
+
const MOTION_ANIMATION_FUNCTION_EXPORT_NAMES = new Set(["animate"]);
|
|
37187
|
+
const REDUCED_MOTION_HOOK_EXPORT_NAME = "useReducedMotion";
|
|
37188
|
+
const MOTION_CONFIG_EXPORT_NAME = "MotionConfig";
|
|
37189
|
+
const REDUCED_MOTION_PROP_NAME = "reducedMotion";
|
|
37190
|
+
const REDUCED_MOTION_CONFIG_VALUES = new Set(["always", "user"]);
|
|
37191
|
+
const SCRIPT_MODULE_EXTENSIONS = [
|
|
37124
37192
|
".ts",
|
|
37125
37193
|
".tsx",
|
|
37126
37194
|
".js",
|
|
37127
|
-
".jsx"
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
]);
|
|
37131
|
-
const
|
|
37132
|
-
const
|
|
37195
|
+
".jsx"
|
|
37196
|
+
];
|
|
37197
|
+
const SCRIPT_FILE_EXTENSIONS = new Set(SCRIPT_MODULE_EXTENSIONS);
|
|
37198
|
+
const STYLE_FILE_EXTENSIONS = new Set([".css", ".scss"]);
|
|
37199
|
+
const MOTION_SOURCE_PREFILTER = /framer-motion|["']motion(?:\/[A-Za-z0-9_./-]+)?["']|MotionConfig|useReducedMotion/;
|
|
37200
|
+
const REDUCED_MOTION_MEDIA_QUERY_PATTERN = /@media\s+([^{}]*\(\s*prefers-reduced-motion\s*:\s*reduce\s*\)[^{}]*)\{/gi;
|
|
37201
|
+
const CSS_DECLARATION_PATTERN = /(?:^|[;{])\s*[-_A-Za-z][-_A-Za-z0-9]*\s*:/;
|
|
37133
37202
|
const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
37134
37203
|
filePath: "package.json",
|
|
37135
37204
|
plugin: "react-doctor",
|
|
@@ -37141,52 +37210,406 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
|
37141
37210
|
column: 0,
|
|
37142
37211
|
category: "Accessibility"
|
|
37143
37212
|
};
|
|
37144
|
-
const
|
|
37213
|
+
const isMotionModuleSource = (moduleSource) => {
|
|
37214
|
+
for (const packageName of MOTION_LIBRARY_PACKAGES) if (moduleSource === packageName || moduleSource.startsWith(`${packageName}/`)) return true;
|
|
37215
|
+
return false;
|
|
37216
|
+
};
|
|
37217
|
+
const classifyMotionExport = (exportName) => ({
|
|
37218
|
+
isAnimationFunction: MOTION_ANIMATION_FUNCTION_EXPORT_NAMES.has(exportName),
|
|
37219
|
+
isMotionComponent: false,
|
|
37220
|
+
isMotionComponentFactory: MOTION_COMPONENT_FACTORY_EXPORT_NAMES.has(exportName),
|
|
37221
|
+
isMotionComponentNamespace: MOTION_COMPONENT_NAMESPACE_EXPORT_NAMES.has(exportName),
|
|
37222
|
+
isMotionConfig: exportName === MOTION_CONFIG_EXPORT_NAME,
|
|
37223
|
+
isMotionNamespace: false,
|
|
37224
|
+
isReducedMotionHook: exportName === REDUCED_MOTION_HOOK_EXPORT_NAME
|
|
37225
|
+
});
|
|
37226
|
+
const getImportModuleSource = (node) => {
|
|
37227
|
+
let currentNode = node;
|
|
37228
|
+
while (currentNode) {
|
|
37229
|
+
if (ts.isImportDeclaration(currentNode) && ts.isStringLiteral(currentNode.moduleSpecifier)) return currentNode.moduleSpecifier.text;
|
|
37230
|
+
currentNode = currentNode.parent;
|
|
37231
|
+
}
|
|
37232
|
+
return null;
|
|
37233
|
+
};
|
|
37234
|
+
const getImportedBindingEvidence = (declaration, typeChecker, program, visitedSymbols) => {
|
|
37235
|
+
const moduleSource = getImportModuleSource(declaration);
|
|
37236
|
+
if (!moduleSource) return null;
|
|
37237
|
+
if (isMotionModuleSource(moduleSource) && ts.isNamespaceImport(declaration)) return {
|
|
37238
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
37239
|
+
isMotionNamespace: true
|
|
37240
|
+
};
|
|
37241
|
+
if (isMotionModuleSource(moduleSource) && ts.isImportSpecifier(declaration)) {
|
|
37242
|
+
if (declaration.isTypeOnly) return null;
|
|
37243
|
+
return classifyMotionExport(declaration.propertyName?.text ?? declaration.name.text);
|
|
37244
|
+
}
|
|
37245
|
+
if (isMotionModuleSource(moduleSource) && ts.isImportClause(declaration)) {
|
|
37246
|
+
if (declaration.isTypeOnly) return null;
|
|
37247
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37248
|
+
}
|
|
37249
|
+
if (ts.isImportSpecifier(declaration) && !declaration.isTypeOnly || ts.isImportClause(declaration) && !declaration.isTypeOnly && declaration.name) {
|
|
37250
|
+
const importedName = ts.isImportSpecifier(declaration) ? declaration.propertyName?.text ?? declaration.name.text : "default";
|
|
37251
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, declaration.getSourceFile().fileName, program);
|
|
37252
|
+
if (!moduleSourceFile) return null;
|
|
37253
|
+
return resolveModuleExportEvidence(moduleSourceFile, importedName, typeChecker, program, visitedSymbols);
|
|
37254
|
+
}
|
|
37255
|
+
return null;
|
|
37256
|
+
};
|
|
37257
|
+
const getLocalModuleSourceFile = (moduleSource, importingFileName, program) => {
|
|
37258
|
+
if (!moduleSource.startsWith(".")) return null;
|
|
37259
|
+
const moduleBasePath = Path.resolve(Path.dirname(importingFileName), moduleSource);
|
|
37260
|
+
const moduleBaseExtension = Path.extname(moduleBasePath);
|
|
37261
|
+
const typescriptModuleBasePath = moduleBaseExtension === ".js" || moduleBaseExtension === ".jsx" ? moduleBasePath.slice(0, -moduleBaseExtension.length) : null;
|
|
37262
|
+
const candidates = [
|
|
37263
|
+
moduleBasePath,
|
|
37264
|
+
...typescriptModuleBasePath ? [
|
|
37265
|
+
`${typescriptModuleBasePath}.ts`,
|
|
37266
|
+
`${typescriptModuleBasePath}.tsx`,
|
|
37267
|
+
Path.join(typescriptModuleBasePath, "index.ts"),
|
|
37268
|
+
Path.join(typescriptModuleBasePath, "index.tsx")
|
|
37269
|
+
] : [],
|
|
37270
|
+
...SCRIPT_MODULE_EXTENSIONS.map((extension) => `${moduleBasePath}${extension}`),
|
|
37271
|
+
...SCRIPT_MODULE_EXTENSIONS.map((extension) => Path.join(moduleBasePath, `index${extension}`))
|
|
37272
|
+
];
|
|
37273
|
+
for (const candidate of candidates) {
|
|
37274
|
+
const sourceFile = program.getSourceFile(candidate);
|
|
37275
|
+
if (sourceFile) return sourceFile;
|
|
37276
|
+
}
|
|
37277
|
+
return null;
|
|
37278
|
+
};
|
|
37279
|
+
const resolveModuleExportEvidence = (sourceFile, exportName, typeChecker, program, visitedSymbols) => {
|
|
37280
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
37281
|
+
if (moduleSymbol && visitedSymbols.has(moduleSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37282
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
37283
|
+
if (moduleSymbol) nextVisitedSymbols.add(moduleSymbol);
|
|
37284
|
+
for (const statement of sourceFile.statements) {
|
|
37285
|
+
if (exportName === "default" && ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
37286
|
+
const evidence = resolveMotionExpressionEvidence(statement.expression, typeChecker, program, nextVisitedSymbols);
|
|
37287
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
37288
|
+
continue;
|
|
37289
|
+
}
|
|
37290
|
+
if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) continue;
|
|
37291
|
+
if (statement.isTypeOnly) continue;
|
|
37292
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
37293
|
+
const moduleSource = statement.moduleSpecifier.text;
|
|
37294
|
+
if (!statement.exportClause) {
|
|
37295
|
+
if (isMotionModuleSource(moduleSource)) {
|
|
37296
|
+
const evidence = classifyMotionExport(exportName);
|
|
37297
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
37298
|
+
continue;
|
|
37299
|
+
}
|
|
37300
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
|
|
37301
|
+
if (moduleSourceFile) {
|
|
37302
|
+
const evidence = resolveModuleExportEvidence(moduleSourceFile, exportName, typeChecker, program, nextVisitedSymbols);
|
|
37303
|
+
if (hasMotionExpressionEvidence(evidence)) return evidence;
|
|
37304
|
+
}
|
|
37305
|
+
continue;
|
|
37306
|
+
}
|
|
37307
|
+
if (!ts.isNamedExports(statement.exportClause)) continue;
|
|
37308
|
+
for (const exportSpecifier of statement.exportClause.elements) {
|
|
37309
|
+
if (exportSpecifier.name.text !== exportName || exportSpecifier.isTypeOnly) continue;
|
|
37310
|
+
const sourceExportName = exportSpecifier.propertyName?.text ?? exportSpecifier.name.text;
|
|
37311
|
+
if (isMotionModuleSource(moduleSource)) return classifyMotionExport(sourceExportName);
|
|
37312
|
+
const moduleSourceFile = getLocalModuleSourceFile(moduleSource, sourceFile.fileName, program);
|
|
37313
|
+
if (!moduleSourceFile) continue;
|
|
37314
|
+
return resolveModuleExportEvidence(moduleSourceFile, sourceExportName, typeChecker, program, nextVisitedSymbols);
|
|
37315
|
+
}
|
|
37316
|
+
}
|
|
37317
|
+
if (!moduleSymbol) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37318
|
+
const exportedSymbol = typeChecker.getExportsOfModule(moduleSymbol).find((candidateSymbol) => candidateSymbol.name === exportName);
|
|
37319
|
+
if (!exportedSymbol || nextVisitedSymbols.has(exportedSymbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37320
|
+
nextVisitedSymbols.add(exportedSymbol);
|
|
37321
|
+
let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37322
|
+
for (const declaration of exportedSymbol.declarations ?? []) {
|
|
37323
|
+
if (ts.isExportSpecifier(declaration)) {
|
|
37324
|
+
const targetSymbol = typeChecker.getExportSpecifierLocalTargetSymbol(declaration);
|
|
37325
|
+
for (const targetDeclaration of targetSymbol?.declarations ?? []) {
|
|
37326
|
+
const importedEvidence = getImportedBindingEvidence(targetDeclaration, typeChecker, program, nextVisitedSymbols);
|
|
37327
|
+
if (importedEvidence) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
|
|
37328
|
+
if (ts.isVariableDeclaration(targetDeclaration) && targetDeclaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(targetDeclaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
37329
|
+
}
|
|
37330
|
+
}
|
|
37331
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
37332
|
+
}
|
|
37333
|
+
return resolvedEvidence;
|
|
37334
|
+
};
|
|
37335
|
+
const mergeMotionExpressionEvidence = (leftEvidence, rightEvidence) => ({
|
|
37336
|
+
isAnimationFunction: leftEvidence.isAnimationFunction || rightEvidence.isAnimationFunction,
|
|
37337
|
+
isMotionComponent: leftEvidence.isMotionComponent || rightEvidence.isMotionComponent,
|
|
37338
|
+
isMotionComponentFactory: leftEvidence.isMotionComponentFactory || rightEvidence.isMotionComponentFactory,
|
|
37339
|
+
isMotionComponentNamespace: leftEvidence.isMotionComponentNamespace || rightEvidence.isMotionComponentNamespace,
|
|
37340
|
+
isMotionConfig: leftEvidence.isMotionConfig || rightEvidence.isMotionConfig,
|
|
37341
|
+
isMotionNamespace: leftEvidence.isMotionNamespace || rightEvidence.isMotionNamespace,
|
|
37342
|
+
isReducedMotionHook: leftEvidence.isReducedMotionHook || rightEvidence.isReducedMotionHook
|
|
37343
|
+
});
|
|
37344
|
+
const hasMotionExpressionEvidence = (evidence) => evidence.isAnimationFunction || evidence.isMotionComponent || evidence.isMotionComponentFactory || evidence.isMotionComponentNamespace || evidence.isMotionConfig || evidence.isMotionNamespace || evidence.isReducedMotionHook;
|
|
37345
|
+
const classifyMotionMember = (receiverEvidence, memberName) => {
|
|
37346
|
+
if (receiverEvidence.isMotionNamespace) return classifyMotionExport(memberName);
|
|
37347
|
+
if (receiverEvidence.isMotionComponentFactory) return {
|
|
37348
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
37349
|
+
isMotionComponent: true
|
|
37350
|
+
};
|
|
37351
|
+
if (receiverEvidence.isMotionComponentNamespace && MOTION_COMPONENT_NAMESPACE_MEMBER_NAMES.has(memberName)) return {
|
|
37352
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
37353
|
+
isMotionComponent: true
|
|
37354
|
+
};
|
|
37355
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37356
|
+
};
|
|
37357
|
+
const resolveMotionExpressionEvidence = (expression, typeChecker, program, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
37358
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
37359
|
+
if (ts.isIdentifier(unwrappedExpression)) {
|
|
37360
|
+
const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
|
|
37361
|
+
if (!symbol || visitedSymbols.has(symbol)) return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37362
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
37363
|
+
nextVisitedSymbols.add(symbol);
|
|
37364
|
+
let resolvedEvidence = EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37365
|
+
for (const declaration of symbol.declarations ?? []) {
|
|
37366
|
+
const importedEvidence = getImportedBindingEvidence(declaration, typeChecker, program, nextVisitedSymbols);
|
|
37367
|
+
if (importedEvidence) {
|
|
37368
|
+
resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, importedEvidence);
|
|
37369
|
+
continue;
|
|
37370
|
+
}
|
|
37371
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, resolveMotionExpressionEvidence(declaration.initializer, typeChecker, program, nextVisitedSymbols));
|
|
37372
|
+
if (ts.isBindingElement(declaration) && ts.isObjectBindingPattern(declaration.parent) && ts.isVariableDeclaration(declaration.parent.parent) && declaration.parent.parent.initializer) {
|
|
37373
|
+
const propertyName = declaration.propertyName ?? declaration.name;
|
|
37374
|
+
if (ts.isIdentifier(propertyName) || ts.isStringLiteral(propertyName)) {
|
|
37375
|
+
const receiverEvidence = resolveMotionExpressionEvidence(declaration.parent.parent.initializer, typeChecker, program, nextVisitedSymbols);
|
|
37376
|
+
resolvedEvidence = mergeMotionExpressionEvidence(resolvedEvidence, classifyMotionMember(receiverEvidence, propertyName.text));
|
|
37377
|
+
}
|
|
37378
|
+
}
|
|
37379
|
+
}
|
|
37380
|
+
return resolvedEvidence;
|
|
37381
|
+
}
|
|
37382
|
+
if (ts.isPropertyAccessExpression(unwrappedExpression)) return classifyMotionMember(resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols), unwrappedExpression.name.text);
|
|
37383
|
+
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);
|
|
37384
|
+
if (ts.isCallExpression(unwrappedExpression)) {
|
|
37385
|
+
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 {
|
|
37386
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
37387
|
+
isMotionNamespace: true
|
|
37388
|
+
};
|
|
37389
|
+
if (ts.isPropertyAccessExpression(unwrappedExpression.expression) && unwrappedExpression.expression.name.text === "bind") {
|
|
37390
|
+
const boundReceiverEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression.expression, typeChecker, program, visitedSymbols);
|
|
37391
|
+
if (boundReceiverEvidence.isAnimationFunction) return boundReceiverEvidence;
|
|
37392
|
+
}
|
|
37393
|
+
const calleeEvidence = resolveMotionExpressionEvidence(unwrappedExpression.expression, typeChecker, program, visitedSymbols);
|
|
37394
|
+
if (calleeEvidence.isMotionComponentFactory) return {
|
|
37395
|
+
...EMPTY_MOTION_EXPRESSION_EVIDENCE,
|
|
37396
|
+
isMotionComponent: true
|
|
37397
|
+
};
|
|
37398
|
+
return calleeEvidence;
|
|
37399
|
+
}
|
|
37400
|
+
return EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
37401
|
+
};
|
|
37402
|
+
const getStaticStringExpressionValue = (expression, typeChecker, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
37403
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
37404
|
+
if (ts.isStringLiteral(unwrappedExpression) || ts.isNoSubstitutionTemplateLiteral(unwrappedExpression)) return unwrappedExpression.text;
|
|
37405
|
+
if (!ts.isIdentifier(unwrappedExpression)) return null;
|
|
37406
|
+
const symbol = typeChecker.getSymbolAtLocation(unwrappedExpression);
|
|
37407
|
+
if (!symbol || visitedSymbols.has(symbol)) return null;
|
|
37408
|
+
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
37409
|
+
nextVisitedSymbols.add(symbol);
|
|
37410
|
+
for (const declaration of symbol.declarations ?? []) {
|
|
37411
|
+
if (!ts.isVariableDeclaration(declaration) || !declaration.initializer) continue;
|
|
37412
|
+
if (!ts.isVariableDeclarationList(declaration.parent)) continue;
|
|
37413
|
+
if (!(declaration.parent.flags & ts.NodeFlags.Const)) continue;
|
|
37414
|
+
const value = getStaticStringExpressionValue(declaration.initializer, typeChecker, nextVisitedSymbols);
|
|
37415
|
+
if (value !== null) return value;
|
|
37416
|
+
}
|
|
37417
|
+
return null;
|
|
37418
|
+
};
|
|
37419
|
+
const getStaticJsxAttributeValue = (attribute, typeChecker) => {
|
|
37420
|
+
if (!attribute.initializer) return null;
|
|
37421
|
+
if (ts.isStringLiteral(attribute.initializer)) return attribute.initializer.text;
|
|
37422
|
+
if (!ts.isJsxExpression(attribute.initializer) || !attribute.initializer.expression) return null;
|
|
37423
|
+
return getStaticStringExpressionValue(attribute.initializer.expression, typeChecker);
|
|
37424
|
+
};
|
|
37425
|
+
const isExpressionValueConsumed = (expression) => {
|
|
37426
|
+
let currentExpression = expression;
|
|
37427
|
+
while (ts.isParenthesizedExpression(currentExpression.parent) || ts.isAsExpression(currentExpression.parent) || ts.isSatisfiesExpression(currentExpression.parent) || ts.isNonNullExpression(currentExpression.parent) || ts.isTypeAssertionExpression(currentExpression.parent)) currentExpression = currentExpression.parent;
|
|
37428
|
+
return !ts.isExpressionStatement(currentExpression.parent) && !ts.isVoidExpression(currentExpression.parent);
|
|
37429
|
+
};
|
|
37430
|
+
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) ?? ""));
|
|
37431
|
+
const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
|
|
37432
|
+
const evidence = {
|
|
37433
|
+
hasMotionUse: false,
|
|
37434
|
+
hasReducedMotionHandling: false
|
|
37435
|
+
};
|
|
37436
|
+
const visitNode = (node) => {
|
|
37437
|
+
if (ts.isCallExpression(node)) {
|
|
37438
|
+
const calleeEvidence = resolveMotionExpressionEvidence(node.expression, typeChecker, program);
|
|
37439
|
+
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;
|
|
37440
|
+
if (calleeEvidence.isAnimationFunction || isAnimationFunctionCallViaCallOrApply) evidence.hasMotionUse = true;
|
|
37441
|
+
if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
|
|
37442
|
+
}
|
|
37443
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
37444
|
+
const tagEvidence = ts.isJsxNamespacedName(node.tagName) ? EMPTY_MOTION_EXPRESSION_EVIDENCE : resolveMotionExpressionEvidence(node.tagName, typeChecker, program);
|
|
37445
|
+
if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
|
|
37446
|
+
if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
|
|
37447
|
+
}
|
|
37448
|
+
ts.forEachChild(node, visitNode);
|
|
37449
|
+
};
|
|
37450
|
+
visitNode(sourceFile);
|
|
37451
|
+
return evidence;
|
|
37452
|
+
};
|
|
37453
|
+
const analyzeReducedMotionSources = (sources) => {
|
|
37454
|
+
if (!sources.some(({ sourceText }) => MOTION_SOURCE_PREFILTER.test(sourceText))) return {
|
|
37455
|
+
hasMotionUse: false,
|
|
37456
|
+
hasReducedMotionHandling: false
|
|
37457
|
+
};
|
|
37458
|
+
const compilerOptions = {
|
|
37459
|
+
allowJs: true,
|
|
37460
|
+
checkJs: false,
|
|
37461
|
+
jsx: ts.JsxEmit.Preserve,
|
|
37462
|
+
module: ts.ModuleKind.ESNext,
|
|
37463
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
37464
|
+
noEmit: true,
|
|
37465
|
+
noLib: true,
|
|
37466
|
+
noResolve: true,
|
|
37467
|
+
skipLibCheck: true,
|
|
37468
|
+
target: ts.ScriptTarget.Latest
|
|
37469
|
+
};
|
|
37470
|
+
const sourceTextByFileName = new Map(sources.map(({ fileName: sourceFileName, sourceText: content }) => [Path.resolve(sourceFileName), content]));
|
|
37471
|
+
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
37472
|
+
const getDefaultSourceFile = compilerHost.getSourceFile.bind(compilerHost);
|
|
37473
|
+
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);
|
|
37474
|
+
const program = ts.createProgram([...sourceTextByFileName.keys()], compilerOptions, compilerHost);
|
|
37475
|
+
const typeChecker = program.getTypeChecker();
|
|
37476
|
+
const evidence = {
|
|
37477
|
+
hasMotionUse: false,
|
|
37478
|
+
hasReducedMotionHandling: false
|
|
37479
|
+
};
|
|
37480
|
+
for (const sourceFileName of sourceTextByFileName.keys()) {
|
|
37481
|
+
const sourceFile = program.getSourceFile(sourceFileName);
|
|
37482
|
+
if (!sourceFile) continue;
|
|
37483
|
+
const fileEvidence = collectScriptMotionEvidence(sourceFile, typeChecker, program);
|
|
37484
|
+
evidence.hasMotionUse ||= fileEvidence.hasMotionUse;
|
|
37485
|
+
evidence.hasReducedMotionHandling ||= fileEvidence.hasReducedMotionHandling;
|
|
37486
|
+
}
|
|
37487
|
+
return evidence;
|
|
37488
|
+
};
|
|
37489
|
+
const removeCssCommentsAndStrings = (content) => {
|
|
37490
|
+
let sanitizedContent = "";
|
|
37491
|
+
let quote = null;
|
|
37492
|
+
let isInsideComment = false;
|
|
37493
|
+
let isInsideLineComment = false;
|
|
37494
|
+
for (let characterIndex = 0; characterIndex < content.length; characterIndex += 1) {
|
|
37495
|
+
const character = content[characterIndex];
|
|
37496
|
+
const nextCharacter = content[characterIndex + 1];
|
|
37497
|
+
if (isInsideLineComment) {
|
|
37498
|
+
sanitizedContent += character === "\n" ? "\n" : " ";
|
|
37499
|
+
if (character === "\n") isInsideLineComment = false;
|
|
37500
|
+
continue;
|
|
37501
|
+
}
|
|
37502
|
+
if (isInsideComment) {
|
|
37503
|
+
if (character === "*" && nextCharacter === "/") {
|
|
37504
|
+
sanitizedContent += " ";
|
|
37505
|
+
characterIndex += 1;
|
|
37506
|
+
isInsideComment = false;
|
|
37507
|
+
} else sanitizedContent += character === "\n" ? "\n" : " ";
|
|
37508
|
+
continue;
|
|
37509
|
+
}
|
|
37510
|
+
if (quote) {
|
|
37511
|
+
if (character === "\\") {
|
|
37512
|
+
sanitizedContent += " ";
|
|
37513
|
+
characterIndex += 1;
|
|
37514
|
+
continue;
|
|
37515
|
+
}
|
|
37516
|
+
if (character === quote) quote = null;
|
|
37517
|
+
sanitizedContent += character === "\n" ? "\n" : " ";
|
|
37518
|
+
continue;
|
|
37519
|
+
}
|
|
37520
|
+
if (character === "/" && nextCharacter === "*") {
|
|
37521
|
+
sanitizedContent += " ";
|
|
37522
|
+
characterIndex += 1;
|
|
37523
|
+
isInsideComment = true;
|
|
37524
|
+
continue;
|
|
37525
|
+
}
|
|
37526
|
+
if (character === "/" && nextCharacter === "/") {
|
|
37527
|
+
sanitizedContent += " ";
|
|
37528
|
+
characterIndex += 1;
|
|
37529
|
+
isInsideLineComment = true;
|
|
37530
|
+
continue;
|
|
37531
|
+
}
|
|
37532
|
+
if (character === "\"" || character === "'") {
|
|
37533
|
+
sanitizedContent += " ";
|
|
37534
|
+
quote = character;
|
|
37535
|
+
continue;
|
|
37536
|
+
}
|
|
37537
|
+
sanitizedContent += character;
|
|
37538
|
+
}
|
|
37539
|
+
return sanitizedContent;
|
|
37540
|
+
};
|
|
37541
|
+
const findMatchingClosingBrace = (content, openingBraceIndex) => {
|
|
37542
|
+
let depth = 0;
|
|
37543
|
+
for (let characterIndex = openingBraceIndex; characterIndex < content.length; characterIndex += 1) {
|
|
37544
|
+
if (content[characterIndex] === "{") depth += 1;
|
|
37545
|
+
if (content[characterIndex] !== "}") continue;
|
|
37546
|
+
depth -= 1;
|
|
37547
|
+
if (depth === 0) return characterIndex;
|
|
37548
|
+
}
|
|
37549
|
+
return -1;
|
|
37550
|
+
};
|
|
37551
|
+
const hasReducedMotionMediaQuery = (content) => {
|
|
37552
|
+
const sanitizedContent = removeCssCommentsAndStrings(content);
|
|
37553
|
+
REDUCED_MOTION_MEDIA_QUERY_PATTERN.lastIndex = 0;
|
|
37554
|
+
for (const match of sanitizedContent.matchAll(REDUCED_MOTION_MEDIA_QUERY_PATTERN)) {
|
|
37555
|
+
const mediaPrelude = match[1];
|
|
37556
|
+
if (/\bnot\b/i.test(mediaPrelude)) continue;
|
|
37557
|
+
const openingBraceIndex = (match.index ?? 0) + match[0].lastIndexOf("{");
|
|
37558
|
+
const closingBraceIndex = findMatchingClosingBrace(sanitizedContent, openingBraceIndex);
|
|
37559
|
+
if (closingBraceIndex === -1) continue;
|
|
37560
|
+
const body = sanitizedContent.slice(openingBraceIndex + 1, closingBraceIndex);
|
|
37561
|
+
if (CSS_DECLARATION_PATTERN.test(body)) return true;
|
|
37562
|
+
}
|
|
37563
|
+
return false;
|
|
37564
|
+
};
|
|
37565
|
+
const collectProjectMotionEvidence = (rootDirectory) => {
|
|
37566
|
+
const scriptSources = [];
|
|
37567
|
+
let hasReducedMotionHandling = false;
|
|
37145
37568
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37146
|
-
|
|
37569
|
+
const extension = Path.extname(name);
|
|
37570
|
+
if (!SCRIPT_FILE_EXTENSIONS.has(extension) && !STYLE_FILE_EXTENSIONS.has(extension)) continue;
|
|
37147
37571
|
let content;
|
|
37148
37572
|
try {
|
|
37149
37573
|
content = NFS.readFileSync(absolutePath, "utf-8");
|
|
37150
37574
|
} catch {
|
|
37151
37575
|
continue;
|
|
37152
37576
|
}
|
|
37153
|
-
if (
|
|
37577
|
+
if (STYLE_FILE_EXTENSIONS.has(extension)) {
|
|
37578
|
+
if (hasReducedMotionMediaQuery(content)) hasReducedMotionHandling = true;
|
|
37579
|
+
continue;
|
|
37580
|
+
}
|
|
37581
|
+
scriptSources.push({
|
|
37582
|
+
fileName: absolutePath,
|
|
37583
|
+
sourceText: content
|
|
37584
|
+
});
|
|
37154
37585
|
}
|
|
37155
|
-
return
|
|
37586
|
+
if (scriptSources.length === 0) return {
|
|
37587
|
+
hasMotionUse: false,
|
|
37588
|
+
hasReducedMotionHandling
|
|
37589
|
+
};
|
|
37590
|
+
const scriptEvidence = analyzeReducedMotionSources(scriptSources);
|
|
37591
|
+
const hasMotionUse = scriptEvidence.hasMotionUse;
|
|
37592
|
+
hasReducedMotionHandling ||= scriptEvidence.hasReducedMotionHandling;
|
|
37593
|
+
return {
|
|
37594
|
+
hasMotionUse,
|
|
37595
|
+
hasReducedMotionHandling
|
|
37596
|
+
};
|
|
37156
37597
|
};
|
|
37157
37598
|
const checkReducedMotion = (rootDirectory) => {
|
|
37158
37599
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
37159
37600
|
if (!isFile(packageJsonPath)) return [];
|
|
37160
|
-
let hasMotionLibrary = false;
|
|
37161
37601
|
try {
|
|
37162
37602
|
const packageJson = readPackageJson(packageJsonPath);
|
|
37163
37603
|
const allDependencies = {
|
|
37164
37604
|
...packageJson.dependencies,
|
|
37165
37605
|
...packageJson.devDependencies
|
|
37166
37606
|
};
|
|
37167
|
-
|
|
37607
|
+
if (!Object.keys(allDependencies).some((packageName) => MOTION_LIBRARY_PACKAGES.has(packageName))) return [];
|
|
37168
37608
|
} catch {
|
|
37169
37609
|
return [];
|
|
37170
37610
|
}
|
|
37171
|
-
|
|
37172
|
-
|
|
37173
|
-
"grep",
|
|
37174
|
-
"--untracked",
|
|
37175
|
-
"-lE",
|
|
37176
|
-
REDUCED_MOTION_GREP_PATTERN,
|
|
37177
|
-
"--",
|
|
37178
|
-
...REDUCED_MOTION_FILE_GLOBS
|
|
37179
|
-
], {
|
|
37180
|
-
cwd: rootDirectory,
|
|
37181
|
-
encoding: "utf-8",
|
|
37182
|
-
stdio: [
|
|
37183
|
-
"ignore",
|
|
37184
|
-
"pipe",
|
|
37185
|
-
"pipe"
|
|
37186
|
-
]
|
|
37187
|
-
});
|
|
37188
|
-
if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
37189
|
-
return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
37611
|
+
const evidence = collectProjectMotionEvidence(rootDirectory);
|
|
37612
|
+
return evidence.hasMotionUse && !evidence.hasReducedMotionHandling ? [MISSING_REDUCED_MOTION_DIAGNOSTIC] : [];
|
|
37190
37613
|
};
|
|
37191
37614
|
const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
|
|
37192
37615
|
filePath: relativePath,
|
|
@@ -37494,9 +37917,7 @@ const highlighter = {
|
|
|
37494
37917
|
gray: import_picocolors.default.gray,
|
|
37495
37918
|
bold: import_picocolors.default.bold
|
|
37496
37919
|
};
|
|
37497
|
-
const
|
|
37498
|
-
const shouldIncludeExplicitLintPath = (filePath, project) => JSX_FILE_PATTERN.test(filePath) || project?.framework === "nextjs" && NEXT_ENTRY_FILE_PATTERN.test(filePath);
|
|
37499
|
-
const computeExplicitLintIncludePaths = (includePaths, project) => includePaths.length > 0 ? includePaths.filter((filePath) => shouldIncludeExplicitLintPath(filePath, project)) : void 0;
|
|
37920
|
+
const computeExplicitLintIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter(isLintableSourceFile) : void 0;
|
|
37500
37921
|
const LINGUIST_ATTRIBUTE_PATTERN = /^linguist-(?:vendored|generated)(?:=([a-zA-Z0-9]+))?$/i;
|
|
37501
37922
|
const FALSY_VALUES = new Set([
|
|
37502
37923
|
"false",
|
|
@@ -38338,11 +38759,11 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
38338
38759
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
38339
38760
|
};
|
|
38340
38761
|
};
|
|
38341
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig
|
|
38762
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
38342
38763
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
38343
38764
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
38344
38765
|
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
38345
|
-
if (!
|
|
38766
|
+
if (!isLintableSourceFile(filePath)) return false;
|
|
38346
38767
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
38347
38768
|
});
|
|
38348
38769
|
};
|
|
@@ -38560,6 +38981,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
38560
38981
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
38561
38982
|
};
|
|
38562
38983
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
38984
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
38563
38985
|
/**
|
|
38564
38986
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
38565
38987
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -38630,6 +39052,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38630
39052
|
args,
|
|
38631
39053
|
directory
|
|
38632
39054
|
});
|
|
39055
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
39056
|
+
"ls-files",
|
|
39057
|
+
"-z",
|
|
39058
|
+
"--others",
|
|
39059
|
+
"--exclude-standard",
|
|
39060
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
39061
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
39062
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
38633
39063
|
const currentBranch = (directory) => runGit(directory, [
|
|
38634
39064
|
"rev-parse",
|
|
38635
39065
|
"--abbrev-ref",
|
|
@@ -38752,7 +39182,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38752
39182
|
githubViewerPermission,
|
|
38753
39183
|
branchExists,
|
|
38754
39184
|
mergeBase,
|
|
38755
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
39185
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
38756
39186
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
38757
39187
|
if (explicitBaseBranch !== void 0) {
|
|
38758
39188
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -38781,7 +39211,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38781
39211
|
"HEAD"
|
|
38782
39212
|
]);
|
|
38783
39213
|
if (uncommitted.status !== 0) return null;
|
|
38784
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
39214
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
38785
39215
|
if (files.length === 0) return null;
|
|
38786
39216
|
return {
|
|
38787
39217
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -38811,7 +39241,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38811
39241
|
currentBranch: resolvedCurrentBranch,
|
|
38812
39242
|
baseBranch,
|
|
38813
39243
|
diffBaseRef: mergeBaseRef,
|
|
38814
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
39244
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
38815
39245
|
isCurrentChanges: false
|
|
38816
39246
|
};
|
|
38817
39247
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -38857,7 +39287,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38857
39287
|
stdout: result.stdout
|
|
38858
39288
|
};
|
|
38859
39289
|
}).pipe(withSpan("Git.grep")),
|
|
38860
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
39290
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
38861
39291
|
if (files.length === 0) return [];
|
|
38862
39292
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
38863
39293
|
const result = yield* runGit(directory, [
|
|
@@ -38871,7 +39301,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38871
39301
|
...files
|
|
38872
39302
|
]);
|
|
38873
39303
|
if (result.status !== 0) return null;
|
|
38874
|
-
|
|
39304
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
39305
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
39306
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
39307
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
39308
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
39309
|
+
file,
|
|
39310
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
39311
|
+
}))];
|
|
38875
39312
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38876
39313
|
});
|
|
38877
39314
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
@@ -39456,13 +39893,13 @@ const encodeSidecarFileEntry = (entry) => ({
|
|
|
39456
39893
|
diagnostics: entry.diagnostics
|
|
39457
39894
|
});
|
|
39458
39895
|
const emptyCache = () => ({
|
|
39459
|
-
version:
|
|
39896
|
+
version: 2,
|
|
39460
39897
|
rulesets: {}
|
|
39461
39898
|
});
|
|
39462
39899
|
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
39463
39900
|
const entries = /* @__PURE__ */ new Map();
|
|
39464
39901
|
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
39465
|
-
if (persisted.version !==
|
|
39902
|
+
if (persisted.version !== 2 || !isRecord(persisted.rulesets)) return entries;
|
|
39466
39903
|
const bucket = persisted.rulesets[rulesetHash];
|
|
39467
39904
|
if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
|
|
39468
39905
|
for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
|
|
@@ -39482,7 +39919,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
39482
39919
|
},
|
|
39483
39920
|
persist: () => {
|
|
39484
39921
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
39485
|
-
const rulesets = onDisk.version ===
|
|
39922
|
+
const rulesets = onDisk.version === 2 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
39486
39923
|
const existingBucket = rulesets[rulesetHash];
|
|
39487
39924
|
const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
|
|
39488
39925
|
const ourFiles = {};
|
|
@@ -39496,7 +39933,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
39496
39933
|
files: Object.fromEntries(cappedEntries)
|
|
39497
39934
|
};
|
|
39498
39935
|
atomicWriteJson(cacheFilePath, {
|
|
39499
|
-
version:
|
|
39936
|
+
version: 2,
|
|
39500
39937
|
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
39501
39938
|
});
|
|
39502
39939
|
}
|
|
@@ -39547,23 +39984,6 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
39547
39984
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
39548
39985
|
return null;
|
|
39549
39986
|
};
|
|
39550
|
-
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
39551
|
-
["no-initialize-state", 0],
|
|
39552
|
-
["no-adjust-state-on-prop-change", 1],
|
|
39553
|
-
["no-derived-state", 2],
|
|
39554
|
-
["no-derived-state-effect", 3]
|
|
39555
|
-
]);
|
|
39556
|
-
const doDiagnosticSpansOverlap = (first, second) => {
|
|
39557
|
-
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
39558
|
-
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
39559
|
-
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
39560
|
-
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
39561
|
-
return first.offset < secondEnd && second.offset < firstEnd;
|
|
39562
|
-
}
|
|
39563
|
-
const firstEndLine = first.endLine ?? first.line;
|
|
39564
|
-
const secondEndLine = second.endLine ?? second.line;
|
|
39565
|
-
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
39566
|
-
};
|
|
39567
39987
|
const dedupeDiagnostics = (diagnostics) => {
|
|
39568
39988
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
39569
39989
|
const uniqueDiagnostics = [];
|
|
@@ -39571,16 +39991,6 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
39571
39991
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
39572
39992
|
if (seenKeys.has(key)) continue;
|
|
39573
39993
|
seenKeys.add(key);
|
|
39574
|
-
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
39575
|
-
if (priority !== void 0) {
|
|
39576
|
-
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
39577
|
-
if (overlappingDiagnosticIndex >= 0) {
|
|
39578
|
-
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
39579
|
-
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
39580
|
-
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
39581
|
-
continue;
|
|
39582
|
-
}
|
|
39583
|
-
}
|
|
39584
39994
|
uniqueDiagnostics.push(diagnostic);
|
|
39585
39995
|
}
|
|
39586
39996
|
return uniqueDiagnostics;
|
|
@@ -39623,6 +40033,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39623
40033
|
if (errors.length > 0) throw errors[0];
|
|
39624
40034
|
return results;
|
|
39625
40035
|
};
|
|
40036
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40037
|
+
let lineNumber = 1;
|
|
40038
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40039
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
40040
|
+
return lineNumber;
|
|
40041
|
+
};
|
|
39626
40042
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
39627
40043
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39628
40044
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -39796,23 +40212,12 @@ const REACT_USE_BINDING_RESOLUTION = {
|
|
|
39796
40212
|
isReactUseBinding: true,
|
|
39797
40213
|
isReactNamespaceBinding: false
|
|
39798
40214
|
};
|
|
39799
|
-
const getScriptKind$2 = (filename) => {
|
|
39800
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
39801
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
39802
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
39803
|
-
return ts.ScriptKind.JS;
|
|
39804
|
-
};
|
|
39805
40215
|
const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
39806
|
-
const unwrapExpression = (expression) => {
|
|
39807
|
-
let currentExpression = expression;
|
|
39808
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
39809
|
-
return currentExpression;
|
|
39810
|
-
};
|
|
39811
40216
|
const getStaticPropertyName = (node) => {
|
|
39812
40217
|
if (!node) return null;
|
|
39813
40218
|
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
|
|
39814
40219
|
if (ts.isComputedPropertyName(node)) {
|
|
39815
|
-
const expression =
|
|
40220
|
+
const expression = unwrapTypescriptExpression(node.expression);
|
|
39816
40221
|
if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) return expression.text;
|
|
39817
40222
|
}
|
|
39818
40223
|
return null;
|
|
@@ -39841,7 +40246,7 @@ const isReactUseObjectBindingElement = (bindingElement) => {
|
|
|
39841
40246
|
return getStaticPropertyName(bindingElement.propertyName) === USE_IDENTIFIER;
|
|
39842
40247
|
};
|
|
39843
40248
|
const isReactRequireCall = (expression) => {
|
|
39844
|
-
const unwrappedExpression =
|
|
40249
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
39845
40250
|
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;
|
|
39846
40251
|
};
|
|
39847
40252
|
const getModuleSource = (node) => {
|
|
@@ -39943,7 +40348,7 @@ const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
|
|
|
39943
40348
|
return false;
|
|
39944
40349
|
};
|
|
39945
40350
|
const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
|
|
39946
|
-
const unwrappedExpression =
|
|
40351
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
39947
40352
|
if (isReactRequireCall(unwrappedExpression)) return true;
|
|
39948
40353
|
if (!ts.isIdentifier(unwrappedExpression)) return false;
|
|
39949
40354
|
if (reactImportBindings.namespaceNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
|
|
@@ -39951,7 +40356,7 @@ const isReactNamespaceExpression = (expression, reactImportBindings, sourceFile,
|
|
|
39951
40356
|
};
|
|
39952
40357
|
const isReactUseExpression = (expression, reactImportBindings, sourceFile, visitedDeclarations) => {
|
|
39953
40358
|
if (!expression) return false;
|
|
39954
|
-
const unwrappedExpression =
|
|
40359
|
+
const unwrappedExpression = unwrapTypescriptExpression(expression);
|
|
39955
40360
|
if (ts.isIdentifier(unwrappedExpression)) {
|
|
39956
40361
|
if (reactImportBindings.useImportNames.has(unwrappedExpression.text) && !isIdentifierShadowedByLocalBinding(unwrappedExpression, sourceFile)) return true;
|
|
39957
40362
|
if (unwrappedExpression.text === USE_IDENTIFIER) return false;
|
|
@@ -40051,7 +40456,7 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
|
|
|
40051
40456
|
return matchedIdentifier;
|
|
40052
40457
|
};
|
|
40053
40458
|
const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
|
|
40054
|
-
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true,
|
|
40459
|
+
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filename));
|
|
40055
40460
|
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
|
|
40056
40461
|
if (!useIdentifier) return null;
|
|
40057
40462
|
return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
|
|
@@ -40087,12 +40492,6 @@ const WORKLET_ACCEPTING_CALLEES = new Set([
|
|
|
40087
40492
|
"executeOnUIRuntimeSync"
|
|
40088
40493
|
]);
|
|
40089
40494
|
const GESTURE_ROOT_IDENTIFIER = "Gesture";
|
|
40090
|
-
const getScriptKind$1 = (filename) => {
|
|
40091
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
40092
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
40093
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
40094
|
-
return ts.ScriptKind.JS;
|
|
40095
|
-
};
|
|
40096
40495
|
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
40097
40496
|
const hasWorkletDirective = (node) => {
|
|
40098
40497
|
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
|
|
@@ -40164,17 +40563,11 @@ const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirecto
|
|
|
40164
40563
|
} catch {
|
|
40165
40564
|
return false;
|
|
40166
40565
|
}
|
|
40167
|
-
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true,
|
|
40566
|
+
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
|
|
40168
40567
|
};
|
|
40169
40568
|
const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
|
|
40170
40569
|
const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
|
|
40171
40570
|
const REACT_COMPILER_PLUGIN = "react-hooks-js";
|
|
40172
|
-
const getScriptKind = (filename) => {
|
|
40173
|
-
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
40174
|
-
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
40175
|
-
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
40176
|
-
return ts.ScriptKind.JS;
|
|
40177
|
-
};
|
|
40178
40571
|
const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
|
|
40179
40572
|
const nodeLineRange = (sourceFile, node) => ({
|
|
40180
40573
|
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
|
|
@@ -40194,13 +40587,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
40194
40587
|
visit(sourceFile);
|
|
40195
40588
|
return foundRange;
|
|
40196
40589
|
};
|
|
40197
|
-
const NEWLINE_BYTE = 10;
|
|
40198
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40199
|
-
let lineNumber = 1;
|
|
40200
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40201
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40202
|
-
return lineNumber;
|
|
40203
|
-
};
|
|
40204
40590
|
/**
|
|
40205
40591
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40206
40592
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -40243,7 +40629,7 @@ const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) =>
|
|
|
40243
40629
|
try {
|
|
40244
40630
|
const buffer = NFS.readFileSync(absolutePath);
|
|
40245
40631
|
parsed = {
|
|
40246
|
-
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true,
|
|
40632
|
+
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getTypescriptScriptKind(absolutePath)),
|
|
40247
40633
|
buffer
|
|
40248
40634
|
};
|
|
40249
40635
|
} catch {
|
|
@@ -40381,11 +40767,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
40381
40767
|
}
|
|
40382
40768
|
return related;
|
|
40383
40769
|
};
|
|
40384
|
-
const
|
|
40385
|
-
|
|
40386
|
-
|
|
40387
|
-
|
|
40388
|
-
|
|
40770
|
+
const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
40771
|
+
const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
|
|
40772
|
+
const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
40773
|
+
const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
40774
|
+
const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
|
|
40389
40775
|
/**
|
|
40390
40776
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
40391
40777
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -40404,23 +40790,59 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40404
40790
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40405
40791
|
}
|
|
40406
40792
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40793
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
40794
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
40407
40795
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40796
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40797
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
40798
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
40799
|
+
const readSourceBuffer = (filename) => {
|
|
40800
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40801
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
40802
|
+
if (cached !== void 0) return cached;
|
|
40803
|
+
let sourceBuffer;
|
|
40804
|
+
try {
|
|
40805
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
40806
|
+
} catch {
|
|
40807
|
+
sourceBuffer = null;
|
|
40808
|
+
}
|
|
40809
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
40810
|
+
return sourceBuffer;
|
|
40811
|
+
};
|
|
40812
|
+
const readSourceLines = (filename) => {
|
|
40813
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40814
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
40815
|
+
if (cached !== void 0) return cached;
|
|
40816
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
40817
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
40818
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
40819
|
+
return sourceLines;
|
|
40820
|
+
};
|
|
40408
40821
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
40409
|
-
const absolutePath =
|
|
40822
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40410
40823
|
const cached = minifiedFileCache.get(absolutePath);
|
|
40411
40824
|
if (cached !== void 0) return cached;
|
|
40412
40825
|
const minified = isMinifiedSource(absolutePath);
|
|
40413
40826
|
minifiedFileCache.set(absolutePath, minified);
|
|
40414
40827
|
return minified;
|
|
40415
40828
|
};
|
|
40416
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
40829
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
40417
40830
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
40418
40831
|
const primaryLabel = diagnostic.labels[0];
|
|
40419
40832
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
40420
40833
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
40421
40834
|
const primarySpan = primaryLabel?.span;
|
|
40835
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
40422
40836
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
40423
40837
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
40838
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
40839
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
40840
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
40841
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
40842
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
40843
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
40844
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
40845
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
40424
40846
|
return {
|
|
40425
40847
|
filePath: normalizedFilePath,
|
|
40426
40848
|
plugin,
|
|
@@ -40434,10 +40856,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40434
40856
|
column: primarySpan?.column ?? 0,
|
|
40435
40857
|
...primarySpan ? {
|
|
40436
40858
|
offset: primarySpan.offset,
|
|
40437
|
-
length: primarySpan.length
|
|
40859
|
+
length: primarySpan.length,
|
|
40860
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
40438
40861
|
} : {},
|
|
40439
40862
|
category,
|
|
40440
|
-
...
|
|
40863
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
40441
40864
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
40442
40865
|
};
|
|
40443
40866
|
}), rootDirectory);
|
|
@@ -42224,7 +42647,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42224
42647
|
directory: scanDirectory,
|
|
42225
42648
|
repo
|
|
42226
42649
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
42227
|
-
const lintIncludePaths = (input.
|
|
42650
|
+
const lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config);
|
|
42228
42651
|
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
42229
42652
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
42230
42653
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
@@ -42238,7 +42661,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42238
42661
|
respectInlineDisables: input.respectInlineDisables,
|
|
42239
42662
|
showWarnings
|
|
42240
42663
|
});
|
|
42241
|
-
const
|
|
42664
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
|
|
42665
|
+
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
42242
42666
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
42243
42667
|
...checkReducedMotion(scanDirectory),
|
|
42244
42668
|
...checkPnpmHardening(scanDirectory),
|
|
@@ -42367,7 +42791,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42367
42791
|
});
|
|
42368
42792
|
return empty$4;
|
|
42369
42793
|
}))));
|
|
42370
|
-
const lintCollected = yield* runCollect(
|
|
42794
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
42371
42795
|
onNone: () => set(lintFailure, {
|
|
42372
42796
|
didFail: true,
|
|
42373
42797
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -42375,7 +42799,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42375
42799
|
reasonKind: null
|
|
42376
42800
|
}).pipe(as([])),
|
|
42377
42801
|
onSome: succeed$2
|
|
42378
|
-
})));
|
|
42802
|
+
}))));
|
|
42803
|
+
yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
|
|
42379
42804
|
const lintFailureState = yield* get$2(lintFailure);
|
|
42380
42805
|
yield* afterLint(lintFailureState.didFail);
|
|
42381
42806
|
if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
|
|
@@ -42719,7 +43144,7 @@ const runEditorScan = async (input) => {
|
|
|
42719
43144
|
warnings,
|
|
42720
43145
|
isCi: false,
|
|
42721
43146
|
resolveLocalGithubViewerPermission: false,
|
|
42722
|
-
|
|
43147
|
+
skipExplicitIncludePathFilter: true
|
|
42723
43148
|
}).pipe(withSpan("runEditorScan", { attributes: {
|
|
42724
43149
|
"editor.lint": lint,
|
|
42725
43150
|
"editor.runDeadCode": runDeadCode
|
|
@@ -42803,7 +43228,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
42803
43228
|
/** Display name used in client-facing messages and progress titles. */
|
|
42804
43229
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
42805
43230
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
42806
|
-
const SERVER_VERSION = "0.7.
|
|
43231
|
+
const SERVER_VERSION = "0.7.7";
|
|
42807
43232
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
42808
43233
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
42809
43234
|
/**
|
|
@@ -43482,7 +43907,7 @@ const createLintCache = (input) => {
|
|
|
43482
43907
|
let persistTimer = null;
|
|
43483
43908
|
try {
|
|
43484
43909
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
43485
|
-
if (parsed.version ===
|
|
43910
|
+
if (parsed.version === 2 && parsed.fingerprint === input.fingerprint) for (const [fsPath, entry] of Object.entries(parsed.entries)) entries.set(fsPath, entry);
|
|
43486
43911
|
} catch {}
|
|
43487
43912
|
const persist = () => {
|
|
43488
43913
|
if (persistTimer) {
|
|
@@ -43493,7 +43918,7 @@ const createLintCache = (input) => {
|
|
|
43493
43918
|
dirty = false;
|
|
43494
43919
|
try {
|
|
43495
43920
|
const payload = {
|
|
43496
|
-
version:
|
|
43921
|
+
version: 2,
|
|
43497
43922
|
fingerprint: input.fingerprint,
|
|
43498
43923
|
entries: Object.fromEntries(entries)
|
|
43499
43924
|
};
|
|
@@ -43506,14 +43931,14 @@ const createLintCache = (input) => {
|
|
|
43506
43931
|
}
|
|
43507
43932
|
};
|
|
43508
43933
|
return {
|
|
43509
|
-
lookup: (fsPath,
|
|
43934
|
+
lookup: (fsPath, identity) => {
|
|
43510
43935
|
const entry = entries.get(fsPath);
|
|
43511
|
-
if (entry !== void 0 && entry.
|
|
43936
|
+
if (entry !== void 0 && entry.contentHash === identity.contentHash) return entry.diagnostics;
|
|
43512
43937
|
return null;
|
|
43513
43938
|
},
|
|
43514
|
-
store: (fsPath,
|
|
43939
|
+
store: (fsPath, identity, diagnostics) => {
|
|
43515
43940
|
entries.set(fsPath, {
|
|
43516
|
-
...
|
|
43941
|
+
...identity,
|
|
43517
43942
|
diagnostics
|
|
43518
43943
|
});
|
|
43519
43944
|
dirty = true;
|
|
@@ -43602,16 +44027,9 @@ const resolveDiagnosticFsPath = (rawFilePath, scanDirectory, projectDirectory, o
|
|
|
43602
44027
|
}
|
|
43603
44028
|
return normalizeFsPath(absolute);
|
|
43604
44029
|
};
|
|
43605
|
-
const
|
|
43606
|
-
|
|
43607
|
-
|
|
43608
|
-
return {
|
|
43609
|
-
mtimeMs: stat.mtimeMs,
|
|
43610
|
-
size: stat.size
|
|
43611
|
-
};
|
|
43612
|
-
} catch {
|
|
43613
|
-
return null;
|
|
43614
|
-
}
|
|
44030
|
+
const readFileIdentity = (fsPath) => {
|
|
44031
|
+
const contentHash = hashFileContents(fsPath);
|
|
44032
|
+
return contentHash === null ? null : { contentHash };
|
|
43615
44033
|
};
|
|
43616
44034
|
/**
|
|
43617
44035
|
* Outcome for a per-file request served without running oxlint (every
|
|
@@ -43637,7 +44055,7 @@ const outcomeWithoutScan = (request, byFile, requestedPaths) => ({
|
|
|
43637
44055
|
* overlay tree (unsaved buffers) or disk, groups diagnostics by canonical
|
|
43638
44056
|
* absolute path, and reports stale-detection metadata.
|
|
43639
44057
|
*
|
|
43640
|
-
* A persistent per-file lint cache (keyed by
|
|
44058
|
+
* A persistent per-file lint cache (keyed by content, namespaced by a
|
|
43641
44059
|
* config fingerprint) short-circuits unchanged files so a re-opened editor
|
|
43642
44060
|
* or repeated workspace scan skips the oxlint subprocess for everything it
|
|
43643
44061
|
* hasn't edited. The cache applies only to disk-based, lint-only, per-file
|
|
@@ -43667,15 +44085,15 @@ const createScanRunner = (options) => {
|
|
|
43667
44085
|
const requestedPaths = request.priority === "background" && !request.useOverlay && options.isOpen ? allRequested.filter((fsPath) => !options.isOpen?.(fsPath)) : allRequested;
|
|
43668
44086
|
const cache = cacheEnabled && !isWholeProject && !request.useOverlay && !request.runDeadCode ? getCache(projectDirectory) : null;
|
|
43669
44087
|
const byFile = /* @__PURE__ */ new Map();
|
|
43670
|
-
const
|
|
44088
|
+
const identityByPath = /* @__PURE__ */ new Map();
|
|
43671
44089
|
let filesToScan = requestedPaths;
|
|
43672
44090
|
if (cache) {
|
|
43673
44091
|
const uncached = [];
|
|
43674
44092
|
for (const fsPath of requestedPaths) {
|
|
43675
|
-
const
|
|
43676
|
-
if (
|
|
43677
|
-
|
|
43678
|
-
const hit = cache.lookup(fsPath,
|
|
44093
|
+
const identity = readFileIdentity(fsPath);
|
|
44094
|
+
if (identity) {
|
|
44095
|
+
identityByPath.set(fsPath, identity);
|
|
44096
|
+
const hit = cache.lookup(fsPath, identity);
|
|
43679
44097
|
if (hit !== null) {
|
|
43680
44098
|
if (hit.length > 0) byFile.set(fsPath, hit);
|
|
43681
44099
|
continue;
|
|
@@ -43718,9 +44136,9 @@ const createScanRunner = (options) => {
|
|
|
43718
44136
|
}
|
|
43719
44137
|
if (cache && result.ok && !result.skipped && !result.didLintFail && result.lintPartialFailures.length === 0) {
|
|
43720
44138
|
for (const fsPath of filesToScan) {
|
|
43721
|
-
const
|
|
43722
|
-
if (!
|
|
43723
|
-
cache.store(fsPath,
|
|
44139
|
+
const identity = identityByPath.get(fsPath);
|
|
44140
|
+
if (!identity) continue;
|
|
44141
|
+
cache.store(fsPath, identity, byFile.get(fsPath) ?? []);
|
|
43724
44142
|
}
|
|
43725
44143
|
cache.schedulePersist();
|
|
43726
44144
|
}
|
|
@@ -44500,6 +44918,7 @@ const METRIC = {
|
|
|
44500
44918
|
cliInvoked: "cli.invoked",
|
|
44501
44919
|
cliError: "cli.error",
|
|
44502
44920
|
cliEnvironmentError: "cli.env_error",
|
|
44921
|
+
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
44503
44922
|
projectDetected: "project.detected",
|
|
44504
44923
|
projectPathSelected: "project.path_selected",
|
|
44505
44924
|
projectConfigSelected: "project.config_selected",
|
|
@@ -44854,5 +45273,5 @@ const startLanguageServer = () => {
|
|
|
44854
45273
|
};
|
|
44855
45274
|
//#endregion
|
|
44856
45275
|
export { startLanguageServer };
|
|
44857
|
-
!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]="
|
|
44858
|
-
//# debugId=
|
|
45276
|
+
!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]="9428cea1-cc36-547a-941f-20487b0140fc")}catch(e){}}();
|
|
45277
|
+
//# debugId=9428cea1-cc36-547a-941f-20487b0140fc
|