diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7f01a3b..7366c9e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: jobs: build: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 @@ -19,6 +20,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} # Only need a cache hit or miss, # cached dependencies aren't used until following jobs @@ -51,4 +53,5 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ steps.restore-build-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4c89a0a9..3cdf1576 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,6 +13,7 @@ on: jobs: deploy: runs-on: ubuntu-22.04 + timeout-minutes: 10 environment: name: ${{ inputs.environment }} url: ${{ inputs.url }} @@ -29,6 +30,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} fail-on-cache-miss: true diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 1fbbe15b..9294c0a1 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -6,6 +6,7 @@ on: jobs: e2e-test: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 @@ -19,6 +20,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} fail-on-cache-miss: true diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index e6129390..83680322 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -6,6 +6,7 @@ on: jobs: install: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3b94f162..185da017 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,6 +6,7 @@ on: jobs: lint: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 @@ -19,6 +20,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} fail-on-cache-miss: true diff --git a/.github/workflows/type-check.yml b/.github/workflows/type-check.yml index c5017cf4..524481ec 100644 --- a/.github/workflows/type-check.yml +++ b/.github/workflows/type-check.yml @@ -6,6 +6,7 @@ on: jobs: type-check: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 @@ -19,6 +20,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} fail-on-cache-miss: true diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index e6b186b6..9719699b 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -6,6 +6,7 @@ on: jobs: unit-test: runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - uses: actions/checkout@v4 @@ -19,6 +20,7 @@ jobs: */node_modules */dist client/lib + components/lib key: ${{ runner.os }}-build-${{ hashFiles('common/config/rush/repo-state.json', '.github/workflows/ci-salt.txt') }} fail-on-cache-miss: true diff --git a/.vscode/settings.json b/.vscode/settings.json index f4c402a0..227e8462 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "eslint.workingDirectories": [ "client", + "components", "deploy", "discord", "eslint-config-jest", @@ -10,7 +11,8 @@ "eslint-config", "jest-stdout-reporter", "markdownlint-config", - "scaffold" + "scaffold", + "typescript" ], "files.associations": { "rush.json": "jsonc", diff --git a/client/babel.config.js b/client/babel.config.js index ef65177f..8e0ee1e6 100644 --- a/client/babel.config.js +++ b/client/babel.config.js @@ -2,21 +2,20 @@ export default { env: { test: { plugins: [ + // Jest still expects CommonJS '@babel/plugin-transform-modules-commonjs', ], }, }, - only: [ - // Only files in src/ are built & transpiled by Babel - './src', - // jest-setup.ts is the only .ts module in the package root, - // needs to be transformed by Babel for Jest to understand it - './jest-setup.ts', - ], - - plugins: [ - '@babel/plugin-syntax-dynamic-import', + /** + * Most files built & transpiled by Babel are just in src/ + * but other local projects and some config file extensions may also need processing: + * i.e. @trshcmpctr/components, jest-setup.ts + */ + ignore: [ + 'common/temp/node_modules/.pnpm', + 'node_modules', ], presets: [ @@ -38,4 +37,8 @@ export default { } ], ], + + targets: { + browsers: 'defaults', + }, }; diff --git a/client/babel.config.lib.js b/client/babel.config.lib.js index 87c385a4..fa54bf92 100644 --- a/client/babel.config.lib.js +++ b/client/babel.config.lib.js @@ -4,6 +4,7 @@ * Differences from babel.config.js: * Does not need dynamic imports * Does not need preset-env + corejs + * Does not need to target browsers */ export default { only: [ @@ -18,4 +19,8 @@ export default { } ], ], + targets: { + // Compile against the current node version + node: 'current', + }, }; diff --git a/client/cypress/tsconfig.json b/client/cypress/tsconfig.json index 014f1083..a5c06f85 100644 --- a/client/cypress/tsconfig.json +++ b/client/cypress/tsconfig.json @@ -1,13 +1,36 @@ { - "extends": "../tsconfig.base.json", + // Cypress throws and hangs if using "extends" with a package reference. + // Issue https://github.com/cypress-io/cypress/issues/28385 + // supposedly fixed in Cypress 13.6.2 but reproducible here with: + // - Cypress 13.17.0 + // - Node 20.19.5 + // - Typescript 5.9.3 + // "extends": "@trshcmpctr/typescript", "compilerOptions": { + // Treat `import x from 'y'` as `import * as x from 'y'` + // when y has no default export + "esModuleInterop": true, + + // Apply stricter rules around properties which have a ? prefix + "exactOptionalPropertyTypes": true, + + // Enforce file case sensitivity + "forceConsistentCasingInFileNames": true, + // Core definitions + DOM definitions "lib": [ "es5", "dom" ], + // Ignore inconsistent global definitions, i.e. from Jest and Cypress + "skipLibCheck": true, + + // Turn on all strict mode family options for + // "stronger guarantees of program correctness" + "strict": true, + // Downlevel for Cypress test runner (?) "target": "es5", diff --git a/client/jest.config.js b/client/jest.config.js index 39034b56..bac5cb91 100644 --- a/client/jest.config.js +++ b/client/jest.config.js @@ -1,26 +1,18 @@ export default { - rootDir: './', - transform: { - // Only files in src/ are built & transpiled by Babel, so only these should be transformed. - // Note that the transform pattern is matched against the full path. - // This creates some trouble in CI because Drone's default workspace already includes src/ (/drone/src). - // To workaround this, include the package directory in the transform pattern (/client/src) - // as a defense against transforming unintended files. - // Unfortunately, we can't use in this pattern. - '\\/client\\/src\\/.+\\.[t|j]sx?$': [ - 'babel-jest', - ], - // jest-setup.ts needs to be .ts to include types for - // extended Jest matchers from @testing-library/jest-dom - '\\/client\\/jest-setup\\.ts$': [ - 'babel-jest', - ], - }, collectCoverage: true, - testEnvironment: 'jsdom', - setupFilesAfterEnv: ['/jest-setup.ts'], moduleNameMapper: { // See https://jestjs.io/docs/webpack#handling-static-assets '\\.(css|less)$': '/__mocks__/styleMock.cjs', }, + setupFilesAfterEnv: ['/jest-setup.ts'], + testEnvironment: 'jsdom', + /** + * Most files built & transpiled by Babel are just in src/ + * but other local projects and some config file extensions may also need processing: + * i.e. @trshcmpctr/components, jest-setup.ts + */ + transformIgnorePatterns: [ + 'common/temp/node_modules/.pnpm', + 'node_modules', + ], }; diff --git a/client/package.json b/client/package.json index 607bb255..b3f1fe0e 100644 --- a/client/package.json +++ b/client/package.json @@ -41,6 +41,8 @@ "watch": "webpack --watch" }, "dependencies": { + "@layoutit/voxcss": "~0.1.8", + "@trshcmpctr/components": "workspace:*", "axios": "~1.13.2", "core-js": "~3.47.0", "prop-types": "~15.8.1", @@ -50,7 +52,6 @@ }, "devDependencies": { "@babel/core": "~7.28.5", - "@babel/plugin-syntax-dynamic-import": "~7.8.3", "@babel/plugin-transform-modules-commonjs": "~7.27.1", "@babel/preset-env": "~7.28.5", "@babel/preset-react": "~7.28.5", @@ -58,6 +59,7 @@ "@testing-library/dom": "~10.4.0", "@testing-library/jest-dom": "~6.9.1", "@testing-library/react": "~16.3.0", + "@testing-library/user-event": "~14.6.1", "@trshcmpctr/eslint-config": "workspace:*", "@trshcmpctr/eslint-config-jest": "workspace:*", "@trshcmpctr/eslint-config-node": "workspace:*", @@ -65,6 +67,7 @@ "@trshcmpctr/eslint-config-typescript": "workspace:*", "@trshcmpctr/jest-stdout-reporter": "workspace:*", "@trshcmpctr/markdownlint-config": "workspace:*", + "@trshcmpctr/typescript": "workspace:*", "@types/jest": "~30.0.0", "@types/node": "~20.19.25", "@types/prop-types": "~15.7.12", @@ -72,7 +75,6 @@ "@types/react-dom": "~19.2.3", "babel-jest": "~30.2.0", "babel-loader": "~10.0.0", - "babel-plugin-syntax-dynamic-import": "~6.18.0", "css-loader": "~7.1.1", "cypress": "13.17.0", "discord-api-types": "~0.38.33", diff --git a/client/src/App/App.tsx b/client/src/App/App.tsx index 52867ea8..d2a375e2 100644 --- a/client/src/App/App.tsx +++ b/client/src/App/App.tsx @@ -3,6 +3,8 @@ import { createBrowserRouter, RouterProvider } from 'react-router'; import './App.css'; +import { Flex } from '@trshcmpctr/components'; + import { ErrorBoundary } from './components/ErrorBoundary'; import { Footer } from './components/Footer'; import { Header } from './components/Header'; @@ -14,21 +16,27 @@ export const App = () => { return ( -
-
-
- -
-