react-doctor 0.8.1-dev.7818922 → 0.8.1-dev.7c9dbed
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 +585 -74
- package/dist/index.d.ts +9 -15
- package/dist/index.js +90 -5
- package/dist/lsp.js +90 -5
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f6c63ef7-3eca-54f6-80d8-221d85cea09b")}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";
|
|
@@ -37977,7 +37977,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
37977
37977
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
37978
37978
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
37979
37979
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37980
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
37981
37980
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
37982
37981
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
37983
37982
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -42334,6 +42333,35 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
42334
42333
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
42335
42334
|
};
|
|
42336
42335
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
42336
|
+
const parseBaselineDiffPlan = (value) => {
|
|
42337
|
+
const entries = splitNullSeparated(value);
|
|
42338
|
+
const baseFiles = /* @__PURE__ */ new Set();
|
|
42339
|
+
const headFiles = /* @__PURE__ */ new Set();
|
|
42340
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
|
|
42341
|
+
const status = entries[entryIndex];
|
|
42342
|
+
const filePath = entries[entryIndex + 1];
|
|
42343
|
+
if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
|
|
42344
|
+
if (status === "A") {
|
|
42345
|
+
headFiles.add(filePath);
|
|
42346
|
+
continue;
|
|
42347
|
+
}
|
|
42348
|
+
if (status === "D") {
|
|
42349
|
+
baseFiles.add(filePath);
|
|
42350
|
+
continue;
|
|
42351
|
+
}
|
|
42352
|
+
if (status === "M" || status === "T") {
|
|
42353
|
+
baseFiles.add(filePath);
|
|
42354
|
+
headFiles.add(filePath);
|
|
42355
|
+
continue;
|
|
42356
|
+
}
|
|
42357
|
+
return null;
|
|
42358
|
+
}
|
|
42359
|
+
return {
|
|
42360
|
+
baseFiles: [...baseFiles],
|
|
42361
|
+
headFiles: [...headFiles],
|
|
42362
|
+
untrackedFiles: []
|
|
42363
|
+
};
|
|
42364
|
+
};
|
|
42337
42365
|
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
42338
42366
|
/**
|
|
42339
42367
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
@@ -42536,6 +42564,45 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42536
42564
|
githubViewerPermission,
|
|
42537
42565
|
branchExists,
|
|
42538
42566
|
mergeBase,
|
|
42567
|
+
baselineDiffPlan: (input) => {
|
|
42568
|
+
if (!isSafeGitRevision(input.ref)) return succeed$2(null);
|
|
42569
|
+
return gen(function* () {
|
|
42570
|
+
const unmerged = yield* runGit(input.directory, [
|
|
42571
|
+
"diff",
|
|
42572
|
+
"--no-ext-diff",
|
|
42573
|
+
"-z",
|
|
42574
|
+
"--name-only",
|
|
42575
|
+
"--diff-filter=U",
|
|
42576
|
+
"--relative"
|
|
42577
|
+
]);
|
|
42578
|
+
if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
|
|
42579
|
+
const result = yield* runGit(input.directory, [
|
|
42580
|
+
"diff",
|
|
42581
|
+
"--no-ext-diff",
|
|
42582
|
+
"--no-textconv",
|
|
42583
|
+
"--no-renames",
|
|
42584
|
+
"-z",
|
|
42585
|
+
"--name-status",
|
|
42586
|
+
"--relative",
|
|
42587
|
+
input.ref
|
|
42588
|
+
]);
|
|
42589
|
+
if (result.status !== 0) return null;
|
|
42590
|
+
const plan = parseBaselineDiffPlan(result.stdout);
|
|
42591
|
+
if (plan === null) return null;
|
|
42592
|
+
const untracked = yield* runGit(input.directory, [
|
|
42593
|
+
"ls-files",
|
|
42594
|
+
"--others",
|
|
42595
|
+
"--exclude-standard",
|
|
42596
|
+
"-z"
|
|
42597
|
+
]);
|
|
42598
|
+
if (untracked.status !== 0) return null;
|
|
42599
|
+
return {
|
|
42600
|
+
baseFiles: plan.baseFiles,
|
|
42601
|
+
headFiles: plan.headFiles,
|
|
42602
|
+
untrackedFiles: splitNullSeparated(untracked.stdout)
|
|
42603
|
+
};
|
|
42604
|
+
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
|
|
42605
|
+
},
|
|
42539
42606
|
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42540
42607
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
42541
42608
|
if (explicitBaseBranch !== void 0) {
|
|
@@ -42685,6 +42752,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42685
42752
|
githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
|
|
42686
42753
|
branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
|
|
42687
42754
|
mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
|
|
42755
|
+
baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
|
|
42688
42756
|
diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
|
|
42689
42757
|
stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
|
|
42690
42758
|
showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
|
|
@@ -44022,12 +44090,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
44022
44090
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
44023
44091
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
44024
44092
|
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`.";
|
|
44093
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
44025
44094
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
44026
44095
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
44027
44096
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44028
44097
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
44029
44098
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
44030
44099
|
};
|
|
44100
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
44101
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44102
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
44103
|
+
};
|
|
44031
44104
|
const PLUGIN_CATEGORY_MAP = {
|
|
44032
44105
|
react: "Bugs",
|
|
44033
44106
|
"react-hooks": "Bugs",
|
|
@@ -44085,6 +44158,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
44085
44158
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
44086
44159
|
help: reasonDetail || help
|
|
44087
44160
|
};
|
|
44161
|
+
if (rule === "set-state-in-effect") return {
|
|
44162
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
44163
|
+
help: reasonDetail || help
|
|
44164
|
+
};
|
|
44088
44165
|
return {
|
|
44089
44166
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
44090
44167
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -46421,12 +46498,19 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
|
46421
46498
|
*/
|
|
46422
46499
|
const materializeSourceTree = (input) => gen(function* () {
|
|
46423
46500
|
const materializedFiles = [];
|
|
46501
|
+
const unmaterializedFiles = [];
|
|
46424
46502
|
const resolvedTempDirectory = path.resolve(input.tempDirectory);
|
|
46425
46503
|
for (const relativePath of input.files) {
|
|
46426
46504
|
const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
|
|
46427
|
-
if (content === null)
|
|
46505
|
+
if (content === null) {
|
|
46506
|
+
unmaterializedFiles.push(relativePath);
|
|
46507
|
+
continue;
|
|
46508
|
+
}
|
|
46428
46509
|
const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
|
|
46429
|
-
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory))
|
|
46510
|
+
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) {
|
|
46511
|
+
unmaterializedFiles.push(relativePath);
|
|
46512
|
+
continue;
|
|
46513
|
+
}
|
|
46430
46514
|
yield* sync(() => {
|
|
46431
46515
|
fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
|
|
46432
46516
|
fs.writeFileSync(candidateTargetPath, content);
|
|
@@ -46443,6 +46527,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
46443
46527
|
return {
|
|
46444
46528
|
tempDirectory: input.tempDirectory,
|
|
46445
46529
|
materializedFiles,
|
|
46530
|
+
unmaterializedFiles,
|
|
46446
46531
|
cleanup: () => {
|
|
46447
46532
|
try {
|
|
46448
46533
|
fs.rmSync(input.tempDirectory, {
|
|
@@ -46712,54 +46797,80 @@ const buildSkippedChecks = (input) => {
|
|
|
46712
46797
|
skippedCheckReasons
|
|
46713
46798
|
};
|
|
46714
46799
|
};
|
|
46715
|
-
const
|
|
46800
|
+
const fingerprintText = (text) => createHash("sha256").update(text).digest("hex");
|
|
46801
|
+
const normalizeEvidence = (evidence) => evidence.replace(/\s+/g, " ").trim();
|
|
46802
|
+
const getDiagnosticMatchKeys = (diagnostic, evidence) => {
|
|
46716
46803
|
const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
46717
|
-
const
|
|
46718
|
-
|
|
46719
|
-
};
|
|
46720
|
-
|
|
46721
|
-
|
|
46722
|
-
|
|
46723
|
-
|
|
46724
|
-
|
|
46725
|
-
|
|
46726
|
-
|
|
46727
|
-
|
|
46728
|
-
|
|
46729
|
-
|
|
46730
|
-
|
|
46731
|
-
|
|
46732
|
-
|
|
46733
|
-
|
|
46734
|
-
|
|
46735
|
-
|
|
46736
|
-
|
|
46737
|
-
|
|
46738
|
-
|
|
46739
|
-
|
|
46740
|
-
|
|
46741
|
-
|
|
46742
|
-
|
|
46743
|
-
*
|
|
46804
|
+
const messageFingerprint = fingerprintText(`${diagnostic.title ?? ""}\0${diagnostic.message}`);
|
|
46805
|
+
const normalizedEvidence = evidence === null ? "" : normalizeEvidence(evidence);
|
|
46806
|
+
const stableEvidenceKey = normalizedEvidence.length > 0 ? `evidence\0${ruleKey}\0${messageFingerprint}\0${fingerprintText(normalizedEvidence)}` : null;
|
|
46807
|
+
return {
|
|
46808
|
+
stableEvidenceKey,
|
|
46809
|
+
sameFileStableEvidenceKey: stableEvidenceKey === null ? null : `${diagnostic.filePath}\0${stableEvidenceKey}`,
|
|
46810
|
+
sameFileFallbackKey: diagnostic.matchByOccurrence || normalizedEvidence.length === 0 ? `fallback\0${diagnostic.filePath}\0${ruleKey}\0${messageFingerprint}` : null
|
|
46811
|
+
};
|
|
46812
|
+
};
|
|
46813
|
+
const addDiagnosticIndex = (buckets, key, diagnosticIndex) => {
|
|
46814
|
+
if (key === null) return;
|
|
46815
|
+
const diagnosticIndexes = buckets.get(key) ?? [];
|
|
46816
|
+
diagnosticIndexes.push(diagnosticIndex);
|
|
46817
|
+
buckets.set(key, diagnosticIndexes);
|
|
46818
|
+
};
|
|
46819
|
+
const takeMatchingDiagnosticIndex = (buckets, key, matchedDiagnosticIndexes) => {
|
|
46820
|
+
if (key === null) return null;
|
|
46821
|
+
for (const diagnosticIndex of buckets.get(key) ?? []) if (!matchedDiagnosticIndexes.has(diagnosticIndex)) return diagnosticIndex;
|
|
46822
|
+
return null;
|
|
46823
|
+
};
|
|
46824
|
+
const readDiagnosticEvidence = (diagnostic, readEvidence, readLine) => readEvidence?.(diagnostic) ?? readLine(diagnostic.filePath, diagnostic.line);
|
|
46825
|
+
const buildMatchCandidates = (diagnostics, readEvidence, readLine) => diagnostics.map((diagnostic, diagnosticIndex) => ({
|
|
46826
|
+
diagnosticIndex,
|
|
46827
|
+
...getDiagnosticMatchKeys(diagnostic, readDiagnosticEvidence(diagnostic, readEvidence, readLine))
|
|
46828
|
+
}));
|
|
46829
|
+
/**
|
|
46830
|
+
* Diffs a head scan against a base scan using a multiset of construct-level
|
|
46831
|
+
* evidence. Stable identities combine plugin/rule, the diagnostic message,
|
|
46832
|
+
* and normalized diagnosed source, so unchanged findings can move across
|
|
46833
|
+
* files while changed constructs or messages remain new. Cardinality is
|
|
46834
|
+
* retained for identical findings. Diagnostics explicitly marked
|
|
46835
|
+
* `matchByOccurrence` may fall back to same-file plugin/rule/message matching
|
|
46836
|
+
* after same-file strict evidence matching. Cross-file evidence matching runs
|
|
46837
|
+
* last so a copy cannot consume a reformatted local occurrence. Unreadable
|
|
46838
|
+
* evidence uses the same conservative fallback rather than matching across
|
|
46839
|
+
* files without proof.
|
|
46744
46840
|
*/
|
|
46745
46841
|
const computeDiagnosticDelta = (input) => {
|
|
46746
|
-
const
|
|
46747
|
-
|
|
46748
|
-
|
|
46749
|
-
|
|
46750
|
-
|
|
46751
|
-
const
|
|
46752
|
-
|
|
46753
|
-
|
|
46754
|
-
|
|
46755
|
-
|
|
46756
|
-
|
|
46757
|
-
|
|
46758
|
-
|
|
46759
|
-
|
|
46842
|
+
const baseCandidates = buildMatchCandidates(input.baseDiagnostics, input.readBaseEvidence, input.readBaseLine);
|
|
46843
|
+
const headCandidates = buildMatchCandidates(input.headDiagnostics, input.readHeadEvidence, input.readHeadLine);
|
|
46844
|
+
const baseByStableEvidence = /* @__PURE__ */ new Map();
|
|
46845
|
+
const baseBySameFileStableEvidence = /* @__PURE__ */ new Map();
|
|
46846
|
+
const baseBySameFileFallback = /* @__PURE__ */ new Map();
|
|
46847
|
+
for (const candidate of baseCandidates) {
|
|
46848
|
+
addDiagnosticIndex(baseByStableEvidence, candidate.stableEvidenceKey, candidate.diagnosticIndex);
|
|
46849
|
+
addDiagnosticIndex(baseBySameFileStableEvidence, candidate.sameFileStableEvidenceKey, candidate.diagnosticIndex);
|
|
46850
|
+
addDiagnosticIndex(baseBySameFileFallback, candidate.sameFileFallbackKey, candidate.diagnosticIndex);
|
|
46851
|
+
}
|
|
46852
|
+
const matchedHeadDiagnosticIndexes = /* @__PURE__ */ new Set();
|
|
46853
|
+
const matchedBaseDiagnosticIndexes = /* @__PURE__ */ new Set();
|
|
46854
|
+
const matchCandidates = (baseBuckets, getKey, onMatch) => {
|
|
46855
|
+
for (const candidate of headCandidates) {
|
|
46856
|
+
if (matchedHeadDiagnosticIndexes.has(candidate.diagnosticIndex)) continue;
|
|
46857
|
+
const baseDiagnosticIndex = takeMatchingDiagnosticIndex(baseBuckets, getKey(candidate), matchedBaseDiagnosticIndexes);
|
|
46858
|
+
if (baseDiagnosticIndex === null) continue;
|
|
46859
|
+
matchedHeadDiagnosticIndexes.add(candidate.diagnosticIndex);
|
|
46860
|
+
matchedBaseDiagnosticIndexes.add(baseDiagnosticIndex);
|
|
46861
|
+
onMatch?.(candidate.diagnosticIndex, baseDiagnosticIndex);
|
|
46862
|
+
}
|
|
46863
|
+
};
|
|
46864
|
+
matchCandidates(baseBySameFileStableEvidence, (candidate) => candidate.sameFileStableEvidenceKey);
|
|
46865
|
+
matchCandidates(baseBySameFileFallback, (candidate) => candidate.sameFileFallbackKey);
|
|
46866
|
+
let crossFileMatchCount = 0;
|
|
46867
|
+
matchCandidates(baseByStableEvidence, (candidate) => candidate.stableEvidenceKey, (head, base) => {
|
|
46868
|
+
if (input.headDiagnostics[head]?.filePath !== input.baseDiagnostics[base]?.filePath) crossFileMatchCount += 1;
|
|
46869
|
+
});
|
|
46760
46870
|
return {
|
|
46761
|
-
newDiagnostics,
|
|
46762
|
-
fixedCount
|
|
46871
|
+
newDiagnostics: input.headDiagnostics.filter((_diagnostic, diagnosticIndex) => !matchedHeadDiagnosticIndexes.has(diagnosticIndex)),
|
|
46872
|
+
fixedCount: input.baseDiagnostics.length - matchedBaseDiagnosticIndexes.size,
|
|
46873
|
+
crossFileMatchCount
|
|
46763
46874
|
};
|
|
46764
46875
|
};
|
|
46765
46876
|
/**
|
|
@@ -46791,6 +46902,12 @@ const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runProm
|
|
|
46791
46902
|
};
|
|
46792
46903
|
}).pipe(provide(Git.layerNode)));
|
|
46793
46904
|
const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
|
|
46905
|
+
const getBaselineDiffPlan = (directory, ref) => runPromise(gen(function* () {
|
|
46906
|
+
return yield* (yield* Git).baselineDiffPlan({
|
|
46907
|
+
directory,
|
|
46908
|
+
ref
|
|
46909
|
+
});
|
|
46910
|
+
}).pipe(provide(Git.layerNode)));
|
|
46794
46911
|
/**
|
|
46795
46912
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
|
46796
46913
|
* `files` with `--unified=0` against `baseRef` (or the index when `cached`),
|
|
@@ -47247,7 +47364,7 @@ const makeNoopConsole = () => ({
|
|
|
47247
47364
|
});
|
|
47248
47365
|
//#endregion
|
|
47249
47366
|
//#region src/cli/utils/version.ts
|
|
47250
|
-
const VERSION = "0.8.1-dev.
|
|
47367
|
+
const VERSION = "0.8.1-dev.7c9dbed";
|
|
47251
47368
|
//#endregion
|
|
47252
47369
|
//#region src/cli/utils/json-mode.ts
|
|
47253
47370
|
let context = null;
|
|
@@ -47599,13 +47716,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47599
47716
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47600
47717
|
* standard `SENTRY_RELEASE` override.
|
|
47601
47718
|
*/
|
|
47602
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.
|
|
47719
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.7c9dbed`;
|
|
47603
47720
|
/**
|
|
47604
47721
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47605
47722
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47606
47723
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47607
47724
|
*/
|
|
47608
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.
|
|
47725
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.7c9dbed") ? "development" : "production");
|
|
47609
47726
|
/**
|
|
47610
47727
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47611
47728
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -60228,6 +60345,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60228
60345
|
new: summary.totalDiagnosticCount,
|
|
60229
60346
|
fixed: result.baselineDelta.fixedCount,
|
|
60230
60347
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
60348
|
+
crossFileMatches: result.baselineDelta.crossFileMatchCount ?? null,
|
|
60231
60349
|
degraded: false
|
|
60232
60350
|
}));
|
|
60233
60351
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -60341,28 +60459,49 @@ const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
|
60341
60459
|
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
60342
60460
|
};
|
|
60343
60461
|
//#endregion
|
|
60462
|
+
//#region src/cli/utils/is-materializable-git-source.ts
|
|
60463
|
+
const GIT_LFS_POINTER_HEADER = "version https://git-lfs.github.com/spec/v1";
|
|
60464
|
+
const isMaterializableGitSource = (content) => !content.includes("\0") && !content.startsWith(GIT_LFS_POINTER_HEADER);
|
|
60465
|
+
//#endregion
|
|
60344
60466
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
60345
60467
|
/**
|
|
60346
|
-
* Materializes the
|
|
60347
|
-
* `
|
|
60348
|
-
*
|
|
60349
|
-
*
|
|
60350
|
-
*
|
|
60351
|
-
*
|
|
60468
|
+
* Materializes the base side of the `ref` → worktree diff into
|
|
60469
|
+
* `tempDirectory`, mirroring the project layout plus head's config files so
|
|
60470
|
+
* both sides lint under the same rules. Git rename heuristics are disabled by
|
|
60471
|
+
* the diff planner, so an old path is retained as a base deletion while its
|
|
60472
|
+
* new path is a head addition. Missing head-only additions are expected;
|
|
60473
|
+
* missing paths that the plan says must exist at base make `isComplete` false.
|
|
60352
60474
|
*/
|
|
60353
60475
|
const materializeBaselineFiles = (input) => runPromise(gen(function* () {
|
|
60354
60476
|
const git = yield* Git;
|
|
60355
|
-
|
|
60477
|
+
const diffPlan = yield* git.baselineDiffPlan({
|
|
60356
60478
|
directory: input.directory,
|
|
60357
|
-
|
|
60479
|
+
ref: input.ref
|
|
60480
|
+
});
|
|
60481
|
+
const baseFiles = input.baseFiles ?? diffPlan?.baseFiles;
|
|
60482
|
+
const headFiles = input.headFiles ?? diffPlan?.headFiles;
|
|
60483
|
+
const untrackedFiles = diffPlan?.untrackedFiles ?? [];
|
|
60484
|
+
if (baseFiles === void 0 || headFiles === void 0) return null;
|
|
60485
|
+
const files = [...new Set([...input.files, ...baseFiles])];
|
|
60486
|
+
const tree = yield* materializeSourceTree({
|
|
60487
|
+
directory: input.directory,
|
|
60488
|
+
files,
|
|
60358
60489
|
tempDirectory: input.tempDirectory,
|
|
60359
60490
|
readContent: (relativePath) => git.showRefContent({
|
|
60360
60491
|
directory: input.directory,
|
|
60361
60492
|
ref: input.ref,
|
|
60362
60493
|
relativePath,
|
|
60363
60494
|
options: { maxBufferBytes: GIT_SHOW_MAX_BUFFER_BYTES }
|
|
60364
|
-
})
|
|
60495
|
+
}).pipe(map$3((content) => content !== null && isMaterializableGitSource(content) ? content : null))
|
|
60365
60496
|
});
|
|
60497
|
+
const materializedFiles = new Set(tree.materializedFiles);
|
|
60498
|
+
return {
|
|
60499
|
+
...tree,
|
|
60500
|
+
baseFiles,
|
|
60501
|
+
headFiles,
|
|
60502
|
+
isComplete: baseFiles.every((filePath) => materializedFiles.has(filePath)),
|
|
60503
|
+
untrackedFiles
|
|
60504
|
+
};
|
|
60366
60505
|
}).pipe(provide(Git.layerNode)));
|
|
60367
60506
|
/**
|
|
60368
60507
|
* Resolves the commit a baseline scan should read base content from — the
|
|
@@ -60385,8 +60524,8 @@ const resolveMergeBaseRef = async (directory, ref) => {
|
|
|
60385
60524
|
//#region src/cli/utils/read-source-line.ts
|
|
60386
60525
|
/**
|
|
60387
60526
|
* Builds a cached `(filePath, line) -> source text` reader rooted at
|
|
60388
|
-
* `rootDirectory`, used
|
|
60389
|
-
*
|
|
60527
|
+
* `rootDirectory`, used as the fallback when construct-level diagnostic
|
|
60528
|
+
* evidence cannot be read. Wraps core's
|
|
60390
60529
|
* `createNodeReadFileLinesSync` with per-file memoization + 1-indexed line
|
|
60391
60530
|
* lookup; unreadable files / out-of-range lines return `null`.
|
|
60392
60531
|
*/
|
|
@@ -60401,6 +60540,338 @@ const createSourceLineReader = (rootDirectory) => {
|
|
|
60401
60540
|
};
|
|
60402
60541
|
};
|
|
60403
60542
|
//#endregion
|
|
60543
|
+
//#region src/cli/utils/read-diagnostic-evidence.ts
|
|
60544
|
+
const SOURCE_EXTENSIONS = [
|
|
60545
|
+
".ts",
|
|
60546
|
+
".tsx",
|
|
60547
|
+
".js",
|
|
60548
|
+
".jsx",
|
|
60549
|
+
".mts",
|
|
60550
|
+
".cts",
|
|
60551
|
+
".mjs",
|
|
60552
|
+
".cjs"
|
|
60553
|
+
];
|
|
60554
|
+
const COMPONENT_WRAPPER_NAMES = new Set(["memo", "forwardRef"]);
|
|
60555
|
+
const getScriptKind = (filePath) => {
|
|
60556
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
60557
|
+
if (extension === ".tsx") return ts.ScriptKind.TSX;
|
|
60558
|
+
if (extension === ".jsx") return ts.ScriptKind.JSX;
|
|
60559
|
+
if (extension === ".js" || extension === ".mjs" || extension === ".cjs") return ts.ScriptKind.JS;
|
|
60560
|
+
return ts.ScriptKind.TS;
|
|
60561
|
+
};
|
|
60562
|
+
const resolveSafeSourcePath = (rootDirectory, filePath) => {
|
|
60563
|
+
try {
|
|
60564
|
+
const resolvedRootDirectory = fs.realpathSync(rootDirectory);
|
|
60565
|
+
const absolutePath = path.resolve(resolvedRootDirectory, filePath);
|
|
60566
|
+
const relativePath = path.relative(resolvedRootDirectory, absolutePath);
|
|
60567
|
+
if (relativePath.length === 0 || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) return null;
|
|
60568
|
+
const stats = fs.lstatSync(absolutePath);
|
|
60569
|
+
if (!stats.isFile() || stats.isSymbolicLink()) return null;
|
|
60570
|
+
const realPath = fs.realpathSync(absolutePath);
|
|
60571
|
+
const realRelativePath = path.relative(resolvedRootDirectory, realPath);
|
|
60572
|
+
return realRelativePath !== ".." && !realRelativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(realRelativePath) ? realPath : null;
|
|
60573
|
+
} catch {
|
|
60574
|
+
return null;
|
|
60575
|
+
}
|
|
60576
|
+
};
|
|
60577
|
+
const readSourceRecord = (rootDirectory, filePath, state) => {
|
|
60578
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
60579
|
+
if (state.recordByFilePath.has(normalizedFilePath)) return state.recordByFilePath.get(normalizedFilePath) ?? null;
|
|
60580
|
+
const absolutePath = resolveSafeSourcePath(rootDirectory, normalizedFilePath);
|
|
60581
|
+
if (absolutePath === null) {
|
|
60582
|
+
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60583
|
+
return null;
|
|
60584
|
+
}
|
|
60585
|
+
try {
|
|
60586
|
+
const sourceText = fs.readFileSync(absolutePath, "utf-8");
|
|
60587
|
+
const record = {
|
|
60588
|
+
absolutePath,
|
|
60589
|
+
filePath: normalizedFilePath,
|
|
60590
|
+
sourceFile: ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
60591
|
+
sourceText
|
|
60592
|
+
};
|
|
60593
|
+
state.recordByFilePath.set(normalizedFilePath, record);
|
|
60594
|
+
return record;
|
|
60595
|
+
} catch {
|
|
60596
|
+
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60597
|
+
return null;
|
|
60598
|
+
}
|
|
60599
|
+
};
|
|
60600
|
+
const listSourceRecords = (rootDirectory, state) => {
|
|
60601
|
+
if (state.sourceRecords !== null) return state.sourceRecords;
|
|
60602
|
+
state.sourceRecords = listSourceFiles(rootDirectory).flatMap((filePath) => {
|
|
60603
|
+
const record = readSourceRecord(rootDirectory, filePath, state);
|
|
60604
|
+
return record === null ? [] : [record];
|
|
60605
|
+
});
|
|
60606
|
+
return state.sourceRecords;
|
|
60607
|
+
};
|
|
60608
|
+
const unwrapExpression = (expression) => {
|
|
60609
|
+
let currentExpression = expression;
|
|
60610
|
+
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
60611
|
+
return currentExpression;
|
|
60612
|
+
};
|
|
60613
|
+
const getCalleeName = (expression) => {
|
|
60614
|
+
const unwrappedExpression = unwrapExpression(expression);
|
|
60615
|
+
if (ts.isIdentifier(unwrappedExpression)) return unwrappedExpression.text;
|
|
60616
|
+
if (ts.isPropertyAccessExpression(unwrappedExpression)) return unwrappedExpression.name.text;
|
|
60617
|
+
return null;
|
|
60618
|
+
};
|
|
60619
|
+
const isComponentWrapperCall = (node) => {
|
|
60620
|
+
const wrapperName = getCalleeName(node.expression);
|
|
60621
|
+
return wrapperName !== null && COMPONENT_WRAPPER_NAMES.has(wrapperName);
|
|
60622
|
+
};
|
|
60623
|
+
const getFunctionName = (node) => {
|
|
60624
|
+
if (ts.isFunctionDeclaration(node) && node.name !== void 0) return node.name.text;
|
|
60625
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
60626
|
+
let componentExpression = node;
|
|
60627
|
+
while (true) {
|
|
60628
|
+
const parent = componentExpression.parent;
|
|
60629
|
+
if (ts.isParenthesizedExpression(parent) || ts.isAsExpression(parent) || ts.isTypeAssertionExpression(parent) || ts.isNonNullExpression(parent) || ts.isSatisfiesExpression(parent)) {
|
|
60630
|
+
componentExpression = parent;
|
|
60631
|
+
continue;
|
|
60632
|
+
}
|
|
60633
|
+
if (ts.isCallExpression(parent) && parent.arguments[0] === componentExpression && isComponentWrapperCall(parent)) {
|
|
60634
|
+
componentExpression = parent;
|
|
60635
|
+
continue;
|
|
60636
|
+
}
|
|
60637
|
+
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) return parent.name.text;
|
|
60638
|
+
return node.name?.text ?? null;
|
|
60639
|
+
}
|
|
60640
|
+
}
|
|
60641
|
+
return null;
|
|
60642
|
+
};
|
|
60643
|
+
const isComponentFunction = (node) => ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node);
|
|
60644
|
+
const isDefaultExportedComponent = (record, node, componentName) => {
|
|
60645
|
+
if (ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)) return true;
|
|
60646
|
+
return record.sourceFile.statements.some((statement) => ts.isExportAssignment(statement) && ts.isIdentifier(statement.expression) && statement.expression.text === componentName);
|
|
60647
|
+
};
|
|
60648
|
+
const findEnclosingComponent = (record, diagnostic) => {
|
|
60649
|
+
if (!Number.isInteger(diagnostic.line) || diagnostic.line < 1) return null;
|
|
60650
|
+
const lineIndex = diagnostic.line - 1;
|
|
60651
|
+
const lineStarts = record.sourceFile.getLineStarts();
|
|
60652
|
+
const lineStart = lineStarts[lineIndex];
|
|
60653
|
+
if (lineStart === void 0) return null;
|
|
60654
|
+
const nextLineStart = lineStarts[lineIndex + 1] ?? record.sourceFile.end;
|
|
60655
|
+
const lineEnd = Math.max(lineStart, nextLineStart - 1);
|
|
60656
|
+
const columnIndex = Number.isInteger(diagnostic.column) ? Math.max(0, diagnostic.column - 1) : 0;
|
|
60657
|
+
const diagnosticPosition = Math.min(lineStart + columnIndex, lineEnd);
|
|
60658
|
+
let component = null;
|
|
60659
|
+
let componentWidth = Number.POSITIVE_INFINITY;
|
|
60660
|
+
const visit = (node) => {
|
|
60661
|
+
if (diagnosticPosition < node.getStart(record.sourceFile) || diagnosticPosition > node.end) return;
|
|
60662
|
+
if (isComponentFunction(node)) {
|
|
60663
|
+
const name = getFunctionName(node);
|
|
60664
|
+
const width = node.end - node.getStart(record.sourceFile);
|
|
60665
|
+
if (name !== null && /^[A-Z]/.test(name) && width < componentWidth) {
|
|
60666
|
+
component = {
|
|
60667
|
+
isDefaultExport: isDefaultExportedComponent(record, node, name),
|
|
60668
|
+
name,
|
|
60669
|
+
node,
|
|
60670
|
+
record
|
|
60671
|
+
};
|
|
60672
|
+
componentWidth = width;
|
|
60673
|
+
}
|
|
60674
|
+
}
|
|
60675
|
+
ts.forEachChild(node, visit);
|
|
60676
|
+
};
|
|
60677
|
+
visit(record.sourceFile);
|
|
60678
|
+
return component;
|
|
60679
|
+
};
|
|
60680
|
+
const moduleSpecifierTargetsComponent = (callerRecord, moduleSpecifier, componentRecord) => {
|
|
60681
|
+
if (!moduleSpecifier.startsWith(".")) return false;
|
|
60682
|
+
const unresolvedPath = path.resolve(path.dirname(callerRecord.absolutePath), moduleSpecifier);
|
|
60683
|
+
const specifiedExtension = path.extname(unresolvedPath).toLowerCase();
|
|
60684
|
+
const sourcePathWithoutExtension = SOURCE_EXTENSIONS.includes(specifiedExtension) ? unresolvedPath.slice(0, -specifiedExtension.length) : unresolvedPath;
|
|
60685
|
+
return [
|
|
60686
|
+
unresolvedPath,
|
|
60687
|
+
...SOURCE_EXTENSIONS.map((extension) => `${sourcePathWithoutExtension}${extension}`),
|
|
60688
|
+
...sourcePathWithoutExtension === unresolvedPath ? SOURCE_EXTENSIONS.map((extension) => path.join(unresolvedPath, `index${extension}`)) : []
|
|
60689
|
+
].includes(componentRecord.absolutePath);
|
|
60690
|
+
};
|
|
60691
|
+
const getComponentTagNames = (record, component) => {
|
|
60692
|
+
if (record.absolutePath === component.record.absolutePath) return new Set([component.name]);
|
|
60693
|
+
const tagNames = /* @__PURE__ */ new Set();
|
|
60694
|
+
for (const statement of record.sourceFile.statements) {
|
|
60695
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || !moduleSpecifierTargetsComponent(record, statement.moduleSpecifier.text, component.record)) continue;
|
|
60696
|
+
const importClause = statement.importClause;
|
|
60697
|
+
if (component.isDefaultExport && importClause?.name !== void 0) tagNames.add(importClause.name.text);
|
|
60698
|
+
const namedBindings = importClause?.namedBindings;
|
|
60699
|
+
if (namedBindings !== void 0 && ts.isNamedImports(namedBindings)) {
|
|
60700
|
+
for (const element of namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === component.name) tagNames.add(element.name.text);
|
|
60701
|
+
}
|
|
60702
|
+
}
|
|
60703
|
+
return tagNames;
|
|
60704
|
+
};
|
|
60705
|
+
const getTransparentCall = (functionNode) => {
|
|
60706
|
+
if (functionNode.body === void 0) return null;
|
|
60707
|
+
if (!ts.isBlock(functionNode.body)) {
|
|
60708
|
+
const expression = unwrapExpression(functionNode.body);
|
|
60709
|
+
return ts.isCallExpression(expression) ? expression : null;
|
|
60710
|
+
}
|
|
60711
|
+
if (functionNode.body.statements.length !== 1) return null;
|
|
60712
|
+
const statement = functionNode.body.statements[0];
|
|
60713
|
+
if (statement === void 0) return null;
|
|
60714
|
+
const expression = ts.isExpressionStatement(statement) || ts.isReturnStatement(statement) ? statement.expression : void 0;
|
|
60715
|
+
if (expression === void 0) return null;
|
|
60716
|
+
const unwrappedExpression = unwrapExpression(expression);
|
|
60717
|
+
return ts.isCallExpression(unwrappedExpression) ? unwrappedExpression : null;
|
|
60718
|
+
};
|
|
60719
|
+
const resolveTransparentTarget = (functionNode) => {
|
|
60720
|
+
const call = getTransparentCall(functionNode);
|
|
60721
|
+
if (call === null) return null;
|
|
60722
|
+
const target = unwrapExpression(call.expression);
|
|
60723
|
+
if (!ts.isIdentifier(target) || call.arguments.length !== functionNode.parameters.length) return null;
|
|
60724
|
+
for (const [parameterIndex, parameter] of functionNode.parameters.entries()) {
|
|
60725
|
+
const argument = call.arguments[parameterIndex];
|
|
60726
|
+
const unwrappedArgument = argument === void 0 ? null : unwrapExpression(argument);
|
|
60727
|
+
if (!ts.isIdentifier(parameter.name) || unwrappedArgument === null || !ts.isIdentifier(unwrappedArgument) || unwrappedArgument.text !== parameter.name.text) return null;
|
|
60728
|
+
}
|
|
60729
|
+
return target.text;
|
|
60730
|
+
};
|
|
60731
|
+
const getForwardingFunction = (declaration) => {
|
|
60732
|
+
if (ts.isFunctionDeclaration(declaration)) return declaration;
|
|
60733
|
+
if (!ts.isVariableDeclaration(declaration) || declaration.initializer === void 0) return null;
|
|
60734
|
+
const initializer = unwrapExpression(declaration.initializer);
|
|
60735
|
+
if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) return initializer;
|
|
60736
|
+
if (ts.isCallExpression(initializer) && getCalleeName(initializer.expression) === "useCallback") {
|
|
60737
|
+
const callback = initializer.arguments[0];
|
|
60738
|
+
if (callback !== void 0 && (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback))) return callback;
|
|
60739
|
+
}
|
|
60740
|
+
return null;
|
|
60741
|
+
};
|
|
60742
|
+
const getDeclaredBindingName = (node) => {
|
|
60743
|
+
if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node) || ts.isFunctionDeclaration(node)) && node.name !== void 0 && ts.isIdentifier(node.name)) return node.name.text;
|
|
60744
|
+
if (ts.isImportClause(node)) return node.name?.text ?? null;
|
|
60745
|
+
if (ts.isImportSpecifier(node) || ts.isNamespaceImport(node)) return node.name.text;
|
|
60746
|
+
return null;
|
|
60747
|
+
};
|
|
60748
|
+
const findBindingScope = (node) => {
|
|
60749
|
+
let currentNode = node;
|
|
60750
|
+
while (currentNode !== void 0) {
|
|
60751
|
+
if (ts.isParameter(currentNode) && ts.isFunctionLike(currentNode.parent)) return currentNode.parent;
|
|
60752
|
+
if (ts.isBlock(currentNode) || ts.isModuleBlock(currentNode) || ts.isSourceFile(currentNode)) return currentNode;
|
|
60753
|
+
currentNode = currentNode.parent;
|
|
60754
|
+
}
|
|
60755
|
+
return node.getSourceFile();
|
|
60756
|
+
};
|
|
60757
|
+
const findBindingDeclaration = (record, binding) => {
|
|
60758
|
+
const usagePosition = binding.getStart(record.sourceFile);
|
|
60759
|
+
const candidates = [];
|
|
60760
|
+
const visit = (node) => {
|
|
60761
|
+
if (getDeclaredBindingName(node) === binding.text) {
|
|
60762
|
+
const scope = findBindingScope(node);
|
|
60763
|
+
const isVisibleScope = usagePosition >= scope.getStart(record.sourceFile) && usagePosition <= scope.end;
|
|
60764
|
+
const isVisibleDeclaration = ts.isFunctionDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node) || ts.isImportClause(node) || ts.isImportSpecifier(node) || ts.isNamespaceImport(node) || node.getStart(record.sourceFile) < usagePosition;
|
|
60765
|
+
if (isVisibleScope && isVisibleDeclaration) candidates.push({
|
|
60766
|
+
declaration: node,
|
|
60767
|
+
scope
|
|
60768
|
+
});
|
|
60769
|
+
}
|
|
60770
|
+
ts.forEachChild(node, visit);
|
|
60771
|
+
};
|
|
60772
|
+
visit(record.sourceFile);
|
|
60773
|
+
let nearestCandidate = null;
|
|
60774
|
+
let nearestScopeWidth = Number.POSITIVE_INFINITY;
|
|
60775
|
+
let isNearestScopeAmbiguous = false;
|
|
60776
|
+
for (const candidate of candidates) {
|
|
60777
|
+
const scopeWidth = candidate.scope.end - candidate.scope.getStart(record.sourceFile);
|
|
60778
|
+
if (scopeWidth < nearestScopeWidth) {
|
|
60779
|
+
nearestCandidate = candidate;
|
|
60780
|
+
nearestScopeWidth = scopeWidth;
|
|
60781
|
+
isNearestScopeAmbiguous = false;
|
|
60782
|
+
} else if (scopeWidth === nearestScopeWidth) isNearestScopeAmbiguous = true;
|
|
60783
|
+
}
|
|
60784
|
+
if (isNearestScopeAmbiguous) return null;
|
|
60785
|
+
const declaration = nearestCandidate?.declaration;
|
|
60786
|
+
return declaration !== void 0 && (ts.isVariableDeclaration(declaration) || ts.isFunctionDeclaration(declaration)) ? declaration : null;
|
|
60787
|
+
};
|
|
60788
|
+
const resolveHandlerBinding = (record, binding) => {
|
|
60789
|
+
const declaration = findBindingDeclaration(record, binding);
|
|
60790
|
+
if (declaration === null) return binding.text;
|
|
60791
|
+
const forwardingFunction = getForwardingFunction(declaration);
|
|
60792
|
+
const target = forwardingFunction === null ? null : resolveTransparentTarget(forwardingFunction);
|
|
60793
|
+
return target === null || target === binding.text ? binding.text : target;
|
|
60794
|
+
};
|
|
60795
|
+
const getJsxTagName = (node) => ts.isIdentifier(node.tagName) ? node.tagName.text : null;
|
|
60796
|
+
const getHandlerBinding = (node, propName) => {
|
|
60797
|
+
for (const property of node.attributes.properties) {
|
|
60798
|
+
if (!ts.isJsxAttribute(property) || property.name.getText() !== propName) continue;
|
|
60799
|
+
const initializer = property.initializer;
|
|
60800
|
+
if (initializer === void 0 || !ts.isJsxExpression(initializer)) return null;
|
|
60801
|
+
if (initializer.expression === void 0) return null;
|
|
60802
|
+
const expression = unwrapExpression(initializer.expression);
|
|
60803
|
+
return ts.isIdentifier(expression) ? expression : null;
|
|
60804
|
+
}
|
|
60805
|
+
return null;
|
|
60806
|
+
};
|
|
60807
|
+
const getForwardedHandlerAliases = (rootDirectory, component, propNames, state) => {
|
|
60808
|
+
const cacheKey = `${component.record.filePath}\0${component.name}\0${[...propNames].sort().join("\0")}`;
|
|
60809
|
+
const cachedAliases = state.aliasesByComponent.get(cacheKey);
|
|
60810
|
+
if (cachedAliases !== void 0) return cachedAliases;
|
|
60811
|
+
const bindingsByPropName = /* @__PURE__ */ new Map();
|
|
60812
|
+
const unresolvedPropNames = /* @__PURE__ */ new Set();
|
|
60813
|
+
let callsiteCount = 0;
|
|
60814
|
+
for (const record of listSourceRecords(rootDirectory, state)) {
|
|
60815
|
+
const tagNames = getComponentTagNames(record, component);
|
|
60816
|
+
if (tagNames.size === 0) continue;
|
|
60817
|
+
const visit = (node) => {
|
|
60818
|
+
if (record === component.record && node === component.node) return;
|
|
60819
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
60820
|
+
const tagName = getJsxTagName(node);
|
|
60821
|
+
if (tagName !== null && tagNames.has(tagName)) {
|
|
60822
|
+
callsiteCount += 1;
|
|
60823
|
+
for (const propName of propNames) {
|
|
60824
|
+
const binding = getHandlerBinding(node, propName);
|
|
60825
|
+
if (binding === null) {
|
|
60826
|
+
unresolvedPropNames.add(propName);
|
|
60827
|
+
continue;
|
|
60828
|
+
}
|
|
60829
|
+
const bindings = bindingsByPropName.get(propName) ?? /* @__PURE__ */ new Set();
|
|
60830
|
+
bindings.add(resolveHandlerBinding(record, binding));
|
|
60831
|
+
bindingsByPropName.set(propName, bindings);
|
|
60832
|
+
}
|
|
60833
|
+
}
|
|
60834
|
+
}
|
|
60835
|
+
ts.forEachChild(node, visit);
|
|
60836
|
+
};
|
|
60837
|
+
visit(record.sourceFile);
|
|
60838
|
+
}
|
|
60839
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
60840
|
+
if (callsiteCount > 0) for (const [propName, bindings] of bindingsByPropName) {
|
|
60841
|
+
if (bindings.size !== 1 || unresolvedPropNames.has(propName)) continue;
|
|
60842
|
+
const [bindingName] = bindings;
|
|
60843
|
+
if (bindingName !== void 0) aliases.set(propName, bindingName);
|
|
60844
|
+
}
|
|
60845
|
+
state.aliasesByComponent.set(cacheKey, aliases);
|
|
60846
|
+
return aliases;
|
|
60847
|
+
};
|
|
60848
|
+
const normalizeForwardedHandlers = (evidence, aliases) => {
|
|
60849
|
+
let normalizedEvidence = evidence;
|
|
60850
|
+
for (const [propName, bindingName] of aliases) normalizedEvidence = normalizedEvidence.replace(new RegExp(`\\{\\s*${propName}\\s*\\}`, "g"), `{${bindingName}}`).replace(new RegExp(`=>\\s*${propName}\\s*\\(`, "g"), `=> ${bindingName}(`);
|
|
60851
|
+
return normalizedEvidence;
|
|
60852
|
+
};
|
|
60853
|
+
const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
60854
|
+
const state = {
|
|
60855
|
+
aliasesByComponent: /* @__PURE__ */ new Map(),
|
|
60856
|
+
recordByFilePath: /* @__PURE__ */ new Map(),
|
|
60857
|
+
sourceRecords: null
|
|
60858
|
+
};
|
|
60859
|
+
return (diagnostic) => {
|
|
60860
|
+
const record = readSourceRecord(rootDirectory, diagnostic.filePath, state);
|
|
60861
|
+
if (record === null || !Number.isInteger(diagnostic.line)) return null;
|
|
60862
|
+
const sourceLines = record.sourceText.split(/\r?\n/);
|
|
60863
|
+
const startLineIndex = Math.max(0, diagnostic.line - 1);
|
|
60864
|
+
const endLineIndex = Math.max(startLineIndex, (diagnostic.endLine ?? diagnostic.line) - 1);
|
|
60865
|
+
const evidence = sourceLines.slice(startLineIndex, endLineIndex + 1).join("\n");
|
|
60866
|
+
if (!options.resolveForwardedHandlers) return evidence;
|
|
60867
|
+
const propNames = new Set(evidence.match(/\bon[A-Z]\w*\b/g) ?? []);
|
|
60868
|
+
if (propNames.size === 0) return evidence;
|
|
60869
|
+
const component = findEnclosingComponent(record, diagnostic);
|
|
60870
|
+
if (component === null) return evidence;
|
|
60871
|
+
return normalizeForwardedHandlers(evidence, getForwardedHandlerAliases(rootDirectory, component, propNames, state));
|
|
60872
|
+
};
|
|
60873
|
+
};
|
|
60874
|
+
//#endregion
|
|
60404
60875
|
//#region src/cli/utils/build-no-score-message.ts
|
|
60405
60876
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
60406
60877
|
const buildNoScoreMessage = (isScoreDisabled) => {
|
|
@@ -61644,6 +62115,8 @@ const runBaselineComparison = async (params) => {
|
|
|
61644
62115
|
directory: params.directory,
|
|
61645
62116
|
ref: params.baselineRef,
|
|
61646
62117
|
files: params.options.includePaths,
|
|
62118
|
+
baseFiles: params.baseFiles,
|
|
62119
|
+
headFiles: params.headFiles,
|
|
61647
62120
|
tempDirectory
|
|
61648
62121
|
}).catch((error) => {
|
|
61649
62122
|
rmSync(tempDirectory, {
|
|
@@ -61652,7 +62125,24 @@ const runBaselineComparison = async (params) => {
|
|
|
61652
62125
|
});
|
|
61653
62126
|
throw error;
|
|
61654
62127
|
});
|
|
62128
|
+
if (snapshot === null) {
|
|
62129
|
+
rmSync(tempDirectory, {
|
|
62130
|
+
recursive: true,
|
|
62131
|
+
force: true
|
|
62132
|
+
});
|
|
62133
|
+
return null;
|
|
62134
|
+
}
|
|
61655
62135
|
try {
|
|
62136
|
+
if (!snapshot.isComplete) return null;
|
|
62137
|
+
const analyzedHeadFiles = new Set(params.headAnalyzedFiles.map(toForwardSlashes));
|
|
62138
|
+
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
62139
|
+
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
62140
|
+
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
62141
|
+
for (const filePath of params.options.includePaths) {
|
|
62142
|
+
const normalizedFilePath = toForwardSlashes(filePath);
|
|
62143
|
+
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
62144
|
+
}
|
|
62145
|
+
if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
61656
62146
|
const baseLayers = buildRuntimeLayers({
|
|
61657
62147
|
directory: snapshot.tempDirectory,
|
|
61658
62148
|
hasConfigOverride: true,
|
|
@@ -61668,7 +62158,7 @@ const runBaselineComparison = async (params) => {
|
|
|
61668
62158
|
});
|
|
61669
62159
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
61670
62160
|
directory: snapshot.tempDirectory,
|
|
61671
|
-
includePaths:
|
|
62161
|
+
includePaths: snapshot.materializedFiles,
|
|
61672
62162
|
customRulesOnly: params.options.customRulesOnly,
|
|
61673
62163
|
respectInlineDisables: params.options.respectInlineDisables,
|
|
61674
62164
|
warnings: params.options.warnings,
|
|
@@ -61685,18 +62175,22 @@ const runBaselineComparison = async (params) => {
|
|
|
61685
62175
|
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
61686
62176
|
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))));
|
|
61687
62177
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
62178
|
+
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
61688
62179
|
const delta = computeDiagnosticDelta({
|
|
61689
62180
|
headDiagnostics: params.headDiagnostics,
|
|
61690
62181
|
baseDiagnostics: baseOutput.diagnostics,
|
|
61691
62182
|
readHeadLine: createSourceLineReader(params.directory),
|
|
61692
|
-
readBaseLine: createSourceLineReader(snapshot.tempDirectory)
|
|
62183
|
+
readBaseLine: createSourceLineReader(snapshot.tempDirectory),
|
|
62184
|
+
readHeadEvidence: createDiagnosticEvidenceReader(params.directory, { resolveForwardedHandlers: true }),
|
|
62185
|
+
readBaseEvidence: createDiagnosticEvidenceReader(snapshot.tempDirectory)
|
|
61693
62186
|
});
|
|
61694
62187
|
return {
|
|
61695
62188
|
displayDiagnostics: delta.newDiagnostics,
|
|
61696
62189
|
baselineDelta: {
|
|
61697
62190
|
baseRef: params.baselineRef,
|
|
61698
|
-
fixedCount: delta.fixedCount,
|
|
61699
|
-
baseTotalCount: baseOutput.diagnostics.length
|
|
62191
|
+
fixedCount: hasUnscannedUntrackedSourceFiles ? 0 : delta.fixedCount,
|
|
62192
|
+
baseTotalCount: baseOutput.diagnostics.length,
|
|
62193
|
+
crossFileMatchCount: delta.crossFileMatchCount
|
|
61700
62194
|
}
|
|
61701
62195
|
};
|
|
61702
62196
|
} finally {
|
|
@@ -61803,6 +62297,9 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
61803
62297
|
headDiagnostics: output.diagnostics,
|
|
61804
62298
|
resolvedNodeBinaryPath,
|
|
61805
62299
|
baselineRef: options.baseline.ref,
|
|
62300
|
+
baseFiles: options.baseline.baseFiles,
|
|
62301
|
+
headFiles: options.baseline.headFiles,
|
|
62302
|
+
headAnalyzedFiles: output.analyzedFiles,
|
|
61806
62303
|
deadlineEpochMs
|
|
61807
62304
|
});
|
|
61808
62305
|
if (comparison) {
|
|
@@ -64087,20 +64584,25 @@ const resolveProjectRelativeDirectory = (rootDirectory, projectDirectory) => {
|
|
|
64087
64584
|
return relativeProjectDirectory;
|
|
64088
64585
|
};
|
|
64089
64586
|
//#endregion
|
|
64090
|
-
//#region src/cli/utils/resolve-project-
|
|
64091
|
-
const
|
|
64587
|
+
//#region src/cli/utils/resolve-project-source-file-paths.ts
|
|
64588
|
+
const resolveProjectSourceFilePaths = (rootDirectory, projectDirectory, filePaths) => {
|
|
64092
64589
|
const relativeProjectDirectory = resolveProjectRelativeDirectory(rootDirectory, projectDirectory);
|
|
64093
64590
|
if (relativeProjectDirectory === null) return [];
|
|
64094
|
-
const
|
|
64095
|
-
if (relativeProjectDirectory.length === 0) return
|
|
64591
|
+
const sourceFilePaths = filterSourceFiles([...filePaths]);
|
|
64592
|
+
if (relativeProjectDirectory.length === 0) return sourceFilePaths;
|
|
64096
64593
|
const projectPrefix = `${relativeProjectDirectory}/`;
|
|
64097
|
-
return
|
|
64594
|
+
return sourceFilePaths.flatMap((filePath) => {
|
|
64098
64595
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
64099
64596
|
if (!normalizedFilePath.startsWith(projectPrefix)) return [];
|
|
64100
64597
|
const projectRelativePath = normalizedFilePath.slice(projectPrefix.length);
|
|
64101
64598
|
return projectRelativePath.length > 0 ? [projectRelativePath] : [];
|
|
64102
64599
|
});
|
|
64103
64600
|
};
|
|
64601
|
+
//#endregion
|
|
64602
|
+
//#region src/cli/utils/resolve-project-diff-include-paths.ts
|
|
64603
|
+
const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
|
|
64604
|
+
return resolveProjectSourceFilePaths(rootDirectory, projectDirectory, diffInfo.changedFiles);
|
|
64605
|
+
};
|
|
64104
64606
|
/**
|
|
64105
64607
|
* Re-keys repo-relative changed line ranges (from `getChangedLineRanges`) to
|
|
64106
64608
|
* the scanned project, mirroring `resolveProjectDiffIncludePaths`'s boundary
|
|
@@ -64639,6 +65141,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
64639
65141
|
const isDiffMode = scope !== "full";
|
|
64640
65142
|
const comparisonBaseRef = isDiffMode && diffInfo && !diffInfo.isCurrentChanges ? diffInfo.baseSha ? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseSha) : diffInfo.diffBaseRef ?? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseBranch) : null;
|
|
64641
65143
|
const baselineRef = scope === "changed" ? comparisonBaseRef : null;
|
|
65144
|
+
const baselineDiffPlan = baselineRef === null ? null : await getBaselineDiffPlan(resolvedDirectory, baselineRef);
|
|
64642
65145
|
const linesBaseRef = diffInfo?.isCurrentChanges ? "HEAD" : comparisonBaseRef;
|
|
64643
65146
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
64644
65147
|
directory: resolvedDirectory,
|
|
@@ -64669,10 +65172,13 @@ const inspectAction = async (directory, flags) => {
|
|
|
64669
65172
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
64670
65173
|
let includePaths;
|
|
64671
65174
|
let supplyChainManifestChanged = false;
|
|
65175
|
+
const projectBaselineBaseFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.baseFiles);
|
|
65176
|
+
const projectBaselineHeadFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.headFiles);
|
|
64672
65177
|
if (isDiffMode) {
|
|
64673
65178
|
const changedSourceFiles = diffInfo === null ? [] : resolveProjectDiffIncludePaths(resolvedDirectory, scanDirectory, diffInfo);
|
|
64674
65179
|
supplyChainManifestChanged = supplyChainEnabled && diffInfo !== null && projectManifestChanged(resolvedDirectory, scanDirectory, diffInfo);
|
|
64675
|
-
|
|
65180
|
+
const hasBaselineOnlyFiles = (projectBaselineBaseFiles?.length ?? 0) > 0;
|
|
65181
|
+
if (changedSourceFiles.length === 0 && !supplyChainManifestChanged && !hasBaselineOnlyFiles) {
|
|
64676
65182
|
if (!isQuiet) {
|
|
64677
65183
|
cliLogger.dim(`No changed source files in ${scanDirectory}, skipping.`);
|
|
64678
65184
|
cliLogger.break();
|
|
@@ -64680,6 +65186,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
64680
65186
|
return null;
|
|
64681
65187
|
}
|
|
64682
65188
|
includePaths = [...changedSourceFiles];
|
|
65189
|
+
if (includePaths.length === 0 && hasBaselineOnlyFiles) includePaths.push(...projectBaselineBaseFiles ?? []);
|
|
64683
65190
|
if (supplyChainManifestChanged) includePaths.push("package.json");
|
|
64684
65191
|
}
|
|
64685
65192
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
@@ -64691,7 +65198,11 @@ const inspectAction = async (directory, flags) => {
|
|
|
64691
65198
|
configSourceDirectory: projectConfigSourceDirectory ?? void 0,
|
|
64692
65199
|
suppressRendering: isMultiProject,
|
|
64693
65200
|
concurrentScan: isMultiProject,
|
|
64694
|
-
baseline: baselineRef
|
|
65201
|
+
baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
|
|
65202
|
+
ref: baselineRef,
|
|
65203
|
+
baseFiles: projectBaselineBaseFiles,
|
|
65204
|
+
headFiles: projectBaselineHeadFiles
|
|
65205
|
+
} : void 0,
|
|
64695
65206
|
changedLineRanges: scope === "lines" && changedLineRanges !== null ? resolveProjectChangedLineRanges(resolvedDirectory, scanDirectory, changedLineRanges) : void 0,
|
|
64696
65207
|
supplyChainManifestChanged
|
|
64697
65208
|
});
|
|
@@ -65891,4 +66402,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65891
66402
|
export {};
|
|
65892
66403
|
|
|
65893
66404
|
//# sourceMappingURL=cli.js.map
|
|
65894
|
-
//# debugId=
|
|
66405
|
+
//# debugId=f6c63ef7-3eca-54f6-80d8-221d85cea09b
|
package/dist/index.d.ts
CHANGED
|
@@ -9030,11 +9030,12 @@ interface Diagnostic {
|
|
|
9030
9030
|
* Set when the finding's identity is the flagged element itself (a missing
|
|
9031
9031
|
* attribute, a wrong element) rather than the flagged line's text — every
|
|
9032
9032
|
* Accessibility-category finding, plus rules that opt in via their
|
|
9033
|
-
* `matchByOccurrence` metadata flag.
|
|
9034
|
-
* by `(
|
|
9035
|
-
*
|
|
9036
|
-
*
|
|
9037
|
-
*
|
|
9033
|
+
* `matchByOccurrence` metadata flag. After strict evidence matching,
|
|
9034
|
+
* `computeDiagnosticDelta` may match these by same-file `(plugin/rule,
|
|
9035
|
+
* message)` occurrence count, so reformatting the flagged element doesn't
|
|
9036
|
+
* reclassify a pre-existing finding as new. Resolved at diagnostic creation,
|
|
9037
|
+
* where rule metadata is available. Absent means diagnosed-source-sensitive
|
|
9038
|
+
* matching (the default).
|
|
9038
9039
|
*/
|
|
9039
9040
|
matchByOccurrence?: boolean;
|
|
9040
9041
|
/**
|
|
@@ -9847,7 +9848,8 @@ interface InspectResult {
|
|
|
9847
9848
|
baselineDelta?: {
|
|
9848
9849
|
/** The commit the base content was read from (resolved merge-base). */baseRef: string; /** Findings present at base but gone at head — resolved by the change. */
|
|
9849
9850
|
fixedCount: number; /** Total findings at base (over the same files), for context. */
|
|
9850
|
-
baseTotalCount: number;
|
|
9851
|
+
baseTotalCount: number; /** Pre-existing findings matched after moving to a different file. */
|
|
9852
|
+
crossFileMatchCount?: number;
|
|
9851
9853
|
};
|
|
9852
9854
|
}
|
|
9853
9855
|
/**
|
|
@@ -10263,15 +10265,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
|
|
|
10263
10265
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
10264
10266
|
*/
|
|
10265
10267
|
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
|
|
10266
|
-
declare const filterSourceFiles: (filePaths: string
|
|
10267
|
-
/**
|
|
10268
|
-
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
|
10269
|
-
* `files` with `--unified=0` against `baseRef` (or the index when `cached`),
|
|
10270
|
-
* returning per-file changed line ranges relative to `directory`. Returns
|
|
10271
|
-
* `null` when the ranges can't be computed (git unavailable / unsafe ref /
|
|
10272
|
-
* non-zero exit) so the caller degrades to file-level scope; an empty array
|
|
10273
|
-
* means git succeeded but the files added no lines.
|
|
10274
|
-
*/
|
|
10268
|
+
declare const filterSourceFiles: (filePaths: ReadonlyArray<string>) => string[];
|
|
10275
10269
|
//#endregion
|
|
10276
10270
|
//#region src/summarize-diagnostics.d.ts
|
|
10277
10271
|
declare const summarizeDiagnostics: (diagnostics: Diagnostic[], worstScore?: number | null, worstScoreLabel?: string | null) => JsonReportSummary; //#endregion
|
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]="f412a400-e7a3-5cfa-a67f-1192f6a38df2")}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";
|
|
@@ -34784,7 +34784,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
34784
34784
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
34785
34785
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
34786
34786
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
34787
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
34788
34787
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
34789
34788
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
34790
34789
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -39090,6 +39089,35 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
39090
39089
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
39091
39090
|
};
|
|
39092
39091
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
39092
|
+
const parseBaselineDiffPlan = (value) => {
|
|
39093
|
+
const entries = splitNullSeparated(value);
|
|
39094
|
+
const baseFiles = /* @__PURE__ */ new Set();
|
|
39095
|
+
const headFiles = /* @__PURE__ */ new Set();
|
|
39096
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
|
|
39097
|
+
const status = entries[entryIndex];
|
|
39098
|
+
const filePath = entries[entryIndex + 1];
|
|
39099
|
+
if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
|
|
39100
|
+
if (status === "A") {
|
|
39101
|
+
headFiles.add(filePath);
|
|
39102
|
+
continue;
|
|
39103
|
+
}
|
|
39104
|
+
if (status === "D") {
|
|
39105
|
+
baseFiles.add(filePath);
|
|
39106
|
+
continue;
|
|
39107
|
+
}
|
|
39108
|
+
if (status === "M" || status === "T") {
|
|
39109
|
+
baseFiles.add(filePath);
|
|
39110
|
+
headFiles.add(filePath);
|
|
39111
|
+
continue;
|
|
39112
|
+
}
|
|
39113
|
+
return null;
|
|
39114
|
+
}
|
|
39115
|
+
return {
|
|
39116
|
+
baseFiles: [...baseFiles],
|
|
39117
|
+
headFiles: [...headFiles],
|
|
39118
|
+
untrackedFiles: []
|
|
39119
|
+
};
|
|
39120
|
+
};
|
|
39093
39121
|
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
39094
39122
|
/**
|
|
39095
39123
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
@@ -39292,6 +39320,45 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39292
39320
|
githubViewerPermission,
|
|
39293
39321
|
branchExists,
|
|
39294
39322
|
mergeBase,
|
|
39323
|
+
baselineDiffPlan: (input) => {
|
|
39324
|
+
if (!isSafeGitRevision(input.ref)) return succeed$2(null);
|
|
39325
|
+
return gen(function* () {
|
|
39326
|
+
const unmerged = yield* runGit(input.directory, [
|
|
39327
|
+
"diff",
|
|
39328
|
+
"--no-ext-diff",
|
|
39329
|
+
"-z",
|
|
39330
|
+
"--name-only",
|
|
39331
|
+
"--diff-filter=U",
|
|
39332
|
+
"--relative"
|
|
39333
|
+
]);
|
|
39334
|
+
if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
|
|
39335
|
+
const result = yield* runGit(input.directory, [
|
|
39336
|
+
"diff",
|
|
39337
|
+
"--no-ext-diff",
|
|
39338
|
+
"--no-textconv",
|
|
39339
|
+
"--no-renames",
|
|
39340
|
+
"-z",
|
|
39341
|
+
"--name-status",
|
|
39342
|
+
"--relative",
|
|
39343
|
+
input.ref
|
|
39344
|
+
]);
|
|
39345
|
+
if (result.status !== 0) return null;
|
|
39346
|
+
const plan = parseBaselineDiffPlan(result.stdout);
|
|
39347
|
+
if (plan === null) return null;
|
|
39348
|
+
const untracked = yield* runGit(input.directory, [
|
|
39349
|
+
"ls-files",
|
|
39350
|
+
"--others",
|
|
39351
|
+
"--exclude-standard",
|
|
39352
|
+
"-z"
|
|
39353
|
+
]);
|
|
39354
|
+
if (untracked.status !== 0) return null;
|
|
39355
|
+
return {
|
|
39356
|
+
baseFiles: plan.baseFiles,
|
|
39357
|
+
headFiles: plan.headFiles,
|
|
39358
|
+
untrackedFiles: splitNullSeparated(untracked.stdout)
|
|
39359
|
+
};
|
|
39360
|
+
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
|
|
39361
|
+
},
|
|
39295
39362
|
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
39296
39363
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
39297
39364
|
if (explicitBaseBranch !== void 0) {
|
|
@@ -39441,6 +39508,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39441
39508
|
githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
|
|
39442
39509
|
branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
|
|
39443
39510
|
mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
|
|
39511
|
+
baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
|
|
39444
39512
|
diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
|
|
39445
39513
|
stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
|
|
39446
39514
|
showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
|
|
@@ -40778,12 +40846,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
40778
40846
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40779
40847
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40780
40848
|
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`.";
|
|
40849
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
40781
40850
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
40782
40851
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
40783
40852
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40784
40853
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
40785
40854
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
40786
40855
|
};
|
|
40856
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
40857
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40858
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
40859
|
+
};
|
|
40787
40860
|
const PLUGIN_CATEGORY_MAP = {
|
|
40788
40861
|
react: "Bugs",
|
|
40789
40862
|
"react-hooks": "Bugs",
|
|
@@ -40841,6 +40914,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40841
40914
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40842
40915
|
help: reasonDetail || help
|
|
40843
40916
|
};
|
|
40917
|
+
if (rule === "set-state-in-effect") return {
|
|
40918
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
40919
|
+
help: reasonDetail || help
|
|
40920
|
+
};
|
|
40844
40921
|
return {
|
|
40845
40922
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40846
40923
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -43166,12 +43243,19 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
|
43166
43243
|
*/
|
|
43167
43244
|
const materializeSourceTree = (input) => gen(function* () {
|
|
43168
43245
|
const materializedFiles = [];
|
|
43246
|
+
const unmaterializedFiles = [];
|
|
43169
43247
|
const resolvedTempDirectory = path.resolve(input.tempDirectory);
|
|
43170
43248
|
for (const relativePath of input.files) {
|
|
43171
43249
|
const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
|
|
43172
|
-
if (content === null)
|
|
43250
|
+
if (content === null) {
|
|
43251
|
+
unmaterializedFiles.push(relativePath);
|
|
43252
|
+
continue;
|
|
43253
|
+
}
|
|
43173
43254
|
const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
|
|
43174
|
-
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory))
|
|
43255
|
+
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) {
|
|
43256
|
+
unmaterializedFiles.push(relativePath);
|
|
43257
|
+
continue;
|
|
43258
|
+
}
|
|
43175
43259
|
yield* sync(() => {
|
|
43176
43260
|
fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
|
|
43177
43261
|
fs.writeFileSync(candidateTargetPath, content);
|
|
@@ -43188,6 +43272,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
43188
43272
|
return {
|
|
43189
43273
|
tempDirectory: input.tempDirectory,
|
|
43190
43274
|
materializedFiles,
|
|
43275
|
+
unmaterializedFiles,
|
|
43191
43276
|
cleanup: () => {
|
|
43192
43277
|
try {
|
|
43193
43278
|
fs.rmSync(input.tempDirectory, {
|
|
@@ -43751,4 +43836,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43751
43836
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43752
43837
|
|
|
43753
43838
|
//# sourceMappingURL=index.js.map
|
|
43754
|
-
//# debugId=
|
|
43839
|
+
//# debugId=f412a400-e7a3-5cfa-a67f-1192f6a38df2
|
package/dist/lsp.js
CHANGED
|
@@ -34836,7 +34836,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
34836
34836
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
34837
34837
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
34838
34838
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
34839
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
34840
34839
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
34841
34840
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
34842
34841
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -39068,6 +39067,35 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
39068
39067
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
39069
39068
|
};
|
|
39070
39069
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
39070
|
+
const parseBaselineDiffPlan = (value) => {
|
|
39071
|
+
const entries = splitNullSeparated(value);
|
|
39072
|
+
const baseFiles = /* @__PURE__ */ new Set();
|
|
39073
|
+
const headFiles = /* @__PURE__ */ new Set();
|
|
39074
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
|
|
39075
|
+
const status = entries[entryIndex];
|
|
39076
|
+
const filePath = entries[entryIndex + 1];
|
|
39077
|
+
if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
|
|
39078
|
+
if (status === "A") {
|
|
39079
|
+
headFiles.add(filePath);
|
|
39080
|
+
continue;
|
|
39081
|
+
}
|
|
39082
|
+
if (status === "D") {
|
|
39083
|
+
baseFiles.add(filePath);
|
|
39084
|
+
continue;
|
|
39085
|
+
}
|
|
39086
|
+
if (status === "M" || status === "T") {
|
|
39087
|
+
baseFiles.add(filePath);
|
|
39088
|
+
headFiles.add(filePath);
|
|
39089
|
+
continue;
|
|
39090
|
+
}
|
|
39091
|
+
return null;
|
|
39092
|
+
}
|
|
39093
|
+
return {
|
|
39094
|
+
baseFiles: [...baseFiles],
|
|
39095
|
+
headFiles: [...headFiles],
|
|
39096
|
+
untrackedFiles: []
|
|
39097
|
+
};
|
|
39098
|
+
};
|
|
39071
39099
|
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
39072
39100
|
/**
|
|
39073
39101
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
@@ -39270,6 +39298,45 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39270
39298
|
githubViewerPermission,
|
|
39271
39299
|
branchExists,
|
|
39272
39300
|
mergeBase,
|
|
39301
|
+
baselineDiffPlan: (input) => {
|
|
39302
|
+
if (!isSafeGitRevision(input.ref)) return succeed$2(null);
|
|
39303
|
+
return gen(function* () {
|
|
39304
|
+
const unmerged = yield* runGit(input.directory, [
|
|
39305
|
+
"diff",
|
|
39306
|
+
"--no-ext-diff",
|
|
39307
|
+
"-z",
|
|
39308
|
+
"--name-only",
|
|
39309
|
+
"--diff-filter=U",
|
|
39310
|
+
"--relative"
|
|
39311
|
+
]);
|
|
39312
|
+
if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
|
|
39313
|
+
const result = yield* runGit(input.directory, [
|
|
39314
|
+
"diff",
|
|
39315
|
+
"--no-ext-diff",
|
|
39316
|
+
"--no-textconv",
|
|
39317
|
+
"--no-renames",
|
|
39318
|
+
"-z",
|
|
39319
|
+
"--name-status",
|
|
39320
|
+
"--relative",
|
|
39321
|
+
input.ref
|
|
39322
|
+
]);
|
|
39323
|
+
if (result.status !== 0) return null;
|
|
39324
|
+
const plan = parseBaselineDiffPlan(result.stdout);
|
|
39325
|
+
if (plan === null) return null;
|
|
39326
|
+
const untracked = yield* runGit(input.directory, [
|
|
39327
|
+
"ls-files",
|
|
39328
|
+
"--others",
|
|
39329
|
+
"--exclude-standard",
|
|
39330
|
+
"-z"
|
|
39331
|
+
]);
|
|
39332
|
+
if (untracked.status !== 0) return null;
|
|
39333
|
+
return {
|
|
39334
|
+
baseFiles: plan.baseFiles,
|
|
39335
|
+
headFiles: plan.headFiles,
|
|
39336
|
+
untrackedFiles: splitNullSeparated(untracked.stdout)
|
|
39337
|
+
};
|
|
39338
|
+
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
|
|
39339
|
+
},
|
|
39273
39340
|
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
39274
39341
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
39275
39342
|
if (explicitBaseBranch !== void 0) {
|
|
@@ -39419,6 +39486,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
39419
39486
|
githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
|
|
39420
39487
|
branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
|
|
39421
39488
|
mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
|
|
39489
|
+
baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
|
|
39422
39490
|
diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
|
|
39423
39491
|
stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
|
|
39424
39492
|
showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
|
|
@@ -40756,12 +40824,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
40756
40824
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40757
40825
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40758
40826
|
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`.";
|
|
40827
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
40759
40828
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
40760
40829
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
40761
40830
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40762
40831
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
40763
40832
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
40764
40833
|
};
|
|
40834
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
40835
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40836
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
40837
|
+
};
|
|
40765
40838
|
const PLUGIN_CATEGORY_MAP = {
|
|
40766
40839
|
react: "Bugs",
|
|
40767
40840
|
"react-hooks": "Bugs",
|
|
@@ -40819,6 +40892,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40819
40892
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40820
40893
|
help: reasonDetail || help
|
|
40821
40894
|
};
|
|
40895
|
+
if (rule === "set-state-in-effect") return {
|
|
40896
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
40897
|
+
help: reasonDetail || help
|
|
40898
|
+
};
|
|
40822
40899
|
return {
|
|
40823
40900
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40824
40901
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -43144,12 +43221,19 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
|
43144
43221
|
*/
|
|
43145
43222
|
const materializeSourceTree = (input) => gen(function* () {
|
|
43146
43223
|
const materializedFiles = [];
|
|
43224
|
+
const unmaterializedFiles = [];
|
|
43147
43225
|
const resolvedTempDirectory = path.resolve(input.tempDirectory);
|
|
43148
43226
|
for (const relativePath of input.files) {
|
|
43149
43227
|
const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
|
|
43150
|
-
if (content === null)
|
|
43228
|
+
if (content === null) {
|
|
43229
|
+
unmaterializedFiles.push(relativePath);
|
|
43230
|
+
continue;
|
|
43231
|
+
}
|
|
43151
43232
|
const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
|
|
43152
|
-
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory))
|
|
43233
|
+
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) {
|
|
43234
|
+
unmaterializedFiles.push(relativePath);
|
|
43235
|
+
continue;
|
|
43236
|
+
}
|
|
43153
43237
|
yield* sync(() => {
|
|
43154
43238
|
fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
|
|
43155
43239
|
fs.writeFileSync(candidateTargetPath, content);
|
|
@@ -43166,6 +43250,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
43166
43250
|
return {
|
|
43167
43251
|
tempDirectory: input.tempDirectory,
|
|
43168
43252
|
materializedFiles,
|
|
43253
|
+
unmaterializedFiles,
|
|
43169
43254
|
cleanup: () => {
|
|
43170
43255
|
try {
|
|
43171
43256
|
fs.rmSync(input.tempDirectory, {
|
|
@@ -45371,5 +45456,5 @@ const startLanguageServer = () => {
|
|
|
45371
45456
|
};
|
|
45372
45457
|
//#endregion
|
|
45373
45458
|
export { startLanguageServer };
|
|
45374
|
-
!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]="
|
|
45375
|
-
//# debugId=
|
|
45459
|
+
!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]="2e6fca06-4d93-5b9e-86fd-92bfb63393e0")}catch(e){}}();
|
|
45460
|
+
//# debugId=2e6fca06-4d93-5b9e-86fd-92bfb63393e0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.8.1-dev.
|
|
3
|
+
"version": "0.8.1-dev.7c9dbed",
|
|
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
|
-
"
|
|
69
|
-
"
|
|
68
|
+
"oxlint-plugin-react-doctor": "0.8.1-dev.7c9dbed",
|
|
69
|
+
"deslop-js": "0.8.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
77
|
"@react-doctor/api": "0.8.1",
|
|
78
|
-
"@react-doctor/
|
|
79
|
-
"@react-doctor/
|
|
78
|
+
"@react-doctor/core": "0.8.1",
|
|
79
|
+
"@react-doctor/language-server": "0.8.1"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|