mirror of
https://github.com/navikt/aksel.git
synced 2026-10-12 16:52:03 +00:00
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ken <ken.aleksander@gmail.com>
290 lines
8.6 KiB
JavaScript
290 lines
8.6 KiB
JavaScript
const js = require("@eslint/js");
|
|
const nextPlugin = require("@next/eslint-plugin-next");
|
|
const vitest = require("@vitest/eslint-plugin");
|
|
const akselLocal = require("eslint-plugin-aksel-local");
|
|
const importPlugin = require("eslint-plugin-import");
|
|
const reactPlugin = require("eslint-plugin-react");
|
|
const reactHooks = require("eslint-plugin-react-hooks");
|
|
const storybook = require("eslint-plugin-storybook");
|
|
const testingLibrary = require("eslint-plugin-testing-library");
|
|
const { globalIgnores, defineConfig } = require("eslint/config");
|
|
const globals = require("globals");
|
|
const tseslint = require("typescript-eslint");
|
|
|
|
/**
|
|
* TODO:
|
|
* - Replace tseslint.configs.recommended with tseslint.configs.recommendedTypeChecked
|
|
* - Look into opinionated stylistic plugin (e.g. tseslint.configs.stylisticTypeChecked)
|
|
* - Consider adding the rule "id-length"
|
|
*/
|
|
|
|
module.exports = defineConfig([
|
|
globalIgnores([
|
|
"**/lib",
|
|
"**/public",
|
|
"**/esm",
|
|
"**/cjs",
|
|
"**/dist",
|
|
"@navikt/aksel/**/*.input.*",
|
|
"@navikt/aksel/**/*.output.*",
|
|
"**/codemod/**/*.js",
|
|
"**/playwright-report/**/*",
|
|
"**/tokens/**/plugin.js",
|
|
"**/.next",
|
|
"**/query-types.ts",
|
|
".yarn",
|
|
"**/next-env.d.ts",
|
|
// Generated/gitignored output (flat config does not read .gitignore)
|
|
"@navikt/aksel-icons/src/**",
|
|
"@navikt/aksel/src/version.ts",
|
|
"**/component-names.generated.ts",
|
|
"@navikt/core/tailwind/{src/tokens.ts,tailwind.config.js,tailwind.darkside.config.js}",
|
|
"@navikt/core/tokens/token_docs.js",
|
|
"apps/playroom/{playroom.config.js,src/auto-generated-imports.ts}",
|
|
"**/.build",
|
|
"**/coverage",
|
|
"**/storybook-static",
|
|
"**/test-results",
|
|
"tooling/analyzer/temp",
|
|
]),
|
|
js.configs.recommended,
|
|
reactPlugin.configs.flat.recommended,
|
|
{
|
|
// Uses the new JSX transform, so React does not need to be in scope
|
|
files: ["aksel.nav.no/sanity-studio/**"],
|
|
...reactPlugin.configs.flat["jsx-runtime"],
|
|
},
|
|
reactHooks.configs.flat["recommended-latest"],
|
|
storybook.configs["flat/recommended"],
|
|
importPlugin.flatConfigs.recommended,
|
|
importPlugin.flatConfigs.typescript,
|
|
{
|
|
settings: { react: { version: "detect" } }, // Allows eslint-plugin-react to detect installed react-version
|
|
languageOptions: {
|
|
// eslint-plugin-import's recommended config sets ecmaVersion 2018
|
|
ecmaVersion: "latest",
|
|
globals: {
|
|
...globals.browser,
|
|
...globals.node,
|
|
},
|
|
},
|
|
rules: {
|
|
"react/jsx-curly-brace-presence": [
|
|
"error",
|
|
{ propElementValues: "always" },
|
|
],
|
|
"react/prop-types": "off", // Temporary
|
|
"react/display-name": "off", // Temporary
|
|
"import/no-unresolved": "off",
|
|
"import/namespace": "off", // Biome has equivalent
|
|
"import/no-named-as-default": "off", // Temporary
|
|
"react-hooks/refs": "off", // Too many false positives
|
|
"react-hooks/exhaustive-deps": [
|
|
"warn",
|
|
{ additionalHooks: "(useClientLayoutEffect)" },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
files: ["**/*.{ts,tsx}"],
|
|
extends: [tseslint.configs.recommended],
|
|
linterOptions: {
|
|
reportUnusedDisableDirectives: true,
|
|
},
|
|
rules: {
|
|
//
|
|
// Start: Covered by TypeScript, and slow without a TS resolver
|
|
"import/default": "off",
|
|
"import/export": "off",
|
|
"import/named": "off",
|
|
"import/no-named-as-default-member": "off",
|
|
// End
|
|
"@typescript-eslint/no-explicit-any": "off", // Temporary
|
|
"@typescript-eslint/no-unused-expressions": [
|
|
"error",
|
|
/* https://eslint.org/docs/latest/rules/no-unused-expressions#allowshortcircuit-and-allowternary */
|
|
{ allowShortCircuit: true, allowTernary: true },
|
|
],
|
|
},
|
|
},
|
|
|
|
{
|
|
files: ["**/*.test.*", "**/__tests__/*"],
|
|
extends: [testingLibrary.configs["flat/react"], vitest.configs.recommended],
|
|
},
|
|
{
|
|
files: ["aksel.nav.no/website/**"],
|
|
plugins: {
|
|
"@next/next": nextPlugin,
|
|
},
|
|
rules: {
|
|
...nextPlugin.configs.recommended.rules,
|
|
"react/no-unknown-property": [2, { ignore: ["jsx", "global"] }],
|
|
"react/react-in-jsx-scope": "off",
|
|
"@next/next/no-html-link-for-pages": [
|
|
"error",
|
|
"aksel.nav.no/website/pages/",
|
|
],
|
|
},
|
|
},
|
|
{
|
|
files: ["aksel.nav.no/website/pages/eksempler/**"],
|
|
rules: {
|
|
"@next/next/no-img-element": "off",
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
"aksel.nav.no/website/pages/eksempler/**/*.tsx",
|
|
"aksel.nav.no/website/pages/templates/**/*.tsx",
|
|
],
|
|
plugins: {
|
|
"aksel-local": akselLocal,
|
|
},
|
|
rules: {
|
|
"aksel-local/args-check": ["error"],
|
|
"aksel-local/comment-check": ["error"],
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
"**/examples/__parts*/*.tsx",
|
|
"**/pages/templates/**/*.tsx",
|
|
"**/pages/eksempler/**/*.tsx",
|
|
],
|
|
plugins: {
|
|
"aksel-local": akselLocal,
|
|
},
|
|
rules: {
|
|
"aksel-local/import-check": ["error"], // Only allow imports from @navikt and react
|
|
},
|
|
},
|
|
{
|
|
files: ["**/examples/__parts-inline/*.tsx"],
|
|
rules: {
|
|
"arrow-body-style": ["error", "never"],
|
|
"func-style": ["error", "expression"],
|
|
"import/no-named-export": "error",
|
|
},
|
|
},
|
|
{
|
|
files: ["@navikt/**"],
|
|
ignores: ["**/*.stories.tsx"],
|
|
rules: {
|
|
"no-restricted-imports": [
|
|
"error",
|
|
{
|
|
paths: [
|
|
{
|
|
name: "react",
|
|
importNames: [
|
|
// React 18
|
|
"useId",
|
|
"useInsertionEffect",
|
|
"useSyncExternalStore",
|
|
"useDeferredValue",
|
|
"useTransition",
|
|
"startTransition",
|
|
// React 19
|
|
"useOptimistic",
|
|
"useActionState",
|
|
"use",
|
|
"cache",
|
|
"useEffectEvent",
|
|
"ViewTransition",
|
|
"addTransitionType",
|
|
],
|
|
message:
|
|
"We currently only support React-features accesible in React 17. To add new features, we will need to update peerDependencies (breaking change).",
|
|
},
|
|
// react-dom 18+
|
|
{
|
|
name: "react-dom/client",
|
|
importNames: ["createRoot", "hydrateRoot"],
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
name: "react-dom",
|
|
importNames: ["browser"],
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
],
|
|
patterns: [
|
|
{
|
|
group: ["fs", "path"],
|
|
message:
|
|
'Use `node:` prefix when importing native node modules, e.g. `import path from "node:path"`.',
|
|
},
|
|
{
|
|
group: ["**/esm/**", "**/cjs/**"],
|
|
message:
|
|
"Do not import from build output directories. Import from source instead.",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
"no-restricted-properties": [
|
|
"error",
|
|
// React 18
|
|
{
|
|
object: "React",
|
|
property: "useId",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useInsertionEffect",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useSyncExternalStore",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useDeferredValue",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useTransition",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "startTransition",
|
|
message: "React 18+ API not allowed (targeting React 17).",
|
|
},
|
|
// React 19
|
|
{
|
|
object: "React",
|
|
property: "useOptimistic",
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useActionState",
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "use",
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "cache",
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
{
|
|
object: "React",
|
|
property: "useEffectEvent",
|
|
message: "React 19+ API not allowed (targeting React 17).",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
]);
|