react-doctor 0.8.1-dev.6fcd9c9 → 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 -749
- package/dist/index.d.ts +15 -33
- package/dist/index.js +23 -254
- package/dist/lsp.js +23 -254
- package/package.json +3 -3
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 > latestSupportedMajor) return false;
|
|
37350
|
-
if (nextMajor > latestSupportedMajor) 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,65 +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 (shouldReplaceSupportedDependencyFact(facts.mobx, nextMobxFact, 6)) 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
|
-
const zustandSpec = getDependencySpec(packageJson, "zustand");
|
|
37393
|
-
if (zustandSpec !== null) {
|
|
37394
|
-
const nextZustandFact = {
|
|
37395
|
-
version: resolveCatalogBackedDependencyVersion({
|
|
37396
|
-
rootDirectory: directory,
|
|
37397
|
-
rootPackageJson: packageJson,
|
|
37398
|
-
packageName: "zustand",
|
|
37399
|
-
version: zustandSpec
|
|
37400
|
-
}),
|
|
37401
|
-
sourceDirectory: directory
|
|
37402
|
-
};
|
|
37403
|
-
if (shouldReplaceSupportedDependencyFact(facts.zustand, nextZustandFact, 5)) facts.zustand = nextZustandFact;
|
|
37404
|
-
}
|
|
37405
37360
|
if (facts.reanimatedVersion === null) {
|
|
37406
37361
|
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
37407
37362
|
if (spec !== null) facts.reanimatedVersion = spec;
|
|
37408
37363
|
}
|
|
37409
|
-
if (facts.valtioVersion === null) {
|
|
37410
|
-
const spec = getDependencySpec(packageJson, "valtio");
|
|
37411
|
-
if (spec !== null) facts.valtioVersion = spec;
|
|
37412
|
-
}
|
|
37413
37364
|
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
37414
37365
|
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
37415
37366
|
facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
|
|
37416
|
-
const remotionSpec = getDependencySpec(packageJson, "remotion");
|
|
37417
|
-
if (remotionSpec !== null) {
|
|
37418
|
-
facts.hasRemotionDependency = true;
|
|
37419
|
-
const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
|
|
37420
|
-
rootDirectory,
|
|
37421
|
-
rootPackageJson,
|
|
37422
|
-
packageName: "remotion",
|
|
37423
|
-
version: remotionSpec
|
|
37424
|
-
});
|
|
37425
|
-
const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
|
|
37426
|
-
if (remotionMajorVersion === null) {
|
|
37427
|
-
facts.hasUnknownRemotionVersion = true;
|
|
37428
|
-
facts.remotionVersion = null;
|
|
37429
|
-
} else if (!facts.hasUnknownRemotionVersion) {
|
|
37430
|
-
const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
|
|
37431
|
-
if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
|
|
37432
|
-
}
|
|
37433
|
-
}
|
|
37434
37367
|
};
|
|
37435
37368
|
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
37436
37369
|
const facts = {
|
|
@@ -37450,28 +37383,12 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37450
37383
|
version: null,
|
|
37451
37384
|
sourceDirectory: null
|
|
37452
37385
|
},
|
|
37453
|
-
valtioVersion: null,
|
|
37454
|
-
mobx: {
|
|
37455
|
-
version: null,
|
|
37456
|
-
sourceDirectory: null
|
|
37457
|
-
},
|
|
37458
|
-
hasMobxReact: false,
|
|
37459
|
-
hasMobxReactLite: false,
|
|
37460
|
-
hasMobxStateTree: false,
|
|
37461
|
-
hasMobxReactObserver: false,
|
|
37462
|
-
zustand: {
|
|
37463
|
-
version: null,
|
|
37464
|
-
sourceDirectory: null
|
|
37465
|
-
},
|
|
37466
37386
|
hasReactNativeAwarePackage: false,
|
|
37467
37387
|
hasReanimatedAwarePackage: false,
|
|
37468
37388
|
hasSsrDependency: false,
|
|
37469
|
-
hasRemotionDependency: false,
|
|
37470
|
-
hasUnknownRemotionVersion: false,
|
|
37471
|
-
remotionVersion: null,
|
|
37472
37389
|
reanimatedVersion: null
|
|
37473
37390
|
};
|
|
37474
|
-
evaluateManifestFacts(facts, rootPackageJson, rootDirectory
|
|
37391
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
37475
37392
|
let isReactGroupSettled = !collectReactGroup;
|
|
37476
37393
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
37477
37394
|
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
@@ -37480,7 +37397,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37480
37397
|
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
37481
37398
|
visitedDirectories.add(workspaceDirectory);
|
|
37482
37399
|
const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
|
|
37483
|
-
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory
|
|
37400
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
37484
37401
|
const info = extractDependencyInfo(workspacePackageJson);
|
|
37485
37402
|
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
37486
37403
|
if (isReactGroupSettled) continue;
|
|
@@ -37701,24 +37618,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37701
37618
|
tailwindVersion: null,
|
|
37702
37619
|
zodVersion: null,
|
|
37703
37620
|
zodMajorVersion: null,
|
|
37704
|
-
mobxVersion: null,
|
|
37705
|
-
mobxMajorVersion: null,
|
|
37706
|
-
hasMobxReact: false,
|
|
37707
|
-
hasMobxReactLite: false,
|
|
37708
|
-
hasMobxStateTree: false,
|
|
37709
|
-
hasMobxReactObserver: false,
|
|
37710
|
-
zustandVersion: null,
|
|
37711
|
-
zustandMajorVersion: null,
|
|
37712
37621
|
framework: "unknown",
|
|
37713
37622
|
hasTypeScript: hasOwnTsConfig,
|
|
37714
37623
|
hasReactCompiler: false,
|
|
37715
37624
|
hasReactCompilerLintPlugin: false,
|
|
37716
37625
|
hasTanStackQuery: false,
|
|
37717
|
-
valtioVersion: null,
|
|
37718
|
-
valtioMajorVersion: null,
|
|
37719
|
-
hasRemotion: false,
|
|
37720
|
-
remotionVersion: null,
|
|
37721
|
-
remotionMajorVersion: null,
|
|
37722
37626
|
hasSsrDependency: false,
|
|
37723
37627
|
preactVersion: null,
|
|
37724
37628
|
preactMajorVersion: null,
|
|
@@ -37817,12 +37721,6 @@ const discoverProject = (directory) => {
|
|
|
37817
37721
|
packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
|
|
37818
37722
|
version: workspaceFacts.shopifyFlashList.version
|
|
37819
37723
|
}) : null;
|
|
37820
|
-
const zustandVersion = resolveCatalogBackedDependencyVersion({
|
|
37821
|
-
rootDirectory: directory,
|
|
37822
|
-
rootPackageJson: packageJson,
|
|
37823
|
-
packageName: "zustand",
|
|
37824
|
-
version: workspaceFacts.zustand.version
|
|
37825
|
-
});
|
|
37826
37724
|
const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
|
|
37827
37725
|
const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
|
|
37828
37726
|
const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
|
|
@@ -37831,20 +37729,7 @@ const discoverProject = (directory) => {
|
|
|
37831
37729
|
packageName: "next",
|
|
37832
37730
|
version: workspaceFacts.next.version
|
|
37833
37731
|
}) : null;
|
|
37834
|
-
const valtioVersion = resolveCatalogBackedDependencyVersion({
|
|
37835
|
-
rootDirectory: directory,
|
|
37836
|
-
rootPackageJson: packageJson,
|
|
37837
|
-
packageName: "valtio",
|
|
37838
|
-
version: workspaceFacts.valtioVersion
|
|
37839
|
-
});
|
|
37840
|
-
const mobxVersion = resolveCatalogBackedDependencyVersion({
|
|
37841
|
-
rootDirectory: directory,
|
|
37842
|
-
rootPackageJson: packageJson,
|
|
37843
|
-
packageName: "mobx",
|
|
37844
|
-
version: workspaceFacts.mobx.version
|
|
37845
|
-
});
|
|
37846
37732
|
const preactVersion = getPreactVersion(packageJson);
|
|
37847
|
-
const remotionVersion = workspaceFacts.remotionVersion;
|
|
37848
37733
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
37849
37734
|
const projectInfo = {
|
|
37850
37735
|
rootDirectory: directory,
|
|
@@ -37854,24 +37739,11 @@ const discoverProject = (directory) => {
|
|
|
37854
37739
|
tailwindVersion,
|
|
37855
37740
|
zodVersion,
|
|
37856
37741
|
zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
|
|
37857
|
-
mobxVersion,
|
|
37858
|
-
mobxMajorVersion: mobxVersion === null ? null : getLowestDependencyMajor(mobxVersion),
|
|
37859
|
-
hasMobxReact: workspaceFacts.hasMobxReact,
|
|
37860
|
-
hasMobxReactLite: workspaceFacts.hasMobxReactLite,
|
|
37861
|
-
hasMobxStateTree: workspaceFacts.hasMobxStateTree,
|
|
37862
|
-
hasMobxReactObserver: workspaceFacts.hasMobxReactObserver,
|
|
37863
|
-
zustandVersion,
|
|
37864
|
-
zustandMajorVersion: zustandVersion === null ? null : getLowestDependencyMajor(zustandVersion),
|
|
37865
37742
|
framework,
|
|
37866
37743
|
hasTypeScript,
|
|
37867
37744
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
37868
37745
|
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
37869
37746
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
37870
|
-
valtioVersion,
|
|
37871
|
-
valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
|
|
37872
|
-
hasRemotion: workspaceFacts.hasRemotionDependency,
|
|
37873
|
-
remotionVersion,
|
|
37874
|
-
remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
|
|
37875
37747
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
37876
37748
|
preactVersion,
|
|
37877
37749
|
preactMajorVersion: parseReactMajor(preactVersion),
|
|
@@ -38105,6 +37977,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
38105
37977
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
38106
37978
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
38107
37979
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37980
|
+
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
38108
37981
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
38109
37982
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
38110
37983
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -40426,25 +40299,9 @@ const BABEL_CONFIG_FILE_NAMES = [
|
|
|
40426
40299
|
".babelrc.json"
|
|
40427
40300
|
];
|
|
40428
40301
|
const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
|
|
40429
|
-
const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
|
|
40430
|
-
const REACT_NATIVE_PACKAGE = "react-native";
|
|
40431
|
-
const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
|
|
40432
|
-
const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
|
|
40433
40302
|
const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
|
|
40434
40303
|
const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
|
|
40435
|
-
const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
|
|
40436
|
-
const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
|
|
40437
|
-
const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson$1(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
|
|
40438
|
-
if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
|
|
40439
|
-
const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
|
|
40440
|
-
if (normalizedVersionRange === null) return false;
|
|
40441
|
-
const minimumVersion = import_semver.minVersion(normalizedVersionRange);
|
|
40442
|
-
return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
|
|
40443
|
-
};
|
|
40444
|
-
const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
|
|
40445
40304
|
const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
40446
|
-
const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
|
|
40447
|
-
const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
|
|
40448
40305
|
for (const fileName of BABEL_CONFIG_FILE_NAMES) {
|
|
40449
40306
|
const filePath = Path.join(rootDirectory, fileName);
|
|
40450
40307
|
if (!isFile(filePath)) continue;
|
|
@@ -40454,7 +40311,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
40454
40311
|
} catch {
|
|
40455
40312
|
continue;
|
|
40456
40313
|
}
|
|
40457
|
-
if (
|
|
40314
|
+
if (contents.includes(LEGACY_PRESET_SPEC)) return [buildReactNativeDiagnostic({
|
|
40458
40315
|
filePath: fileName,
|
|
40459
40316
|
rule: "rn-no-metro-babel-preset",
|
|
40460
40317
|
severity: "error",
|
|
@@ -40477,6 +40334,12 @@ const checkReactNativeProject = (rootDirectory, project) => {
|
|
|
40477
40334
|
return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
|
|
40478
40335
|
};
|
|
40479
40336
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
40337
|
+
const DEPENDENCY_SECTIONS = [
|
|
40338
|
+
"dependencies",
|
|
40339
|
+
"devDependencies",
|
|
40340
|
+
"peerDependencies",
|
|
40341
|
+
"optionalDependencies"
|
|
40342
|
+
];
|
|
40480
40343
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
40481
40344
|
"19.0": {
|
|
40482
40345
|
rceFixedVersion: "19.0.1",
|
|
@@ -40523,13 +40386,20 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
|
|
|
40523
40386
|
for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
|
|
40524
40387
|
return [...directories];
|
|
40525
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
|
+
};
|
|
40526
40396
|
const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
|
|
40527
40397
|
const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
|
|
40528
40398
|
if (isFile(manifestPath)) {
|
|
40529
40399
|
const installedVersion = import_semver.valid(readPackageJson$1(manifestPath).version ?? null);
|
|
40530
40400
|
if (installedVersion !== null) return installedVersion;
|
|
40531
40401
|
}
|
|
40532
|
-
const candidateSpecs = [
|
|
40402
|
+
const candidateSpecs = [readDeclaredSpec(readPackageJson$1(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
|
|
40533
40403
|
for (const spec of candidateSpecs) {
|
|
40534
40404
|
const pinnedVersion = spec === null ? null : import_semver.valid(spec);
|
|
40535
40405
|
if (pinnedVersion !== null) return pinnedVersion;
|
|
@@ -41209,24 +41079,9 @@ const buildCapabilities = (project) => {
|
|
|
41209
41079
|
})) capabilities.add("tailwind:3.4");
|
|
41210
41080
|
if (project.zodVersion !== null) capabilities.add("zod");
|
|
41211
41081
|
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
41212
|
-
if (project.mobxVersion !== void 0 && project.mobxVersion !== null || project.hasMobxReact === true || project.hasMobxReactLite === true || project.hasMobxStateTree === true || project.hasMobxReactObserver === true) capabilities.add("mobx");
|
|
41213
|
-
if (project.hasMobxReact === true) capabilities.add("mobx-react");
|
|
41214
|
-
if (project.hasMobxReactLite === true) capabilities.add("mobx-react-lite");
|
|
41215
|
-
if (project.hasMobxReact === true || project.hasMobxReactLite === true) capabilities.add("mobx-react-binding");
|
|
41216
|
-
if (project.hasMobxStateTree === true) capabilities.add("mobx-state-tree");
|
|
41217
|
-
if (project.hasMobxReactObserver === true) capabilities.add("mobx-react-observer");
|
|
41218
|
-
if (project.mobxMajorVersion !== void 0 && project.mobxMajorVersion !== null && project.mobxMajorVersion >= 4 && project.mobxMajorVersion <= 6) addMajorLadder(capabilities, "mobx", project.mobxMajorVersion, 4, 6);
|
|
41219
|
-
if (project.zustandVersion !== void 0 && project.zustandVersion !== null) capabilities.add("zustand");
|
|
41220
|
-
if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addMajorLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
|
|
41221
41082
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
41222
41083
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
41223
41084
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
41224
|
-
if (project.valtioVersion !== null) capabilities.add("valtio");
|
|
41225
|
-
addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
|
|
41226
|
-
if (project.hasRemotion) {
|
|
41227
|
-
capabilities.add("remotion");
|
|
41228
|
-
addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
|
|
41229
|
-
}
|
|
41230
41085
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
41231
41086
|
if (project.preactVersion !== null) capabilities.add("preact");
|
|
41232
41087
|
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
@@ -42479,35 +42334,6 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
42479
42334
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
42480
42335
|
};
|
|
42481
42336
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
42482
|
-
const parseBaselineDiffPlan = (value) => {
|
|
42483
|
-
const entries = splitNullSeparated(value);
|
|
42484
|
-
const baseFiles = /* @__PURE__ */ new Set();
|
|
42485
|
-
const headFiles = /* @__PURE__ */ new Set();
|
|
42486
|
-
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
|
|
42487
|
-
const status = entries[entryIndex];
|
|
42488
|
-
const filePath = entries[entryIndex + 1];
|
|
42489
|
-
if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
|
|
42490
|
-
if (status === "A") {
|
|
42491
|
-
headFiles.add(filePath);
|
|
42492
|
-
continue;
|
|
42493
|
-
}
|
|
42494
|
-
if (status === "D") {
|
|
42495
|
-
baseFiles.add(filePath);
|
|
42496
|
-
continue;
|
|
42497
|
-
}
|
|
42498
|
-
if (status === "M" || status === "T") {
|
|
42499
|
-
baseFiles.add(filePath);
|
|
42500
|
-
headFiles.add(filePath);
|
|
42501
|
-
continue;
|
|
42502
|
-
}
|
|
42503
|
-
return null;
|
|
42504
|
-
}
|
|
42505
|
-
return {
|
|
42506
|
-
baseFiles: [...baseFiles],
|
|
42507
|
-
headFiles: [...headFiles],
|
|
42508
|
-
untrackedFiles: []
|
|
42509
|
-
};
|
|
42510
|
-
};
|
|
42511
42337
|
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
42512
42338
|
/**
|
|
42513
42339
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
@@ -42710,45 +42536,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42710
42536
|
githubViewerPermission,
|
|
42711
42537
|
branchExists,
|
|
42712
42538
|
mergeBase,
|
|
42713
|
-
baselineDiffPlan: (input) => {
|
|
42714
|
-
if (!isSafeGitRevision(input.ref)) return succeed$2(null);
|
|
42715
|
-
return gen(function* () {
|
|
42716
|
-
const unmerged = yield* runGit(input.directory, [
|
|
42717
|
-
"diff",
|
|
42718
|
-
"--no-ext-diff",
|
|
42719
|
-
"-z",
|
|
42720
|
-
"--name-only",
|
|
42721
|
-
"--diff-filter=U",
|
|
42722
|
-
"--relative"
|
|
42723
|
-
]);
|
|
42724
|
-
if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
|
|
42725
|
-
const result = yield* runGit(input.directory, [
|
|
42726
|
-
"diff",
|
|
42727
|
-
"--no-ext-diff",
|
|
42728
|
-
"--no-textconv",
|
|
42729
|
-
"--no-renames",
|
|
42730
|
-
"-z",
|
|
42731
|
-
"--name-status",
|
|
42732
|
-
"--relative",
|
|
42733
|
-
input.ref
|
|
42734
|
-
]);
|
|
42735
|
-
if (result.status !== 0) return null;
|
|
42736
|
-
const plan = parseBaselineDiffPlan(result.stdout);
|
|
42737
|
-
if (plan === null) return null;
|
|
42738
|
-
const untracked = yield* runGit(input.directory, [
|
|
42739
|
-
"ls-files",
|
|
42740
|
-
"--others",
|
|
42741
|
-
"--exclude-standard",
|
|
42742
|
-
"-z"
|
|
42743
|
-
]);
|
|
42744
|
-
if (untracked.status !== 0) return null;
|
|
42745
|
-
return {
|
|
42746
|
-
baseFiles: plan.baseFiles,
|
|
42747
|
-
headFiles: plan.headFiles,
|
|
42748
|
-
untrackedFiles: splitNullSeparated(untracked.stdout)
|
|
42749
|
-
};
|
|
42750
|
-
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
|
|
42751
|
-
},
|
|
42752
42539
|
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42753
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." }) }));
|
|
42754
42541
|
if (explicitBaseBranch !== void 0) {
|
|
@@ -42898,7 +42685,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42898
42685
|
githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
|
|
42899
42686
|
branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
|
|
42900
42687
|
mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
|
|
42901
|
-
baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
|
|
42902
42688
|
diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
|
|
42903
42689
|
stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
|
|
42904
42690
|
showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
|
|
@@ -44236,17 +44022,12 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
44236
44022
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
44237
44023
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
44238
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`.";
|
|
44239
|
-
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`.";
|
|
44240
44025
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
44241
44026
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
44242
44027
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44243
44028
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
44244
44029
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
44245
44030
|
};
|
|
44246
|
-
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
44247
|
-
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44248
|
-
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
44249
|
-
};
|
|
44250
44031
|
const PLUGIN_CATEGORY_MAP = {
|
|
44251
44032
|
react: "Bugs",
|
|
44252
44033
|
"react-hooks": "Bugs",
|
|
@@ -44304,10 +44085,6 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
44304
44085
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
44305
44086
|
help: reasonDetail || help
|
|
44306
44087
|
};
|
|
44307
|
-
if (rule === "set-state-in-effect") return {
|
|
44308
|
-
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
44309
|
-
help: reasonDetail || help
|
|
44310
|
-
};
|
|
44311
44088
|
return {
|
|
44312
44089
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
44313
44090
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -46644,19 +46421,12 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
|
46644
46421
|
*/
|
|
46645
46422
|
const materializeSourceTree = (input) => gen(function* () {
|
|
46646
46423
|
const materializedFiles = [];
|
|
46647
|
-
const unmaterializedFiles = [];
|
|
46648
46424
|
const resolvedTempDirectory = path.resolve(input.tempDirectory);
|
|
46649
46425
|
for (const relativePath of input.files) {
|
|
46650
46426
|
const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
|
|
46651
|
-
if (content === null)
|
|
46652
|
-
unmaterializedFiles.push(relativePath);
|
|
46653
|
-
continue;
|
|
46654
|
-
}
|
|
46427
|
+
if (content === null) continue;
|
|
46655
46428
|
const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
|
|
46656
|
-
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory))
|
|
46657
|
-
unmaterializedFiles.push(relativePath);
|
|
46658
|
-
continue;
|
|
46659
|
-
}
|
|
46429
|
+
if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) continue;
|
|
46660
46430
|
yield* sync(() => {
|
|
46661
46431
|
fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
|
|
46662
46432
|
fs.writeFileSync(candidateTargetPath, content);
|
|
@@ -46673,7 +46443,6 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
46673
46443
|
return {
|
|
46674
46444
|
tempDirectory: input.tempDirectory,
|
|
46675
46445
|
materializedFiles,
|
|
46676
|
-
unmaterializedFiles,
|
|
46677
46446
|
cleanup: () => {
|
|
46678
46447
|
try {
|
|
46679
46448
|
fs.rmSync(input.tempDirectory, {
|
|
@@ -46943,80 +46712,54 @@ const buildSkippedChecks = (input) => {
|
|
|
46943
46712
|
skippedCheckReasons
|
|
46944
46713
|
};
|
|
46945
46714
|
};
|
|
46946
|
-
const
|
|
46947
|
-
const normalizeEvidence = (evidence) => evidence.replace(/\s+/g, " ").trim();
|
|
46948
|
-
const getDiagnosticMatchKeys = (diagnostic, evidence) => {
|
|
46715
|
+
const fingerprintDiagnostic = (diagnostic, lineText) => {
|
|
46949
46716
|
const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
46950
|
-
const
|
|
46951
|
-
|
|
46952
|
-
|
|
46953
|
-
|
|
46954
|
-
|
|
46955
|
-
|
|
46956
|
-
|
|
46957
|
-
|
|
46958
|
-
|
|
46959
|
-
|
|
46960
|
-
|
|
46961
|
-
|
|
46962
|
-
|
|
46963
|
-
|
|
46964
|
-
|
|
46965
|
-
|
|
46966
|
-
|
|
46967
|
-
|
|
46968
|
-
|
|
46969
|
-
|
|
46970
|
-
|
|
46971
|
-
|
|
46972
|
-
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
*
|
|
46977
|
-
* evidence. Stable identities combine plugin/rule, the diagnostic message,
|
|
46978
|
-
* and normalized diagnosed source, so unchanged findings can move across
|
|
46979
|
-
* files while changed constructs or messages remain new. Cardinality is
|
|
46980
|
-
* retained for identical findings. Diagnostics explicitly marked
|
|
46981
|
-
* `matchByOccurrence` may fall back to same-file plugin/rule/message matching
|
|
46982
|
-
* after same-file strict evidence matching. Cross-file evidence matching runs
|
|
46983
|
-
* last so a copy cannot consume a reformatted local occurrence. Unreadable
|
|
46984
|
-
* evidence uses the same conservative fallback rather than matching across
|
|
46985
|
-
* 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.
|
|
46986
46744
|
*/
|
|
46987
46745
|
const computeDiagnosticDelta = (input) => {
|
|
46988
|
-
const
|
|
46989
|
-
const
|
|
46990
|
-
|
|
46991
|
-
|
|
46992
|
-
|
|
46993
|
-
|
|
46994
|
-
|
|
46995
|
-
|
|
46996
|
-
|
|
46997
|
-
|
|
46998
|
-
|
|
46999
|
-
|
|
47000
|
-
|
|
47001
|
-
|
|
47002
|
-
if (matchedHeadDiagnosticIndexes.has(candidate.diagnosticIndex)) continue;
|
|
47003
|
-
const baseDiagnosticIndex = takeMatchingDiagnosticIndex(baseBuckets, getKey(candidate), matchedBaseDiagnosticIndexes);
|
|
47004
|
-
if (baseDiagnosticIndex === null) continue;
|
|
47005
|
-
matchedHeadDiagnosticIndexes.add(candidate.diagnosticIndex);
|
|
47006
|
-
matchedBaseDiagnosticIndexes.add(baseDiagnosticIndex);
|
|
47007
|
-
onMatch?.(candidate.diagnosticIndex, baseDiagnosticIndex);
|
|
47008
|
-
}
|
|
47009
|
-
};
|
|
47010
|
-
matchCandidates(baseBySameFileStableEvidence, (candidate) => candidate.sameFileStableEvidenceKey);
|
|
47011
|
-
matchCandidates(baseBySameFileFallback, (candidate) => candidate.sameFileFallbackKey);
|
|
47012
|
-
let crossFileMatchCount = 0;
|
|
47013
|
-
matchCandidates(baseByStableEvidence, (candidate) => candidate.stableEvidenceKey, (head, base) => {
|
|
47014
|
-
if (input.headDiagnostics[head]?.filePath !== input.baseDiagnostics[base]?.filePath) crossFileMatchCount += 1;
|
|
47015
|
-
});
|
|
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;
|
|
47016
46760
|
return {
|
|
47017
|
-
newDiagnostics
|
|
47018
|
-
fixedCount
|
|
47019
|
-
crossFileMatchCount
|
|
46761
|
+
newDiagnostics,
|
|
46762
|
+
fixedCount
|
|
47020
46763
|
};
|
|
47021
46764
|
};
|
|
47022
46765
|
/**
|
|
@@ -47048,12 +46791,6 @@ const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runProm
|
|
|
47048
46791
|
};
|
|
47049
46792
|
}).pipe(provide(Git.layerNode)));
|
|
47050
46793
|
const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
|
|
47051
|
-
const getBaselineDiffPlan = (directory, ref) => runPromise(gen(function* () {
|
|
47052
|
-
return yield* (yield* Git).baselineDiffPlan({
|
|
47053
|
-
directory,
|
|
47054
|
-
ref
|
|
47055
|
-
});
|
|
47056
|
-
}).pipe(provide(Git.layerNode)));
|
|
47057
46794
|
/**
|
|
47058
46795
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
|
47059
46796
|
* `files` with `--unified=0` against `baseRef` (or the index when `cached`),
|
|
@@ -47510,7 +47247,7 @@ const makeNoopConsole = () => ({
|
|
|
47510
47247
|
});
|
|
47511
47248
|
//#endregion
|
|
47512
47249
|
//#region src/cli/utils/version.ts
|
|
47513
|
-
const VERSION = "0.8.1-dev.
|
|
47250
|
+
const VERSION = "0.8.1-dev.7818922";
|
|
47514
47251
|
//#endregion
|
|
47515
47252
|
//#region src/cli/utils/json-mode.ts
|
|
47516
47253
|
let context = null;
|
|
@@ -47862,13 +47599,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47862
47599
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47863
47600
|
* standard `SENTRY_RELEASE` override.
|
|
47864
47601
|
*/
|
|
47865
|
-
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`;
|
|
47866
47603
|
/**
|
|
47867
47604
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47868
47605
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47869
47606
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47870
47607
|
*/
|
|
47871
|
-
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");
|
|
47872
47609
|
/**
|
|
47873
47610
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47874
47611
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -60491,7 +60228,6 @@ const buildOutcomeAttributes = (input) => {
|
|
|
60491
60228
|
new: summary.totalDiagnosticCount,
|
|
60492
60229
|
fixed: result.baselineDelta.fixedCount,
|
|
60493
60230
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
60494
|
-
crossFileMatches: result.baselineDelta.crossFileMatchCount ?? null,
|
|
60495
60231
|
degraded: false
|
|
60496
60232
|
}));
|
|
60497
60233
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -60605,49 +60341,28 @@ const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
|
60605
60341
|
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
60606
60342
|
};
|
|
60607
60343
|
//#endregion
|
|
60608
|
-
//#region src/cli/utils/is-materializable-git-source.ts
|
|
60609
|
-
const GIT_LFS_POINTER_HEADER = "version https://git-lfs.github.com/spec/v1";
|
|
60610
|
-
const isMaterializableGitSource = (content) => !content.includes("\0") && !content.startsWith(GIT_LFS_POINTER_HEADER);
|
|
60611
|
-
//#endregion
|
|
60612
60344
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
60613
60345
|
/**
|
|
60614
|
-
* Materializes the
|
|
60615
|
-
* `
|
|
60616
|
-
*
|
|
60617
|
-
*
|
|
60618
|
-
*
|
|
60619
|
-
*
|
|
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".
|
|
60620
60352
|
*/
|
|
60621
60353
|
const materializeBaselineFiles = (input) => runPromise(gen(function* () {
|
|
60622
60354
|
const git = yield* Git;
|
|
60623
|
-
|
|
60624
|
-
directory: input.directory,
|
|
60625
|
-
ref: input.ref
|
|
60626
|
-
});
|
|
60627
|
-
const baseFiles = input.baseFiles ?? diffPlan?.baseFiles;
|
|
60628
|
-
const headFiles = input.headFiles ?? diffPlan?.headFiles;
|
|
60629
|
-
const untrackedFiles = diffPlan?.untrackedFiles ?? [];
|
|
60630
|
-
if (baseFiles === void 0 || headFiles === void 0) return null;
|
|
60631
|
-
const files = [...new Set([...input.files, ...baseFiles])];
|
|
60632
|
-
const tree = yield* materializeSourceTree({
|
|
60355
|
+
return yield* materializeSourceTree({
|
|
60633
60356
|
directory: input.directory,
|
|
60634
|
-
files,
|
|
60357
|
+
files: input.files,
|
|
60635
60358
|
tempDirectory: input.tempDirectory,
|
|
60636
60359
|
readContent: (relativePath) => git.showRefContent({
|
|
60637
60360
|
directory: input.directory,
|
|
60638
60361
|
ref: input.ref,
|
|
60639
60362
|
relativePath,
|
|
60640
60363
|
options: { maxBufferBytes: GIT_SHOW_MAX_BUFFER_BYTES }
|
|
60641
|
-
})
|
|
60364
|
+
})
|
|
60642
60365
|
});
|
|
60643
|
-
const materializedFiles = new Set(tree.materializedFiles);
|
|
60644
|
-
return {
|
|
60645
|
-
...tree,
|
|
60646
|
-
baseFiles,
|
|
60647
|
-
headFiles,
|
|
60648
|
-
isComplete: baseFiles.every((filePath) => materializedFiles.has(filePath)),
|
|
60649
|
-
untrackedFiles
|
|
60650
|
-
};
|
|
60651
60366
|
}).pipe(provide(Git.layerNode)));
|
|
60652
60367
|
/**
|
|
60653
60368
|
* Resolves the commit a baseline scan should read base content from — the
|
|
@@ -60670,8 +60385,8 @@ const resolveMergeBaseRef = async (directory, ref) => {
|
|
|
60670
60385
|
//#region src/cli/utils/read-source-line.ts
|
|
60671
60386
|
/**
|
|
60672
60387
|
* Builds a cached `(filePath, line) -> source text` reader rooted at
|
|
60673
|
-
* `rootDirectory`, used
|
|
60674
|
-
*
|
|
60388
|
+
* `rootDirectory`, used to fingerprint a diagnostic by the content of its
|
|
60389
|
+
* flagged line (see `computeDiagnosticDelta`). Wraps core's
|
|
60675
60390
|
* `createNodeReadFileLinesSync` with per-file memoization + 1-indexed line
|
|
60676
60391
|
* lookup; unreadable files / out-of-range lines return `null`.
|
|
60677
60392
|
*/
|
|
@@ -60686,338 +60401,6 @@ const createSourceLineReader = (rootDirectory) => {
|
|
|
60686
60401
|
};
|
|
60687
60402
|
};
|
|
60688
60403
|
//#endregion
|
|
60689
|
-
//#region src/cli/utils/read-diagnostic-evidence.ts
|
|
60690
|
-
const SOURCE_EXTENSIONS = [
|
|
60691
|
-
".ts",
|
|
60692
|
-
".tsx",
|
|
60693
|
-
".js",
|
|
60694
|
-
".jsx",
|
|
60695
|
-
".mts",
|
|
60696
|
-
".cts",
|
|
60697
|
-
".mjs",
|
|
60698
|
-
".cjs"
|
|
60699
|
-
];
|
|
60700
|
-
const COMPONENT_WRAPPER_NAMES = new Set(["memo", "forwardRef"]);
|
|
60701
|
-
const getScriptKind = (filePath) => {
|
|
60702
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
60703
|
-
if (extension === ".tsx") return ts.ScriptKind.TSX;
|
|
60704
|
-
if (extension === ".jsx") return ts.ScriptKind.JSX;
|
|
60705
|
-
if (extension === ".js" || extension === ".mjs" || extension === ".cjs") return ts.ScriptKind.JS;
|
|
60706
|
-
return ts.ScriptKind.TS;
|
|
60707
|
-
};
|
|
60708
|
-
const resolveSafeSourcePath = (rootDirectory, filePath) => {
|
|
60709
|
-
try {
|
|
60710
|
-
const resolvedRootDirectory = fs.realpathSync(rootDirectory);
|
|
60711
|
-
const absolutePath = path.resolve(resolvedRootDirectory, filePath);
|
|
60712
|
-
const relativePath = path.relative(resolvedRootDirectory, absolutePath);
|
|
60713
|
-
if (relativePath.length === 0 || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) return null;
|
|
60714
|
-
const stats = fs.lstatSync(absolutePath);
|
|
60715
|
-
if (!stats.isFile() || stats.isSymbolicLink()) return null;
|
|
60716
|
-
const realPath = fs.realpathSync(absolutePath);
|
|
60717
|
-
const realRelativePath = path.relative(resolvedRootDirectory, realPath);
|
|
60718
|
-
return realRelativePath !== ".." && !realRelativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(realRelativePath) ? realPath : null;
|
|
60719
|
-
} catch {
|
|
60720
|
-
return null;
|
|
60721
|
-
}
|
|
60722
|
-
};
|
|
60723
|
-
const readSourceRecord = (rootDirectory, filePath, state) => {
|
|
60724
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
60725
|
-
if (state.recordByFilePath.has(normalizedFilePath)) return state.recordByFilePath.get(normalizedFilePath) ?? null;
|
|
60726
|
-
const absolutePath = resolveSafeSourcePath(rootDirectory, normalizedFilePath);
|
|
60727
|
-
if (absolutePath === null) {
|
|
60728
|
-
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60729
|
-
return null;
|
|
60730
|
-
}
|
|
60731
|
-
try {
|
|
60732
|
-
const sourceText = fs.readFileSync(absolutePath, "utf-8");
|
|
60733
|
-
const record = {
|
|
60734
|
-
absolutePath,
|
|
60735
|
-
filePath: normalizedFilePath,
|
|
60736
|
-
sourceFile: ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
60737
|
-
sourceText
|
|
60738
|
-
};
|
|
60739
|
-
state.recordByFilePath.set(normalizedFilePath, record);
|
|
60740
|
-
return record;
|
|
60741
|
-
} catch {
|
|
60742
|
-
state.recordByFilePath.set(normalizedFilePath, null);
|
|
60743
|
-
return null;
|
|
60744
|
-
}
|
|
60745
|
-
};
|
|
60746
|
-
const listSourceRecords = (rootDirectory, state) => {
|
|
60747
|
-
if (state.sourceRecords !== null) return state.sourceRecords;
|
|
60748
|
-
state.sourceRecords = listSourceFiles(rootDirectory).flatMap((filePath) => {
|
|
60749
|
-
const record = readSourceRecord(rootDirectory, filePath, state);
|
|
60750
|
-
return record === null ? [] : [record];
|
|
60751
|
-
});
|
|
60752
|
-
return state.sourceRecords;
|
|
60753
|
-
};
|
|
60754
|
-
const unwrapExpression = (expression) => {
|
|
60755
|
-
let currentExpression = expression;
|
|
60756
|
-
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
60757
|
-
return currentExpression;
|
|
60758
|
-
};
|
|
60759
|
-
const getCalleeName = (expression) => {
|
|
60760
|
-
const unwrappedExpression = unwrapExpression(expression);
|
|
60761
|
-
if (ts.isIdentifier(unwrappedExpression)) return unwrappedExpression.text;
|
|
60762
|
-
if (ts.isPropertyAccessExpression(unwrappedExpression)) return unwrappedExpression.name.text;
|
|
60763
|
-
return null;
|
|
60764
|
-
};
|
|
60765
|
-
const isComponentWrapperCall = (node) => {
|
|
60766
|
-
const wrapperName = getCalleeName(node.expression);
|
|
60767
|
-
return wrapperName !== null && COMPONENT_WRAPPER_NAMES.has(wrapperName);
|
|
60768
|
-
};
|
|
60769
|
-
const getFunctionName = (node) => {
|
|
60770
|
-
if (ts.isFunctionDeclaration(node) && node.name !== void 0) return node.name.text;
|
|
60771
|
-
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
60772
|
-
let componentExpression = node;
|
|
60773
|
-
while (true) {
|
|
60774
|
-
const parent = componentExpression.parent;
|
|
60775
|
-
if (ts.isParenthesizedExpression(parent) || ts.isAsExpression(parent) || ts.isTypeAssertionExpression(parent) || ts.isNonNullExpression(parent) || ts.isSatisfiesExpression(parent)) {
|
|
60776
|
-
componentExpression = parent;
|
|
60777
|
-
continue;
|
|
60778
|
-
}
|
|
60779
|
-
if (ts.isCallExpression(parent) && parent.arguments[0] === componentExpression && isComponentWrapperCall(parent)) {
|
|
60780
|
-
componentExpression = parent;
|
|
60781
|
-
continue;
|
|
60782
|
-
}
|
|
60783
|
-
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) return parent.name.text;
|
|
60784
|
-
return node.name?.text ?? null;
|
|
60785
|
-
}
|
|
60786
|
-
}
|
|
60787
|
-
return null;
|
|
60788
|
-
};
|
|
60789
|
-
const isComponentFunction = (node) => ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node);
|
|
60790
|
-
const isDefaultExportedComponent = (record, node, componentName) => {
|
|
60791
|
-
if (ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)) return true;
|
|
60792
|
-
return record.sourceFile.statements.some((statement) => ts.isExportAssignment(statement) && ts.isIdentifier(statement.expression) && statement.expression.text === componentName);
|
|
60793
|
-
};
|
|
60794
|
-
const findEnclosingComponent = (record, diagnostic) => {
|
|
60795
|
-
if (!Number.isInteger(diagnostic.line) || diagnostic.line < 1) return null;
|
|
60796
|
-
const lineIndex = diagnostic.line - 1;
|
|
60797
|
-
const lineStarts = record.sourceFile.getLineStarts();
|
|
60798
|
-
const lineStart = lineStarts[lineIndex];
|
|
60799
|
-
if (lineStart === void 0) return null;
|
|
60800
|
-
const nextLineStart = lineStarts[lineIndex + 1] ?? record.sourceFile.end;
|
|
60801
|
-
const lineEnd = Math.max(lineStart, nextLineStart - 1);
|
|
60802
|
-
const columnIndex = Number.isInteger(diagnostic.column) ? Math.max(0, diagnostic.column - 1) : 0;
|
|
60803
|
-
const diagnosticPosition = Math.min(lineStart + columnIndex, lineEnd);
|
|
60804
|
-
let component = null;
|
|
60805
|
-
let componentWidth = Number.POSITIVE_INFINITY;
|
|
60806
|
-
const visit = (node) => {
|
|
60807
|
-
if (diagnosticPosition < node.getStart(record.sourceFile) || diagnosticPosition > node.end) return;
|
|
60808
|
-
if (isComponentFunction(node)) {
|
|
60809
|
-
const name = getFunctionName(node);
|
|
60810
|
-
const width = node.end - node.getStart(record.sourceFile);
|
|
60811
|
-
if (name !== null && /^[A-Z]/.test(name) && width < componentWidth) {
|
|
60812
|
-
component = {
|
|
60813
|
-
isDefaultExport: isDefaultExportedComponent(record, node, name),
|
|
60814
|
-
name,
|
|
60815
|
-
node,
|
|
60816
|
-
record
|
|
60817
|
-
};
|
|
60818
|
-
componentWidth = width;
|
|
60819
|
-
}
|
|
60820
|
-
}
|
|
60821
|
-
ts.forEachChild(node, visit);
|
|
60822
|
-
};
|
|
60823
|
-
visit(record.sourceFile);
|
|
60824
|
-
return component;
|
|
60825
|
-
};
|
|
60826
|
-
const moduleSpecifierTargetsComponent = (callerRecord, moduleSpecifier, componentRecord) => {
|
|
60827
|
-
if (!moduleSpecifier.startsWith(".")) return false;
|
|
60828
|
-
const unresolvedPath = path.resolve(path.dirname(callerRecord.absolutePath), moduleSpecifier);
|
|
60829
|
-
const specifiedExtension = path.extname(unresolvedPath).toLowerCase();
|
|
60830
|
-
const sourcePathWithoutExtension = SOURCE_EXTENSIONS.includes(specifiedExtension) ? unresolvedPath.slice(0, -specifiedExtension.length) : unresolvedPath;
|
|
60831
|
-
return [
|
|
60832
|
-
unresolvedPath,
|
|
60833
|
-
...SOURCE_EXTENSIONS.map((extension) => `${sourcePathWithoutExtension}${extension}`),
|
|
60834
|
-
...sourcePathWithoutExtension === unresolvedPath ? SOURCE_EXTENSIONS.map((extension) => path.join(unresolvedPath, `index${extension}`)) : []
|
|
60835
|
-
].includes(componentRecord.absolutePath);
|
|
60836
|
-
};
|
|
60837
|
-
const getComponentTagNames = (record, component) => {
|
|
60838
|
-
if (record.absolutePath === component.record.absolutePath) return new Set([component.name]);
|
|
60839
|
-
const tagNames = /* @__PURE__ */ new Set();
|
|
60840
|
-
for (const statement of record.sourceFile.statements) {
|
|
60841
|
-
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || !moduleSpecifierTargetsComponent(record, statement.moduleSpecifier.text, component.record)) continue;
|
|
60842
|
-
const importClause = statement.importClause;
|
|
60843
|
-
if (component.isDefaultExport && importClause?.name !== void 0) tagNames.add(importClause.name.text);
|
|
60844
|
-
const namedBindings = importClause?.namedBindings;
|
|
60845
|
-
if (namedBindings !== void 0 && ts.isNamedImports(namedBindings)) {
|
|
60846
|
-
for (const element of namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === component.name) tagNames.add(element.name.text);
|
|
60847
|
-
}
|
|
60848
|
-
}
|
|
60849
|
-
return tagNames;
|
|
60850
|
-
};
|
|
60851
|
-
const getTransparentCall = (functionNode) => {
|
|
60852
|
-
if (functionNode.body === void 0) return null;
|
|
60853
|
-
if (!ts.isBlock(functionNode.body)) {
|
|
60854
|
-
const expression = unwrapExpression(functionNode.body);
|
|
60855
|
-
return ts.isCallExpression(expression) ? expression : null;
|
|
60856
|
-
}
|
|
60857
|
-
if (functionNode.body.statements.length !== 1) return null;
|
|
60858
|
-
const statement = functionNode.body.statements[0];
|
|
60859
|
-
if (statement === void 0) return null;
|
|
60860
|
-
const expression = ts.isExpressionStatement(statement) || ts.isReturnStatement(statement) ? statement.expression : void 0;
|
|
60861
|
-
if (expression === void 0) return null;
|
|
60862
|
-
const unwrappedExpression = unwrapExpression(expression);
|
|
60863
|
-
return ts.isCallExpression(unwrappedExpression) ? unwrappedExpression : null;
|
|
60864
|
-
};
|
|
60865
|
-
const resolveTransparentTarget = (functionNode) => {
|
|
60866
|
-
const call = getTransparentCall(functionNode);
|
|
60867
|
-
if (call === null) return null;
|
|
60868
|
-
const target = unwrapExpression(call.expression);
|
|
60869
|
-
if (!ts.isIdentifier(target) || call.arguments.length !== functionNode.parameters.length) return null;
|
|
60870
|
-
for (const [parameterIndex, parameter] of functionNode.parameters.entries()) {
|
|
60871
|
-
const argument = call.arguments[parameterIndex];
|
|
60872
|
-
const unwrappedArgument = argument === void 0 ? null : unwrapExpression(argument);
|
|
60873
|
-
if (!ts.isIdentifier(parameter.name) || unwrappedArgument === null || !ts.isIdentifier(unwrappedArgument) || unwrappedArgument.text !== parameter.name.text) return null;
|
|
60874
|
-
}
|
|
60875
|
-
return target.text;
|
|
60876
|
-
};
|
|
60877
|
-
const getForwardingFunction = (declaration) => {
|
|
60878
|
-
if (ts.isFunctionDeclaration(declaration)) return declaration;
|
|
60879
|
-
if (!ts.isVariableDeclaration(declaration) || declaration.initializer === void 0) return null;
|
|
60880
|
-
const initializer = unwrapExpression(declaration.initializer);
|
|
60881
|
-
if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) return initializer;
|
|
60882
|
-
if (ts.isCallExpression(initializer) && getCalleeName(initializer.expression) === "useCallback") {
|
|
60883
|
-
const callback = initializer.arguments[0];
|
|
60884
|
-
if (callback !== void 0 && (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback))) return callback;
|
|
60885
|
-
}
|
|
60886
|
-
return null;
|
|
60887
|
-
};
|
|
60888
|
-
const getDeclaredBindingName = (node) => {
|
|
60889
|
-
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;
|
|
60890
|
-
if (ts.isImportClause(node)) return node.name?.text ?? null;
|
|
60891
|
-
if (ts.isImportSpecifier(node) || ts.isNamespaceImport(node)) return node.name.text;
|
|
60892
|
-
return null;
|
|
60893
|
-
};
|
|
60894
|
-
const findBindingScope = (node) => {
|
|
60895
|
-
let currentNode = node;
|
|
60896
|
-
while (currentNode !== void 0) {
|
|
60897
|
-
if (ts.isParameter(currentNode) && ts.isFunctionLike(currentNode.parent)) return currentNode.parent;
|
|
60898
|
-
if (ts.isBlock(currentNode) || ts.isModuleBlock(currentNode) || ts.isSourceFile(currentNode)) return currentNode;
|
|
60899
|
-
currentNode = currentNode.parent;
|
|
60900
|
-
}
|
|
60901
|
-
return node.getSourceFile();
|
|
60902
|
-
};
|
|
60903
|
-
const findBindingDeclaration = (record, binding) => {
|
|
60904
|
-
const usagePosition = binding.getStart(record.sourceFile);
|
|
60905
|
-
const candidates = [];
|
|
60906
|
-
const visit = (node) => {
|
|
60907
|
-
if (getDeclaredBindingName(node) === binding.text) {
|
|
60908
|
-
const scope = findBindingScope(node);
|
|
60909
|
-
const isVisibleScope = usagePosition >= scope.getStart(record.sourceFile) && usagePosition <= scope.end;
|
|
60910
|
-
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;
|
|
60911
|
-
if (isVisibleScope && isVisibleDeclaration) candidates.push({
|
|
60912
|
-
declaration: node,
|
|
60913
|
-
scope
|
|
60914
|
-
});
|
|
60915
|
-
}
|
|
60916
|
-
ts.forEachChild(node, visit);
|
|
60917
|
-
};
|
|
60918
|
-
visit(record.sourceFile);
|
|
60919
|
-
let nearestCandidate = null;
|
|
60920
|
-
let nearestScopeWidth = Number.POSITIVE_INFINITY;
|
|
60921
|
-
let isNearestScopeAmbiguous = false;
|
|
60922
|
-
for (const candidate of candidates) {
|
|
60923
|
-
const scopeWidth = candidate.scope.end - candidate.scope.getStart(record.sourceFile);
|
|
60924
|
-
if (scopeWidth < nearestScopeWidth) {
|
|
60925
|
-
nearestCandidate = candidate;
|
|
60926
|
-
nearestScopeWidth = scopeWidth;
|
|
60927
|
-
isNearestScopeAmbiguous = false;
|
|
60928
|
-
} else if (scopeWidth === nearestScopeWidth) isNearestScopeAmbiguous = true;
|
|
60929
|
-
}
|
|
60930
|
-
if (isNearestScopeAmbiguous) return null;
|
|
60931
|
-
const declaration = nearestCandidate?.declaration;
|
|
60932
|
-
return declaration !== void 0 && (ts.isVariableDeclaration(declaration) || ts.isFunctionDeclaration(declaration)) ? declaration : null;
|
|
60933
|
-
};
|
|
60934
|
-
const resolveHandlerBinding = (record, binding) => {
|
|
60935
|
-
const declaration = findBindingDeclaration(record, binding);
|
|
60936
|
-
if (declaration === null) return binding.text;
|
|
60937
|
-
const forwardingFunction = getForwardingFunction(declaration);
|
|
60938
|
-
const target = forwardingFunction === null ? null : resolveTransparentTarget(forwardingFunction);
|
|
60939
|
-
return target === null || target === binding.text ? binding.text : target;
|
|
60940
|
-
};
|
|
60941
|
-
const getJsxTagName = (node) => ts.isIdentifier(node.tagName) ? node.tagName.text : null;
|
|
60942
|
-
const getHandlerBinding = (node, propName) => {
|
|
60943
|
-
for (const property of node.attributes.properties) {
|
|
60944
|
-
if (!ts.isJsxAttribute(property) || property.name.getText() !== propName) continue;
|
|
60945
|
-
const initializer = property.initializer;
|
|
60946
|
-
if (initializer === void 0 || !ts.isJsxExpression(initializer)) return null;
|
|
60947
|
-
if (initializer.expression === void 0) return null;
|
|
60948
|
-
const expression = unwrapExpression(initializer.expression);
|
|
60949
|
-
return ts.isIdentifier(expression) ? expression : null;
|
|
60950
|
-
}
|
|
60951
|
-
return null;
|
|
60952
|
-
};
|
|
60953
|
-
const getForwardedHandlerAliases = (rootDirectory, component, propNames, state) => {
|
|
60954
|
-
const cacheKey = `${component.record.filePath}\0${component.name}\0${[...propNames].sort().join("\0")}`;
|
|
60955
|
-
const cachedAliases = state.aliasesByComponent.get(cacheKey);
|
|
60956
|
-
if (cachedAliases !== void 0) return cachedAliases;
|
|
60957
|
-
const bindingsByPropName = /* @__PURE__ */ new Map();
|
|
60958
|
-
const unresolvedPropNames = /* @__PURE__ */ new Set();
|
|
60959
|
-
let callsiteCount = 0;
|
|
60960
|
-
for (const record of listSourceRecords(rootDirectory, state)) {
|
|
60961
|
-
const tagNames = getComponentTagNames(record, component);
|
|
60962
|
-
if (tagNames.size === 0) continue;
|
|
60963
|
-
const visit = (node) => {
|
|
60964
|
-
if (record === component.record && node === component.node) return;
|
|
60965
|
-
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
60966
|
-
const tagName = getJsxTagName(node);
|
|
60967
|
-
if (tagName !== null && tagNames.has(tagName)) {
|
|
60968
|
-
callsiteCount += 1;
|
|
60969
|
-
for (const propName of propNames) {
|
|
60970
|
-
const binding = getHandlerBinding(node, propName);
|
|
60971
|
-
if (binding === null) {
|
|
60972
|
-
unresolvedPropNames.add(propName);
|
|
60973
|
-
continue;
|
|
60974
|
-
}
|
|
60975
|
-
const bindings = bindingsByPropName.get(propName) ?? /* @__PURE__ */ new Set();
|
|
60976
|
-
bindings.add(resolveHandlerBinding(record, binding));
|
|
60977
|
-
bindingsByPropName.set(propName, bindings);
|
|
60978
|
-
}
|
|
60979
|
-
}
|
|
60980
|
-
}
|
|
60981
|
-
ts.forEachChild(node, visit);
|
|
60982
|
-
};
|
|
60983
|
-
visit(record.sourceFile);
|
|
60984
|
-
}
|
|
60985
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
60986
|
-
if (callsiteCount > 0) for (const [propName, bindings] of bindingsByPropName) {
|
|
60987
|
-
if (bindings.size !== 1 || unresolvedPropNames.has(propName)) continue;
|
|
60988
|
-
const [bindingName] = bindings;
|
|
60989
|
-
if (bindingName !== void 0) aliases.set(propName, bindingName);
|
|
60990
|
-
}
|
|
60991
|
-
state.aliasesByComponent.set(cacheKey, aliases);
|
|
60992
|
-
return aliases;
|
|
60993
|
-
};
|
|
60994
|
-
const normalizeForwardedHandlers = (evidence, aliases) => {
|
|
60995
|
-
let normalizedEvidence = evidence;
|
|
60996
|
-
for (const [propName, bindingName] of aliases) normalizedEvidence = normalizedEvidence.replace(new RegExp(`\\{\\s*${propName}\\s*\\}`, "g"), `{${bindingName}}`).replace(new RegExp(`=>\\s*${propName}\\s*\\(`, "g"), `=> ${bindingName}(`);
|
|
60997
|
-
return normalizedEvidence;
|
|
60998
|
-
};
|
|
60999
|
-
const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
61000
|
-
const state = {
|
|
61001
|
-
aliasesByComponent: /* @__PURE__ */ new Map(),
|
|
61002
|
-
recordByFilePath: /* @__PURE__ */ new Map(),
|
|
61003
|
-
sourceRecords: null
|
|
61004
|
-
};
|
|
61005
|
-
return (diagnostic) => {
|
|
61006
|
-
const record = readSourceRecord(rootDirectory, diagnostic.filePath, state);
|
|
61007
|
-
if (record === null || !Number.isInteger(diagnostic.line)) return null;
|
|
61008
|
-
const sourceLines = record.sourceText.split(/\r?\n/);
|
|
61009
|
-
const startLineIndex = Math.max(0, diagnostic.line - 1);
|
|
61010
|
-
const endLineIndex = Math.max(startLineIndex, (diagnostic.endLine ?? diagnostic.line) - 1);
|
|
61011
|
-
const evidence = sourceLines.slice(startLineIndex, endLineIndex + 1).join("\n");
|
|
61012
|
-
if (!options.resolveForwardedHandlers) return evidence;
|
|
61013
|
-
const propNames = new Set(evidence.match(/\bon[A-Z]\w*\b/g) ?? []);
|
|
61014
|
-
if (propNames.size === 0) return evidence;
|
|
61015
|
-
const component = findEnclosingComponent(record, diagnostic);
|
|
61016
|
-
if (component === null) return evidence;
|
|
61017
|
-
return normalizeForwardedHandlers(evidence, getForwardedHandlerAliases(rootDirectory, component, propNames, state));
|
|
61018
|
-
};
|
|
61019
|
-
};
|
|
61020
|
-
//#endregion
|
|
61021
60404
|
//#region src/cli/utils/build-no-score-message.ts
|
|
61022
60405
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
61023
60406
|
const buildNoScoreMessage = (isScoreDisabled) => {
|
|
@@ -62261,8 +61644,6 @@ const runBaselineComparison = async (params) => {
|
|
|
62261
61644
|
directory: params.directory,
|
|
62262
61645
|
ref: params.baselineRef,
|
|
62263
61646
|
files: params.options.includePaths,
|
|
62264
|
-
baseFiles: params.baseFiles,
|
|
62265
|
-
headFiles: params.headFiles,
|
|
62266
61647
|
tempDirectory
|
|
62267
61648
|
}).catch((error) => {
|
|
62268
61649
|
rmSync(tempDirectory, {
|
|
@@ -62271,24 +61652,7 @@ const runBaselineComparison = async (params) => {
|
|
|
62271
61652
|
});
|
|
62272
61653
|
throw error;
|
|
62273
61654
|
});
|
|
62274
|
-
if (snapshot === null) {
|
|
62275
|
-
rmSync(tempDirectory, {
|
|
62276
|
-
recursive: true,
|
|
62277
|
-
force: true
|
|
62278
|
-
});
|
|
62279
|
-
return null;
|
|
62280
|
-
}
|
|
62281
61655
|
try {
|
|
62282
|
-
if (!snapshot.isComplete) return null;
|
|
62283
|
-
const analyzedHeadFiles = new Set(params.headAnalyzedFiles.map(toForwardSlashes));
|
|
62284
|
-
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
62285
|
-
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
62286
|
-
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
62287
|
-
for (const filePath of params.options.includePaths) {
|
|
62288
|
-
const normalizedFilePath = toForwardSlashes(filePath);
|
|
62289
|
-
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
62290
|
-
}
|
|
62291
|
-
if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
62292
61656
|
const baseLayers = buildRuntimeLayers({
|
|
62293
61657
|
directory: snapshot.tempDirectory,
|
|
62294
61658
|
hasConfigOverride: true,
|
|
@@ -62304,7 +61668,7 @@ const runBaselineComparison = async (params) => {
|
|
|
62304
61668
|
});
|
|
62305
61669
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
62306
61670
|
directory: snapshot.tempDirectory,
|
|
62307
|
-
includePaths:
|
|
61671
|
+
includePaths: params.options.includePaths,
|
|
62308
61672
|
customRulesOnly: params.options.customRulesOnly,
|
|
62309
61673
|
respectInlineDisables: params.options.respectInlineDisables,
|
|
62310
61674
|
warnings: params.options.warnings,
|
|
@@ -62321,22 +61685,18 @@ const runBaselineComparison = async (params) => {
|
|
|
62321
61685
|
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
62322
61686
|
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))));
|
|
62323
61687
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
62324
|
-
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
62325
61688
|
const delta = computeDiagnosticDelta({
|
|
62326
61689
|
headDiagnostics: params.headDiagnostics,
|
|
62327
61690
|
baseDiagnostics: baseOutput.diagnostics,
|
|
62328
61691
|
readHeadLine: createSourceLineReader(params.directory),
|
|
62329
|
-
readBaseLine: createSourceLineReader(snapshot.tempDirectory)
|
|
62330
|
-
readHeadEvidence: createDiagnosticEvidenceReader(params.directory, { resolveForwardedHandlers: true }),
|
|
62331
|
-
readBaseEvidence: createDiagnosticEvidenceReader(snapshot.tempDirectory)
|
|
61692
|
+
readBaseLine: createSourceLineReader(snapshot.tempDirectory)
|
|
62332
61693
|
});
|
|
62333
61694
|
return {
|
|
62334
61695
|
displayDiagnostics: delta.newDiagnostics,
|
|
62335
61696
|
baselineDelta: {
|
|
62336
61697
|
baseRef: params.baselineRef,
|
|
62337
|
-
fixedCount:
|
|
62338
|
-
baseTotalCount: baseOutput.diagnostics.length
|
|
62339
|
-
crossFileMatchCount: delta.crossFileMatchCount
|
|
61698
|
+
fixedCount: delta.fixedCount,
|
|
61699
|
+
baseTotalCount: baseOutput.diagnostics.length
|
|
62340
61700
|
}
|
|
62341
61701
|
};
|
|
62342
61702
|
} finally {
|
|
@@ -62443,9 +61803,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
62443
61803
|
headDiagnostics: output.diagnostics,
|
|
62444
61804
|
resolvedNodeBinaryPath,
|
|
62445
61805
|
baselineRef: options.baseline.ref,
|
|
62446
|
-
baseFiles: options.baseline.baseFiles,
|
|
62447
|
-
headFiles: options.baseline.headFiles,
|
|
62448
|
-
headAnalyzedFiles: output.analyzedFiles,
|
|
62449
61806
|
deadlineEpochMs
|
|
62450
61807
|
});
|
|
62451
61808
|
if (comparison) {
|
|
@@ -64730,25 +64087,20 @@ const resolveProjectRelativeDirectory = (rootDirectory, projectDirectory) => {
|
|
|
64730
64087
|
return relativeProjectDirectory;
|
|
64731
64088
|
};
|
|
64732
64089
|
//#endregion
|
|
64733
|
-
//#region src/cli/utils/resolve-project-
|
|
64734
|
-
const
|
|
64090
|
+
//#region src/cli/utils/resolve-project-diff-include-paths.ts
|
|
64091
|
+
const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
|
|
64735
64092
|
const relativeProjectDirectory = resolveProjectRelativeDirectory(rootDirectory, projectDirectory);
|
|
64736
64093
|
if (relativeProjectDirectory === null) return [];
|
|
64737
|
-
const
|
|
64738
|
-
if (relativeProjectDirectory.length === 0) return
|
|
64094
|
+
const changedSourceFiles = filterSourceFiles(diffInfo.changedFiles);
|
|
64095
|
+
if (relativeProjectDirectory.length === 0) return changedSourceFiles;
|
|
64739
64096
|
const projectPrefix = `${relativeProjectDirectory}/`;
|
|
64740
|
-
return
|
|
64097
|
+
return changedSourceFiles.flatMap((filePath) => {
|
|
64741
64098
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
64742
64099
|
if (!normalizedFilePath.startsWith(projectPrefix)) return [];
|
|
64743
64100
|
const projectRelativePath = normalizedFilePath.slice(projectPrefix.length);
|
|
64744
64101
|
return projectRelativePath.length > 0 ? [projectRelativePath] : [];
|
|
64745
64102
|
});
|
|
64746
64103
|
};
|
|
64747
|
-
//#endregion
|
|
64748
|
-
//#region src/cli/utils/resolve-project-diff-include-paths.ts
|
|
64749
|
-
const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
|
|
64750
|
-
return resolveProjectSourceFilePaths(rootDirectory, projectDirectory, diffInfo.changedFiles);
|
|
64751
|
-
};
|
|
64752
64104
|
/**
|
|
64753
64105
|
* Re-keys repo-relative changed line ranges (from `getChangedLineRanges`) to
|
|
64754
64106
|
* the scanned project, mirroring `resolveProjectDiffIncludePaths`'s boundary
|
|
@@ -65287,7 +64639,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
65287
64639
|
const isDiffMode = scope !== "full";
|
|
65288
64640
|
const comparisonBaseRef = isDiffMode && diffInfo && !diffInfo.isCurrentChanges ? diffInfo.baseSha ? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseSha) : diffInfo.diffBaseRef ?? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseBranch) : null;
|
|
65289
64641
|
const baselineRef = scope === "changed" ? comparisonBaseRef : null;
|
|
65290
|
-
const baselineDiffPlan = baselineRef === null ? null : await getBaselineDiffPlan(resolvedDirectory, baselineRef);
|
|
65291
64642
|
const linesBaseRef = diffInfo?.isCurrentChanges ? "HEAD" : comparisonBaseRef;
|
|
65292
64643
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
65293
64644
|
directory: resolvedDirectory,
|
|
@@ -65318,13 +64669,10 @@ const inspectAction = async (directory, flags) => {
|
|
|
65318
64669
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
65319
64670
|
let includePaths;
|
|
65320
64671
|
let supplyChainManifestChanged = false;
|
|
65321
|
-
const projectBaselineBaseFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.baseFiles);
|
|
65322
|
-
const projectBaselineHeadFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.headFiles);
|
|
65323
64672
|
if (isDiffMode) {
|
|
65324
64673
|
const changedSourceFiles = diffInfo === null ? [] : resolveProjectDiffIncludePaths(resolvedDirectory, scanDirectory, diffInfo);
|
|
65325
64674
|
supplyChainManifestChanged = supplyChainEnabled && diffInfo !== null && projectManifestChanged(resolvedDirectory, scanDirectory, diffInfo);
|
|
65326
|
-
|
|
65327
|
-
if (changedSourceFiles.length === 0 && !supplyChainManifestChanged && !hasBaselineOnlyFiles) {
|
|
64675
|
+
if (changedSourceFiles.length === 0 && !supplyChainManifestChanged) {
|
|
65328
64676
|
if (!isQuiet) {
|
|
65329
64677
|
cliLogger.dim(`No changed source files in ${scanDirectory}, skipping.`);
|
|
65330
64678
|
cliLogger.break();
|
|
@@ -65332,7 +64680,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
65332
64680
|
return null;
|
|
65333
64681
|
}
|
|
65334
64682
|
includePaths = [...changedSourceFiles];
|
|
65335
|
-
if (includePaths.length === 0 && hasBaselineOnlyFiles) includePaths.push(...projectBaselineBaseFiles ?? []);
|
|
65336
64683
|
if (supplyChainManifestChanged) includePaths.push("package.json");
|
|
65337
64684
|
}
|
|
65338
64685
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
@@ -65344,11 +64691,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
65344
64691
|
configSourceDirectory: projectConfigSourceDirectory ?? void 0,
|
|
65345
64692
|
suppressRendering: isMultiProject,
|
|
65346
64693
|
concurrentScan: isMultiProject,
|
|
65347
|
-
baseline: baselineRef
|
|
65348
|
-
ref: baselineRef,
|
|
65349
|
-
baseFiles: projectBaselineBaseFiles,
|
|
65350
|
-
headFiles: projectBaselineHeadFiles
|
|
65351
|
-
} : void 0,
|
|
64694
|
+
baseline: baselineRef ? { ref: baselineRef } : void 0,
|
|
65352
64695
|
changedLineRanges: scope === "lines" && changedLineRanges !== null ? resolveProjectChangedLineRanges(resolvedDirectory, scanDirectory, changedLineRanges) : void 0,
|
|
65353
64696
|
supplyChainManifestChanged
|
|
65354
64697
|
});
|
|
@@ -66548,4 +65891,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
66548
65891
|
export {};
|
|
66549
65892
|
|
|
66550
65893
|
//# sourceMappingURL=cli.js.map
|
|
66551
|
-
//# debugId=
|
|
65894
|
+
//# debugId=9e32582f-b296-5216-a028-7cc19ed83931
|