react-doctor 0.8.1-dev.61bf03e → 0.8.1-dev.7818922
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 +92 -720
- package/dist/index.d.ts +15 -29
- package/dist/index.js +23 -225
- package/dist/lsp.js +23 -225
- package/package.json +4 -4
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]="9e32582f-b296-5216-a028-7cc19ed83931")}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";
|
|
@@ -37317,10 +37317,6 @@ const listWorkspacePackages = (rootDirectory) => {
|
|
|
37317
37317
|
return packages;
|
|
37318
37318
|
};
|
|
37319
37319
|
const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
|
|
37320
|
-
const MOBX_REACT_PACKAGE_NAME = "mobx-react";
|
|
37321
|
-
const MOBX_REACT_LITE_PACKAGE_NAME = "mobx-react-lite";
|
|
37322
|
-
const MOBX_STATE_TREE_PACKAGE_NAME = "mobx-state-tree";
|
|
37323
|
-
const MOBX_REACT_OBSERVER_PACKAGE_NAME = "mobx-react-observer";
|
|
37324
37320
|
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
37325
37321
|
const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
|
|
37326
37322
|
const dependencyDeclaration = getDependencyDeclaration({
|
|
@@ -37339,18 +37335,7 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
|
37339
37335
|
if (nextMajor === null) return false;
|
|
37340
37336
|
return nextMajor < currentMajor;
|
|
37341
37337
|
};
|
|
37342
|
-
const
|
|
37343
|
-
if (currentFact.version === null) return true;
|
|
37344
|
-
if (nextFact.version === null) return false;
|
|
37345
|
-
const currentMajor = getLowestDependencyMajor(currentFact.version);
|
|
37346
|
-
const nextMajor = getLowestDependencyMajor(nextFact.version);
|
|
37347
|
-
if (currentMajor === null) return false;
|
|
37348
|
-
if (nextMajor === null) return true;
|
|
37349
|
-
if (currentMajor > 6) return false;
|
|
37350
|
-
if (nextMajor > 6) return true;
|
|
37351
|
-
return nextMajor < currentMajor;
|
|
37352
|
-
};
|
|
37353
|
-
const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
|
|
37338
|
+
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
37354
37339
|
if (facts.expo.version === null) {
|
|
37355
37340
|
const spec = getDependencySpec(packageJson, "expo");
|
|
37356
37341
|
if (spec !== null) facts.expo = {
|
|
@@ -37372,52 +37357,13 @@ const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, roo
|
|
|
37372
37357
|
sourceDirectory: directory
|
|
37373
37358
|
};
|
|
37374
37359
|
}
|
|
37375
|
-
const mobxSpec = getDependencySpec(packageJson, "mobx");
|
|
37376
|
-
if (mobxSpec !== null) {
|
|
37377
|
-
const nextMobxFact = {
|
|
37378
|
-
version: resolveCatalogBackedDependencyVersion({
|
|
37379
|
-
rootDirectory: directory,
|
|
37380
|
-
rootPackageJson: packageJson,
|
|
37381
|
-
packageName: "mobx",
|
|
37382
|
-
version: mobxSpec
|
|
37383
|
-
}),
|
|
37384
|
-
sourceDirectory: directory
|
|
37385
|
-
};
|
|
37386
|
-
if (shouldReplaceMobxFact(facts.mobx, nextMobxFact)) facts.mobx = nextMobxFact;
|
|
37387
|
-
}
|
|
37388
|
-
facts.hasMobxReact = facts.hasMobxReact || getDependencySpec(packageJson, MOBX_REACT_PACKAGE_NAME) !== null;
|
|
37389
|
-
facts.hasMobxReactLite = facts.hasMobxReactLite || getDependencySpec(packageJson, MOBX_REACT_LITE_PACKAGE_NAME) !== null;
|
|
37390
|
-
facts.hasMobxStateTree = facts.hasMobxStateTree || getDependencySpec(packageJson, MOBX_STATE_TREE_PACKAGE_NAME) !== null;
|
|
37391
|
-
facts.hasMobxReactObserver = facts.hasMobxReactObserver || getDependencySpec(packageJson, MOBX_REACT_OBSERVER_PACKAGE_NAME) !== null;
|
|
37392
37360
|
if (facts.reanimatedVersion === null) {
|
|
37393
37361
|
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
37394
37362
|
if (spec !== null) facts.reanimatedVersion = spec;
|
|
37395
37363
|
}
|
|
37396
|
-
if (facts.valtioVersion === null) {
|
|
37397
|
-
const spec = getDependencySpec(packageJson, "valtio");
|
|
37398
|
-
if (spec !== null) facts.valtioVersion = spec;
|
|
37399
|
-
}
|
|
37400
37364
|
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
37401
37365
|
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
37402
37366
|
facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
|
|
37403
|
-
const remotionSpec = getDependencySpec(packageJson, "remotion");
|
|
37404
|
-
if (remotionSpec !== null) {
|
|
37405
|
-
facts.hasRemotionDependency = true;
|
|
37406
|
-
const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
|
|
37407
|
-
rootDirectory,
|
|
37408
|
-
rootPackageJson,
|
|
37409
|
-
packageName: "remotion",
|
|
37410
|
-
version: remotionSpec
|
|
37411
|
-
});
|
|
37412
|
-
const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
|
|
37413
|
-
if (remotionMajorVersion === null) {
|
|
37414
|
-
facts.hasUnknownRemotionVersion = true;
|
|
37415
|
-
facts.remotionVersion = null;
|
|
37416
|
-
} else if (!facts.hasUnknownRemotionVersion) {
|
|
37417
|
-
const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
|
|
37418
|
-
if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
|
|
37419
|
-
}
|
|
37420
|
-
}
|
|
37421
37367
|
};
|
|
37422
37368
|
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
37423
37369
|
const facts = {
|
|
@@ -37437,24 +37383,12 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37437
37383
|
version: null,
|
|
37438
37384
|
sourceDirectory: null
|
|
37439
37385
|
},
|
|
37440
|
-
valtioVersion: null,
|
|
37441
|
-
mobx: {
|
|
37442
|
-
version: null,
|
|
37443
|
-
sourceDirectory: null
|
|
37444
|
-
},
|
|
37445
|
-
hasMobxReact: false,
|
|
37446
|
-
hasMobxReactLite: false,
|
|
37447
|
-
hasMobxStateTree: false,
|
|
37448
|
-
hasMobxReactObserver: false,
|
|
37449
37386
|
hasReactNativeAwarePackage: false,
|
|
37450
37387
|
hasReanimatedAwarePackage: false,
|
|
37451
37388
|
hasSsrDependency: false,
|
|
37452
|
-
hasRemotionDependency: false,
|
|
37453
|
-
hasUnknownRemotionVersion: false,
|
|
37454
|
-
remotionVersion: null,
|
|
37455
37389
|
reanimatedVersion: null
|
|
37456
37390
|
};
|
|
37457
|
-
evaluateManifestFacts(facts, rootPackageJson, rootDirectory
|
|
37391
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
37458
37392
|
let isReactGroupSettled = !collectReactGroup;
|
|
37459
37393
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
37460
37394
|
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
@@ -37463,7 +37397,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37463
37397
|
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
37464
37398
|
visitedDirectories.add(workspaceDirectory);
|
|
37465
37399
|
const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
|
|
37466
|
-
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory
|
|
37400
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
37467
37401
|
const info = extractDependencyInfo(workspacePackageJson);
|
|
37468
37402
|
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
37469
37403
|
if (isReactGroupSettled) continue;
|
|
@@ -37684,22 +37618,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37684
37618
|
tailwindVersion: null,
|
|
37685
37619
|
zodVersion: null,
|
|
37686
37620
|
zodMajorVersion: null,
|
|
37687
|
-
mobxVersion: null,
|
|
37688
|
-
mobxMajorVersion: null,
|
|
37689
|
-
hasMobxReact: false,
|
|
37690
|
-
hasMobxReactLite: false,
|
|
37691
|
-
hasMobxStateTree: false,
|
|
37692
|
-
hasMobxReactObserver: false,
|
|
37693
37621
|
framework: "unknown",
|
|
37694
37622
|
hasTypeScript: hasOwnTsConfig,
|
|
37695
37623
|
hasReactCompiler: false,
|
|
37696
37624
|
hasReactCompilerLintPlugin: false,
|
|
37697
37625
|
hasTanStackQuery: false,
|
|
37698
|
-
valtioVersion: null,
|
|
37699
|
-
valtioMajorVersion: null,
|
|
37700
|
-
hasRemotion: false,
|
|
37701
|
-
remotionVersion: null,
|
|
37702
|
-
remotionMajorVersion: null,
|
|
37703
37626
|
hasSsrDependency: false,
|
|
37704
37627
|
preactVersion: null,
|
|
37705
37628
|
preactMajorVersion: null,
|
|
@@ -37806,20 +37729,7 @@ const discoverProject = (directory) => {
|
|
|
37806
37729
|
packageName: "next",
|
|
37807
37730
|
version: workspaceFacts.next.version
|
|
37808
37731
|
}) : null;
|
|
37809
|
-
const valtioVersion = resolveCatalogBackedDependencyVersion({
|
|
37810
|
-
rootDirectory: directory,
|
|
37811
|
-
rootPackageJson: packageJson,
|
|
37812
|
-
packageName: "valtio",
|
|
37813
|
-
version: workspaceFacts.valtioVersion
|
|
37814
|
-
});
|
|
37815
|
-
const mobxVersion = resolveCatalogBackedDependencyVersion({
|
|
37816
|
-
rootDirectory: directory,
|
|
37817
|
-
rootPackageJson: packageJson,
|
|
37818
|
-
packageName: "mobx",
|
|
37819
|
-
version: workspaceFacts.mobx.version
|
|
37820
|
-
});
|
|
37821
37732
|
const preactVersion = getPreactVersion(packageJson);
|
|
37822
|
-
const remotionVersion = workspaceFacts.remotionVersion;
|
|
37823
37733
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
37824
37734
|
const projectInfo = {
|
|
37825
37735
|
rootDirectory: directory,
|
|
@@ -37829,22 +37739,11 @@ const discoverProject = (directory) => {
|
|
|
37829
37739
|
tailwindVersion,
|
|
37830
37740
|
zodVersion,
|
|
37831
37741
|
zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
|
|
37832
|
-
mobxVersion,
|
|
37833
|
-
mobxMajorVersion: mobxVersion === null ? null : getLowestDependencyMajor(mobxVersion),
|
|
37834
|
-
hasMobxReact: workspaceFacts.hasMobxReact,
|
|
37835
|
-
hasMobxReactLite: workspaceFacts.hasMobxReactLite,
|
|
37836
|
-
hasMobxStateTree: workspaceFacts.hasMobxStateTree,
|
|
37837
|
-
hasMobxReactObserver: workspaceFacts.hasMobxReactObserver,
|
|
37838
37742
|
framework,
|
|
37839
37743
|
hasTypeScript,
|
|
37840
37744
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
37841
37745
|
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
37842
37746
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
37843
|
-
valtioVersion,
|
|
37844
|
-
valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
|
|
37845
|
-
hasRemotion: workspaceFacts.hasRemotionDependency,
|
|
37846
|
-
remotionVersion,
|
|
37847
|
-
remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
|
|
37848
37747
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
37849
37748
|
preactVersion,
|
|
37850
37749
|
preactMajorVersion: parseReactMajor(preactVersion),
|
|
@@ -38078,6 +37977,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
38078
37977
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
38079
37978
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
38080
37979
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37980
|
+
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
38081
37981
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
38082
37982
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
38083
37983
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -40399,25 +40299,9 @@ const BABEL_CONFIG_FILE_NAMES = [
|
|
|
40399
40299
|
".babelrc.json"
|
|
40400
40300
|
];
|
|
40401
40301
|
const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
|
|
40402
|
-
const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
|
|
40403
|
-
const REACT_NATIVE_PACKAGE = "react-native";
|
|
40404
|
-
const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
|
|
40405
|
-
const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
|
|
40406
40302
|
const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
|
|
40407
40303
|
const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
|
|
40408
|
-
const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
|
|
40409
|
-
const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
|
|
40410
|
-
const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson$1(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
|
|
40411
|
-
if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
|
|
40412
|
-
const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
|
|
40413
|
-
if (normalizedVersionRange === null) return false;
|
|
40414
|
-
const minimumVersion = import_semver.minVersion(normalizedVersionRange);
|
|
40415
|
-
return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
|
|
40416
|
-
};
|
|
40417
|
-
const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
|
|
40418
40304
|
const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
40419
|
-
const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
|
|
40420
|
-
const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
|
|
40421
40305
|
for (const fileName of BABEL_CONFIG_FILE_NAMES) {
|
|
40422
40306
|
const filePath = Path.join(rootDirectory, fileName);
|
|
40423
40307
|
if (!isFile(filePath)) continue;
|
|
@@ -40427,7 +40311,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
40427
40311
|
} catch {
|
|
40428
40312
|
continue;
|
|
40429
40313
|
}
|
|
40430
|
-
if (
|
|
40314
|
+
if (contents.includes(LEGACY_PRESET_SPEC)) return [buildReactNativeDiagnostic({
|
|
40431
40315
|
filePath: fileName,
|
|
40432
40316
|
rule: "rn-no-metro-babel-preset",
|
|
40433
40317
|
severity: "error",
|
|
@@ -40450,6 +40334,12 @@ const checkReactNativeProject = (rootDirectory, project) => {
|
|
|
40450
40334
|
return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
|
|
40451
40335
|
};
|
|
40452
40336
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
40337
|
+
const DEPENDENCY_SECTIONS = [
|
|
40338
|
+
"dependencies",
|
|
40339
|
+
"devDependencies",
|
|
40340
|
+
"peerDependencies",
|
|
40341
|
+
"optionalDependencies"
|
|
40342
|
+
];
|
|
40453
40343
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
40454
40344
|
"19.0": {
|
|
40455
40345
|
rceFixedVersion: "19.0.1",
|
|
@@ -40496,13 +40386,20 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
|
|
|
40496
40386
|
for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
|
|
40497
40387
|
return [...directories];
|
|
40498
40388
|
};
|
|
40389
|
+
const readDeclaredSpec = (packageJson, packageName) => {
|
|
40390
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
40391
|
+
const spec = packageJson[section]?.[packageName];
|
|
40392
|
+
if (typeof spec === "string") return spec;
|
|
40393
|
+
}
|
|
40394
|
+
return null;
|
|
40395
|
+
};
|
|
40499
40396
|
const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
|
|
40500
40397
|
const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
|
|
40501
40398
|
if (isFile(manifestPath)) {
|
|
40502
40399
|
const installedVersion = import_semver.valid(readPackageJson$1(manifestPath).version ?? null);
|
|
40503
40400
|
if (installedVersion !== null) return installedVersion;
|
|
40504
40401
|
}
|
|
40505
|
-
const candidateSpecs = [
|
|
40402
|
+
const candidateSpecs = [readDeclaredSpec(readPackageJson$1(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
|
|
40506
40403
|
for (const spec of candidateSpecs) {
|
|
40507
40404
|
const pinnedVersion = spec === null ? null : import_semver.valid(spec);
|
|
40508
40405
|
if (pinnedVersion !== null) return pinnedVersion;
|
|
@@ -41182,22 +41079,9 @@ const buildCapabilities = (project) => {
|
|
|
41182
41079
|
})) capabilities.add("tailwind:3.4");
|
|
41183
41080
|
if (project.zodVersion !== null) capabilities.add("zod");
|
|
41184
41081
|
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
41185
|
-
if (project.mobxVersion !== void 0 && project.mobxVersion !== null || project.hasMobxReact === true || project.hasMobxReactLite === true || project.hasMobxStateTree === true || project.hasMobxReactObserver === true) capabilities.add("mobx");
|
|
41186
|
-
if (project.hasMobxReact === true) capabilities.add("mobx-react");
|
|
41187
|
-
if (project.hasMobxReactLite === true) capabilities.add("mobx-react-lite");
|
|
41188
|
-
if (project.hasMobxReact === true || project.hasMobxReactLite === true) capabilities.add("mobx-react-binding");
|
|
41189
|
-
if (project.hasMobxStateTree === true) capabilities.add("mobx-state-tree");
|
|
41190
|
-
if (project.hasMobxReactObserver === true) capabilities.add("mobx-react-observer");
|
|
41191
|
-
if (project.mobxMajorVersion !== void 0 && project.mobxMajorVersion !== null && project.mobxMajorVersion >= 4 && project.mobxMajorVersion <= 6) addMajorLadder(capabilities, "mobx", project.mobxMajorVersion, 4, 6);
|
|
41192
41082
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
41193
41083
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
41194
41084
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
41195
|
-
if (project.valtioVersion !== null) capabilities.add("valtio");
|
|
41196
|
-
addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
|
|
41197
|
-
if (project.hasRemotion) {
|
|
41198
|
-
capabilities.add("remotion");
|
|
41199
|
-
addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
|
|
41200
|
-
}
|
|
41201
41085
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
41202
41086
|
if (project.preactVersion !== null) capabilities.add("preact");
|
|
41203
41087
|
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
@@ -42450,35 +42334,6 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
42450
42334
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
42451
42335
|
};
|
|
42452
42336
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
42453
|
-
const parseBaselineDiffPlan = (value) => {
|
|
42454
|
-
const entries = splitNullSeparated(value);
|
|
42455
|
-
const baseFiles = /* @__PURE__ */ new Set();
|
|
42456
|
-
const headFiles = /* @__PURE__ */ new Set();
|
|
42457
|
-
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
|
|
42458
|
-
const status = entries[entryIndex];
|
|
42459
|
-
const filePath = entries[entryIndex + 1];
|
|
42460
|
-
if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
|
|
42461
|
-
if (status === "A") {
|
|
42462
|
-
headFiles.add(filePath);
|
|
42463
|
-
continue;
|
|
42464
|
-
}
|
|
42465
|
-
if (status === "D") {
|
|
42466
|
-
baseFiles.add(filePath);
|
|
42467
|
-
continue;
|
|
42468
|
-
}
|
|
42469
|
-
if (status === "M" || status === "T") {
|
|
42470
|
-
baseFiles.add(filePath);
|
|
42471
|
-
headFiles.add(filePath);
|
|
42472
|
-
continue;
|
|
42473
|
-
}
|
|
42474
|
-
return null;
|
|
42475
|
-
}
|
|
42476
|
-
return {
|
|
42477
|
-
baseFiles: [...baseFiles],
|
|
42478
|
-
headFiles: [...headFiles],
|
|
42479
|
-
untrackedFiles: []
|
|
42480
|
-
};
|
|
42481
|
-
};
|
|
42482
42337
|
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
42483
42338
|
/**
|
|
42484
42339
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
@@ -42681,45 +42536,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42681
42536
|
githubViewerPermission,
|
|
42682
42537
|
branchExists,
|
|
42683
42538
|
mergeBase,
|
|
42684
|
-
baselineDiffPlan: (input) => {
|
|
42685
|
-
if (!isSafeGitRevision(input.ref)) return succeed$2(null);
|
|
42686
|
-
return gen(function* () {
|
|
42687
|
-
const unmerged = yield* runGit(input.directory, [
|
|
42688
|
-
"diff",
|
|
42689
|
-
"--no-ext-diff",
|
|
42690
|
-
"-z",
|
|
42691
|
-
"--name-only",
|
|
42692
|
-
"--diff-filter=U",
|
|
42693
|
-
"--relative"
|
|
42694
|
-
]);
|
|
42695
|
-
if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
|
|
42696
|
-
const result = yield* runGit(input.directory, [
|
|
42697
|
-
"diff",
|
|
42698
|
-
"--no-ext-diff",
|
|
42699
|
-
"--no-textconv",
|
|
42700
|
-
"--no-renames",
|
|
42701
|
-
"-z",
|
|
42702
|
-
"--name-status",
|
|
42703
|
-
"--relative",
|
|
42704
|
-
input.ref
|
|
42705
|
-
]);
|
|
42706
|
-
if (result.status !== 0) return null;
|
|
42707
|
-
const plan = parseBaselineDiffPlan(result.stdout);
|
|
42708
|
-
if (plan === null) return null;
|
|
42709
|
-
const untracked = yield* runGit(input.directory, [
|
|
42710
|
-
"ls-files",
|
|
42711
|
-
"--others",
|
|
42712
|
-
"--exclude-standard",
|
|
42713
|
-
"-z"
|
|
42714
|
-
]);
|
|
42715
|
-
if (untracked.status !== 0) return null;
|
|
42716
|
-
return {
|
|
42717
|
-
baseFiles: plan.baseFiles,
|
|
42718
|
-
headFiles: plan.headFiles,
|
|
42719
|
-
untrackedFiles: splitNullSeparated(untracked.stdout)
|
|
42720
|
-
};
|
|
42721
|
-
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
|
|
42722
|
-
},
|
|
42723
42539
|
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42724
42540
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
42725
42541
|
if (explicitBaseBranch !== void 0) {
|
|
@@ -42869,7 +42685,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42869
42685
|
githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
|
|
42870
42686
|
branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
|
|
42871
42687
|
mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
|
|
42872
|
-
baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
|
|
42873
42688
|
diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
|
|
42874
42689
|
stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
|
|
42875
42690
|
showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
|
|
@@ -44207,17 +44022,12 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
44207
44022
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
44208
44023
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
44209
44024
|
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`.";
|
|
44210
|
-
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`.";
|
|
44211
44025
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
44212
44026
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
44213
44027
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44214
44028
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
44215
44029
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
44216
44030
|
};
|
|
44217
|
-
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
44218
|
-
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44219
|
-
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
44220
|
-
};
|
|
44221
44031
|
const PLUGIN_CATEGORY_MAP = {
|
|
44222
44032
|
react: "Bugs",
|
|
44223
44033
|
"react-hooks": "Bugs",
|
|
@@ -44275,10 +44085,6 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
44275
44085
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
44276
44086
|
help: reasonDetail || help
|
|
44277
44087
|
};
|
|
44278
|
-
if (rule === "set-state-in-effect") return {
|
|
44279
|
-
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
44280
|
-
help: reasonDetail || help
|
|
44281
|
-
};
|
|
44282
44088
|
return {
|
|
44283
44089
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
44284
44090
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -46615,19 +46421,12 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
|
46615
46421
|
*/
|
|
46616
46422
|
const materializeSourceTree = (input) => gen(function* () {
|
|
46617
46423
|
const materializedFiles = [];
|
|
46618
|
-
const unmaterializedFiles = [];
|
|
46619
46424
|
const resolvedTempDirectory = path.resolve(input.tempDirectory);
|
|
46620
46425
|
for (const relativePath of input.files) {
|
|
46621
46426
|
const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
|
|
46622
|
-
if (content === null)
|
|
46623
|
-
unmaterializedFiles.push(relativePath);
|
|
46624
|
-
continue;
|
|
46625
|
-
}
|
|
46427
|
+
if (content === null) continue;
|
|
46626
46428
|
const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
|
|
46627
|
-
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory))
|
|
46628
|
-
unmaterializedFiles.push(relativePath);
|
|
46629
|
-
continue;
|
|
46630
|
-
}
|
|
46429
|
+
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) continue;
|
|
46631
46430
|
yield* sync(() => {
|
|
46632
46431
|
fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
|
|
46633
46432
|
fs.writeFileSync(candidateTargetPath, content);
|
|
@@ -46644,7 +46443,6 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
46644
46443
|
return {
|
|
46645
46444
|
tempDirectory: input.tempDirectory,
|
|
46646
46445
|
materializedFiles,
|
|
46647
|
-
unmaterializedFiles,
|
|
46648
46446
|
cleanup: () => {
|
|
46649
46447
|
try {
|
|
46650
46448
|
fs.rmSync(input.tempDirectory, {
|
|
@@ -46914,80 +46712,54 @@ const buildSkippedChecks = (input) => {
|
|
|
46914
46712
|
skippedCheckReasons
|
|
46915
46713
|
};
|
|
46916
46714
|
};
|
|
46917
|
-
const
|
|
46918
|
-
const normalizeEvidence = (evidence) => evidence.replace(/\s+/g, " ").trim();
|
|
46919
|
-
const getDiagnosticMatchKeys = (diagnostic, evidence) => {
|
|
46715
|
+
const fingerprintDiagnostic = (diagnostic, lineText) => {
|
|
46920
46716
|
const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
46921
|
-
const
|
|
46922
|
-
|
|
46923
|
-
|
|
46924
|
-
|
|
46925
|
-
|
|
46926
|
-
|
|
46927
|
-
|
|
46928
|
-
|
|
46929
|
-
|
|
46930
|
-
|
|
46931
|
-
|
|
46932
|
-
|
|
46933
|
-
|
|
46934
|
-
|
|
46935
|
-
|
|
46936
|
-
|
|
46937
|
-
|
|
46938
|
-
|
|
46939
|
-
|
|
46940
|
-
|
|
46941
|
-
|
|
46942
|
-
|
|
46943
|
-
|
|
46944
|
-
|
|
46945
|
-
|
|
46946
|
-
|
|
46947
|
-
*
|
|
46948
|
-
* evidence. Stable identities combine plugin/rule, the diagnostic message,
|
|
46949
|
-
* and normalized diagnosed source, so unchanged findings can move across
|
|
46950
|
-
* files while changed constructs or messages remain new. Cardinality is
|
|
46951
|
-
* retained for identical findings. Diagnostics explicitly marked
|
|
46952
|
-
* `matchByOccurrence` may fall back to same-file plugin/rule/message matching
|
|
46953
|
-
* after same-file strict evidence matching. Cross-file evidence matching runs
|
|
46954
|
-
* last so a copy cannot consume a reformatted local occurrence. Unreadable
|
|
46955
|
-
* evidence uses the same conservative fallback rather than matching across
|
|
46956
|
-
* files without proof.
|
|
46717
|
+
const snippet = lineText === null || diagnostic.matchByOccurrence ? "" : createHash("sha1").update(lineText.trim()).digest("hex");
|
|
46718
|
+
return `${diagnostic.filePath}\u0000${ruleKey}\u0000${snippet}`;
|
|
46719
|
+
};
|
|
46720
|
+
/**
|
|
46721
|
+
* Diffs a head scan against a base scan to isolate the diagnostics a change
|
|
46722
|
+
* introduced (and count the ones it resolved). Matching is a multiset over a
|
|
46723
|
+
* position-independent fingerprint — `(filePath, plugin/rule, hash(flagged
|
|
46724
|
+
* line text))` — so inserting lines above an existing issue doesn't make it
|
|
46725
|
+
* look new, while a genuinely new occurrence (new line text, or one more of
|
|
46726
|
+
* the same) surfaces. Identical repeated findings are matched by count.
|
|
46727
|
+
*
|
|
46728
|
+
* Diagnostics carrying `matchByOccurrence` (resolved at diagnostic creation:
|
|
46729
|
+
* every Accessibility-category finding, plus rules opting in via their
|
|
46730
|
+
* `matchByOccurrence` metadata flag) drop the line-text snippet and match by
|
|
46731
|
+
* `(filePath, plugin/rule)` occurrence count alone. Their identity is the
|
|
46732
|
+
* flagged element, not the line's text, so editing the line (reindentation,
|
|
46733
|
+
* prettier reflow, collapsing a multi-line JSX element) doesn't reclassify a
|
|
46734
|
+
* pre-existing finding as new — while one MORE occurrence of the same rule in
|
|
46735
|
+
* the file still surfaces. Expression-level rules keep the line-text snippet:
|
|
46736
|
+
* there the flagged expression IS the finding, so changed text means new +
|
|
46737
|
+
* fixed.
|
|
46738
|
+
*
|
|
46739
|
+
* v1 limitation: the fingerprint keys on the head-relative `filePath`, and base
|
|
46740
|
+
* content is read at that same path. A file renamed by the change therefore has
|
|
46741
|
+
* no base match, so its pre-existing findings are reported as new. This
|
|
46742
|
+
* over-reports (never hides a real issue) and is rare; rename-aware base
|
|
46743
|
+
* resolution is a follow-up.
|
|
46957
46744
|
*/
|
|
46958
46745
|
const computeDiagnosticDelta = (input) => {
|
|
46959
|
-
const
|
|
46960
|
-
const
|
|
46961
|
-
|
|
46962
|
-
|
|
46963
|
-
|
|
46964
|
-
|
|
46965
|
-
|
|
46966
|
-
|
|
46967
|
-
|
|
46968
|
-
|
|
46969
|
-
|
|
46970
|
-
|
|
46971
|
-
|
|
46972
|
-
|
|
46973
|
-
if (matchedHeadDiagnosticIndexes.has(candidate.diagnosticIndex)) continue;
|
|
46974
|
-
const baseDiagnosticIndex = takeMatchingDiagnosticIndex(baseBuckets, getKey(candidate), matchedBaseDiagnosticIndexes);
|
|
46975
|
-
if (baseDiagnosticIndex === null) continue;
|
|
46976
|
-
matchedHeadDiagnosticIndexes.add(candidate.diagnosticIndex);
|
|
46977
|
-
matchedBaseDiagnosticIndexes.add(baseDiagnosticIndex);
|
|
46978
|
-
onMatch?.(candidate.diagnosticIndex, baseDiagnosticIndex);
|
|
46979
|
-
}
|
|
46980
|
-
};
|
|
46981
|
-
matchCandidates(baseBySameFileStableEvidence, (candidate) => candidate.sameFileStableEvidenceKey);
|
|
46982
|
-
matchCandidates(baseBySameFileFallback, (candidate) => candidate.sameFileFallbackKey);
|
|
46983
|
-
let crossFileMatchCount = 0;
|
|
46984
|
-
matchCandidates(baseByStableEvidence, (candidate) => candidate.stableEvidenceKey, (head, base) => {
|
|
46985
|
-
if (input.headDiagnostics[head]?.filePath !== input.baseDiagnostics[base]?.filePath) crossFileMatchCount += 1;
|
|
46986
|
-
});
|
|
46746
|
+
const unmatchedBaseByFingerprint = /* @__PURE__ */ new Map();
|
|
46747
|
+
for (const diagnostic of input.baseDiagnostics) {
|
|
46748
|
+
const key = fingerprintDiagnostic(diagnostic, input.readBaseLine(diagnostic.filePath, diagnostic.line));
|
|
46749
|
+
unmatchedBaseByFingerprint.set(key, (unmatchedBaseByFingerprint.get(key) ?? 0) + 1);
|
|
46750
|
+
}
|
|
46751
|
+
const newDiagnostics = [];
|
|
46752
|
+
for (const diagnostic of input.headDiagnostics) {
|
|
46753
|
+
const key = fingerprintDiagnostic(diagnostic, input.readHeadLine(diagnostic.filePath, diagnostic.line));
|
|
46754
|
+
const availableMatches = unmatchedBaseByFingerprint.get(key) ?? 0;
|
|
46755
|
+
if (availableMatches > 0) unmatchedBaseByFingerprint.set(key, availableMatches - 1);
|
|
46756
|
+
else newDiagnostics.push(diagnostic);
|
|
46757
|
+
}
|
|
46758
|
+
let fixedCount = 0;
|
|
46759
|
+
for (const remaining of unmatchedBaseByFingerprint.values()) fixedCount += remaining;
|
|
46987
46760
|
return {
|
|
46988
|
-
newDiagnostics
|
|
46989
|
-
fixedCount
|
|
46990
|
-
crossFileMatchCount
|
|
46761
|
+
newDiagnostics,
|
|
46762
|
+
fixedCount
|
|
46991
46763
|
};
|
|
46992
46764
|
};
|
|
46993
46765
|
/**
|
|
@@ -47019,12 +46791,6 @@ const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runProm
|
|
|
47019
46791
|
};
|
|
47020
46792
|
}).pipe(provide(Git.layerNode)));
|
|
47021
46793
|
const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
|
|
47022
|
-
const getBaselineDiffPlan = (directory, ref) => runPromise(gen(function* () {
|
|
47023
|
-
return yield* (yield* Git).baselineDiffPlan({
|
|
47024
|
-
directory,
|
|
47025
|
-
ref
|
|
47026
|
-
});
|
|
47027
|
-
}).pipe(provide(Git.layerNode)));
|
|
47028
46794
|
/**
|
|
47029
46795
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
|
47030
46796
|
* `files` with `--unified=0` against `baseRef` (or the index when `cached`),
|
|
@@ -47481,7 +47247,7 @@ const makeNoopConsole = () => ({
|
|
|
47481
47247
|
});
|
|
47482
47248
|
//#endregion
|
|
47483
47249
|
//#region src/cli/utils/version.ts
|
|
47484
|
-
const VERSION = "0.8.1-dev.
|
|
47250
|
+
const VERSION = "0.8.1-dev.7818922";
|
|
47485
47251
|
//#endregion
|
|
47486
47252
|
//#region src/cli/utils/json-mode.ts
|
|
47487
47253
|
let context = null;
|
|
@@ -47833,13 +47599,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47833
47599
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47834
47600
|
* standard `SENTRY_RELEASE` override.
|
|
47835
47601
|
*/
|
|
47836
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.
|
|
47602
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.7818922`;
|
|
47837
47603
|
/**
|
|
47838
47604
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47839
47605
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47840
47606
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47841
47607
|
*/
|
|
47842
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.
|
|
47608
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.7818922") ? "development" : "production");
|
|
47843
47609
|
/**
|
|
47844
47610
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47845
47611
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -60462,7 +60228,6 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60462
60228
|
new: summary.totalDiagnosticCount,
|
|
60463
60229
|
fixed: result.baselineDelta.fixedCount,
|
|
60464
60230
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
60465
|
-
crossFileMatches: result.baselineDelta.crossFileMatchCount ?? null,
|
|
60466
60231
|
degraded: false
|
|
60467
60232
|
}));
|
|
60468
60233
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -60576,49 +60341,28 @@ const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
|
60576
60341
|
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
60577
60342
|
};
|
|
60578
60343
|
//#endregion
|
|
60579
|
-
//#region src/cli/utils/is-materializable-git-source.ts
|
|
60580
|
-
const GIT_LFS_POINTER_HEADER = "version https://git-lfs.github.com/spec/v1";
|
|
60581
|
-
const isMaterializableGitSource = (content) => !content.includes("\0") && !content.startsWith(GIT_LFS_POINTER_HEADER);
|
|
60582
|
-
//#endregion
|
|
60583
60344
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
60584
60345
|
/**
|
|
60585
|
-
* Materializes the
|
|
60586
|
-
* `
|
|
60587
|
-
*
|
|
60588
|
-
*
|
|
60589
|
-
*
|
|
60590
|
-
*
|
|
60346
|
+
* Materializes the `ref` version of `files` into `tempDirectory` (via
|
|
60347
|
+
* `git show <ref>:<path>`), mirroring the project layout + head's config files
|
|
60348
|
+
* so the base lints under the same rules as head. Reuses the shared,
|
|
60349
|
+
* zip-slip-guarded `materializeSourceTree`. Per-file read misses (e.g. a file
|
|
60350
|
+
* the PR added, absent at base) are skipped, which is exactly what we want:
|
|
60351
|
+
* a brand-new file has no base counterpart, so all its findings are "new".
|
|
60591
60352
|
*/
|
|
60592
60353
|
const materializeBaselineFiles = (input) => runPromise(gen(function* () {
|
|
60593
60354
|
const git = yield* Git;
|
|
60594
|
-
|
|
60595
|
-
directory: input.directory,
|
|
60596
|
-
ref: input.ref
|
|
60597
|
-
});
|
|
60598
|
-
const baseFiles = input.baseFiles ?? diffPlan?.baseFiles;
|
|
60599
|
-
const headFiles = input.headFiles ?? diffPlan?.headFiles;
|
|
60600
|
-
const untrackedFiles = diffPlan?.untrackedFiles ?? [];
|
|
60601
|
-
if (baseFiles === void 0 || headFiles === void 0) return null;
|
|
60602
|
-
const files = [...new Set([...input.files, ...baseFiles])];
|
|
60603
|
-
const tree = yield* materializeSourceTree({
|
|
60355
|
+
return yield* materializeSourceTree({
|
|
60604
60356
|
directory: input.directory,
|
|
60605
|
-
files,
|
|
60357
|
+
files: input.files,
|
|
60606
60358
|
tempDirectory: input.tempDirectory,
|
|
60607
60359
|
readContent: (relativePath) => git.showRefContent({
|
|
60608
60360
|
directory: input.directory,
|
|
60609
60361
|
ref: input.ref,
|
|
60610
60362
|
relativePath,
|
|
60611
60363
|
options: { maxBufferBytes: GIT_SHOW_MAX_BUFFER_BYTES }
|
|
60612
|
-
})
|
|
60364
|
+
})
|
|
60613
60365
|
});
|
|
60614
|
-
const materializedFiles = new Set(tree.materializedFiles);
|
|
60615
|
-
return {
|
|
60616
|
-
...tree,
|
|
60617
|
-
baseFiles,
|
|
60618
|
-
headFiles,
|
|
60619
|
-
isComplete: baseFiles.every((filePath) => materializedFiles.has(filePath)),
|
|
60620
|
-
untrackedFiles
|
|
60621
|
-
};
|
|
60622
60366
|
}).pipe(provide(Git.layerNode)));
|
|
60623
60367
|
/**
|
|
60624
60368
|
* Resolves the commit a baseline scan should read base content from — the
|
|
@@ -60641,8 +60385,8 @@ const resolveMergeBaseRef = async (directory, ref) => {
|
|
|
60641
60385
|
//#region src/cli/utils/read-source-line.ts
|
|
60642
60386
|
/**
|
|
60643
60387
|
* Builds a cached `(filePath, line) -> source text` reader rooted at
|
|
60644
|
-
* `rootDirectory`, used
|
|
60645
|
-
*
|
|
60388
|
+
* `rootDirectory`, used to fingerprint a diagnostic by the content of its
|
|
60389
|
+
* flagged line (see `computeDiagnosticDelta`). Wraps core's
|
|
60646
60390
|
* `createNodeReadFileLinesSync` with per-file memoization + 1-indexed line
|
|
60647
60391
|
* lookup; unreadable files / out-of-range lines return `null`.
|
|
60648
60392
|
*/
|
|
@@ -60657,338 +60401,6 @@ const createSourceLineReader = (rootDirectory) => {
|
|
|
60657
60401
|
};
|
|
60658
60402
|
};
|
|
60659
60403
|
//#endregion
|
|
60660
|
-
//#region src/cli/utils/read-diagnostic-evidence.ts
|
|
60661
|
-
const SOURCE_EXTENSIONS = [
|
|
60662
|
-
".ts",
|
|
60663
|
-
".tsx",
|
|
60664
|
-
".js",
|
|
60665
|
-
".jsx",
|
|
60666
|
-
".mts",
|
|
60667
|
-
".cts",
|
|
60668
|
-
".mjs",
|
|
60669
|
-
".cjs"
|
|
60670
|
-
];
|
|
60671
|
-
const COMPONENT_WRAPPER_NAMES = new Set(["memo", "forwardRef"]);
|
|
60672
|
-
const getScriptKind = (filePath) => {
|
|
60673
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
60674
|
-
if (extension === ".tsx") return ts.ScriptKind.TSX;
|
|
60675
|
-
if (extension === ".jsx") return ts.ScriptKind.JSX;
|
|
60676
|
-
if (extension === ".js" || extension === ".mjs" || extension === ".cjs") return ts.ScriptKind.JS;
|
|
60677
|
-
return ts.ScriptKind.TS;
|
|
60678
|
-
};
|
|
60679
|
-
const resolveSafeSourcePath = (rootDirectory, filePath) => {
|
|
60680
|
-
try {
|
|
60681
|
-
const resolvedRootDirectory = fs.realpathSync(rootDirectory);
|
|
60682
|
-
const absolutePath = path.resolve(resolvedRootDirectory, filePath);
|
|
60683
|
-
const relativePath = path.relative(resolvedRootDirectory, absolutePath);
|
|
60684
|
-
if (relativePath.length === 0 || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) return null;
|
|
60685
|
-
const stats = fs.lstatSync(absolutePath);
|
|
60686
|
-
if (!stats.isFile() || stats.isSymbolicLink()) return null;
|
|
60687
|
-
const realPath = fs.realpathSync(absolutePath);
|
|
60688
|
-
const realRelativePath = path.relative(resolvedRootDirectory, realPath);
|
|
60689
|
-
return realRelativePath !== ".." && !realRelativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(realRelativePath) ? realPath : null;
|
|
60690
|
-
} catch {
|
|
60691
|
-
return null;
|
|
60692
|
-
}
|
|
60693
|
-
};
|
|
60694
|
-
const readSourceRecord = (rootDirectory, filePath, state) => {
|
|
60695
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
60696
|
-
if (state.recordByFilePath.has(normalizedFilePath)) return state.recordByFilePath.get(normalizedFilePath) ?? null;
|
|
60697
|
-
const absolutePath = resolveSafeSourcePath(rootDirectory, normalizedFilePath);
|
|
60698
|
-
if (absolutePath === null) {
|
|
60699
|
-
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60700
|
-
return null;
|
|
60701
|
-
}
|
|
60702
|
-
try {
|
|
60703
|
-
const sourceText = fs.readFileSync(absolutePath, "utf-8");
|
|
60704
|
-
const record = {
|
|
60705
|
-
absolutePath,
|
|
60706
|
-
filePath: normalizedFilePath,
|
|
60707
|
-
sourceFile: ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
60708
|
-
sourceText
|
|
60709
|
-
};
|
|
60710
|
-
state.recordByFilePath.set(normalizedFilePath, record);
|
|
60711
|
-
return record;
|
|
60712
|
-
} catch {
|
|
60713
|
-
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60714
|
-
return null;
|
|
60715
|
-
}
|
|
60716
|
-
};
|
|
60717
|
-
const listSourceRecords = (rootDirectory, state) => {
|
|
60718
|
-
if (state.sourceRecords !== null) return state.sourceRecords;
|
|
60719
|
-
state.sourceRecords = listSourceFiles(rootDirectory).flatMap((filePath) => {
|
|
60720
|
-
const record = readSourceRecord(rootDirectory, filePath, state);
|
|
60721
|
-
return record === null ? [] : [record];
|
|
60722
|
-
});
|
|
60723
|
-
return state.sourceRecords;
|
|
60724
|
-
};
|
|
60725
|
-
const unwrapExpression = (expression) => {
|
|
60726
|
-
let currentExpression = expression;
|
|
60727
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
60728
|
-
return currentExpression;
|
|
60729
|
-
};
|
|
60730
|
-
const getCalleeName = (expression) => {
|
|
60731
|
-
const unwrappedExpression = unwrapExpression(expression);
|
|
60732
|
-
if (ts.isIdentifier(unwrappedExpression)) return unwrappedExpression.text;
|
|
60733
|
-
if (ts.isPropertyAccessExpression(unwrappedExpression)) return unwrappedExpression.name.text;
|
|
60734
|
-
return null;
|
|
60735
|
-
};
|
|
60736
|
-
const isComponentWrapperCall = (node) => {
|
|
60737
|
-
const wrapperName = getCalleeName(node.expression);
|
|
60738
|
-
return wrapperName !== null && COMPONENT_WRAPPER_NAMES.has(wrapperName);
|
|
60739
|
-
};
|
|
60740
|
-
const getFunctionName = (node) => {
|
|
60741
|
-
if (ts.isFunctionDeclaration(node) && node.name !== void 0) return node.name.text;
|
|
60742
|
-
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
60743
|
-
let componentExpression = node;
|
|
60744
|
-
while (true) {
|
|
60745
|
-
const parent = componentExpression.parent;
|
|
60746
|
-
if (ts.isParenthesizedExpression(parent) || ts.isAsExpression(parent) || ts.isTypeAssertionExpression(parent) || ts.isNonNullExpression(parent) || ts.isSatisfiesExpression(parent)) {
|
|
60747
|
-
componentExpression = parent;
|
|
60748
|
-
continue;
|
|
60749
|
-
}
|
|
60750
|
-
if (ts.isCallExpression(parent) && parent.arguments[0] === componentExpression && isComponentWrapperCall(parent)) {
|
|
60751
|
-
componentExpression = parent;
|
|
60752
|
-
continue;
|
|
60753
|
-
}
|
|
60754
|
-
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) return parent.name.text;
|
|
60755
|
-
return node.name?.text ?? null;
|
|
60756
|
-
}
|
|
60757
|
-
}
|
|
60758
|
-
return null;
|
|
60759
|
-
};
|
|
60760
|
-
const isComponentFunction = (node) => ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node);
|
|
60761
|
-
const isDefaultExportedComponent = (record, node, componentName) => {
|
|
60762
|
-
if (ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)) return true;
|
|
60763
|
-
return record.sourceFile.statements.some((statement) => ts.isExportAssignment(statement) && ts.isIdentifier(statement.expression) && statement.expression.text === componentName);
|
|
60764
|
-
};
|
|
60765
|
-
const findEnclosingComponent = (record, diagnostic) => {
|
|
60766
|
-
if (!Number.isInteger(diagnostic.line) || diagnostic.line < 1) return null;
|
|
60767
|
-
const lineIndex = diagnostic.line - 1;
|
|
60768
|
-
const lineStarts = record.sourceFile.getLineStarts();
|
|
60769
|
-
const lineStart = lineStarts[lineIndex];
|
|
60770
|
-
if (lineStart === void 0) return null;
|
|
60771
|
-
const nextLineStart = lineStarts[lineIndex + 1] ?? record.sourceFile.end;
|
|
60772
|
-
const lineEnd = Math.max(lineStart, nextLineStart - 1);
|
|
60773
|
-
const columnIndex = Number.isInteger(diagnostic.column) ? Math.max(0, diagnostic.column - 1) : 0;
|
|
60774
|
-
const diagnosticPosition = Math.min(lineStart + columnIndex, lineEnd);
|
|
60775
|
-
let component = null;
|
|
60776
|
-
let componentWidth = Number.POSITIVE_INFINITY;
|
|
60777
|
-
const visit = (node) => {
|
|
60778
|
-
if (diagnosticPosition < node.getStart(record.sourceFile) || diagnosticPosition > node.end) return;
|
|
60779
|
-
if (isComponentFunction(node)) {
|
|
60780
|
-
const name = getFunctionName(node);
|
|
60781
|
-
const width = node.end - node.getStart(record.sourceFile);
|
|
60782
|
-
if (name !== null && /^[A-Z]/.test(name) && width < componentWidth) {
|
|
60783
|
-
component = {
|
|
60784
|
-
isDefaultExport: isDefaultExportedComponent(record, node, name),
|
|
60785
|
-
name,
|
|
60786
|
-
node,
|
|
60787
|
-
record
|
|
60788
|
-
};
|
|
60789
|
-
componentWidth = width;
|
|
60790
|
-
}
|
|
60791
|
-
}
|
|
60792
|
-
ts.forEachChild(node, visit);
|
|
60793
|
-
};
|
|
60794
|
-
visit(record.sourceFile);
|
|
60795
|
-
return component;
|
|
60796
|
-
};
|
|
60797
|
-
const moduleSpecifierTargetsComponent = (callerRecord, moduleSpecifier, componentRecord) => {
|
|
60798
|
-
if (!moduleSpecifier.startsWith(".")) return false;
|
|
60799
|
-
const unresolvedPath = path.resolve(path.dirname(callerRecord.absolutePath), moduleSpecifier);
|
|
60800
|
-
const specifiedExtension = path.extname(unresolvedPath).toLowerCase();
|
|
60801
|
-
const sourcePathWithoutExtension = SOURCE_EXTENSIONS.includes(specifiedExtension) ? unresolvedPath.slice(0, -specifiedExtension.length) : unresolvedPath;
|
|
60802
|
-
return [
|
|
60803
|
-
unresolvedPath,
|
|
60804
|
-
...SOURCE_EXTENSIONS.map((extension) => `${sourcePathWithoutExtension}${extension}`),
|
|
60805
|
-
...sourcePathWithoutExtension === unresolvedPath ? SOURCE_EXTENSIONS.map((extension) => path.join(unresolvedPath, `index${extension}`)) : []
|
|
60806
|
-
].includes(componentRecord.absolutePath);
|
|
60807
|
-
};
|
|
60808
|
-
const getComponentTagNames = (record, component) => {
|
|
60809
|
-
if (record.absolutePath === component.record.absolutePath) return new Set([component.name]);
|
|
60810
|
-
const tagNames = /* @__PURE__ */ new Set();
|
|
60811
|
-
for (const statement of record.sourceFile.statements) {
|
|
60812
|
-
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || !moduleSpecifierTargetsComponent(record, statement.moduleSpecifier.text, component.record)) continue;
|
|
60813
|
-
const importClause = statement.importClause;
|
|
60814
|
-
if (component.isDefaultExport && importClause?.name !== void 0) tagNames.add(importClause.name.text);
|
|
60815
|
-
const namedBindings = importClause?.namedBindings;
|
|
60816
|
-
if (namedBindings !== void 0 && ts.isNamedImports(namedBindings)) {
|
|
60817
|
-
for (const element of namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === component.name) tagNames.add(element.name.text);
|
|
60818
|
-
}
|
|
60819
|
-
}
|
|
60820
|
-
return tagNames;
|
|
60821
|
-
};
|
|
60822
|
-
const getTransparentCall = (functionNode) => {
|
|
60823
|
-
if (functionNode.body === void 0) return null;
|
|
60824
|
-
if (!ts.isBlock(functionNode.body)) {
|
|
60825
|
-
const expression = unwrapExpression(functionNode.body);
|
|
60826
|
-
return ts.isCallExpression(expression) ? expression : null;
|
|
60827
|
-
}
|
|
60828
|
-
if (functionNode.body.statements.length !== 1) return null;
|
|
60829
|
-
const statement = functionNode.body.statements[0];
|
|
60830
|
-
if (statement === void 0) return null;
|
|
60831
|
-
const expression = ts.isExpressionStatement(statement) || ts.isReturnStatement(statement) ? statement.expression : void 0;
|
|
60832
|
-
if (expression === void 0) return null;
|
|
60833
|
-
const unwrappedExpression = unwrapExpression(expression);
|
|
60834
|
-
return ts.isCallExpression(unwrappedExpression) ? unwrappedExpression : null;
|
|
60835
|
-
};
|
|
60836
|
-
const resolveTransparentTarget = (functionNode) => {
|
|
60837
|
-
const call = getTransparentCall(functionNode);
|
|
60838
|
-
if (call === null) return null;
|
|
60839
|
-
const target = unwrapExpression(call.expression);
|
|
60840
|
-
if (!ts.isIdentifier(target) || call.arguments.length !== functionNode.parameters.length) return null;
|
|
60841
|
-
for (const [parameterIndex, parameter] of functionNode.parameters.entries()) {
|
|
60842
|
-
const argument = call.arguments[parameterIndex];
|
|
60843
|
-
const unwrappedArgument = argument === void 0 ? null : unwrapExpression(argument);
|
|
60844
|
-
if (!ts.isIdentifier(parameter.name) || unwrappedArgument === null || !ts.isIdentifier(unwrappedArgument) || unwrappedArgument.text !== parameter.name.text) return null;
|
|
60845
|
-
}
|
|
60846
|
-
return target.text;
|
|
60847
|
-
};
|
|
60848
|
-
const getForwardingFunction = (declaration) => {
|
|
60849
|
-
if (ts.isFunctionDeclaration(declaration)) return declaration;
|
|
60850
|
-
if (!ts.isVariableDeclaration(declaration) || declaration.initializer === void 0) return null;
|
|
60851
|
-
const initializer = unwrapExpression(declaration.initializer);
|
|
60852
|
-
if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) return initializer;
|
|
60853
|
-
if (ts.isCallExpression(initializer) && getCalleeName(initializer.expression) === "useCallback") {
|
|
60854
|
-
const callback = initializer.arguments[0];
|
|
60855
|
-
if (callback !== void 0 && (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback))) return callback;
|
|
60856
|
-
}
|
|
60857
|
-
return null;
|
|
60858
|
-
};
|
|
60859
|
-
const getDeclaredBindingName = (node) => {
|
|
60860
|
-
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;
|
|
60861
|
-
if (ts.isImportClause(node)) return node.name?.text ?? null;
|
|
60862
|
-
if (ts.isImportSpecifier(node) || ts.isNamespaceImport(node)) return node.name.text;
|
|
60863
|
-
return null;
|
|
60864
|
-
};
|
|
60865
|
-
const findBindingScope = (node) => {
|
|
60866
|
-
let currentNode = node;
|
|
60867
|
-
while (currentNode !== void 0) {
|
|
60868
|
-
if (ts.isParameter(currentNode) && ts.isFunctionLike(currentNode.parent)) return currentNode.parent;
|
|
60869
|
-
if (ts.isBlock(currentNode) || ts.isModuleBlock(currentNode) || ts.isSourceFile(currentNode)) return currentNode;
|
|
60870
|
-
currentNode = currentNode.parent;
|
|
60871
|
-
}
|
|
60872
|
-
return node.getSourceFile();
|
|
60873
|
-
};
|
|
60874
|
-
const findBindingDeclaration = (record, binding) => {
|
|
60875
|
-
const usagePosition = binding.getStart(record.sourceFile);
|
|
60876
|
-
const candidates = [];
|
|
60877
|
-
const visit = (node) => {
|
|
60878
|
-
if (getDeclaredBindingName(node) === binding.text) {
|
|
60879
|
-
const scope = findBindingScope(node);
|
|
60880
|
-
const isVisibleScope = usagePosition >= scope.getStart(record.sourceFile) && usagePosition <= scope.end;
|
|
60881
|
-
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;
|
|
60882
|
-
if (isVisibleScope && isVisibleDeclaration) candidates.push({
|
|
60883
|
-
declaration: node,
|
|
60884
|
-
scope
|
|
60885
|
-
});
|
|
60886
|
-
}
|
|
60887
|
-
ts.forEachChild(node, visit);
|
|
60888
|
-
};
|
|
60889
|
-
visit(record.sourceFile);
|
|
60890
|
-
let nearestCandidate = null;
|
|
60891
|
-
let nearestScopeWidth = Number.POSITIVE_INFINITY;
|
|
60892
|
-
let isNearestScopeAmbiguous = false;
|
|
60893
|
-
for (const candidate of candidates) {
|
|
60894
|
-
const scopeWidth = candidate.scope.end - candidate.scope.getStart(record.sourceFile);
|
|
60895
|
-
if (scopeWidth < nearestScopeWidth) {
|
|
60896
|
-
nearestCandidate = candidate;
|
|
60897
|
-
nearestScopeWidth = scopeWidth;
|
|
60898
|
-
isNearestScopeAmbiguous = false;
|
|
60899
|
-
} else if (scopeWidth === nearestScopeWidth) isNearestScopeAmbiguous = true;
|
|
60900
|
-
}
|
|
60901
|
-
if (isNearestScopeAmbiguous) return null;
|
|
60902
|
-
const declaration = nearestCandidate?.declaration;
|
|
60903
|
-
return declaration !== void 0 && (ts.isVariableDeclaration(declaration) || ts.isFunctionDeclaration(declaration)) ? declaration : null;
|
|
60904
|
-
};
|
|
60905
|
-
const resolveHandlerBinding = (record, binding) => {
|
|
60906
|
-
const declaration = findBindingDeclaration(record, binding);
|
|
60907
|
-
if (declaration === null) return binding.text;
|
|
60908
|
-
const forwardingFunction = getForwardingFunction(declaration);
|
|
60909
|
-
const target = forwardingFunction === null ? null : resolveTransparentTarget(forwardingFunction);
|
|
60910
|
-
return target === null || target === binding.text ? binding.text : target;
|
|
60911
|
-
};
|
|
60912
|
-
const getJsxTagName = (node) => ts.isIdentifier(node.tagName) ? node.tagName.text : null;
|
|
60913
|
-
const getHandlerBinding = (node, propName) => {
|
|
60914
|
-
for (const property of node.attributes.properties) {
|
|
60915
|
-
if (!ts.isJsxAttribute(property) || property.name.getText() !== propName) continue;
|
|
60916
|
-
const initializer = property.initializer;
|
|
60917
|
-
if (initializer === void 0 || !ts.isJsxExpression(initializer)) return null;
|
|
60918
|
-
if (initializer.expression === void 0) return null;
|
|
60919
|
-
const expression = unwrapExpression(initializer.expression);
|
|
60920
|
-
return ts.isIdentifier(expression) ? expression : null;
|
|
60921
|
-
}
|
|
60922
|
-
return null;
|
|
60923
|
-
};
|
|
60924
|
-
const getForwardedHandlerAliases = (rootDirectory, component, propNames, state) => {
|
|
60925
|
-
const cacheKey = `${component.record.filePath}\0${component.name}\0${[...propNames].sort().join("\0")}`;
|
|
60926
|
-
const cachedAliases = state.aliasesByComponent.get(cacheKey);
|
|
60927
|
-
if (cachedAliases !== void 0) return cachedAliases;
|
|
60928
|
-
const bindingsByPropName = /* @__PURE__ */ new Map();
|
|
60929
|
-
const unresolvedPropNames = /* @__PURE__ */ new Set();
|
|
60930
|
-
let callsiteCount = 0;
|
|
60931
|
-
for (const record of listSourceRecords(rootDirectory, state)) {
|
|
60932
|
-
const tagNames = getComponentTagNames(record, component);
|
|
60933
|
-
if (tagNames.size === 0) continue;
|
|
60934
|
-
const visit = (node) => {
|
|
60935
|
-
if (record === component.record && node === component.node) return;
|
|
60936
|
-
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
60937
|
-
const tagName = getJsxTagName(node);
|
|
60938
|
-
if (tagName !== null && tagNames.has(tagName)) {
|
|
60939
|
-
callsiteCount += 1;
|
|
60940
|
-
for (const propName of propNames) {
|
|
60941
|
-
const binding = getHandlerBinding(node, propName);
|
|
60942
|
-
if (binding === null) {
|
|
60943
|
-
unresolvedPropNames.add(propName);
|
|
60944
|
-
continue;
|
|
60945
|
-
}
|
|
60946
|
-
const bindings = bindingsByPropName.get(propName) ?? /* @__PURE__ */ new Set();
|
|
60947
|
-
bindings.add(resolveHandlerBinding(record, binding));
|
|
60948
|
-
bindingsByPropName.set(propName, bindings);
|
|
60949
|
-
}
|
|
60950
|
-
}
|
|
60951
|
-
}
|
|
60952
|
-
ts.forEachChild(node, visit);
|
|
60953
|
-
};
|
|
60954
|
-
visit(record.sourceFile);
|
|
60955
|
-
}
|
|
60956
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
60957
|
-
if (callsiteCount > 0) for (const [propName, bindings] of bindingsByPropName) {
|
|
60958
|
-
if (bindings.size !== 1 || unresolvedPropNames.has(propName)) continue;
|
|
60959
|
-
const [bindingName] = bindings;
|
|
60960
|
-
if (bindingName !== void 0) aliases.set(propName, bindingName);
|
|
60961
|
-
}
|
|
60962
|
-
state.aliasesByComponent.set(cacheKey, aliases);
|
|
60963
|
-
return aliases;
|
|
60964
|
-
};
|
|
60965
|
-
const normalizeForwardedHandlers = (evidence, aliases) => {
|
|
60966
|
-
let normalizedEvidence = evidence;
|
|
60967
|
-
for (const [propName, bindingName] of aliases) normalizedEvidence = normalizedEvidence.replace(new RegExp(`\\{\\s*${propName}\\s*\\}`, "g"), `{${bindingName}}`).replace(new RegExp(`=>\\s*${propName}\\s*\\(`, "g"), `=> ${bindingName}(`);
|
|
60968
|
-
return normalizedEvidence;
|
|
60969
|
-
};
|
|
60970
|
-
const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
60971
|
-
const state = {
|
|
60972
|
-
aliasesByComponent: /* @__PURE__ */ new Map(),
|
|
60973
|
-
recordByFilePath: /* @__PURE__ */ new Map(),
|
|
60974
|
-
sourceRecords: null
|
|
60975
|
-
};
|
|
60976
|
-
return (diagnostic) => {
|
|
60977
|
-
const record = readSourceRecord(rootDirectory, diagnostic.filePath, state);
|
|
60978
|
-
if (record === null || !Number.isInteger(diagnostic.line)) return null;
|
|
60979
|
-
const sourceLines = record.sourceText.split(/\r?\n/);
|
|
60980
|
-
const startLineIndex = Math.max(0, diagnostic.line - 1);
|
|
60981
|
-
const endLineIndex = Math.max(startLineIndex, (diagnostic.endLine ?? diagnostic.line) - 1);
|
|
60982
|
-
const evidence = sourceLines.slice(startLineIndex, endLineIndex + 1).join("\n");
|
|
60983
|
-
if (!options.resolveForwardedHandlers) return evidence;
|
|
60984
|
-
const propNames = new Set(evidence.match(/\bon[A-Z]\w*\b/g) ?? []);
|
|
60985
|
-
if (propNames.size === 0) return evidence;
|
|
60986
|
-
const component = findEnclosingComponent(record, diagnostic);
|
|
60987
|
-
if (component === null) return evidence;
|
|
60988
|
-
return normalizeForwardedHandlers(evidence, getForwardedHandlerAliases(rootDirectory, component, propNames, state));
|
|
60989
|
-
};
|
|
60990
|
-
};
|
|
60991
|
-
//#endregion
|
|
60992
60404
|
//#region src/cli/utils/build-no-score-message.ts
|
|
60993
60405
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
60994
60406
|
const buildNoScoreMessage = (isScoreDisabled) => {
|
|
@@ -62232,8 +61644,6 @@ const runBaselineComparison = async (params) => {
|
|
|
62232
61644
|
directory: params.directory,
|
|
62233
61645
|
ref: params.baselineRef,
|
|
62234
61646
|
files: params.options.includePaths,
|
|
62235
|
-
baseFiles: params.baseFiles,
|
|
62236
|
-
headFiles: params.headFiles,
|
|
62237
61647
|
tempDirectory
|
|
62238
61648
|
}).catch((error) => {
|
|
62239
61649
|
rmSync(tempDirectory, {
|
|
@@ -62242,24 +61652,7 @@ const runBaselineComparison = async (params) => {
|
|
|
62242
61652
|
});
|
|
62243
61653
|
throw error;
|
|
62244
61654
|
});
|
|
62245
|
-
if (snapshot === null) {
|
|
62246
|
-
rmSync(tempDirectory, {
|
|
62247
|
-
recursive: true,
|
|
62248
|
-
force: true
|
|
62249
|
-
});
|
|
62250
|
-
return null;
|
|
62251
|
-
}
|
|
62252
61655
|
try {
|
|
62253
|
-
if (!snapshot.isComplete) return null;
|
|
62254
|
-
const analyzedHeadFiles = new Set(params.headAnalyzedFiles.map(toForwardSlashes));
|
|
62255
|
-
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
62256
|
-
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
62257
|
-
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
62258
|
-
for (const filePath of params.options.includePaths) {
|
|
62259
|
-
const normalizedFilePath = toForwardSlashes(filePath);
|
|
62260
|
-
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
62261
|
-
}
|
|
62262
|
-
if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
62263
61656
|
const baseLayers = buildRuntimeLayers({
|
|
62264
61657
|
directory: snapshot.tempDirectory,
|
|
62265
61658
|
hasConfigOverride: true,
|
|
@@ -62275,7 +61668,7 @@ const runBaselineComparison = async (params) => {
|
|
|
62275
61668
|
});
|
|
62276
61669
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
62277
61670
|
directory: snapshot.tempDirectory,
|
|
62278
|
-
includePaths:
|
|
61671
|
+
includePaths: params.options.includePaths,
|
|
62279
61672
|
customRulesOnly: params.options.customRulesOnly,
|
|
62280
61673
|
respectInlineDisables: params.options.respectInlineDisables,
|
|
62281
61674
|
warnings: params.options.warnings,
|
|
@@ -62292,22 +61685,18 @@ const runBaselineComparison = async (params) => {
|
|
|
62292
61685
|
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
62293
61686
|
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))));
|
|
62294
61687
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
62295
|
-
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
62296
61688
|
const delta = computeDiagnosticDelta({
|
|
62297
61689
|
headDiagnostics: params.headDiagnostics,
|
|
62298
61690
|
baseDiagnostics: baseOutput.diagnostics,
|
|
62299
61691
|
readHeadLine: createSourceLineReader(params.directory),
|
|
62300
|
-
readBaseLine: createSourceLineReader(snapshot.tempDirectory)
|
|
62301
|
-
readHeadEvidence: createDiagnosticEvidenceReader(params.directory, { resolveForwardedHandlers: true }),
|
|
62302
|
-
readBaseEvidence: createDiagnosticEvidenceReader(snapshot.tempDirectory)
|
|
61692
|
+
readBaseLine: createSourceLineReader(snapshot.tempDirectory)
|
|
62303
61693
|
});
|
|
62304
61694
|
return {
|
|
62305
61695
|
displayDiagnostics: delta.newDiagnostics,
|
|
62306
61696
|
baselineDelta: {
|
|
62307
61697
|
baseRef: params.baselineRef,
|
|
62308
|
-
fixedCount:
|
|
62309
|
-
baseTotalCount: baseOutput.diagnostics.length
|
|
62310
|
-
crossFileMatchCount: delta.crossFileMatchCount
|
|
61698
|
+
fixedCount: delta.fixedCount,
|
|
61699
|
+
baseTotalCount: baseOutput.diagnostics.length
|
|
62311
61700
|
}
|
|
62312
61701
|
};
|
|
62313
61702
|
} finally {
|
|
@@ -62414,9 +61803,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
62414
61803
|
headDiagnostics: output.diagnostics,
|
|
62415
61804
|
resolvedNodeBinaryPath,
|
|
62416
61805
|
baselineRef: options.baseline.ref,
|
|
62417
|
-
baseFiles: options.baseline.baseFiles,
|
|
62418
|
-
headFiles: options.baseline.headFiles,
|
|
62419
|
-
headAnalyzedFiles: output.analyzedFiles,
|
|
62420
61806
|
deadlineEpochMs
|
|
62421
61807
|
});
|
|
62422
61808
|
if (comparison) {
|
|
@@ -64701,25 +64087,20 @@ const resolveProjectRelativeDirectory = (rootDirectory, projectDirectory) => {
|
|
|
64701
64087
|
return relativeProjectDirectory;
|
|
64702
64088
|
};
|
|
64703
64089
|
//#endregion
|
|
64704
|
-
//#region src/cli/utils/resolve-project-
|
|
64705
|
-
const
|
|
64090
|
+
//#region src/cli/utils/resolve-project-diff-include-paths.ts
|
|
64091
|
+
const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
|
|
64706
64092
|
const relativeProjectDirectory = resolveProjectRelativeDirectory(rootDirectory, projectDirectory);
|
|
64707
64093
|
if (relativeProjectDirectory === null) return [];
|
|
64708
|
-
const
|
|
64709
|
-
if (relativeProjectDirectory.length === 0) return
|
|
64094
|
+
const changedSourceFiles = filterSourceFiles(diffInfo.changedFiles);
|
|
64095
|
+
if (relativeProjectDirectory.length === 0) return changedSourceFiles;
|
|
64710
64096
|
const projectPrefix = `${relativeProjectDirectory}/`;
|
|
64711
|
-
return
|
|
64097
|
+
return changedSourceFiles.flatMap((filePath) => {
|
|
64712
64098
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
64713
64099
|
if (!normalizedFilePath.startsWith(projectPrefix)) return [];
|
|
64714
64100
|
const projectRelativePath = normalizedFilePath.slice(projectPrefix.length);
|
|
64715
64101
|
return projectRelativePath.length > 0 ? [projectRelativePath] : [];
|
|
64716
64102
|
});
|
|
64717
64103
|
};
|
|
64718
|
-
//#endregion
|
|
64719
|
-
//#region src/cli/utils/resolve-project-diff-include-paths.ts
|
|
64720
|
-
const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
|
|
64721
|
-
return resolveProjectSourceFilePaths(rootDirectory, projectDirectory, diffInfo.changedFiles);
|
|
64722
|
-
};
|
|
64723
64104
|
/**
|
|
64724
64105
|
* Re-keys repo-relative changed line ranges (from `getChangedLineRanges`) to
|
|
64725
64106
|
* the scanned project, mirroring `resolveProjectDiffIncludePaths`'s boundary
|
|
@@ -65258,7 +64639,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
65258
64639
|
const isDiffMode = scope !== "full";
|
|
65259
64640
|
const comparisonBaseRef = isDiffMode && diffInfo && !diffInfo.isCurrentChanges ? diffInfo.baseSha ? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseSha) : diffInfo.diffBaseRef ?? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseBranch) : null;
|
|
65260
64641
|
const baselineRef = scope === "changed" ? comparisonBaseRef : null;
|
|
65261
|
-
const baselineDiffPlan = baselineRef === null ? null : await getBaselineDiffPlan(resolvedDirectory, baselineRef);
|
|
65262
64642
|
const linesBaseRef = diffInfo?.isCurrentChanges ? "HEAD" : comparisonBaseRef;
|
|
65263
64643
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
65264
64644
|
directory: resolvedDirectory,
|
|
@@ -65289,13 +64669,10 @@ const inspectAction = async (directory, flags) => {
|
|
|
65289
64669
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
65290
64670
|
let includePaths;
|
|
65291
64671
|
let supplyChainManifestChanged = false;
|
|
65292
|
-
const projectBaselineBaseFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.baseFiles);
|
|
65293
|
-
const projectBaselineHeadFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.headFiles);
|
|
65294
64672
|
if (isDiffMode) {
|
|
65295
64673
|
const changedSourceFiles = diffInfo === null ? [] : resolveProjectDiffIncludePaths(resolvedDirectory, scanDirectory, diffInfo);
|
|
65296
64674
|
supplyChainManifestChanged = supplyChainEnabled && diffInfo !== null && projectManifestChanged(resolvedDirectory, scanDirectory, diffInfo);
|
|
65297
|
-
|
|
65298
|
-
if (changedSourceFiles.length === 0 && !supplyChainManifestChanged && !hasBaselineOnlyFiles) {
|
|
64675
|
+
if (changedSourceFiles.length === 0 && !supplyChainManifestChanged) {
|
|
65299
64676
|
if (!isQuiet) {
|
|
65300
64677
|
cliLogger.dim(`No changed source files in ${scanDirectory}, skipping.`);
|
|
65301
64678
|
cliLogger.break();
|
|
@@ -65303,7 +64680,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
65303
64680
|
return null;
|
|
65304
64681
|
}
|
|
65305
64682
|
includePaths = [...changedSourceFiles];
|
|
65306
|
-
if (includePaths.length === 0 && hasBaselineOnlyFiles) includePaths.push(...projectBaselineBaseFiles ?? []);
|
|
65307
64683
|
if (supplyChainManifestChanged) includePaths.push("package.json");
|
|
65308
64684
|
}
|
|
65309
64685
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
@@ -65315,11 +64691,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
65315
64691
|
configSourceDirectory: projectConfigSourceDirectory ?? void 0,
|
|
65316
64692
|
suppressRendering: isMultiProject,
|
|
65317
64693
|
concurrentScan: isMultiProject,
|
|
65318
|
-
baseline: baselineRef
|
|
65319
|
-
ref: baselineRef,
|
|
65320
|
-
baseFiles: projectBaselineBaseFiles,
|
|
65321
|
-
headFiles: projectBaselineHeadFiles
|
|
65322
|
-
} : void 0,
|
|
64694
|
+
baseline: baselineRef ? { ref: baselineRef } : void 0,
|
|
65323
64695
|
changedLineRanges: scope === "lines" && changedLineRanges !== null ? resolveProjectChangedLineRanges(resolvedDirectory, scanDirectory, changedLineRanges) : void 0,
|
|
65324
64696
|
supplyChainManifestChanged
|
|
65325
64697
|
});
|
|
@@ -66519,4 +65891,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
66519
65891
|
export {};
|
|
66520
65892
|
|
|
66521
65893
|
//# sourceMappingURL=cli.js.map
|
|
66522
|
-
//# debugId=
|
|
65894
|
+
//# debugId=9e32582f-b296-5216-a028-7cc19ed83931
|