Assist_Design/eslint.config.mjs
T. Narantuya 59cf55ae95 Refactor layout components and enforce import restrictions in portal pages
- Removed direct imports of DashboardLayout from various portal pages to enforce the use of shared layout components.
- Introduced ESLint rules to prevent importing DashboardLayout directly in (portal) pages, encouraging the use of the shared route-group layout.
- Updated navigation methods to utilize Next.js router instead of window.location for improved routing consistency.
- Enhanced loading states and error handling across multiple pages for better user experience.
2025-09-11 14:23:33 +09:00

177 lines
5.0 KiB
JavaScript

/* eslint-env node */
import process from "node:process";
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import prettier from "eslint-plugin-prettier";
import { FlatCompat } from "@eslint/eslintrc";
import path from "node:path";
import globals from "globals";
// Use FlatCompat to consume Next.js' legacy shareable configs under apps/portal
const compat = new FlatCompat({ baseDirectory: path.resolve("apps/portal") });
export default [
// Global ignores
{
ignores: [
"**/node_modules/**",
"**/dist/**",
"**/.next/**",
"**/build/**",
"**/coverage/**",
"**/next-env.d.ts",
],
},
// Base JS recommendations for all files
js.configs.recommended,
// Prettier integration (warn-only)
{
plugins: { prettier },
rules: {
"prettier/prettier": "warn",
},
},
// TypeScript (type-checked) for TS files only
...tseslint.configs.recommendedTypeChecked.map(config => ({
...config,
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
...(config.languageOptions || {}),
globals: {
...globals.node,
},
},
})),
{
files: ["apps/bff/**/*.ts", "packages/shared/**/*.ts"],
languageOptions: {
parserOptions: {
// Enable project service for monorepos without per-invocation project config
projectService: true,
tsconfigRootDir: process.cwd(),
},
},
rules: {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"no-console": ["warn", { allow: ["warn", "error"] }],
},
},
// Enforce consistent strict rules across shared as well
{
files: ["packages/shared/**/*.ts"],
rules: {
"@typescript-eslint/no-redundant-type-constituents": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
},
},
// Next.js app: apply Next's recommended config; TS rules only on TS files
...compat
.extends("next/core-web-vitals")
.map(config => ({ ...config, files: ["apps/portal/**/*.{js,jsx,ts,tsx}"] })),
...compat
.extends("next/typescript")
.map(config => ({ ...config, files: ["apps/portal/**/*.{ts,tsx}"] })),
// Ensure type-aware rules in portal have parser services
{
files: ["apps/portal/**/*.{ts,tsx}"],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: process.cwd(),
},
},
rules: {
// App Router: disable pages-directory specific rule
"@next/next/no-html-link-for-pages": "off",
},
},
// Prevent importing the DashboardLayout directly in (portal) pages.
// Pages should rely on the shared route-group layout at (portal)/layout.tsx.
{
files: ["apps/portal/src/app/(portal)/**/*.{ts,tsx}"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: [
{
group: ["@/components/layout/dashboard-layout"],
message:
"Use the shared (portal)/layout.tsx instead of importing DashboardLayout in pages.",
},
],
},
],
// Prefer Next.js <Link> and router, forbid window/location hard reload in portal pages
"no-restricted-syntax": [
"error",
{
selector:
"MemberExpression[object.name='window'][property.name='location']",
message:
"Use next/link or useRouter for navigation, not window.location.",
},
{
selector:
"MemberExpression[object.name='location'][property.name=/^(href|assign|replace)$/]",
message:
"Use next/link or useRouter for navigation, not location.*.",
},
],
},
},
// Allow the shared layout file itself to import the layout component
{
files: ["apps/portal/src/app/(portal)/layout.tsx"],
rules: {
"no-restricted-imports": "off",
},
},
// Allow controlled window.location usage for invoice SSO download
{
files: ["apps/portal/src/app/(portal)/billing/invoices/[id]/page.tsx"],
rules: {
"no-restricted-syntax": "off",
},
},
// Node globals for Next config file
{
files: ["apps/portal/next.config.mjs"],
languageOptions: {
globals: {
...globals.node,
},
},
},
// BFF: strict rules enforced
{
files: ["apps/bff/**/*.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/require-await": "error",
"@typescript-eslint/no-floating-promises": "error",
},
},
];