Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@
"lint:fix": "vp lint --fix",
"format": "vp fmt",
"format:check": "vp fmt --check",
"check": "vp check",
"check": "pnpm run skills:check && tsx scripts/check-source-architecture.ts && node --experimental-strip-types --no-warnings scripts/compatibility/check-public-packages.ts && node --experimental-strip-types --no-warnings scripts/compatibility/check-approved-deltas.ts && node --experimental-strip-types --no-warnings --test scripts/compatibility/cli-help.test.ts && tsx --test scripts/build/read-package-version.test.ts && vp check",
"architecture:check": "tsx scripts/check-source-architecture.ts",
"test:architecture": "tsx --test scripts/check-source-architecture.test.ts",
"compatibility:check": "node --experimental-strip-types --no-warnings scripts/compatibility/check-public-packages.ts && node --experimental-strip-types --no-warnings scripts/compatibility/check-approved-deltas.ts",
"compatibility:update": "node --experimental-strip-types --no-warnings scripts/compatibility/check-public-packages.ts --update",
"compatibility:packed:update": "node --experimental-strip-types --no-warnings scripts/smoke-packed-cli-install.ts --update-snapshots",
"test:compatibility": "node --experimental-strip-types --no-warnings --test scripts/compatibility/cli-help.test.ts",
"test:build-policy": "tsx --test scripts/build/read-package-version.test.ts",
"test:skills": "node --test scripts/check-skills.test.mjs .agents/skills/run-parity/scripts/compare-parity.test.mjs .agents/skills/run-parity/scripts/validate-parity-input.test.mjs",
"skills:sync": "node scripts/sync-react-doctor-skill.mjs",
"skills:check": "node scripts/sync-react-doctor-skill.mjs --check && node scripts/check-skills.mjs && pnpm run test:skills",
"changeset": "changeset",
"version": "changeset version",
"release": "pnpm build && pnpm check:published-deps && node scripts/sentry-sourcemaps.mjs && changeset publish",
Expand All @@ -46,6 +56,7 @@
"@voidzero-dev/vite-plus-core": "^0.1.15",
"commander": "^14.0.3",
"cross-env": "^10.1.0",
"oxc-parser": "^0.141.0",
"simple-statistics": "^7.9.3",
"tsx": "^4.22.4",
"turbo": "^2.9.7",
Expand Down
19 changes: 8 additions & 11 deletions packages/api/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { defineConfig } from "vite-plus";
import {
DEFAULT_TEST_TIMEOUT_MS,
ENGINE_RUNTIME_EXTERNALS,
NODE_PACK_TARGET,
} from "../../scripts/build/constants.js";

export default defineConfig({
pack: [
{
entry: { index: "./src/index.ts" },
deps: {
neverBundle: [
"deslop-js",
"effect",
"oxc-parser",
"oxc-resolver",
"oxlint",
"oxlint-plugin-react-doctor",
"typescript",
],
neverBundle: ENGINE_RUNTIME_EXTERNALS,
},
dts: true,
target: "node20",
target: NODE_PACK_TARGET,
platform: "node",
fixedExtension: false,
},
],
test: {
testTimeout: 30_000,
testTimeout: DEFAULT_TEST_TIMEOUT_MS,
},
});
1 change: 0 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"typescript": ">=5.0.4 <7"
},
"devDependencies": {
"@effect/vitest": "4.0.0-beta.70",
"@types/node": "^25.6.0",
"@types/picomatch": "^4.0.3",
"@types/semver": "^7.7.1"
Expand Down
29 changes: 11 additions & 18 deletions packages/core/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite-plus";
import {
DEFAULT_TEST_TIMEOUT_MS,
ENGINE_RUNTIME_EXTERNALS,
NODE_PACK_TARGET,
} from "../../scripts/build/constants.js";
import { readPackageVersion } from "../../scripts/utils/read-package-version.js";

const packageRoot = path.dirname(fileURLToPath(import.meta.url));

const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
version: string;
};
const packageVersion = readPackageVersion(import.meta.url);

export default defineConfig({
pack: [
{
entry: { index: "./src/index.ts", schemas: "./src/schemas.ts" },
deps: {
neverBundle: [
"@effect/platform-node-shared",
"deslop-js",
"effect",
"oxc-parser",
"oxc-resolver",
"oxlint",
"oxlint-plugin-react-doctor",
"typescript",
],
neverBundle: ["@effect/platform-node-shared", ...ENGINE_RUNTIME_EXTERNALS],
},
dts: true,
target: "node20",
target: NODE_PACK_TARGET,
platform: "node",
fixedExtension: false,
env: {
REACT_DOCTOR_CORE_VERSION: packageJson.version,
REACT_DOCTOR_CORE_VERSION: packageVersion,
},
},
],
Expand All @@ -49,6 +42,6 @@ export default defineConfig({
replacement: path.join(packageRoot, "../oxlint-plugin-react-doctor/src/index.ts"),
},
],
testTimeout: 30_000,
testTimeout: DEFAULT_TEST_TIMEOUT_MS,
},
});
1 change: 0 additions & 1 deletion packages/deslop-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"typescript": ">=5.0.4 <6"
},
"devDependencies": {
"@types/minimatch": "^5.1.2",
"@types/node": "^25.6.0",
"tsx": "^4.21.0"
}
Expand Down
80 changes: 80 additions & 0 deletions packages/deslop-js/src/utils/find-strongly-connected-components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
interface StronglyConnectedComponentFrame {
nodeIndex: number;
successorIndex: number;
}

export const findStronglyConnectedComponents = (
adjacencyList: ReadonlyArray<ReadonlyArray<number>>,
): number[][] => {
const nodeIndices: Array<number | undefined> = new Array(adjacencyList.length);
const lowLinks: number[] = new Array(adjacencyList.length).fill(0);
const nodesOnStack: boolean[] = new Array(adjacencyList.length).fill(false);
const componentStack: number[] = [];
const components: number[][] = [];
let nextNodeIndex = 0;

for (let startNodeIndex = 0; startNodeIndex < adjacencyList.length; startNodeIndex++) {
if (nodeIndices[startNodeIndex] !== undefined) continue;

nodeIndices[startNodeIndex] = nextNodeIndex;
lowLinks[startNodeIndex] = nextNodeIndex;
nextNodeIndex++;
nodesOnStack[startNodeIndex] = true;
componentStack.push(startNodeIndex);

const traversalStack: StronglyConnectedComponentFrame[] = [
{ nodeIndex: startNodeIndex, successorIndex: 0 },
];

while (traversalStack.length > 0) {
const frame = traversalStack[traversalStack.length - 1];
const successors = adjacencyList[frame.nodeIndex];

if (frame.successorIndex < successors.length) {
const successorNodeIndex = successors[frame.successorIndex];
frame.successorIndex++;
const successorTraversalIndex = nodeIndices[successorNodeIndex];

if (successorTraversalIndex === undefined) {
nodeIndices[successorNodeIndex] = nextNodeIndex;
lowLinks[successorNodeIndex] = nextNodeIndex;
nextNodeIndex++;
nodesOnStack[successorNodeIndex] = true;
componentStack.push(successorNodeIndex);
traversalStack.push({ nodeIndex: successorNodeIndex, successorIndex: 0 });
} else if (nodesOnStack[successorNodeIndex]) {
lowLinks[frame.nodeIndex] = Math.min(lowLinks[frame.nodeIndex], successorTraversalIndex);
}
continue;
}

const currentNodeIndex = frame.nodeIndex;
const currentTraversalIndex = nodeIndices[currentNodeIndex];
traversalStack.pop();

if (traversalStack.length > 0) {
const parentFrame = traversalStack[traversalStack.length - 1];
lowLinks[parentFrame.nodeIndex] = Math.min(
lowLinks[parentFrame.nodeIndex],
lowLinks[currentNodeIndex],
);
}

if (currentTraversalIndex !== lowLinks[currentNodeIndex]) continue;

const component: number[] = [];
let componentNodeIndex: number | undefined;
do {
componentNodeIndex = componentStack.pop();
if (componentNodeIndex === undefined) {
throw new Error("Strongly connected component stack was unexpectedly empty.");
}
nodesOnStack[componentNodeIndex] = false;
component.push(componentNodeIndex);
} while (componentNodeIndex !== currentNodeIndex);
components.push(component);
}
}

return components;
};
15 changes: 5 additions & 10 deletions packages/eslint-plugin-react-doctor/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite-plus";
import { NODE_PACK_TARGET } from "../../scripts/build/constants.js";
import { readPackageVersion } from "../../scripts/utils/read-package-version.js";

const packageRoot = path.dirname(fileURLToPath(import.meta.url));

const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
version: string;
};
const packageVersion = readPackageVersion(import.meta.url);

export default defineConfig({
pack: [
{
entry: { index: "./src/index.ts" },
deps: { neverBundle: ["oxlint-plugin-react-doctor"] },
dts: true,
target: "node20",
target: NODE_PACK_TARGET,
platform: "node",
fixedExtension: false,
env: {
VERSION: process.env.VERSION ?? packageJson.version,
VERSION: process.env.VERSION ?? packageVersion,
},
},
],
Expand Down
39 changes: 13 additions & 26 deletions packages/language-server/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,41 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite-plus";
import {
DEFAULT_TEST_TIMEOUT_MS,
ENGINE_RUNTIME_EXTERNALS,
LSP_RUNTIME_EXTERNALS,
NODE_PACK_TARGET,
} from "../../scripts/build/constants.js";
import { readPackageVersion } from "../../scripts/utils/read-package-version.js";

const packageRoot = path.dirname(fileURLToPath(import.meta.url));
const { version } = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
version: string;
};
const packageVersion = readPackageVersion(import.meta.url);

export default defineConfig({
pack: [
{
entry: { index: "./src/index.ts" },
env: {
VERSION: process.env.VERSION ?? version,
VERSION: process.env.VERSION ?? packageVersion,
},
deps: {
// Keep the heavy engine + LSP transport external so the
// language-server dist stays lean and runnable standalone via
// its own node_modules. The react-doctor CLI re-bundles this
// dist and decides which of these to inline at publish time.
neverBundle: [
"@react-doctor/core",
"deslop-js",
"effect",
"oxc-parser",
"oxc-resolver",
"oxlint",
"oxlint-plugin-react-doctor",
"typescript",
"vscode-languageserver",
"vscode-languageserver-protocol",
"vscode-languageserver-textdocument",
"vscode-jsonrpc",
"vscode-uri",
],
neverBundle: ["@react-doctor/core", ...ENGINE_RUNTIME_EXTERNALS, ...LSP_RUNTIME_EXTERNALS],
},
dts: true,
target: "node20",
target: NODE_PACK_TARGET,
platform: "node",
fixedExtension: false,
},
],
test: {
testTimeout: 30_000,
testTimeout: DEFAULT_TEST_TIMEOUT_MS,
// The integration suite boots a real LSP server subprocess and waits up
// to 20s for it to publish diagnostics inside `beforeAll`. The default
// 10s hook timeout is shorter than that wait, so a slow cold start on
// macOS / Windows CI runners trips the hook before the server is ready.
// Match it to `testTimeout` so the hook gets the same budget as the tests.
hookTimeout: 30_000,
hookTimeout: DEFAULT_TEST_TIMEOUT_MS,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,6 @@ const RULES_NOT_PORTED_FROM_EXTERNAL = new Set([
"role-button-requires-complete-keyboard-activation",
]);

// Rule ids whose source files are kept on disk but intentionally NOT
// registered. Use sparingly — the canonical way to retire a rule is to
// delete its file (and its tests, fixture references, etc.). This
// skiplist exists for rules we want to stop shipping right away while
// preserving their implementation, tests, and regression fixtures so
// re-enabling is a one-line change. Add a brief justification next to
// every entry.
const RULE_IDS_TO_SKIP_REGISTRATION = new Set([
// The React-Compiler memoization premise didn't hold: the three
// canonical hooks it targeted (`useRouter`, `useSearchParams`,
// `useNavigation`) all return stable references, so destructuring
// their methods produces no measurable compiler win — and on Pages
// Router (`next/router`) destructuring `push` captures a stale
// reference. Implementation + regression suite + fixture lines kept
// in place; remove this entry to re-enable.
"react-compiler-destructure-method",
]);

// Fine-grained category → the clear, user-facing bucket the scan output
// groups & labels by. Rules (and the buckets below) declare a detailed
// category for intent; the reporter only ever shows these five outcome
Expand Down Expand Up @@ -300,7 +282,6 @@ for (const bucket of fs.readdirSync(PLUGIN_RULES_ROOT, { withFileTypes: true }))
process.exit(1);
}
const ruleId = idMatch[1];
if (RULE_IDS_TO_SKIP_REGISTRATION.has(ruleId)) continue;
const category = toBucket(categoryMatch ? categoryMatch[1] : defaultCategory);
const severity = severityMatch[1];
// Force POSIX separators — `path.relative()` returns backslashes on
Expand Down
17 changes: 6 additions & 11 deletions packages/oxlint-plugin-react-doctor/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite-plus";
import { DEFAULT_TEST_TIMEOUT_MS, NODE_PACK_TARGET } from "../../scripts/build/constants.js";
import { readPackageVersion } from "../../scripts/utils/read-package-version.js";

const packageRoot = path.dirname(fileURLToPath(import.meta.url));

const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
version: string;
};
const packageVersion = readPackageVersion(import.meta.url);

export default defineConfig({
pack: [
Expand All @@ -29,15 +24,15 @@ export default defineConfig({
neverBundle: ["oxc-parser"],
},
dts: true,
target: "node20",
target: NODE_PACK_TARGET,
platform: "node",
fixedExtension: false,
env: {
VERSION: process.env.VERSION ?? packageJson.version,
VERSION: process.env.VERSION ?? packageVersion,
},
},
],
test: {
testTimeout: 30_000,
testTimeout: DEFAULT_TEST_TIMEOUT_MS,
},
});
Loading
Loading