react-doctor 0.7.1-dev.d353dad → 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 +409 -36
- package/dist/index.d.ts +7 -0
- package/dist/index.js +402 -31
- package/dist/lsp.js +403 -32
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a8ebd975-0f3b-5218-ad38-b9c8095f6668")}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";
|
|
@@ -9,7 +9,7 @@ import * as NFS from "node:fs";
|
|
|
9
9
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
10
10
|
import process$1 from "node: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, { tmpdir } from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
@@ -35968,8 +35968,10 @@ const isFile = (filePath) => {
|
|
|
35968
35968
|
};
|
|
35969
35969
|
const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
|
|
35970
35970
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
35971
|
+
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
35971
35972
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
35972
35973
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
35974
|
+
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
35973
35975
|
const IGNORED_DIRECTORIES = new Set([
|
|
35974
35976
|
".angular",
|
|
35975
35977
|
".astro",
|
|
@@ -36144,7 +36146,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
36144
36146
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36145
36147
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36146
36148
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36147
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
36149
|
+
const CORE_PACKAGE_VERSION = "0.7.2";
|
|
36148
36150
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36149
36151
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36150
36152
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -36154,8 +36156,9 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
36154
36156
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
36155
36157
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
36156
36158
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
36157
|
-
const
|
|
36158
|
-
const
|
|
36159
|
+
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
36160
|
+
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
36161
|
+
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
36159
36162
|
const isMinifiedSource = (absolutePath) => {
|
|
36160
36163
|
let fileDescriptor;
|
|
36161
36164
|
try {
|
|
@@ -36216,7 +36219,7 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
36216
36219
|
for (const entry of readDirectoryEntries(currentDirectory)) {
|
|
36217
36220
|
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
36218
36221
|
if (entry.isDirectory()) {
|
|
36219
|
-
if (!
|
|
36222
|
+
if (!isIgnoredDirectoryName(entry.name)) stack.push(absolutePath);
|
|
36220
36223
|
continue;
|
|
36221
36224
|
}
|
|
36222
36225
|
if (entry.isFile()) yield {
|
|
@@ -37179,6 +37182,10 @@ const normalizeCompilerOptions = (compilerOptions) => {
|
|
|
37179
37182
|
hasExplicitLib
|
|
37180
37183
|
};
|
|
37181
37184
|
};
|
|
37185
|
+
const normalizeReferencePaths = (references) => {
|
|
37186
|
+
if (!Array.isArray(references)) return [];
|
|
37187
|
+
return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
|
|
37188
|
+
};
|
|
37182
37189
|
const readTsConfig = (filePath) => {
|
|
37183
37190
|
let content;
|
|
37184
37191
|
try {
|
|
@@ -37190,6 +37197,7 @@ const readTsConfig = (filePath) => {
|
|
|
37190
37197
|
if (!isPlainObject(parsed.config)) return null;
|
|
37191
37198
|
return {
|
|
37192
37199
|
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
37200
|
+
referencePaths: normalizeReferencePaths(parsed.config.references),
|
|
37193
37201
|
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
37194
37202
|
};
|
|
37195
37203
|
};
|
|
@@ -37230,20 +37238,37 @@ const libEntryIncludesES2023Array = (entry) => {
|
|
|
37230
37238
|
};
|
|
37231
37239
|
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
37232
37240
|
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
37241
|
+
if (compilerOptions.target && targetYearIsPreES2023(compilerOptions.target)) return true;
|
|
37233
37242
|
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
37234
|
-
if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
|
|
37235
37243
|
return false;
|
|
37236
37244
|
};
|
|
37237
37245
|
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
37238
|
-
const detectPreES2023FromConfig = (tsConfigPath) => {
|
|
37246
|
+
const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
|
|
37247
|
+
if (visitedConfigPaths.has(tsConfigPath)) return false;
|
|
37239
37248
|
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
37240
37249
|
if (!compilerOptions) return false;
|
|
37241
|
-
if (!compilerOptionsDeclareTargetOrLib(compilerOptions))
|
|
37250
|
+
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
|
|
37251
|
+
const tsConfig = readTsConfig(tsConfigPath);
|
|
37252
|
+
if (!tsConfig) return false;
|
|
37253
|
+
const nextVisitedConfigPaths = new Set(visitedConfigPaths);
|
|
37254
|
+
nextVisitedConfigPaths.add(tsConfigPath);
|
|
37255
|
+
const configDirectory = Path.dirname(tsConfigPath);
|
|
37256
|
+
return tsConfig.referencePaths.some((referencePath) => {
|
|
37257
|
+
const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
|
|
37258
|
+
const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
|
|
37259
|
+
return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
|
|
37260
|
+
});
|
|
37261
|
+
}
|
|
37242
37262
|
return compilerOptionsArePreES2023(compilerOptions);
|
|
37243
37263
|
};
|
|
37264
|
+
const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
|
|
37244
37265
|
const detectPreES2023Target = (directory) => {
|
|
37245
37266
|
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
37246
37267
|
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
37268
|
+
for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
|
|
37269
|
+
const fallbackPath = Path.join(directory, fallbackFilename);
|
|
37270
|
+
if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
|
|
37271
|
+
}
|
|
37247
37272
|
return false;
|
|
37248
37273
|
};
|
|
37249
37274
|
const REACT_DEPENDENCY_NAMES = new Set([
|
|
@@ -37402,6 +37427,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37402
37427
|
shopifyFlashListVersion: null,
|
|
37403
37428
|
shopifyFlashListMajorVersion: null,
|
|
37404
37429
|
hasReanimated: false,
|
|
37430
|
+
reanimatedVersion: null,
|
|
37405
37431
|
isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
|
|
37406
37432
|
sourceFileCount
|
|
37407
37433
|
};
|
|
@@ -37494,6 +37520,7 @@ const discoverProject = (directory) => {
|
|
|
37494
37520
|
version: findShopifyFlashListVersion(directory, packageJson)
|
|
37495
37521
|
}) : null;
|
|
37496
37522
|
const hasReanimated = hasReactNativeWorkspace && someWorkspacePackageJson(directory, packageJson, isPackageJsonReanimatedAware);
|
|
37523
|
+
const reanimatedVersion = hasReanimated ? findInWorkspacePackageJsons(directory, packageJson, (workspacePackageJson) => getDependencySpec(workspacePackageJson, REANIMATED_DEPENDENCY_NAME)) : null;
|
|
37497
37524
|
const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
|
|
37498
37525
|
rootDirectory: directory,
|
|
37499
37526
|
rootPackageJson: packageJson,
|
|
@@ -37523,6 +37550,7 @@ const discoverProject = (directory) => {
|
|
|
37523
37550
|
shopifyFlashListVersion,
|
|
37524
37551
|
shopifyFlashListMajorVersion: shopifyFlashListVersion === null ? null : getLowestDependencyMajor(shopifyFlashListVersion),
|
|
37525
37552
|
hasReanimated,
|
|
37553
|
+
reanimatedVersion,
|
|
37526
37554
|
isPreES2023Target,
|
|
37527
37555
|
sourceFileCount
|
|
37528
37556
|
};
|
|
@@ -37715,6 +37743,9 @@ const buildRuleSeverityControls = (config) => {
|
|
|
37715
37743
|
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
37716
37744
|
};
|
|
37717
37745
|
};
|
|
37746
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
37747
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
37748
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
37718
37749
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
37719
37750
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
37720
37751
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -37792,6 +37823,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
37792
37823
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
37793
37824
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
37794
37825
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37826
|
+
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
37795
37827
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
37796
37828
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
37797
37829
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -37876,9 +37908,6 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
37876
37908
|
if (!ruleSection) return [];
|
|
37877
37909
|
return ruleSection.split(/[,\s]+/).map((token) => token.trim()).filter(Boolean);
|
|
37878
37910
|
};
|
|
37879
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
37880
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
37881
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
37882
37911
|
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}\`).`;
|
|
37883
37912
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
37884
37913
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
@@ -37932,6 +37961,53 @@ const detectForeignDisableNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
37932
37961
|
if (!ruleId.startsWith("react-doctor/")) return null;
|
|
37933
37962
|
return detectInlineNearMiss(lines, diagnosticLineIndex, ruleId) ?? detectBlockNearMiss(lines, diagnosticLineIndex, ruleId);
|
|
37934
37963
|
};
|
|
37964
|
+
const ESLINT_REACT_HOOKS_PLUGIN_PREFIX = "react-hooks/";
|
|
37965
|
+
const buildAcceptedTokens = (ruleId) => {
|
|
37966
|
+
const shortRuleId = ruleId.slice(15);
|
|
37967
|
+
return new Set([ruleId, `${ESLINT_REACT_HOOKS_PLUGIN_PREFIX}${shortRuleId}`]);
|
|
37968
|
+
};
|
|
37969
|
+
const listNamesRule = (ruleList, acceptedTokens) => tokenizeRuleList(ruleList).some((token) => acceptedTokens.has(token));
|
|
37970
|
+
const isInlineDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
37971
|
+
const candidates = [{
|
|
37972
|
+
line: lines[diagnosticLineIndex],
|
|
37973
|
+
requiredScope: "line"
|
|
37974
|
+
}, {
|
|
37975
|
+
line: lines[diagnosticLineIndex - 1],
|
|
37976
|
+
requiredScope: "next-line"
|
|
37977
|
+
}];
|
|
37978
|
+
for (const { line, requiredScope } of candidates) {
|
|
37979
|
+
const match = line?.match(FOREIGN_INLINE_DISABLE_PATTERN);
|
|
37980
|
+
if (!match) continue;
|
|
37981
|
+
const [, , scope, ruleList] = match;
|
|
37982
|
+
if (scope !== requiredScope) continue;
|
|
37983
|
+
if (listNamesRule(ruleList, acceptedTokens)) return true;
|
|
37984
|
+
}
|
|
37985
|
+
return false;
|
|
37986
|
+
};
|
|
37987
|
+
const isBlockDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
37988
|
+
let isRangeOpen = false;
|
|
37989
|
+
const lastLineIndex = Math.min(diagnosticLineIndex, lines.length - 1);
|
|
37990
|
+
for (let lineIndex = 0; lineIndex <= lastLineIndex; lineIndex++) {
|
|
37991
|
+
const line = lines[lineIndex];
|
|
37992
|
+
if (line === void 0 || !line.includes("-disable") && !line.includes("-enable")) continue;
|
|
37993
|
+
const disableMatch = line.match(FOREIGN_BLOCK_DISABLE_PATTERN);
|
|
37994
|
+
if (disableMatch && listNamesRule(disableMatch[2], acceptedTokens)) {
|
|
37995
|
+
isRangeOpen = true;
|
|
37996
|
+
continue;
|
|
37997
|
+
}
|
|
37998
|
+
const enableMatch = line.match(FOREIGN_BLOCK_ENABLE_PATTERN);
|
|
37999
|
+
if (enableMatch) {
|
|
38000
|
+
const enabledRules = tokenizeRuleList(enableMatch[1]);
|
|
38001
|
+
if (enabledRules.length === 0 || enabledRules.some((rule) => acceptedTokens.has(rule))) isRangeOpen = false;
|
|
38002
|
+
}
|
|
38003
|
+
}
|
|
38004
|
+
return isRangeOpen;
|
|
38005
|
+
};
|
|
38006
|
+
const isCompilerRuleForeignDisabled = (lines, diagnosticLineIndex, ruleId) => {
|
|
38007
|
+
if (!ruleId.startsWith("react-hooks-js/")) return false;
|
|
38008
|
+
const acceptedTokens = buildAcceptedTokens(ruleId);
|
|
38009
|
+
return isInlineDisabled(lines, diagnosticLineIndex, acceptedTokens) || isBlockDisabled(lines, diagnosticLineIndex, acceptedTokens);
|
|
38010
|
+
};
|
|
37935
38011
|
const JSX_OPENER_TAG_PATTERN = /<[A-Za-z][\w.]*/g;
|
|
37936
38012
|
const JSX_TAG_NAME_FOLLOW = /[A-Za-z]/;
|
|
37937
38013
|
const isOpenerMatchInsideLineComment = (line, openerCharIndex) => {
|
|
@@ -38075,6 +38151,11 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
38075
38151
|
isSuppressed: true,
|
|
38076
38152
|
nearMissHint: null
|
|
38077
38153
|
};
|
|
38154
|
+
if (isCompilerRuleForeignDisabled(lines, diagnosticLineIndex, ruleId)) return {
|
|
38155
|
+
isSuppressed: true,
|
|
38156
|
+
nearMissHint: null,
|
|
38157
|
+
isForeignDirective: true
|
|
38158
|
+
};
|
|
38078
38159
|
const directComments = findStackedDisableCommentsAbove(lines, diagnosticLineIndex);
|
|
38079
38160
|
if (hasChainSuppressor(directComments, ruleId)) return {
|
|
38080
38161
|
isSuppressed: true,
|
|
@@ -38356,12 +38437,18 @@ const collectStringSet = (values) => {
|
|
|
38356
38437
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
38357
38438
|
};
|
|
38358
38439
|
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
38440
|
+
const NODE_CLI_PATH_SEGMENTS = new Set([
|
|
38441
|
+
"scripts",
|
|
38442
|
+
"bin",
|
|
38443
|
+
"tools"
|
|
38444
|
+
]);
|
|
38445
|
+
const hasNodeCliPathSegment = (filePath) => filePath.split(/[\\/]/).slice(0, -1).some((segment) => NODE_CLI_PATH_SEGMENTS.has(segment));
|
|
38359
38446
|
/**
|
|
38360
38447
|
* Pre-compiles every stateful filter and returns a single
|
|
38361
38448
|
* `apply(diagnostic)` closure that runs (in order):
|
|
38362
38449
|
*
|
|
38363
38450
|
* 1. auto-suppress (test-noise rules in test files; `migration-hint`
|
|
38364
|
-
* wins over `test-noise`)
|
|
38451
|
+
* wins over `test-noise`; `no-console` on Node CLI scripts)
|
|
38365
38452
|
* 2. severity overrides (top-level `rules` / `categories`, with
|
|
38366
38453
|
* `"off"` dropping)
|
|
38367
38454
|
* 3. warning suppression (only when `showWarnings` is false: drops every
|
|
@@ -38459,6 +38546,12 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38459
38546
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
38460
38547
|
return false;
|
|
38461
38548
|
};
|
|
38549
|
+
const isNoConsoleOnNodeCliScript = (diagnostic) => {
|
|
38550
|
+
if (diagnostic.rule !== "no-console") return false;
|
|
38551
|
+
if (hasNodeCliPathSegment(diagnostic.filePath)) return true;
|
|
38552
|
+
const lines = getFileLines(diagnostic.filePath);
|
|
38553
|
+
return lines !== null && Boolean(lines[0]?.startsWith("#!"));
|
|
38554
|
+
};
|
|
38462
38555
|
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
38463
38556
|
if (!hasRuntimeGlobals) return false;
|
|
38464
38557
|
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
@@ -38468,6 +38561,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38468
38561
|
return {
|
|
38469
38562
|
apply: (diagnostic) => {
|
|
38470
38563
|
if (shouldAutoSuppress(diagnostic)) return null;
|
|
38564
|
+
if (isNoConsoleOnNodeCliScript(diagnostic)) return null;
|
|
38471
38565
|
let current = diagnostic;
|
|
38472
38566
|
let explicitSeverityOverride;
|
|
38473
38567
|
let explicitRuleOverride;
|
|
@@ -38497,7 +38591,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38497
38591
|
if (lines) {
|
|
38498
38592
|
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
38499
38593
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
38500
|
-
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
38594
|
+
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
38501
38595
|
if (evaluation.nearMissHint) current = {
|
|
38502
38596
|
...current,
|
|
38503
38597
|
suppressionHint: evaluation.nearMissHint
|
|
@@ -42673,9 +42767,17 @@ const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
|
|
|
42673
42767
|
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`;
|
|
42674
42768
|
};
|
|
42675
42769
|
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";
|
|
42770
|
+
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42771
|
+
const supportsAccessors = (reanimatedVersion) => {
|
|
42772
|
+
if (reanimatedVersion === null) return true;
|
|
42773
|
+
const lowerBound = import_semver.validRange(reanimatedVersion) !== null ? import_semver.minVersion(reanimatedVersion) : import_semver.coerce(reanimatedVersion);
|
|
42774
|
+
if (lowerBound === null) return true;
|
|
42775
|
+
return import_semver.gte(lowerBound, ACCESSORS_MIN_VERSION);
|
|
42776
|
+
};
|
|
42676
42777
|
const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
42677
42778
|
if (rule !== "immutability") return help;
|
|
42678
42779
|
if (!project.hasReanimated) return help;
|
|
42780
|
+
if (!supportsAccessors(project.reanimatedVersion)) return help;
|
|
42679
42781
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
42680
42782
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
42681
42783
|
};
|
|
@@ -42837,13 +42939,13 @@ const REACT_USE_BINDING_RESOLUTION = {
|
|
|
42837
42939
|
isReactUseBinding: true,
|
|
42838
42940
|
isReactNamespaceBinding: false
|
|
42839
42941
|
};
|
|
42840
|
-
const getScriptKind = (filename) => {
|
|
42942
|
+
const getScriptKind$2 = (filename) => {
|
|
42841
42943
|
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
42842
42944
|
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
42843
42945
|
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
42844
42946
|
return ts.ScriptKind.JS;
|
|
42845
42947
|
};
|
|
42846
|
-
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
42948
|
+
const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
42847
42949
|
const unwrapExpression = (expression) => {
|
|
42848
42950
|
let currentExpression = expression;
|
|
42849
42951
|
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
@@ -43092,8 +43194,8 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
|
|
|
43092
43194
|
return matchedIdentifier;
|
|
43093
43195
|
};
|
|
43094
43196
|
const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
|
|
43095
|
-
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filename));
|
|
43096
|
-
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset(sourceText, utf8Offset));
|
|
43197
|
+
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$2(filename));
|
|
43198
|
+
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
|
|
43097
43199
|
if (!useIdentifier) return null;
|
|
43098
43200
|
return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
|
|
43099
43201
|
};
|
|
@@ -43114,6 +43216,201 @@ const shouldSuppressLocalUseHookDiagnostic = (diagnostic, rootDirectory) => {
|
|
|
43114
43216
|
const bindingResolution = resolveUseCallBinding(sourceText, absolutePath, primaryLabel.span.offset);
|
|
43115
43217
|
return bindingResolution !== null && !bindingResolution.isReactUseBinding;
|
|
43116
43218
|
};
|
|
43219
|
+
const SUPPRESSED_COMPILER_CODES = new Set(["react-hooks-js(immutability)", "react-hooks-js(refs)"]);
|
|
43220
|
+
const WORKLET_DIRECTIVE = "worklet";
|
|
43221
|
+
const WORKLET_ACCEPTING_CALLEES = new Set([
|
|
43222
|
+
"useAnimatedStyle",
|
|
43223
|
+
"useAnimatedProps",
|
|
43224
|
+
"useDerivedValue",
|
|
43225
|
+
"useAnimatedReaction",
|
|
43226
|
+
"useAnimatedScrollHandler",
|
|
43227
|
+
"useAnimatedGestureHandler",
|
|
43228
|
+
"useFrameCallback",
|
|
43229
|
+
"runOnUI",
|
|
43230
|
+
"executeOnUIRuntimeSync"
|
|
43231
|
+
]);
|
|
43232
|
+
const GESTURE_ROOT_IDENTIFIER = "Gesture";
|
|
43233
|
+
const getScriptKind$1 = (filename) => {
|
|
43234
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43235
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43236
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43237
|
+
return ts.ScriptKind.JS;
|
|
43238
|
+
};
|
|
43239
|
+
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
43240
|
+
const hasWorkletDirective = (node) => {
|
|
43241
|
+
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
|
|
43242
|
+
const body = node.body;
|
|
43243
|
+
if (!body || !ts.isBlock(body)) return false;
|
|
43244
|
+
for (const statement of body.statements) {
|
|
43245
|
+
if (!ts.isExpressionStatement(statement)) return false;
|
|
43246
|
+
if (!ts.isStringLiteral(statement.expression)) return false;
|
|
43247
|
+
if (statement.expression.text === WORKLET_DIRECTIVE) return true;
|
|
43248
|
+
}
|
|
43249
|
+
return false;
|
|
43250
|
+
};
|
|
43251
|
+
const rootIdentifierOfCallChain = (expression) => {
|
|
43252
|
+
let currentExpression = expression;
|
|
43253
|
+
while (true) {
|
|
43254
|
+
if (ts.isIdentifier(currentExpression)) return currentExpression.text;
|
|
43255
|
+
if (ts.isPropertyAccessExpression(currentExpression)) {
|
|
43256
|
+
currentExpression = currentExpression.expression;
|
|
43257
|
+
continue;
|
|
43258
|
+
}
|
|
43259
|
+
if (ts.isCallExpression(currentExpression)) {
|
|
43260
|
+
currentExpression = currentExpression.expression;
|
|
43261
|
+
continue;
|
|
43262
|
+
}
|
|
43263
|
+
return null;
|
|
43264
|
+
}
|
|
43265
|
+
};
|
|
43266
|
+
const isWorkletAcceptingCall = (callExpression) => {
|
|
43267
|
+
const callee = callExpression.expression;
|
|
43268
|
+
if (ts.isIdentifier(callee)) return WORKLET_ACCEPTING_CALLEES.has(callee.text);
|
|
43269
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
43270
|
+
if (WORKLET_ACCEPTING_CALLEES.has(callee.name.text)) return true;
|
|
43271
|
+
return rootIdentifierOfCallChain(callee) === GESTURE_ROOT_IDENTIFIER;
|
|
43272
|
+
}
|
|
43273
|
+
return false;
|
|
43274
|
+
};
|
|
43275
|
+
const isFunctionWorklet = (functionNode) => {
|
|
43276
|
+
if (hasWorkletDirective(functionNode)) return true;
|
|
43277
|
+
const parent = functionNode.parent;
|
|
43278
|
+
return ts.isCallExpression(parent) && parent.arguments.some((argument) => argument === functionNode) && isWorkletAcceptingCall(parent);
|
|
43279
|
+
};
|
|
43280
|
+
const findNodeAtOffset = (sourceFile, targetOffset) => {
|
|
43281
|
+
let matchedNode = null;
|
|
43282
|
+
const visit = (node) => {
|
|
43283
|
+
if (node.getStart(sourceFile) > targetOffset || node.getEnd() <= targetOffset) return;
|
|
43284
|
+
matchedNode = node;
|
|
43285
|
+
ts.forEachChild(node, visit);
|
|
43286
|
+
};
|
|
43287
|
+
visit(sourceFile);
|
|
43288
|
+
return matchedNode;
|
|
43289
|
+
};
|
|
43290
|
+
const isOffsetInsideWorklet = (sourceFile, targetOffset) => {
|
|
43291
|
+
let currentNode = findNodeAtOffset(sourceFile, targetOffset);
|
|
43292
|
+
while (currentNode) {
|
|
43293
|
+
if (ts.isFunctionLike(currentNode) && isFunctionWorklet(currentNode)) return true;
|
|
43294
|
+
currentNode = currentNode.parent;
|
|
43295
|
+
}
|
|
43296
|
+
return false;
|
|
43297
|
+
};
|
|
43298
|
+
const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirectory) => {
|
|
43299
|
+
if (!SUPPRESSED_COMPILER_CODES.has(diagnostic.code)) return false;
|
|
43300
|
+
if (!project.hasReanimated) return false;
|
|
43301
|
+
const primaryLabel = diagnostic.labels[0];
|
|
43302
|
+
if (!primaryLabel) return false;
|
|
43303
|
+
const absolutePath = Path.isAbsolute(diagnostic.filename) ? diagnostic.filename : Path.join(rootDirectory, diagnostic.filename);
|
|
43304
|
+
let sourceText;
|
|
43305
|
+
try {
|
|
43306
|
+
sourceText = NFS.readFileSync(absolutePath, "utf-8");
|
|
43307
|
+
} catch {
|
|
43308
|
+
return false;
|
|
43309
|
+
}
|
|
43310
|
+
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
|
|
43311
|
+
};
|
|
43312
|
+
const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
|
|
43313
|
+
const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
|
|
43314
|
+
const REACT_COMPILER_PLUGIN = "react-hooks-js";
|
|
43315
|
+
const getScriptKind = (filename) => {
|
|
43316
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43317
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43318
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43319
|
+
return ts.ScriptKind.JS;
|
|
43320
|
+
};
|
|
43321
|
+
const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
|
|
43322
|
+
const nodeLineRange = (sourceFile, node) => ({
|
|
43323
|
+
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
|
|
43324
|
+
endLine: sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1
|
|
43325
|
+
});
|
|
43326
|
+
const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
43327
|
+
let foundRange = null;
|
|
43328
|
+
const visit = (node) => {
|
|
43329
|
+
if (foundRange) return;
|
|
43330
|
+
if (ts.isFunctionLike(node)) {
|
|
43331
|
+
const range = nodeLineRange(sourceFile, node);
|
|
43332
|
+
if (range.startLine <= targetLine && targetLine <= range.endLine) foundRange = range;
|
|
43333
|
+
return;
|
|
43334
|
+
}
|
|
43335
|
+
ts.forEachChild(node, visit);
|
|
43336
|
+
};
|
|
43337
|
+
visit(sourceFile);
|
|
43338
|
+
return foundRange;
|
|
43339
|
+
};
|
|
43340
|
+
const NEWLINE_BYTE = 10;
|
|
43341
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
43342
|
+
let lineNumber = 1;
|
|
43343
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
43344
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
43345
|
+
return lineNumber;
|
|
43346
|
+
};
|
|
43347
|
+
/**
|
|
43348
|
+
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43349
|
+
* whose enclosing function also carries a `react-hooks-js` (React
|
|
43350
|
+
* Compiler) diagnostic. Every react-hooks-js finding marks a function the
|
|
43351
|
+
* compiler could NOT compile (a bail-out / skip / preserve-manual-
|
|
43352
|
+
* memoization failure), so "React Compiler already caches every value
|
|
43353
|
+
* here" is false for that function and the manual memoization is
|
|
43354
|
+
* load-bearing, not dead weight.
|
|
43355
|
+
*
|
|
43356
|
+
* Matching is per enclosing function, not per file: a bailed-out
|
|
43357
|
+
* component must not silence redundant-memo findings in a sibling
|
|
43358
|
+
* component the compiler did optimize.
|
|
43359
|
+
*
|
|
43360
|
+
* Module-scope `memo(() => …)` wrappers have no enclosing function; for
|
|
43361
|
+
* those the memo call's own span is the containment range, so a bail-out
|
|
43362
|
+
* inside the inline-wrapped component still suppresses. Every failure
|
|
43363
|
+
* path (unreadable file, missing span) fails open and keeps the
|
|
43364
|
+
* diagnostic.
|
|
43365
|
+
*/
|
|
43366
|
+
const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) => {
|
|
43367
|
+
const compilerDiagnosticLinesByFile = /* @__PURE__ */ new Map();
|
|
43368
|
+
let hasManualMemoizationDiagnostic = false;
|
|
43369
|
+
for (const diagnostic of diagnostics) {
|
|
43370
|
+
if (isManualMemoizationDiagnostic(diagnostic)) {
|
|
43371
|
+
hasManualMemoizationDiagnostic = true;
|
|
43372
|
+
continue;
|
|
43373
|
+
}
|
|
43374
|
+
if (diagnostic.plugin !== REACT_COMPILER_PLUGIN || diagnostic.line <= 0) continue;
|
|
43375
|
+
const lines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
43376
|
+
if (lines) lines.push(diagnostic.line);
|
|
43377
|
+
else compilerDiagnosticLinesByFile.set(diagnostic.filePath, [diagnostic.line]);
|
|
43378
|
+
}
|
|
43379
|
+
if (!hasManualMemoizationDiagnostic || compilerDiagnosticLinesByFile.size === 0) return diagnostics;
|
|
43380
|
+
const parsedFileCache = /* @__PURE__ */ new Map();
|
|
43381
|
+
const readAndParse = (filePath) => {
|
|
43382
|
+
const cached = parsedFileCache.get(filePath);
|
|
43383
|
+
if (cached !== void 0) return cached;
|
|
43384
|
+
const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory || ".", filePath);
|
|
43385
|
+
let parsed;
|
|
43386
|
+
try {
|
|
43387
|
+
const buffer = NFS.readFileSync(absolutePath);
|
|
43388
|
+
parsed = {
|
|
43389
|
+
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
43390
|
+
buffer
|
|
43391
|
+
};
|
|
43392
|
+
} catch {
|
|
43393
|
+
parsed = null;
|
|
43394
|
+
}
|
|
43395
|
+
parsedFileCache.set(filePath, parsed);
|
|
43396
|
+
return parsed;
|
|
43397
|
+
};
|
|
43398
|
+
const isInsideBailedOutFunction = (diagnostic) => {
|
|
43399
|
+
const compilerLines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
43400
|
+
if (!compilerLines || diagnostic.line <= 0) return false;
|
|
43401
|
+
const parsed = readAndParse(diagnostic.filePath);
|
|
43402
|
+
if (!parsed) return false;
|
|
43403
|
+
let containmentRange = findOutermostEnclosingFunctionRange(parsed.sourceFile, diagnostic.line);
|
|
43404
|
+
if (!containmentRange && diagnostic.offset !== void 0 && diagnostic.length !== void 0) containmentRange = {
|
|
43405
|
+
startLine: diagnostic.line,
|
|
43406
|
+
endLine: lineOfUtf8Offset(parsed.buffer, diagnostic.offset + diagnostic.length)
|
|
43407
|
+
};
|
|
43408
|
+
if (!containmentRange) return false;
|
|
43409
|
+
const { startLine, endLine } = containmentRange;
|
|
43410
|
+
return compilerLines.some((compilerLine) => startLine <= compilerLine && compilerLine <= endLine);
|
|
43411
|
+
};
|
|
43412
|
+
return diagnostics.filter((diagnostic) => !isManualMemoizationDiagnostic(diagnostic) || !isInsideBailedOutFunction(diagnostic));
|
|
43413
|
+
};
|
|
43117
43414
|
const FILEPATH_WITH_LOCATION_PATTERN = /\S+\.\w+:\d+:\d+[\s\S]*$/;
|
|
43118
43415
|
const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
43119
43416
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
@@ -43253,7 +43550,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43253
43550
|
minifiedFileCache.set(absolutePath, minified);
|
|
43254
43551
|
return minified;
|
|
43255
43552
|
};
|
|
43256
|
-
return parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory)).map((diagnostic) => {
|
|
43553
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
43257
43554
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
43258
43555
|
const primaryLabel = diagnostic.labels[0];
|
|
43259
43556
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -43280,7 +43577,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43280
43577
|
...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
|
|
43281
43578
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43282
43579
|
};
|
|
43283
|
-
});
|
|
43580
|
+
}), rootDirectory);
|
|
43284
43581
|
};
|
|
43285
43582
|
const buildOxlintChildEnv = (sourceEnv) => {
|
|
43286
43583
|
const childEnv = {};
|
|
@@ -43791,6 +44088,7 @@ const runOxlint = async (options) => {
|
|
|
43791
44088
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
43792
44089
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
43793
44090
|
validateRuleRegistration();
|
|
44091
|
+
resetManifestCaches();
|
|
43794
44092
|
if (includePaths !== void 0 && includePaths.length === 0) return [];
|
|
43795
44093
|
const pluginPath = resolvePluginPath();
|
|
43796
44094
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
@@ -44453,9 +44751,10 @@ const CONTEXT_AXES = [
|
|
|
44453
44751
|
}
|
|
44454
44752
|
];
|
|
44455
44753
|
const SCORE_AXES = [...GATED_AXES, ...CONTEXT_AXES];
|
|
44456
|
-
const
|
|
44457
|
-
|
|
44458
|
-
|
|
44754
|
+
const gatedAxesForSection = (section) => section === "devDependencies" ? GATED_AXES.filter((axis) => axis.key !== "vulnerability") : GATED_AXES;
|
|
44755
|
+
const worstGatedAxis = (score, gatedAxes) => {
|
|
44756
|
+
let worst = gatedAxes[0];
|
|
44757
|
+
for (const axis of gatedAxes) if (score[axis.key] < score[worst.key]) worst = axis;
|
|
44459
44758
|
return worst;
|
|
44460
44759
|
};
|
|
44461
44760
|
const clampScore = (value) => {
|
|
@@ -44479,6 +44778,69 @@ const resolveConcreteVersion = (spec) => {
|
|
|
44479
44778
|
if (range === null || range === "*") return null;
|
|
44480
44779
|
return import_semver.minVersion(trimmed)?.version ?? null;
|
|
44481
44780
|
};
|
|
44781
|
+
const readTextFileOrNull = (filePath) => {
|
|
44782
|
+
try {
|
|
44783
|
+
return NFS.readFileSync(filePath, "utf-8");
|
|
44784
|
+
} catch {
|
|
44785
|
+
return null;
|
|
44786
|
+
}
|
|
44787
|
+
};
|
|
44788
|
+
const buildNpmLockLookup = (lockText) => {
|
|
44789
|
+
let parsed;
|
|
44790
|
+
try {
|
|
44791
|
+
parsed = JSON.parse(lockText);
|
|
44792
|
+
} catch {
|
|
44793
|
+
return null;
|
|
44794
|
+
}
|
|
44795
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
44796
|
+
const lock = parsed;
|
|
44797
|
+
return (name) => {
|
|
44798
|
+
const entry = lock.packages?.[`node_modules/${name}`] ?? lock.dependencies?.[name];
|
|
44799
|
+
return typeof entry?.version === "string" ? entry.version : null;
|
|
44800
|
+
};
|
|
44801
|
+
};
|
|
44802
|
+
const buildYarnLockLookup = (lockText) => {
|
|
44803
|
+
const lines = lockText.split(/\r?\n/);
|
|
44804
|
+
return (name, spec) => {
|
|
44805
|
+
const descriptor = `${name}@${spec}`;
|
|
44806
|
+
const berryDescriptor = `${name}@npm:${spec}`;
|
|
44807
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
44808
|
+
const line = lines[lineIndex];
|
|
44809
|
+
if (line.startsWith(" ") || line.startsWith("#") || !line.includes(":")) continue;
|
|
44810
|
+
const descriptors = line.replace(/:\s*$/, "").split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
|
|
44811
|
+
if (!descriptors.includes(descriptor) && !descriptors.includes(berryDescriptor)) continue;
|
|
44812
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && (lines[bodyIndex].startsWith(" ") || lines[bodyIndex] === ""); bodyIndex += 1) {
|
|
44813
|
+
const versionMatch = /^\s+version:?\s+"?([^"\s]+)"?/.exec(lines[bodyIndex]);
|
|
44814
|
+
if (versionMatch) return versionMatch[1];
|
|
44815
|
+
}
|
|
44816
|
+
return null;
|
|
44817
|
+
}
|
|
44818
|
+
return null;
|
|
44819
|
+
};
|
|
44820
|
+
};
|
|
44821
|
+
const buildPnpmLockLookup = (lockText) => {
|
|
44822
|
+
return (name) => {
|
|
44823
|
+
const entryPattern = new RegExp(`^ {4,8}['"]?${name.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}['"]?:\\s*$`);
|
|
44824
|
+
const lines = lockText.split(/\r?\n/);
|
|
44825
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
44826
|
+
if (!entryPattern.test(lines[lineIndex])) continue;
|
|
44827
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && /^ {5,}\S/.test(lines[bodyIndex]); bodyIndex += 1) {
|
|
44828
|
+
const versionMatch = /^\s+version:\s+['"]?([^'"\s(]+)/.exec(lines[bodyIndex]);
|
|
44829
|
+
if (versionMatch) return versionMatch[1];
|
|
44830
|
+
}
|
|
44831
|
+
}
|
|
44832
|
+
return null;
|
|
44833
|
+
};
|
|
44834
|
+
};
|
|
44835
|
+
const buildLockedVersionLookup = (rootDirectory) => {
|
|
44836
|
+
const npmLockText = readTextFileOrNull(Path.join(rootDirectory, "package-lock.json"));
|
|
44837
|
+
if (npmLockText !== null) return buildNpmLockLookup(npmLockText);
|
|
44838
|
+
const yarnLockText = readTextFileOrNull(Path.join(rootDirectory, "yarn.lock"));
|
|
44839
|
+
if (yarnLockText !== null) return buildYarnLockLookup(yarnLockText);
|
|
44840
|
+
const pnpmLockText = readTextFileOrNull(Path.join(rootDirectory, "pnpm-lock.yaml"));
|
|
44841
|
+
if (pnpmLockText !== null) return buildPnpmLockLookup(pnpmLockText);
|
|
44842
|
+
return null;
|
|
44843
|
+
};
|
|
44482
44844
|
const locateDependencyKey = (packageJsonText, section, name) => {
|
|
44483
44845
|
const needle = `"${name}"`;
|
|
44484
44846
|
const sectionHeader = new RegExp(`"${section}"\\s*:\\s*\\{`);
|
|
@@ -44505,7 +44867,7 @@ const locateDependencyKey = (packageJsonText, section, name) => {
|
|
|
44505
44867
|
}
|
|
44506
44868
|
return null;
|
|
44507
44869
|
};
|
|
44508
|
-
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies) => {
|
|
44870
|
+
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies, lockedVersionLookup) => {
|
|
44509
44871
|
const sectionByName = /* @__PURE__ */ new Map();
|
|
44510
44872
|
for (const name of Object.keys(packageJson.dependencies ?? {})) sectionByName.set(name, "dependencies");
|
|
44511
44873
|
if (includeDevDependencies) {
|
|
@@ -44515,13 +44877,18 @@ const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDepe
|
|
|
44515
44877
|
for (const [name, section] of sectionByName) {
|
|
44516
44878
|
if (SUPPLY_CHAIN_IGNORED_PACKAGES.has(name)) continue;
|
|
44517
44879
|
const spec = (packageJson[section] ?? {})[name] ?? "";
|
|
44518
|
-
const
|
|
44519
|
-
if (
|
|
44880
|
+
const floorVersion = resolveConcreteVersion(spec);
|
|
44881
|
+
if (floorVersion === null) continue;
|
|
44882
|
+
const lockedVersion = lockedVersionLookup?.(name, spec) ?? null;
|
|
44883
|
+
const lockSatisfiesSpec = lockedVersion !== null && import_semver.valid(lockedVersion) !== null && import_semver.satisfies(lockedVersion, spec);
|
|
44884
|
+
const isExactPin = import_semver.valid(spec.trim()) !== null;
|
|
44520
44885
|
const location = locateDependencyKey(packageJsonText, section, name);
|
|
44521
44886
|
dependencies.push({
|
|
44522
44887
|
name,
|
|
44523
|
-
version,
|
|
44888
|
+
version: lockSatisfiesSpec ? lockedVersion : floorVersion,
|
|
44524
44889
|
spec,
|
|
44890
|
+
section,
|
|
44891
|
+
resolution: isExactPin ? "pin" : lockSatisfiesSpec ? "locked" : "floor",
|
|
44525
44892
|
line: location?.line ?? 0,
|
|
44526
44893
|
column: location?.column ?? 0
|
|
44527
44894
|
});
|
|
@@ -44672,7 +45039,11 @@ const formatAlertReason = (topAlerts, totalCount) => {
|
|
|
44672
45039
|
}
|
|
44673
45040
|
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)}.`;
|
|
44674
45041
|
};
|
|
44675
|
-
const formatDependencyIdentity = (dependency) =>
|
|
45042
|
+
const formatDependencyIdentity = (dependency) => {
|
|
45043
|
+
if (dependency.resolution === "pin") return `${dependency.name}@${dependency.version}`;
|
|
45044
|
+
if (dependency.resolution === "locked") return `${dependency.name}@${dependency.version} (lockfile resolution of "${dependency.spec}")`;
|
|
45045
|
+
return `${dependency.name}@${dependency.version} (lowest version "${dependency.spec}" allows)`;
|
|
45046
|
+
};
|
|
44676
45047
|
const buildSupplyChainHelp = (dependency, failingAxis, topAlerts, packagePageUrl, options) => {
|
|
44677
45048
|
const hasCriticalAlert = topAlerts.some((alert) => alert.severity.toLowerCase() === "critical");
|
|
44678
45049
|
const entry = `\`"${dependency.name}": "${dependency.spec}"\``;
|
|
@@ -44715,7 +45086,7 @@ const buildLowScoreDiagnostic = (dependency, artifact, failingAxis, options) =>
|
|
|
44715
45086
|
const checkSupplyChain = (input) => gen(function* () {
|
|
44716
45087
|
const options = resolveOptions(input.userConfig);
|
|
44717
45088
|
const packageJsonPath = Path.join(input.rootDirectory, "package.json");
|
|
44718
|
-
const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
|
|
45089
|
+
const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies, buildLockedVersionLookup(input.rootDirectory));
|
|
44719
45090
|
if (dependencies.length === 0) return [];
|
|
44720
45091
|
const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
|
|
44721
45092
|
if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
|
|
@@ -44724,7 +45095,7 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
44724
45095
|
for (let index = 0; index < dependencies.length; index += 1) {
|
|
44725
45096
|
const artifact = artifacts[index];
|
|
44726
45097
|
if (!artifact) continue;
|
|
44727
|
-
const worstAxis = worstGatedAxis(artifact.score);
|
|
45098
|
+
const worstAxis = worstGatedAxis(artifact.score, gatedAxesForSection(dependencies[index].section));
|
|
44728
45099
|
if (toHundred(artifact.score[worstAxis.key]) >= options.minScore) continue;
|
|
44729
45100
|
diagnostics.push(buildLowScoreDiagnostic(dependencies[index], artifact, worstAxis, options));
|
|
44730
45101
|
}
|
|
@@ -46007,7 +46378,7 @@ const makeNoopConsole = () => ({
|
|
|
46007
46378
|
});
|
|
46008
46379
|
//#endregion
|
|
46009
46380
|
//#region src/cli/utils/version.ts
|
|
46010
|
-
const VERSION = "0.7.
|
|
46381
|
+
const VERSION = "0.7.2-dev.aa519e5";
|
|
46011
46382
|
//#endregion
|
|
46012
46383
|
//#region src/cli/utils/json-mode.ts
|
|
46013
46384
|
let context = null;
|
|
@@ -46376,13 +46747,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46376
46747
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46377
46748
|
* standard `SENTRY_RELEASE` override.
|
|
46378
46749
|
*/
|
|
46379
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.
|
|
46750
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.aa519e5`;
|
|
46380
46751
|
/**
|
|
46381
46752
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
46382
46753
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
46383
46754
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
46384
46755
|
*/
|
|
46385
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.
|
|
46756
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.aa519e5") ? "development" : "production");
|
|
46386
46757
|
/**
|
|
46387
46758
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
46388
46759
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -58899,13 +59270,15 @@ const buildOutcomeAttributes = (input) => {
|
|
|
58899
59270
|
const countBySource = {
|
|
58900
59271
|
config: 0,
|
|
58901
59272
|
override: 0,
|
|
58902
|
-
inline: 0
|
|
59273
|
+
inline: 0,
|
|
59274
|
+
"foreign-inline": 0
|
|
58903
59275
|
};
|
|
58904
59276
|
for (const suppression of input.suppressedRuleCounts) countBySource[suppression.source] += suppression.count;
|
|
58905
|
-
suppressionRollup.suppressed = countBySource.config + countBySource.override + countBySource.inline;
|
|
59277
|
+
suppressionRollup.suppressed = countBySource.config + countBySource.override + countBySource.inline + countBySource["foreign-inline"];
|
|
58906
59278
|
suppressionRollup.suppressedConfig = countBySource.config;
|
|
58907
59279
|
suppressionRollup.suppressedOverride = countBySource.override;
|
|
58908
59280
|
suppressionRollup.suppressedInline = countBySource.inline;
|
|
59281
|
+
suppressionRollup.suppressedForeignInline = countBySource["foreign-inline"];
|
|
58909
59282
|
}
|
|
58910
59283
|
const attributes = {
|
|
58911
59284
|
...withNamespace("outcome", {
|
|
@@ -64555,4 +64928,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
64555
64928
|
export {};
|
|
64556
64929
|
|
|
64557
64930
|
//# sourceMappingURL=cli.js.map
|
|
64558
|
-
//# debugId=
|
|
64931
|
+
//# debugId=a8ebd975-0f3b-5218-ad38-b9c8095f6668
|