Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Browsers the app supports (also drives the esbuild output target).
# Without this, browserslist falls back to `defaults`, which pulls in pre-2020
# engines (Opera Mini, KaiOS, old UC) that lack top-level await — breaking the
# Angular unit-test build. This is the standard Angular CLI matrix.
last 2 Chrome versions
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CI

# Run lint + unit tests on every pull request targeting master.
on:
pull_request:
branches: [master]

# Cancel an in-progress run when the PR gets new commits.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
test:
name: Lint & test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

# pnpm must be installed before setup-node so its cache can resolve the store.
# Version comes from the "packageManager" field in package.json.
- name: Set up pnpm
uses: pnpm/action-setup@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

# Formatting / linting gate — ESLint flat config (eslint.config.mjs).
# Fails the build on errors; tracked `no-explicit-any` warnings do not fail it.
- name: Lint & format check
run: pnpm lint

# Unit tests run headless in Node via Vitest (jsdom) — no browser required.
# Invoke ng directly so --watch=false isn't swallowed by the npm-script `--`.
- name: Run unit tests
run: pnpm exec ng test --watch=false
30 changes: 9 additions & 21 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
},
"ci": {
"progress": false
},
"test": {
"polyfills": [
"zone.js",
"zone.js/testing"
]
}
}
},
Expand Down Expand Up @@ -97,32 +103,14 @@
}
},
"test": {
"builder": "@angular/build:karma",
"builder": "@angular/build:unit-test",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"buildTarget": "app:build:test",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [],
"scripts": [],
"assets": [
{
"glob": "favicon.ico",
"input": "src/",
"output": "/"
},
{
"glob": "**/*",
"input": "src/assets",
"output": "/assets"
},
"src/manifest.json",
"src/manifest.webmanifest"
]
"runner": "vitest"
},
"configurations": {
"ci": {
"progress": false,
"watch": false
}
}
Expand Down
48 changes: 18 additions & 30 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// @ts-check
// Flat ESLint config (ESLint 10 + angular-eslint 21).
// Uses the individual angular-eslint / typescript-eslint plugins directly,
// since the `angular-eslint` / `typescript-eslint` meta-packages are not installed.
import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import templateParser from '@angular-eslint/template-parser';
// Flat ESLint config (ESLint 10 + angular-eslint 22).
// angular-eslint 22 moved its rule presets out of the @angular-eslint/* plugins
// and into the `angular-eslint` meta-package, so the recommended rule sets are
// pulled from there (and from `typescript-eslint`) via the tseslint.config helper.
import tseslint from 'typescript-eslint';
import angular from 'angular-eslint';

export default [
export default tseslint.config(
{
// src/assets holds the static Harvard Classics reading content (raw HTML,
// not Angular templates) — never lint it.
Expand All @@ -17,19 +15,14 @@ export default [
// TypeScript source files.
{
files: ['**/*.ts'],
languageOptions: {
parser: tsParser,
sourceType: 'module',
},
plugins: {
'@typescript-eslint': tsPlugin,
'@angular-eslint': angular,
},
extends: [
...tseslint.configs.recommended,
...angular.configs.tsRecommended,
],
processor: angular.processInlineTemplates,
rules: {
...tsPlugin.configs.recommended.rules,
...angular.configs.recommended.rules,
// This app is intentionally NgModule-based with constructor DI (see CLAUDE.md);
// these v21 defaults push the opposite architecture, so they're disabled here.
// these defaults push the opposite architecture, so they're disabled here.
'@angular-eslint/prefer-standalone': 'off',
'@angular-eslint/prefer-inject': 'off',
// `any` is pervasive and tracked as a Known Issue — surface it without blocking lint.
Expand All @@ -39,14 +32,9 @@ export default [
// Angular HTML templates.
{
files: ['**/*.html'],
languageOptions: {
parser: templateParser,
},
plugins: {
'@angular-eslint/template': angularTemplate,
},
rules: {
...angularTemplate.configs.recommended.rules,
},
extends: [
...angular.configs.templateRecommended,
],
rules: {},
},
];
);
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
"zone.js": "~0.16.2"
},
"devDependencies": {
"@angular/build": "^22.0.1",
"@angular-eslint/builder": "22.0.0",
"@angular-eslint/eslint-plugin": "22.0.0",
"@angular-eslint/eslint-plugin-template": "22.0.0",
"@angular-eslint/schematics": "22.0.0",
"@angular-eslint/template-parser": "22.0.0",
"@angular/build": "^22.0.1",
"@angular/cli": "~22.0.1",
"@angular/compiler": "~22.0.1",
"@angular/compiler-cli": "~22.0.1",
Expand All @@ -55,16 +55,20 @@
"@types/node": "~25.9.3",
"@typescript-eslint/eslint-plugin": "^8.61",
"@typescript-eslint/parser": "^8.61",
"angular-eslint": "^22.0.0",
"eslint": "^10.5.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^10.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-react": "^7.37.5",
"jsdom": "^29.1.1",
"prettier": "^3.8.4",
"ts-node": "~10.9.2",
"typescript": "^6.0.3"
"typescript": "^6.0.3",
"typescript-eslint": "^8.61.0",
"vitest": "^4.1.8"
},
"packageManager": "pnpm@11.7.0"
}
Loading
Loading