From 81ad2a4dd04388717cefbcb857ba7e3f0fff3533 Mon Sep 17 00:00:00 2001 From: Alexandre Monjol Date: Mon, 7 Sep 2026 11:31:02 +0200 Subject: [PATCH] fix(tooling): restore import guards and streamline verification --- .github/workflows/codegen.yml | 3 -- .github/workflows/linter.yml | 4 +- .github/workflows/tests.yml | 3 -- jest.config.ts | 2 +- package.json | 1 + packages/configs/eslint.config.mjs | 22 +++------ packages/configs/eslint.config.test.mjs | 46 +++++++++++++++++++ packages/design-system/vite.config.ts | 5 +- .../SubscriptionProgressiveBillingForm.tsx | 2 +- 9 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 packages/configs/eslint.config.test.mjs diff --git a/.github/workflows/codegen.yml b/.github/workflows/codegen.yml index 9033f3d2b4..83f38189b0 100644 --- a/.github/workflows/codegen.yml +++ b/.github/workflows/codegen.yml @@ -49,9 +49,6 @@ jobs: - name: Install Node.js dependencies run: pnpm install - - name: Build packages - run: pnpm prebuild - - name: Wait for API to be ready run: | timeout 5m bash -c ' diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index cba1c08eeb..72e9a825cd 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -25,6 +25,6 @@ jobs: - name: Install Node.js dependencies run: pnpm install - - name: Run code:style check + - name: Run lint, tooling, and translation checks run: | - pnpm run code:style + pnpm exec run-p lint test:tooling translations:inspect translations:ensure-consistency diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7169134ae2..4bd1906d8f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,9 +30,6 @@ jobs: - name: Install Node.js dependencies run: pnpm install - - name: Build packages - run: pnpm prebuild - - name: Run Typescript run: pnpm tsc diff --git a/jest.config.ts b/jest.config.ts index 1306264fcb..d3488c39e4 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -42,7 +42,7 @@ export default { '!src/**/*Const.ts', ], coverageReporters: ['text-summary', 'lcov'], - collectCoverage: true, + collectCoverage: false, testEnvironment: 'jsdom', diff --git a/package.json b/package.json index a7f5e39570..8e972f8dcd 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "codegen:watch": "graphql-codegen --config codegen.yml -r dotenv/config --watch", "test": "jest --config jest.config.ts", "test:coverage": "jest --config jest.config.ts --coverage --runInBand", + "test:tooling": "node --test packages/configs/eslint.config.test.mjs", "test:e2e": "cypress open --config-file cypress/cypress.config.js", "translations:add": "node scripts/translations/add", "translations:inspect": "node scripts/translations/inspect", diff --git a/packages/configs/eslint.config.mjs b/packages/configs/eslint.config.mjs index 5f354008c8..7d4c1939c9 100644 --- a/packages/configs/eslint.config.mjs +++ b/packages/configs/eslint.config.mjs @@ -1,6 +1,3 @@ -import { dirname, resolve } from 'node:path' -import { fileURLToPath } from 'node:url' - import { fixupPluginRules } from '@eslint/compat' import pluginJs from '@eslint/js' import pluginImport from 'eslint-plugin-import' @@ -10,6 +7,8 @@ import pluginReact from 'eslint-plugin-react' import pluginReactHooks from 'eslint-plugin-react-hooks' import pluginTailwind from 'eslint-plugin-tailwindcss' import globals from 'globals' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' import pluginTypescriptEslint from 'typescript-eslint' import noDirectRrdNavImport from './eslint-rules/no-direct-rrd-nav-import.js' @@ -106,22 +105,15 @@ export default [ 'no-unneeded-ternary': 'warn', 'no-duplicate-imports': 'error', - // Prevent barrel imports from large libraries (impacts bundle size and dev performance) - 'no-restricted-imports': [ + 'no-restricted-syntax': [ 'error', { - paths: [ - { - name: '@mui/material', - message: - 'Import from @mui/material/* instead. E.g., import Button from "@mui/material/Button"', - }, - ], + selector: + ":matches(ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration)[source.value='@mui/material']", + message: + 'Import from @mui/material/* instead. E.g., import Button from "@mui/material/Button"', }, ], - // Enforce slug-aware navigation wrappers (custom rule — error level, - // kept separate from `no-restricted-imports` which is also used below - // at `warn` severity for formik/dialog deprecations). 'lago/no-direct-rrd-nav-import': 'error', // Plugins diff --git a/packages/configs/eslint.config.test.mjs b/packages/configs/eslint.config.test.mjs new file mode 100644 index 0000000000..397749a11f --- /dev/null +++ b/packages/configs/eslint.config.test.mjs @@ -0,0 +1,46 @@ +import { ESLint } from 'eslint' +import assert from 'node:assert/strict' +import test from 'node:test' +import { fileURLToPath } from 'node:url' + +const eslint = new ESLint({ cwd: fileURLToPath(new URL('../../', import.meta.url)) }) +const restrictionRules = new Set([ + 'no-restricted-imports', + 'no-restricted-syntax', + 'lago/no-direct-rrd-nav-import', +]) + +const cases = [ + ['MUI named imports', "import { Button } from '@mui/material'", 2], + ['MUI namespace imports', "import * as Material from '@mui/material'", 2], + ['MUI named re-exports', "export { Button } from '@mui/material'", 2], + ['MUI star re-exports', "export * from '@mui/material'", 2], + ['direct MUI imports', "import Button from '@mui/material/Button'", undefined], + ['Formik imports', "import { useFormik } from 'formik'", 1], + [ + 'legacy dialog imports', + "import { AddCustomerDialog } from '~/components/customers/AddCustomerDialog'", + 1, + ], + [ + 'migrated dialog hooks', + "import { useAddCustomerDialog } from '~/components/customers/AddCustomerDialog'", + undefined, + ], + ['direct router navigation', "import { useNavigate } from 'react-router-dom'", 2], + ['slug-aware router navigation', "import { useNavigate } from '~/core/router'", undefined], +] + +for (const [name, source, severity] of cases) { + test(`effective import policy: ${name}`, async () => { + const [result] = await eslint.lintText(`${source}\n`, { + filePath: 'src/import-policy-probe.tsx', + }) + const restrictions = result.messages.filter(({ ruleId }) => restrictionRules.has(ruleId)) + + assert.deepEqual( + restrictions.map((message) => message.severity), + severity === undefined ? [] : [severity], + ) + }) +} diff --git a/packages/design-system/vite.config.ts b/packages/design-system/vite.config.ts index 56a7f5d9af..790c68a014 100644 --- a/packages/design-system/vite.config.ts +++ b/packages/design-system/vite.config.ts @@ -3,6 +3,8 @@ import path from 'path' import { defineConfig } from 'vite' import svgr from 'vite-plugin-svgr' +const peers = ['react', 'react-dom', '@mui/material', '@mui/x-date-pickers'] + /** * Vite configuration for building a React component library */ @@ -43,7 +45,8 @@ export default defineConfig({ '@mui/x-date-pickers': 'MaterialUIXDatePickers', }, }, - external: ['react', 'react-dom', '@mui/material', '@mui/x-date-pickers'], + external: (id: string): boolean => + peers.some((peer) => id === peer || id.startsWith(`${peer}/`)), }, cssCodeSplit: false, }, diff --git a/src/pages/subscriptions/SubscriptionProgressiveBillingForm.tsx b/src/pages/subscriptions/SubscriptionProgressiveBillingForm.tsx index d118798200..6818d25171 100644 --- a/src/pages/subscriptions/SubscriptionProgressiveBillingForm.tsx +++ b/src/pages/subscriptions/SubscriptionProgressiveBillingForm.tsx @@ -1,5 +1,5 @@ import { gql } from '@apollo/client' -import { InputAdornment } from '@mui/material' +import InputAdornment from '@mui/material/InputAdornment' import { useCallback, useMemo } from 'react' import { generatePath, useParams } from 'react-router-dom'