{
    "root": true,
    "env": {
        "es2022": true,
        "commonjs": true
    },
    "extends": [
        "standard",
        "plugin:import/recommended",
        "plugin:promise/recommended",
        "plugin:n/recommended"
    ],
    "parserOptions": {
        "ecmaVersion": 2022
    },
    "ignorePatterns": ["frontend/dist/", "var/", "*.svg", "*.xml", "**/*.d.ts", "frontend/src/types/generated.ts"],
    "plugins": ["promise", "no-only-tests"],
    "settings": {
        "import/core-modules": [ "lottie-web-vue", "@heroicons/vue" ],
        "node": {
            "allowModules": [
                "flowforge-test-utils", // exists at test/node_modules/flowforge-test-utils
                "promptly",
                "lottie-web-vue",
                "@heroicons/vue"
            ]
        }
    },
    "rules": {
        // Inbuilt
        "indent": ["error", 4],
        "object-shorthand": ["error"],
        "sort-imports": [
            "error",
            {
                "ignoreDeclarationSort": true
            }
        ],
        "no-console": ["error", { "allow": ["debug", "info", "warn", "error"] }],

        // plugin:import
        "import/order": [
            "error",
            {
                "alphabetize": {
                    "order": "asc"
                },
                "newlines-between": "always-and-inside-groups"
            }
        ],
        "import/no-unresolved": "error",

        // plugin:n
        "n/file-extension-in-import": "error",
        "n/no-missing-import": "error",
        "n/no-missing-require": "error",

        // plugin:no-only-tests
        "no-only-tests/no-only-tests": "error",

        // plugin:promise
        "promise/catch-or-return": ["error", { "allowFinally": true }]
    },
    "overrides": [
        // Ignore unresolved @ imports and Node import errors only for test stores files, has issues with aliases
        {
            "files": [
                "test/unit/frontend/stores/**/*"
            ],
            "rules": {
                "import/no-unresolved": [2, { "ignore": ["^@/"] }],
                "n/no-missing-import": "off"
            }
        },
        // Frontend runs in the browser and builds with webpack
        {
            "files": "frontend/**",
            "extends": ["plugin:vue/vue3-recommended"],
            "env": {
                "browser": true,
                "commonjs": false
            },
            "parserOptions": {
                "ecmaVersion": 2022,
                "sourceType": "module"
            },
            "rules": {
                // plugin:vue
                "vue/html-indent": ["warn", 4],

                // plugin:vue - style rules
                "vue/max-attributes-per-line": "off",
                "vue/attribute-hyphenation": "off",
                "vue/singleline-html-element-content-newline": "off",
                "vue/component-definition-name-casing": "off",

                // plugin:promise
                "promise/always-return": "off", // common Vue.js pattern

                // Frontend runs through webpack, not Node — Node-based resolution rules
                // do not understand webpack aliases (@/) or .vue file resolution
                "import/no-unresolved": "off",
                "n/no-missing-import": "off",
                "n/no-missing-require": "off"
            }
        },

        // TypeScript files in the frontend — must come after the Vue/frontend override so
        // @typescript-eslint/parser wins for .ts files (later overrides take precedence)
        {
            "files": ["frontend/src/**/*.ts"],
            "parser": "@typescript-eslint/parser",
            "parserOptions": {
                "sourceType": "module"
            },
            "plugins": ["@typescript-eslint"],
            "extends": ["plugin:@typescript-eslint/recommended"],
            "rules": {
                "@typescript-eslint/no-explicit-any": "warn",
                "import/no-unresolved": "off",
                "n/no-missing-import": "off",
                "n/no-missing-require": "off"
            }
        },

        // UI components use kebab-case
        {
            "files": "frontend/src/ui-components/**",
            "rules": {
                "vue/component-definition-name-casing": ["error", "kebab-case"],
                "vue/first-attribute-linebreak": "off",
                "vue/html-self-closing": "off"
            }
        },

        // System, unit, and E2E tests run using Mocha
        {
            "files": ["test/**"],
            "env": {
                "mocha": true
            }
        },

        // Cypress is used for E2E
        {
            "files": "test/e2e/**",
            "env": {
                "cypress/globals": true
            },
            "extends": ["plugin:cypress/recommended"],
            "plugins": ["cypress"],
            "rules": {
                // plugin:cypress
                "cypress/require-data-selectors": "warn",
                "cypress/assertion-before-screenshot": "error",
                "cypress/no-force": "warn",
                "cypress/no-pause": "error",

                // plugin:n
                "promise/always-return": "off",
                "promise/catch-or-return": "off"
            }
        },

        // Front ends tests are modules
        {
            "files": ["test/unit/frontend/**", "test/e2e/frontend/**"],
            "parserOptions": {
                "sourceType": "module"
            }
        },

        //////////
        // Auto-generated rule overrides for old files that fail on newly introduced rules
        //////////
        {
            "rules": {
                "cypress/require-data-selectors": "off"
            },
            "files": [
                "test/e2e/frontend/cypress/tests/admin.spec.js",
                "test/e2e/frontend/cypress/tests/admin/instance-types.spec.js",
                "test/e2e/frontend/cypress/tests/admin/stacks.spec.js",
                "test/e2e/frontend/cypress/tests/admin/templates.spec.js",
                "test/e2e/frontend/cypress/tests/applications.spec.js",
                "test/e2e/frontend/cypress/tests/auth.spec.js",
                "test/e2e/frontend/cypress/tests/devices.spec.js",
                "test/e2e/frontend/cypress/tests/instances.spec.js",
                "test/e2e/frontend/cypress/tests/instances/devices.spec.js",
                "test/e2e/frontend/cypress/tests/instances/snapshots.spec.js",
                "test/e2e/frontend/cypress/tests/instances/staging.spec.js",
                "test/e2e/frontend/cypress/tests/invitations.spec.js",
                "test/e2e/frontend/cypress/tests/team/team.spec.js",
                "test/e2e/frontend/cypress/tests/team/audit-log.spec.js",
                "test/e2e/frontend/cypress/tests/team/team-membership.spec.js",
                "test/e2e/frontend/cypress/tests/terms-and-conditions.spec.js"
            ]
        },
        {
            "rules": {
                "promise/catch-or-return": "off"
            },
            "files": [
                "frontend/src/pages/instance/Overview.vue"
            ]
        },
        {
            "rules": {
                "vue/attributes-order": "off"
            },
            "files": [
                "frontend/src/components/auth/UpdateExpiredPassword.vue",
                "frontend/src/components/FormRow.vue",
                "frontend/src/components/NavItem.vue",
                "frontend/src/components/PageHeader.vue",
                "frontend/src/components/SectionTopMenu.vue",
                "frontend/src/components/TeamSelection.vue",
                "frontend/src/pages/device/Overview.vue",
                "frontend/src/pages/device/Settings/Danger.vue",
                "frontend/src/pages/device/Settings/dialogs/ConfirmDeviceDeleteDialog.vue",
                "frontend/src/pages/device/Settings/Environment.vue",
                "frontend/src/pages/device/Settings/General.vue",
                "frontend/src/pages/team/components/MemberSummaryList.vue",
                "frontend/src/pages/team/components/ProjectSummaryList.vue",
                "frontend/src/pages/team/Devices/dialogs/CreateProvisioningTokenDialog.vue"
            ]
        },
        {
            "rules": {
                "vue/component-definition-name-casing": "off"
            },
            "files": [
                "frontend/src/components/Accordion.vue",
                "frontend/src/components/CodePreviewer.vue",
                "frontend/src/components/Loading.vue",
                "frontend/src/layouts/Box.vue",
                "frontend/src/layouts/Platform.vue",
                "frontend/src/main.js"
            ]
        },
        {
            "rules": {
                "vue/order-in-components": "off"
            },
            "files": [
                "frontend/src/components/audit-log/AuditEntry.vue",
                "frontend/src/components/audit-log/AuditEntryIcon.vue",
                "frontend/src/components/audit-log/AuditEntryVerbose.vue",
                "frontend/src/components/audit-log/AuditLog.vue",
                "frontend/src/components/auth/UpdateExpiredPassword.vue",
                "frontend/src/components/DropdownMenu.vue",
                "frontend/src/components/FormRow.vue",
                "frontend/src/components/PageHeader.vue",
                "frontend/src/components/SectionTopMenu.vue",
                "frontend/src/components/tables/cells/InviteUserCell.vue",
                "frontend/src/components/TeamSelection.vue",
                "frontend/src/pages/device/components/DeviceLastSeenBadge.vue",
                "frontend/src/pages/device/Overview.vue",
                "frontend/src/pages/device/Settings/Danger.vue",
                "frontend/src/pages/device/Settings/dialogs/ConfirmDeviceDeleteDialog.vue",
                "frontend/src/pages/device/Settings/Environment.vue",
                "frontend/src/pages/device/Settings/General.vue",
                "frontend/src/pages/device/Settings/index.vue",
                "frontend/src/pages/team/components/LibraryEntryTypeIcon.vue",
                "frontend/src/pages/team/components/MemberSummaryList.vue",
                "frontend/src/pages/team/components/ProjectSummaryList.vue",
                "frontend/src/pages/team/components/TeamUserEditButton.vue",
                "frontend/src/pages/team/create.vue",
                "frontend/src/pages/team/Devices/dialogs/CreateProvisioningTokenDialog.vue",
                "frontend/src/pages/team/Devices/dialogs/DeviceCredentialsDialog.vue",
                "frontend/src/pages/team/Devices/dialogs/ProvisioningCredentialsDialog.vue",
                "frontend/src/pages/team/dialogs/ChangeTeamRoleDialog.vue",
                "frontend/src/pages/team/dialogs/ConfirmTeamDeleteDialog.vue",
                "frontend/src/pages/team/dialogs/ConfirmTeamUserRemoveDialog.vue"
            ]
        },
        {
            "rules": {
                "vue/require-prop-types": "off"
            },
            "files": [
                "frontend/src/components/DropdownMenu.vue",
                "frontend/src/components/FormRow.vue",
                "frontend/src/components/SectionSideMenu.vue",
                "frontend/src/components/SideNavigation.vue",
                "frontend/src/components/tables/cells/InviteUserCell.vue",
                "frontend/src/components/tables/cells/TeamCell.vue",
                "frontend/src/components/tables/cells/TeamTypeCell.vue",
                "frontend/src/components/tables/cells/UserCell.vue",
                "frontend/src/components/tables/cells/UserRoleCell.vue",
                "frontend/src/pages/application/Debug.vue",
                "frontend/src/pages/device/components/DeviceLastSeenBadge.vue",
                "frontend/src/pages/device/Overview.vue",
                "frontend/src/pages/device/Settings/Danger.vue",
                "frontend/src/pages/device/Settings/Environment.vue",
                "frontend/src/pages/device/Settings/General.vue",
                "frontend/src/pages/device/Settings/index.vue",
                "frontend/src/pages/instance/components/ExportInstanceComponents.vue",
                "frontend/src/pages/instance/components/ImportInstanceComponents.vue",
                "frontend/src/pages/team/components/MemberSummaryList.vue",
                "frontend/src/pages/team/components/ProjectSummaryList.vue",
                "frontend/src/pages/team/components/ProjectTypeSummary.vue",
                "frontend/src/pages/team/components/TeamUserEditButton.vue",
                "frontend/src/pages/team/Devices/dialogs/CreateProvisioningTokenDialog.vue",
                "frontend/src/pages/team/Devices/dialogs/DeviceCredentialsDialog.vue",
                "frontend/src/pages/team/Devices/dialogs/ProvisioningCredentialsDialog.vue"
            ]
        }
    ]
}
