oxlint-plugin-react-doctor 0.6.3-dev.ea9a775 → 0.6.3
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/index.d.ts +1 -81
- package/dist/index.js +11 -246
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { StaticImport } from "oxc-parser";
|
|
2
1
|
import { TSESTree } from "@typescript-eslint/types";
|
|
3
2
|
|
|
4
3
|
//#region src/plugin/utils/es-tree-node.d.ts
|
|
@@ -16645,85 +16644,6 @@ declare const MOTION_LIBRARY_PACKAGES: Set<string>;
|
|
|
16645
16644
|
//#region src/plugin/constants/cross-file-rule-ids.d.ts
|
|
16646
16645
|
declare const CROSS_FILE_RULE_IDS: ReadonlySet<string>;
|
|
16647
16646
|
//#endregion
|
|
16648
|
-
//#region src/plugin/utils/cross-file-probe-recorder.d.ts
|
|
16649
|
-
interface CrossFileProbeTrace {
|
|
16650
|
-
/** Paths whose existence classification (file / dir / none) was consulted. */
|
|
16651
|
-
readonly existencePaths: Set<string>;
|
|
16652
|
-
/** Paths whose content (bytes, exports, parse) was consulted. */
|
|
16653
|
-
readonly contentPaths: Set<string>;
|
|
16654
|
-
}
|
|
16655
|
-
//#endregion
|
|
16656
|
-
//#region src/plugin/cross-file-dependencies.d.ts
|
|
16657
|
-
/**
|
|
16658
|
-
* Per-rule cross-file dependency collectors — the foundation of the sidecar
|
|
16659
|
-
* lint cache's dependency fingerprints (`@react-doctor/core`,
|
|
16660
|
-
* `runners/oxlint/sidecar-lint-cache.ts`).
|
|
16661
|
-
*
|
|
16662
|
-
* A collector re-runs the SAME resolver helpers its rule calls at lint time
|
|
16663
|
-
* (import resolution, barrel following, ancestor-layout walks, package.json
|
|
16664
|
-
* classification) under the probe recorder
|
|
16665
|
-
* (`utils/cross-file-probe-recorder.ts`), so the recorded probe set is the
|
|
16666
|
-
* exact set of filesystem facts the rule's execution consulted — including
|
|
16667
|
-
* the negative probes (extension candidates that did NOT exist) that make
|
|
16668
|
-
* resolution shadowing detectable when a new file appears.
|
|
16669
|
-
*
|
|
16670
|
-
* SOUNDNESS INVARIANT — each collector's probe set must be a SUPERSET of
|
|
16671
|
-
* every filesystem read its rule can make while linting the file, for any
|
|
16672
|
-
* file content and any filesystem state:
|
|
16673
|
-
*
|
|
16674
|
-
* - Over-approximation is always safe (extra probes only cause a spurious
|
|
16675
|
-
* re-lint); under-approximation is forbidden (a missed probe could
|
|
16676
|
-
* replay a stale verdict).
|
|
16677
|
-
* - Where a rule gates its cross-file reads on in-file conditions, a
|
|
16678
|
-
* collector may skip the gate (probe more) or must mirror it EXACTLY
|
|
16679
|
-
* (probe the same). Each collector documents which it does per gate.
|
|
16680
|
-
* - The replay argument: the stored probe set is the complete read set of
|
|
16681
|
-
* the collector's execution at store time. If every stored probe has
|
|
16682
|
-
* the same answer on a later scan, re-executing the collector — and
|
|
16683
|
-
* therefore the rule, whose reads are a subset — reproduces the same
|
|
16684
|
-
* execution step by step (the file's own content is pinned separately
|
|
16685
|
-
* by the cache key's content hash), so the stored diagnostics are
|
|
16686
|
-
* exactly what a fresh sidecar lint would produce.
|
|
16687
|
-
*
|
|
16688
|
-
* A cross-file rule WITHOUT a collector here must be listed in
|
|
16689
|
-
* `UNBOUNDED_CROSS_FILE_RULE_IDS` — it then re-lints every file on every
|
|
16690
|
-
* scan (the sound fallback). `cross-file-rule-ids.test.ts` in
|
|
16691
|
-
* `@react-doctor/core` forces every `CROSS_FILE_RULE_IDS` entry into
|
|
16692
|
-
* exactly one of the two classifications.
|
|
16693
|
-
*/
|
|
16694
|
-
interface CrossFileDependencyCollectorInput {
|
|
16695
|
-
/** Absolute, forward-slash-normalized path of the file being fingerprinted. */
|
|
16696
|
-
readonly absoluteFilePath: string;
|
|
16697
|
-
readonly sourceText: string;
|
|
16698
|
-
/** oxc module record — the file's static import declarations. */
|
|
16699
|
-
readonly staticImports: ReadonlyArray<StaticImport>;
|
|
16700
|
-
/** Parsed program (no parent references attached). */
|
|
16701
|
-
readonly program: EsTreeNode;
|
|
16702
|
-
}
|
|
16703
|
-
type CrossFileDependencyCollector = (input: CrossFileDependencyCollectorInput) => void;
|
|
16704
|
-
declare const CROSS_FILE_DEPENDENCY_COLLECTORS: ReadonlyMap<string, CrossFileDependencyCollector>;
|
|
16705
|
-
/**
|
|
16706
|
-
* Cross-file rules whose dependency set CANNOT be soundly bounded — they are
|
|
16707
|
-
* excluded from fingerprinting and re-lint every file on every scan. Empty
|
|
16708
|
-
* today; a new cross-file rule must be added either here or to
|
|
16709
|
-
* `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
|
|
16710
|
-
* partition), forcing a conscious classification.
|
|
16711
|
-
*/
|
|
16712
|
-
declare const UNBOUNDED_CROSS_FILE_RULE_IDS: ReadonlySet<string>;
|
|
16713
|
-
/**
|
|
16714
|
-
* Runs the collectors for `ruleIds` over one file and returns every
|
|
16715
|
-
* filesystem probe they made — the file's cross-file dependency set.
|
|
16716
|
-
*
|
|
16717
|
-
* Returns `null` (caller must treat the file as unfingerprintable and always
|
|
16718
|
-
* re-lint it) when the file has a fatal parse error — an execution the
|
|
16719
|
-
* collectors cannot mirror — or when a requested rule has no collector.
|
|
16720
|
-
*/
|
|
16721
|
-
declare const collectCrossFileDependencyProbes: (input: {
|
|
16722
|
-
absoluteFilePath: string;
|
|
16723
|
-
sourceText: string;
|
|
16724
|
-
ruleIds: ReadonlyArray<string>;
|
|
16725
|
-
}) => CrossFileProbeTrace | null;
|
|
16726
|
-
//#endregion
|
|
16727
16647
|
//#region src/plugin/rules/security-scan/utils/classify-security-scan-file.d.ts
|
|
16728
16648
|
interface SecurityScanFileClassification {
|
|
16729
16649
|
readonly bucket: "priority" | "artifact" | "other";
|
|
@@ -16737,5 +16657,5 @@ declare const REACT_NATIVE_DEPENDENCY_NAMES: ReadonlySet<string>;
|
|
|
16737
16657
|
declare const REACT_NATIVE_DEPENDENCY_PREFIXES: ReadonlyArray<string>;
|
|
16738
16658
|
declare const isReactNativeDependencyName: (dependencyName: string) => boolean;
|
|
16739
16659
|
//#endregion
|
|
16740
|
-
export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS,
|
|
16660
|
+
export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, EXTERNAL_RULES, type EsTreeNode, FRAMEWORK_SPECIFIC_RULE_KEYS, type FileScan, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, type OxlintRuleSeverity, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, type Rule, type RuleFramework, type RulePlugin, type RuleSeverity, type RuleVisitors, type ScanFinding, type ScannedFile, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, classifySecurityScanFile, plugin as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
|
|
16741
16661
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15564,30 +15564,6 @@ const nextjsInlineScriptMissingId = defineRule({
|
|
|
15564
15564
|
} })
|
|
15565
15565
|
});
|
|
15566
15566
|
//#endregion
|
|
15567
|
-
//#region src/plugin/utils/cross-file-probe-recorder.ts
|
|
15568
|
-
let activeProbeTrace = null;
|
|
15569
|
-
const recordExistenceProbe = (absolutePath) => {
|
|
15570
|
-
activeProbeTrace?.existencePaths.add(absolutePath);
|
|
15571
|
-
};
|
|
15572
|
-
const recordContentProbe = (absolutePath) => {
|
|
15573
|
-
activeProbeTrace?.contentPaths.add(absolutePath);
|
|
15574
|
-
};
|
|
15575
|
-
const isProbeRecorderActive = () => activeProbeTrace !== null;
|
|
15576
|
-
const collectCrossFileProbes = (collect) => {
|
|
15577
|
-
const previousTrace = activeProbeTrace;
|
|
15578
|
-
const trace = {
|
|
15579
|
-
existencePaths: /* @__PURE__ */ new Set(),
|
|
15580
|
-
contentPaths: /* @__PURE__ */ new Set()
|
|
15581
|
-
};
|
|
15582
|
-
activeProbeTrace = trace;
|
|
15583
|
-
try {
|
|
15584
|
-
collect();
|
|
15585
|
-
} finally {
|
|
15586
|
-
activeProbeTrace = previousTrace;
|
|
15587
|
-
}
|
|
15588
|
-
return trace;
|
|
15589
|
-
};
|
|
15590
|
-
//#endregion
|
|
15591
15567
|
//#region src/plugin/utils/parse-export-specifiers.ts
|
|
15592
15568
|
const getSpecifierName = (rawName) => rawName.replace(/^type\s+/, "").trim();
|
|
15593
15569
|
const parseExportSpecifiers = (specifiersText, declarationIsTypeOnly) => specifiersText.split(",").map((specifierText) => specifierText.trim()).filter(Boolean).map((specifierText) => {
|
|
@@ -15623,7 +15599,6 @@ const collectSourceTextExportNames = (sourceText) => {
|
|
|
15623
15599
|
};
|
|
15624
15600
|
const exportNamesCache = /* @__PURE__ */ new Map();
|
|
15625
15601
|
const doesModuleExportName = (filePath, exportedName) => {
|
|
15626
|
-
recordContentProbe(filePath);
|
|
15627
15602
|
try {
|
|
15628
15603
|
const fileStat = fs.statSync(filePath);
|
|
15629
15604
|
const cached = exportNamesCache.get(filePath);
|
|
@@ -16668,7 +16643,6 @@ const parseSourceText = (filename, sourceText) => {
|
|
|
16668
16643
|
};
|
|
16669
16644
|
const parseCache = /* @__PURE__ */ new Map();
|
|
16670
16645
|
const parseSourceFile = (absoluteFilePath) => {
|
|
16671
|
-
if (!(absoluteFilePath.endsWith(".d.ts") || absoluteFilePath.endsWith(".d.mts") || absoluteFilePath.endsWith(".d.cts"))) recordContentProbe(absoluteFilePath);
|
|
16672
16646
|
let fileStat;
|
|
16673
16647
|
try {
|
|
16674
16648
|
fileStat = fs.statSync(absoluteFilePath);
|
|
@@ -16815,27 +16789,15 @@ const classifyBarrelModule = (sourceText) => {
|
|
|
16815
16789
|
};
|
|
16816
16790
|
const getBarrelIndexModuleInfo = (filePath) => {
|
|
16817
16791
|
if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
|
|
16818
|
-
recordContentProbe(filePath);
|
|
16819
|
-
let fileStat;
|
|
16820
|
-
try {
|
|
16821
|
-
fileStat = fs.statSync(filePath);
|
|
16822
|
-
} catch {
|
|
16823
|
-
fileStat = null;
|
|
16824
|
-
}
|
|
16825
16792
|
const cachedResult = barrelIndexModuleInfoCache.get(filePath);
|
|
16826
|
-
if (cachedResult !== void 0
|
|
16827
|
-
if (fileStat === null) return createNonBarrelInfo();
|
|
16793
|
+
if (cachedResult !== void 0) return cachedResult;
|
|
16828
16794
|
let moduleInfo = createNonBarrelInfo();
|
|
16829
16795
|
try {
|
|
16830
16796
|
moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
|
|
16831
16797
|
} catch {
|
|
16832
16798
|
moduleInfo = createNonBarrelInfo();
|
|
16833
16799
|
}
|
|
16834
|
-
barrelIndexModuleInfoCache.set(filePath,
|
|
16835
|
-
mtimeMs: fileStat.mtimeMs,
|
|
16836
|
-
size: fileStat.size,
|
|
16837
|
-
moduleInfo
|
|
16838
|
-
});
|
|
16800
|
+
barrelIndexModuleInfoCache.set(filePath, moduleInfo);
|
|
16839
16801
|
return moduleInfo;
|
|
16840
16802
|
};
|
|
16841
16803
|
const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
|
|
@@ -16864,7 +16826,6 @@ const PACKAGE_ENTRY_FIELDS = [
|
|
|
16864
16826
|
"browser"
|
|
16865
16827
|
];
|
|
16866
16828
|
const getExistingFilePath = (filePath) => {
|
|
16867
|
-
recordExistenceProbe(filePath);
|
|
16868
16829
|
try {
|
|
16869
16830
|
return fs.statSync(filePath).isFile() ? filePath : null;
|
|
16870
16831
|
} catch {
|
|
@@ -16872,7 +16833,6 @@ const getExistingFilePath = (filePath) => {
|
|
|
16872
16833
|
}
|
|
16873
16834
|
};
|
|
16874
16835
|
const getExistingDirectoryPath = (directoryPath) => {
|
|
16875
|
-
recordExistenceProbe(directoryPath);
|
|
16876
16836
|
try {
|
|
16877
16837
|
return fs.statSync(directoryPath).isDirectory() ? directoryPath : null;
|
|
16878
16838
|
} catch {
|
|
@@ -16928,7 +16888,6 @@ const resolvePackageDirectoryEntry = (directoryPath) => {
|
|
|
16928
16888
|
const existingDirectoryPath = getExistingDirectoryPath(directoryPath);
|
|
16929
16889
|
if (!existingDirectoryPath) return null;
|
|
16930
16890
|
const packageJsonPath = path.join(existingDirectoryPath, "package.json");
|
|
16931
|
-
recordContentProbe(packageJsonPath);
|
|
16932
16891
|
try {
|
|
16933
16892
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
16934
16893
|
const packageEntry = getPackageExportEntry(packageJson) ?? PACKAGE_ENTRY_FIELDS.map((fieldName) => packageJson[fieldName]).find((value) => typeof value === "string");
|
|
@@ -17037,9 +16996,7 @@ const stripJsonComments = (text) => {
|
|
|
17037
16996
|
}
|
|
17038
16997
|
return output.replace(/,(\s*[}\]])/g, "$1");
|
|
17039
16998
|
};
|
|
17040
|
-
const parseTsconfigFile = (configFilePath
|
|
17041
|
-
recordContentProbe(configFilePath);
|
|
17042
|
-
probedPaths.push(configFilePath);
|
|
16999
|
+
const parseTsconfigFile = (configFilePath) => {
|
|
17043
17000
|
let sourceText;
|
|
17044
17001
|
try {
|
|
17045
17002
|
sourceText = fs.readFileSync(configFilePath, "utf8");
|
|
@@ -17068,8 +17025,8 @@ const parsePathsField = (pathsField) => {
|
|
|
17068
17025
|
}
|
|
17069
17026
|
return paths;
|
|
17070
17027
|
};
|
|
17071
|
-
const readResolvedTsconfig = (configFilePath, extendsDepth
|
|
17072
|
-
const parsed = parseTsconfigFile(configFilePath
|
|
17028
|
+
const readResolvedTsconfig = (configFilePath, extendsDepth) => {
|
|
17029
|
+
const parsed = parseTsconfigFile(configFilePath);
|
|
17073
17030
|
if (!parsed) return null;
|
|
17074
17031
|
const configDirectory = path.dirname(configFilePath);
|
|
17075
17032
|
const compilerOptions = isObjectRecord(parsed.compilerOptions) ? parsed.compilerOptions : {};
|
|
@@ -17083,7 +17040,7 @@ const readResolvedTsconfig = (configFilePath, extendsDepth, probedPaths) => {
|
|
|
17083
17040
|
};
|
|
17084
17041
|
if (typeof parsed.extends === "string" && extendsDepth < 8) {
|
|
17085
17042
|
const parentPath = resolveExtendsPath(parsed.extends, configDirectory);
|
|
17086
|
-
const inherited = parentPath ? readResolvedTsconfig(parentPath, extendsDepth + 1
|
|
17043
|
+
const inherited = parentPath ? readResolvedTsconfig(parentPath, extendsDepth + 1) : null;
|
|
17087
17044
|
if (inherited) return inherited;
|
|
17088
17045
|
}
|
|
17089
17046
|
return hasExplicitBaseUrl ? {
|
|
@@ -17094,7 +17051,6 @@ const readResolvedTsconfig = (configFilePath, extendsDepth, probedPaths) => {
|
|
|
17094
17051
|
};
|
|
17095
17052
|
const configByFilePath = /* @__PURE__ */ new Map();
|
|
17096
17053
|
const loadTsconfigCached = (configFilePath) => {
|
|
17097
|
-
recordContentProbe(configFilePath);
|
|
17098
17054
|
let fileStat;
|
|
17099
17055
|
try {
|
|
17100
17056
|
fileStat = fs.statSync(configFilePath);
|
|
@@ -17102,16 +17058,11 @@ const loadTsconfigCached = (configFilePath) => {
|
|
|
17102
17058
|
return null;
|
|
17103
17059
|
}
|
|
17104
17060
|
const cached = configByFilePath.get(configFilePath);
|
|
17105
|
-
if (cached && cached.mtimeMs === fileStat.mtimeMs)
|
|
17106
|
-
|
|
17107
|
-
return cached.config;
|
|
17108
|
-
}
|
|
17109
|
-
const probedChainPaths = [];
|
|
17110
|
-
const config = readResolvedTsconfig(configFilePath, 0, probedChainPaths);
|
|
17061
|
+
if (cached && cached.mtimeMs === fileStat.mtimeMs) return cached.config;
|
|
17062
|
+
const config = readResolvedTsconfig(configFilePath, 0);
|
|
17111
17063
|
configByFilePath.set(configFilePath, {
|
|
17112
17064
|
mtimeMs: fileStat.mtimeMs,
|
|
17113
|
-
config
|
|
17114
|
-
probedChainPaths
|
|
17065
|
+
config
|
|
17115
17066
|
});
|
|
17116
17067
|
return config;
|
|
17117
17068
|
};
|
|
@@ -18958,23 +18909,10 @@ const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
|
|
|
18958
18909
|
const findNearestPackageDirectory = (filename) => {
|
|
18959
18910
|
if (!filename) return null;
|
|
18960
18911
|
const fromCache = cachedPackageDirectoryByFilename.get(filename);
|
|
18961
|
-
if (fromCache !== void 0)
|
|
18962
|
-
if (isProbeRecorderActive()) {
|
|
18963
|
-
let probedDirectory = path.dirname(filename);
|
|
18964
|
-
while (true) {
|
|
18965
|
-
recordExistenceProbe(path.join(probedDirectory, "package.json"));
|
|
18966
|
-
if (probedDirectory === fromCache) break;
|
|
18967
|
-
const parentDirectory = path.dirname(probedDirectory);
|
|
18968
|
-
if (parentDirectory === probedDirectory) break;
|
|
18969
|
-
probedDirectory = parentDirectory;
|
|
18970
|
-
}
|
|
18971
|
-
}
|
|
18972
|
-
return fromCache;
|
|
18973
|
-
}
|
|
18912
|
+
if (fromCache !== void 0) return fromCache;
|
|
18974
18913
|
let currentDirectory = path.dirname(filename);
|
|
18975
18914
|
while (true) {
|
|
18976
18915
|
const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
|
|
18977
|
-
recordExistenceProbe(candidatePackageJsonPath);
|
|
18978
18916
|
let hasPackageJson = false;
|
|
18979
18917
|
try {
|
|
18980
18918
|
hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
|
|
@@ -19037,7 +18975,6 @@ const isWebFrameworkOnly = (packageJson) => {
|
|
|
19037
18975
|
const classifyPackagePlatform = (filename) => {
|
|
19038
18976
|
const packageDirectory = findNearestPackageDirectory(filename);
|
|
19039
18977
|
if (!packageDirectory) return "unknown";
|
|
19040
|
-
recordContentProbe(path.join(packageDirectory, "package.json"));
|
|
19041
18978
|
const cached = cachedPlatformByPackageDirectory.get(packageDirectory);
|
|
19042
18979
|
if (cached !== void 0) return cached;
|
|
19043
18980
|
const packageJson = readPackageJsonSafe(path.join(packageDirectory, "package.json"));
|
|
@@ -47549,178 +47486,6 @@ const CROSS_FILE_RULE_IDS = new Set([
|
|
|
47549
47486
|
"rn-prefer-expo-image"
|
|
47550
47487
|
]);
|
|
47551
47488
|
//#endregion
|
|
47552
|
-
//#region src/plugin/cross-file-dependencies.ts
|
|
47553
|
-
const flattenImportEntries = (staticImports) => {
|
|
47554
|
-
const flattened = [];
|
|
47555
|
-
for (const staticImport of staticImports) for (const entry of staticImport.entries) {
|
|
47556
|
-
if (entry.importName.kind === "NamespaceObject") continue;
|
|
47557
|
-
const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name ?? null;
|
|
47558
|
-
if (exportedName === null) continue;
|
|
47559
|
-
flattened.push({
|
|
47560
|
-
source: staticImport.moduleRequest.value,
|
|
47561
|
-
exportedName
|
|
47562
|
-
});
|
|
47563
|
-
}
|
|
47564
|
-
return flattened;
|
|
47565
|
-
};
|
|
47566
|
-
const collectNoBarrelImportDependencies = ({ absoluteFilePath, staticImports }) => {
|
|
47567
|
-
let didProbePackagePlatform = false;
|
|
47568
|
-
for (const staticImport of staticImports) {
|
|
47569
|
-
const source = staticImport.moduleRequest.value;
|
|
47570
|
-
if (!source.startsWith(".")) continue;
|
|
47571
|
-
const runtimeEntries = staticImport.entries.filter((entry) => !entry.isType);
|
|
47572
|
-
if (runtimeEntries.length === 0) continue;
|
|
47573
|
-
const resolvedImportPath = resolveRelativeImportPath(absoluteFilePath, source);
|
|
47574
|
-
if (!resolvedImportPath || !isBarrelIndexModule(resolvedImportPath)) continue;
|
|
47575
|
-
if (!didProbePackagePlatform) {
|
|
47576
|
-
didProbePackagePlatform = true;
|
|
47577
|
-
classifyPackagePlatform(absoluteFilePath);
|
|
47578
|
-
}
|
|
47579
|
-
for (const entry of runtimeEntries) {
|
|
47580
|
-
const importedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
|
|
47581
|
-
if (importedName) resolveBarrelExportFilePath(resolvedImportPath, importedName);
|
|
47582
|
-
}
|
|
47583
|
-
}
|
|
47584
|
-
};
|
|
47585
|
-
const collectNextjsMissingMetadataDependencies = ({ absoluteFilePath }) => {
|
|
47586
|
-
if (!PAGE_FILE_PATTERN.test(absoluteFilePath)) return;
|
|
47587
|
-
if (INTERNAL_PAGE_PATH_PATTERN.test(absoluteFilePath)) return;
|
|
47588
|
-
hasAncestorMetadataLayout(absoluteFilePath);
|
|
47589
|
-
};
|
|
47590
|
-
const collectNextjsSearchParamsDependencies = ({ absoluteFilePath, staticImports }) => {
|
|
47591
|
-
if (!PAGE_OR_LAYOUT_FILE_PATTERN.test(absoluteFilePath)) return;
|
|
47592
|
-
if (hasAncestorSuspenseLayout(absoluteFilePath)) return;
|
|
47593
|
-
for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
|
|
47594
|
-
};
|
|
47595
|
-
const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
|
|
47596
|
-
const namedUseReducerLocals = /* @__PURE__ */ new Set();
|
|
47597
|
-
const reactObjectLocals = /* @__PURE__ */ new Set();
|
|
47598
|
-
for (const staticImport of staticImports) {
|
|
47599
|
-
if (staticImport.moduleRequest.value !== "react") continue;
|
|
47600
|
-
for (const entry of staticImport.entries) if (entry.importName.kind === "Name" && entry.importName.name === "useReducer") namedUseReducerLocals.add(entry.localName.value);
|
|
47601
|
-
else if (entry.importName.kind === "Default" || entry.importName.kind === "NamespaceObject") reactObjectLocals.add(entry.localName.value);
|
|
47602
|
-
}
|
|
47603
|
-
if (!(namedUseReducerLocals.size > 0 || reactObjectLocals.size > 0 && (sourceText.includes("useReducer") || sourceText.includes("\\u")))) return;
|
|
47604
|
-
const identifierName = (node) => {
|
|
47605
|
-
if (node?.type !== "Identifier") return null;
|
|
47606
|
-
const { name } = node;
|
|
47607
|
-
return typeof name === "string" ? name : null;
|
|
47608
|
-
};
|
|
47609
|
-
const isUseReducerCallee = (callee) => {
|
|
47610
|
-
const calleeName = identifierName(callee);
|
|
47611
|
-
if (calleeName !== null) return namedUseReducerLocals.has(calleeName);
|
|
47612
|
-
if (callee.type !== "MemberExpression") return false;
|
|
47613
|
-
const member = callee;
|
|
47614
|
-
const objectName = identifierName(member.object);
|
|
47615
|
-
return objectName !== null && reactObjectLocals.has(objectName) && identifierName(member.property) === "useReducer";
|
|
47616
|
-
};
|
|
47617
|
-
const reducerArgumentNames = /* @__PURE__ */ new Set();
|
|
47618
|
-
walkAst(program, (node) => {
|
|
47619
|
-
if (node.type !== "CallExpression") return;
|
|
47620
|
-
const call = node;
|
|
47621
|
-
if (!call.callee || !isUseReducerCallee(call.callee)) return;
|
|
47622
|
-
const reducerArgument = call.arguments?.[0];
|
|
47623
|
-
if (!reducerArgument) return;
|
|
47624
|
-
const argumentName = identifierName(stripParenExpression(reducerArgument));
|
|
47625
|
-
if (argumentName !== null) reducerArgumentNames.add(argumentName);
|
|
47626
|
-
});
|
|
47627
|
-
if (reducerArgumentNames.size === 0) return;
|
|
47628
|
-
for (const staticImport of staticImports) for (const entry of staticImport.entries) {
|
|
47629
|
-
if (entry.importName.kind === "NamespaceObject") continue;
|
|
47630
|
-
if (!reducerArgumentNames.has(entry.localName.value)) continue;
|
|
47631
|
-
const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
|
|
47632
|
-
if (exportedName) resolveCrossFileFunctionExport(absoluteFilePath, staticImport.moduleRequest.value, exportedName);
|
|
47633
|
-
}
|
|
47634
|
-
};
|
|
47635
|
-
const collectJsxBoundaryNames = (program) => {
|
|
47636
|
-
const jsxNames = /* @__PURE__ */ new Set();
|
|
47637
|
-
walkAst(program, (node) => {
|
|
47638
|
-
if (node.type !== "JSXOpeningElement") return;
|
|
47639
|
-
const nameNode = node.name;
|
|
47640
|
-
if (!nameNode) return;
|
|
47641
|
-
if (nameNode.type === "JSXIdentifier") {
|
|
47642
|
-
const name = nameNode.name;
|
|
47643
|
-
if (typeof name === "string") jsxNames.add(name);
|
|
47644
|
-
} else if (nameNode.type === "JSXMemberExpression") {
|
|
47645
|
-
const property = nameNode.property;
|
|
47646
|
-
if (property?.type === "JSXIdentifier" && typeof property.name === "string") jsxNames.add(property.name);
|
|
47647
|
-
} else if (nameNode.type === "JSXNamespacedName") {
|
|
47648
|
-
const namespace = nameNode.namespace;
|
|
47649
|
-
if (typeof namespace?.name === "string") jsxNames.add(namespace.name);
|
|
47650
|
-
}
|
|
47651
|
-
});
|
|
47652
|
-
return jsxNames;
|
|
47653
|
-
};
|
|
47654
|
-
const collectRnNoRawTextDependencies = ({ absoluteFilePath, staticImports, program }) => {
|
|
47655
|
-
classifyPackagePlatform(absoluteFilePath);
|
|
47656
|
-
const jsxNames = collectJsxBoundaryNames(program);
|
|
47657
|
-
if (jsxNames.size === 0) return;
|
|
47658
|
-
for (const staticImport of staticImports) for (const entry of staticImport.entries) {
|
|
47659
|
-
if (entry.importName.kind === "NamespaceObject") continue;
|
|
47660
|
-
if (!jsxNames.has(entry.localName.value)) continue;
|
|
47661
|
-
const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
|
|
47662
|
-
if (exportedName) resolveCrossFileFunctionExport(absoluteFilePath, staticImport.moduleRequest.value, exportedName);
|
|
47663
|
-
}
|
|
47664
|
-
};
|
|
47665
|
-
const collectRnPreferExpoImageDependencies = ({ absoluteFilePath }) => {
|
|
47666
|
-
classifyPackagePlatform(absoluteFilePath);
|
|
47667
|
-
};
|
|
47668
|
-
const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
|
|
47669
|
-
["no-barrel-import", collectNoBarrelImportDependencies],
|
|
47670
|
-
["nextjs-missing-metadata", collectNextjsMissingMetadataDependencies],
|
|
47671
|
-
["nextjs-no-use-search-params-without-suspense", collectNextjsSearchParamsDependencies],
|
|
47672
|
-
["no-mutating-reducer-state", collectMutatingReducerDependencies],
|
|
47673
|
-
["rn-no-raw-text", collectRnNoRawTextDependencies],
|
|
47674
|
-
["rn-prefer-expo-image", collectRnPreferExpoImageDependencies]
|
|
47675
|
-
]);
|
|
47676
|
-
/**
|
|
47677
|
-
* Cross-file rules whose dependency set CANNOT be soundly bounded — they are
|
|
47678
|
-
* excluded from fingerprinting and re-lint every file on every scan. Empty
|
|
47679
|
-
* today; a new cross-file rule must be added either here or to
|
|
47680
|
-
* `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
|
|
47681
|
-
* partition), forcing a conscious classification.
|
|
47682
|
-
*/
|
|
47683
|
-
const UNBOUNDED_CROSS_FILE_RULE_IDS = /* @__PURE__ */ new Set();
|
|
47684
|
-
/**
|
|
47685
|
-
* Runs the collectors for `ruleIds` over one file and returns every
|
|
47686
|
-
* filesystem probe they made — the file's cross-file dependency set.
|
|
47687
|
-
*
|
|
47688
|
-
* Returns `null` (caller must treat the file as unfingerprintable and always
|
|
47689
|
-
* re-lint it) when the file has a fatal parse error — an execution the
|
|
47690
|
-
* collectors cannot mirror — or when a requested rule has no collector.
|
|
47691
|
-
*/
|
|
47692
|
-
const collectCrossFileDependencyProbes = (input) => {
|
|
47693
|
-
const collectors = [];
|
|
47694
|
-
for (const ruleId of input.ruleIds) {
|
|
47695
|
-
const collector = CROSS_FILE_DEPENDENCY_COLLECTORS.get(ruleId);
|
|
47696
|
-
if (!collector) return null;
|
|
47697
|
-
collectors.push(collector);
|
|
47698
|
-
}
|
|
47699
|
-
const absoluteFilePath = normalizeFilename(input.absoluteFilePath);
|
|
47700
|
-
let staticImports;
|
|
47701
|
-
let program;
|
|
47702
|
-
try {
|
|
47703
|
-
const parseResult = parseSync(absoluteFilePath, input.sourceText, {
|
|
47704
|
-
astType: "ts",
|
|
47705
|
-
lang: resolveLang(absoluteFilePath)
|
|
47706
|
-
});
|
|
47707
|
-
if (parseResult.errors.some((parseError) => parseError.severity === "Error")) return null;
|
|
47708
|
-
staticImports = parseResult.module.staticImports;
|
|
47709
|
-
program = parseResult.program;
|
|
47710
|
-
} catch {
|
|
47711
|
-
return null;
|
|
47712
|
-
}
|
|
47713
|
-
const collectorInput = {
|
|
47714
|
-
absoluteFilePath,
|
|
47715
|
-
sourceText: input.sourceText,
|
|
47716
|
-
staticImports,
|
|
47717
|
-
program
|
|
47718
|
-
};
|
|
47719
|
-
return collectCrossFileProbes(() => {
|
|
47720
|
-
for (const collector of collectors) collector(collectorInput);
|
|
47721
|
-
});
|
|
47722
|
-
};
|
|
47723
|
-
//#endregion
|
|
47724
47489
|
//#region src/plugin/rules/security-scan/utils/is-probably-text-file.ts
|
|
47725
47490
|
const isProbablyTextFile = (relativePath) => TEXT_FILE_PATTERN.test(relativePath) || DOTENV_FILE_PATTERN.test(relativePath);
|
|
47726
47491
|
//#endregion
|
|
@@ -47746,6 +47511,6 @@ const shouldReadSecurityScanContent = (relativePath, isGeneratedBundle) => isGen
|
|
|
47746
47511
|
//#region src/index.ts
|
|
47747
47512
|
var src_default = plugin;
|
|
47748
47513
|
//#endregion
|
|
47749
|
-
export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS,
|
|
47514
|
+
export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, EXTERNAL_RULES, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, classifySecurityScanFile, src_default as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
|
|
47750
47515
|
|
|
47751
47516
|
//# sourceMappingURL=index.js.map
|