diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3ab69765a9..c584ea1f7c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -73,14 +73,6 @@ updates: - "react-dom" - "@types/react" - "@types/react-dom" - typescript-eslint: - patterns: - - "@typescript-eslint/*" - - "typescript-eslint" - eslint: - patterns: - - "eslint" - - "@eslint/*" vite: patterns: - "vite" diff --git a/.taskfiles/frontend.yml b/.taskfiles/frontend.yml index d3ce0d86dd..edbeb9f14d 100644 --- a/.taskfiles/frontend.yml +++ b/.taskfiles/frontend.yml @@ -260,8 +260,7 @@ tasks: desc: "Run linting" deps: [install] cmds: - - task: lint:eslint - - task: lint:dpdm + - task: lint:oxlint - task: lint:colors - task: lint:css @@ -290,25 +289,17 @@ tasks: cmds: - node editor/scripts/lint/theme-lint.mjs contrast - lint:eslint: - desc: "Run ESLint linting" + lint:oxlint: + desc: "Run oxlint linting" deps: [install] cmds: - - npx eslint --max-warnings=0 - - lint:dpdm: - desc: "Run circular import linting" - deps: [install] - cmds: - # Globs so dpdm walks the whole tree. dpdm expands the braces itself, so this is - # shell-agnostic. Covers the whole editor tree, including the portal layer. - - npx dpdm "editor/src/**/*.{ts,tsx}" --circular --no-warning --no-tree --exit-code circular:1 + - npx oxlint --config oxlint.config.ts --max-warnings=0 lint:fix: desc: "Auto-fix lint issues" deps: [install] cmds: - - npx eslint --fix + - npx oxlint --config oxlint.config.ts --fix format: desc: "Auto-fix code formatting" diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a02437abe7..752208565c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -19,6 +19,6 @@ "yzhang.markdown-all-in-one", // Markdown All-in-One extension for enhanced Markdown editing "stylelint.vscode-stylelint", // Stylelint extension for CSS and SCSS linting "redhat.vscode-yaml", // YAML extension for Visual Studio Code - "dbaeumer.vscode-eslint", // ESLint extension for TypeScript linting + "oxc.oxc-vscode", // Oxc (oxlint) extension for JavaScript/TypeScript linting ] } diff --git a/AGENTS.md b/AGENTS.md index 259729bc04..7383ab3c3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -192,7 +192,7 @@ What goes where: - **saas** — web-only: Supabase web auth, AuthCallback, avatar canvas, `window.location`. - **desktop** — Tauri-only: keyring authService, tauriHttpClient, native files/windows, backend routing. -`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (enforced by ESLint). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`. +`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (all enforced by the linter). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`. Rule of thumb — **move, don't copy**: share via `cloud/`, override by shadowing the same `@app/*` path in a leaf (`saas/` or `desktop/`). diff --git a/DeveloperGuide.md b/DeveloperGuide.md index 286bf47fa5..7c2e21d5ca 100644 --- a/DeveloperGuide.md +++ b/DeveloperGuide.md @@ -158,7 +158,7 @@ Stirling-PDF/ │ │ │ └── locales/ # Internationalization files (JSON) │ │ └── vite.config.ts # Vite configuration │ ├── package.json # Shared workspace dependencies -│ └── eslint.config.mjs # Shared lint config +│ └── oxlint.config.ts # Shared lint config ├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files) ├── docs/ # Documentation files ├── exampleYmlFiles/ # Example YAML configuration files diff --git a/frontend/.storybook/vitest.setup.ts b/frontend/.storybook/vitest.setup.ts index 22fa4c027e..c6fe770b55 100644 --- a/frontend/.storybook/vitest.setup.ts +++ b/frontend/.storybook/vitest.setup.ts @@ -1,7 +1,7 @@ import { beforeAll } from "vitest"; import { setProjectAnnotations } from "@storybook/react-vite"; import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; -// eslint-disable-next-line no-restricted-imports -- Storybook-only: the sibling preview config has no @-alias. +// oxlint-disable-next-line no-restricted-imports -- Storybook-only: the sibling preview config has no @-alias. import * as projectAnnotations from "./preview"; // Include addon-a11y's annotations so its axe checks run under Vitest, not only diff --git a/frontend/README.md b/frontend/README.md index fafed9bede..ea759538e6 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -6,7 +6,7 @@ All frontend commands are run from the repository root using [Task](https://task - `task frontend:build` — production build - `task frontend:test` — run tests - `task frontend:test:watch` — run tests in watch mode -- `task frontend:lint` — run ESLint + cycle detection +- `task frontend:lint` — run linting - `task frontend:typecheck` — run TypeScript type checking - `task frontend:check` — run typecheck + lint + test - `task frontend:install` — install npm dependencies @@ -18,7 +18,7 @@ For desktop app development, see the [Tauri](#tauri) section below. `frontend/` is a workspace containing one or more apps. Today it holds the PDF editor under `frontend/editor/`; new apps (the developer portal, etc.) will sit alongside it as siblings. Shared tooling — `package.json`, `node_modules`, -`.storybook/`, ESLint, Prettier — lives at `frontend/` so every app installs +`.storybook/`, oxlint, Prettier — lives at `frontend/` so every app installs once and lints with the same config. ## Environment Variables diff --git a/frontend/editor/scripts/sync-portal-docs.mts b/frontend/editor/scripts/sync-portal-docs.mts index 08ba00137b..96f616eb46 100644 --- a/frontend/editor/scripts/sync-portal-docs.mts +++ b/frontend/editor/scripts/sync-portal-docs.mts @@ -13,7 +13,7 @@ import { mkdirSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; // tsx/node16 can't resolve the @portal alias here, so import by relative .ts path. -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import { buildManifest, type CategoryMap, diff --git a/frontend/editor/scripts/tsconfig.json b/frontend/editor/scripts/tsconfig.json index d80666471b..9377b2c7f3 100644 --- a/frontend/editor/scripts/tsconfig.json +++ b/frontend/editor/scripts/tsconfig.json @@ -8,5 +8,5 @@ // sync-portal-docs.mts imports the shared transform by its .ts path (run via tsx). "allowImportingTsExtensions": true }, - "include": ["./**/*.ts", "./**/*.mts"] + "include": ["./**/*.ts", "./**/*.mts", "../../oxlint.config.ts"] } diff --git a/frontend/editor/src/cloud/components/shared/config/configSections/Payg.tsx b/frontend/editor/src/cloud/components/shared/config/configSections/Payg.tsx index 7774b6bc72..da6db6e46c 100644 --- a/frontend/editor/src/cloud/components/shared/config/configSections/Payg.tsx +++ b/frontend/editor/src/cloud/components/shared/config/configSections/Payg.tsx @@ -32,9 +32,9 @@ import { prepaidSnapshotFromWallet, } from "@app/components/shared/config/configSections/usageMeters"; // Relative (not @app/*) so the co-located CSS + sibling component resolve directly. -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import "./Payg.css"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import SpendCapControl from "./SpendCapControl"; import { useTranslation } from "react-i18next"; import type { Wallet } from "@app/hooks/useWallet"; diff --git a/frontend/editor/src/cloud/components/shared/config/configSections/PaygFree.tsx b/frontend/editor/src/cloud/components/shared/config/configSections/PaygFree.tsx index ae3738bd5f..0df1f56d64 100644 --- a/frontend/editor/src/cloud/components/shared/config/configSections/PaygFree.tsx +++ b/frontend/editor/src/cloud/components/shared/config/configSections/PaygFree.tsx @@ -33,13 +33,13 @@ import LockIcon from "@mui/icons-material/LockOutlined"; import { useTranslation } from "react-i18next"; import { useRenderCount } from "@app/hooks/useRenderCount"; import { useWallet } from "@app/hooks/useWallet"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import "./Payg.css"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import "./PaygFree.css"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import UpgradeModal from "./UpgradeModal"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import { DocHelp } from "./Payg"; import { FreeMeterPanel, diff --git a/frontend/editor/src/cloud/components/shared/config/configSections/SpendCapControl.tsx b/frontend/editor/src/cloud/components/shared/config/configSections/SpendCapControl.tsx index d6b8b336f4..6cd00b1b3e 100644 --- a/frontend/editor/src/cloud/components/shared/config/configSections/SpendCapControl.tsx +++ b/frontend/editor/src/cloud/components/shared/config/configSections/SpendCapControl.tsx @@ -12,7 +12,7 @@ import { DEFAULT_CAP_PRESETS, SpendCapControl as SharedSpendCapControl, } from "@app/billing"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import "./SpendCapControl.css"; export { DEFAULT_CAP_PRESETS }; diff --git a/frontend/editor/src/cloud/components/shared/config/configSections/UpgradeModal.tsx b/frontend/editor/src/cloud/components/shared/config/configSections/UpgradeModal.tsx index bdbd84c6bc..f5ee5054fc 100644 --- a/frontend/editor/src/cloud/components/shared/config/configSections/UpgradeModal.tsx +++ b/frontend/editor/src/cloud/components/shared/config/configSections/UpgradeModal.tsx @@ -25,9 +25,9 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBackRounded"; import ShieldIcon from "@mui/icons-material/ShieldOutlined"; import CheckCircleIcon from "@mui/icons-material/CheckCircleRounded"; import { useTranslation } from "react-i18next"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import "./UpgradeModal.css"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import SpendCapControl from "./SpendCapControl"; /** diff --git a/frontend/editor/src/cloud/hooks/walletDevPreview.ts b/frontend/editor/src/cloud/hooks/walletDevPreview.ts index 7a1477cb4f..25bd9d261a 100644 --- a/frontend/editor/src/cloud/hooks/walletDevPreview.ts +++ b/frontend/editor/src/cloud/hooks/walletDevPreview.ts @@ -4,7 +4,7 @@ * The cloud/ layer is the SHARED hosted experience consumed by BOTH the saas * (web) and desktop (Tauri) leaves, so it must stay platform-portable: it can't * read {@code import.meta.env}, {@code window.location} or web storage directly - * (the cloud ESLint guardrail enforces this). The PAYG dev-preview route + * (the cloud oxlint guardrail enforces this). The PAYG dev-preview route * ({@code /dev/payg-preview}) is a saas-only local-design affordance that * synthesises a wallet from {@code localStorage} when the real backend isn't * mounted — all three of those banned reads. {@link useWallet} reaches that diff --git a/frontend/editor/src/core/components/shared/LocalIcon.tsx b/frontend/editor/src/core/components/shared/LocalIcon.tsx index fc51cb419b..cd278ff95d 100644 --- a/frontend/editor/src/core/components/shared/LocalIcon.tsx +++ b/frontend/editor/src/core/components/shared/LocalIcon.tsx @@ -1,6 +1,6 @@ import React from "react"; import { addCollection, Icon } from "@iconify/react"; -import iconSet from "../../../assets/material-symbols-icons.json"; // eslint-disable-line no-restricted-imports -- Outside app paths +import iconSet from "../../../assets/material-symbols-icons.json"; // oxlint-disable-line no-restricted-imports -- Outside app paths // Load icons synchronously at import time - guaranteed to be ready on first render let iconsLoaded = false; diff --git a/frontend/editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.tsx b/frontend/editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.tsx index 334e769a74..e3f7b6b990 100644 --- a/frontend/editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.tsx +++ b/frontend/editor/src/core/components/shared/config/configSections/ThirdPartyLicensesSection.tsx @@ -12,7 +12,7 @@ import { } from "@mantine/core"; import { isAxiosError } from "axios"; import apiClient from "@app/services/apiClient"; -import frontendLicenses from "../../../../../assets/3rdPartyLicenses.json"; // eslint-disable-line no-restricted-imports -- asset lives outside @app alias root +import frontendLicenses from "../../../../../assets/3rdPartyLicenses.json"; // oxlint-disable-line no-restricted-imports -- asset lives outside @app alias root interface Dependency { moduleName?: string; diff --git a/frontend/editor/src/core/data/ogMetadata.test.ts b/frontend/editor/src/core/data/ogMetadata.test.ts index e43ef11b9e..dd19737d46 100644 --- a/frontend/editor/src/core/data/ogMetadata.test.ts +++ b/frontend/editor/src/core/data/ogMetadata.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; import { getToolOgImage } from "@app/data/ogImage"; // Build tooling (plain ESM, node:fs only) - import the helpers for coverage. -// eslint-disable-next-line no-restricted-imports -- build script lives outside the @app alias root +// oxlint-disable-next-line no-restricted-imports -- build script lives outside the @app alias root import { buildOgTags, injectOg, diff --git a/frontend/editor/src/core/hooks/tools/compare/useCompareOperation.ts b/frontend/editor/src/core/hooks/tools/compare/useCompareOperation.ts index 7a43a443e4..3a40d9b699 100644 --- a/frontend/editor/src/core/hooks/tools/compare/useCompareOperation.ts +++ b/frontend/editor/src/core/hooks/tools/compare/useCompareOperation.ts @@ -665,7 +665,7 @@ export const useCompareOperation = (): CompareOperationHook => { if (workerRef.current) { try { workerRef.current.terminate(); - // eslint-disable-next-line no-empty + // oxlint-disable-next-line no-empty } catch {} workerRef.current = null; } diff --git a/frontend/editor/src/desktop/services/tauriHttpClient.ts b/frontend/editor/src/desktop/services/tauriHttpClient.ts index f3d674e7e8..ae226685cb 100644 --- a/frontend/editor/src/desktop/services/tauriHttpClient.ts +++ b/frontend/editor/src/desktop/services/tauriHttpClient.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any -- Axios-compatible API requires matching axios's `any` signatures */ +/* oxlint-disable typescript/no-explicit-any -- Axios-compatible API requires matching axios's `any` signatures */ import { fetch } from "@tauri-apps/plugin-http"; import { shouldUseFastLocalTransport, diff --git a/frontend/editor/src/index.tsx b/frontend/editor/src/index.tsx index 6ee0f6df51..d66f092bab 100644 --- a/frontend/editor/src/index.tsx +++ b/frontend/editor/src/index.tsx @@ -5,7 +5,7 @@ import "@app/utils/patchDomForTranslators"; import "@mantine/core/styles.css"; import "@mantine/dates/styles.css"; -import "../vite-env.d.ts"; // eslint-disable-line no-restricted-imports -- Outside app paths +import "../vite-env.d.ts"; // oxlint-disable-line no-restricted-imports -- Outside app paths import "@app/styles/index.css"; // Import global styles import React from "react"; import ReactDOM from "react-dom/client"; diff --git a/frontend/editor/src/portal/api/usersBackend.saas.test.ts b/frontend/editor/src/portal/api/usersBackend.saas.test.ts index d8facade5b..aa03ae15a8 100644 --- a/frontend/editor/src/portal/api/usersBackend.saas.test.ts +++ b/frontend/editor/src/portal/api/usersBackend.saas.test.ts @@ -31,7 +31,7 @@ vi.mock("@portal/auth/saasSupabase", () => ({ ensureSaasSupabase: vi.fn() })); // The SaaS usersBackend lives under src/saas; the portal vitest project resolves // @app to proprietary (there's no @saas alias here), so the SaaS impl can only be // exercised by importing it directly by path. -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import { usersBackend } from "../../saas/portal/usersBackend"; const server = setupServer(...teamSaasHandlers); diff --git a/frontend/editor/src/portal/components/users/UsersDirectory.test.tsx b/frontend/editor/src/portal/components/users/UsersDirectory.test.tsx index 2c047c74e2..21e68f24d4 100644 --- a/frontend/editor/src/portal/components/users/UsersDirectory.test.tsx +++ b/frontend/editor/src/portal/components/users/UsersDirectory.test.tsx @@ -19,7 +19,7 @@ import type { Team } from "@portal/api/teams"; // Prove the gating against the real flavor capability files. The portal vitest // project resolves @app to proprietary and has no @saas alias, so the SaaS set is // reached by path; the self-hosted set uses the @proprietary alias. -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import { usersCapabilities as saasCaps } from "../../../saas/portal/usersCapabilities"; import { usersCapabilities as selfHostedCaps } from "@proprietary/portal/usersCapabilities"; diff --git a/frontend/editor/src/portal/views/Users.caching.test.tsx b/frontend/editor/src/portal/views/Users.caching.test.tsx index a43cd8a835..64d839ac62 100644 --- a/frontend/editor/src/portal/views/Users.caching.test.tsx +++ b/frontend/editor/src/portal/views/Users.caching.test.tsx @@ -37,10 +37,12 @@ vi.mock("@app/auth/supabase/supabaseClient", () => ({ vi.mock("@portal/auth/saasSupabase", () => ({ ensureSaasSupabase: vi.fn() })); vi.mock("@app/portal/usersCapabilities", async () => ({ + // oxlint-disable-next-line no-restricted-imports -- resolve the real SaaS module past the mocked @app alias usersCapabilities: (await import("../../saas/portal/usersCapabilities")) .usersCapabilities, })); vi.mock("@app/portal/usersBackend", async () => ({ + // oxlint-disable-next-line no-restricted-imports -- resolve the real SaaS module past the mocked @app alias usersBackend: (await import("../../saas/portal/usersBackend")).usersBackend, })); diff --git a/frontend/editor/src/portal/views/Users.saas.test.tsx b/frontend/editor/src/portal/views/Users.saas.test.tsx index 3b63171c5d..a81dcf1c28 100644 --- a/frontend/editor/src/portal/views/Users.saas.test.tsx +++ b/frontend/editor/src/portal/views/Users.saas.test.tsx @@ -45,10 +45,12 @@ vi.mock("@portal/auth/saasSupabase", () => ({ ensureSaasSupabase: vi.fn() })); // Force the SaaS flavor: the portal vitest project resolves @app to proprietary, // so redirect the two flavor seams to their real SaaS implementations. vi.mock("@app/portal/usersCapabilities", async () => ({ + // oxlint-disable-next-line no-restricted-imports -- resolve the real SaaS module past the mocked @app alias usersCapabilities: (await import("../../saas/portal/usersCapabilities")) .usersCapabilities, })); vi.mock("@app/portal/usersBackend", async () => ({ + // oxlint-disable-next-line no-restricted-imports -- resolve the real SaaS module past the mocked @app alias usersBackend: (await import("../../saas/portal/usersBackend")).usersBackend, })); diff --git a/frontend/editor/src/portal/views/teamMyCache.saas.test.ts b/frontend/editor/src/portal/views/teamMyCache.saas.test.ts index f18c70dd0a..a4ba296535 100644 --- a/frontend/editor/src/portal/views/teamMyCache.saas.test.ts +++ b/frontend/editor/src/portal/views/teamMyCache.saas.test.ts @@ -35,6 +35,7 @@ vi.mock("@app/auth/supabase/supabaseClient", () => ({ // The portal test project's @app points at proprietary; resolve the flavor seam // to the real SaaS backend (same approach as Users.saas.test). vi.mock("@app/portal/usersBackend", async () => ({ + // oxlint-disable-next-line no-restricted-imports -- resolve the real SaaS module past the mocked @app alias usersBackend: (await import("../../saas/portal/usersBackend")).usersBackend, })); diff --git a/frontend/editor/src/proprietary/components/shared/config/configSections/ApiKeys.tsx b/frontend/editor/src/proprietary/components/shared/config/configSections/ApiKeys.tsx index e5ccfdd727..8a3175303f 100644 --- a/frontend/editor/src/proprietary/components/shared/config/configSections/ApiKeys.tsx +++ b/frontend/editor/src/proprietary/components/shared/config/configSections/ApiKeys.tsx @@ -1,10 +1,10 @@ import React, { useState } from "react"; import { Anchor, Group, Stack, Text, Paper, Skeleton } from "@mantine/core"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import ApiKeySection from "./apiKeys/ApiKeySection"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import RefreshModal from "./apiKeys/RefreshModal"; -// eslint-disable-next-line no-restricted-imports +// oxlint-disable-next-line no-restricted-imports import useApiKey from "./apiKeys/hooks/useApiKey"; import { useTranslation } from "react-i18next"; import LocalIcon from "@app/components/shared/LocalIcon"; diff --git a/frontend/editor/src/proprietary/services/heuristic/heuristicEngine.ts b/frontend/editor/src/proprietary/services/heuristic/heuristicEngine.ts index e030a9e93a..ed01b9542e 100644 --- a/frontend/editor/src/proprietary/services/heuristic/heuristicEngine.ts +++ b/frontend/editor/src/proprietary/services/heuristic/heuristicEngine.ts @@ -282,7 +282,7 @@ const LATIN_LETTER = /[a-z]/gi; const WORD = /[\p{L}']+/gu; // ASCII whitespace plus the no-break spaces pdf.js extraction commonly emits. -// eslint-disable-next-line no-control-regex -- vertical tab is intentional ASCII whitespace +// oxlint-disable-next-line no-control-regex -- vertical tab is intentional ASCII whitespace const WHITESPACE = /[\t\n\x0B\f\r \u00A0\u2007\u202F]+/g; // Structural signal patterns. Boolean-presence ones stay non-global (safe .test()), diff --git a/frontend/editor/src/saas/auth/AuthProvider.test.tsx b/frontend/editor/src/saas/auth/AuthProvider.test.tsx index 588507d59c..ffeca5433d 100644 --- a/frontend/editor/src/saas/auth/AuthProvider.test.tsx +++ b/frontend/editor/src/saas/auth/AuthProvider.test.tsx @@ -52,7 +52,7 @@ vi.mock("@app/services/userService", () => ({ })); // Imported after the mocks so the provider picks them up. -const { AuthProvider, useAuth } = await import("./UseSession"); +const { AuthProvider, useAuth } = await import("@app/auth/UseSession"); /** Surfaces `loading` so a test can assert on it rather than on the container. */ function LoadingProbe() { diff --git a/frontend/editor/vite.config.ts b/frontend/editor/vite.config.ts index 2e72d1efec..3dc3272d59 100644 --- a/frontend/editor/vite.config.ts +++ b/frontend/editor/vite.config.ts @@ -98,6 +98,7 @@ function prerenderOgPlugin(isSaas: boolean): PluginOption { name: "prerender-og", apply: "build" as const, async closeBundle() { + // oxlint-disable-next-line no-restricted-imports -- vite config runs before path aliases resolve, so a relative import is required here const { prerenderOg } = await import("./scripts/og-prerender.mjs"); const ogBase = ( process.env.VITE_OG_BASE_URL || diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs deleted file mode 100644 index 43fe732a4e..0000000000 --- a/frontend/eslint.config.mjs +++ /dev/null @@ -1,327 +0,0 @@ -// @ts-check - -import eslint from "@eslint/js"; -import globals from "globals"; -import { defineConfig } from "eslint/config"; -import tseslint from "typescript-eslint"; - -const srcGlobs = [ - // The portal layers live under editor/src/portal (base) and - // editor/src/portal-saas (saas override), so editor/src/** covers them. - "editor/src/**/*.{js,mjs,jsx,ts,tsx}", -]; -const nodeGlobs = [ - "scripts/**/*.{js,ts,mjs,mts}", - "editor/scripts/**/*.{js,ts,mjs,mts}", - // Covers editor/vite.config.ts and editor/vitest.config.ts. - "editor/*.config.{js,ts,mjs}", - "*.config.{js,ts,mjs}", - ".storybook/*.{js,ts,mjs,mts,tsx}", -]; - -const baseRestrictedImportPatterns = [ - { - regex: "^\\.", - message: - "Use a workspace alias (@app/* for editor, @portal/* for portal) instead of relative imports.", - }, - { - regex: "^src/", - message: "Use a workspace alias instead of absolute src/ imports.", - }, -]; - -// Button/SegmentedControl/Chip must come from the shared DS (@app/ui), not Mantine. -// If no variant fits, extend @app/ui — that layer (editor/src/core/ui) is exempt below. -const mantineComponentImportRestrictions = [ - { - selector: - "ImportDeclaration[source.value='@mantine/core'] > ImportSpecifier[imported.name=/^(Button|ActionIcon|UnstyledButton|CloseButton|FileButton)$/]", - message: - 'Use the shared Button (@app/ui/Button) instead of the Mantine button family. variant=primary|secondary|tertiary, accent=default|neutral|brand|ai|premium|danger|success|warning; an icon-only button is `