Files
clean-architecture-frontend…/eslint.config.js

297 lines
6.6 KiB
JavaScript

import babelParser from "@babel/eslint-parser";
import eslint from "@eslint/js";
import reactHooks from "eslint-plugin-react-hooks";
import globals from "globals";
const sourceExtensions = "{js,jsx,mjs,ts,tsx,mts}";
const layerPatterns = {
domain: [
"**/application/**",
"**/presentation/**",
"**/adapters/**",
"**/bootstrap/**",
"react",
"react-dom",
"@tanstack/**",
],
application: [
"**/presentation/**",
"**/adapters/**",
"**/bootstrap/**",
"react",
"react-dom",
"@tanstack/**",
],
presentation: [
"**/adapters/**",
"**/bootstrap/**",
"**/application/ports/out/**",
"@tanstack/**",
],
adapters: ["**/presentation/**", "**/bootstrap/**"],
};
function restrictedImports(patterns) {
return ["error", { patterns }];
}
const commonLanguageOptions = {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
...globals.node,
},
};
const commonSecurityRules = {
"no-eval": "error",
"no-new-func": "error",
"no-script-url": "error",
"no-restricted-syntax": [
"error",
{
selector: "JSXAttribute[name.name='dangerouslySetInnerHTML']",
message: "Raw HTML injection is prohibited by FE-OC-019.",
},
{
selector:
"CallExpression[callee.object.name='document'][callee.property.name='createElement'][arguments.0.value='script']",
message: "Runtime script construction is prohibited by FE-OC-019.",
},
],
};
const hookRules = {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
};
export default [
{
ignores: [
"dist/**",
"node_modules/**",
"artifacts/**",
"tests/fixtures/typecheck/**",
"tests/fixtures/architecture/forbidden/**",
"tests/fixtures/security/forbidden/**",
],
},
eslint.configs.recommended,
{
files: [`**/*.${sourceExtensions}`],
languageOptions: {
...commonLanguageOptions,
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
plugins: {
"react-hooks": reactHooks,
},
rules: {
...commonSecurityRules,
...hookRules,
},
},
{
files: ["**/*.{ts,mts}"],
languageOptions: {
...commonLanguageOptions,
parser: babelParser,
parserOptions: {
requireConfigFile: false,
babelOptions: {
plugins: [
["@babel/plugin-syntax-typescript", { isTSX: false }],
],
},
},
},
rules: {
"no-undef": "off",
"no-unused-vars": "off",
},
},
{
files: ["**/*.tsx"],
languageOptions: {
...commonLanguageOptions,
parser: babelParser,
parserOptions: {
requireConfigFile: false,
babelOptions: {
plugins: [
[
"@babel/plugin-syntax-typescript",
{ allExtensions: true, isTSX: true },
],
"@babel/plugin-syntax-jsx",
],
},
},
},
rules: {
"no-undef": "off",
"no-unused-vars": "off",
},
},
{
files: [`src/domain/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.domain),
"no-restricted-globals": [
"error",
"window",
"document",
"localStorage",
"fetch",
],
},
},
{
files: [`src/application/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.application),
"no-restricted-globals": [
"error",
"window",
"document",
"localStorage",
"fetch",
],
},
},
{
files: [`src/presentation/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.presentation),
"no-restricted-globals": [
"error",
"fetch",
"localStorage",
"sessionStorage",
],
},
},
{
files: [
`src/presentation/adapters/query/**/*.${sourceExtensions}`,
],
rules: {
"no-restricted-imports": restrictedImports([
"**/adapters/http/**",
"**/adapters/storage/**",
"**/adapters/auth/**",
"**/bootstrap/**",
"**/application/ports/out/**",
]),
},
},
{
files: [`src/presentation/templates/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports([
"**/application/**",
"**/adapters/**",
"**/bootstrap/**",
"@tanstack/**",
]),
},
},
{
files: [`src/adapters/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.adapters),
},
},
{
files: [`src/features/*/domain/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.domain),
"no-restricted-globals": [
"error",
"window",
"document",
"localStorage",
"fetch",
],
},
},
{
files: [`src/features/*/application/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports(layerPatterns.application),
"no-restricted-globals": [
"error",
"window",
"document",
"localStorage",
"fetch",
],
},
},
{
files: [`src/features/*/presentation/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports([
"**/features/*/adapters/**",
"**/adapters/http/**",
"**/adapters/storage/**",
"**/adapters/auth/**",
"**/bootstrap/**",
"**/application/ports/out/**",
"@tanstack/**",
]),
"no-restricted-globals": [
"error",
"fetch",
"localStorage",
"sessionStorage",
],
},
},
{
files: [`src/features/*/adapters/**/*.${sourceExtensions}`],
rules: {
"no-restricted-imports": restrictedImports([
"**/presentation/**",
"**/bootstrap/**",
"@tanstack/**",
]),
},
},
{
files: [`tests/**/*.${sourceExtensions}`],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
{
files: [
`tests/fixtures/architecture/forbidden/**/*.${sourceExtensions}`,
],
rules: {
"no-restricted-imports": restrictedImports([
"**/adapters/**",
"**/application/ports/out/**",
"@tanstack/**",
"react",
"react-dom",
"**/application/**",
]),
"no-restricted-globals": [
"error",
"fetch",
"localStorage",
"sessionStorage",
],
},
},
];