react-doctor 0.8.1-dev.d970055 → 0.8.1-dev.f4aa821
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 +84 -24
- package/dist/index.d.ts +12 -0
- package/dist/index.js +81 -21
- package/dist/lsp.js +81 -21
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1b59ca76-9135-54e6-9391-ce7fec27b699")}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";
|
|
@@ -37335,7 +37335,7 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
|
37335
37335
|
if (nextMajor === null) return false;
|
|
37336
37336
|
return nextMajor < currentMajor;
|
|
37337
37337
|
};
|
|
37338
|
-
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
37338
|
+
const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
|
|
37339
37339
|
if (facts.expo.version === null) {
|
|
37340
37340
|
const spec = getDependencySpec(packageJson, "expo");
|
|
37341
37341
|
if (spec !== null) facts.expo = {
|
|
@@ -37361,9 +37361,31 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
|
37361
37361
|
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
37362
37362
|
if (spec !== null) facts.reanimatedVersion = spec;
|
|
37363
37363
|
}
|
|
37364
|
+
if (facts.valtioVersion === null) {
|
|
37365
|
+
const spec = getDependencySpec(packageJson, "valtio");
|
|
37366
|
+
if (spec !== null) facts.valtioVersion = spec;
|
|
37367
|
+
}
|
|
37364
37368
|
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
37365
37369
|
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
37366
37370
|
facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
|
|
37371
|
+
const remotionSpec = getDependencySpec(packageJson, "remotion");
|
|
37372
|
+
if (remotionSpec !== null) {
|
|
37373
|
+
facts.hasRemotionDependency = true;
|
|
37374
|
+
const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
|
|
37375
|
+
rootDirectory,
|
|
37376
|
+
rootPackageJson,
|
|
37377
|
+
packageName: "remotion",
|
|
37378
|
+
version: remotionSpec
|
|
37379
|
+
});
|
|
37380
|
+
const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
|
|
37381
|
+
if (remotionMajorVersion === null) {
|
|
37382
|
+
facts.hasUnknownRemotionVersion = true;
|
|
37383
|
+
facts.remotionVersion = null;
|
|
37384
|
+
} else if (!facts.hasUnknownRemotionVersion) {
|
|
37385
|
+
const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
|
|
37386
|
+
if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
|
|
37387
|
+
}
|
|
37388
|
+
}
|
|
37367
37389
|
};
|
|
37368
37390
|
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
37369
37391
|
const facts = {
|
|
@@ -37383,12 +37405,16 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37383
37405
|
version: null,
|
|
37384
37406
|
sourceDirectory: null
|
|
37385
37407
|
},
|
|
37408
|
+
valtioVersion: null,
|
|
37386
37409
|
hasReactNativeAwarePackage: false,
|
|
37387
37410
|
hasReanimatedAwarePackage: false,
|
|
37388
37411
|
hasSsrDependency: false,
|
|
37412
|
+
hasRemotionDependency: false,
|
|
37413
|
+
hasUnknownRemotionVersion: false,
|
|
37414
|
+
remotionVersion: null,
|
|
37389
37415
|
reanimatedVersion: null
|
|
37390
37416
|
};
|
|
37391
|
-
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
37417
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
|
|
37392
37418
|
let isReactGroupSettled = !collectReactGroup;
|
|
37393
37419
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
37394
37420
|
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
@@ -37397,7 +37423,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
37397
37423
|
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
37398
37424
|
visitedDirectories.add(workspaceDirectory);
|
|
37399
37425
|
const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
|
|
37400
|
-
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
37426
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
|
|
37401
37427
|
const info = extractDependencyInfo(workspacePackageJson);
|
|
37402
37428
|
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
37403
37429
|
if (isReactGroupSettled) continue;
|
|
@@ -37623,6 +37649,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37623
37649
|
hasReactCompiler: false,
|
|
37624
37650
|
hasReactCompilerLintPlugin: false,
|
|
37625
37651
|
hasTanStackQuery: false,
|
|
37652
|
+
valtioVersion: null,
|
|
37653
|
+
valtioMajorVersion: null,
|
|
37654
|
+
hasRemotion: false,
|
|
37655
|
+
remotionVersion: null,
|
|
37656
|
+
remotionMajorVersion: null,
|
|
37626
37657
|
hasSsrDependency: false,
|
|
37627
37658
|
preactVersion: null,
|
|
37628
37659
|
preactMajorVersion: null,
|
|
@@ -37729,7 +37760,14 @@ const discoverProject = (directory) => {
|
|
|
37729
37760
|
packageName: "next",
|
|
37730
37761
|
version: workspaceFacts.next.version
|
|
37731
37762
|
}) : null;
|
|
37763
|
+
const valtioVersion = resolveCatalogBackedDependencyVersion({
|
|
37764
|
+
rootDirectory: directory,
|
|
37765
|
+
rootPackageJson: packageJson,
|
|
37766
|
+
packageName: "valtio",
|
|
37767
|
+
version: workspaceFacts.valtioVersion
|
|
37768
|
+
});
|
|
37732
37769
|
const preactVersion = getPreactVersion(packageJson);
|
|
37770
|
+
const remotionVersion = workspaceFacts.remotionVersion;
|
|
37733
37771
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
37734
37772
|
const projectInfo = {
|
|
37735
37773
|
rootDirectory: directory,
|
|
@@ -37744,6 +37782,11 @@ const discoverProject = (directory) => {
|
|
|
37744
37782
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
37745
37783
|
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
37746
37784
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
37785
|
+
valtioVersion,
|
|
37786
|
+
valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
|
|
37787
|
+
hasRemotion: workspaceFacts.hasRemotionDependency,
|
|
37788
|
+
remotionVersion,
|
|
37789
|
+
remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
|
|
37747
37790
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
37748
37791
|
preactVersion,
|
|
37749
37792
|
preactMajorVersion: parseReactMajor(preactVersion),
|
|
@@ -37977,7 +38020,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
37977
38020
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
37978
38021
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
37979
38022
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37980
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
37981
38023
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
37982
38024
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
37983
38025
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -40299,9 +40341,25 @@ const BABEL_CONFIG_FILE_NAMES = [
|
|
|
40299
40341
|
".babelrc.json"
|
|
40300
40342
|
];
|
|
40301
40343
|
const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
|
|
40344
|
+
const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
|
|
40345
|
+
const REACT_NATIVE_PACKAGE = "react-native";
|
|
40346
|
+
const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
|
|
40347
|
+
const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
|
|
40302
40348
|
const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
|
|
40303
40349
|
const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
|
|
40350
|
+
const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
|
|
40351
|
+
const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
|
|
40352
|
+
const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson$1(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
|
|
40353
|
+
if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
|
|
40354
|
+
const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
|
|
40355
|
+
if (normalizedVersionRange === null) return false;
|
|
40356
|
+
const minimumVersion = import_semver.minVersion(normalizedVersionRange);
|
|
40357
|
+
return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
|
|
40358
|
+
};
|
|
40359
|
+
const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
|
|
40304
40360
|
const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
40361
|
+
const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
|
|
40362
|
+
const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
|
|
40305
40363
|
for (const fileName of BABEL_CONFIG_FILE_NAMES) {
|
|
40306
40364
|
const filePath = Path.join(rootDirectory, fileName);
|
|
40307
40365
|
if (!isFile(filePath)) continue;
|
|
@@ -40311,7 +40369,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
40311
40369
|
} catch {
|
|
40312
40370
|
continue;
|
|
40313
40371
|
}
|
|
40314
|
-
if (
|
|
40372
|
+
if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
|
|
40315
40373
|
filePath: fileName,
|
|
40316
40374
|
rule: "rn-no-metro-babel-preset",
|
|
40317
40375
|
severity: "error",
|
|
@@ -40334,12 +40392,6 @@ const checkReactNativeProject = (rootDirectory, project) => {
|
|
|
40334
40392
|
return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
|
|
40335
40393
|
};
|
|
40336
40394
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
40337
|
-
const DEPENDENCY_SECTIONS = [
|
|
40338
|
-
"dependencies",
|
|
40339
|
-
"devDependencies",
|
|
40340
|
-
"peerDependencies",
|
|
40341
|
-
"optionalDependencies"
|
|
40342
|
-
];
|
|
40343
40395
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
40344
40396
|
"19.0": {
|
|
40345
40397
|
rceFixedVersion: "19.0.1",
|
|
@@ -40386,20 +40438,13 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
|
|
|
40386
40438
|
for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
|
|
40387
40439
|
return [...directories];
|
|
40388
40440
|
};
|
|
40389
|
-
const readDeclaredSpec = (packageJson, packageName) => {
|
|
40390
|
-
for (const section of DEPENDENCY_SECTIONS) {
|
|
40391
|
-
const spec = packageJson[section]?.[packageName];
|
|
40392
|
-
if (typeof spec === "string") return spec;
|
|
40393
|
-
}
|
|
40394
|
-
return null;
|
|
40395
|
-
};
|
|
40396
40441
|
const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
|
|
40397
40442
|
const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
|
|
40398
40443
|
if (isFile(manifestPath)) {
|
|
40399
40444
|
const installedVersion = import_semver.valid(readPackageJson$1(manifestPath).version ?? null);
|
|
40400
40445
|
if (installedVersion !== null) return installedVersion;
|
|
40401
40446
|
}
|
|
40402
|
-
const candidateSpecs = [
|
|
40447
|
+
const candidateSpecs = [getDependencySpec(readPackageJson$1(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
|
|
40403
40448
|
for (const spec of candidateSpecs) {
|
|
40404
40449
|
const pinnedVersion = spec === null ? null : import_semver.valid(spec);
|
|
40405
40450
|
if (pinnedVersion !== null) return pinnedVersion;
|
|
@@ -41082,6 +41127,12 @@ const buildCapabilities = (project) => {
|
|
|
41082
41127
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
41083
41128
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
41084
41129
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
41130
|
+
if (project.valtioVersion !== null) capabilities.add("valtio");
|
|
41131
|
+
addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
|
|
41132
|
+
if (project.hasRemotion) {
|
|
41133
|
+
capabilities.add("remotion");
|
|
41134
|
+
addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
|
|
41135
|
+
}
|
|
41085
41136
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
41086
41137
|
if (project.preactVersion !== null) capabilities.add("preact");
|
|
41087
41138
|
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
@@ -44091,12 +44142,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
44091
44142
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
44092
44143
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
44093
44144
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
44145
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
44094
44146
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
44095
44147
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
44096
44148
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44097
44149
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
44098
44150
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
44099
44151
|
};
|
|
44152
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
44153
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
44154
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
44155
|
+
};
|
|
44100
44156
|
const PLUGIN_CATEGORY_MAP = {
|
|
44101
44157
|
react: "Bugs",
|
|
44102
44158
|
"react-hooks": "Bugs",
|
|
@@ -44154,6 +44210,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
44154
44210
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
44155
44211
|
help: reasonDetail || help
|
|
44156
44212
|
};
|
|
44213
|
+
if (rule === "set-state-in-effect") return {
|
|
44214
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
44215
|
+
help: reasonDetail || help
|
|
44216
|
+
};
|
|
44157
44217
|
return {
|
|
44158
44218
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
44159
44219
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -47356,7 +47416,7 @@ const makeNoopConsole = () => ({
|
|
|
47356
47416
|
});
|
|
47357
47417
|
//#endregion
|
|
47358
47418
|
//#region src/cli/utils/version.ts
|
|
47359
|
-
const VERSION = "0.8.1-dev.
|
|
47419
|
+
const VERSION = "0.8.1-dev.f4aa821";
|
|
47360
47420
|
//#endregion
|
|
47361
47421
|
//#region src/cli/utils/json-mode.ts
|
|
47362
47422
|
let context = null;
|
|
@@ -47708,13 +47768,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47708
47768
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47709
47769
|
* standard `SENTRY_RELEASE` override.
|
|
47710
47770
|
*/
|
|
47711
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.
|
|
47771
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.f4aa821`;
|
|
47712
47772
|
/**
|
|
47713
47773
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47714
47774
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47715
47775
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47716
47776
|
*/
|
|
47717
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.
|
|
47777
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.f4aa821") ? "development" : "production");
|
|
47718
47778
|
/**
|
|
47719
47779
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47720
47780
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -66394,4 +66454,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
66394
66454
|
export {};
|
|
66395
66455
|
|
|
66396
66456
|
//# sourceMappingURL=cli.js.map
|
|
66397
|
-
//# debugId=
|
|
66457
|
+
//# debugId=1b59ca76-9135-54e6-9391-ce7fec27b699
|
package/dist/index.d.ts
CHANGED
|
@@ -9548,6 +9548,18 @@ interface ProjectInfo {
|
|
|
9548
9548
|
hasReactCompiler: boolean;
|
|
9549
9549
|
hasReactCompilerLintPlugin?: boolean;
|
|
9550
9550
|
hasTanStackQuery: boolean;
|
|
9551
|
+
hasRemotion?: boolean;
|
|
9552
|
+
remotionVersion?: string | null;
|
|
9553
|
+
remotionMajorVersion?: number | null;
|
|
9554
|
+
/**
|
|
9555
|
+
* The declared `valtio` version spec, or `null` when no package in the
|
|
9556
|
+
* analyzed project declares Valtio. `useSnapshot` has kept the same
|
|
9557
|
+
* render-read contract across Valtio 1 and 2, so presence alone drives the
|
|
9558
|
+
* `valtio` capability and its parsed major-version ladder.
|
|
9559
|
+
*/
|
|
9560
|
+
valtioVersion: string | null;
|
|
9561
|
+
/** Parsed major from `valtioVersion`, or `null` when absent/unparseable. */
|
|
9562
|
+
valtioMajorVersion: number | null;
|
|
9551
9563
|
/**
|
|
9552
9564
|
* `true` when the project or a workspace declares a Vite-based SSR runtime
|
|
9553
9565
|
* such as React Router's Node adapter, Vike, or vite-plugin-ssr.
|
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]="92325f1f-c593-5b4c-af5a-c8b7177212e1")}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 reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -34139,7 +34139,7 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
|
34139
34139
|
if (nextMajor === null) return false;
|
|
34140
34140
|
return nextMajor < currentMajor;
|
|
34141
34141
|
};
|
|
34142
|
-
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
34142
|
+
const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
|
|
34143
34143
|
if (facts.expo.version === null) {
|
|
34144
34144
|
const spec = getDependencySpec(packageJson, "expo");
|
|
34145
34145
|
if (spec !== null) facts.expo = {
|
|
@@ -34165,9 +34165,31 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
|
34165
34165
|
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
34166
34166
|
if (spec !== null) facts.reanimatedVersion = spec;
|
|
34167
34167
|
}
|
|
34168
|
+
if (facts.valtioVersion === null) {
|
|
34169
|
+
const spec = getDependencySpec(packageJson, "valtio");
|
|
34170
|
+
if (spec !== null) facts.valtioVersion = spec;
|
|
34171
|
+
}
|
|
34168
34172
|
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
34169
34173
|
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
34170
34174
|
facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
|
|
34175
|
+
const remotionSpec = getDependencySpec(packageJson, "remotion");
|
|
34176
|
+
if (remotionSpec !== null) {
|
|
34177
|
+
facts.hasRemotionDependency = true;
|
|
34178
|
+
const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
|
|
34179
|
+
rootDirectory,
|
|
34180
|
+
rootPackageJson,
|
|
34181
|
+
packageName: "remotion",
|
|
34182
|
+
version: remotionSpec
|
|
34183
|
+
});
|
|
34184
|
+
const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
|
|
34185
|
+
if (remotionMajorVersion === null) {
|
|
34186
|
+
facts.hasUnknownRemotionVersion = true;
|
|
34187
|
+
facts.remotionVersion = null;
|
|
34188
|
+
} else if (!facts.hasUnknownRemotionVersion) {
|
|
34189
|
+
const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
|
|
34190
|
+
if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
|
|
34191
|
+
}
|
|
34192
|
+
}
|
|
34171
34193
|
};
|
|
34172
34194
|
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
34173
34195
|
const facts = {
|
|
@@ -34187,12 +34209,16 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
34187
34209
|
version: null,
|
|
34188
34210
|
sourceDirectory: null
|
|
34189
34211
|
},
|
|
34212
|
+
valtioVersion: null,
|
|
34190
34213
|
hasReactNativeAwarePackage: false,
|
|
34191
34214
|
hasReanimatedAwarePackage: false,
|
|
34192
34215
|
hasSsrDependency: false,
|
|
34216
|
+
hasRemotionDependency: false,
|
|
34217
|
+
hasUnknownRemotionVersion: false,
|
|
34218
|
+
remotionVersion: null,
|
|
34193
34219
|
reanimatedVersion: null
|
|
34194
34220
|
};
|
|
34195
|
-
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
34221
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
|
|
34196
34222
|
let isReactGroupSettled = !collectReactGroup;
|
|
34197
34223
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
34198
34224
|
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
@@ -34201,7 +34227,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
34201
34227
|
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
34202
34228
|
visitedDirectories.add(workspaceDirectory);
|
|
34203
34229
|
const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
|
|
34204
|
-
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
34230
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
|
|
34205
34231
|
const info = extractDependencyInfo(workspacePackageJson);
|
|
34206
34232
|
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
34207
34233
|
if (isReactGroupSettled) continue;
|
|
@@ -34430,6 +34456,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34430
34456
|
hasReactCompiler: false,
|
|
34431
34457
|
hasReactCompilerLintPlugin: false,
|
|
34432
34458
|
hasTanStackQuery: false,
|
|
34459
|
+
valtioVersion: null,
|
|
34460
|
+
valtioMajorVersion: null,
|
|
34461
|
+
hasRemotion: false,
|
|
34462
|
+
remotionVersion: null,
|
|
34463
|
+
remotionMajorVersion: null,
|
|
34433
34464
|
hasSsrDependency: false,
|
|
34434
34465
|
preactVersion: null,
|
|
34435
34466
|
preactMajorVersion: null,
|
|
@@ -34536,7 +34567,14 @@ const discoverProject = (directory) => {
|
|
|
34536
34567
|
packageName: "next",
|
|
34537
34568
|
version: workspaceFacts.next.version
|
|
34538
34569
|
}) : null;
|
|
34570
|
+
const valtioVersion = resolveCatalogBackedDependencyVersion({
|
|
34571
|
+
rootDirectory: directory,
|
|
34572
|
+
rootPackageJson: packageJson,
|
|
34573
|
+
packageName: "valtio",
|
|
34574
|
+
version: workspaceFacts.valtioVersion
|
|
34575
|
+
});
|
|
34539
34576
|
const preactVersion = getPreactVersion(packageJson);
|
|
34577
|
+
const remotionVersion = workspaceFacts.remotionVersion;
|
|
34540
34578
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
34541
34579
|
const projectInfo = {
|
|
34542
34580
|
rootDirectory: directory,
|
|
@@ -34551,6 +34589,11 @@ const discoverProject = (directory) => {
|
|
|
34551
34589
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
34552
34590
|
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
34553
34591
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
34592
|
+
valtioVersion,
|
|
34593
|
+
valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
|
|
34594
|
+
hasRemotion: workspaceFacts.hasRemotionDependency,
|
|
34595
|
+
remotionVersion,
|
|
34596
|
+
remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
|
|
34554
34597
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
34555
34598
|
preactVersion,
|
|
34556
34599
|
preactMajorVersion: parseReactMajor(preactVersion),
|
|
@@ -34784,7 +34827,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
34784
34827
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
34785
34828
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
34786
34829
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
34787
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
34788
34830
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
34789
34831
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
34790
34832
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -37071,9 +37113,25 @@ const BABEL_CONFIG_FILE_NAMES = [
|
|
|
37071
37113
|
".babelrc.json"
|
|
37072
37114
|
];
|
|
37073
37115
|
const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
|
|
37116
|
+
const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
|
|
37117
|
+
const REACT_NATIVE_PACKAGE = "react-native";
|
|
37118
|
+
const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
|
|
37119
|
+
const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
|
|
37074
37120
|
const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
|
|
37075
37121
|
const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
|
|
37122
|
+
const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
|
|
37123
|
+
const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
|
|
37124
|
+
const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
|
|
37125
|
+
if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
|
|
37126
|
+
const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
|
|
37127
|
+
if (normalizedVersionRange === null) return false;
|
|
37128
|
+
const minimumVersion = import_semver.minVersion(normalizedVersionRange);
|
|
37129
|
+
return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
|
|
37130
|
+
};
|
|
37131
|
+
const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
|
|
37076
37132
|
const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
37133
|
+
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
37134
|
+
const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
|
|
37077
37135
|
for (const fileName of BABEL_CONFIG_FILE_NAMES) {
|
|
37078
37136
|
const filePath = Path.join(rootDirectory, fileName);
|
|
37079
37137
|
if (!isFile(filePath)) continue;
|
|
@@ -37083,7 +37141,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
37083
37141
|
} catch {
|
|
37084
37142
|
continue;
|
|
37085
37143
|
}
|
|
37086
|
-
if (
|
|
37144
|
+
if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
|
|
37087
37145
|
filePath: fileName,
|
|
37088
37146
|
rule: "rn-no-metro-babel-preset",
|
|
37089
37147
|
severity: "error",
|
|
@@ -37106,12 +37164,6 @@ const checkReactNativeProject = (rootDirectory, project) => {
|
|
|
37106
37164
|
return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
|
|
37107
37165
|
};
|
|
37108
37166
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
37109
|
-
const DEPENDENCY_SECTIONS = [
|
|
37110
|
-
"dependencies",
|
|
37111
|
-
"devDependencies",
|
|
37112
|
-
"peerDependencies",
|
|
37113
|
-
"optionalDependencies"
|
|
37114
|
-
];
|
|
37115
37167
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
37116
37168
|
"19.0": {
|
|
37117
37169
|
rceFixedVersion: "19.0.1",
|
|
@@ -37158,20 +37210,13 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
|
|
|
37158
37210
|
for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
|
|
37159
37211
|
return [...directories];
|
|
37160
37212
|
};
|
|
37161
|
-
const readDeclaredSpec = (packageJson, packageName) => {
|
|
37162
|
-
for (const section of DEPENDENCY_SECTIONS) {
|
|
37163
|
-
const spec = packageJson[section]?.[packageName];
|
|
37164
|
-
if (typeof spec === "string") return spec;
|
|
37165
|
-
}
|
|
37166
|
-
return null;
|
|
37167
|
-
};
|
|
37168
37213
|
const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
|
|
37169
37214
|
const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
|
|
37170
37215
|
if (isFile(manifestPath)) {
|
|
37171
37216
|
const installedVersion = import_semver.valid(readPackageJson(manifestPath).version ?? null);
|
|
37172
37217
|
if (installedVersion !== null) return installedVersion;
|
|
37173
37218
|
}
|
|
37174
|
-
const candidateSpecs = [
|
|
37219
|
+
const candidateSpecs = [getDependencySpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
|
|
37175
37220
|
for (const spec of candidateSpecs) {
|
|
37176
37221
|
const pinnedVersion = spec === null ? null : import_semver.valid(spec);
|
|
37177
37222
|
if (pinnedVersion !== null) return pinnedVersion;
|
|
@@ -37854,6 +37899,12 @@ const buildCapabilities = (project) => {
|
|
|
37854
37899
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
37855
37900
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
37856
37901
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
37902
|
+
if (project.valtioVersion !== null) capabilities.add("valtio");
|
|
37903
|
+
addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
|
|
37904
|
+
if (project.hasRemotion) {
|
|
37905
|
+
capabilities.add("remotion");
|
|
37906
|
+
addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
|
|
37907
|
+
}
|
|
37857
37908
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
37858
37909
|
if (project.preactVersion !== null) capabilities.add("preact");
|
|
37859
37910
|
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
@@ -40847,12 +40898,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
40847
40898
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40848
40899
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40849
40900
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
40901
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
40850
40902
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
40851
40903
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
40852
40904
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40853
40905
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
40854
40906
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
40855
40907
|
};
|
|
40908
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
40909
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40910
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
40911
|
+
};
|
|
40856
40912
|
const PLUGIN_CATEGORY_MAP = {
|
|
40857
40913
|
react: "Bugs",
|
|
40858
40914
|
"react-hooks": "Bugs",
|
|
@@ -40910,6 +40966,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40910
40966
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40911
40967
|
help: reasonDetail || help
|
|
40912
40968
|
};
|
|
40969
|
+
if (rule === "set-state-in-effect") return {
|
|
40970
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
40971
|
+
help: reasonDetail || help
|
|
40972
|
+
};
|
|
40913
40973
|
return {
|
|
40914
40974
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40915
40975
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -43828,4 +43888,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43828
43888
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43829
43889
|
|
|
43830
43890
|
//# sourceMappingURL=index.js.map
|
|
43831
|
-
//# debugId=
|
|
43891
|
+
//# debugId=92325f1f-c593-5b4c-af5a-c8b7177212e1
|
package/dist/lsp.js
CHANGED
|
@@ -34191,7 +34191,7 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
|
34191
34191
|
if (nextMajor === null) return false;
|
|
34192
34192
|
return nextMajor < currentMajor;
|
|
34193
34193
|
};
|
|
34194
|
-
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
34194
|
+
const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
|
|
34195
34195
|
if (facts.expo.version === null) {
|
|
34196
34196
|
const spec = getDependencySpec(packageJson, "expo");
|
|
34197
34197
|
if (spec !== null) facts.expo = {
|
|
@@ -34217,9 +34217,31 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
|
34217
34217
|
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
34218
34218
|
if (spec !== null) facts.reanimatedVersion = spec;
|
|
34219
34219
|
}
|
|
34220
|
+
if (facts.valtioVersion === null) {
|
|
34221
|
+
const spec = getDependencySpec(packageJson, "valtio");
|
|
34222
|
+
if (spec !== null) facts.valtioVersion = spec;
|
|
34223
|
+
}
|
|
34220
34224
|
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
34221
34225
|
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
34222
34226
|
facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
|
|
34227
|
+
const remotionSpec = getDependencySpec(packageJson, "remotion");
|
|
34228
|
+
if (remotionSpec !== null) {
|
|
34229
|
+
facts.hasRemotionDependency = true;
|
|
34230
|
+
const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
|
|
34231
|
+
rootDirectory,
|
|
34232
|
+
rootPackageJson,
|
|
34233
|
+
packageName: "remotion",
|
|
34234
|
+
version: remotionSpec
|
|
34235
|
+
});
|
|
34236
|
+
const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
|
|
34237
|
+
if (remotionMajorVersion === null) {
|
|
34238
|
+
facts.hasUnknownRemotionVersion = true;
|
|
34239
|
+
facts.remotionVersion = null;
|
|
34240
|
+
} else if (!facts.hasUnknownRemotionVersion) {
|
|
34241
|
+
const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
|
|
34242
|
+
if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
|
|
34243
|
+
}
|
|
34244
|
+
}
|
|
34223
34245
|
};
|
|
34224
34246
|
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
34225
34247
|
const facts = {
|
|
@@ -34239,12 +34261,16 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
34239
34261
|
version: null,
|
|
34240
34262
|
sourceDirectory: null
|
|
34241
34263
|
},
|
|
34264
|
+
valtioVersion: null,
|
|
34242
34265
|
hasReactNativeAwarePackage: false,
|
|
34243
34266
|
hasReanimatedAwarePackage: false,
|
|
34244
34267
|
hasSsrDependency: false,
|
|
34268
|
+
hasRemotionDependency: false,
|
|
34269
|
+
hasUnknownRemotionVersion: false,
|
|
34270
|
+
remotionVersion: null,
|
|
34245
34271
|
reanimatedVersion: null
|
|
34246
34272
|
};
|
|
34247
|
-
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
34273
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
|
|
34248
34274
|
let isReactGroupSettled = !collectReactGroup;
|
|
34249
34275
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
34250
34276
|
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
@@ -34253,7 +34279,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
|
|
|
34253
34279
|
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
34254
34280
|
visitedDirectories.add(workspaceDirectory);
|
|
34255
34281
|
const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
|
|
34256
|
-
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
34282
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
|
|
34257
34283
|
const info = extractDependencyInfo(workspacePackageJson);
|
|
34258
34284
|
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
34259
34285
|
if (isReactGroupSettled) continue;
|
|
@@ -34482,6 +34508,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34482
34508
|
hasReactCompiler: false,
|
|
34483
34509
|
hasReactCompilerLintPlugin: false,
|
|
34484
34510
|
hasTanStackQuery: false,
|
|
34511
|
+
valtioVersion: null,
|
|
34512
|
+
valtioMajorVersion: null,
|
|
34513
|
+
hasRemotion: false,
|
|
34514
|
+
remotionVersion: null,
|
|
34515
|
+
remotionMajorVersion: null,
|
|
34485
34516
|
hasSsrDependency: false,
|
|
34486
34517
|
preactVersion: null,
|
|
34487
34518
|
preactMajorVersion: null,
|
|
@@ -34588,7 +34619,14 @@ const discoverProject = (directory) => {
|
|
|
34588
34619
|
packageName: "next",
|
|
34589
34620
|
version: workspaceFacts.next.version
|
|
34590
34621
|
}) : null;
|
|
34622
|
+
const valtioVersion = resolveCatalogBackedDependencyVersion({
|
|
34623
|
+
rootDirectory: directory,
|
|
34624
|
+
rootPackageJson: packageJson,
|
|
34625
|
+
packageName: "valtio",
|
|
34626
|
+
version: workspaceFacts.valtioVersion
|
|
34627
|
+
});
|
|
34591
34628
|
const preactVersion = getPreactVersion(packageJson);
|
|
34629
|
+
const remotionVersion = workspaceFacts.remotionVersion;
|
|
34592
34630
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
34593
34631
|
const projectInfo = {
|
|
34594
34632
|
rootDirectory: directory,
|
|
@@ -34603,6 +34641,11 @@ const discoverProject = (directory) => {
|
|
|
34603
34641
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
34604
34642
|
hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
|
|
34605
34643
|
hasTanStackQuery: hasTanStackQuery(packageJson),
|
|
34644
|
+
valtioVersion,
|
|
34645
|
+
valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
|
|
34646
|
+
hasRemotion: workspaceFacts.hasRemotionDependency,
|
|
34647
|
+
remotionVersion,
|
|
34648
|
+
remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
|
|
34606
34649
|
hasSsrDependency: workspaceFacts.hasSsrDependency,
|
|
34607
34650
|
preactVersion,
|
|
34608
34651
|
preactMajorVersion: parseReactMajor(preactVersion),
|
|
@@ -34836,7 +34879,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
34836
34879
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
34837
34880
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
34838
34881
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
34839
|
-
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
34840
34882
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
34841
34883
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
34842
34884
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -37049,9 +37091,25 @@ const BABEL_CONFIG_FILE_NAMES = [
|
|
|
37049
37091
|
".babelrc.json"
|
|
37050
37092
|
];
|
|
37051
37093
|
const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
|
|
37094
|
+
const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
|
|
37095
|
+
const REACT_NATIVE_PACKAGE = "react-native";
|
|
37096
|
+
const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
|
|
37097
|
+
const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
|
|
37052
37098
|
const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
|
|
37053
37099
|
const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
|
|
37100
|
+
const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
|
|
37101
|
+
const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
|
|
37102
|
+
const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
|
|
37103
|
+
if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
|
|
37104
|
+
const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
|
|
37105
|
+
if (normalizedVersionRange === null) return false;
|
|
37106
|
+
const minimumVersion = import_semver.minVersion(normalizedVersionRange);
|
|
37107
|
+
return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
|
|
37108
|
+
};
|
|
37109
|
+
const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
|
|
37054
37110
|
const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
37111
|
+
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
37112
|
+
const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
|
|
37055
37113
|
for (const fileName of BABEL_CONFIG_FILE_NAMES) {
|
|
37056
37114
|
const filePath = Path.join(rootDirectory, fileName);
|
|
37057
37115
|
if (!isFile(filePath)) continue;
|
|
@@ -37061,7 +37119,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
|
|
|
37061
37119
|
} catch {
|
|
37062
37120
|
continue;
|
|
37063
37121
|
}
|
|
37064
|
-
if (
|
|
37122
|
+
if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
|
|
37065
37123
|
filePath: fileName,
|
|
37066
37124
|
rule: "rn-no-metro-babel-preset",
|
|
37067
37125
|
severity: "error",
|
|
@@ -37084,12 +37142,6 @@ const checkReactNativeProject = (rootDirectory, project) => {
|
|
|
37084
37142
|
return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
|
|
37085
37143
|
};
|
|
37086
37144
|
const RULE_KEY = "no-vulnerable-react-server-components";
|
|
37087
|
-
const DEPENDENCY_SECTIONS = [
|
|
37088
|
-
"dependencies",
|
|
37089
|
-
"devDependencies",
|
|
37090
|
-
"peerDependencies",
|
|
37091
|
-
"optionalDependencies"
|
|
37092
|
-
];
|
|
37093
37145
|
const REACT_RSC_ADVISORIES_BY_MINOR = {
|
|
37094
37146
|
"19.0": {
|
|
37095
37147
|
rceFixedVersion: "19.0.1",
|
|
@@ -37136,20 +37188,13 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
|
|
|
37136
37188
|
for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
|
|
37137
37189
|
return [...directories];
|
|
37138
37190
|
};
|
|
37139
|
-
const readDeclaredSpec = (packageJson, packageName) => {
|
|
37140
|
-
for (const section of DEPENDENCY_SECTIONS) {
|
|
37141
|
-
const spec = packageJson[section]?.[packageName];
|
|
37142
|
-
if (typeof spec === "string") return spec;
|
|
37143
|
-
}
|
|
37144
|
-
return null;
|
|
37145
|
-
};
|
|
37146
37191
|
const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
|
|
37147
37192
|
const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
|
|
37148
37193
|
if (isFile(manifestPath)) {
|
|
37149
37194
|
const installedVersion = import_semver.valid(readPackageJson(manifestPath).version ?? null);
|
|
37150
37195
|
if (installedVersion !== null) return installedVersion;
|
|
37151
37196
|
}
|
|
37152
|
-
const candidateSpecs = [
|
|
37197
|
+
const candidateSpecs = [getDependencySpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
|
|
37153
37198
|
for (const spec of candidateSpecs) {
|
|
37154
37199
|
const pinnedVersion = spec === null ? null : import_semver.valid(spec);
|
|
37155
37200
|
if (pinnedVersion !== null) return pinnedVersion;
|
|
@@ -37832,6 +37877,12 @@ const buildCapabilities = (project) => {
|
|
|
37832
37877
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
37833
37878
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
37834
37879
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
37880
|
+
if (project.valtioVersion !== null) capabilities.add("valtio");
|
|
37881
|
+
addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
|
|
37882
|
+
if (project.hasRemotion) {
|
|
37883
|
+
capabilities.add("remotion");
|
|
37884
|
+
addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
|
|
37885
|
+
}
|
|
37835
37886
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
37836
37887
|
if (project.preactVersion !== null) capabilities.add("preact");
|
|
37837
37888
|
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
@@ -40825,12 +40876,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
|
|
|
40825
40876
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
40826
40877
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
40827
40878
|
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
40879
|
+
const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
|
|
40828
40880
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
40829
40881
|
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
40830
40882
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40831
40883
|
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
40832
40884
|
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
40833
40885
|
};
|
|
40886
|
+
const buildSetStateInEffectMessage = (reasonSummary) => {
|
|
40887
|
+
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
40888
|
+
return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
|
|
40889
|
+
};
|
|
40834
40890
|
const PLUGIN_CATEGORY_MAP = {
|
|
40835
40891
|
react: "Bugs",
|
|
40836
40892
|
"react-hooks": "Bugs",
|
|
@@ -40888,6 +40944,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
40888
40944
|
message: REACT_ERROR_BOUNDARY_MESSAGE,
|
|
40889
40945
|
help: reasonDetail || help
|
|
40890
40946
|
};
|
|
40947
|
+
if (rule === "set-state-in-effect") return {
|
|
40948
|
+
message: buildSetStateInEffectMessage(reasonSummary.trim()),
|
|
40949
|
+
help: reasonDetail || help
|
|
40950
|
+
};
|
|
40891
40951
|
return {
|
|
40892
40952
|
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
40893
40953
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
@@ -45448,5 +45508,5 @@ const startLanguageServer = () => {
|
|
|
45448
45508
|
};
|
|
45449
45509
|
//#endregion
|
|
45450
45510
|
export { startLanguageServer };
|
|
45451
|
-
!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]="
|
|
45452
|
-
//# debugId=
|
|
45511
|
+
!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]="91af1a13-aefd-5ec0-bbb6-9e2cb5c458ee")}catch(e){}}();
|
|
45512
|
+
//# debugId=91af1a13-aefd-5ec0-bbb6-9e2cb5c458ee
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.8.1-dev.
|
|
3
|
+
"version": "0.8.1-dev.f4aa821",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
|
-
"oxlint-plugin-react-doctor": "0.8.1-dev.
|
|
68
|
+
"oxlint-plugin-react-doctor": "0.8.1-dev.f4aa821",
|
|
69
69
|
"deslop-js": "0.8.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
77
|
"@react-doctor/api": "0.8.1",
|
|
78
|
-
"@react-doctor/
|
|
79
|
-
"@react-doctor/
|
|
78
|
+
"@react-doctor/language-server": "0.8.1",
|
|
79
|
+
"@react-doctor/core": "0.8.1"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|