// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ const path = require('path') const fs = require('fs') const mobilePagesDir = path.resolve(__dirname, 'app/frontend/apps/mobile/pages') const mobilePagesFolder = fs.readdirSync(mobilePagesDir) const desktopPagesDir = path.resolve( __dirname, 'app/frontend/apps/desktop/pages', ) const desktopPagesFolder = fs.readdirSync(desktopPagesDir) module.exports = { root: true, env: { browser: true, node: true, }, plugins: [ '@typescript-eslint', 'vue', 'prettier', 'sonarjs', 'security', 'zammad', ], extends: [ 'airbnb-base', 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', '@vue/prettier', '@vue/typescript/recommended', 'prettier', 'plugin:sonarjs/recommended', 'plugin:security/recommended-legacy', ], rules: { 'zammad/zammad-copyright': 'error', 'zammad/zammad-detect-translatable-string': 'error', 'zammad/zammad-tailwind-ltr': 'error', 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'consistent-return': 'off', // allow implicit return 'class-methods-use-this': 'off', 'prefer-destructuring': [ 'error', { VariableDeclarator: { array: false, object: true, }, AssignmentExpression: { array: false, object: true, }, }, { enforceForRenamedProperties: false, }, ], // Loosen AirBnB's strict rules a bit to allow 'for .. of' 'no-restricted-syntax': [ 'error', 'ForInStatement', // "ForOfStatement", // We want to allow this 'LabeledStatement', 'WithStatement', ], 'no-underscore-dangle': 'off', 'no-param-reassign': 'off', 'func-style': ['error', 'expression'], 'no-restricted-imports': 'off', 'import/no-extraneous-dependencies': 'off', 'import/extensions': ['error', 'ignorePackages'], 'import/prefer-default-export': 'off', 'import/no-restricted-paths': [ 'error', { zones: [ // restrict import inside shared context from app context { target: './app/frontend/shared', from: './app/frontend/apps', }, { target: './app/frontend/apps/desktop', from: './app/frontend/apps/mobile', }, { target: './app/frontend/apps/mobile', from: './app/frontend/apps/desktop', }, // restrict imports between different pages folder ...mobilePagesFolder.map((page) => { return { target: `./app/frontend/apps/mobile/pages/!(${page})/**/*`, from: `./app/frontend/apps/mobile/pages/${page}/**/*`, } }), ...desktopPagesFolder.map((page) => { return { target: `./app/frontend/apps/desktop/pages/!(${page})/**/*`, from: `./app/frontend/apps/desktop/pages/${page}/**/*`, } }), ], }, ], /* We strongly recommend that you do not use the no-undef lint rule on TypeScript projects. The checks it provides are already provided by TypeScript without the need for configuration - TypeScript just does this significantly better (Source: https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors). */ 'no-undef': 'off', // We need to use the extended 'no-shadow' rule from typescript: // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-shadow.md 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'off', '@typescript-eslint/no-explicit-any': ['error', { ignoreRestArgs: true }], '@typescript-eslint/naming-convention': [ 'error', { selector: 'enumMember', format: ['StrictPascalCase'], }, { selector: 'typeLike', format: ['PascalCase'], }, ], 'vue/component-tags-order': [ 'error', { order: ['script', 'template', 'style'], }, ], 'vue/script-setup-uses-vars': 'error', // Don't require a default value for the props. 'vue/require-default-prop': 'off', // Don't require multi word component names. 'vue/multi-word-component-names': 'off', // Enforce v-bind directive usage in short form as error instead of warning. 'vue/v-bind-style': ['error', 'shorthand'], // Enforce v-on directive usage in short form as error instead of warning. 'vue/v-on-style': ['error', 'shorthand'], // Enforce v-slot directive usage in short form as error instead of warning. 'vue/v-slot-style': ['error', 'shorthand'], 'no-promise-executor-return': 'off', // We have quite a lot of constant strings in our code. 'sonarjs/no-duplicate-string': 'off', // It also supresses local function returns. 'sonarjs/prefer-immediate-return': 'off', 'sonarjs/prefer-single-boolean-return': 'off', }, overrides: [ { files: ['*.js'], rules: { '@typescript-eslint/no-var-requires': 'off', 'security/detect-object-injection': 'off', 'security/detect-non-literal-fs-filename': 'off', 'security/detect-non-literal-regexp': 'off', }, }, { files: [ 'app/frontend/tests/**', 'app/frontend/**/__tests__/**', 'app/frontend/**/*.spec.*', 'app/frontend/cypress/**', '.eslint-plugin-zammad/**', '.eslintrc.js', ], rules: { 'zammad/zammad-tailwind-ltr': 'off', 'zammad/zammad-detect-translatable-string': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': 'off', }, }, // rules that require type information { files: ['*.ts', '*.tsx', '*.vue'], rules: { // handled by typescript itself with "verbatimModuleSyntax" '@typescript-eslint/consistent-type-imports': 'off', '@typescript-eslint/consistent-type-exports': 'error', 'security/detect-object-injection': 'off', 'security/detect-non-literal-fs-filename': 'off', 'security/detect-non-literal-regexp': 'off', }, parserOptions: { project: ['./tsconfig.json', './app/frontend/cypress/tsconfig.json'], }, }, ], settings: { 'import/core-modules': ['virtual:pwa-register'], 'import/parsers': { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { typescript: { alwaysTryTypes: true, }, alias: { map: [ [ 'vue-easy-lightbox/dist/external-css/vue-easy-lightbox.css', path.resolve( __dirname, 'node_modules/vue-easy-lightbox/dist/external-css/vue-easy-lightbox.css', ), ], ], extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'], }, node: { extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'], }, }, // Adding typescript file types, because airbnb doesn't allow this by default. 'import/extensions': ['.js', '.jsx', '.ts', '.tsx', '.vue'], }, globals: { defineProps: 'readonly', defineEmits: 'readonly', defineExpose: 'readonly', withDefaults: 'readonly', }, parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', // the typescript-parser for eslint, instead of tslint sourceType: 'module', // allow the use of imports statements ecmaVersion: 2020, // allow the parsing of modern ecmascript }, }