react-doctor 0.9.4-dev.8b97fdc → 0.9.4-dev.bafef41
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 +319 -142
- package/dist/index.d.ts +1 -0
- package/dist/index.js +168 -35
- package/dist/lsp.js +208 -30
- package/dist/{run-scan-app-QbtG43NL.js → run-scan-app-C8iWVgvR.js} +10 -10
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e74e6a68-20e1-59fe-8db0-62b87e8fe049")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -65501,17 +65501,20 @@ const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
|
65501
65501
|
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
65502
65502
|
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
65503
65503
|
"EACCES",
|
|
65504
|
+
"EBUSY",
|
|
65504
65505
|
"EPERM",
|
|
65505
65506
|
"ENOENT",
|
|
65506
65507
|
"ENOTDIR",
|
|
65507
65508
|
"EINVAL",
|
|
65508
65509
|
"ELOOP",
|
|
65509
|
-
"ENAMETOOLONG"
|
|
65510
|
+
"ENAMETOOLONG",
|
|
65511
|
+
"ETIMEDOUT",
|
|
65512
|
+
"UNKNOWN"
|
|
65510
65513
|
]);
|
|
65511
65514
|
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
65512
65515
|
const readDirectoryEntries = (directoryPath) => {
|
|
65513
65516
|
try {
|
|
65514
|
-
return
|
|
65517
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
65515
65518
|
} catch (error) {
|
|
65516
65519
|
if (isIgnorableReaddirError(error)) return [];
|
|
65517
65520
|
throw error;
|
|
@@ -65519,14 +65522,14 @@ const readDirectoryEntries = (directoryPath) => {
|
|
|
65519
65522
|
};
|
|
65520
65523
|
const isFile = (filePath) => {
|
|
65521
65524
|
try {
|
|
65522
|
-
return
|
|
65525
|
+
return fs.statSync(filePath).isFile();
|
|
65523
65526
|
} catch {
|
|
65524
65527
|
return false;
|
|
65525
65528
|
}
|
|
65526
65529
|
};
|
|
65527
65530
|
const isDirectory = (directoryPath) => {
|
|
65528
65531
|
try {
|
|
65529
|
-
return
|
|
65532
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
65530
65533
|
} catch {
|
|
65531
65534
|
return false;
|
|
65532
65535
|
}
|
|
@@ -65950,7 +65953,12 @@ const mergeCompilerOptions = (inherited, current) => {
|
|
|
65950
65953
|
};
|
|
65951
65954
|
};
|
|
65952
65955
|
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
65953
|
-
|
|
65956
|
+
let realPath;
|
|
65957
|
+
try {
|
|
65958
|
+
realPath = NFS.realpathSync.native(tsConfigPath);
|
|
65959
|
+
} catch {
|
|
65960
|
+
return null;
|
|
65961
|
+
}
|
|
65954
65962
|
if (visitedPaths.has(realPath)) return null;
|
|
65955
65963
|
const tsConfig = readTsConfig(realPath);
|
|
65956
65964
|
if (!tsConfig) return null;
|
|
@@ -66478,27 +66486,30 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
66478
66486
|
}
|
|
66479
66487
|
return null;
|
|
66480
66488
|
};
|
|
66481
|
-
const configExpressionMayDefineProperty = (expression, propertyName, analysis) => {
|
|
66489
|
+
const configExpressionMayDefineProperty = (expression, propertyName, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
|
|
66482
66490
|
let resolvedExpression = expression;
|
|
66483
66491
|
while (ts.isParenthesizedExpression(resolvedExpression) || ts.isAsExpression(resolvedExpression) || ts.isTypeAssertionExpression(resolvedExpression) || ts.isSatisfiesExpression(resolvedExpression) || ts.isNonNullExpression(resolvedExpression)) resolvedExpression = resolvedExpression.expression;
|
|
66492
|
+
if (visitedExpressions.has(resolvedExpression)) return true;
|
|
66493
|
+
const nextVisitedExpressions = new Set(visitedExpressions);
|
|
66494
|
+
nextVisitedExpressions.add(resolvedExpression);
|
|
66484
66495
|
if (ts.isIdentifier(resolvedExpression)) {
|
|
66485
66496
|
if (analysis.localBindings.has(resolvedExpression.text)) {
|
|
66486
66497
|
const localReference = analysis.localBindings.get(resolvedExpression.text);
|
|
66487
|
-
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis) : true;
|
|
66498
|
+
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis, nextVisitedExpressions) : true;
|
|
66488
66499
|
}
|
|
66489
66500
|
const scopedBinding = getScopedConfigBinding(resolvedExpression);
|
|
66490
|
-
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis) : true;
|
|
66501
|
+
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis, nextVisitedExpressions) : true;
|
|
66491
66502
|
const topLevelBinding = getTopLevelBinding(analysis.sourceFile, resolvedExpression.text);
|
|
66492
|
-
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis) : true;
|
|
66503
|
+
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis, nextVisitedExpressions) : true;
|
|
66493
66504
|
}
|
|
66494
66505
|
if (ts.isConditionalExpression(resolvedExpression)) {
|
|
66495
|
-
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis);
|
|
66496
|
-
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
66497
|
-
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
66506
|
+
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions);
|
|
66507
|
+
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
66508
|
+
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
66498
66509
|
}
|
|
66499
66510
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return true;
|
|
66500
66511
|
return resolvedExpression.properties.some((property) => {
|
|
66501
|
-
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
66512
|
+
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis, nextVisitedExpressions);
|
|
66502
66513
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
66503
66514
|
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
66504
66515
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
@@ -66574,7 +66585,13 @@ const analyzeConfigModuleExport = (filePath, exportName, allowCompilerTransform,
|
|
|
66574
66585
|
if (visitedModules.has(moduleVisitKey)) return false;
|
|
66575
66586
|
const nextVisitedModules = new Set(visitedModules);
|
|
66576
66587
|
nextVisitedModules.add(moduleVisitKey);
|
|
66577
|
-
|
|
66588
|
+
let sourceText;
|
|
66589
|
+
try {
|
|
66590
|
+
sourceText = NFS.readFileSync(filePath, "utf-8");
|
|
66591
|
+
} catch {
|
|
66592
|
+
return false;
|
|
66593
|
+
}
|
|
66594
|
+
return analyzeConfigSourceFileExport(parseConfigSourceFile(filePath, sourceText), filePath, exportName, allowCompilerTransform, importDepth, nextVisitedModules, argumentsList, argumentAnalysis);
|
|
66578
66595
|
};
|
|
66579
66596
|
const analyzeConfigSourceFileExport = (sourceFile, filePath, exportName, allowCompilerTransform, importDepth, visitedModules, argumentsList = [], argumentAnalysis) => {
|
|
66580
66597
|
const analysis = {
|
|
@@ -70077,6 +70094,43 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
70077
70094
|
return false;
|
|
70078
70095
|
});
|
|
70079
70096
|
};
|
|
70097
|
+
const validateIgnoreOverride = (rawOverride, index) => {
|
|
70098
|
+
const fieldName = `ignore.overrides[${index}]`;
|
|
70099
|
+
if (!isRecord$1(rawOverride)) {
|
|
70100
|
+
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawOverride}); ignoring this override.`);
|
|
70101
|
+
return;
|
|
70102
|
+
}
|
|
70103
|
+
const files = validateStringArrayField(`${fieldName}.files`, rawOverride.files);
|
|
70104
|
+
if (files === void 0) return void 0;
|
|
70105
|
+
const validatedOverride = { files };
|
|
70106
|
+
if (rawOverride.rules !== void 0) {
|
|
70107
|
+
const rules = validateStringArrayField(`${fieldName}.rules`, rawOverride.rules);
|
|
70108
|
+
if (rules !== void 0) validatedOverride.rules = rules;
|
|
70109
|
+
}
|
|
70110
|
+
return validatedOverride;
|
|
70111
|
+
};
|
|
70112
|
+
const validateIgnoreField = (rawIgnore) => {
|
|
70113
|
+
if (!isRecord$1(rawIgnore)) {
|
|
70114
|
+
warnConfigIssue(`config field "ignore" must be an object (got ${typeof rawIgnore}); ignoring this field.`);
|
|
70115
|
+
return;
|
|
70116
|
+
}
|
|
70117
|
+
const validatedIgnore = {};
|
|
70118
|
+
for (const fieldName of [
|
|
70119
|
+
"rules",
|
|
70120
|
+
"files",
|
|
70121
|
+
"tags"
|
|
70122
|
+
]) {
|
|
70123
|
+
if (rawIgnore[fieldName] === void 0) continue;
|
|
70124
|
+
const validatedValues = validateStringArrayField(`ignore.${fieldName}`, rawIgnore[fieldName]);
|
|
70125
|
+
if (validatedValues !== void 0) validatedIgnore[fieldName] = validatedValues;
|
|
70126
|
+
}
|
|
70127
|
+
if (rawIgnore.overrides !== void 0) if (!Array.isArray(rawIgnore.overrides)) warnConfigIssue(`config field "ignore.overrides" must be an array (got ${typeof rawIgnore.overrides}); ignoring this field.`);
|
|
70128
|
+
else validatedIgnore.overrides = rawIgnore.overrides.flatMap((rawOverride, index) => {
|
|
70129
|
+
const validatedOverride = validateIgnoreOverride(rawOverride, index);
|
|
70130
|
+
return validatedOverride ? [validatedOverride] : [];
|
|
70131
|
+
});
|
|
70132
|
+
return validatedIgnore;
|
|
70133
|
+
};
|
|
70080
70134
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
70081
70135
|
if (!isRecord$1(rawControls)) {
|
|
70082
70136
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
@@ -70152,6 +70206,7 @@ const validateConfigTypes = (config) => {
|
|
|
70152
70206
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
70153
70207
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
70154
70208
|
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
70209
|
+
applyFieldValidator(config, validated, "ignore", validateIgnoreField);
|
|
70155
70210
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
70156
70211
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
70157
70212
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -71844,7 +71899,7 @@ const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
|
|
|
71844
71899
|
if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
|
|
71845
71900
|
}
|
|
71846
71901
|
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
71847
|
-
const tagEvidence = ts.
|
|
71902
|
+
const tagEvidence = ts.isIdentifier(node.tagName) || ts.isPropertyAccessExpression(node.tagName) ? resolveMotionExpressionEvidence(node.tagName, typeChecker, program) : EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
71848
71903
|
if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
|
|
71849
71904
|
if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
|
|
71850
71905
|
}
|
|
@@ -73844,7 +73899,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
73844
73899
|
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
73845
73900
|
isCurrentChanges: false
|
|
73846
73901
|
};
|
|
73847
|
-
}).pipe(withSpan("Git.diffSelection")),
|
|
73902
|
+
}).pipe(catchReasons("ReactDoctorError", { GitInvocationFailed: () => succeed$2(null) }, (_reason, error) => fail$4(error)), withSpan("Git.diffSelection")),
|
|
73848
73903
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
73849
73904
|
"diff",
|
|
73850
73905
|
"--no-ext-diff",
|
|
@@ -74060,6 +74115,33 @@ const detectUserLintConfigPaths = (rootDirectory) => {
|
|
|
74060
74115
|
return [];
|
|
74061
74116
|
};
|
|
74062
74117
|
const DISABLE_DIRECTIVE_PATTERN = /(eslint|oxlint)-disable/;
|
|
74118
|
+
const neutralizedFileLeases = /* @__PURE__ */ new Map();
|
|
74119
|
+
const activeNeutralizationInitializations = /* @__PURE__ */ new Set();
|
|
74120
|
+
const directoriesOverlap = (left, right) => {
|
|
74121
|
+
const isWithin = (parent, candidate) => {
|
|
74122
|
+
const relativePath = Path.relative(parent, candidate);
|
|
74123
|
+
return relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath);
|
|
74124
|
+
};
|
|
74125
|
+
return isWithin(left, right) || isWithin(right, left);
|
|
74126
|
+
};
|
|
74127
|
+
const withOverlappingInitializationLock = async (rootDirectory, operation) => {
|
|
74128
|
+
const blockers = [...activeNeutralizationInitializations].filter((initialization) => directoriesOverlap(initialization.rootDirectory, rootDirectory)).map((initialization) => initialization.done);
|
|
74129
|
+
let release;
|
|
74130
|
+
const initialization = {
|
|
74131
|
+
rootDirectory,
|
|
74132
|
+
done: new Promise((resolve) => {
|
|
74133
|
+
release = resolve;
|
|
74134
|
+
})
|
|
74135
|
+
};
|
|
74136
|
+
activeNeutralizationInitializations.add(initialization);
|
|
74137
|
+
await Promise.all(blockers);
|
|
74138
|
+
try {
|
|
74139
|
+
return await operation();
|
|
74140
|
+
} finally {
|
|
74141
|
+
activeNeutralizationInitializations.delete(initialization);
|
|
74142
|
+
release();
|
|
74143
|
+
}
|
|
74144
|
+
};
|
|
74063
74145
|
const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths) => {
|
|
74064
74146
|
const program = gen(function* () {
|
|
74065
74147
|
return yield* (yield* Git).grep({
|
|
@@ -74102,34 +74184,84 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
74102
74184
|
};
|
|
74103
74185
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
74104
74186
|
const neutralizeContent = (content) => content.replaceAll("eslint-disable", "eslint_disable").replaceAll("oxlint-disable", "oxlint_disable");
|
|
74105
|
-
const
|
|
74106
|
-
const
|
|
74107
|
-
const
|
|
74187
|
+
const collectNeutralizationCandidatePaths = (rootDirectory, discoveredRelativePaths, includePaths) => {
|
|
74188
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
74189
|
+
const requestedPaths = includePaths && includePaths.length > 0 ? new Set(includePaths.flatMap((includePath) => {
|
|
74190
|
+
try {
|
|
74191
|
+
return [NFS.realpathSync(Path.resolve(resolvedRootDirectory, includePath))];
|
|
74192
|
+
} catch {
|
|
74193
|
+
return [];
|
|
74194
|
+
}
|
|
74195
|
+
})) : null;
|
|
74196
|
+
const candidatePaths = /* @__PURE__ */ new Set();
|
|
74197
|
+
for (const relativePath of discoveredRelativePaths) try {
|
|
74198
|
+
candidatePaths.add(NFS.realpathSync(Path.resolve(resolvedRootDirectory, relativePath)));
|
|
74199
|
+
} catch {
|
|
74200
|
+
continue;
|
|
74201
|
+
}
|
|
74202
|
+
for (const leasedPath of neutralizedFileLeases.keys()) {
|
|
74203
|
+
const relativePath = Path.relative(resolvedRootDirectory, leasedPath);
|
|
74204
|
+
if (!(relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath)) || requestedPaths && !requestedPaths.has(leasedPath)) continue;
|
|
74205
|
+
candidatePaths.add(leasedPath);
|
|
74206
|
+
}
|
|
74207
|
+
return candidatePaths;
|
|
74208
|
+
};
|
|
74209
|
+
const neutralizeDisableDirectives = async (rootDirectory, includePaths, options = {}) => {
|
|
74210
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
74211
|
+
const leasedPaths = /* @__PURE__ */ new Set();
|
|
74108
74212
|
let isRestored = false;
|
|
74109
74213
|
const restore = () => {
|
|
74110
74214
|
if (isRestored) return;
|
|
74111
74215
|
isRestored = true;
|
|
74112
|
-
for (const
|
|
74113
|
-
|
|
74114
|
-
|
|
74115
|
-
|
|
74216
|
+
for (const absolutePath of leasedPaths) {
|
|
74217
|
+
const lease = neutralizedFileLeases.get(absolutePath);
|
|
74218
|
+
if (!lease) continue;
|
|
74219
|
+
lease.referenceCount -= 1;
|
|
74220
|
+
if (lease.referenceCount > 0) continue;
|
|
74221
|
+
neutralizedFileLeases.delete(absolutePath);
|
|
74222
|
+
try {
|
|
74223
|
+
NFS.writeFileSync(absolutePath, lease.originalContent);
|
|
74224
|
+
} catch (error) {
|
|
74225
|
+
process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
|
|
74226
|
+
}
|
|
74116
74227
|
}
|
|
74117
74228
|
};
|
|
74118
74229
|
const onExit = () => restore();
|
|
74119
74230
|
process.once("exit", onExit);
|
|
74120
|
-
|
|
74121
|
-
|
|
74231
|
+
const leaseCandidatePath = (absolutePath) => {
|
|
74232
|
+
if (leasedPaths.has(absolutePath)) return;
|
|
74233
|
+
const existingLease = neutralizedFileLeases.get(absolutePath);
|
|
74234
|
+
if (existingLease) {
|
|
74235
|
+
existingLease.referenceCount += 1;
|
|
74236
|
+
leasedPaths.add(absolutePath);
|
|
74237
|
+
return;
|
|
74238
|
+
}
|
|
74122
74239
|
let originalContent;
|
|
74123
74240
|
try {
|
|
74124
74241
|
originalContent = NFS.readFileSync(absolutePath, "utf-8");
|
|
74125
74242
|
} catch {
|
|
74126
|
-
|
|
74243
|
+
return;
|
|
74127
74244
|
}
|
|
74128
74245
|
const neutralizedContent = neutralizeContent(originalContent);
|
|
74129
74246
|
if (neutralizedContent !== originalContent) {
|
|
74130
|
-
originalContents.set(absolutePath, originalContent);
|
|
74131
74247
|
NFS.writeFileSync(absolutePath, neutralizedContent);
|
|
74248
|
+
neutralizedFileLeases.set(absolutePath, {
|
|
74249
|
+
originalContent,
|
|
74250
|
+
referenceCount: 1
|
|
74251
|
+
});
|
|
74252
|
+
leasedPaths.add(absolutePath);
|
|
74132
74253
|
}
|
|
74254
|
+
};
|
|
74255
|
+
try {
|
|
74256
|
+
await withOverlappingInitializationLock(resolvedRootDirectory, async () => {
|
|
74257
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, [], includePaths)) if (neutralizedFileLeases.has(absolutePath)) leaseCandidatePath(absolutePath);
|
|
74258
|
+
const discoveredRelativePaths = await (options.findFiles ?? findFilesWithDisableDirectives)(resolvedRootDirectory, includePaths);
|
|
74259
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, discoveredRelativePaths, includePaths)) leaseCandidatePath(absolutePath);
|
|
74260
|
+
});
|
|
74261
|
+
} catch (error) {
|
|
74262
|
+
restore();
|
|
74263
|
+
process.removeListener("exit", onExit);
|
|
74264
|
+
throw error;
|
|
74133
74265
|
}
|
|
74134
74266
|
return () => {
|
|
74135
74267
|
restore();
|
|
@@ -78475,7 +78607,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
78475
78607
|
const sourcePath = path.join(input.directory, subdirectory, configFilename);
|
|
78476
78608
|
const targetPath = path.resolve(resolvedTempDirectory, subdirectory, configFilename);
|
|
78477
78609
|
if (!isPathInsideDirectory(targetPath, resolvedTempDirectory)) continue;
|
|
78478
|
-
if (
|
|
78610
|
+
if (isFile(sourcePath) && !fs.existsSync(targetPath)) {
|
|
78479
78611
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
78480
78612
|
fs.cpSync(sourcePath, targetPath);
|
|
78481
78613
|
}
|
|
@@ -78993,16 +79125,17 @@ const groupBy = (items, keyFn) => {
|
|
|
78993
79125
|
* a 404. Gate the directive on this predicate.
|
|
78994
79126
|
*/
|
|
78995
79127
|
const hasPublishedFixRecipe = (diagnostic) => diagnostic.plugin === "react-doctor" && Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, diagnostic.rule);
|
|
78996
|
-
const
|
|
79128
|
+
const stringValues = (values) => Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
|
|
79129
|
+
const unionValues = (baseValues, overrideValues) => [...new Set([...stringValues(baseValues), ...stringValues(overrideValues)])];
|
|
78997
79130
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
78998
79131
|
const mergedIgnore = {
|
|
78999
79132
|
...baseIgnore,
|
|
79000
79133
|
...overrideIgnore
|
|
79001
79134
|
};
|
|
79002
|
-
if (baseIgnore.rules
|
|
79003
|
-
if (baseIgnore.files
|
|
79004
|
-
if (baseIgnore.tags
|
|
79005
|
-
if (baseIgnore.overrides
|
|
79135
|
+
if (baseIgnore.rules !== void 0 || overrideIgnore.rules !== void 0) mergedIgnore.rules = unionValues(baseIgnore.rules, overrideIgnore.rules);
|
|
79136
|
+
if (baseIgnore.files !== void 0 || overrideIgnore.files !== void 0) mergedIgnore.files = unionValues(baseIgnore.files, overrideIgnore.files);
|
|
79137
|
+
if (baseIgnore.tags !== void 0 || overrideIgnore.tags !== void 0) mergedIgnore.tags = unionValues(baseIgnore.tags, overrideIgnore.tags);
|
|
79138
|
+
if (baseIgnore.overrides !== void 0 || overrideIgnore.overrides !== void 0) mergedIgnore.overrides = [...Array.isArray(baseIgnore.overrides) ? baseIgnore.overrides : [], ...Array.isArray(overrideIgnore.overrides) ? overrideIgnore.overrides : []];
|
|
79006
79139
|
return mergedIgnore;
|
|
79007
79140
|
};
|
|
79008
79141
|
/**
|
|
@@ -79197,6 +79330,21 @@ const NON_INTERACTIVE_ENVIRONMENT_VARIABLES = [
|
|
|
79197
79330
|
];
|
|
79198
79331
|
const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || isCodingAgentEnvironment();
|
|
79199
79332
|
//#endregion
|
|
79333
|
+
//#region src/cli/utils/cli-input-error.ts
|
|
79334
|
+
/**
|
|
79335
|
+
* A mistake in *how the CLI was invoked* — a malformed `<file>:<line>`
|
|
79336
|
+
* argument, mutually exclusive flags, or an unknown `--project` name. This is
|
|
79337
|
+
* user input, not a react-doctor bug, so `isExpectedUserError` routes it
|
|
79338
|
+
* through `handleUserError`: a clean, single-line message with no Sentry
|
|
79339
|
+
* report and no "Something went wrong, open a prefilled issue" block.
|
|
79340
|
+
*
|
|
79341
|
+
* The `message` is rendered verbatim, so it must read as a complete,
|
|
79342
|
+
* human-readable sentence that tells the user how to fix their invocation.
|
|
79343
|
+
*/
|
|
79344
|
+
var CliInputError = class extends Error {
|
|
79345
|
+
name = "CliInputError";
|
|
79346
|
+
};
|
|
79347
|
+
//#endregion
|
|
79200
79348
|
//#region src/cli/utils/constants.ts
|
|
79201
79349
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
79202
79350
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
@@ -79348,7 +79496,7 @@ const makeNoopConsole = () => ({
|
|
|
79348
79496
|
});
|
|
79349
79497
|
//#endregion
|
|
79350
79498
|
//#region src/cli/utils/version.ts
|
|
79351
|
-
const VERSION = "0.9.4-dev.
|
|
79499
|
+
const VERSION = "0.9.4-dev.bafef41";
|
|
79352
79500
|
//#endregion
|
|
79353
79501
|
//#region src/cli/utils/json-mode.ts
|
|
79354
79502
|
let context = null;
|
|
@@ -79392,9 +79540,14 @@ const enableJsonMode = ({ compact, directory, outputFile }) => {
|
|
|
79392
79540
|
directory,
|
|
79393
79541
|
startTime: performance$1.now(),
|
|
79394
79542
|
mode: "full",
|
|
79395
|
-
outputFile:
|
|
79543
|
+
outputFile: null
|
|
79396
79544
|
};
|
|
79397
79545
|
installSilentConsole();
|
|
79546
|
+
if (outputFile) {
|
|
79547
|
+
const resolvedOutputFile = Path.resolve(outputFile);
|
|
79548
|
+
if (NFS.existsSync(resolvedOutputFile) && !NFS.statSync(resolvedOutputFile).isFile()) throw new CliInputError(`--json-out must point to a file, not ${resolvedOutputFile}.`);
|
|
79549
|
+
context.outputFile = outputFile;
|
|
79550
|
+
}
|
|
79398
79551
|
};
|
|
79399
79552
|
const isJsonModeActive = () => context !== null;
|
|
79400
79553
|
const setJsonReportDirectory = (directory) => {
|
|
@@ -79588,6 +79741,101 @@ const buildSentryScope = (runContext = buildRunContext()) => {
|
|
|
79588
79741
|
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
79589
79742
|
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79590
79743
|
//#endregion
|
|
79744
|
+
//#region src/cli/utils/is-environment-error.ts
|
|
79745
|
+
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
79746
|
+
"EACCES",
|
|
79747
|
+
"EIO",
|
|
79748
|
+
"ENOSPC",
|
|
79749
|
+
"ENOTDIR",
|
|
79750
|
+
"EPERM",
|
|
79751
|
+
"EROFS"
|
|
79752
|
+
]);
|
|
79753
|
+
const ENVIRONMENT_FILESYSTEM_SYSCALLS = new Set([
|
|
79754
|
+
"lstat",
|
|
79755
|
+
"open",
|
|
79756
|
+
"read",
|
|
79757
|
+
"readdir",
|
|
79758
|
+
"readlink",
|
|
79759
|
+
"realpath",
|
|
79760
|
+
"scandir",
|
|
79761
|
+
"stat"
|
|
79762
|
+
]);
|
|
79763
|
+
const TRANSIENT_FILESYSTEM_ERROR_CODES = new Set([
|
|
79764
|
+
"EBUSY",
|
|
79765
|
+
"ETIMEDOUT",
|
|
79766
|
+
"UNKNOWN"
|
|
79767
|
+
]);
|
|
79768
|
+
const findEnvironmentError = (error) => {
|
|
79769
|
+
const pendingErrors = [error];
|
|
79770
|
+
const visitedErrors = /* @__PURE__ */ new Set();
|
|
79771
|
+
while (pendingErrors.length > 0) {
|
|
79772
|
+
const currentError = pendingErrors.pop();
|
|
79773
|
+
if (typeof currentError !== "object" || currentError === null) continue;
|
|
79774
|
+
if (visitedErrors.has(currentError)) continue;
|
|
79775
|
+
visitedErrors.add(currentError);
|
|
79776
|
+
if (isErrnoException(currentError) && typeof currentError.code === "string") {
|
|
79777
|
+
const syscall = currentError.syscall ?? "";
|
|
79778
|
+
if (ENVIRONMENT_ERROR_CODES.has(currentError.code) || currentError.code === "ENOENT" && syscall.startsWith("spawn") || TRANSIENT_FILESYSTEM_ERROR_CODES.has(currentError.code) && ENVIRONMENT_FILESYSTEM_SYSCALLS.has(syscall)) return currentError;
|
|
79779
|
+
}
|
|
79780
|
+
if (currentError instanceof Error && currentError.cause !== void 0) pendingErrors.push(currentError.cause);
|
|
79781
|
+
if (isReactDoctorError(currentError) && "cause" in currentError.reason) pendingErrors.push(currentError.reason.cause);
|
|
79782
|
+
}
|
|
79783
|
+
return null;
|
|
79784
|
+
};
|
|
79785
|
+
const isEnvironmentError = (error) => findEnvironmentError(error) !== null;
|
|
79786
|
+
const formatEnvironmentError = (error) => {
|
|
79787
|
+
const environmentError = findEnvironmentError(error);
|
|
79788
|
+
if (!environmentError) return messageFromUnknown(error);
|
|
79789
|
+
switch (environmentError.code) {
|
|
79790
|
+
case "EBUSY": return environmentError.path ? `Resource busy or locked at ${environmentError.path}. Close the process using it and try again.` : "Resource busy or locked. Close the process using it and try again.";
|
|
79791
|
+
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
79792
|
+
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
79793
|
+
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
79794
|
+
case "EACCES":
|
|
79795
|
+
case "EPERM": return environmentError.path ? `Permission denied accessing ${environmentError.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
79796
|
+
case "ENOTDIR": return environmentError.path ? `A file exists at ${environmentError.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
79797
|
+
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
79798
|
+
case "ETIMEDOUT": return environmentError.path ? `Timed out accessing ${environmentError.path}. Check the network or cloud drive and try again.` : "Filesystem access timed out. Check the network or cloud drive and try again.";
|
|
79799
|
+
case "UNKNOWN": return environmentError.path ? `The operating system could not access ${environmentError.path}. Check the filesystem and try again.` : "The operating system could not access the filesystem. Check it and try again.";
|
|
79800
|
+
default: return environmentError.message;
|
|
79801
|
+
}
|
|
79802
|
+
};
|
|
79803
|
+
//#endregion
|
|
79804
|
+
//#region src/cli/utils/is-expected-user-error.ts
|
|
79805
|
+
/**
|
|
79806
|
+
* Whether `error` is an expected, user-actionable failure — the user's project
|
|
79807
|
+
* or input, not a react-doctor bug. Such failures must be kept out of crash
|
|
79808
|
+
* reporting (Sentry + the alertable error-rate metric) and rendered via
|
|
79809
|
+
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
79810
|
+
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
79811
|
+
*
|
|
79812
|
+
* Four distinct shapes reach the CLI's catch blocks:
|
|
79813
|
+
*
|
|
79814
|
+
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
79815
|
+
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
79816
|
+
* `AmbiguousProjectError`) arrive as their plain legacy classes (so
|
|
79817
|
+
* `isReactDoctorError` is `false` for them) — narrow with
|
|
79818
|
+
* `isProjectDiscoveryError`. Running react-doctor against a directory that
|
|
79819
|
+
* has no React, or a path that doesn't exist, is the canonical example.
|
|
79820
|
+
* - **CLI invocation mistakes** (`CliInputError`): a malformed
|
|
79821
|
+
* `<file>:<line>` argument, mutually exclusive flags, or an unknown
|
|
79822
|
+
* `--project` name.
|
|
79823
|
+
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
79824
|
+
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
79825
|
+
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
79826
|
+
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
79827
|
+
* full / failing / read-only, permission denied, or a path blocked by a
|
|
79828
|
+
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
79829
|
+
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
79830
|
+
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
79831
|
+
* Sentry).
|
|
79832
|
+
*
|
|
79833
|
+
* This composes the existing core narrowers rather than introducing a new
|
|
79834
|
+
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
79835
|
+
* knowledge of the `ReactDoctorError` shape.
|
|
79836
|
+
*/
|
|
79837
|
+
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
79838
|
+
//#endregion
|
|
79591
79839
|
//#region src/cli/utils/anonymize-text.ts
|
|
79592
79840
|
/**
|
|
79593
79841
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -79709,13 +79957,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79709
79957
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79710
79958
|
* standard `SENTRY_RELEASE` override.
|
|
79711
79959
|
*/
|
|
79712
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.4-dev.
|
|
79960
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.4-dev.bafef41`;
|
|
79713
79961
|
/**
|
|
79714
79962
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79715
79963
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79716
79964
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79717
79965
|
*/
|
|
79718
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.4-dev.
|
|
79966
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.4-dev.bafef41") ? "development" : "production");
|
|
79719
79967
|
/**
|
|
79720
79968
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79721
79969
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -79780,21 +80028,25 @@ const initializeSentry = () => {
|
|
|
79780
80028
|
isInitialized = true;
|
|
79781
80029
|
resolvedTracesSampleRate = isDebugFlagEnabled() ? 1 : resolveTracesSampleRate();
|
|
79782
80030
|
const { tags, contexts } = buildSentryScope();
|
|
79783
|
-
|
|
79784
|
-
|
|
79785
|
-
|
|
79786
|
-
|
|
79787
|
-
|
|
79788
|
-
|
|
79789
|
-
|
|
79790
|
-
|
|
79791
|
-
|
|
79792
|
-
|
|
79793
|
-
|
|
79794
|
-
|
|
79795
|
-
|
|
79796
|
-
|
|
79797
|
-
|
|
80031
|
+
try {
|
|
80032
|
+
Sentry.init({
|
|
80033
|
+
dsn: process.env.SENTRY_DSN || "https://f253d570240a59b8dbd77b7a548ef133@o4510226365743104.ingest.us.sentry.io/4511487817809920",
|
|
80034
|
+
release: resolveSentryRelease(),
|
|
80035
|
+
environment: resolveSentryEnvironment(),
|
|
80036
|
+
sendDefaultPii: false,
|
|
80037
|
+
tracesSampleRate: resolvedTracesSampleRate,
|
|
80038
|
+
debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
|
|
80039
|
+
initialScope: {
|
|
80040
|
+
tags,
|
|
80041
|
+
contexts
|
|
80042
|
+
},
|
|
80043
|
+
beforeSend: (event, hint) => isExpectedUserError(hint.originalException) ? null : scrubSentryEvent(event),
|
|
80044
|
+
beforeSendTransaction: (event) => scrubSentryEvent(event),
|
|
80045
|
+
beforeSendMetric: (metric) => scrubSentryMetric(metric)
|
|
80046
|
+
});
|
|
80047
|
+
} catch {
|
|
80048
|
+
resolvedTracesSampleRate = 0;
|
|
80049
|
+
}
|
|
79798
80050
|
};
|
|
79799
80051
|
//#endregion
|
|
79800
80052
|
//#region src/cli/utils/cli-logger.ts
|
|
@@ -91109,34 +91361,6 @@ const runCiConfig = async (options = {}) => {
|
|
|
91109
91361
|
});
|
|
91110
91362
|
};
|
|
91111
91363
|
//#endregion
|
|
91112
|
-
//#region src/cli/utils/is-environment-error.ts
|
|
91113
|
-
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
91114
|
-
"ENOSPC",
|
|
91115
|
-
"EIO",
|
|
91116
|
-
"EROFS",
|
|
91117
|
-
"EACCES",
|
|
91118
|
-
"EPERM",
|
|
91119
|
-
"ENOTDIR"
|
|
91120
|
-
]);
|
|
91121
|
-
const isEnvironmentError = (error) => {
|
|
91122
|
-
if (!isErrnoException(error) || typeof error.code !== "string") return false;
|
|
91123
|
-
if (error.code === "ENOENT") return error.syscall?.startsWith("spawn") ?? false;
|
|
91124
|
-
return ENVIRONMENT_ERROR_CODES.has(error.code);
|
|
91125
|
-
};
|
|
91126
|
-
const formatEnvironmentError = (error) => {
|
|
91127
|
-
if (!isErrnoException(error)) return messageFromUnknown(error);
|
|
91128
|
-
switch (error.code) {
|
|
91129
|
-
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
91130
|
-
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
91131
|
-
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
91132
|
-
case "EACCES":
|
|
91133
|
-
case "EPERM": return error.path ? `Permission denied accessing ${error.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
91134
|
-
case "ENOTDIR": return error.path ? `A file exists at ${error.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
91135
|
-
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
91136
|
-
default: return error.message;
|
|
91137
|
-
}
|
|
91138
|
-
};
|
|
91139
|
-
//#endregion
|
|
91140
91364
|
//#region src/cli/utils/handle-error.ts
|
|
91141
91365
|
const OTLP_ENDPOINT_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_ENDPOINT";
|
|
91142
91366
|
const OTLP_AUTH_HEADER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_AUTH_HEADER";
|
|
@@ -91238,56 +91462,6 @@ const handleUserError = (error, options = {}) => {
|
|
|
91238
91462
|
process.exitCode = 1;
|
|
91239
91463
|
};
|
|
91240
91464
|
//#endregion
|
|
91241
|
-
//#region src/cli/utils/cli-input-error.ts
|
|
91242
|
-
/**
|
|
91243
|
-
* A mistake in *how the CLI was invoked* — a malformed `<file>:<line>`
|
|
91244
|
-
* argument, mutually exclusive flags, or an unknown `--project` name. This is
|
|
91245
|
-
* user input, not a react-doctor bug, so `isExpectedUserError` routes it
|
|
91246
|
-
* through `handleUserError`: a clean, single-line message with no Sentry
|
|
91247
|
-
* report and no "Something went wrong, open a prefilled issue" block.
|
|
91248
|
-
*
|
|
91249
|
-
* The `message` is rendered verbatim, so it must read as a complete,
|
|
91250
|
-
* human-readable sentence that tells the user how to fix their invocation.
|
|
91251
|
-
*/
|
|
91252
|
-
var CliInputError = class extends Error {
|
|
91253
|
-
name = "CliInputError";
|
|
91254
|
-
};
|
|
91255
|
-
//#endregion
|
|
91256
|
-
//#region src/cli/utils/is-expected-user-error.ts
|
|
91257
|
-
/**
|
|
91258
|
-
* Whether `error` is an expected, user-actionable failure — the user's project
|
|
91259
|
-
* or input, not a react-doctor bug. Such failures must be kept out of crash
|
|
91260
|
-
* reporting (Sentry + the alertable error-rate metric) and rendered via
|
|
91261
|
-
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
91262
|
-
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
91263
|
-
*
|
|
91264
|
-
* Four distinct shapes reach the CLI's catch blocks:
|
|
91265
|
-
*
|
|
91266
|
-
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
91267
|
-
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
91268
|
-
* `AmbiguousProjectError`) arrive as their plain legacy classes (so
|
|
91269
|
-
* `isReactDoctorError` is `false` for them) — narrow with
|
|
91270
|
-
* `isProjectDiscoveryError`. Running react-doctor against a directory that
|
|
91271
|
-
* has no React, or a path that doesn't exist, is the canonical example.
|
|
91272
|
-
* - **CLI invocation mistakes** (`CliInputError`): a malformed
|
|
91273
|
-
* `<file>:<line>` argument, mutually exclusive flags, or an unknown
|
|
91274
|
-
* `--project` name.
|
|
91275
|
-
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
91276
|
-
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
91277
|
-
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
91278
|
-
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
91279
|
-
* full / failing / read-only, permission denied, or a path blocked by a
|
|
91280
|
-
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
91281
|
-
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
91282
|
-
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
91283
|
-
* Sentry).
|
|
91284
|
-
*
|
|
91285
|
-
* This composes the existing core narrowers rather than introducing a new
|
|
91286
|
-
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
91287
|
-
* knowledge of the `ReactDoctorError` shape.
|
|
91288
|
-
*/
|
|
91289
|
-
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
91290
|
-
//#endregion
|
|
91291
91465
|
//#region src/cli/utils/print-branded-header.ts
|
|
91292
91466
|
/**
|
|
91293
91467
|
* Single branded line every command prints first when not in JSON
|
|
@@ -97260,16 +97434,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97260
97434
|
const requestedDirectory = Path.resolve(directory);
|
|
97261
97435
|
const startTime = performance$1.now();
|
|
97262
97436
|
let scanStartupSpinner = null;
|
|
97263
|
-
if (isJsonMode) {
|
|
97264
|
-
enableJsonMode({
|
|
97265
|
-
compact: Boolean(flags.jsonCompact),
|
|
97266
|
-
directory: requestedDirectory,
|
|
97267
|
-
outputFile: flags.jsonOut
|
|
97268
|
-
});
|
|
97269
|
-
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
97270
|
-
}
|
|
97271
|
-
recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
|
|
97272
97437
|
try {
|
|
97438
|
+
if (isJsonMode) {
|
|
97439
|
+
enableJsonMode({
|
|
97440
|
+
compact: Boolean(flags.jsonCompact),
|
|
97441
|
+
directory: requestedDirectory,
|
|
97442
|
+
outputFile: flags.jsonOut
|
|
97443
|
+
});
|
|
97444
|
+
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
97445
|
+
}
|
|
97446
|
+
recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
|
|
97273
97447
|
validateModeFlags(flags);
|
|
97274
97448
|
if (flags.staged) setJsonReportMode("staged");
|
|
97275
97449
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
@@ -97823,7 +97997,7 @@ const runScanCommand = async (input) => {
|
|
|
97823
97997
|
return;
|
|
97824
97998
|
}
|
|
97825
97999
|
recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
|
|
97826
|
-
const { runScanApp } = await import("./run-scan-app-
|
|
98000
|
+
const { runScanApp } = await import("./run-scan-app-C8iWVgvR.js");
|
|
97827
98001
|
const { shouldFail } = await runScanApp({
|
|
97828
98002
|
directory: input.directory,
|
|
97829
98003
|
scanTarget,
|
|
@@ -98956,6 +99130,9 @@ program.command("experimental-tui [directory]", { hidden: true }).description("[
|
|
|
98956
99130
|
process.stdout.on("error", (error) => {
|
|
98957
99131
|
if (error.code === "EPIPE") process.exit(0);
|
|
98958
99132
|
});
|
|
99133
|
+
process.stderr.on("error", (error) => {
|
|
99134
|
+
if (error.code === "EPIPE") process.exit(0);
|
|
99135
|
+
});
|
|
98959
99136
|
const knownCommands = program.commands.flatMap((command) => [command.name(), ...command.aliases()]);
|
|
98960
99137
|
const strippedArgv = stripUnknownCliFlags(process.argv);
|
|
98961
99138
|
if (process.argv.includes("-V") && !strippedArgv.includes("-V")) {
|
|
@@ -98982,4 +99159,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98982
99159
|
export { TUI_REPORT_DETAIL_WIDTH_FRACTION as $, shouldRecordOnboarding as A, buildSortedRuleGroups as B, createInvocationInspect as C, markOnboardingComplete as D, doctorFace as E, buildEmptyReportMessage as F, recordCount as G, formatLearnMoreLine as H, hasIncompleteScoreAnalysis as I, findNearestPackageDirectory as J, recordDistribution as K, buildNoScoreMessage as L, computeProjectedScore as M, formatElapsedTime as N, canAnimateOnboarding as O, buildCodeFrame as P, TUI_REPORT_COMPACT_MAX_ROWS as Q, hasLintHardFailure as R, deduplicateProjectScans as S, easeOutCubic as T, resolveBlockingLevel as U, formatFixRecipeLine as V, isReactDoctorWorkflowInstalled as W, CI_TRUST_COMPANIES as X, isUnicodeSupported as Y, METRIC as Z, launchCliAgent as _, succeed$3 as _t, selectProjects as a, buildRuleDocsUrl as at, formatSkippedProjectsMessage as b, resolveWorkspaceDeadCodeOwner as c, highlighter as ct, countUniqueScannedFiles as d, remainingDeadlineBudgetMs as dt, isCiEnvironment as et, setUpGitHubActions as f, resolveScanTarget as ft, copyToClipboard as g, void_ as gt, CLI_AGENT_BINARIES as h, sync as ht, discoverWorkspacePackages as i, Reporter as it, filterDiagnosticsByCategories as j, isOnboardingForced as k, resolveCliInspectOptions as l, isPathInsideDirectory as lt, detectLaunchableAgents as m, runPromise as mt, registerActiveTuiRenderer as n, GITHUB_ACTIONS_SETUP_URL as nt, resolveProjectScan as o, getCategoryImpact as ot, openUrl as p, yieldToEventLoop as pt, toForwardSlashes as q, shouldFailScanGate as r, Progress as rt, retryMissingProjectScores as s, hasPublishedFixRecipe as st, exitGracefully as t, DIAGNOSTIC_CATEGORY_BUCKETS as tt, filterScansForSurface as u, mapWithConcurrency as ut, buildHandoffPayload as v, printFooter as w, collectProjectSourceFileCounts as x, isShareOptedOut as y, buildRulePriorityMap as z };
|
|
98983
99160
|
|
|
98984
99161
|
//# sourceMappingURL=cli.js.map
|
|
98985
|
-
//# debugId=
|
|
99162
|
+
//# debugId=e74e6a68-20e1-59fe-8db0-62b87e8fe049
|