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
14 changes: 13 additions & 1 deletion src/eslint/configs/javascript.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import eslint from "@eslint/js";
import globals from "globals";
import { getPackageInfo } from "local-pkg";
import semver from "semver";
import type { ConfigWithExtends } from "typescript-eslint";

import { pluginAntfu, pluginUnusedImports } from "../plugins";

export function javascript(): ConfigWithExtends[] {
export async function javascript(): Promise<ConfigWithExtends[]> {
// Check if ESLint version is >= 9.35.0 to enable preserve-caught-error rule
const eslintInfo = await getPackageInfo("eslint");
const supportsPreserveCaughtError =
eslintInfo?.version !== undefined &&
semver.gte(eslintInfo.version, "9.35.0");

return [
{
languageOptions: {
Expand Down Expand Up @@ -221,6 +229,10 @@ export function javascript(): ConfigWithExtends[] {
"valid-typeof": ["error", { requireStringLiterals: true }],
"vars-on-top": "error",
yoda: ["error", "never"],
// ESLint 9.35.0+ rule for preserving error cause when rethrowing
...(supportsPreserveCaughtError
? { "preserve-caught-error": "error" }
: {}),
},
},
];
Expand Down
2 changes: 1 addition & 1 deletion src/eslint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const solvro = async (...overrides: ConfigWithExtends[]) => {
throw new Error("You can't use both Adonis and React in the same project");
}

const configs = basePreset();
const configs = await basePreset();
const projectConfigs: ConfigWithExtends[] = [];

if (isAdonis) {
Expand Down
4 changes: 2 additions & 2 deletions src/eslint/presets/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { javascript } from "../configs/javascript";
import { jsdoc } from "../configs/jsdoc";
import { typescriptRelaxed } from "../configs/typescript-relaxed";

export const basePreset = (): ConfigWithExtends[] => [
export const basePreset = async (): Promise<ConfigWithExtends[]> => [
gitignore(),
...javascript(),
...(await javascript()),
...jsdoc(),
...comments(),
...typescriptRelaxed(),
Expand Down
94 changes: 94 additions & 0 deletions tests/preserve-caught-error.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { getPackageInfo } from "local-pkg";
import { describe, expect, it, vi } from "vitest";

// Mock the local-pkg module to simulate different ESLint versions
vi.mock("local-pkg", () => ({
getPackageInfo: vi.fn(),
}));

describe("Preserve Caught Error Rule Tests", () => {
const mockGetPackageInfo = vi.mocked(getPackageInfo);

it("should include preserve-caught-error rule when ESLint >= 9.35.0", async () => {
// Mock ESLint version 9.35.0
mockGetPackageInfo.mockResolvedValue({
name: "eslint",
version: "9.35.0",
rootPath: "/mock/path",
packageJsonPath: "/mock/path/package.json",
});

// Import the javascript function after mocking
const { javascript } = await import("../src/eslint/configs/javascript");
const configs = await javascript();

// Find the config with rules
const rulesConfig = configs.find((config) => config.rules);
expect(rulesConfig).toBeDefined();
expect(rulesConfig?.rules).toBeDefined();

// Check if preserve-caught-error rule is present and set to "error"
expect(rulesConfig?.rules?.["preserve-caught-error"]).toBe("error");
});

it("should include preserve-caught-error rule when ESLint > 9.35.0", async () => {
// Mock ESLint version 9.36.0 (newer version)
mockGetPackageInfo.mockResolvedValue({
name: "eslint",
version: "9.36.0",
rootPath: "/mock/path",
packageJsonPath: "/mock/path/package.json",
});

// Import the javascript function after mocking
const { javascript } = await import("../src/eslint/configs/javascript");
const configs = await javascript();

// Find the config with rules
const rulesConfig = configs.find((config) => config.rules);
expect(rulesConfig).toBeDefined();
expect(rulesConfig?.rules).toBeDefined();

// Check if preserve-caught-error rule is present and set to "error"
expect(rulesConfig?.rules?.["preserve-caught-error"]).toBe("error");
});

it("should NOT include preserve-caught-error rule when ESLint < 9.35.0", async () => {
// Mock ESLint version 9.34.0 (older version)
mockGetPackageInfo.mockResolvedValue({
name: "eslint",
version: "9.34.0",
rootPath: "/mock/path",
packageJsonPath: "/mock/path/package.json",
});

// Import the javascript function after mocking
const { javascript } = await import("../src/eslint/configs/javascript");
const configs = await javascript();

// Find the config with rules
const rulesConfig = configs.find((config) => config.rules);
expect(rulesConfig).toBeDefined();
expect(rulesConfig?.rules).toBeDefined();

// Check if preserve-caught-error rule is NOT present
expect(rulesConfig?.rules?.["preserve-caught-error"]).toBeUndefined();
});

it("should handle missing ESLint package gracefully", async () => {
// Mock no ESLint package found
mockGetPackageInfo.mockResolvedValue(undefined);

// Import the javascript function after mocking
const { javascript } = await import("../src/eslint/configs/javascript");
const configs = await javascript();

// Find the config with rules
const rulesConfig = configs.find((config) => config.rules);
expect(rulesConfig).toBeDefined();
expect(rulesConfig?.rules).toBeDefined();

// Check if preserve-caught-error rule is NOT present when ESLint is not found
expect(rulesConfig?.rules?.["preserve-caught-error"]).toBeUndefined();
});
});
Loading