diff --git a/.github/workflows/frontend_lint_and_test.yml b/.github/workflows/frontend_lint_and_test.yml deleted file mode 100644 index 111d11f3b..000000000 --- a/.github/workflows/frontend_lint_and_test.yml +++ /dev/null @@ -1,80 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Frontend -permissions: - contents: read - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: "frontend" - strategy: - matrix: - node-version: [20.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f #v6 - with: - node-version: ${{ matrix.node-version }} - cache: "npm" - cache-dependency-path: "**/package-lock.json" - - run: npm ci - - run: npm run build - - run: npm test - env: - PORT: 3001 - VITE_AI_CONNECTION_STRING: "" - VITE_BACKEND_URL: https://localhost:8000 - VITE_BACKEND_API_SCOPE: api://ea4c7b92-47b3-45fb-bd25-a8070f0c495c/user_impersonation - VITE_FRONTEND_URL: http://localhost:3001 - VITE_FRONTEND_BASE_ROUTE: "" - VITE_AD_CLIENT_ID: f5993820-b7e2-4791-886f-f9f5027dc7be - VITE_AD_TENANT_ID: 3aa4a235-b6e2-48d5-9195-7fcf05b459b0 - - check_formatting: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./frontend - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6 - - - name: Run Prettier - run: npm run prettier_check - - run_eslint: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./frontend - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6 - - name: Install modules - run: npm ci - - name: Run ESLint - run: npx eslint src - - check_exports: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./frontend - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6 - - name: Install modules - run: npm ci - - name: Run Unused Exports Check - run: ./node_modules/.bin/ts-unused-exports tsconfig.json diff --git a/.github/workflows/lint_frontend_package.yml b/.github/workflows/lint_frontend_package.yml new file mode 100644 index 000000000..408b74043 --- /dev/null +++ b/.github/workflows/lint_frontend_package.yml @@ -0,0 +1,22 @@ +name: Frontend + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + lint-frontend-package: + uses: equinor/armada/.github/workflows/lint_frontend_package.yml@main + permissions: + contents: read + with: + working-directory: frontend + run-prettier: true + run-eslint: true + run-build: true + run-unused-exports: true diff --git a/.gitignore b/.gitignore index 0bfd8dcb8..c2ee2cfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Frontend frontend/node_modules/ frontend/dist/ +frontend/out/ # Editors .vscode diff --git a/frontend/Makefile b/frontend/Makefile index 29af37a8e..4a54b6541 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -1,5 +1,5 @@ run: - npm start + npm run dev build: npm run build diff --git a/frontend/README.md b/frontend/README.md index e4029335b..ffc7b6899 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -30,7 +30,7 @@ npm ci To start the app, run the following command in the root folder: ``` -npm start +npm run dev ``` This command runs the app in development mode. Open [http://localhost:3001/robotics-frontend](http://localhost:3001/robotics-frontend) to view it in the browser. diff --git a/frontend/best_practices.md b/frontend/best_practices.md index 51cf085db..94654b542 100644 --- a/frontend/best_practices.md +++ b/frontend/best_practices.md @@ -29,7 +29,7 @@ npx prettier --write [path to source] We also avoid any warnings or errors from ESLint before we merge in any code. These warnings appear when compiling the code using -npm start +npm run dev but can also be run with npx eslint [path to src] diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 2a8f45e10..0fa205ecb 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,21 +1,33 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; +import js from '@eslint/js' +import globals from 'globals' +import tseslint from 'typescript-eslint' +import pluginReact from 'eslint-plugin-react' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2024, + globals: globals.browser, + }, + plugins: { + react: pluginReact, + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...pluginReact.configs.flat.recommended.rules, -/** @type {import('eslint').Linter.Config[]} */ -export default [ - {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, - {languageOptions: { globals: globals.browser }}, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - pluginReact.configs.flat.recommended, - { - rules: { - "react/react-in-jsx-scope": "off", - "react/display-name": "off", - "@typescript-eslint/no-explicit-any": "off", + 'react/react-in-jsx-scope': 'off', + 'react/display-name': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + }, } - } -]; +) diff --git a/frontend/index.html b/frontend/index.html index a29f2c2ea..44ae6bdd0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -35,7 +35,7 @@ You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the tag. - To begin the development, run `npm start` or `yarn start`. - To create a production bundle, use `npm run build` or `yarn build`. + To begin the development, run `npm run dev`. + To create a production bundle, use `npm run build`. --> diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e97feebaa..da85a2d12 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -44,6 +44,8 @@ "@types/styled-components": "^5.1.36", "eslint": "^9.39.3", "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^6.0.0", + "eslint-plugin-react-refresh": "^0.4.26", "globals": "^17.4.0", "jsdom": "^28.1.0", "ts-unused-exports": "^11.0.1", @@ -4360,6 +4362,33 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.1.tgz", + "integrity": "sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "zod": "^3.22.4 || ^4.0.0", + "zod-validation-error": "^3.0.3 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "peerDependencies": { + "eslint": ">=8.40" + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -8036,6 +8065,27 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } } } diff --git a/frontend/package.json b/frontend/package.json index 83aa13abe..b5b8e8bb2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,10 +34,11 @@ "vite-plugin-svgr": "^4.5.0" }, "scripts": { - "start": "vite", - "build": "tsc && vite build", - "serve": "vite preview", + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", "test": "vitest", + "lint": "eslint src", "prettier_check": "npx prettier --check src" }, "browserslist": { @@ -60,6 +61,8 @@ "@types/styled-components": "^5.1.36", "eslint": "^9.39.3", "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^6.0.0", + "eslint-plugin-react-refresh": "^0.4.26", "globals": "^17.4.0", "jsdom": "^28.1.0", "ts-unused-exports": "^11.0.1", diff --git a/frontend/src/components/Contexts/InspectionsContext.tsx b/frontend/src/components/Contexts/InspectionsContext.tsx index f13fa533b..185294110 100644 --- a/frontend/src/components/Contexts/InspectionsContext.tsx +++ b/frontend/src/components/Contexts/InspectionsContext.tsx @@ -62,6 +62,7 @@ export const InspectionsProvider: FC = ({ children }) => { }, [registerEvent, connectionReady]) const fetchImageData = (inspectionId: string): IImageData => { + // eslint-disable-next-line react-hooks/rules-of-hooks -- pre-existing design issue, tracked in #2698 const result = useQuery({ queryKey: ['fetchInspectionData', inspectionId], queryFn: async () => { @@ -77,6 +78,7 @@ export const InspectionsProvider: FC = ({ children }) => { } const fetchAnalysisData = (inspectionId: string): IImageData => { + // eslint-disable-next-line react-hooks/rules-of-hooks -- pre-existing design issue, tracked in #2698 const result = useQuery({ queryKey: ['fetchAnalysisData', inspectionId], queryFn: async () => { @@ -91,6 +93,7 @@ export const InspectionsProvider: FC = ({ children }) => { } const fetchValueData = (inspectionId: string): IValueData => { + // eslint-disable-next-line react-hooks/rules-of-hooks -- pre-existing design issue, tracked in #2698 const result = useQuery({ queryKey: ['fetchValueData', inspectionId], queryFn: async () => {