import storybook from "eslint-plugin-storybook";
import betterTailwindcss from "eslint-plugin-better-tailwindcss";
import prettier from "eslint-config-prettier";
import path from "node:path";
import js from "@eslint/js";
import svelte from "eslint-plugin-svelte";
import { defineConfig, includeIgnoreFile } from "eslint/config";
import globals from "globals";
import ts from "typescript-eslint";
import { staleAwait } from "./scripts/staleAwait.ts";
const gitignorePath = path.resolve(import.meta.dirname, ".gitignore");
const fixtures = {
// the leading # is escaped: these are gitignore-style patterns, where # starts a comment
group: ["\\#fixtures/*", "**/src/fixtures", "**/src/fixtures/*"],
message: "fixtures are storybook and test input: import them from a story or a *.test.ts."
};
const relative = {
group: ["./*", "../*"],
message: "import through #lib, not a relative path."
};
const relativeRoute = {
group: ["./*", "../*", "!./$types", "!./+*", "!../app.css"],
message: "import through #lib or #routes, not a relative path."
};
export default defineConfig(
{ ignores: ["src/lib/api/lexicons/"] },
includeIgnoreFile(gitignorePath),
js.configs.recommended,
ts.configs.recommended,
svelte.configs.recommended,
storybook.configs["flat/recommended"],
prettier,
svelte.configs.prettier,
{
languageOptions: { globals: { ...globals.browser, ...globals.node } },
rules: {
// typescript owns undefined-name checks.
"no-undef": "off",
"no-empty": ["error", { allowEmptyCatch: true }],
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" }
]
}
},
{
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: [
".storybook/MockAuth.svelte",
".storybook/MockNotifications.svelte",
".storybook/BoneCaptureDecorator.svelte",
".storybook/WorkflowRunReplay.svelte",
".storybook/SimulatedRepoSettings.svelte",
".storybook/DatasetToggle.svelte"
]
},
extraFileExtensions: [".svelte"],
parser: ts.parser
}
}
},
{
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js", "**/*.ts", "**/*.js"],
plugins: { "better-tailwindcss": betterTailwindcss },
settings: {
"better-tailwindcss": {
entryPoint: "src/app.css",
// without this the rule silently skips px -> spacing-scale rewrites.
rootFontSize: 16
}
},
rules: {
"better-tailwindcss/enforce-canonical-classes": "warn",
"better-tailwindcss/no-deprecated-classes": "warn"
}
},
{
// fixtures feed stories and nothing else. without this the app can import
// them and ship sample data, which is how the notifications page got its
// mock feed
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js", "**/*.ts", "**/*.js"],
ignores: ["**/*.stories.svelte", "**/*.test.ts", "src/lib/**", "src/routes/**"],
rules: {
"no-restricted-imports": ["error", { patterns: [fixtures] }]
}
},
{
files: ["src/routes/**/*.svelte"],
ignores: ["**/*.stories.svelte"],
rules: { "no-restricted-syntax": ["error", ...staleAwait] }
},
{
files: ["src/routes/**"],
ignores: ["**/*.stories.svelte", "**/*.test.ts"],
rules: {
"no-restricted-imports": ["error", { patterns: [fixtures, relativeRoute] }]
}
},
{
files: ["src/routes/**/*.stories.svelte", "src/routes/**/*.test.ts"],
rules: {
"no-restricted-imports": ["error", { patterns: [relativeRoute] }]
}
},
{
files: ["src/lib/**"],
ignores: ["**/*.stories.svelte", "**/*.test.ts"],
rules: {
"no-restricted-imports": ["error", { patterns: [fixtures, relative] }]
}
},
{
files: ["src/lib/**/*.stories.svelte", "src/lib/**/*.test.ts"],
rules: {
"no-restricted-imports": ["error", { patterns: [relative] }]
}
}
);