react-doctor 0.8.3 → 0.9.0-dev.1e67af0
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 +274 -179
- package/dist/index.js +33 -17
- package/dist/lsp.js +36 -20
- package/dist/run-scan-app-DZ5jGWFf.js +1661 -0
- package/package.json +13 -7
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]="2fb9bb5f-dfff-55d9-9d36-af738b2c29ea")}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";
|
|
@@ -64656,6 +64656,13 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
64656
64656
|
"storybook-static"
|
|
64657
64657
|
]);
|
|
64658
64658
|
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
64659
|
+
const TYPESCRIPT_DECLARATION_FILE_PATTERN = /\.d\.(?:ts|mts|cts)$/;
|
|
64660
|
+
const OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES = new Set([
|
|
64661
|
+
"TS(1036)",
|
|
64662
|
+
"TS(1038)",
|
|
64663
|
+
"TS(1183)",
|
|
64664
|
+
"TS(1319)"
|
|
64665
|
+
]);
|
|
64659
64666
|
const MILLISECONDS_PER_SECOND = 1e3;
|
|
64660
64667
|
const REACT_ROUTER_CAPABILITY_THRESHOLDS = [
|
|
64661
64668
|
{
|
|
@@ -64957,6 +64964,14 @@ const DIAGNOSTIC_CATEGORY_BUCKETS = [
|
|
|
64957
64964
|
"Maintainability"
|
|
64958
64965
|
];
|
|
64959
64966
|
const OCCURRENCE_MATCHED_CATEGORIES = new Set(["Accessibility"]);
|
|
64967
|
+
const CATEGORY_IMPACT_BY_NAME = new Map(Object.entries({
|
|
64968
|
+
Security: "An attacker can read user data, act as your users, or run code you never shipped.",
|
|
64969
|
+
Bugs: "Real users hit crashes, wrong output, or state that silently corrupts.",
|
|
64970
|
+
Performance: "Users feel extra latency and wasted renders on every interaction.",
|
|
64971
|
+
Accessibility: "Users on screen readers, keyboards, or assistive tech get locked out.",
|
|
64972
|
+
Maintainability: "Every future change gets slower and riskier to make."
|
|
64973
|
+
}));
|
|
64974
|
+
const getCategoryImpact = (category) => CATEGORY_IMPACT_BY_NAME.get(category);
|
|
64960
64975
|
const APP_ONLY_RULE_KEYS = new Set([
|
|
64961
64976
|
"react-hooks-js/static-components",
|
|
64962
64977
|
"react-doctor/no-render-prop-children",
|
|
@@ -64979,7 +64994,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
64979
64994
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
64980
64995
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
64981
64996
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
64982
|
-
const CORE_PACKAGE_VERSION = "0.
|
|
64997
|
+
const CORE_PACKAGE_VERSION = "0.9.0";
|
|
64983
64998
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
64984
64999
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
64985
65000
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -65700,18 +65715,20 @@ const getReactCompilerFlagState = (expression, analysis) => {
|
|
|
65700
65715
|
}
|
|
65701
65716
|
return null;
|
|
65702
65717
|
};
|
|
65703
|
-
const getSelectedObjectProperty = (expression, propertyName, analysis) => {
|
|
65718
|
+
const getSelectedObjectProperty = (expression, propertyName, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
|
|
65704
65719
|
let resolvedExpression = expression;
|
|
65705
65720
|
while (ts.isParenthesizedExpression(resolvedExpression) || ts.isAsExpression(resolvedExpression) || ts.isTypeAssertionExpression(resolvedExpression) || ts.isSatisfiesExpression(resolvedExpression) || ts.isNonNullExpression(resolvedExpression)) resolvedExpression = resolvedExpression.expression;
|
|
65721
|
+
if (visitedExpressions.has(resolvedExpression)) return null;
|
|
65722
|
+
visitedExpressions.add(resolvedExpression);
|
|
65706
65723
|
if (ts.isIdentifier(resolvedExpression)) {
|
|
65707
65724
|
if (analysis.localBindings.has(resolvedExpression.text)) {
|
|
65708
65725
|
const localInitializer = analysis.localBindings.get(resolvedExpression.text);
|
|
65709
|
-
return localInitializer ? getSelectedObjectProperty(localInitializer, propertyName, analysis) : null;
|
|
65726
|
+
return localInitializer ? getSelectedObjectProperty(localInitializer, propertyName, analysis, visitedExpressions) : null;
|
|
65710
65727
|
}
|
|
65711
65728
|
const scopedBinding = getScopedConfigBinding(resolvedExpression);
|
|
65712
|
-
if (scopedBinding.wasFound) return scopedBinding.initializer ? getSelectedObjectProperty(scopedBinding.initializer, propertyName, analysis) : null;
|
|
65729
|
+
if (scopedBinding.wasFound) return scopedBinding.initializer ? getSelectedObjectProperty(scopedBinding.initializer, propertyName, analysis, visitedExpressions) : null;
|
|
65713
65730
|
const topLevelBinding = getTopLevelBinding(analysis.sourceFile, resolvedExpression.text);
|
|
65714
|
-
return topLevelBinding && ts.isExpression(topLevelBinding) ? getSelectedObjectProperty(topLevelBinding, propertyName, analysis) : null;
|
|
65731
|
+
return topLevelBinding && ts.isExpression(topLevelBinding) ? getSelectedObjectProperty(topLevelBinding, propertyName, analysis, visitedExpressions) : null;
|
|
65715
65732
|
}
|
|
65716
65733
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
|
|
65717
65734
|
for (const property of [...resolvedExpression.properties].reverse()) {
|
|
@@ -65719,7 +65736,7 @@ const getSelectedObjectProperty = (expression, propertyName, analysis) => {
|
|
|
65719
65736
|
if (ts.isMethodDeclaration(property) && getStaticPropertyName$1(property.name) === propertyName) return property;
|
|
65720
65737
|
if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
|
|
65721
65738
|
if (ts.isSpreadAssignment(property)) {
|
|
65722
|
-
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis);
|
|
65739
|
+
const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
|
|
65723
65740
|
if (spreadProperty) return spreadProperty;
|
|
65724
65741
|
}
|
|
65725
65742
|
}
|
|
@@ -73188,13 +73205,13 @@ const decodeFileDiagnostics = (raw) => {
|
|
|
73188
73205
|
}
|
|
73189
73206
|
};
|
|
73190
73207
|
const emptyCache$1 = () => ({
|
|
73191
|
-
version:
|
|
73208
|
+
version: 2,
|
|
73192
73209
|
rulesets: {}
|
|
73193
73210
|
});
|
|
73194
73211
|
const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
|
|
73195
73212
|
const entries = /* @__PURE__ */ new Map();
|
|
73196
73213
|
const persisted = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
73197
|
-
if (persisted.version !==
|
|
73214
|
+
if (persisted.version !== 2 || !isRecord$1(persisted.rulesets)) return entries;
|
|
73198
73215
|
const bucket = persisted.rulesets[rulesetHash];
|
|
73199
73216
|
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
73200
73217
|
for (const [fileKey, rawDiagnostics] of Object.entries(bucket.files)) {
|
|
@@ -73214,7 +73231,7 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
73214
73231
|
},
|
|
73215
73232
|
persist: () => {
|
|
73216
73233
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
73217
|
-
const rulesets = onDisk.version ===
|
|
73234
|
+
const rulesets = onDisk.version === 2 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
73218
73235
|
const existingBucket = rulesets[rulesetHash];
|
|
73219
73236
|
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
73220
73237
|
const ourFiles = {};
|
|
@@ -73228,7 +73245,7 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
73228
73245
|
files: Object.fromEntries(cappedEntries)
|
|
73229
73246
|
};
|
|
73230
73247
|
atomicWriteJson(cacheFilePath, {
|
|
73231
|
-
version:
|
|
73248
|
+
version: 2,
|
|
73232
73249
|
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord$1(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
73233
73250
|
});
|
|
73234
73251
|
}
|
|
@@ -73317,13 +73334,13 @@ const encodeSidecarFileEntry = (entry) => ({
|
|
|
73317
73334
|
diagnostics: entry.diagnostics
|
|
73318
73335
|
});
|
|
73319
73336
|
const emptyCache = () => ({
|
|
73320
|
-
version:
|
|
73337
|
+
version: 3,
|
|
73321
73338
|
rulesets: {}
|
|
73322
73339
|
});
|
|
73323
73340
|
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
73324
73341
|
const entries = /* @__PURE__ */ new Map();
|
|
73325
73342
|
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
73326
|
-
if (persisted.version !==
|
|
73343
|
+
if (persisted.version !== 3 || !isRecord$1(persisted.rulesets)) return entries;
|
|
73327
73344
|
const bucket = persisted.rulesets[rulesetHash];
|
|
73328
73345
|
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
73329
73346
|
for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
|
|
@@ -73343,7 +73360,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
73343
73360
|
},
|
|
73344
73361
|
persist: () => {
|
|
73345
73362
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
73346
|
-
const rulesets = onDisk.version ===
|
|
73363
|
+
const rulesets = onDisk.version === 3 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
73347
73364
|
const existingBucket = rulesets[rulesetHash];
|
|
73348
73365
|
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
73349
73366
|
const ourFiles = {};
|
|
@@ -73357,7 +73374,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
73357
73374
|
files: Object.fromEntries(cappedEntries)
|
|
73358
73375
|
};
|
|
73359
73376
|
atomicWriteJson(cacheFilePath, {
|
|
73360
|
-
version:
|
|
73377
|
+
version: 3,
|
|
73361
73378
|
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord$1(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
73362
73379
|
});
|
|
73363
73380
|
}
|
|
@@ -74290,7 +74307,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
74290
74307
|
minifiedFileCache.set(absolutePath, minified);
|
|
74291
74308
|
return minified;
|
|
74292
74309
|
};
|
|
74293
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
74310
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
74294
74311
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
74295
74312
|
const primaryLabel = diagnostic.labels[0];
|
|
74296
74313
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -77300,6 +77317,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
|
77300
77317
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
77301
77318
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
77302
77319
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
77320
|
+
const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
|
|
77303
77321
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
77304
77322
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
77305
77323
|
const CATEGORY_COUNTUP_SETTLE_HOLD_MS = 1e3;
|
|
@@ -77391,7 +77409,7 @@ const makeNoopConsole = () => ({
|
|
|
77391
77409
|
});
|
|
77392
77410
|
//#endregion
|
|
77393
77411
|
//#region src/cli/utils/version.ts
|
|
77394
|
-
const VERSION = "0.
|
|
77412
|
+
const VERSION = "0.9.0-dev.1e67af0";
|
|
77395
77413
|
//#endregion
|
|
77396
77414
|
//#region src/cli/utils/json-mode.ts
|
|
77397
77415
|
let context = null;
|
|
@@ -77749,13 +77767,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
77749
77767
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
77750
77768
|
* standard `SENTRY_RELEASE` override.
|
|
77751
77769
|
*/
|
|
77752
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.
|
|
77770
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.0-dev.1e67af0`;
|
|
77753
77771
|
/**
|
|
77754
77772
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
77755
77773
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
77756
77774
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
77757
77775
|
*/
|
|
77758
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.
|
|
77776
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.0-dev.1e67af0") ? "development" : "production");
|
|
77759
77777
|
/**
|
|
77760
77778
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
77761
77779
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -89602,12 +89620,13 @@ const buildRuntimeLayers = (input) => {
|
|
|
89602
89620
|
const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
|
|
89603
89621
|
const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
|
|
89604
89622
|
const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
|
|
89605
|
-
const progressLayer = input.shouldShowProgressSpinners ? Progress.layerOra(buildSpinnerProgressHandle) : Progress.layerNoop;
|
|
89623
|
+
const progressLayer = input.progressLayer ?? (input.shouldShowProgressSpinners ? Progress.layerOra(buildSpinnerProgressHandle) : Progress.layerNoop);
|
|
89624
|
+
const reporterLayer = input.reporterLayer ?? Reporter.layerNoop;
|
|
89606
89625
|
const baseLayers = mergeAll$1(projectLayer, input.hasConfigOverride ? Config.layerOf({
|
|
89607
89626
|
config: input.userConfig,
|
|
89608
89627
|
resolvedDirectory: input.directory,
|
|
89609
89628
|
configSourceDirectory: input.configSourceDirectory
|
|
89610
|
-
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer,
|
|
89629
|
+
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
|
|
89611
89630
|
return input.oxlintConcurrency === void 0 ? baseLayers : mergeAll$1(baseLayers, succeed$3(OxlintConcurrency, input.oxlintConcurrency));
|
|
89612
89631
|
};
|
|
89613
89632
|
//#endregion
|
|
@@ -89977,12 +89996,12 @@ const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorC
|
|
|
89977
89996
|
const readPersistedCache = (cacheFilePath) => {
|
|
89978
89997
|
try {
|
|
89979
89998
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
89980
|
-
if (!isRecord(parsed) || parsed.version !==
|
|
89981
|
-
version:
|
|
89999
|
+
if (!isRecord(parsed) || parsed.version !== 6) return {
|
|
90000
|
+
version: 6,
|
|
89982
90001
|
entries: []
|
|
89983
90002
|
};
|
|
89984
90003
|
if (!Array.isArray(parsed.entries)) return {
|
|
89985
|
-
version:
|
|
90004
|
+
version: 6,
|
|
89986
90005
|
entries: []
|
|
89987
90006
|
};
|
|
89988
90007
|
const entries = [];
|
|
@@ -89992,12 +90011,12 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
89992
90011
|
entries.push(entry);
|
|
89993
90012
|
}
|
|
89994
90013
|
return {
|
|
89995
|
-
version:
|
|
90014
|
+
version: 6,
|
|
89996
90015
|
entries
|
|
89997
90016
|
};
|
|
89998
90017
|
} catch {
|
|
89999
90018
|
return {
|
|
90000
|
-
version:
|
|
90019
|
+
version: 6,
|
|
90001
90020
|
entries: []
|
|
90002
90021
|
};
|
|
90003
90022
|
}
|
|
@@ -90059,7 +90078,7 @@ const buildScanResultCacheKey = (input) => {
|
|
|
90059
90078
|
if (headSha === null) return null;
|
|
90060
90079
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
90061
90080
|
const cacheKeyJson = stringifyStableJson({
|
|
90062
|
-
schemaVersion:
|
|
90081
|
+
schemaVersion: 6,
|
|
90063
90082
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
90064
90083
|
headSha,
|
|
90065
90084
|
worktreeFingerprint,
|
|
@@ -90102,7 +90121,7 @@ const createScanResultCache = (projectDirectory) => {
|
|
|
90102
90121
|
for (const entry of persistedCache.entries) entries.set(entry.key, entry);
|
|
90103
90122
|
const persist = () => {
|
|
90104
90123
|
writePersistedCache(cacheFilePath, {
|
|
90105
|
-
version:
|
|
90124
|
+
version: 6,
|
|
90106
90125
|
entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
90107
90126
|
});
|
|
90108
90127
|
};
|
|
@@ -91442,6 +91461,9 @@ const supportsHyperlinks = (stream = process.stdout, env = process.env) => {
|
|
|
91442
91461
|
* would choke on the escape sequences).
|
|
91443
91462
|
*/
|
|
91444
91463
|
const shouldRenderHyperlinks = (stream = process.stdout) => supportsHyperlinks(stream) && !isCodingAgentEnvironment();
|
|
91464
|
+
//#endregion
|
|
91465
|
+
//#region src/cli/utils/should-show-share-link.ts
|
|
91466
|
+
const shouldShowShareLink = ({ noScore, share, isCi }) => !noScore && share && !isCi;
|
|
91445
91467
|
const FORCE_ONBOARDING_ENV_VAR = "REACT_DOCTOR_FORCE_ONBOARDING";
|
|
91446
91468
|
const FALSY_FLAG_VALUES = new Set([
|
|
91447
91469
|
"",
|
|
@@ -91660,6 +91682,13 @@ const colorizeByScore = (text, score) => {
|
|
|
91660
91682
|
return highlighter.error(text);
|
|
91661
91683
|
};
|
|
91662
91684
|
//#endregion
|
|
91685
|
+
//#region src/cli/utils/doctor-face.ts
|
|
91686
|
+
const doctorFace = (score) => {
|
|
91687
|
+
if (score >= 75) return ["◠ ◠", " ▽ "];
|
|
91688
|
+
if (score >= 50) return ["• •", " ─ "];
|
|
91689
|
+
return ["x x", " ▽ "];
|
|
91690
|
+
};
|
|
91691
|
+
//#endregion
|
|
91663
91692
|
//#region src/cli/utils/ease-out-cubic.ts
|
|
91664
91693
|
const easeOutCubic = (progress) => 1 - (1 - progress) ** 3;
|
|
91665
91694
|
//#endregion
|
|
@@ -91725,15 +91754,10 @@ const buildProjectedScoreBar = (currentScore, potentialScore) => {
|
|
|
91725
91754
|
const emptyCount = Math.max(0, barWidth - currentFill - gainCount);
|
|
91726
91755
|
return colorizeByScore("█".repeat(currentFill), currentScore) + highlighter.dim(colorizeByScore("▓".repeat(gainCount), currentScore)) + highlighter.dim("░".repeat(emptyCount));
|
|
91727
91756
|
};
|
|
91728
|
-
const getDoctorFace = (score) => {
|
|
91729
|
-
if (score >= 75) return ["◠ ◠", " ▽ "];
|
|
91730
|
-
if (score >= 50) return ["• •", " ─ "];
|
|
91731
|
-
return ["x x", " ▽ "];
|
|
91732
|
-
};
|
|
91733
91757
|
const BRANDING_LINE = `React Doctor ${highlighter.dim("(https://react.doctor)")}`;
|
|
91734
91758
|
const RAW_BRANDING_LINE = "React Doctor (https://react.doctor)";
|
|
91735
91759
|
const buildRawFaceLines = (score) => {
|
|
91736
|
-
const [eyes, mouth] =
|
|
91760
|
+
const [eyes, mouth] = doctorFace(score);
|
|
91737
91761
|
return [
|
|
91738
91762
|
FACE_BOX_TOP_BORDER,
|
|
91739
91763
|
`│ ${eyes} │`,
|
|
@@ -92067,7 +92091,8 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
|
|
|
92067
92091
|
includeTagDefaults: inputOptions.includeTagDefaults ?? false,
|
|
92068
92092
|
scoreDisabledMessage: inputOptions.scoreDisabledMessage,
|
|
92069
92093
|
outputSurface: inputOptions.outputSurface ?? "cli",
|
|
92070
|
-
suppressRendering: inputOptions.suppressRendering ?? false,
|
|
92094
|
+
suppressRendering: (inputOptions.suppressRendering ?? false) || inputOptions.uiLayers != null,
|
|
92095
|
+
uiLayers: inputOptions.uiLayers ?? null,
|
|
92071
92096
|
concurrentScan: inputOptions.concurrentScan ?? false,
|
|
92072
92097
|
concurrency: inputOptions.concurrency,
|
|
92073
92098
|
maxDurationMs: inputOptions.maxDurationMs ?? null,
|
|
@@ -92291,7 +92316,9 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
92291
92316
|
shouldRunSupplyChain: options.supplyChain,
|
|
92292
92317
|
shouldComputeScore: !options.noScore,
|
|
92293
92318
|
shouldShowProgressSpinners,
|
|
92294
|
-
oxlintConcurrency: options.concurrency
|
|
92319
|
+
oxlintConcurrency: options.concurrency,
|
|
92320
|
+
reporterLayer: options.uiLayers?.reporter,
|
|
92321
|
+
progressLayer: options.uiLayers?.progress
|
|
92295
92322
|
});
|
|
92296
92323
|
const program = runInspect({
|
|
92297
92324
|
directory,
|
|
@@ -92329,7 +92356,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
92329
92356
|
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
|
|
92330
92357
|
const didLintFail = lintBindingMissing || output.didLintFail;
|
|
92331
92358
|
const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
|
|
92332
|
-
if (!options.scoreOnly && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runConsole(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
|
|
92359
|
+
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runConsole(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
|
|
92333
92360
|
else runConsole(error$1(highlighter.error(lintFailureReason)));
|
|
92334
92361
|
let inspectDiagnostics = output.diagnostics;
|
|
92335
92362
|
let baselineDelta;
|
|
@@ -92564,7 +92591,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
92564
92591
|
const scoreDiagnostics = filterDiagnosticsForSurface([...diagnostics], "score", userConfig);
|
|
92565
92592
|
const displayedScoreDiagnostics = filterDiagnosticsForSurface([...printedDiagnostics], "score", userConfig);
|
|
92566
92593
|
const potentialScore = score ? yield* promise(() => computeProjectedScore(displayedScoreDiagnostics, scoreDiagnostics, score)) : null;
|
|
92567
|
-
const
|
|
92594
|
+
const showShareLink = shouldShowShareLink(options);
|
|
92568
92595
|
yield* pause;
|
|
92569
92596
|
yield* printSummary({
|
|
92570
92597
|
diagnostics: [...printedDiagnostics],
|
|
@@ -92587,7 +92614,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
92587
92614
|
diagnostics: [...printedDiagnostics],
|
|
92588
92615
|
scoreResult: score,
|
|
92589
92616
|
projectName: project.projectName,
|
|
92590
|
-
isOffline: !
|
|
92617
|
+
isOffline: !showShareLink
|
|
92591
92618
|
});
|
|
92592
92619
|
return buildResult();
|
|
92593
92620
|
});
|
|
@@ -92697,6 +92724,127 @@ const computeDefaultSelectedAgents = (detectedAgents, rememberedAgents) => {
|
|
|
92697
92724
|
return detectedAgents.length === 1 ? [...detectedAgents] : [];
|
|
92698
92725
|
};
|
|
92699
92726
|
//#endregion
|
|
92727
|
+
//#region src/cli/utils/launch-agent.ts
|
|
92728
|
+
const isWindows = process.platform === "win32";
|
|
92729
|
+
const CLI_AGENT_IDS = [
|
|
92730
|
+
"claude-code",
|
|
92731
|
+
"codex",
|
|
92732
|
+
"cursor"
|
|
92733
|
+
];
|
|
92734
|
+
const CLI_AGENT_BINARIES = {
|
|
92735
|
+
"claude-code": "claude",
|
|
92736
|
+
codex: "codex",
|
|
92737
|
+
cursor: "cursor-agent"
|
|
92738
|
+
};
|
|
92739
|
+
const CLI_AGENT_AUTO_FLAGS = {
|
|
92740
|
+
"claude-code": ["--dangerously-skip-permissions"],
|
|
92741
|
+
codex: ["--yolo"],
|
|
92742
|
+
cursor: ["--force"]
|
|
92743
|
+
};
|
|
92744
|
+
const resolveCursorBundledNode = () => {
|
|
92745
|
+
if (!isWindows) return null;
|
|
92746
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
92747
|
+
if (!localAppData) return null;
|
|
92748
|
+
const cursorAgentRoot = Path.join(localAppData, "cursor-agent", "versions");
|
|
92749
|
+
try {
|
|
92750
|
+
const versions = NFS.readdirSync(cursorAgentRoot);
|
|
92751
|
+
if (versions.length === 0) return null;
|
|
92752
|
+
const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
|
|
92753
|
+
for (const version of sortedVersions) try {
|
|
92754
|
+
const bundledNodePath = Path.join(cursorAgentRoot, version, "node.exe");
|
|
92755
|
+
const entryScriptPath = Path.join(cursorAgentRoot, version, "index.js");
|
|
92756
|
+
if (NFS.statSync(bundledNodePath).isFile() && NFS.statSync(entryScriptPath).isFile()) return {
|
|
92757
|
+
command: bundledNodePath,
|
|
92758
|
+
args: [entryScriptPath]
|
|
92759
|
+
};
|
|
92760
|
+
} catch {}
|
|
92761
|
+
} catch {}
|
|
92762
|
+
return null;
|
|
92763
|
+
};
|
|
92764
|
+
const resolveWindowsCmdEntryScript = (command) => {
|
|
92765
|
+
if (!isWindows) return null;
|
|
92766
|
+
const pathDirectories = (process.env.PATH ?? "").split(Path.delimiter).filter(Boolean);
|
|
92767
|
+
for (const directory of pathDirectories) {
|
|
92768
|
+
const cmdFilePath = Path.join(directory, `${command}.cmd`);
|
|
92769
|
+
try {
|
|
92770
|
+
if (!NFS.statSync(cmdFilePath).isFile()) continue;
|
|
92771
|
+
} catch {
|
|
92772
|
+
continue;
|
|
92773
|
+
}
|
|
92774
|
+
try {
|
|
92775
|
+
const entryScriptMatch = NFS.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
|
|
92776
|
+
if (!entryScriptMatch) continue;
|
|
92777
|
+
const resolvedScriptPath = Path.resolve(directory, entryScriptMatch[1]);
|
|
92778
|
+
if (NFS.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
|
|
92779
|
+
} catch {}
|
|
92780
|
+
}
|
|
92781
|
+
return null;
|
|
92782
|
+
};
|
|
92783
|
+
const spawnAgent = (command, args, cwd) => new Promise((resolve, reject) => {
|
|
92784
|
+
const child = spawn(command, [...args], {
|
|
92785
|
+
cwd,
|
|
92786
|
+
stdio: "inherit"
|
|
92787
|
+
});
|
|
92788
|
+
child.on("error", reject);
|
|
92789
|
+
child.on("close", (code) => resolve(code ?? 0));
|
|
92790
|
+
});
|
|
92791
|
+
const launchCliAgent = async (agentId, prompt, cwd) => {
|
|
92792
|
+
const binary = CLI_AGENT_BINARIES[agentId];
|
|
92793
|
+
const agentArgs = [...CLI_AGENT_AUTO_FLAGS[agentId], prompt];
|
|
92794
|
+
if (isWindows) {
|
|
92795
|
+
if (agentId === "cursor") {
|
|
92796
|
+
const cursorResolution = resolveCursorBundledNode();
|
|
92797
|
+
if (cursorResolution) return spawnAgent(cursorResolution.command, [...cursorResolution.args, ...agentArgs], cwd);
|
|
92798
|
+
}
|
|
92799
|
+
const entryScript = resolveWindowsCmdEntryScript(binary);
|
|
92800
|
+
if (entryScript) return spawnAgent(process.execPath, [entryScript, ...agentArgs], cwd);
|
|
92801
|
+
}
|
|
92802
|
+
try {
|
|
92803
|
+
return await spawnAgent(binary, agentArgs, cwd);
|
|
92804
|
+
} catch {
|
|
92805
|
+
throw new Error(`Failed to launch ${binary}`);
|
|
92806
|
+
}
|
|
92807
|
+
};
|
|
92808
|
+
const CLIPBOARD_COMMANDS = [
|
|
92809
|
+
{
|
|
92810
|
+
binary: "pbcopy",
|
|
92811
|
+
args: []
|
|
92812
|
+
},
|
|
92813
|
+
{
|
|
92814
|
+
binary: "wl-copy",
|
|
92815
|
+
args: []
|
|
92816
|
+
},
|
|
92817
|
+
{
|
|
92818
|
+
binary: "xclip",
|
|
92819
|
+
args: ["-selection", "clipboard"]
|
|
92820
|
+
},
|
|
92821
|
+
{
|
|
92822
|
+
binary: "xsel",
|
|
92823
|
+
args: ["--clipboard", "--input"]
|
|
92824
|
+
},
|
|
92825
|
+
{
|
|
92826
|
+
binary: "clip",
|
|
92827
|
+
args: []
|
|
92828
|
+
}
|
|
92829
|
+
];
|
|
92830
|
+
const copyToClipboard = (text) => {
|
|
92831
|
+
const command = CLIPBOARD_COMMANDS.find((candidate) => isCommandAvailable(candidate.binary));
|
|
92832
|
+
if (!command) return Promise.resolve(false);
|
|
92833
|
+
return new Promise((resolve) => {
|
|
92834
|
+
const child = spawn(command.binary, command.args);
|
|
92835
|
+
child.on("error", () => resolve(false));
|
|
92836
|
+
child.on("close", (code) => resolve(code === 0));
|
|
92837
|
+
child.stdin.on("error", () => resolve(false));
|
|
92838
|
+
child.stdin.end(text);
|
|
92839
|
+
});
|
|
92840
|
+
};
|
|
92841
|
+
//#endregion
|
|
92842
|
+
//#region src/cli/utils/detect-launchable-agents.ts
|
|
92843
|
+
const detectLaunchableAgents = async () => {
|
|
92844
|
+
const detected = new Set(await detectAvailableAgents());
|
|
92845
|
+
return CLI_AGENT_IDS.filter((agentId) => detected.has(agentId) && isCommandAvailable(CLI_AGENT_BINARIES[agentId]));
|
|
92846
|
+
};
|
|
92847
|
+
//#endregion
|
|
92700
92848
|
//#region src/cli/utils/action-upgrade-prompt.ts
|
|
92701
92849
|
const ACTION_UPGRADE_GATE = {
|
|
92702
92850
|
id: ACTION_UPGRADE_EVENT,
|
|
@@ -93939,116 +94087,6 @@ const installReactDoctorSkillForAgent = async (agent, projectRoot) => {
|
|
|
93939
94087
|
return result.skills.length > 0 && result.failed.length === 0;
|
|
93940
94088
|
};
|
|
93941
94089
|
//#endregion
|
|
93942
|
-
//#region src/cli/utils/launch-agent.ts
|
|
93943
|
-
const isWindows = process.platform === "win32";
|
|
93944
|
-
const CLI_AGENT_BINARIES = {
|
|
93945
|
-
"claude-code": "claude",
|
|
93946
|
-
codex: "codex",
|
|
93947
|
-
cursor: "cursor-agent"
|
|
93948
|
-
};
|
|
93949
|
-
const CLI_AGENT_AUTO_FLAGS = {
|
|
93950
|
-
"claude-code": ["--dangerously-skip-permissions"],
|
|
93951
|
-
codex: ["--yolo"],
|
|
93952
|
-
cursor: ["--force"]
|
|
93953
|
-
};
|
|
93954
|
-
const resolveCursorBundledNode = () => {
|
|
93955
|
-
if (!isWindows) return null;
|
|
93956
|
-
const localAppData = process.env.LOCALAPPDATA;
|
|
93957
|
-
if (!localAppData) return null;
|
|
93958
|
-
const cursorAgentRoot = Path.join(localAppData, "cursor-agent", "versions");
|
|
93959
|
-
try {
|
|
93960
|
-
const versions = NFS.readdirSync(cursorAgentRoot);
|
|
93961
|
-
if (versions.length === 0) return null;
|
|
93962
|
-
const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
|
|
93963
|
-
for (const version of sortedVersions) try {
|
|
93964
|
-
const bundledNodePath = Path.join(cursorAgentRoot, version, "node.exe");
|
|
93965
|
-
const entryScriptPath = Path.join(cursorAgentRoot, version, "index.js");
|
|
93966
|
-
if (NFS.statSync(bundledNodePath).isFile() && NFS.statSync(entryScriptPath).isFile()) return {
|
|
93967
|
-
command: bundledNodePath,
|
|
93968
|
-
args: [entryScriptPath]
|
|
93969
|
-
};
|
|
93970
|
-
} catch {}
|
|
93971
|
-
} catch {}
|
|
93972
|
-
return null;
|
|
93973
|
-
};
|
|
93974
|
-
const resolveWindowsCmdEntryScript = (command) => {
|
|
93975
|
-
if (!isWindows) return null;
|
|
93976
|
-
const pathDirectories = (process.env.PATH ?? "").split(Path.delimiter).filter(Boolean);
|
|
93977
|
-
for (const directory of pathDirectories) {
|
|
93978
|
-
const cmdFilePath = Path.join(directory, `${command}.cmd`);
|
|
93979
|
-
try {
|
|
93980
|
-
if (!NFS.statSync(cmdFilePath).isFile()) continue;
|
|
93981
|
-
} catch {
|
|
93982
|
-
continue;
|
|
93983
|
-
}
|
|
93984
|
-
try {
|
|
93985
|
-
const entryScriptMatch = NFS.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
|
|
93986
|
-
if (!entryScriptMatch) continue;
|
|
93987
|
-
const resolvedScriptPath = Path.resolve(directory, entryScriptMatch[1]);
|
|
93988
|
-
if (NFS.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
|
|
93989
|
-
} catch {}
|
|
93990
|
-
}
|
|
93991
|
-
return null;
|
|
93992
|
-
};
|
|
93993
|
-
const spawnAgent = (command, args, cwd) => new Promise((resolve, reject) => {
|
|
93994
|
-
const child = spawn(command, [...args], {
|
|
93995
|
-
cwd,
|
|
93996
|
-
stdio: "inherit"
|
|
93997
|
-
});
|
|
93998
|
-
child.on("error", reject);
|
|
93999
|
-
child.on("close", (code) => resolve(code ?? 0));
|
|
94000
|
-
});
|
|
94001
|
-
const launchCliAgent = async (agentId, prompt, cwd) => {
|
|
94002
|
-
const binary = CLI_AGENT_BINARIES[agentId];
|
|
94003
|
-
const agentArgs = [...CLI_AGENT_AUTO_FLAGS[agentId], prompt];
|
|
94004
|
-
if (isWindows) {
|
|
94005
|
-
if (agentId === "cursor") {
|
|
94006
|
-
const cursorResolution = resolveCursorBundledNode();
|
|
94007
|
-
if (cursorResolution) return spawnAgent(cursorResolution.command, [...cursorResolution.args, ...agentArgs], cwd);
|
|
94008
|
-
}
|
|
94009
|
-
const entryScript = resolveWindowsCmdEntryScript(binary);
|
|
94010
|
-
if (entryScript) return spawnAgent(process.execPath, [entryScript, ...agentArgs], cwd);
|
|
94011
|
-
}
|
|
94012
|
-
try {
|
|
94013
|
-
return await spawnAgent(binary, agentArgs, cwd);
|
|
94014
|
-
} catch {
|
|
94015
|
-
throw new Error(`Failed to launch ${binary}`);
|
|
94016
|
-
}
|
|
94017
|
-
};
|
|
94018
|
-
const CLIPBOARD_COMMANDS = [
|
|
94019
|
-
{
|
|
94020
|
-
binary: "pbcopy",
|
|
94021
|
-
args: []
|
|
94022
|
-
},
|
|
94023
|
-
{
|
|
94024
|
-
binary: "wl-copy",
|
|
94025
|
-
args: []
|
|
94026
|
-
},
|
|
94027
|
-
{
|
|
94028
|
-
binary: "xclip",
|
|
94029
|
-
args: ["-selection", "clipboard"]
|
|
94030
|
-
},
|
|
94031
|
-
{
|
|
94032
|
-
binary: "xsel",
|
|
94033
|
-
args: ["--clipboard", "--input"]
|
|
94034
|
-
},
|
|
94035
|
-
{
|
|
94036
|
-
binary: "clip",
|
|
94037
|
-
args: []
|
|
94038
|
-
}
|
|
94039
|
-
];
|
|
94040
|
-
const copyToClipboard = (text) => {
|
|
94041
|
-
const command = CLIPBOARD_COMMANDS.find((candidate) => isCommandAvailable(candidate.binary));
|
|
94042
|
-
if (!command) return Promise.resolve(false);
|
|
94043
|
-
return new Promise((resolve) => {
|
|
94044
|
-
const child = spawn(command.binary, command.args);
|
|
94045
|
-
child.on("error", () => resolve(false));
|
|
94046
|
-
child.on("close", (code) => resolve(code === 0));
|
|
94047
|
-
child.stdin.on("error", () => resolve(false));
|
|
94048
|
-
child.stdin.end(text);
|
|
94049
|
-
});
|
|
94050
|
-
};
|
|
94051
|
-
//#endregion
|
|
94052
94090
|
//#region src/cli/utils/handoff-to-agent.ts
|
|
94053
94091
|
const CLIPBOARD_CHOICE = "clipboard";
|
|
94054
94092
|
const SKIP_CHOICE = "skip";
|
|
@@ -94112,10 +94150,6 @@ const maybeOfferActionUpgrade = async (projectRoot) => {
|
|
|
94112
94150
|
}
|
|
94113
94151
|
if (await upgradeGitHubActionsWorkflow(workflow)) recordActionUpgradeDecision(projectRoot, "accepted");
|
|
94114
94152
|
};
|
|
94115
|
-
const detectLaunchableAgents = async () => {
|
|
94116
|
-
const detected = new Set(await detectAvailableAgents());
|
|
94117
|
-
return Object.keys(CLI_AGENT_BINARIES).filter((agentId) => detected.has(agentId) && isCommandAvailable(CLI_AGENT_BINARIES[agentId]));
|
|
94118
|
-
};
|
|
94119
94153
|
const handoffToAgent = async (input) => {
|
|
94120
94154
|
if (!input.interactive || input.diagnostics.length === 0) return;
|
|
94121
94155
|
cliLogger.break();
|
|
@@ -94430,15 +94464,29 @@ const readChangedFilesFrom = (filePath) => {
|
|
|
94430
94464
|
return [...uniqueFiles];
|
|
94431
94465
|
};
|
|
94432
94466
|
//#endregion
|
|
94433
|
-
//#region src/cli/utils/
|
|
94434
|
-
const
|
|
94467
|
+
//#region src/cli/utils/count-unique-scanned-files.ts
|
|
94468
|
+
const countUniqueScannedFiles = (results) => {
|
|
94469
|
+
const uniqueScannedFilePaths = /* @__PURE__ */ new Set();
|
|
94470
|
+
let fileCountFromScansWithoutPaths = 0;
|
|
94471
|
+
for (const result of results) {
|
|
94472
|
+
const scannedFilePaths = result.scannedFilePaths;
|
|
94473
|
+
if (scannedFilePaths && scannedFilePaths.length > 0) for (const filePath of scannedFilePaths) uniqueScannedFilePaths.add(filePath);
|
|
94474
|
+
else fileCountFromScansWithoutPaths += result.scannedFileCount ?? result.project.sourceFileCount;
|
|
94475
|
+
}
|
|
94476
|
+
return uniqueScannedFilePaths.size + fileCountFromScansWithoutPaths;
|
|
94477
|
+
};
|
|
94435
94478
|
//#endregion
|
|
94436
|
-
//#region src/cli/utils/
|
|
94437
|
-
const
|
|
94479
|
+
//#region src/cli/utils/score-band-label.ts
|
|
94480
|
+
const scoreBandLabel = (score) => {
|
|
94438
94481
|
if (score >= 75) return "Great";
|
|
94439
94482
|
if (score >= 50) return "OK";
|
|
94440
94483
|
return "Needs work";
|
|
94441
94484
|
};
|
|
94485
|
+
//#endregion
|
|
94486
|
+
//#region src/cli/utils/filter-scans-for-surface.ts
|
|
94487
|
+
const filterScansForSurface = (completedScans, surface) => completedScans.flatMap((scan) => filterDiagnosticsForSurface([...scan.result.diagnostics], surface, scan.config));
|
|
94488
|
+
//#endregion
|
|
94489
|
+
//#region src/cli/utils/render-multi-project-summary.ts
|
|
94442
94490
|
const buildSummaryLine = (entry, longestProjectNameLength) => {
|
|
94443
94491
|
const paddedName = entry.projectName.padEnd(longestProjectNameLength);
|
|
94444
94492
|
const nameRendering = entry.score !== null ? colorizeByScore(paddedName, entry.score) : highlighter.dim(paddedName);
|
|
@@ -94447,7 +94495,7 @@ const buildSummaryLine = (entry, longestProjectNameLength) => {
|
|
|
94447
94495
|
return ` ${nameRendering} ${highlighter.dim("no score")} ${highlighter.dim(issueLabel)}`;
|
|
94448
94496
|
}
|
|
94449
94497
|
const scoreRendering = colorizeByScore(String(entry.score).padStart(3), entry.score);
|
|
94450
|
-
const label = colorizeByScore(
|
|
94498
|
+
const label = colorizeByScore(scoreBandLabel(entry.score), entry.score);
|
|
94451
94499
|
const issuesParts = [];
|
|
94452
94500
|
if (entry.errorCount > 0) issuesParts.push(highlighter.error(`${entry.errorCount} ${entry.errorCount === 1 ? "error" : "errors"}`));
|
|
94453
94501
|
const warningCount = entry.issueCount - entry.errorCount;
|
|
@@ -94469,14 +94517,7 @@ const printMultiProjectSummary = (input) => gen(function* () {
|
|
|
94469
94517
|
const projectRootByDiagnostic = /* @__PURE__ */ new WeakMap();
|
|
94470
94518
|
for (const scan of completedScans) for (const diagnostic of scan.result.diagnostics) projectRootByDiagnostic.set(diagnostic, scan.result.project.rootDirectory);
|
|
94471
94519
|
const resolveDiagnosticSourceRoot = (diagnostic) => projectRootByDiagnostic.get(diagnostic) ?? "";
|
|
94472
|
-
const
|
|
94473
|
-
let fileCountFromScansWithoutPaths = 0;
|
|
94474
|
-
for (const scan of completedScans) {
|
|
94475
|
-
const scannedFilePaths = scan.result.scannedFilePaths;
|
|
94476
|
-
if (scannedFilePaths && scannedFilePaths.length > 0) for (const filePath of scannedFilePaths) uniqueScannedFilePaths.add(filePath);
|
|
94477
|
-
else fileCountFromScansWithoutPaths += scan.result.scannedFileCount ?? scan.result.project.sourceFileCount;
|
|
94478
|
-
}
|
|
94479
|
-
const totalScannedFileCount = uniqueScannedFilePaths.size + fileCountFromScansWithoutPaths;
|
|
94520
|
+
const totalScannedFileCount = countUniqueScannedFiles(completedScans.map((scan) => scan.result));
|
|
94480
94521
|
yield* log(`${highlighter.success("✔")} Scanned ${totalScannedFileCount} ${totalScannedFileCount === 1 ? "file" : "files"} in ${formatElapsedTime(totalElapsedMilliseconds)}`);
|
|
94481
94522
|
if (displayDiagnostics.length > 0) {
|
|
94482
94523
|
yield* log("");
|
|
@@ -94748,10 +94789,14 @@ const parseFileLineArgument = (rawArgument) => {
|
|
|
94748
94789
|
};
|
|
94749
94790
|
//#endregion
|
|
94750
94791
|
//#region src/cli/utils/select-projects.ts
|
|
94751
|
-
const
|
|
94792
|
+
const discoverWorkspacePackages = (rootDirectory) => {
|
|
94752
94793
|
const hasRootPackageJson = isFile(Path.join(rootDirectory, "package.json"));
|
|
94753
|
-
|
|
94754
|
-
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory)))
|
|
94794
|
+
const packages = listWorkspacePackages(rootDirectory);
|
|
94795
|
+
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverReactSubprojects(rootDirectory);
|
|
94796
|
+
return packages;
|
|
94797
|
+
};
|
|
94798
|
+
const selectProjects = async (rootDirectory, projectFlag, skipPrompts, configProjects) => {
|
|
94799
|
+
const packages = discoverWorkspacePackages(rootDirectory);
|
|
94755
94800
|
if (projectFlag) return resolveProjectFlag(projectFlag, packages, rootDirectory);
|
|
94756
94801
|
const configRequestedNames = (configProjects ?? []).map((requestedName) => requestedName.trim()).filter((requestedName) => requestedName.length > 0);
|
|
94757
94802
|
if (configRequestedNames.length > 0) {
|
|
@@ -94880,6 +94925,14 @@ const projectManifestChanged = (rootDirectory, projectDirectory, diffInfo) => {
|
|
|
94880
94925
|
return diffInfo.changedFiles.some((filePath) => toForwardSlashes(filePath) === manifestPath);
|
|
94881
94926
|
};
|
|
94882
94927
|
//#endregion
|
|
94928
|
+
//#region src/cli/utils/should-fail-scan-gate.ts
|
|
94929
|
+
const shouldFailScanGate = (input) => {
|
|
94930
|
+
if (input.blockingLevel === "none") return false;
|
|
94931
|
+
if (input.scans.some(({ result }) => hasLintHardFailure(result))) return true;
|
|
94932
|
+
if (input.diagnosticsAreGateExempt === true) return false;
|
|
94933
|
+
return shouldBlockCi(filterScansForSurface(input.scans, "ciFailure"), input.blockingLevel);
|
|
94934
|
+
};
|
|
94935
|
+
//#endregion
|
|
94883
94936
|
//#region src/cli/utils/warn-deprecated-fail-on.ts
|
|
94884
94937
|
const warnDeprecatedFailOn = (flags, userConfig) => {
|
|
94885
94938
|
const usedFlag = flags.failOn !== void 0;
|
|
@@ -95006,12 +95059,11 @@ const finalizeScans = (input) => {
|
|
|
95006
95059
|
}));
|
|
95007
95060
|
}
|
|
95008
95061
|
const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
|
|
95009
|
-
if (
|
|
95010
|
-
|
|
95011
|
-
|
|
95012
|
-
|
|
95013
|
-
|
|
95014
|
-
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
|
|
95062
|
+
if (shouldFailScanGate({
|
|
95063
|
+
scans: input.completedScans,
|
|
95064
|
+
blockingLevel,
|
|
95065
|
+
diagnosticsAreGateExempt: input.isScoreOnly || baselineDegraded
|
|
95066
|
+
})) process.exitCode = 1;
|
|
95015
95067
|
};
|
|
95016
95068
|
const buildChangedFilesDiffInfo = (changedFiles) => ({
|
|
95017
95069
|
currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
|
|
@@ -95287,13 +95339,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
95287
95339
|
completedScans.push(scanOutcome);
|
|
95288
95340
|
}
|
|
95289
95341
|
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
95290
|
-
const
|
|
95342
|
+
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
95291
95343
|
await runPromise(printMultiProjectSummary({
|
|
95292
95344
|
completedScans,
|
|
95293
95345
|
categoryFilters,
|
|
95294
95346
|
verbose: Boolean(flags.verbose),
|
|
95295
95347
|
outputDirectory: flags.outputDir,
|
|
95296
|
-
isOffline: !
|
|
95348
|
+
isOffline: !showShareLink,
|
|
95297
95349
|
projectName: Path.basename(resolvedDirectory),
|
|
95298
95350
|
totalElapsedMilliseconds: performance$1.now() - scanLoopStartTime
|
|
95299
95351
|
}));
|
|
@@ -96268,6 +96320,20 @@ const COMMAND_FLAG_SPECS = new Map([
|
|
|
96268
96320
|
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
96269
96321
|
shortOptionsWithoutValues: new Set(["-h"]),
|
|
96270
96322
|
shortOptionsWithRequiredValues: new Set(["-c"])
|
|
96323
|
+
}],
|
|
96324
|
+
["experimental-tui", {
|
|
96325
|
+
longOptionsWithoutValues: new Set([
|
|
96326
|
+
"--color",
|
|
96327
|
+
"--help",
|
|
96328
|
+
"--no-color",
|
|
96329
|
+
"--no-dead-code",
|
|
96330
|
+
"--no-score",
|
|
96331
|
+
"--yes"
|
|
96332
|
+
]),
|
|
96333
|
+
longOptionsWithRequiredValues: new Set(["--blocking", "--project"]),
|
|
96334
|
+
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
96335
|
+
shortOptionsWithoutValues: new Set(["-h", "-y"]),
|
|
96336
|
+
shortOptionsWithRequiredValues: new Set(["-p"])
|
|
96271
96337
|
}]
|
|
96272
96338
|
]);
|
|
96273
96339
|
const isFlagLike = (argument) => argument.startsWith("-") && argument !== "-";
|
|
@@ -96447,6 +96513,35 @@ rules.command("category <category> <severity>").description("Set the severity fo
|
|
|
96447
96513
|
rules.command("ignore-tag <tag>").description("Skip a whole rule family by tag before linting (e.g. design)").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesIgnoreTagAction(tag, command.optsWithGlobals()));
|
|
96448
96514
|
rules.command("unignore-tag <tag>").description("Stop ignoring a tag previously skipped via ignore-tag").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesUnignoreTagAction(tag, command.optsWithGlobals()));
|
|
96449
96515
|
program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
|
|
96516
|
+
program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(async (directory = ".", _localOptions, command) => {
|
|
96517
|
+
const options = command.optsWithGlobals();
|
|
96518
|
+
const deadCode = options.deadCode === false ? false : void 0;
|
|
96519
|
+
const noScore = options.score === false ? true : void 0;
|
|
96520
|
+
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
96521
|
+
if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
|
|
96522
|
+
await inspectAction(directory, {
|
|
96523
|
+
deadCode,
|
|
96524
|
+
score: options.score === false ? false : void 0,
|
|
96525
|
+
project: options.project,
|
|
96526
|
+
yes: options.yes,
|
|
96527
|
+
blocking: options.blocking
|
|
96528
|
+
});
|
|
96529
|
+
return;
|
|
96530
|
+
}
|
|
96531
|
+
recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
|
|
96532
|
+
const { runScanApp } = await import("./run-scan-app-DZ5jGWFf.js");
|
|
96533
|
+
const { shouldFail } = await runScanApp({
|
|
96534
|
+
directory,
|
|
96535
|
+
options: {
|
|
96536
|
+
deadCode,
|
|
96537
|
+
noScore
|
|
96538
|
+
},
|
|
96539
|
+
projectFlag: options.project,
|
|
96540
|
+
skipPrompts: options.yes ?? false,
|
|
96541
|
+
blocking: options.blocking
|
|
96542
|
+
});
|
|
96543
|
+
if (shouldFail) process.exitCode = 1;
|
|
96544
|
+
});
|
|
96450
96545
|
process.stdout.on("error", (error) => {
|
|
96451
96546
|
if (error.code === "EPIPE") process.exit(0);
|
|
96452
96547
|
});
|
|
@@ -96473,7 +96568,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
96473
96568
|
handleError(error, { sentryEventId });
|
|
96474
96569
|
});
|
|
96475
96570
|
//#endregion
|
|
96476
|
-
export {};
|
|
96571
|
+
export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, succeed$3 as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, getCategoryImpact as z };
|
|
96477
96572
|
|
|
96478
96573
|
//# sourceMappingURL=cli.js.map
|
|
96479
|
-
//# debugId=
|
|
96574
|
+
//# debugId=2fb9bb5f-dfff-55d9-9d36-af738b2c29ea
|