Skip to content

Commit 424fb0a

Browse files
authored
chore(frontend): migrate to pnpm and add lint tooling (#942)
## Summary - migrate frontend install/test/lint workflows from npm to pnpm - add eslint and stylelint workspace configuration - update local/dev docker and helper scripts for pnpm-based frontend flow - remove npm lockfile and commit pnpm lockfile This pull request migrates the frontend tooling from npm to pnpm, introduces new linting tools (ESLint and Stylelint) for the frontend, and updates related configuration and CI/CD workflows. The changes modernize dependency management, improve code quality enforcement, and streamline development and CI processes. **Frontend package management migration:** * Migrated the frontend from npm to pnpm by updating scripts in `Makefile`, `Dockerfile`, CI workflows, and `frontend/package.json`, and replacing `package-lock.json` with `pnpm-lock.yaml` in `.prettierignore`. Corepack is now used to manage pnpm installation. (`[[1]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L21-R21)`, `[[2]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L32-R51)`, `[[3]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L70-R78)`, `[[4]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52R90-R93)`, `[[5]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L113-R122)`, `[[6]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L135-R141)`, `[[7]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L8-R11)`, `[[8]](diffhunk://#diff-13bd9d7a30bf46656bc81f1ad5b908a627f9247be3f7d76df862b0578b534fc6R19-R22)`, `[[9]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR57-R70)`, `[[10]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR87-R121)`, `[[11]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR191-R208)`, `[[12]](diffhunk://#diff-1ed7245e0c7c634928b52839600c3d493c9d29150d369e9ab20979e61e7f8f97L24-R24)`, `[[13]](diffhunk://#diff-9f7d6b98294fa2c5e3b126728d626151528ac1fce8eaf8858e403b6096e86ce5L2-R2)`, `[[14]](diffhunk://#diff-da6498268e99511d9ba0df3c13e439d10556a812881c9d03955b2ef7c6c1c655L20-R49)`) **Linting and formatting improvements:** * Added ESLint and Stylelint for the frontend, including configuration files `frontend/eslint.config.js` and `frontend/stylelint.config.mjs`, and integrated them into the Makefile and CI workflows. Updated VS Code recommendations and settings to enable ESLint and auto-fix on save. (`[[1]](diffhunk://#diff-8b60be22f06ef19ea3a74fcfb99817cadbc339930dcb44aeeb7ee968098f447dR1-R38)`, `[[2]](diffhunk://#diff-ed88dcf2af5b09a17a7cee7d1c08fd560a0816b0f0e7e07f12cc073f64768d02R1-R13)`, `[[3]](diffhunk://#diff-2633572b1eb62578838877abeb39ff8053af5fa47a1a731129b889db37e59610R1-R9)`, `[[4]](diffhunk://#diff-24ad71c8613ddcf6fd23818cb3bb477a1fb6d83af4550b0bad43099813088686L11-R20)`, `[[5]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L70-R78)`, `[[6]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52R90-R93)`, `[[7]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR87-R121)`) **Dependency and tool updates:** * Updated frontend dependencies in `package.json` (including `preact`, dev tools, and linting packages), and added `stylelint` and related configs. (`[frontend/package.jsonR4-R13](diffhunk://#diff-da6498268e99511d9ba0df3c13e439d10556a812881c9d03955b2ef7c6c1c655R4-R13)`) **Project hygiene and ignore files:** * Updated `.gitignore` in the frontend to ignore `node_modules`, `dist/`, and `.astro` directories. (`[frontend/.gitignoreR1-R3](diffhunk://#diff-8e548ede50532796f028e75d8f3384d5bdb57b9fbe2bda95521774d0c46d0f97R1-R3)`) **Minor improvements:** * Improved test and build script consistency by using pnpm throughout, and made minor formatting updates (e.g., numeric literals in `frontend/vitest.config.js`). (`[frontend/vitest.config.jsL8-R9](diffhunk://#diff-211d3e0ed8954e3626dc7543c7b4bfc87e73d0b4b25abf5829a13a11eaafb788L8-R9)`) These changes collectively modernize the frontend developer experience, improve code quality, and ensure consistent dependency management across local and CI environments.
1 parent 981b24c commit 424fb0a

18 files changed

Lines changed: 5326 additions & 5835 deletions

.devcontainer/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ RUN apk add --no-cache \
1616
yaml-dev \
1717
tzdata
1818

19+
RUN if ! command -v corepack >/dev/null 2>&1; then npm install -g corepack; fi \
20+
&& corepack enable \
21+
&& corepack prepare "pnpm@latest" --activate
22+
1923
ARG USER=vscode
2024
ARG UID=1000
2125
ARG GID=1000

.devcontainer/devcontainer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@
88
"vscode": {
99
"extensions": [
1010
"rebornix.ruby",
11-
"esbenp.prettier-vscode"
11+
"esbenp.prettier-vscode",
12+
"dbaeumer.vscode-eslint"
1213
],
1314
"settings": {
1415
"editor.formatOnSave": true,
1516
"editor.defaultFormatter": "esbenp.prettier-vscode",
17+
"editor.codeActionsOnSave": {
18+
"source.fixAll.eslint": "explicit"
19+
},
20+
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
1621
"prettier.configPath": "./frontend/prettier.config.js",
1722
"ruby.format": "rubocop",
1823
"ruby.lint": { "rubocop": true },

.github/workflows/ci.yml

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,20 @@ jobs:
5454
with:
5555
bundler-cache: true
5656

57+
- name: Setup pnpm
58+
uses: pnpm/action-setup@v4
59+
with:
60+
cache: true
61+
cache_dependency_path: frontend/pnpm-lock.yaml
62+
package_json_file: frontend/package.json
63+
5764
- name: Setup Node.js for OpenAPI lint tooling
5865
uses: actions/setup-node@v6
5966
with:
6067
node-version-file: ".tool-versions"
61-
cache: npm
62-
cache-dependency-path: frontend/package-lock.json
6368

6469
- name: Install frontend dependencies for OpenAPI client verification
65-
run: npm ci
70+
run: pnpm install --frozen-lockfile
6671
working-directory: frontend
6772

6873
- name: Verify generated OpenAPI spec and client are up to date
@@ -79,33 +84,41 @@ jobs:
7984
steps:
8085
- uses: actions/checkout@v6
8186

87+
- name: Setup pnpm
88+
uses: pnpm/action-setup@v4
89+
with:
90+
cache: true
91+
cache_dependency_path: frontend/pnpm-lock.yaml
92+
package_json_file: frontend/package.json
93+
8294
- name: Setup Node.js
8395
uses: actions/setup-node@v6
8496
with:
8597
node-version-file: ".tool-versions"
86-
cache: npm
87-
cache-dependency-path: frontend/package-lock.json
8898

8999
- name: Install dependencies
90-
run: npm ci
100+
run: pnpm install --frozen-lockfile
91101

92102
- name: Typecheck frontend
93-
run: npm run typecheck
103+
run: pnpm run typecheck
104+
105+
- name: Lint CSS with Stylelint
106+
run: pnpm exec stylelint "**/*.css"
94107

95108
- name: Check formatting
96-
run: npm run format:check
109+
run: pnpm run format:check
97110

98111
- name: Audit dependencies
99-
run: npm audit --audit-level=moderate
112+
run: pnpm audit --audit-level=moderate
100113

101114
- name: Run frontend tests
102-
run: npm run test:ci
115+
run: pnpm run test:ci
103116

104117
- name: Install Playwright Chromium
105-
run: npx playwright install --with-deps chromium
118+
run: pnpm exec playwright install --with-deps chromium
106119

107120
- name: Run frontend smoke test
108-
run: npm run test:e2e
121+
run: pnpm run test:e2e
109122

110123
docker-build-smoke-image:
111124
needs:
@@ -175,19 +188,24 @@ jobs:
175188
- name: Checkout code
176189
uses: actions/checkout@v6
177190

191+
- name: Setup pnpm
192+
uses: pnpm/action-setup@v4
193+
with:
194+
cache: true
195+
cache_dependency_path: frontend/pnpm-lock.yaml
196+
package_json_file: frontend/package.json
197+
178198
- name: Setup Node.js for Docker build
179199
uses: actions/setup-node@v6
180200
with:
181201
node-version-file: ".tool-versions"
182-
cache: npm
183-
cache-dependency-path: frontend/package-lock.json
184202

185203
- name: Install frontend dependencies
186-
run: npm ci
204+
run: pnpm install --frozen-lockfile
187205
working-directory: frontend
188206

189207
- name: Build frontend static assets
190-
run: npm run build
208+
run: pnpm run build
191209
working-directory: frontend
192210

193211
- name: Set up QEMU

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ ARG NODE_BASE_IMAGE=node:22-alpine@sha256:8094c002d08262dba12645a3b4a15cd6cd627d
55
FROM ${NODE_BASE_IMAGE} AS frontend-builder
66

77
WORKDIR /app/frontend
8-
COPY frontend/package*.json ./
9-
RUN npm ci
8+
COPY frontend/package.json frontend/pnpm-lock.yaml ./
9+
RUN corepack enable && pnpm install --frozen-lockfile
1010
COPY frontend/ ./
11-
RUN npm run build
11+
RUN pnpm run build
1212

1313
# Stage 2: Ruby Build
1414
FROM ${RUBY_BASE_IMAGE} AS builder

Makefile

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ setup: ## Full development setup
1818
fi
1919
@mkdir -p tmp/rack-cache-body tmp/rack-cache-meta
2020
@echo "Setting up frontend..."
21-
@cd frontend && npm install
21+
@cd frontend && CI=1 pnpm install --frozen-lockfile
2222
@echo "Setup complete!"
2323

2424
dev: ## Start development server with live reload
@@ -29,26 +29,26 @@ dev-ruby: ## Start Ruby server only
2929
@bin/dev-ruby
3030

3131
dev-frontend: ## Start frontend dev server only
32-
@cd frontend && npm run dev
32+
@cd frontend && pnpm run dev
3333

3434
test: ## Run all tests (Ruby + Frontend)
3535
bundle exec rspec
36-
@cd frontend && npm run test:ci
36+
@cd frontend && pnpm run test:ci
3737

3838
test-ruby: ## Run Ruby tests only
3939
bundle exec rspec
4040

4141
test-frontend: ## Run frontend tests only
42-
@cd frontend && npm run test:ci
42+
@cd frontend && pnpm run test:ci
4343

4444
test-frontend-unit: ## Run frontend unit tests only
45-
@cd frontend && npm run test:unit
45+
@cd frontend && pnpm run test:unit
4646

4747
test-frontend-contract: ## Run frontend contract tests only
48-
@cd frontend && npm run test:contract
48+
@cd frontend && pnpm run test:contract
4949

5050
test-frontend-e2e: ## Run frontend Playwright smoke tests
51-
@cd frontend && npm run test:e2e
51+
@cd frontend && pnpm run test:e2e
5252

5353
check-frontend: ## Run frontend typecheck, format, and test checks
5454
$(MAKE) lint-js
@@ -67,11 +67,15 @@ lint-ruby: ## Run Ruby linter (RuboCop) - errors when issues found
6767
bundle exec rake yard:verify_public_docs
6868
@echo "Ruby linting complete!"
6969

70-
lint-js: ## Run JavaScript/Frontend linter (Prettier) - errors when issues found
70+
lint-js: ## Run JavaScript/Frontend linting (TypeScript + ESLint + Stylelint + Prettier) - errors when issues found
7171
@echo "Running TypeScript typecheck..."
72-
@cd frontend && npm run typecheck
72+
@cd frontend && pnpm run typecheck
73+
@echo "Running ESLint..."
74+
@cd frontend && pnpm run lint
75+
@echo "Running Stylelint..."
76+
@cd frontend && pnpm exec stylelint "../public/shared-ui.css" "**/*.css"
7377
@echo "Running Prettier format check..."
74-
@cd frontend && npm run format:check
78+
@cd frontend && pnpm run format:check
7579
@echo "JavaScript linting complete!"
7680

7781
lintfix: lintfix-ruby lintfix-js ## Auto-fix all linting issues (Ruby + Frontend)
@@ -83,8 +87,10 @@ lintfix-ruby: ## Auto-fix Ruby linting issues
8387
@echo "Ruby lintfix complete!"
8488

8589
lintfix-js: ## Auto-fix JavaScript/Frontend linting issues
90+
@echo "Running ESLint auto-fix..."
91+
@cd frontend && pnpm run lint:fix
8692
@echo "Running Prettier formatting..."
87-
@cd frontend && npm run format
93+
@cd frontend && pnpm run format
8894
@echo "JavaScript lintfix complete!"
8995

9096
quick-check: ## Fast local checks (Ruby lint/docs + frontend format/typecheck)
@@ -110,10 +116,10 @@ openapi-verify: ## Regenerate OpenAPI and fail if public/openapi.yaml or fronten
110116
$(MAKE) openapi-client-verify
111117

112118
openapi-client: ## Generate frontend OpenAPI client/types from public/openapi.yaml
113-
@cd frontend && npm run openapi:generate
119+
@cd frontend && pnpm run openapi:generate
114120

115121
openapi-client-verify: ## Generate frontend OpenAPI client and fail if generated files are stale
116-
@cd frontend && npm run openapi:verify
122+
@cd frontend && pnpm run openapi:verify
117123

118124
openapi-lint: openapi-lint-redocly openapi-lint-spectral ## Lint public/openapi.yaml with Redocly and Spectral
119125

@@ -132,5 +138,5 @@ clean: ## Clean temporary files
132138

133139
frontend-setup: ## Setup frontend dependencies
134140
@echo "Setting up frontend dependencies..."
135-
@cd frontend && npm install
141+
@cd frontend && CI=1 pnpm install --frozen-lockfile
136142
@echo "Frontend setup complete!"

bin/dev

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ cleanup() {
2828

2929
# Kill frontend dev server and its children
3030
if [ ! -z "$FRONTEND_PID" ]; then
31-
# Kill the npm process and its children
31+
# Kill the frontend package-manager process and its children
3232
pkill -P $FRONTEND_PID 2>/dev/null || true
3333
kill $FRONTEND_PID 2>/dev/null || true
3434
wait $FRONTEND_PID 2>/dev/null || true
@@ -79,7 +79,7 @@ fi
7979

8080
# Start frontend dev server
8181
cd frontend
82-
npm run dev &
82+
pnpm run dev &
8383
FRONTEND_PID=$!
8484

8585
# Verify frontend server started

bin/dev-with-frontend

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ echo "Starting frontend dev server with API proxy..."
4949
cd frontend
5050

5151
# Start frontend dev server (it will proxy API calls to Ruby server)
52-
npm run dev &
52+
pnpm run dev &
5353
FRONTEND_PID=$!
5454

5555
# Wait a moment for the frontend server to start

frontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist/
3+
.astro

frontend/.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Frontend package files
2-
package-lock.json
2+
pnpm-lock.yaml
33
yarn.lock
44

55
# Generated and transient frontend output

frontend/eslint.config.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import js from '@eslint/js';
2+
import globals from 'globals';
3+
import reactHooks from 'eslint-plugin-react-hooks';
4+
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
5+
import tseslint from 'typescript-eslint';
6+
7+
export default tseslint.config(
8+
{
9+
ignores: ['.astro/**', 'dist/**', 'node_modules/**', 'src/api/generated/**', 'test-results/**'],
10+
},
11+
{
12+
files: ['src/**/*.{js,jsx,ts,tsx}', 'e2e/**/*.ts', './*.{js,ts}'],
13+
extends: [
14+
js.configs.recommended,
15+
...tseslint.configs.recommended,
16+
eslintPluginUnicorn.configs.recommended,
17+
],
18+
languageOptions: {
19+
ecmaVersion: 'latest',
20+
sourceType: 'module',
21+
globals: {
22+
...globals.browser,
23+
...globals.node,
24+
...globals.vitest,
25+
},
26+
},
27+
plugins: {
28+
'react-hooks': reactHooks,
29+
},
30+
rules: {
31+
'react-hooks/rules-of-hooks': 'error',
32+
'react-hooks/exhaustive-deps': 'off',
33+
'@typescript-eslint/no-explicit-any': 'off',
34+
'unicorn/filename-case': 'off',
35+
'unicorn/better-regex': 'warn',
36+
},
37+
}
38+
);

0 commit comments

Comments
 (0)