mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
248 lines
8.3 KiB
JavaScript
248 lines
8.3 KiB
JavaScript
// @ts-check
|
|
|
|
import eslint from "@eslint/js";
|
|
import globals from "globals";
|
|
import { defineConfig } from "eslint/config";
|
|
import tseslint from "typescript-eslint";
|
|
|
|
const srcGlobs = [
|
|
"editor/src/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"portal/src/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"portal/main.tsx",
|
|
"shared/**/*.{js,mjs,jsx,ts,tsx}",
|
|
];
|
|
const nodeGlobs = [
|
|
"scripts/**/*.{js,ts,mjs,mts}",
|
|
"editor/scripts/**/*.{js,ts,mjs,mts}",
|
|
"editor/*.config.{js,ts,mjs}",
|
|
"portal/*.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, @shared/*) instead of relative imports.",
|
|
},
|
|
{
|
|
regex: "^src/",
|
|
message: "Use a workspace alias instead of absolute src/ imports.",
|
|
},
|
|
];
|
|
|
|
export default defineConfig(
|
|
{
|
|
// Everything that contains 3rd party code that we don't want to lint
|
|
ignores: [
|
|
"dist",
|
|
"dist-portal",
|
|
"node_modules",
|
|
"playwright-report",
|
|
"storybook-static",
|
|
"test-results",
|
|
"editor/dist",
|
|
"editor/public",
|
|
"editor/src-tauri",
|
|
"editor/playwright-report",
|
|
"editor/test-results",
|
|
"portal/public",
|
|
],
|
|
},
|
|
eslint.configs.recommended,
|
|
tseslint.configs.recommended,
|
|
{
|
|
rules: {
|
|
"no-restricted-imports": [
|
|
"error",
|
|
{
|
|
patterns: baseRestrictedImportPatterns,
|
|
},
|
|
],
|
|
"@typescript-eslint/no-empty-object-type": [
|
|
"error",
|
|
{
|
|
// Allow empty extending interfaces because there's no real reason not to, and it makes it obvious where to put extra attributes in the future
|
|
allowInterfaces: "with-single-extends",
|
|
},
|
|
],
|
|
"@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant
|
|
"@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant
|
|
"@typescript-eslint/no-unused-vars": [
|
|
"error",
|
|
{
|
|
args: "all", // All function args must be used (or explicitly ignored)
|
|
argsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
|
|
caughtErrors: "all", // Caught errors must be used (or explicitly ignored)
|
|
caughtErrorsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
|
|
destructuredArrayIgnorePattern: "^_", // Allow unused variables beginning with an underscore
|
|
varsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
|
|
ignoreRestSiblings: true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky)
|
|
},
|
|
],
|
|
},
|
|
},
|
|
// Desktop-only packages must not be imported from core or proprietary code.
|
|
// Use the stub/shadow pattern instead: define a stub in editor/src/core/ and override in editor/src/desktop/.
|
|
{
|
|
files: srcGlobs,
|
|
ignores: ["editor/src/desktop/**"],
|
|
rules: {
|
|
"no-restricted-imports": [
|
|
"error",
|
|
{
|
|
patterns: [
|
|
...baseRestrictedImportPatterns,
|
|
{
|
|
regex: "^@tauri-apps/",
|
|
message:
|
|
"Tauri APIs are desktop-only. Review frontend/editor/DeveloperGuide.md for structure advice.",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
// The cloud/ layer is the SHARED hosted/SaaS experience consumed by BOTH the
|
|
// saas and desktop leaves, so it must stay platform-portable. It must not
|
|
// reach platform-specific things directly (Supabase, Tauri, raw fetch,
|
|
// window.location, web storage, or import.meta.env.VITE_*) — those arrive via
|
|
// @app/* seams (services/apiClient, auth/session, platform/openExternal, ...)
|
|
// that each leaf provides for its own platform.
|
|
{
|
|
files: ["editor/src/cloud/**/*.{js,mjs,jsx,ts,tsx}"],
|
|
rules: {
|
|
"no-restricted-imports": [
|
|
"error",
|
|
{
|
|
patterns: [
|
|
...baseRestrictedImportPatterns,
|
|
{
|
|
regex: "^@supabase/",
|
|
message:
|
|
"cloud/ must stay platform-portable. Reach Supabase via an @app/* seam (e.g. @app/auth/supabase, @app/auth/session) provided per-platform in saas/ and desktop/.",
|
|
},
|
|
{
|
|
regex: "^@tauri-apps/",
|
|
message:
|
|
"cloud/ must stay platform-portable. Tauri APIs are desktop-only — reach native features via an @app/* seam (e.g. @app/platform/openExternal).",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
"no-restricted-globals": [
|
|
"error",
|
|
{
|
|
name: "fetch",
|
|
message:
|
|
"cloud/ must not call raw fetch — use @app/services/apiClient so each platform supplies its own transport.",
|
|
},
|
|
{
|
|
name: "localStorage",
|
|
message:
|
|
"cloud/ must not touch localStorage — use an @app/* storage seam so desktop/web can differ.",
|
|
},
|
|
{
|
|
name: "sessionStorage",
|
|
message:
|
|
"cloud/ must not touch sessionStorage — use an @app/* storage seam so desktop/web can differ.",
|
|
},
|
|
],
|
|
"no-restricted-syntax": [
|
|
"error",
|
|
{
|
|
selector:
|
|
"MemberExpression[object.name='window'][property.name='location']",
|
|
message:
|
|
"cloud/ must not touch window.location — use an @app/* seam (e.g. @app/platform/openExternal) so desktop/web can differ.",
|
|
},
|
|
{
|
|
selector:
|
|
"MemberExpression[property.name='env'][object.type='MetaProperty'][object.meta.name='import'][object.property.name='meta']",
|
|
message:
|
|
"cloud/ must not read import.meta.env — use @app/constants/app / @app/platform seams so config is supplied per-platform.",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
// The shared/ layer is the seed of a future packages/shared-ui — it must
|
|
// only depend on third-party packages and on itself. If it ever imports
|
|
// from editor or portal layers, extraction to a standalone package later
|
|
// becomes a rewrite instead of a `git mv`.
|
|
{
|
|
files: ["shared/**/*.{js,mjs,jsx,ts,tsx}"],
|
|
rules: {
|
|
"no-restricted-imports": [
|
|
"error",
|
|
{
|
|
patterns: [
|
|
...baseRestrictedImportPatterns,
|
|
{
|
|
regex: "^@app/",
|
|
message:
|
|
"shared/ must not depend on the editor layer (@app/* resolves into editor/src/).",
|
|
},
|
|
{
|
|
regex: "^@portal/",
|
|
message:
|
|
"shared/ must not depend on the portal layer. Use @shared/* or third-party imports only.",
|
|
},
|
|
{
|
|
regex: "^@core/",
|
|
message: "shared/ must not depend on editor/src/core/.",
|
|
},
|
|
{
|
|
regex: "^@proprietary/",
|
|
message: "shared/ must not depend on editor/src/proprietary/.",
|
|
},
|
|
{
|
|
regex: "^@tauri-apps/",
|
|
message: "shared/ must remain web-compatible (no Tauri APIs).",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
// Stricter rules that not all sub-folders are conformant to yet.
|
|
// Keep this non-type-aware: `parserOptions.project`/`projectService` here OOMs
|
|
// the lint step (builds the whole TS program); tsc covers type correctness.
|
|
{
|
|
files: srcGlobs,
|
|
ignores: [
|
|
"editor/src/core/components/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/contexts/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/data/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/hooks/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/pages/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/services/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/tests/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/tools/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/types/**/*.{js,mjs,jsx,ts,tsx}",
|
|
"editor/src/core/utils/**/*.{js,mjs,jsx,ts,tsx}",
|
|
],
|
|
rules: {
|
|
"@typescript-eslint/no-explicit-any": "error",
|
|
},
|
|
},
|
|
// Config for browser scripts
|
|
{
|
|
files: srcGlobs,
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
},
|
|
},
|
|
},
|
|
// Config for node scripts
|
|
{
|
|
files: nodeGlobs,
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.node,
|
|
},
|
|
},
|
|
},
|
|
);
|