react-doctor 0.7.9-dev.e86bc57 → 0.8.0-dev.0618ff6
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 +136 -57
- package/dist/index.d.ts +14 -12
- package/dist/index.js +123 -35
- package/dist/lsp.js +124 -53
- package/package.json +6 -6
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]="4433c20d-e5c5-50db-ab5b-0ff72ef6f226")}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";
|
|
@@ -36242,7 +36242,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
36242
36242
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36243
36243
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36244
36244
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36245
|
-
const CORE_PACKAGE_VERSION = "0.
|
|
36245
|
+
const CORE_PACKAGE_VERSION = "0.8.0";
|
|
36246
36246
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36247
36247
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36248
36248
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -36252,6 +36252,53 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
36252
36252
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
36253
36253
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
36254
36254
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
36255
|
+
const lastSourceMappingUrl = (fileContent) => {
|
|
36256
|
+
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
36257
|
+
return lastReference ? lastReference[1] : null;
|
|
36258
|
+
};
|
|
36259
|
+
const mapTargetsSource = (mapContent, input) => {
|
|
36260
|
+
const parsedMap = JSON.parse(mapContent);
|
|
36261
|
+
if (typeof parsedMap !== "object" || parsedMap === null) return false;
|
|
36262
|
+
const { file, sources } = parsedMap;
|
|
36263
|
+
if (file !== input.expectedFile) return false;
|
|
36264
|
+
if (!Array.isArray(sources) || sources.length !== 1 || typeof sources[0] !== "string") return false;
|
|
36265
|
+
return Path.posix.normalize(Path.posix.join(input.mapDirectory, sources[0])) === input.sourcePath;
|
|
36266
|
+
};
|
|
36267
|
+
const quartetProvesEmit = (stem, sourcePath, readFileText) => {
|
|
36268
|
+
try {
|
|
36269
|
+
const emitBaseName = Path.posix.basename(stem);
|
|
36270
|
+
const mapDirectory = Path.posix.dirname(stem);
|
|
36271
|
+
return lastSourceMappingUrl(readFileText(`${stem}.js`)) === `${emitBaseName}.js.map` && lastSourceMappingUrl(readFileText(`${stem}.d.ts`)) === `${emitBaseName}.d.ts.map` && mapTargetsSource(readFileText(`${stem}.js.map`), {
|
|
36272
|
+
expectedFile: `${emitBaseName}.js`,
|
|
36273
|
+
mapDirectory,
|
|
36274
|
+
sourcePath
|
|
36275
|
+
}) && mapTargetsSource(readFileText(`${stem}.d.ts.map`), {
|
|
36276
|
+
expectedFile: `${emitBaseName}.d.ts`,
|
|
36277
|
+
mapDirectory,
|
|
36278
|
+
sourcePath
|
|
36279
|
+
});
|
|
36280
|
+
} catch {
|
|
36281
|
+
return false;
|
|
36282
|
+
}
|
|
36283
|
+
};
|
|
36284
|
+
const collectTypeScriptEmitDuplicateJsPaths = (input) => {
|
|
36285
|
+
const untracked = new Set(input.untrackedPaths);
|
|
36286
|
+
const duplicateJsPaths = /* @__PURE__ */ new Set();
|
|
36287
|
+
for (const jsPath of untracked) {
|
|
36288
|
+
if (!jsPath.endsWith(".js")) continue;
|
|
36289
|
+
const stem = jsPath.slice(0, -3);
|
|
36290
|
+
if (![
|
|
36291
|
+
jsPath,
|
|
36292
|
+
`${stem}.js.map`,
|
|
36293
|
+
`${stem}.d.ts`,
|
|
36294
|
+
`${stem}.d.ts.map`
|
|
36295
|
+
].every((emitPath) => untracked.has(emitPath) && !input.trackedPaths.has(emitPath))) continue;
|
|
36296
|
+
const sourcePath = [`${stem}.ts`, `${stem}.tsx`].find((candidatePath) => input.trackedPaths.has(candidatePath));
|
|
36297
|
+
if (!sourcePath) continue;
|
|
36298
|
+
if (quartetProvesEmit(stem, sourcePath, input.readFileText)) duplicateJsPaths.add(jsPath);
|
|
36299
|
+
}
|
|
36300
|
+
return duplicateJsPaths;
|
|
36301
|
+
};
|
|
36255
36302
|
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
36256
36303
|
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
36257
36304
|
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
@@ -36319,20 +36366,29 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
36319
36366
|
}
|
|
36320
36367
|
return entries;
|
|
36321
36368
|
};
|
|
36322
|
-
const
|
|
36369
|
+
const gitLsFiles = (rootDirectory, flags) => {
|
|
36323
36370
|
const result = spawnSync("git", [
|
|
36324
36371
|
"ls-files",
|
|
36325
36372
|
"-z",
|
|
36326
|
-
|
|
36327
|
-
"--others",
|
|
36328
|
-
"--exclude-standard"
|
|
36373
|
+
...flags
|
|
36329
36374
|
], {
|
|
36330
36375
|
cwd: rootDirectory,
|
|
36331
36376
|
encoding: "utf-8",
|
|
36332
36377
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
36333
36378
|
});
|
|
36334
36379
|
if (result.error || result.status !== 0) return null;
|
|
36335
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0
|
|
36380
|
+
return result.stdout.split("\0").filter((filePath) => filePath.length > 0);
|
|
36381
|
+
};
|
|
36382
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
36383
|
+
const trackedPaths = gitLsFiles(rootDirectory, ["--cached"]);
|
|
36384
|
+
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
36385
|
+
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
36386
|
+
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
36387
|
+
trackedPaths: new Set(trackedPaths),
|
|
36388
|
+
untrackedPaths,
|
|
36389
|
+
readFileText: (relativePath) => NFS.readFileSync(Path.resolve(rootDirectory, relativePath), "utf-8")
|
|
36390
|
+
});
|
|
36391
|
+
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
36336
36392
|
};
|
|
36337
36393
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
36338
36394
|
const filePaths = [];
|
|
@@ -36532,11 +36588,8 @@ const frameworkMergeRank = (framework) => {
|
|
|
36532
36588
|
if (framework === "unknown") return 3;
|
|
36533
36589
|
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
36534
36590
|
};
|
|
36535
|
-
const
|
|
36536
|
-
|
|
36537
|
-
"react-compiler-runtime",
|
|
36538
|
-
"eslint-plugin-react-compiler"
|
|
36539
|
-
]);
|
|
36591
|
+
const REACT_COMPILER_TRANSFORM_PACKAGES = new Set(["babel-plugin-react-compiler", "react-compiler-runtime"]);
|
|
36592
|
+
const REACT_COMPILER_LINT_PACKAGES = new Set(["eslint-plugin-react-compiler"]);
|
|
36540
36593
|
const NEXT_CONFIG_FILENAMES = [
|
|
36541
36594
|
"next.config.js",
|
|
36542
36595
|
"next.config.mjs",
|
|
@@ -36566,16 +36619,29 @@ const EXPO_APP_CONFIG_FILENAMES = [
|
|
|
36566
36619
|
"app.config.js",
|
|
36567
36620
|
"app.config.ts"
|
|
36568
36621
|
];
|
|
36569
|
-
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|
|
|
36622
|
+
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|["']react-compiler["']/;
|
|
36570
36623
|
const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
|
|
36571
36624
|
const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
|
|
36572
|
-
const hasCompilerPackage = (packageJson) => {
|
|
36625
|
+
const hasCompilerPackage = (packageJson, compilerPackages) => {
|
|
36573
36626
|
const allDependencies = {
|
|
36574
36627
|
...packageJson.peerDependencies,
|
|
36575
36628
|
...packageJson.dependencies,
|
|
36576
36629
|
...packageJson.devDependencies
|
|
36577
36630
|
};
|
|
36578
|
-
return Object.keys(allDependencies).some((packageName) =>
|
|
36631
|
+
return Object.keys(allDependencies).some((packageName) => compilerPackages.has(packageName));
|
|
36632
|
+
};
|
|
36633
|
+
const hasCompilerPackageInAncestors = (directory, compilerPackages) => {
|
|
36634
|
+
if (isProjectBoundary(directory)) return false;
|
|
36635
|
+
let ancestorDirectory = Path.dirname(directory);
|
|
36636
|
+
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
36637
|
+
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
36638
|
+
if (isFile(ancestorPackagePath)) {
|
|
36639
|
+
if (hasCompilerPackage(readPackageJson$1(ancestorPackagePath), compilerPackages)) return true;
|
|
36640
|
+
}
|
|
36641
|
+
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
36642
|
+
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
36643
|
+
}
|
|
36644
|
+
return false;
|
|
36579
36645
|
};
|
|
36580
36646
|
const hasCompilerInConfigFile = (filePath) => {
|
|
36581
36647
|
if (!isFile(filePath)) return false;
|
|
@@ -36584,23 +36650,14 @@ const hasCompilerInConfigFile = (filePath) => {
|
|
|
36584
36650
|
};
|
|
36585
36651
|
const hasCompilerInConfigFiles = (directory, filenames) => filenames.some((filename) => hasCompilerInConfigFile(Path.join(directory, filename)));
|
|
36586
36652
|
const detectReactCompiler = (directory, packageJson) => {
|
|
36587
|
-
if (hasCompilerPackage(packageJson)) return true;
|
|
36653
|
+
if (hasCompilerPackage(packageJson, REACT_COMPILER_TRANSFORM_PACKAGES)) return true;
|
|
36588
36654
|
if (hasCompilerInConfigFiles(directory, NEXT_CONFIG_FILENAMES)) return true;
|
|
36589
36655
|
if (hasCompilerInConfigFiles(directory, BABEL_CONFIG_FILENAMES)) return true;
|
|
36590
36656
|
if (hasCompilerInConfigFiles(directory, VITE_CONFIG_FILENAMES)) return true;
|
|
36591
36657
|
if (hasCompilerInConfigFiles(directory, EXPO_APP_CONFIG_FILENAMES)) return true;
|
|
36592
|
-
|
|
36593
|
-
let ancestorDirectory = Path.dirname(directory);
|
|
36594
|
-
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
36595
|
-
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
36596
|
-
if (isFile(ancestorPackagePath)) {
|
|
36597
|
-
if (hasCompilerPackage(readPackageJson$1(ancestorPackagePath))) return true;
|
|
36598
|
-
}
|
|
36599
|
-
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
36600
|
-
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
36601
|
-
}
|
|
36602
|
-
return false;
|
|
36658
|
+
return hasCompilerPackageInAncestors(directory, REACT_COMPILER_TRANSFORM_PACKAGES);
|
|
36603
36659
|
};
|
|
36660
|
+
const detectReactCompilerLintPlugin = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_LINT_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_LINT_PACKAGES);
|
|
36604
36661
|
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
36605
36662
|
const filePath = Path.join(directory, filename);
|
|
36606
36663
|
return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
|
|
@@ -37564,6 +37621,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37564
37621
|
framework: "unknown",
|
|
37565
37622
|
hasTypeScript: hasOwnTsConfig,
|
|
37566
37623
|
hasReactCompiler: false,
|
|
37624
|
+
hasReactCompilerLintPlugin: false,
|
|
37567
37625
|
hasTanStackQuery: false,
|
|
37568
37626
|
hasSsrDependency: false,
|
|
37569
37627
|
preactVersion: null,
|
|
@@ -37684,6 +37742,7 @@ const discoverProject = (directory) => {
|
|
|
37684
37742
|
framework,
|
|
37685
37743
|
hasTypeScript,
|
|
37686
37744
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
37745
|
+
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
37687
37746
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
37688
37747
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
37689
37748
|
preactVersion,
|
|
@@ -39169,8 +39228,10 @@ const isDiagnosticSurface = (value) => typeof value === "string" && DIAGNOSTIC_S
|
|
|
39169
39228
|
* to the local CLI so developers see them while editing, but they're
|
|
39170
39229
|
* removed from the PR comment surface, the score, and the CI gate so
|
|
39171
39230
|
* they can't bury real React findings or fail a build over a Tailwind
|
|
39172
|
-
* shorthand.
|
|
39173
|
-
*
|
|
39231
|
+
* shorthand. Diagnostics from test and story files likewise remain in
|
|
39232
|
+
* raw output while staying out of the production-health score and CI
|
|
39233
|
+
* gate. Override per-surface via `config.surfaces.<surface>` to promote
|
|
39234
|
+
* individual rules back in by tag, category, or rule id.
|
|
39174
39235
|
*/
|
|
39175
39236
|
const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
39176
39237
|
cli: [],
|
|
@@ -42001,6 +42062,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
42001
42062
|
if (resolved.includeRuleKeys.has(ruleKey)) return true;
|
|
42002
42063
|
if (resolved.includeCategories.has(category)) return true;
|
|
42003
42064
|
if (intersects(tags, resolved.includeTags)) return true;
|
|
42065
|
+
if (diagnostic.fileContext !== void 0 && (surface === "score" || surface === "ciFailure")) return false;
|
|
42004
42066
|
if (resolved.excludeRuleKeys.has(ruleKey)) return false;
|
|
42005
42067
|
if (resolved.excludeCategories.has(category)) return false;
|
|
42006
42068
|
if (intersects(tags, resolved.excludeTags)) return false;
|
|
@@ -42425,6 +42487,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42425
42487
|
}
|
|
42426
42488
|
const diff = yield* runGit(input.directory, [
|
|
42427
42489
|
"diff",
|
|
42490
|
+
"--no-ext-diff",
|
|
42428
42491
|
"-z",
|
|
42429
42492
|
"--name-only",
|
|
42430
42493
|
"--diff-filter=ACMR",
|
|
@@ -42471,6 +42534,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42471
42534
|
if (resolvedCurrentBranch !== null && resolvedCurrentBranch === baseBranch) {
|
|
42472
42535
|
const uncommitted = yield* runGit(directory, [
|
|
42473
42536
|
"diff",
|
|
42537
|
+
"--no-ext-diff",
|
|
42474
42538
|
"-z",
|
|
42475
42539
|
"--name-only",
|
|
42476
42540
|
"--diff-filter=ACMR",
|
|
@@ -42497,6 +42561,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42497
42561
|
if (mergeBaseRef === null) return null;
|
|
42498
42562
|
const diff = yield* runGit(directory, [
|
|
42499
42563
|
"diff",
|
|
42564
|
+
"--no-ext-diff",
|
|
42500
42565
|
"-z",
|
|
42501
42566
|
"--name-only",
|
|
42502
42567
|
"--diff-filter=ACMR",
|
|
@@ -42514,6 +42579,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42514
42579
|
}).pipe(withSpan("Git.diffSelection")),
|
|
42515
42580
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
42516
42581
|
"diff",
|
|
42582
|
+
"--no-ext-diff",
|
|
42517
42583
|
"--cached",
|
|
42518
42584
|
"-z",
|
|
42519
42585
|
"--name-only",
|
|
@@ -42559,6 +42625,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42559
42625
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
42560
42626
|
const result = yield* runGit(directory, [
|
|
42561
42627
|
"diff",
|
|
42628
|
+
"--no-ext-diff",
|
|
42562
42629
|
"--unified=0",
|
|
42563
42630
|
"--diff-filter=ACMR",
|
|
42564
42631
|
"--relative",
|
|
@@ -42963,7 +43030,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
42963
43030
|
return enabled;
|
|
42964
43031
|
};
|
|
42965
43032
|
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
42966
|
-
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
43033
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
42967
43034
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
42968
43035
|
const jsPlugins = [];
|
|
42969
43036
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -43926,6 +43993,8 @@ const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
|
43926
43993
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
43927
43994
|
const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
43928
43995
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
43996
|
+
const REACT_ERROR_BOUNDARY_TITLE = "JSX render errors need an Error Boundary";
|
|
43997
|
+
const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown while the JSX child renders. Use an Error Boundary instead.";
|
|
43929
43998
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
43930
43999
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
43931
44000
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
@@ -43969,6 +44038,7 @@ const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.categor
|
|
|
43969
44038
|
const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
|
|
43970
44039
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
43971
44040
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
44041
|
+
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
43972
44042
|
return rule === "todo" ? REACT_COMPILER_TODO_TITLE : REACT_COMPILER_TITLE;
|
|
43973
44043
|
};
|
|
43974
44044
|
const cleanDiagnosticMessage = (message, help, plugin, rule, project) => {
|
|
@@ -43987,6 +44057,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
43987
44057
|
};
|
|
43988
44058
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
43989
44059
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
44060
|
+
if (rule === "error-boundaries") return {
|
|
44061
|
+
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
44062
|
+
help: reasonDetail || help
|
|
44063
|
+
};
|
|
43990
44064
|
return {
|
|
43991
44065
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
43992
44066
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -44008,7 +44082,10 @@ const parseRuleCode = (code) => {
|
|
|
44008
44082
|
rule: match[2]
|
|
44009
44083
|
};
|
|
44010
44084
|
};
|
|
44011
|
-
const resolveDiagnosticCategory = (plugin, rule) =>
|
|
44085
|
+
const resolveDiagnosticCategory = (plugin, rule) => {
|
|
44086
|
+
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
44087
|
+
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
44088
|
+
};
|
|
44012
44089
|
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(reactDoctorPlugin.rules[rule]?.matchByOccurrence);
|
|
44013
44090
|
/**
|
|
44014
44091
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
@@ -44821,7 +44898,7 @@ const runOxlint = async (options) => {
|
|
|
44821
44898
|
};
|
|
44822
44899
|
}
|
|
44823
44900
|
};
|
|
44824
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
44901
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
44825
44902
|
const rulesetHash = computeRulesetHash({
|
|
44826
44903
|
config: buildConfig({
|
|
44827
44904
|
extendsPaths: [],
|
|
@@ -45263,6 +45340,14 @@ var Reporter = class Reporter extends Service()("react-doctor/Reporter") {
|
|
|
45263
45340
|
});
|
|
45264
45341
|
}));
|
|
45265
45342
|
};
|
|
45343
|
+
const HOME_DIRECTORY = os.homedir();
|
|
45344
|
+
const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
|
|
45345
|
+
const scrubSensitivePaths = (text) => {
|
|
45346
|
+
let scrubbed = text;
|
|
45347
|
+
if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
|
|
45348
|
+
for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
|
|
45349
|
+
return scrubbed;
|
|
45350
|
+
};
|
|
45266
45351
|
const ScoreApiResponseSchema = Struct({
|
|
45267
45352
|
score: Number$1,
|
|
45268
45353
|
label: String$1,
|
|
@@ -45277,7 +45362,10 @@ const ScoreApiResponseSchema = Struct({
|
|
|
45277
45362
|
})))
|
|
45278
45363
|
});
|
|
45279
45364
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
45280
|
-
const
|
|
45365
|
+
const sanitizeScoreDiagnostics = (diagnostics) => diagnostics.map(({ filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => ({
|
|
45366
|
+
...rest,
|
|
45367
|
+
filePath: redactSensitiveText(scrubSensitivePaths(filePath))
|
|
45368
|
+
}));
|
|
45281
45369
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
45282
45370
|
const describeFailure = (error) => {
|
|
45283
45371
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -45290,7 +45378,7 @@ const calculateScore = async (diagnostics, options = {}) => {
|
|
|
45290
45378
|
const requestUrl = options.isCi ? `${SCORE_API_URL}?ci=1` : SCORE_API_URL;
|
|
45291
45379
|
try {
|
|
45292
45380
|
const compressedBody = gzipSync(JSON.stringify({
|
|
45293
|
-
diagnostics:
|
|
45381
|
+
diagnostics: sanitizeScoreDiagnostics(diagnostics),
|
|
45294
45382
|
...options.metadata?.repo ? { repo: options.metadata.repo } : {},
|
|
45295
45383
|
...options.metadata?.sha ? { sha: options.metadata.sha } : {},
|
|
45296
45384
|
...options.metadata?.framework ? { framework: options.metadata.framework } : {},
|
|
@@ -47135,7 +47223,7 @@ const makeNoopConsole = () => ({
|
|
|
47135
47223
|
});
|
|
47136
47224
|
//#endregion
|
|
47137
47225
|
//#region src/cli/utils/version.ts
|
|
47138
|
-
const VERSION = "0.
|
|
47226
|
+
const VERSION = "0.8.0-dev.0618ff6";
|
|
47139
47227
|
//#endregion
|
|
47140
47228
|
//#region src/cli/utils/json-mode.ts
|
|
47141
47229
|
let context = null;
|
|
@@ -47221,23 +47309,6 @@ const getRunId = () => {
|
|
|
47221
47309
|
return cachedRunId;
|
|
47222
47310
|
};
|
|
47223
47311
|
//#endregion
|
|
47224
|
-
//#region src/cli/utils/scrub-sensitive-text.ts
|
|
47225
|
-
const HOME_DIRECTORY = os.homedir();
|
|
47226
|
-
const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
|
|
47227
|
-
/**
|
|
47228
|
-
* Replaces the user's home directory (and generic `/Users|/home|C:\Users\<name>`
|
|
47229
|
-
* roots) with `~` so absolute paths can't be tied back to an individual. Keeps
|
|
47230
|
-
* the path's relative structure intact, which stays useful for debugging while
|
|
47231
|
-
* dropping the personally-identifying prefix. Idempotent — re-running on an
|
|
47232
|
-
* already-scrubbed `~/...` path is a no-op.
|
|
47233
|
-
*/
|
|
47234
|
-
const scrubSensitivePaths = (text) => {
|
|
47235
|
-
let scrubbed = text;
|
|
47236
|
-
if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
|
|
47237
|
-
for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
|
|
47238
|
-
return scrubbed;
|
|
47239
|
-
};
|
|
47240
|
-
//#endregion
|
|
47241
47312
|
//#region src/cli/utils/build-run-context.ts
|
|
47242
47313
|
const ROOT_SUBCOMMANDS = new Set(["install", "setup"]);
|
|
47243
47314
|
const detectInvokedVia = () => {
|
|
@@ -47504,13 +47575,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47504
47575
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47505
47576
|
* standard `SENTRY_RELEASE` override.
|
|
47506
47577
|
*/
|
|
47507
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.
|
|
47578
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.0-dev.0618ff6`;
|
|
47508
47579
|
/**
|
|
47509
47580
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47510
47581
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47511
47582
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47512
47583
|
*/
|
|
47513
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.
|
|
47584
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.0-dev.0618ff6") ? "development" : "production");
|
|
47514
47585
|
/**
|
|
47515
47586
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47516
47587
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -60005,6 +60076,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60005
60076
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
60006
60077
|
const blockingLevel = resolveTelemetryBlocking(input.userConfig);
|
|
60007
60078
|
const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
|
|
60079
|
+
const gateDiagnosticSet = new Set(gateDiagnostics);
|
|
60008
60080
|
const complete = isInspectResultComplete(result);
|
|
60009
60081
|
const didLintHardFail = hasLintHardFailure(result);
|
|
60010
60082
|
const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
|
|
@@ -60027,11 +60099,13 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60027
60099
|
const largestRuleBucket = buildRuleBlastRadii(result.diagnostics)[0] ?? null;
|
|
60028
60100
|
let diagnosticsInTestFiles = 0;
|
|
60029
60101
|
let diagnosticsInStoryFiles = 0;
|
|
60102
|
+
let nonProductionGateExcluded = 0;
|
|
60030
60103
|
const diagnosticSiteCounts = /* @__PURE__ */ new Map();
|
|
60031
60104
|
const findingsPerFixGroup = /* @__PURE__ */ new Map();
|
|
60032
60105
|
for (const diagnostic of result.diagnostics) {
|
|
60033
60106
|
if (diagnostic.fileContext === "test") diagnosticsInTestFiles += 1;
|
|
60034
60107
|
if (diagnostic.fileContext === "story") diagnosticsInStoryFiles += 1;
|
|
60108
|
+
if (diagnostic.fileContext !== void 0 && !gateDiagnosticSet.has(diagnostic)) nonProductionGateExcluded += 1;
|
|
60035
60109
|
const diagnosticSite = JSON.stringify([
|
|
60036
60110
|
diagnostic.filePath,
|
|
60037
60111
|
diagnostic.line,
|
|
@@ -60080,6 +60154,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60080
60154
|
affectedFiles: summary.affectedFileCount,
|
|
60081
60155
|
inTestFiles: diagnosticsInTestFiles,
|
|
60082
60156
|
inStoryFiles: diagnosticsInStoryFiles,
|
|
60157
|
+
nonProductionGateExcluded,
|
|
60083
60158
|
distinctRules: countByRule.size,
|
|
60084
60159
|
topRule,
|
|
60085
60160
|
sameSiteOccurrences,
|
|
@@ -61919,7 +61994,9 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
61919
61994
|
yield* log(highlighter.gray(` ${demotedDiagnosticCount} demoted from the ${options.outputSurface} surface (e.g. design cleanup) — run \`npx react-doctor@latest .\` locally for the full list.`));
|
|
61920
61995
|
yield* log("");
|
|
61921
61996
|
}
|
|
61922
|
-
const
|
|
61997
|
+
const scoreDiagnostics = filterDiagnosticsForSurface([...diagnostics], "score", userConfig);
|
|
61998
|
+
const displayedScoreDiagnostics = filterDiagnosticsForSurface([...printedDiagnostics], "score", userConfig);
|
|
61999
|
+
const potentialScore = score ? yield* promise(() => computeProjectedScore(displayedScoreDiagnostics, scoreDiagnostics, score)) : null;
|
|
61923
62000
|
const shouldShowShareLink = !options.noScore && options.share && !options.isCi;
|
|
61924
62001
|
yield* pause;
|
|
61925
62002
|
yield* printSummary({
|
|
@@ -63820,6 +63897,8 @@ const printMultiProjectSummary = (input) => gen(function* () {
|
|
|
63820
63897
|
const categoryFilters = input.categoryFilters ?? /* @__PURE__ */ new Set();
|
|
63821
63898
|
const animateRender = !verbose && canAnimateOnboarding(process.stdout);
|
|
63822
63899
|
const displayDiagnostics = filterDiagnosticsByCategories(filterScansForSurface(completedScans, "cli"), categoryFilters);
|
|
63900
|
+
const scoreDiagnostics = new Set(filterScansForSurface(completedScans, "score"));
|
|
63901
|
+
const displayedScoreDiagnostics = displayDiagnostics.filter((diagnostic) => scoreDiagnostics.has(diagnostic));
|
|
63823
63902
|
const projectRootByDiagnostic = /* @__PURE__ */ new WeakMap();
|
|
63824
63903
|
for (const scan of completedScans) for (const diagnostic of scan.result.diagnostics) projectRootByDiagnostic.set(diagnostic, scan.result.project.rootDirectory);
|
|
63825
63904
|
const resolveDiagnosticSourceRoot = (diagnostic) => projectRootByDiagnostic.get(diagnostic) ?? "";
|
|
@@ -63843,7 +63922,7 @@ const printMultiProjectSummary = (input) => gen(function* () {
|
|
|
63843
63922
|
diagnostics: displayDiagnostics,
|
|
63844
63923
|
elapsedMilliseconds: totalElapsedMilliseconds,
|
|
63845
63924
|
scoreResult: aggregateScore,
|
|
63846
|
-
potentialScore: lowestScoredScan ? yield* promise(() => computeProjectedScore(
|
|
63925
|
+
potentialScore: lowestScoredScan ? yield* promise(() => computeProjectedScore(displayedScoreDiagnostics, filterScansForSurface([lowestScoredScan], "score"), lowestScoredScan.result.score)) : null,
|
|
63847
63926
|
totalSourceFileCount,
|
|
63848
63927
|
noScoreMessage: "Score unavailable.",
|
|
63849
63928
|
verbose,
|
|
@@ -65788,4 +65867,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65788
65867
|
export {};
|
|
65789
65868
|
|
|
65790
65869
|
//# sourceMappingURL=cli.js.map
|
|
65791
|
-
//# debugId=
|
|
65870
|
+
//# debugId=4433c20d-e5c5-50db-ab5b-0ff72ef6f226
|
package/dist/index.d.ts
CHANGED
|
@@ -9056,8 +9056,10 @@ interface ReactDoctorIgnoreConfig {
|
|
|
9056
9056
|
*
|
|
9057
9057
|
* Defaults: design rules (tag `"design"`) are excluded from `prComment`,
|
|
9058
9058
|
* `score`, and `ciFailure` so style cleanup doesn't dilute meaningful
|
|
9059
|
-
* React findings.
|
|
9060
|
-
*
|
|
9059
|
+
* React findings. Diagnostics stamped with `fileContext: "test" | "story"`
|
|
9060
|
+
* are excluded from `score` and `ciFailure`. Both remain in `cli`, and an
|
|
9061
|
+
* explicit rule, category, or tag include promotes a diagnostic back onto
|
|
9062
|
+
* the selected surface.
|
|
9061
9063
|
*/
|
|
9062
9064
|
type DiagnosticSurface = "cli" | "prComment" | "score" | "ciFailure";
|
|
9063
9065
|
/**
|
|
@@ -9338,23 +9340,22 @@ interface ReactDoctorConfig {
|
|
|
9338
9340
|
*/
|
|
9339
9341
|
adoptExistingLintConfig?: boolean;
|
|
9340
9342
|
/**
|
|
9341
|
-
* Per-surface include/exclude controls. Each `DiagnosticSurface`
|
|
9342
|
-
*
|
|
9343
|
-
*
|
|
9344
|
-
*
|
|
9345
|
-
* without touching the rule's severity or activation.
|
|
9343
|
+
* Per-surface include/exclude controls. Each `DiagnosticSurface` resolves
|
|
9344
|
+
* rule tags, categories, and identifiers independently. This lets you keep a rule
|
|
9345
|
+
* visible locally while hiding it from PR comments, the score, or the CI
|
|
9346
|
+
* gate. These controls don't change rule severity or activation.
|
|
9346
9347
|
*
|
|
9347
9348
|
* Defaults (applied before user overrides):
|
|
9348
9349
|
*
|
|
9349
9350
|
* - `prComment` excludes tag `"design"`
|
|
9350
9351
|
* - `score` excludes tag `"design"`
|
|
9351
9352
|
* - `ciFailure` excludes tag `"design"`
|
|
9353
|
+
* - `score` and `ciFailure` exclude test/story file contexts unless explicitly included
|
|
9352
9354
|
*
|
|
9353
|
-
* Pass any controls block
|
|
9354
|
-
*
|
|
9355
|
-
*
|
|
9356
|
-
*
|
|
9357
|
-
* score or PR-comment surface.
|
|
9355
|
+
* Pass any controls block, including an empty `{}`, to keep the defaults.
|
|
9356
|
+
* Your include/exclude entries layer on top. Includes always win over
|
|
9357
|
+
* excludes. For example, you can promote one high-signal `design-*` rule
|
|
9358
|
+
* back into the score or PR-comment surface.
|
|
9358
9359
|
*/
|
|
9359
9360
|
surfaces?: Partial<Record<DiagnosticSurface, SurfaceControls>>;
|
|
9360
9361
|
/**
|
|
@@ -9540,6 +9541,7 @@ interface ProjectInfo {
|
|
|
9540
9541
|
framework: Framework;
|
|
9541
9542
|
hasTypeScript: boolean;
|
|
9542
9543
|
hasReactCompiler: boolean;
|
|
9544
|
+
hasReactCompilerLintPlugin?: boolean;
|
|
9543
9545
|
hasTanStackQuery: boolean;
|
|
9544
9546
|
/**
|
|
9545
9547
|
* `true` when the project or a workspace declares a Vite-based SSR runtime
|
package/dist/index.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]="81e065f1-a43b-51c2-ac53-2f38390032ed")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -33040,7 +33040,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33040
33040
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33041
33041
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33042
33042
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33043
|
-
const CORE_PACKAGE_VERSION = "0.
|
|
33043
|
+
const CORE_PACKAGE_VERSION = "0.8.0";
|
|
33044
33044
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33045
33045
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33046
33046
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33050,6 +33050,53 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
33050
33050
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
33051
33051
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
33052
33052
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
33053
|
+
const lastSourceMappingUrl = (fileContent) => {
|
|
33054
|
+
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
33055
|
+
return lastReference ? lastReference[1] : null;
|
|
33056
|
+
};
|
|
33057
|
+
const mapTargetsSource = (mapContent, input) => {
|
|
33058
|
+
const parsedMap = JSON.parse(mapContent);
|
|
33059
|
+
if (typeof parsedMap !== "object" || parsedMap === null) return false;
|
|
33060
|
+
const { file, sources } = parsedMap;
|
|
33061
|
+
if (file !== input.expectedFile) return false;
|
|
33062
|
+
if (!Array.isArray(sources) || sources.length !== 1 || typeof sources[0] !== "string") return false;
|
|
33063
|
+
return Path.posix.normalize(Path.posix.join(input.mapDirectory, sources[0])) === input.sourcePath;
|
|
33064
|
+
};
|
|
33065
|
+
const quartetProvesEmit = (stem, sourcePath, readFileText) => {
|
|
33066
|
+
try {
|
|
33067
|
+
const emitBaseName = Path.posix.basename(stem);
|
|
33068
|
+
const mapDirectory = Path.posix.dirname(stem);
|
|
33069
|
+
return lastSourceMappingUrl(readFileText(`${stem}.js`)) === `${emitBaseName}.js.map` && lastSourceMappingUrl(readFileText(`${stem}.d.ts`)) === `${emitBaseName}.d.ts.map` && mapTargetsSource(readFileText(`${stem}.js.map`), {
|
|
33070
|
+
expectedFile: `${emitBaseName}.js`,
|
|
33071
|
+
mapDirectory,
|
|
33072
|
+
sourcePath
|
|
33073
|
+
}) && mapTargetsSource(readFileText(`${stem}.d.ts.map`), {
|
|
33074
|
+
expectedFile: `${emitBaseName}.d.ts`,
|
|
33075
|
+
mapDirectory,
|
|
33076
|
+
sourcePath
|
|
33077
|
+
});
|
|
33078
|
+
} catch {
|
|
33079
|
+
return false;
|
|
33080
|
+
}
|
|
33081
|
+
};
|
|
33082
|
+
const collectTypeScriptEmitDuplicateJsPaths = (input) => {
|
|
33083
|
+
const untracked = new Set(input.untrackedPaths);
|
|
33084
|
+
const duplicateJsPaths = /* @__PURE__ */ new Set();
|
|
33085
|
+
for (const jsPath of untracked) {
|
|
33086
|
+
if (!jsPath.endsWith(".js")) continue;
|
|
33087
|
+
const stem = jsPath.slice(0, -3);
|
|
33088
|
+
if (![
|
|
33089
|
+
jsPath,
|
|
33090
|
+
`${stem}.js.map`,
|
|
33091
|
+
`${stem}.d.ts`,
|
|
33092
|
+
`${stem}.d.ts.map`
|
|
33093
|
+
].every((emitPath) => untracked.has(emitPath) && !input.trackedPaths.has(emitPath))) continue;
|
|
33094
|
+
const sourcePath = [`${stem}.ts`, `${stem}.tsx`].find((candidatePath) => input.trackedPaths.has(candidatePath));
|
|
33095
|
+
if (!sourcePath) continue;
|
|
33096
|
+
if (quartetProvesEmit(stem, sourcePath, input.readFileText)) duplicateJsPaths.add(jsPath);
|
|
33097
|
+
}
|
|
33098
|
+
return duplicateJsPaths;
|
|
33099
|
+
};
|
|
33053
33100
|
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
33054
33101
|
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
33055
33102
|
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
@@ -33120,20 +33167,29 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
33120
33167
|
}
|
|
33121
33168
|
return entries;
|
|
33122
33169
|
};
|
|
33123
|
-
const
|
|
33170
|
+
const gitLsFiles = (rootDirectory, flags) => {
|
|
33124
33171
|
const result = spawnSync("git", [
|
|
33125
33172
|
"ls-files",
|
|
33126
33173
|
"-z",
|
|
33127
|
-
|
|
33128
|
-
"--others",
|
|
33129
|
-
"--exclude-standard"
|
|
33174
|
+
...flags
|
|
33130
33175
|
], {
|
|
33131
33176
|
cwd: rootDirectory,
|
|
33132
33177
|
encoding: "utf-8",
|
|
33133
33178
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
33134
33179
|
});
|
|
33135
33180
|
if (result.error || result.status !== 0) return null;
|
|
33136
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0
|
|
33181
|
+
return result.stdout.split("\0").filter((filePath) => filePath.length > 0);
|
|
33182
|
+
};
|
|
33183
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
33184
|
+
const trackedPaths = gitLsFiles(rootDirectory, ["--cached"]);
|
|
33185
|
+
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
33186
|
+
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
33187
|
+
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
33188
|
+
trackedPaths: new Set(trackedPaths),
|
|
33189
|
+
untrackedPaths,
|
|
33190
|
+
readFileText: (relativePath) => NFS.readFileSync(Path.resolve(rootDirectory, relativePath), "utf-8")
|
|
33191
|
+
});
|
|
33192
|
+
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
33137
33193
|
};
|
|
33138
33194
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
33139
33195
|
const filePaths = [];
|
|
@@ -33336,11 +33392,8 @@ const frameworkMergeRank = (framework) => {
|
|
|
33336
33392
|
if (framework === "unknown") return 3;
|
|
33337
33393
|
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
33338
33394
|
};
|
|
33339
|
-
const
|
|
33340
|
-
|
|
33341
|
-
"react-compiler-runtime",
|
|
33342
|
-
"eslint-plugin-react-compiler"
|
|
33343
|
-
]);
|
|
33395
|
+
const REACT_COMPILER_TRANSFORM_PACKAGES = new Set(["babel-plugin-react-compiler", "react-compiler-runtime"]);
|
|
33396
|
+
const REACT_COMPILER_LINT_PACKAGES = new Set(["eslint-plugin-react-compiler"]);
|
|
33344
33397
|
const NEXT_CONFIG_FILENAMES = [
|
|
33345
33398
|
"next.config.js",
|
|
33346
33399
|
"next.config.mjs",
|
|
@@ -33370,16 +33423,29 @@ const EXPO_APP_CONFIG_FILENAMES = [
|
|
|
33370
33423
|
"app.config.js",
|
|
33371
33424
|
"app.config.ts"
|
|
33372
33425
|
];
|
|
33373
|
-
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|
|
|
33426
|
+
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|["']react-compiler["']/;
|
|
33374
33427
|
const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
|
|
33375
33428
|
const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
|
|
33376
|
-
const hasCompilerPackage = (packageJson) => {
|
|
33429
|
+
const hasCompilerPackage = (packageJson, compilerPackages) => {
|
|
33377
33430
|
const allDependencies = {
|
|
33378
33431
|
...packageJson.peerDependencies,
|
|
33379
33432
|
...packageJson.dependencies,
|
|
33380
33433
|
...packageJson.devDependencies
|
|
33381
33434
|
};
|
|
33382
|
-
return Object.keys(allDependencies).some((packageName) =>
|
|
33435
|
+
return Object.keys(allDependencies).some((packageName) => compilerPackages.has(packageName));
|
|
33436
|
+
};
|
|
33437
|
+
const hasCompilerPackageInAncestors = (directory, compilerPackages) => {
|
|
33438
|
+
if (isProjectBoundary(directory)) return false;
|
|
33439
|
+
let ancestorDirectory = Path.dirname(directory);
|
|
33440
|
+
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
33441
|
+
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
33442
|
+
if (isFile(ancestorPackagePath)) {
|
|
33443
|
+
if (hasCompilerPackage(readPackageJson(ancestorPackagePath), compilerPackages)) return true;
|
|
33444
|
+
}
|
|
33445
|
+
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
33446
|
+
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
33447
|
+
}
|
|
33448
|
+
return false;
|
|
33383
33449
|
};
|
|
33384
33450
|
const hasCompilerInConfigFile = (filePath) => {
|
|
33385
33451
|
if (!isFile(filePath)) return false;
|
|
@@ -33388,23 +33454,14 @@ const hasCompilerInConfigFile = (filePath) => {
|
|
|
33388
33454
|
};
|
|
33389
33455
|
const hasCompilerInConfigFiles = (directory, filenames) => filenames.some((filename) => hasCompilerInConfigFile(Path.join(directory, filename)));
|
|
33390
33456
|
const detectReactCompiler = (directory, packageJson) => {
|
|
33391
|
-
if (hasCompilerPackage(packageJson)) return true;
|
|
33457
|
+
if (hasCompilerPackage(packageJson, REACT_COMPILER_TRANSFORM_PACKAGES)) return true;
|
|
33392
33458
|
if (hasCompilerInConfigFiles(directory, NEXT_CONFIG_FILENAMES)) return true;
|
|
33393
33459
|
if (hasCompilerInConfigFiles(directory, BABEL_CONFIG_FILENAMES)) return true;
|
|
33394
33460
|
if (hasCompilerInConfigFiles(directory, VITE_CONFIG_FILENAMES)) return true;
|
|
33395
33461
|
if (hasCompilerInConfigFiles(directory, EXPO_APP_CONFIG_FILENAMES)) return true;
|
|
33396
|
-
|
|
33397
|
-
let ancestorDirectory = Path.dirname(directory);
|
|
33398
|
-
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
33399
|
-
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
33400
|
-
if (isFile(ancestorPackagePath)) {
|
|
33401
|
-
if (hasCompilerPackage(readPackageJson(ancestorPackagePath))) return true;
|
|
33402
|
-
}
|
|
33403
|
-
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
33404
|
-
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
33405
|
-
}
|
|
33406
|
-
return false;
|
|
33462
|
+
return hasCompilerPackageInAncestors(directory, REACT_COMPILER_TRANSFORM_PACKAGES);
|
|
33407
33463
|
};
|
|
33464
|
+
const detectReactCompilerLintPlugin = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_LINT_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_LINT_PACKAGES);
|
|
33408
33465
|
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
33409
33466
|
const filePath = Path.join(directory, filename);
|
|
33410
33467
|
return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
|
|
@@ -34371,6 +34428,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34371
34428
|
framework: "unknown",
|
|
34372
34429
|
hasTypeScript: hasOwnTsConfig,
|
|
34373
34430
|
hasReactCompiler: false,
|
|
34431
|
+
hasReactCompilerLintPlugin: false,
|
|
34374
34432
|
hasTanStackQuery: false,
|
|
34375
34433
|
hasSsrDependency: false,
|
|
34376
34434
|
preactVersion: null,
|
|
@@ -34491,6 +34549,7 @@ const discoverProject = (directory) => {
|
|
|
34491
34549
|
framework,
|
|
34492
34550
|
hasTypeScript,
|
|
34493
34551
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
34552
|
+
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
34494
34553
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
34495
34554
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
34496
34555
|
preactVersion,
|
|
@@ -35969,8 +36028,10 @@ const isDiagnosticSurface = (value) => typeof value === "string" && DIAGNOSTIC_S
|
|
|
35969
36028
|
* to the local CLI so developers see them while editing, but they're
|
|
35970
36029
|
* removed from the PR comment surface, the score, and the CI gate so
|
|
35971
36030
|
* they can't bury real React findings or fail a build over a Tailwind
|
|
35972
|
-
* shorthand.
|
|
35973
|
-
*
|
|
36031
|
+
* shorthand. Diagnostics from test and story files likewise remain in
|
|
36032
|
+
* raw output while staying out of the production-health score and CI
|
|
36033
|
+
* gate. Override per-surface via `config.surfaces.<surface>` to promote
|
|
36034
|
+
* individual rules back in by tag, category, or rule id.
|
|
35974
36035
|
*/
|
|
35975
36036
|
const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
35976
36037
|
cli: [],
|
|
@@ -38757,6 +38818,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
38757
38818
|
if (resolved.includeRuleKeys.has(ruleKey)) return true;
|
|
38758
38819
|
if (resolved.includeCategories.has(category)) return true;
|
|
38759
38820
|
if (intersects(tags, resolved.includeTags)) return true;
|
|
38821
|
+
if (diagnostic.fileContext !== void 0 && (surface === "score" || surface === "ciFailure")) return false;
|
|
38760
38822
|
if (resolved.excludeRuleKeys.has(ruleKey)) return false;
|
|
38761
38823
|
if (resolved.excludeCategories.has(category)) return false;
|
|
38762
38824
|
if (intersects(tags, resolved.excludeTags)) return false;
|
|
@@ -39181,6 +39243,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39181
39243
|
}
|
|
39182
39244
|
const diff = yield* runGit(input.directory, [
|
|
39183
39245
|
"diff",
|
|
39246
|
+
"--no-ext-diff",
|
|
39184
39247
|
"-z",
|
|
39185
39248
|
"--name-only",
|
|
39186
39249
|
"--diff-filter=ACMR",
|
|
@@ -39227,6 +39290,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39227
39290
|
if (resolvedCurrentBranch !== null && resolvedCurrentBranch === baseBranch) {
|
|
39228
39291
|
const uncommitted = yield* runGit(directory, [
|
|
39229
39292
|
"diff",
|
|
39293
|
+
"--no-ext-diff",
|
|
39230
39294
|
"-z",
|
|
39231
39295
|
"--name-only",
|
|
39232
39296
|
"--diff-filter=ACMR",
|
|
@@ -39253,6 +39317,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39253
39317
|
if (mergeBaseRef === null) return null;
|
|
39254
39318
|
const diff = yield* runGit(directory, [
|
|
39255
39319
|
"diff",
|
|
39320
|
+
"--no-ext-diff",
|
|
39256
39321
|
"-z",
|
|
39257
39322
|
"--name-only",
|
|
39258
39323
|
"--diff-filter=ACMR",
|
|
@@ -39270,6 +39335,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39270
39335
|
}).pipe(withSpan("Git.diffSelection")),
|
|
39271
39336
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
39272
39337
|
"diff",
|
|
39338
|
+
"--no-ext-diff",
|
|
39273
39339
|
"--cached",
|
|
39274
39340
|
"-z",
|
|
39275
39341
|
"--name-only",
|
|
@@ -39315,6 +39381,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39315
39381
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
39316
39382
|
const result = yield* runGit(directory, [
|
|
39317
39383
|
"diff",
|
|
39384
|
+
"--no-ext-diff",
|
|
39318
39385
|
"--unified=0",
|
|
39319
39386
|
"--diff-filter=ACMR",
|
|
39320
39387
|
"--relative",
|
|
@@ -39719,7 +39786,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
39719
39786
|
return enabled;
|
|
39720
39787
|
};
|
|
39721
39788
|
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
39722
|
-
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
39789
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
39723
39790
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
39724
39791
|
const jsPlugins = [];
|
|
39725
39792
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -40682,6 +40749,8 @@ const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
|
40682
40749
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
40683
40750
|
const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
40684
40751
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
40752
|
+
const REACT_ERROR_BOUNDARY_TITLE = "JSX render errors need an Error Boundary";
|
|
40753
|
+
const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown while the JSX child renders. Use an Error Boundary instead.";
|
|
40685
40754
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40686
40755
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40687
40756
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
@@ -40725,6 +40794,7 @@ const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.categor
|
|
|
40725
40794
|
const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
|
|
40726
40795
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
40727
40796
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
40797
|
+
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
40728
40798
|
return rule === "todo" ? REACT_COMPILER_TODO_TITLE : REACT_COMPILER_TITLE;
|
|
40729
40799
|
};
|
|
40730
40800
|
const cleanDiagnosticMessage = (message, help, plugin, rule, project) => {
|
|
@@ -40743,6 +40813,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40743
40813
|
};
|
|
40744
40814
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
40745
40815
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
40816
|
+
if (rule === "error-boundaries") return {
|
|
40817
|
+
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40818
|
+
help: reasonDetail || help
|
|
40819
|
+
};
|
|
40746
40820
|
return {
|
|
40747
40821
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40748
40822
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -40764,7 +40838,10 @@ const parseRuleCode = (code) => {
|
|
|
40764
40838
|
rule: match[2]
|
|
40765
40839
|
};
|
|
40766
40840
|
};
|
|
40767
|
-
const resolveDiagnosticCategory = (plugin, rule) =>
|
|
40841
|
+
const resolveDiagnosticCategory = (plugin, rule) => {
|
|
40842
|
+
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
40843
|
+
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
40844
|
+
};
|
|
40768
40845
|
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(reactDoctorPlugin.rules[rule]?.matchByOccurrence);
|
|
40769
40846
|
/**
|
|
40770
40847
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
@@ -41577,7 +41654,7 @@ const runOxlint = async (options) => {
|
|
|
41577
41654
|
};
|
|
41578
41655
|
}
|
|
41579
41656
|
};
|
|
41580
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41657
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41581
41658
|
const rulesetHash = computeRulesetHash({
|
|
41582
41659
|
config: buildConfig({
|
|
41583
41660
|
extendsPaths: [],
|
|
@@ -42019,6 +42096,14 @@ var Reporter = class Reporter extends Service()("react-doctor/Reporter") {
|
|
|
42019
42096
|
});
|
|
42020
42097
|
}));
|
|
42021
42098
|
};
|
|
42099
|
+
const HOME_DIRECTORY = os.homedir();
|
|
42100
|
+
const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
|
|
42101
|
+
const scrubSensitivePaths = (text) => {
|
|
42102
|
+
let scrubbed = text;
|
|
42103
|
+
if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
|
|
42104
|
+
for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
|
|
42105
|
+
return scrubbed;
|
|
42106
|
+
};
|
|
42022
42107
|
const ScoreApiResponseSchema = Struct({
|
|
42023
42108
|
score: Number$1,
|
|
42024
42109
|
label: String$1,
|
|
@@ -42033,7 +42118,10 @@ const ScoreApiResponseSchema = Struct({
|
|
|
42033
42118
|
})))
|
|
42034
42119
|
});
|
|
42035
42120
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
42036
|
-
const
|
|
42121
|
+
const sanitizeScoreDiagnostics = (diagnostics) => diagnostics.map(({ filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => ({
|
|
42122
|
+
...rest,
|
|
42123
|
+
filePath: redactSensitiveText(scrubSensitivePaths(filePath))
|
|
42124
|
+
}));
|
|
42037
42125
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
42038
42126
|
const describeFailure = (error) => {
|
|
42039
42127
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -42046,7 +42134,7 @@ const calculateScore = async (diagnostics, options = {}) => {
|
|
|
42046
42134
|
const requestUrl = options.isCi ? `${SCORE_API_URL}?ci=1` : SCORE_API_URL;
|
|
42047
42135
|
try {
|
|
42048
42136
|
const compressedBody = gzipSync(JSON.stringify({
|
|
42049
|
-
diagnostics:
|
|
42137
|
+
diagnostics: sanitizeScoreDiagnostics(diagnostics),
|
|
42050
42138
|
...options.metadata?.repo ? { repo: options.metadata.repo } : {},
|
|
42051
42139
|
...options.metadata?.sha ? { sha: options.metadata.sha } : {},
|
|
42052
42140
|
...options.metadata?.framework ? { framework: options.metadata.framework } : {},
|
|
@@ -43639,4 +43727,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43639
43727
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43640
43728
|
|
|
43641
43729
|
//# sourceMappingURL=index.js.map
|
|
43642
|
-
//# debugId=
|
|
43730
|
+
//# debugId=81e065f1-a43b-51c2-ac53-2f38390032ed
|
package/dist/lsp.js
CHANGED
|
@@ -33092,7 +33092,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33092
33092
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33093
33093
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33094
33094
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33095
|
-
const CORE_PACKAGE_VERSION = "0.
|
|
33095
|
+
const CORE_PACKAGE_VERSION = "0.8.0";
|
|
33096
33096
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33097
33097
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33098
33098
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33102,6 +33102,53 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
33102
33102
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
33103
33103
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
33104
33104
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
33105
|
+
const lastSourceMappingUrl = (fileContent) => {
|
|
33106
|
+
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
33107
|
+
return lastReference ? lastReference[1] : null;
|
|
33108
|
+
};
|
|
33109
|
+
const mapTargetsSource = (mapContent, input) => {
|
|
33110
|
+
const parsedMap = JSON.parse(mapContent);
|
|
33111
|
+
if (typeof parsedMap !== "object" || parsedMap === null) return false;
|
|
33112
|
+
const { file, sources } = parsedMap;
|
|
33113
|
+
if (file !== input.expectedFile) return false;
|
|
33114
|
+
if (!Array.isArray(sources) || sources.length !== 1 || typeof sources[0] !== "string") return false;
|
|
33115
|
+
return Path.posix.normalize(Path.posix.join(input.mapDirectory, sources[0])) === input.sourcePath;
|
|
33116
|
+
};
|
|
33117
|
+
const quartetProvesEmit = (stem, sourcePath, readFileText) => {
|
|
33118
|
+
try {
|
|
33119
|
+
const emitBaseName = Path.posix.basename(stem);
|
|
33120
|
+
const mapDirectory = Path.posix.dirname(stem);
|
|
33121
|
+
return lastSourceMappingUrl(readFileText(`${stem}.js`)) === `${emitBaseName}.js.map` && lastSourceMappingUrl(readFileText(`${stem}.d.ts`)) === `${emitBaseName}.d.ts.map` && mapTargetsSource(readFileText(`${stem}.js.map`), {
|
|
33122
|
+
expectedFile: `${emitBaseName}.js`,
|
|
33123
|
+
mapDirectory,
|
|
33124
|
+
sourcePath
|
|
33125
|
+
}) && mapTargetsSource(readFileText(`${stem}.d.ts.map`), {
|
|
33126
|
+
expectedFile: `${emitBaseName}.d.ts`,
|
|
33127
|
+
mapDirectory,
|
|
33128
|
+
sourcePath
|
|
33129
|
+
});
|
|
33130
|
+
} catch {
|
|
33131
|
+
return false;
|
|
33132
|
+
}
|
|
33133
|
+
};
|
|
33134
|
+
const collectTypeScriptEmitDuplicateJsPaths = (input) => {
|
|
33135
|
+
const untracked = new Set(input.untrackedPaths);
|
|
33136
|
+
const duplicateJsPaths = /* @__PURE__ */ new Set();
|
|
33137
|
+
for (const jsPath of untracked) {
|
|
33138
|
+
if (!jsPath.endsWith(".js")) continue;
|
|
33139
|
+
const stem = jsPath.slice(0, -3);
|
|
33140
|
+
if (![
|
|
33141
|
+
jsPath,
|
|
33142
|
+
`${stem}.js.map`,
|
|
33143
|
+
`${stem}.d.ts`,
|
|
33144
|
+
`${stem}.d.ts.map`
|
|
33145
|
+
].every((emitPath) => untracked.has(emitPath) && !input.trackedPaths.has(emitPath))) continue;
|
|
33146
|
+
const sourcePath = [`${stem}.ts`, `${stem}.tsx`].find((candidatePath) => input.trackedPaths.has(candidatePath));
|
|
33147
|
+
if (!sourcePath) continue;
|
|
33148
|
+
if (quartetProvesEmit(stem, sourcePath, input.readFileText)) duplicateJsPaths.add(jsPath);
|
|
33149
|
+
}
|
|
33150
|
+
return duplicateJsPaths;
|
|
33151
|
+
};
|
|
33105
33152
|
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
33106
33153
|
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
33107
33154
|
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
@@ -33172,20 +33219,29 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
33172
33219
|
}
|
|
33173
33220
|
return entries;
|
|
33174
33221
|
};
|
|
33175
|
-
const
|
|
33222
|
+
const gitLsFiles = (rootDirectory, flags) => {
|
|
33176
33223
|
const result = spawnSync("git", [
|
|
33177
33224
|
"ls-files",
|
|
33178
33225
|
"-z",
|
|
33179
|
-
|
|
33180
|
-
"--others",
|
|
33181
|
-
"--exclude-standard"
|
|
33226
|
+
...flags
|
|
33182
33227
|
], {
|
|
33183
33228
|
cwd: rootDirectory,
|
|
33184
33229
|
encoding: "utf-8",
|
|
33185
33230
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
33186
33231
|
});
|
|
33187
33232
|
if (result.error || result.status !== 0) return null;
|
|
33188
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0
|
|
33233
|
+
return result.stdout.split("\0").filter((filePath) => filePath.length > 0);
|
|
33234
|
+
};
|
|
33235
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
33236
|
+
const trackedPaths = gitLsFiles(rootDirectory, ["--cached"]);
|
|
33237
|
+
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
33238
|
+
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
33239
|
+
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
33240
|
+
trackedPaths: new Set(trackedPaths),
|
|
33241
|
+
untrackedPaths,
|
|
33242
|
+
readFileText: (relativePath) => NFS.readFileSync(Path.resolve(rootDirectory, relativePath), "utf-8")
|
|
33243
|
+
});
|
|
33244
|
+
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
33189
33245
|
};
|
|
33190
33246
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
33191
33247
|
const filePaths = [];
|
|
@@ -33388,11 +33444,8 @@ const frameworkMergeRank = (framework) => {
|
|
|
33388
33444
|
if (framework === "unknown") return 3;
|
|
33389
33445
|
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
33390
33446
|
};
|
|
33391
|
-
const
|
|
33392
|
-
|
|
33393
|
-
"react-compiler-runtime",
|
|
33394
|
-
"eslint-plugin-react-compiler"
|
|
33395
|
-
]);
|
|
33447
|
+
const REACT_COMPILER_TRANSFORM_PACKAGES = new Set(["babel-plugin-react-compiler", "react-compiler-runtime"]);
|
|
33448
|
+
const REACT_COMPILER_LINT_PACKAGES = new Set(["eslint-plugin-react-compiler"]);
|
|
33396
33449
|
const NEXT_CONFIG_FILENAMES = [
|
|
33397
33450
|
"next.config.js",
|
|
33398
33451
|
"next.config.mjs",
|
|
@@ -33422,16 +33475,29 @@ const EXPO_APP_CONFIG_FILENAMES = [
|
|
|
33422
33475
|
"app.config.js",
|
|
33423
33476
|
"app.config.ts"
|
|
33424
33477
|
];
|
|
33425
|
-
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|
|
|
33478
|
+
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|["']react-compiler["']/;
|
|
33426
33479
|
const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
|
|
33427
33480
|
const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
|
|
33428
|
-
const hasCompilerPackage = (packageJson) => {
|
|
33481
|
+
const hasCompilerPackage = (packageJson, compilerPackages) => {
|
|
33429
33482
|
const allDependencies = {
|
|
33430
33483
|
...packageJson.peerDependencies,
|
|
33431
33484
|
...packageJson.dependencies,
|
|
33432
33485
|
...packageJson.devDependencies
|
|
33433
33486
|
};
|
|
33434
|
-
return Object.keys(allDependencies).some((packageName) =>
|
|
33487
|
+
return Object.keys(allDependencies).some((packageName) => compilerPackages.has(packageName));
|
|
33488
|
+
};
|
|
33489
|
+
const hasCompilerPackageInAncestors = (directory, compilerPackages) => {
|
|
33490
|
+
if (isProjectBoundary(directory)) return false;
|
|
33491
|
+
let ancestorDirectory = Path.dirname(directory);
|
|
33492
|
+
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
33493
|
+
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
33494
|
+
if (isFile(ancestorPackagePath)) {
|
|
33495
|
+
if (hasCompilerPackage(readPackageJson(ancestorPackagePath), compilerPackages)) return true;
|
|
33496
|
+
}
|
|
33497
|
+
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
33498
|
+
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
33499
|
+
}
|
|
33500
|
+
return false;
|
|
33435
33501
|
};
|
|
33436
33502
|
const hasCompilerInConfigFile = (filePath) => {
|
|
33437
33503
|
if (!isFile(filePath)) return false;
|
|
@@ -33440,23 +33506,14 @@ const hasCompilerInConfigFile = (filePath) => {
|
|
|
33440
33506
|
};
|
|
33441
33507
|
const hasCompilerInConfigFiles = (directory, filenames) => filenames.some((filename) => hasCompilerInConfigFile(Path.join(directory, filename)));
|
|
33442
33508
|
const detectReactCompiler = (directory, packageJson) => {
|
|
33443
|
-
if (hasCompilerPackage(packageJson)) return true;
|
|
33509
|
+
if (hasCompilerPackage(packageJson, REACT_COMPILER_TRANSFORM_PACKAGES)) return true;
|
|
33444
33510
|
if (hasCompilerInConfigFiles(directory, NEXT_CONFIG_FILENAMES)) return true;
|
|
33445
33511
|
if (hasCompilerInConfigFiles(directory, BABEL_CONFIG_FILENAMES)) return true;
|
|
33446
33512
|
if (hasCompilerInConfigFiles(directory, VITE_CONFIG_FILENAMES)) return true;
|
|
33447
33513
|
if (hasCompilerInConfigFiles(directory, EXPO_APP_CONFIG_FILENAMES)) return true;
|
|
33448
|
-
|
|
33449
|
-
let ancestorDirectory = Path.dirname(directory);
|
|
33450
|
-
while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
|
|
33451
|
-
const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
|
|
33452
|
-
if (isFile(ancestorPackagePath)) {
|
|
33453
|
-
if (hasCompilerPackage(readPackageJson(ancestorPackagePath))) return true;
|
|
33454
|
-
}
|
|
33455
|
-
if (isProjectBoundary(ancestorDirectory)) return false;
|
|
33456
|
-
ancestorDirectory = Path.dirname(ancestorDirectory);
|
|
33457
|
-
}
|
|
33458
|
-
return false;
|
|
33514
|
+
return hasCompilerPackageInAncestors(directory, REACT_COMPILER_TRANSFORM_PACKAGES);
|
|
33459
33515
|
};
|
|
33516
|
+
const detectReactCompilerLintPlugin = (directory, packageJson) => hasCompilerPackage(packageJson, REACT_COMPILER_LINT_PACKAGES) || hasCompilerPackageInAncestors(directory, REACT_COMPILER_LINT_PACKAGES);
|
|
33460
33517
|
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
33461
33518
|
const filePath = Path.join(directory, filename);
|
|
33462
33519
|
return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
|
|
@@ -34423,6 +34480,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34423
34480
|
framework: "unknown",
|
|
34424
34481
|
hasTypeScript: hasOwnTsConfig,
|
|
34425
34482
|
hasReactCompiler: false,
|
|
34483
|
+
hasReactCompilerLintPlugin: false,
|
|
34426
34484
|
hasTanStackQuery: false,
|
|
34427
34485
|
hasSsrDependency: false,
|
|
34428
34486
|
preactVersion: null,
|
|
@@ -34543,6 +34601,7 @@ const discoverProject = (directory) => {
|
|
|
34543
34601
|
framework,
|
|
34544
34602
|
hasTypeScript,
|
|
34545
34603
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
34604
|
+
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
34546
34605
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
34547
34606
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
34548
34607
|
preactVersion,
|
|
@@ -35995,8 +36054,10 @@ const isDiagnosticSurface = (value) => typeof value === "string" && DIAGNOSTIC_S
|
|
|
35995
36054
|
* to the local CLI so developers see them while editing, but they're
|
|
35996
36055
|
* removed from the PR comment surface, the score, and the CI gate so
|
|
35997
36056
|
* they can't bury real React findings or fail a build over a Tailwind
|
|
35998
|
-
* shorthand.
|
|
35999
|
-
*
|
|
36057
|
+
* shorthand. Diagnostics from test and story files likewise remain in
|
|
36058
|
+
* raw output while staying out of the production-health score and CI
|
|
36059
|
+
* gate. Override per-surface via `config.surfaces.<surface>` to promote
|
|
36060
|
+
* individual rules back in by tag, category, or rule id.
|
|
36000
36061
|
*/
|
|
36001
36062
|
const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
36002
36063
|
cli: [],
|
|
@@ -38735,6 +38796,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
38735
38796
|
if (resolved.includeRuleKeys.has(ruleKey)) return true;
|
|
38736
38797
|
if (resolved.includeCategories.has(category)) return true;
|
|
38737
38798
|
if (intersects(tags, resolved.includeTags)) return true;
|
|
38799
|
+
if (diagnostic.fileContext !== void 0 && (surface === "score" || surface === "ciFailure")) return false;
|
|
38738
38800
|
if (resolved.excludeRuleKeys.has(ruleKey)) return false;
|
|
38739
38801
|
if (resolved.excludeCategories.has(category)) return false;
|
|
38740
38802
|
if (intersects(tags, resolved.excludeTags)) return false;
|
|
@@ -39159,6 +39221,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39159
39221
|
}
|
|
39160
39222
|
const diff = yield* runGit(input.directory, [
|
|
39161
39223
|
"diff",
|
|
39224
|
+
"--no-ext-diff",
|
|
39162
39225
|
"-z",
|
|
39163
39226
|
"--name-only",
|
|
39164
39227
|
"--diff-filter=ACMR",
|
|
@@ -39205,6 +39268,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39205
39268
|
if (resolvedCurrentBranch !== null && resolvedCurrentBranch === baseBranch) {
|
|
39206
39269
|
const uncommitted = yield* runGit(directory, [
|
|
39207
39270
|
"diff",
|
|
39271
|
+
"--no-ext-diff",
|
|
39208
39272
|
"-z",
|
|
39209
39273
|
"--name-only",
|
|
39210
39274
|
"--diff-filter=ACMR",
|
|
@@ -39231,6 +39295,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39231
39295
|
if (mergeBaseRef === null) return null;
|
|
39232
39296
|
const diff = yield* runGit(directory, [
|
|
39233
39297
|
"diff",
|
|
39298
|
+
"--no-ext-diff",
|
|
39234
39299
|
"-z",
|
|
39235
39300
|
"--name-only",
|
|
39236
39301
|
"--diff-filter=ACMR",
|
|
@@ -39248,6 +39313,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39248
39313
|
}).pipe(withSpan("Git.diffSelection")),
|
|
39249
39314
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
39250
39315
|
"diff",
|
|
39316
|
+
"--no-ext-diff",
|
|
39251
39317
|
"--cached",
|
|
39252
39318
|
"-z",
|
|
39253
39319
|
"--name-only",
|
|
@@ -39293,6 +39359,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39293
39359
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
39294
39360
|
const result = yield* runGit(directory, [
|
|
39295
39361
|
"diff",
|
|
39362
|
+
"--no-ext-diff",
|
|
39296
39363
|
"--unified=0",
|
|
39297
39364
|
"--diff-filter=ACMR",
|
|
39298
39365
|
"--relative",
|
|
@@ -39697,7 +39764,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
39697
39764
|
return enabled;
|
|
39698
39765
|
};
|
|
39699
39766
|
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
39700
|
-
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
39767
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
39701
39768
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
39702
39769
|
const jsPlugins = [];
|
|
39703
39770
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -40660,6 +40727,8 @@ const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
|
40660
40727
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
40661
40728
|
const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
40662
40729
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
40730
|
+
const REACT_ERROR_BOUNDARY_TITLE = "JSX render errors need an Error Boundary";
|
|
40731
|
+
const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown while the JSX child renders. Use an Error Boundary instead.";
|
|
40663
40732
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40664
40733
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40665
40734
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
@@ -40703,6 +40772,7 @@ const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.categor
|
|
|
40703
40772
|
const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
|
|
40704
40773
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
40705
40774
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
40775
|
+
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
40706
40776
|
return rule === "todo" ? REACT_COMPILER_TODO_TITLE : REACT_COMPILER_TITLE;
|
|
40707
40777
|
};
|
|
40708
40778
|
const cleanDiagnosticMessage = (message, help, plugin, rule, project) => {
|
|
@@ -40721,6 +40791,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40721
40791
|
};
|
|
40722
40792
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
40723
40793
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
40794
|
+
if (rule === "error-boundaries") return {
|
|
40795
|
+
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40796
|
+
help: reasonDetail || help
|
|
40797
|
+
};
|
|
40724
40798
|
return {
|
|
40725
40799
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40726
40800
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -40742,7 +40816,10 @@ const parseRuleCode = (code) => {
|
|
|
40742
40816
|
rule: match[2]
|
|
40743
40817
|
};
|
|
40744
40818
|
};
|
|
40745
|
-
const resolveDiagnosticCategory = (plugin, rule) =>
|
|
40819
|
+
const resolveDiagnosticCategory = (plugin, rule) => {
|
|
40820
|
+
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
40821
|
+
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
40822
|
+
};
|
|
40746
40823
|
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(reactDoctorPlugin.rules[rule]?.matchByOccurrence);
|
|
40747
40824
|
/**
|
|
40748
40825
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
@@ -41555,7 +41632,7 @@ const runOxlint = async (options) => {
|
|
|
41555
41632
|
};
|
|
41556
41633
|
}
|
|
41557
41634
|
};
|
|
41558
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41635
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41559
41636
|
const rulesetHash = computeRulesetHash({
|
|
41560
41637
|
config: buildConfig({
|
|
41561
41638
|
extendsPaths: [],
|
|
@@ -41997,6 +42074,14 @@ var Reporter = class Reporter extends Service()("react-doctor/Reporter") {
|
|
|
41997
42074
|
});
|
|
41998
42075
|
}));
|
|
41999
42076
|
};
|
|
42077
|
+
const HOME_DIRECTORY = os.homedir();
|
|
42078
|
+
const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
|
|
42079
|
+
const scrubSensitivePaths = (text) => {
|
|
42080
|
+
let scrubbed = text;
|
|
42081
|
+
if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
|
|
42082
|
+
for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
|
|
42083
|
+
return scrubbed;
|
|
42084
|
+
};
|
|
42000
42085
|
const ScoreApiResponseSchema = Struct({
|
|
42001
42086
|
score: Number$1,
|
|
42002
42087
|
label: String$1,
|
|
@@ -42011,7 +42096,10 @@ const ScoreApiResponseSchema = Struct({
|
|
|
42011
42096
|
})))
|
|
42012
42097
|
});
|
|
42013
42098
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
42014
|
-
const
|
|
42099
|
+
const sanitizeScoreDiagnostics = (diagnostics) => diagnostics.map(({ filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => ({
|
|
42100
|
+
...rest,
|
|
42101
|
+
filePath: redactSensitiveText(scrubSensitivePaths(filePath))
|
|
42102
|
+
}));
|
|
42015
42103
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
42016
42104
|
const describeFailure = (error) => {
|
|
42017
42105
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -42024,7 +42112,7 @@ const calculateScore = async (diagnostics, options = {}) => {
|
|
|
42024
42112
|
const requestUrl = options.isCi ? `${SCORE_API_URL}?ci=1` : SCORE_API_URL;
|
|
42025
42113
|
try {
|
|
42026
42114
|
const compressedBody = gzipSync(JSON.stringify({
|
|
42027
|
-
diagnostics:
|
|
42115
|
+
diagnostics: sanitizeScoreDiagnostics(diagnostics),
|
|
42028
42116
|
...options.metadata?.repo ? { repo: options.metadata.repo } : {},
|
|
42029
42117
|
...options.metadata?.sha ? { sha: options.metadata.sha } : {},
|
|
42030
42118
|
...options.metadata?.framework ? { framework: options.metadata.framework } : {},
|
|
@@ -43229,7 +43317,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
43229
43317
|
/** Display name used in client-facing messages and progress titles. */
|
|
43230
43318
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
43231
43319
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
43232
|
-
const SERVER_VERSION = "0.
|
|
43320
|
+
const SERVER_VERSION = "0.8.0";
|
|
43233
43321
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
43234
43322
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
43235
43323
|
/**
|
|
@@ -44971,23 +45059,6 @@ const METRIC = {
|
|
|
44971
45059
|
*/
|
|
44972
45060
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
44973
45061
|
//#endregion
|
|
44974
|
-
//#region src/cli/utils/scrub-sensitive-text.ts
|
|
44975
|
-
const HOME_DIRECTORY = os.homedir();
|
|
44976
|
-
const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
|
|
44977
|
-
/**
|
|
44978
|
-
* Replaces the user's home directory (and generic `/Users|/home|C:\Users\<name>`
|
|
44979
|
-
* roots) with `~` so absolute paths can't be tied back to an individual. Keeps
|
|
44980
|
-
* the path's relative structure intact, which stays useful for debugging while
|
|
44981
|
-
* dropping the personally-identifying prefix. Idempotent — re-running on an
|
|
44982
|
-
* already-scrubbed `~/...` path is a no-op.
|
|
44983
|
-
*/
|
|
44984
|
-
const scrubSensitivePaths = (text) => {
|
|
44985
|
-
let scrubbed = text;
|
|
44986
|
-
if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
|
|
44987
|
-
for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
|
|
44988
|
-
return scrubbed;
|
|
44989
|
-
};
|
|
44990
|
-
//#endregion
|
|
44991
45062
|
//#region src/cli/utils/anonymize-text.ts
|
|
44992
45063
|
/**
|
|
44993
45064
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -45276,5 +45347,5 @@ const startLanguageServer = () => {
|
|
|
45276
45347
|
};
|
|
45277
45348
|
//#endregion
|
|
45278
45349
|
export { startLanguageServer };
|
|
45279
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
45280
|
-
//# debugId=
|
|
45350
|
+
!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]="9eca6678-56ba-5032-b04c-b1ed965dffb4")}catch(e){}}();
|
|
45351
|
+
//# debugId=9eca6678-56ba-5032-b04c-b1ed965dffb4
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-dev.0618ff6",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
|
-
"deslop-js": "0.
|
|
69
|
-
"oxlint-plugin-react-doctor": "0.
|
|
68
|
+
"deslop-js": "0.8.0",
|
|
69
|
+
"oxlint-plugin-react-doctor": "0.8.0-dev.0618ff6"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
"@xterm/headless": "^6.0.0",
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
|
-
"@react-doctor/api": "0.
|
|
78
|
-
"@react-doctor/
|
|
79
|
-
"@react-doctor/
|
|
77
|
+
"@react-doctor/api": "0.8.0",
|
|
78
|
+
"@react-doctor/language-server": "0.8.0",
|
|
79
|
+
"@react-doctor/core": "0.8.0"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|