react-doctor 0.7.1 → 0.7.2-dev.aa519e5
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 +543 -69
- package/dist/index.d.ts +7 -0
- package/dist/index.js +500 -46
- package/dist/lsp.js +501 -47
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="10e3e1b0-f291-5306-b5fd-79b158b00159")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as NFS from "node:fs";
|
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import * as NodeChildProcess from "node:child_process";
|
|
10
10
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
|
-
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
12
|
+
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
14
14
|
import os from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
@@ -32800,8 +32800,10 @@ const isFile = (filePath) => {
|
|
|
32800
32800
|
};
|
|
32801
32801
|
const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
|
|
32802
32802
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
32803
|
+
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
32803
32804
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
32804
32805
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
32806
|
+
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
32805
32807
|
const IGNORED_DIRECTORIES = new Set([
|
|
32806
32808
|
".angular",
|
|
32807
32809
|
".astro",
|
|
@@ -32943,7 +32945,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
32943
32945
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32944
32946
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32945
32947
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
32946
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
32948
|
+
const CORE_PACKAGE_VERSION = "0.7.2";
|
|
32947
32949
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32948
32950
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
32949
32951
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -32953,8 +32955,9 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
32953
32955
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
32954
32956
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
32955
32957
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
32956
|
-
const
|
|
32957
|
-
const
|
|
32958
|
+
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
32959
|
+
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
32960
|
+
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
32958
32961
|
const isMinifiedSource = (absolutePath) => {
|
|
32959
32962
|
let fileDescriptor;
|
|
32960
32963
|
try {
|
|
@@ -33018,7 +33021,7 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
33018
33021
|
for (const entry of readDirectoryEntries(currentDirectory)) {
|
|
33019
33022
|
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
33020
33023
|
if (entry.isDirectory()) {
|
|
33021
|
-
if (!
|
|
33024
|
+
if (!isIgnoredDirectoryName(entry.name)) stack.push(absolutePath);
|
|
33022
33025
|
continue;
|
|
33023
33026
|
}
|
|
33024
33027
|
if (entry.isFile()) yield {
|
|
@@ -33064,6 +33067,23 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33064
33067
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33065
33068
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33066
33069
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33070
|
+
/**
|
|
33071
|
+
* The chain of ancestor directories of `startDirectory`, nearest first, up to
|
|
33072
|
+
* and including the filesystem root. With `includeStart`, the chain begins at
|
|
33073
|
+
* `startDirectory` itself; otherwise at its parent. The one place the "walk up
|
|
33074
|
+
* the tree" traversal is expressed, so each caller adds only its own match /
|
|
33075
|
+
* stop policy instead of re-deriving the `path.dirname` loop.
|
|
33076
|
+
*/
|
|
33077
|
+
const ancestorDirectories = (startDirectory, { includeStart }) => {
|
|
33078
|
+
const directories = [];
|
|
33079
|
+
let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
|
|
33080
|
+
while (true) {
|
|
33081
|
+
directories.push(currentDirectory);
|
|
33082
|
+
const parentDirectory = Path.dirname(currentDirectory);
|
|
33083
|
+
if (parentDirectory === currentDirectory) return directories;
|
|
33084
|
+
currentDirectory = parentDirectory;
|
|
33085
|
+
}
|
|
33086
|
+
};
|
|
33067
33087
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33068
33088
|
const clearPackageJsonCache = () => {
|
|
33069
33089
|
cachedPackageJsons.clear();
|
|
@@ -33097,11 +33117,7 @@ const isMonorepoRoot = (directory) => {
|
|
|
33097
33117
|
return Array.isArray(packageJson.workspaces) || Boolean(packageJson.workspaces?.packages);
|
|
33098
33118
|
};
|
|
33099
33119
|
const findMonorepoRoot = (startDirectory) => {
|
|
33100
|
-
|
|
33101
|
-
while (currentDirectory !== Path.dirname(currentDirectory)) {
|
|
33102
|
-
if (isMonorepoRoot(currentDirectory)) return currentDirectory;
|
|
33103
|
-
currentDirectory = Path.dirname(currentDirectory);
|
|
33104
|
-
}
|
|
33120
|
+
for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) if (isMonorepoRoot(directory)) return directory;
|
|
33105
33121
|
return null;
|
|
33106
33122
|
};
|
|
33107
33123
|
/**
|
|
@@ -33747,6 +33763,74 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
|
|
|
33747
33763
|
framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
|
|
33748
33764
|
};
|
|
33749
33765
|
};
|
|
33766
|
+
/**
|
|
33767
|
+
* Walk up from `startDirectory` to the nearest ancestor that owns a
|
|
33768
|
+
* `package.json`, stopping at (and including) the enclosing project boundary
|
|
33769
|
+
* — the working tree's git root or a monorepo root. Lets a scan of a package
|
|
33770
|
+
* subfolder that has no `package.json` of its own adopt the nearest enclosing
|
|
33771
|
+
* package as its project root, so it inherits that package's dependency +
|
|
33772
|
+
* framework detection instead of synthesizing an empty (React-blind) project.
|
|
33773
|
+
*
|
|
33774
|
+
* Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
|
|
33775
|
+
* most specific owning package (a leaf workspace, not just the repo root), so
|
|
33776
|
+
* a subfolder of one workspace no longer borrows a sibling's capabilities.
|
|
33777
|
+
*
|
|
33778
|
+
* Returns `null` when no ancestor `package.json` exists inside the project
|
|
33779
|
+
* boundary (a loose tree of files, or a subfolder of a package-less repo).
|
|
33780
|
+
*/
|
|
33781
|
+
const findNearestAncestorPackageJson = (startDirectory) => {
|
|
33782
|
+
if (isProjectBoundary(startDirectory)) return null;
|
|
33783
|
+
for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
|
|
33784
|
+
if (isFile(Path.join(directory, "package.json"))) return directory;
|
|
33785
|
+
if (isProjectBoundary(directory)) return null;
|
|
33786
|
+
}
|
|
33787
|
+
return null;
|
|
33788
|
+
};
|
|
33789
|
+
/**
|
|
33790
|
+
* Last-resort React detection: locate React the way Node's `node_modules`
|
|
33791
|
+
* resolution would from `directory` — check each `node_modules/react` up the
|
|
33792
|
+
* tree — but bounded to the enclosing repo so a globally installed React can't
|
|
33793
|
+
* masquerade as the project's version. Stops at (and including) the nearest
|
|
33794
|
+
* project boundary — the git root or a monorepo root — and returns the first
|
|
33795
|
+
* installed `version`.
|
|
33796
|
+
*
|
|
33797
|
+
* Makes "React is installed and importable" ⇒ "React is detected" an invariant
|
|
33798
|
+
* for packages whose only React declaration is a version-less spec
|
|
33799
|
+
* (`workspace:*`, `*`, a dist-tag) or where React lives solely in a hoisted
|
|
33800
|
+
* `node_modules` the declaration walks never reach — the profile of a component
|
|
33801
|
+
* package inside a monorepo. Matching the `node_modules/react` entry rather than
|
|
33802
|
+
* its realpath means a project whose `node_modules` is symlinked elsewhere (a
|
|
33803
|
+
* Docker volume, a shared store) still counts, while a global install outside
|
|
33804
|
+
* the repo does not.
|
|
33805
|
+
*
|
|
33806
|
+
* Returns `null` when React isn't installed within the repo or its package.json
|
|
33807
|
+
* carries no version string. A tree with no boundary marker at all bounds the
|
|
33808
|
+
* search to `directory`, so a dependency hoisted above the scanned package there
|
|
33809
|
+
* is conservatively not adopted.
|
|
33810
|
+
*/
|
|
33811
|
+
/**
|
|
33812
|
+
* The nearest enclosing project boundary (git root or monorepo root), or
|
|
33813
|
+
* `directory` itself when the scan target sits outside any repo. Bounds the
|
|
33814
|
+
* node_modules walk so a React hoisted anywhere inside the repo counts, but a
|
|
33815
|
+
* React in an ancestor above the repo — a home-directory or global
|
|
33816
|
+
* `node_modules` — can't leak in.
|
|
33817
|
+
*/
|
|
33818
|
+
const findContainmentRoot = (directory) => {
|
|
33819
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) if (isProjectBoundary(ancestorDirectory)) return ancestorDirectory;
|
|
33820
|
+
return directory;
|
|
33821
|
+
};
|
|
33822
|
+
const resolveInstalledReactVersion = (directory) => {
|
|
33823
|
+
const containmentRoot = findContainmentRoot(directory);
|
|
33824
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) {
|
|
33825
|
+
const reactPackageJsonPath = Path.join(ancestorDirectory, "node_modules", "react", "package.json");
|
|
33826
|
+
if (isFile(reactPackageJsonPath)) {
|
|
33827
|
+
const installedVersion = readPackageJson(reactPackageJsonPath).version;
|
|
33828
|
+
return typeof installedVersion === "string" ? installedVersion : null;
|
|
33829
|
+
}
|
|
33830
|
+
if (ancestorDirectory === containmentRoot) return null;
|
|
33831
|
+
}
|
|
33832
|
+
return null;
|
|
33833
|
+
};
|
|
33750
33834
|
const findInWorkspacePackageJsons = (rootDirectory, rootPackageJson, select) => {
|
|
33751
33835
|
const rootValue = select(rootPackageJson);
|
|
33752
33836
|
if (rootValue !== null) return rootValue;
|
|
@@ -33903,6 +33987,10 @@ const normalizeCompilerOptions = (compilerOptions) => {
|
|
|
33903
33987
|
hasExplicitLib
|
|
33904
33988
|
};
|
|
33905
33989
|
};
|
|
33990
|
+
const normalizeReferencePaths = (references) => {
|
|
33991
|
+
if (!Array.isArray(references)) return [];
|
|
33992
|
+
return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
|
|
33993
|
+
};
|
|
33906
33994
|
const readTsConfig = (filePath) => {
|
|
33907
33995
|
let content;
|
|
33908
33996
|
try {
|
|
@@ -33914,6 +34002,7 @@ const readTsConfig = (filePath) => {
|
|
|
33914
34002
|
if (!isPlainObject(parsed.config)) return null;
|
|
33915
34003
|
return {
|
|
33916
34004
|
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
34005
|
+
referencePaths: normalizeReferencePaths(parsed.config.references),
|
|
33917
34006
|
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
33918
34007
|
};
|
|
33919
34008
|
};
|
|
@@ -33954,20 +34043,37 @@ const libEntryIncludesES2023Array = (entry) => {
|
|
|
33954
34043
|
};
|
|
33955
34044
|
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
33956
34045
|
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
34046
|
+
if (compilerOptions.target && targetYearIsPreES2023(compilerOptions.target)) return true;
|
|
33957
34047
|
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
33958
|
-
if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
|
|
33959
34048
|
return false;
|
|
33960
34049
|
};
|
|
33961
34050
|
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
33962
|
-
const detectPreES2023FromConfig = (tsConfigPath) => {
|
|
34051
|
+
const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
|
|
34052
|
+
if (visitedConfigPaths.has(tsConfigPath)) return false;
|
|
33963
34053
|
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
33964
34054
|
if (!compilerOptions) return false;
|
|
33965
|
-
if (!compilerOptionsDeclareTargetOrLib(compilerOptions))
|
|
34055
|
+
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
|
|
34056
|
+
const tsConfig = readTsConfig(tsConfigPath);
|
|
34057
|
+
if (!tsConfig) return false;
|
|
34058
|
+
const nextVisitedConfigPaths = new Set(visitedConfigPaths);
|
|
34059
|
+
nextVisitedConfigPaths.add(tsConfigPath);
|
|
34060
|
+
const configDirectory = Path.dirname(tsConfigPath);
|
|
34061
|
+
return tsConfig.referencePaths.some((referencePath) => {
|
|
34062
|
+
const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
|
|
34063
|
+
const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
|
|
34064
|
+
return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
|
|
34065
|
+
});
|
|
34066
|
+
}
|
|
33966
34067
|
return compilerOptionsArePreES2023(compilerOptions);
|
|
33967
34068
|
};
|
|
34069
|
+
const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
|
|
33968
34070
|
const detectPreES2023Target = (directory) => {
|
|
33969
34071
|
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
33970
34072
|
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
34073
|
+
for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
|
|
34074
|
+
const fallbackPath = Path.join(directory, fallbackFilename);
|
|
34075
|
+
if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
|
|
34076
|
+
}
|
|
33971
34077
|
return false;
|
|
33972
34078
|
};
|
|
33973
34079
|
const REACT_DEPENDENCY_NAMES = new Set([
|
|
@@ -34086,19 +34192,20 @@ const clearProjectCache = () => {
|
|
|
34086
34192
|
};
|
|
34087
34193
|
/**
|
|
34088
34194
|
* Build a `ProjectInfo` for a directory that has no `package.json` of
|
|
34089
|
-
* its own — a
|
|
34090
|
-
* of TypeScript/JavaScript files. Dependency + framework
|
|
34091
|
-
* inherited from the enclosing
|
|
34092
|
-
*
|
|
34093
|
-
*
|
|
34094
|
-
*
|
|
34095
|
-
*
|
|
34195
|
+
* its own — a package subfolder like `repo/packages` or `app/src/features`,
|
|
34196
|
+
* or any loose tree of TypeScript/JavaScript files. Dependency + framework
|
|
34197
|
+
* detection is inherited from the nearest enclosing package (a leaf
|
|
34198
|
+
* workspace, a plain app root, or a monorepo root — whichever is closest,
|
|
34199
|
+
* bounded by the git root), so scanning a subdirectory of a React project
|
|
34200
|
+
* still gets the React capabilities; a standalone non-React directory simply
|
|
34201
|
+
* scans with the framework-agnostic rules. Throws only when the directory has
|
|
34202
|
+
* nothing to scan (no enclosing project and no source files of its own).
|
|
34096
34203
|
*/
|
|
34097
34204
|
const discoverProjectWithoutPackageJson = (directory) => {
|
|
34098
34205
|
const sourceFileCount = countSourceFiles(directory);
|
|
34099
34206
|
const hasOwnTsConfig = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
34100
|
-
const
|
|
34101
|
-
const enclosingProject =
|
|
34207
|
+
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
34208
|
+
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
34102
34209
|
if (enclosingProject !== null) return {
|
|
34103
34210
|
...enclosingProject,
|
|
34104
34211
|
rootDirectory: directory,
|
|
@@ -34128,6 +34235,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34128
34235
|
shopifyFlashListVersion: null,
|
|
34129
34236
|
shopifyFlashListMajorVersion: null,
|
|
34130
34237
|
hasReanimated: false,
|
|
34238
|
+
reanimatedVersion: null,
|
|
34131
34239
|
isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
|
|
34132
34240
|
sourceFileCount
|
|
34133
34241
|
};
|
|
@@ -34202,6 +34310,7 @@ const discoverProject = (directory) => {
|
|
|
34202
34310
|
if (!reactVersion && reactDeclaration.version && !isCatalogReference(reactDeclaration.version)) reactVersion = reactDeclaration.version;
|
|
34203
34311
|
if (!tailwindVersion && tailwindDeclaration.version && !isCatalogReference(tailwindDeclaration.version)) tailwindVersion = tailwindDeclaration.version;
|
|
34204
34312
|
if (!zodVersion && zodDeclaration.version && !isCatalogReference(zodDeclaration.version)) zodVersion = zodDeclaration.version;
|
|
34313
|
+
if (!reactVersion || parseReactMajor(reactVersion) === null) reactVersion = resolveInstalledReactVersion(directory) ?? reactVersion;
|
|
34205
34314
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
34206
34315
|
const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
34207
34316
|
const sourceFileCount = countSourceFiles(directory);
|
|
@@ -34219,6 +34328,7 @@ const discoverProject = (directory) => {
|
|
|
34219
34328
|
version: findShopifyFlashListVersion(directory, packageJson)
|
|
34220
34329
|
}) : null;
|
|
34221
34330
|
const hasReanimated = hasReactNativeWorkspace && someWorkspacePackageJson(directory, packageJson, isPackageJsonReanimatedAware);
|
|
34331
|
+
const reanimatedVersion = hasReanimated ? findInWorkspacePackageJsons(directory, packageJson, (workspacePackageJson) => getDependencySpec(workspacePackageJson, REANIMATED_DEPENDENCY_NAME)) : null;
|
|
34222
34332
|
const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
|
|
34223
34333
|
rootDirectory: directory,
|
|
34224
34334
|
rootPackageJson: packageJson,
|
|
@@ -34248,6 +34358,7 @@ const discoverProject = (directory) => {
|
|
|
34248
34358
|
shopifyFlashListVersion,
|
|
34249
34359
|
shopifyFlashListMajorVersion: shopifyFlashListVersion === null ? null : getLowestDependencyMajor(shopifyFlashListVersion),
|
|
34250
34360
|
hasReanimated,
|
|
34361
|
+
reanimatedVersion,
|
|
34251
34362
|
isPreES2023Target,
|
|
34252
34363
|
sourceFileCount
|
|
34253
34364
|
};
|
|
@@ -34440,6 +34551,9 @@ const buildRuleSeverityControls = (config) => {
|
|
|
34440
34551
|
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
34441
34552
|
};
|
|
34442
34553
|
};
|
|
34554
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34555
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34556
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34443
34557
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
34444
34558
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
34445
34559
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -34517,6 +34631,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
34517
34631
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
34518
34632
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
34519
34633
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
34634
|
+
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
34520
34635
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
34521
34636
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
34522
34637
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -34590,9 +34705,6 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34590
34705
|
if (!ruleSection) return [];
|
|
34591
34706
|
return ruleSection.split(/[,\s]+/).map((token) => token.trim()).filter(Boolean);
|
|
34592
34707
|
};
|
|
34593
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34594
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34595
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34596
34708
|
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` — add \`${ruleId}\` to the comment (e.g. \`${token}, ${ruleId}\`).`;
|
|
34597
34709
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34598
34710
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
@@ -34646,6 +34758,53 @@ const detectForeignDisableNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
34646
34758
|
if (!ruleId.startsWith("react-doctor/")) return null;
|
|
34647
34759
|
return detectInlineNearMiss(lines, diagnosticLineIndex, ruleId) ?? detectBlockNearMiss(lines, diagnosticLineIndex, ruleId);
|
|
34648
34760
|
};
|
|
34761
|
+
const ESLINT_REACT_HOOKS_PLUGIN_PREFIX = "react-hooks/";
|
|
34762
|
+
const buildAcceptedTokens = (ruleId) => {
|
|
34763
|
+
const shortRuleId = ruleId.slice(15);
|
|
34764
|
+
return new Set([ruleId, `${ESLINT_REACT_HOOKS_PLUGIN_PREFIX}${shortRuleId}`]);
|
|
34765
|
+
};
|
|
34766
|
+
const listNamesRule = (ruleList, acceptedTokens) => tokenizeRuleList(ruleList).some((token) => acceptedTokens.has(token));
|
|
34767
|
+
const isInlineDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
34768
|
+
const candidates = [{
|
|
34769
|
+
line: lines[diagnosticLineIndex],
|
|
34770
|
+
requiredScope: "line"
|
|
34771
|
+
}, {
|
|
34772
|
+
line: lines[diagnosticLineIndex - 1],
|
|
34773
|
+
requiredScope: "next-line"
|
|
34774
|
+
}];
|
|
34775
|
+
for (const { line, requiredScope } of candidates) {
|
|
34776
|
+
const match = line?.match(FOREIGN_INLINE_DISABLE_PATTERN);
|
|
34777
|
+
if (!match) continue;
|
|
34778
|
+
const [, , scope, ruleList] = match;
|
|
34779
|
+
if (scope !== requiredScope) continue;
|
|
34780
|
+
if (listNamesRule(ruleList, acceptedTokens)) return true;
|
|
34781
|
+
}
|
|
34782
|
+
return false;
|
|
34783
|
+
};
|
|
34784
|
+
const isBlockDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
34785
|
+
let isRangeOpen = false;
|
|
34786
|
+
const lastLineIndex = Math.min(diagnosticLineIndex, lines.length - 1);
|
|
34787
|
+
for (let lineIndex = 0; lineIndex <= lastLineIndex; lineIndex++) {
|
|
34788
|
+
const line = lines[lineIndex];
|
|
34789
|
+
if (line === void 0 || !line.includes("-disable") && !line.includes("-enable")) continue;
|
|
34790
|
+
const disableMatch = line.match(FOREIGN_BLOCK_DISABLE_PATTERN);
|
|
34791
|
+
if (disableMatch && listNamesRule(disableMatch[2], acceptedTokens)) {
|
|
34792
|
+
isRangeOpen = true;
|
|
34793
|
+
continue;
|
|
34794
|
+
}
|
|
34795
|
+
const enableMatch = line.match(FOREIGN_BLOCK_ENABLE_PATTERN);
|
|
34796
|
+
if (enableMatch) {
|
|
34797
|
+
const enabledRules = tokenizeRuleList(enableMatch[1]);
|
|
34798
|
+
if (enabledRules.length === 0 || enabledRules.some((rule) => acceptedTokens.has(rule))) isRangeOpen = false;
|
|
34799
|
+
}
|
|
34800
|
+
}
|
|
34801
|
+
return isRangeOpen;
|
|
34802
|
+
};
|
|
34803
|
+
const isCompilerRuleForeignDisabled = (lines, diagnosticLineIndex, ruleId) => {
|
|
34804
|
+
if (!ruleId.startsWith("react-hooks-js/")) return false;
|
|
34805
|
+
const acceptedTokens = buildAcceptedTokens(ruleId);
|
|
34806
|
+
return isInlineDisabled(lines, diagnosticLineIndex, acceptedTokens) || isBlockDisabled(lines, diagnosticLineIndex, acceptedTokens);
|
|
34807
|
+
};
|
|
34649
34808
|
const JSX_OPENER_TAG_PATTERN = /<[A-Za-z][\w.]*/g;
|
|
34650
34809
|
const JSX_TAG_NAME_FOLLOW = /[A-Za-z]/;
|
|
34651
34810
|
const isOpenerMatchInsideLineComment = (line, openerCharIndex) => {
|
|
@@ -34789,6 +34948,11 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
34789
34948
|
isSuppressed: true,
|
|
34790
34949
|
nearMissHint: null
|
|
34791
34950
|
};
|
|
34951
|
+
if (isCompilerRuleForeignDisabled(lines, diagnosticLineIndex, ruleId)) return {
|
|
34952
|
+
isSuppressed: true,
|
|
34953
|
+
nearMissHint: null,
|
|
34954
|
+
isForeignDirective: true
|
|
34955
|
+
};
|
|
34792
34956
|
const directComments = findStackedDisableCommentsAbove(lines, diagnosticLineIndex);
|
|
34793
34957
|
if (hasChainSuppressor(directComments, ruleId)) return {
|
|
34794
34958
|
isSuppressed: true,
|
|
@@ -35074,12 +35238,18 @@ const collectStringSet = (values) => {
|
|
|
35074
35238
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
35075
35239
|
};
|
|
35076
35240
|
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
35241
|
+
const NODE_CLI_PATH_SEGMENTS = new Set([
|
|
35242
|
+
"scripts",
|
|
35243
|
+
"bin",
|
|
35244
|
+
"tools"
|
|
35245
|
+
]);
|
|
35246
|
+
const hasNodeCliPathSegment = (filePath) => filePath.split(/[\\/]/).slice(0, -1).some((segment) => NODE_CLI_PATH_SEGMENTS.has(segment));
|
|
35077
35247
|
/**
|
|
35078
35248
|
* Pre-compiles every stateful filter and returns a single
|
|
35079
35249
|
* `apply(diagnostic)` closure that runs (in order):
|
|
35080
35250
|
*
|
|
35081
35251
|
* 1. auto-suppress (test-noise rules in test files; `migration-hint`
|
|
35082
|
-
* wins over `test-noise`)
|
|
35252
|
+
* wins over `test-noise`; `no-console` on Node CLI scripts)
|
|
35083
35253
|
* 2. severity overrides (top-level `rules` / `categories`, with
|
|
35084
35254
|
* `"off"` dropping)
|
|
35085
35255
|
* 3. warning suppression (only when `showWarnings` is false: drops every
|
|
@@ -35177,6 +35347,12 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35177
35347
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35178
35348
|
return false;
|
|
35179
35349
|
};
|
|
35350
|
+
const isNoConsoleOnNodeCliScript = (diagnostic) => {
|
|
35351
|
+
if (diagnostic.rule !== "no-console") return false;
|
|
35352
|
+
if (hasNodeCliPathSegment(diagnostic.filePath)) return true;
|
|
35353
|
+
const lines = getFileLines(diagnostic.filePath);
|
|
35354
|
+
return lines !== null && Boolean(lines[0]?.startsWith("#!"));
|
|
35355
|
+
};
|
|
35180
35356
|
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
35181
35357
|
if (!hasRuntimeGlobals) return false;
|
|
35182
35358
|
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
@@ -35186,6 +35362,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35186
35362
|
return {
|
|
35187
35363
|
apply: (diagnostic) => {
|
|
35188
35364
|
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35365
|
+
if (isNoConsoleOnNodeCliScript(diagnostic)) return null;
|
|
35189
35366
|
let current = diagnostic;
|
|
35190
35367
|
let explicitSeverityOverride;
|
|
35191
35368
|
let explicitRuleOverride;
|
|
@@ -35215,7 +35392,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35215
35392
|
if (lines) {
|
|
35216
35393
|
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35217
35394
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35218
|
-
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35395
|
+
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
35219
35396
|
if (evaluation.nearMissHint) current = {
|
|
35220
35397
|
...current,
|
|
35221
35398
|
suppressionHint: evaluation.nearMissHint
|
|
@@ -38564,7 +38741,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38564
38741
|
})),
|
|
38565
38742
|
showStagedContent: (directory, relativePath, options) => runCommand({
|
|
38566
38743
|
command: "git",
|
|
38567
|
-
args: ["show",
|
|
38744
|
+
args: ["show", `:./${relativePath}`],
|
|
38568
38745
|
directory,
|
|
38569
38746
|
maxStdoutBytes: options?.maxBufferBytes
|
|
38570
38747
|
}).pipe(map$3((result) => result.status === 0 ? result.stdout : null)),
|
|
@@ -39347,9 +39524,17 @@ const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
|
|
|
39347
39524
|
return `Move secrets to server-only code. In ${formatFrameworkName(project.framework)}, only \`${publicEnvPrefix}\` env vars are exposed to the browser, and they must not contain secrets`;
|
|
39348
39525
|
};
|
|
39349
39526
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
39527
|
+
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39528
|
+
const supportsAccessors = (reanimatedVersion) => {
|
|
39529
|
+
if (reanimatedVersion === null) return true;
|
|
39530
|
+
const lowerBound = import_semver.validRange(reanimatedVersion) !== null ? import_semver.minVersion(reanimatedVersion) : import_semver.coerce(reanimatedVersion);
|
|
39531
|
+
if (lowerBound === null) return true;
|
|
39532
|
+
return import_semver.gte(lowerBound, ACCESSORS_MIN_VERSION);
|
|
39533
|
+
};
|
|
39350
39534
|
const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
39351
39535
|
if (rule !== "immutability") return help;
|
|
39352
39536
|
if (!project.hasReanimated) return help;
|
|
39537
|
+
if (!supportsAccessors(project.reanimatedVersion)) return help;
|
|
39353
39538
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
39354
39539
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
39355
39540
|
};
|
|
@@ -39511,13 +39696,13 @@ const REACT_USE_BINDING_RESOLUTION = {
|
|
|
39511
39696
|
isReactUseBinding: true,
|
|
39512
39697
|
isReactNamespaceBinding: false
|
|
39513
39698
|
};
|
|
39514
|
-
const getScriptKind = (filename) => {
|
|
39699
|
+
const getScriptKind$2 = (filename) => {
|
|
39515
39700
|
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
39516
39701
|
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
39517
39702
|
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
39518
39703
|
return ts.ScriptKind.JS;
|
|
39519
39704
|
};
|
|
39520
|
-
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
39705
|
+
const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
39521
39706
|
const unwrapExpression = (expression) => {
|
|
39522
39707
|
let currentExpression = expression;
|
|
39523
39708
|
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
@@ -39766,8 +39951,8 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
|
|
|
39766
39951
|
return matchedIdentifier;
|
|
39767
39952
|
};
|
|
39768
39953
|
const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
|
|
39769
|
-
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filename));
|
|
39770
|
-
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset(sourceText, utf8Offset));
|
|
39954
|
+
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$2(filename));
|
|
39955
|
+
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
|
|
39771
39956
|
if (!useIdentifier) return null;
|
|
39772
39957
|
return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
|
|
39773
39958
|
};
|
|
@@ -39788,6 +39973,201 @@ const shouldSuppressLocalUseHookDiagnostic = (diagnostic, rootDirectory) => {
|
|
|
39788
39973
|
const bindingResolution = resolveUseCallBinding(sourceText, absolutePath, primaryLabel.span.offset);
|
|
39789
39974
|
return bindingResolution !== null && !bindingResolution.isReactUseBinding;
|
|
39790
39975
|
};
|
|
39976
|
+
const SUPPRESSED_COMPILER_CODES = new Set(["react-hooks-js(immutability)", "react-hooks-js(refs)"]);
|
|
39977
|
+
const WORKLET_DIRECTIVE = "worklet";
|
|
39978
|
+
const WORKLET_ACCEPTING_CALLEES = new Set([
|
|
39979
|
+
"useAnimatedStyle",
|
|
39980
|
+
"useAnimatedProps",
|
|
39981
|
+
"useDerivedValue",
|
|
39982
|
+
"useAnimatedReaction",
|
|
39983
|
+
"useAnimatedScrollHandler",
|
|
39984
|
+
"useAnimatedGestureHandler",
|
|
39985
|
+
"useFrameCallback",
|
|
39986
|
+
"runOnUI",
|
|
39987
|
+
"executeOnUIRuntimeSync"
|
|
39988
|
+
]);
|
|
39989
|
+
const GESTURE_ROOT_IDENTIFIER = "Gesture";
|
|
39990
|
+
const getScriptKind$1 = (filename) => {
|
|
39991
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
39992
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
39993
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
39994
|
+
return ts.ScriptKind.JS;
|
|
39995
|
+
};
|
|
39996
|
+
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
39997
|
+
const hasWorkletDirective = (node) => {
|
|
39998
|
+
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
|
|
39999
|
+
const body = node.body;
|
|
40000
|
+
if (!body || !ts.isBlock(body)) return false;
|
|
40001
|
+
for (const statement of body.statements) {
|
|
40002
|
+
if (!ts.isExpressionStatement(statement)) return false;
|
|
40003
|
+
if (!ts.isStringLiteral(statement.expression)) return false;
|
|
40004
|
+
if (statement.expression.text === WORKLET_DIRECTIVE) return true;
|
|
40005
|
+
}
|
|
40006
|
+
return false;
|
|
40007
|
+
};
|
|
40008
|
+
const rootIdentifierOfCallChain = (expression) => {
|
|
40009
|
+
let currentExpression = expression;
|
|
40010
|
+
while (true) {
|
|
40011
|
+
if (ts.isIdentifier(currentExpression)) return currentExpression.text;
|
|
40012
|
+
if (ts.isPropertyAccessExpression(currentExpression)) {
|
|
40013
|
+
currentExpression = currentExpression.expression;
|
|
40014
|
+
continue;
|
|
40015
|
+
}
|
|
40016
|
+
if (ts.isCallExpression(currentExpression)) {
|
|
40017
|
+
currentExpression = currentExpression.expression;
|
|
40018
|
+
continue;
|
|
40019
|
+
}
|
|
40020
|
+
return null;
|
|
40021
|
+
}
|
|
40022
|
+
};
|
|
40023
|
+
const isWorkletAcceptingCall = (callExpression) => {
|
|
40024
|
+
const callee = callExpression.expression;
|
|
40025
|
+
if (ts.isIdentifier(callee)) return WORKLET_ACCEPTING_CALLEES.has(callee.text);
|
|
40026
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
40027
|
+
if (WORKLET_ACCEPTING_CALLEES.has(callee.name.text)) return true;
|
|
40028
|
+
return rootIdentifierOfCallChain(callee) === GESTURE_ROOT_IDENTIFIER;
|
|
40029
|
+
}
|
|
40030
|
+
return false;
|
|
40031
|
+
};
|
|
40032
|
+
const isFunctionWorklet = (functionNode) => {
|
|
40033
|
+
if (hasWorkletDirective(functionNode)) return true;
|
|
40034
|
+
const parent = functionNode.parent;
|
|
40035
|
+
return ts.isCallExpression(parent) && parent.arguments.some((argument) => argument === functionNode) && isWorkletAcceptingCall(parent);
|
|
40036
|
+
};
|
|
40037
|
+
const findNodeAtOffset = (sourceFile, targetOffset) => {
|
|
40038
|
+
let matchedNode = null;
|
|
40039
|
+
const visit = (node) => {
|
|
40040
|
+
if (node.getStart(sourceFile) > targetOffset || node.getEnd() <= targetOffset) return;
|
|
40041
|
+
matchedNode = node;
|
|
40042
|
+
ts.forEachChild(node, visit);
|
|
40043
|
+
};
|
|
40044
|
+
visit(sourceFile);
|
|
40045
|
+
return matchedNode;
|
|
40046
|
+
};
|
|
40047
|
+
const isOffsetInsideWorklet = (sourceFile, targetOffset) => {
|
|
40048
|
+
let currentNode = findNodeAtOffset(sourceFile, targetOffset);
|
|
40049
|
+
while (currentNode) {
|
|
40050
|
+
if (ts.isFunctionLike(currentNode) && isFunctionWorklet(currentNode)) return true;
|
|
40051
|
+
currentNode = currentNode.parent;
|
|
40052
|
+
}
|
|
40053
|
+
return false;
|
|
40054
|
+
};
|
|
40055
|
+
const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirectory) => {
|
|
40056
|
+
if (!SUPPRESSED_COMPILER_CODES.has(diagnostic.code)) return false;
|
|
40057
|
+
if (!project.hasReanimated) return false;
|
|
40058
|
+
const primaryLabel = diagnostic.labels[0];
|
|
40059
|
+
if (!primaryLabel) return false;
|
|
40060
|
+
const absolutePath = Path.isAbsolute(diagnostic.filename) ? diagnostic.filename : Path.join(rootDirectory, diagnostic.filename);
|
|
40061
|
+
let sourceText;
|
|
40062
|
+
try {
|
|
40063
|
+
sourceText = NFS.readFileSync(absolutePath, "utf-8");
|
|
40064
|
+
} catch {
|
|
40065
|
+
return false;
|
|
40066
|
+
}
|
|
40067
|
+
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
|
|
40068
|
+
};
|
|
40069
|
+
const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
|
|
40070
|
+
const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
|
|
40071
|
+
const REACT_COMPILER_PLUGIN = "react-hooks-js";
|
|
40072
|
+
const getScriptKind = (filename) => {
|
|
40073
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
40074
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
40075
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
40076
|
+
return ts.ScriptKind.JS;
|
|
40077
|
+
};
|
|
40078
|
+
const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
|
|
40079
|
+
const nodeLineRange = (sourceFile, node) => ({
|
|
40080
|
+
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
|
|
40081
|
+
endLine: sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1
|
|
40082
|
+
});
|
|
40083
|
+
const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
40084
|
+
let foundRange = null;
|
|
40085
|
+
const visit = (node) => {
|
|
40086
|
+
if (foundRange) return;
|
|
40087
|
+
if (ts.isFunctionLike(node)) {
|
|
40088
|
+
const range = nodeLineRange(sourceFile, node);
|
|
40089
|
+
if (range.startLine <= targetLine && targetLine <= range.endLine) foundRange = range;
|
|
40090
|
+
return;
|
|
40091
|
+
}
|
|
40092
|
+
ts.forEachChild(node, visit);
|
|
40093
|
+
};
|
|
40094
|
+
visit(sourceFile);
|
|
40095
|
+
return foundRange;
|
|
40096
|
+
};
|
|
40097
|
+
const NEWLINE_BYTE = 10;
|
|
40098
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40099
|
+
let lineNumber = 1;
|
|
40100
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40101
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40102
|
+
return lineNumber;
|
|
40103
|
+
};
|
|
40104
|
+
/**
|
|
40105
|
+
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40106
|
+
* whose enclosing function also carries a `react-hooks-js` (React
|
|
40107
|
+
* Compiler) diagnostic. Every react-hooks-js finding marks a function the
|
|
40108
|
+
* compiler could NOT compile (a bail-out / skip / preserve-manual-
|
|
40109
|
+
* memoization failure), so "React Compiler already caches every value
|
|
40110
|
+
* here" is false for that function and the manual memoization is
|
|
40111
|
+
* load-bearing, not dead weight.
|
|
40112
|
+
*
|
|
40113
|
+
* Matching is per enclosing function, not per file: a bailed-out
|
|
40114
|
+
* component must not silence redundant-memo findings in a sibling
|
|
40115
|
+
* component the compiler did optimize.
|
|
40116
|
+
*
|
|
40117
|
+
* Module-scope `memo(() => …)` wrappers have no enclosing function; for
|
|
40118
|
+
* those the memo call's own span is the containment range, so a bail-out
|
|
40119
|
+
* inside the inline-wrapped component still suppresses. Every failure
|
|
40120
|
+
* path (unreadable file, missing span) fails open and keeps the
|
|
40121
|
+
* diagnostic.
|
|
40122
|
+
*/
|
|
40123
|
+
const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) => {
|
|
40124
|
+
const compilerDiagnosticLinesByFile = /* @__PURE__ */ new Map();
|
|
40125
|
+
let hasManualMemoizationDiagnostic = false;
|
|
40126
|
+
for (const diagnostic of diagnostics) {
|
|
40127
|
+
if (isManualMemoizationDiagnostic(diagnostic)) {
|
|
40128
|
+
hasManualMemoizationDiagnostic = true;
|
|
40129
|
+
continue;
|
|
40130
|
+
}
|
|
40131
|
+
if (diagnostic.plugin !== REACT_COMPILER_PLUGIN || diagnostic.line <= 0) continue;
|
|
40132
|
+
const lines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
40133
|
+
if (lines) lines.push(diagnostic.line);
|
|
40134
|
+
else compilerDiagnosticLinesByFile.set(diagnostic.filePath, [diagnostic.line]);
|
|
40135
|
+
}
|
|
40136
|
+
if (!hasManualMemoizationDiagnostic || compilerDiagnosticLinesByFile.size === 0) return diagnostics;
|
|
40137
|
+
const parsedFileCache = /* @__PURE__ */ new Map();
|
|
40138
|
+
const readAndParse = (filePath) => {
|
|
40139
|
+
const cached = parsedFileCache.get(filePath);
|
|
40140
|
+
if (cached !== void 0) return cached;
|
|
40141
|
+
const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory || ".", filePath);
|
|
40142
|
+
let parsed;
|
|
40143
|
+
try {
|
|
40144
|
+
const buffer = NFS.readFileSync(absolutePath);
|
|
40145
|
+
parsed = {
|
|
40146
|
+
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
40147
|
+
buffer
|
|
40148
|
+
};
|
|
40149
|
+
} catch {
|
|
40150
|
+
parsed = null;
|
|
40151
|
+
}
|
|
40152
|
+
parsedFileCache.set(filePath, parsed);
|
|
40153
|
+
return parsed;
|
|
40154
|
+
};
|
|
40155
|
+
const isInsideBailedOutFunction = (diagnostic) => {
|
|
40156
|
+
const compilerLines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
40157
|
+
if (!compilerLines || diagnostic.line <= 0) return false;
|
|
40158
|
+
const parsed = readAndParse(diagnostic.filePath);
|
|
40159
|
+
if (!parsed) return false;
|
|
40160
|
+
let containmentRange = findOutermostEnclosingFunctionRange(parsed.sourceFile, diagnostic.line);
|
|
40161
|
+
if (!containmentRange && diagnostic.offset !== void 0 && diagnostic.length !== void 0) containmentRange = {
|
|
40162
|
+
startLine: diagnostic.line,
|
|
40163
|
+
endLine: lineOfUtf8Offset(parsed.buffer, diagnostic.offset + diagnostic.length)
|
|
40164
|
+
};
|
|
40165
|
+
if (!containmentRange) return false;
|
|
40166
|
+
const { startLine, endLine } = containmentRange;
|
|
40167
|
+
return compilerLines.some((compilerLine) => startLine <= compilerLine && compilerLine <= endLine);
|
|
40168
|
+
};
|
|
40169
|
+
return diagnostics.filter((diagnostic) => !isManualMemoizationDiagnostic(diagnostic) || !isInsideBailedOutFunction(diagnostic));
|
|
40170
|
+
};
|
|
39791
40171
|
const FILEPATH_WITH_LOCATION_PATTERN = /\S+\.\w+:\d+:\d+[\s\S]*$/;
|
|
39792
40172
|
const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
39793
40173
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
@@ -39927,7 +40307,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
39927
40307
|
minifiedFileCache.set(absolutePath, minified);
|
|
39928
40308
|
return minified;
|
|
39929
40309
|
};
|
|
39930
|
-
return parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory)).map((diagnostic) => {
|
|
40310
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
39931
40311
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
39932
40312
|
const primaryLabel = diagnostic.labels[0];
|
|
39933
40313
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -39954,7 +40334,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
39954
40334
|
...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
|
|
39955
40335
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
39956
40336
|
};
|
|
39957
|
-
});
|
|
40337
|
+
}), rootDirectory);
|
|
39958
40338
|
};
|
|
39959
40339
|
const buildOxlintChildEnv = (sourceEnv) => {
|
|
39960
40340
|
const childEnv = {};
|
|
@@ -40465,6 +40845,7 @@ const runOxlint = async (options) => {
|
|
|
40465
40845
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
40466
40846
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
40467
40847
|
validateRuleRegistration();
|
|
40848
|
+
resetManifestCaches();
|
|
40468
40849
|
if (includePaths !== void 0 && includePaths.length === 0) return [];
|
|
40469
40850
|
const pluginPath = resolvePluginPath();
|
|
40470
40851
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
@@ -41127,9 +41508,10 @@ const CONTEXT_AXES = [
|
|
|
41127
41508
|
}
|
|
41128
41509
|
];
|
|
41129
41510
|
const SCORE_AXES = [...GATED_AXES, ...CONTEXT_AXES];
|
|
41130
|
-
const
|
|
41131
|
-
|
|
41132
|
-
|
|
41511
|
+
const gatedAxesForSection = (section) => section === "devDependencies" ? GATED_AXES.filter((axis) => axis.key !== "vulnerability") : GATED_AXES;
|
|
41512
|
+
const worstGatedAxis = (score, gatedAxes) => {
|
|
41513
|
+
let worst = gatedAxes[0];
|
|
41514
|
+
for (const axis of gatedAxes) if (score[axis.key] < score[worst.key]) worst = axis;
|
|
41133
41515
|
return worst;
|
|
41134
41516
|
};
|
|
41135
41517
|
const clampScore = (value) => {
|
|
@@ -41153,6 +41535,69 @@ const resolveConcreteVersion = (spec) => {
|
|
|
41153
41535
|
if (range === null || range === "*") return null;
|
|
41154
41536
|
return import_semver.minVersion(trimmed)?.version ?? null;
|
|
41155
41537
|
};
|
|
41538
|
+
const readTextFileOrNull = (filePath) => {
|
|
41539
|
+
try {
|
|
41540
|
+
return NFS.readFileSync(filePath, "utf-8");
|
|
41541
|
+
} catch {
|
|
41542
|
+
return null;
|
|
41543
|
+
}
|
|
41544
|
+
};
|
|
41545
|
+
const buildNpmLockLookup = (lockText) => {
|
|
41546
|
+
let parsed;
|
|
41547
|
+
try {
|
|
41548
|
+
parsed = JSON.parse(lockText);
|
|
41549
|
+
} catch {
|
|
41550
|
+
return null;
|
|
41551
|
+
}
|
|
41552
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
41553
|
+
const lock = parsed;
|
|
41554
|
+
return (name) => {
|
|
41555
|
+
const entry = lock.packages?.[`node_modules/${name}`] ?? lock.dependencies?.[name];
|
|
41556
|
+
return typeof entry?.version === "string" ? entry.version : null;
|
|
41557
|
+
};
|
|
41558
|
+
};
|
|
41559
|
+
const buildYarnLockLookup = (lockText) => {
|
|
41560
|
+
const lines = lockText.split(/\r?\n/);
|
|
41561
|
+
return (name, spec) => {
|
|
41562
|
+
const descriptor = `${name}@${spec}`;
|
|
41563
|
+
const berryDescriptor = `${name}@npm:${spec}`;
|
|
41564
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
41565
|
+
const line = lines[lineIndex];
|
|
41566
|
+
if (line.startsWith(" ") || line.startsWith("#") || !line.includes(":")) continue;
|
|
41567
|
+
const descriptors = line.replace(/:\s*$/, "").split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
|
|
41568
|
+
if (!descriptors.includes(descriptor) && !descriptors.includes(berryDescriptor)) continue;
|
|
41569
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && (lines[bodyIndex].startsWith(" ") || lines[bodyIndex] === ""); bodyIndex += 1) {
|
|
41570
|
+
const versionMatch = /^\s+version:?\s+"?([^"\s]+)"?/.exec(lines[bodyIndex]);
|
|
41571
|
+
if (versionMatch) return versionMatch[1];
|
|
41572
|
+
}
|
|
41573
|
+
return null;
|
|
41574
|
+
}
|
|
41575
|
+
return null;
|
|
41576
|
+
};
|
|
41577
|
+
};
|
|
41578
|
+
const buildPnpmLockLookup = (lockText) => {
|
|
41579
|
+
return (name) => {
|
|
41580
|
+
const entryPattern = new RegExp(`^ {4,8}['"]?${name.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}['"]?:\\s*$`);
|
|
41581
|
+
const lines = lockText.split(/\r?\n/);
|
|
41582
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
41583
|
+
if (!entryPattern.test(lines[lineIndex])) continue;
|
|
41584
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && /^ {5,}\S/.test(lines[bodyIndex]); bodyIndex += 1) {
|
|
41585
|
+
const versionMatch = /^\s+version:\s+['"]?([^'"\s(]+)/.exec(lines[bodyIndex]);
|
|
41586
|
+
if (versionMatch) return versionMatch[1];
|
|
41587
|
+
}
|
|
41588
|
+
}
|
|
41589
|
+
return null;
|
|
41590
|
+
};
|
|
41591
|
+
};
|
|
41592
|
+
const buildLockedVersionLookup = (rootDirectory) => {
|
|
41593
|
+
const npmLockText = readTextFileOrNull(Path.join(rootDirectory, "package-lock.json"));
|
|
41594
|
+
if (npmLockText !== null) return buildNpmLockLookup(npmLockText);
|
|
41595
|
+
const yarnLockText = readTextFileOrNull(Path.join(rootDirectory, "yarn.lock"));
|
|
41596
|
+
if (yarnLockText !== null) return buildYarnLockLookup(yarnLockText);
|
|
41597
|
+
const pnpmLockText = readTextFileOrNull(Path.join(rootDirectory, "pnpm-lock.yaml"));
|
|
41598
|
+
if (pnpmLockText !== null) return buildPnpmLockLookup(pnpmLockText);
|
|
41599
|
+
return null;
|
|
41600
|
+
};
|
|
41156
41601
|
const locateDependencyKey = (packageJsonText, section, name) => {
|
|
41157
41602
|
const needle = `"${name}"`;
|
|
41158
41603
|
const sectionHeader = new RegExp(`"${section}"\\s*:\\s*\\{`);
|
|
@@ -41179,7 +41624,7 @@ const locateDependencyKey = (packageJsonText, section, name) => {
|
|
|
41179
41624
|
}
|
|
41180
41625
|
return null;
|
|
41181
41626
|
};
|
|
41182
|
-
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies) => {
|
|
41627
|
+
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies, lockedVersionLookup) => {
|
|
41183
41628
|
const sectionByName = /* @__PURE__ */ new Map();
|
|
41184
41629
|
for (const name of Object.keys(packageJson.dependencies ?? {})) sectionByName.set(name, "dependencies");
|
|
41185
41630
|
if (includeDevDependencies) {
|
|
@@ -41189,13 +41634,18 @@ const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDepe
|
|
|
41189
41634
|
for (const [name, section] of sectionByName) {
|
|
41190
41635
|
if (SUPPLY_CHAIN_IGNORED_PACKAGES.has(name)) continue;
|
|
41191
41636
|
const spec = (packageJson[section] ?? {})[name] ?? "";
|
|
41192
|
-
const
|
|
41193
|
-
if (
|
|
41637
|
+
const floorVersion = resolveConcreteVersion(spec);
|
|
41638
|
+
if (floorVersion === null) continue;
|
|
41639
|
+
const lockedVersion = lockedVersionLookup?.(name, spec) ?? null;
|
|
41640
|
+
const lockSatisfiesSpec = lockedVersion !== null && import_semver.valid(lockedVersion) !== null && import_semver.satisfies(lockedVersion, spec);
|
|
41641
|
+
const isExactPin = import_semver.valid(spec.trim()) !== null;
|
|
41194
41642
|
const location = locateDependencyKey(packageJsonText, section, name);
|
|
41195
41643
|
dependencies.push({
|
|
41196
41644
|
name,
|
|
41197
|
-
version,
|
|
41645
|
+
version: lockSatisfiesSpec ? lockedVersion : floorVersion,
|
|
41198
41646
|
spec,
|
|
41647
|
+
section,
|
|
41648
|
+
resolution: isExactPin ? "pin" : lockSatisfiesSpec ? "locked" : "floor",
|
|
41199
41649
|
line: location?.line ?? 0,
|
|
41200
41650
|
column: location?.column ?? 0
|
|
41201
41651
|
});
|
|
@@ -41346,7 +41796,11 @@ const formatAlertReason = (topAlerts, totalCount) => {
|
|
|
41346
41796
|
}
|
|
41347
41797
|
return `Socket flagged ${totalCount} alerts (${topAlerts.map((alert) => friendlyAlertType(alert.type)).join(", ")}${totalCount > topAlerts.length ? ` (+${totalCount - topAlerts.length} more)` : ""}); most severe: ${displaySeverity(topAlerts[0].severity)}.`;
|
|
41348
41798
|
};
|
|
41349
|
-
const formatDependencyIdentity = (dependency) =>
|
|
41799
|
+
const formatDependencyIdentity = (dependency) => {
|
|
41800
|
+
if (dependency.resolution === "pin") return `${dependency.name}@${dependency.version}`;
|
|
41801
|
+
if (dependency.resolution === "locked") return `${dependency.name}@${dependency.version} (lockfile resolution of "${dependency.spec}")`;
|
|
41802
|
+
return `${dependency.name}@${dependency.version} (lowest version "${dependency.spec}" allows)`;
|
|
41803
|
+
};
|
|
41350
41804
|
const buildSupplyChainHelp = (dependency, failingAxis, topAlerts, packagePageUrl, options) => {
|
|
41351
41805
|
const hasCriticalAlert = topAlerts.some((alert) => alert.severity.toLowerCase() === "critical");
|
|
41352
41806
|
const entry = `\`"${dependency.name}": "${dependency.spec}"\``;
|
|
@@ -41389,7 +41843,7 @@ const buildLowScoreDiagnostic = (dependency, artifact, failingAxis, options) =>
|
|
|
41389
41843
|
const checkSupplyChain = (input) => gen(function* () {
|
|
41390
41844
|
const options = resolveOptions(input.userConfig);
|
|
41391
41845
|
const packageJsonPath = Path.join(input.rootDirectory, "package.json");
|
|
41392
|
-
const dependencies = collectDependenciesToScore(readPackageJson(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
|
|
41846
|
+
const dependencies = collectDependenciesToScore(readPackageJson(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies, buildLockedVersionLookup(input.rootDirectory));
|
|
41393
41847
|
if (dependencies.length === 0) return [];
|
|
41394
41848
|
const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
|
|
41395
41849
|
if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
|
|
@@ -41398,7 +41852,7 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
41398
41852
|
for (let index = 0; index < dependencies.length; index += 1) {
|
|
41399
41853
|
const artifact = artifacts[index];
|
|
41400
41854
|
if (!artifact) continue;
|
|
41401
|
-
const worstAxis = worstGatedAxis(artifact.score);
|
|
41855
|
+
const worstAxis = worstGatedAxis(artifact.score, gatedAxesForSection(dependencies[index].section));
|
|
41402
41856
|
if (toHundred(artifact.score[worstAxis.key]) >= options.minScore) continue;
|
|
41403
41857
|
diagnostics.push(buildLowScoreDiagnostic(dependencies[index], artifact, worstAxis, options));
|
|
41404
41858
|
}
|
|
@@ -42441,4 +42895,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
42441
42895
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
42442
42896
|
|
|
42443
42897
|
//# sourceMappingURL=index.js.map
|
|
42444
|
-
//# debugId=
|
|
42898
|
+
//# debugId=10e3e1b0-f291-5306-b5fd-79b158b00159
|