Skip to content
Open
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
42 changes: 42 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"root": true,
"ignorePatterns": ["projects/**/*", "dist/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.app.json", "tsconfig.spec.json"],
"createDefaultProgram": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended", "plugin:@angular-eslint/template/accessibility"],
"rules": {}
}
]
}
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CI

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
name: Lint, Test & Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Unit tests
run: npm run test:ci

- name: Production build
run: npm run build

deploy:
name: Deploy to Firebase Hosting
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Production build
run: npm run build

- name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: live
projectId: angular2-hn
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
/dist
/tmp
/out-tsc
# Angular CLI cache
/.angular/cache
# Only exists if Bazel was run
/bazel-out

Expand Down Expand Up @@ -31,6 +33,11 @@ speed-measure-plugin.json
!.vscode/extensions.json
.history/*

# e2e (Playwright)
/test-results
/playwright-report
/playwright/.cache

# misc
/.sass-cache
/connect.lock
Expand Down
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

34 changes: 25 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

<p align="center">
A progressive Hacker News client built with Angular
A progressive Hacker News client built with Angular (modernized to Angular 17)
</p>

<p align="center">
Expand All @@ -14,7 +14,6 @@

<p align="center">
<a href="/CONTRIBUTING.md"><img alt="PRs Welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"></a>
<a href="https://travis-ci.org/housseindjirdeh/angular2-hn"><img alt="Build Status" src="https://travis-ci.org/housseindjirdeh/angular2-hn.svg?branch=master"></a>
</p>

---
Expand All @@ -41,9 +40,21 @@
<img src = "http://i.imgur.com/MrKHaln.gif">
</p>

## Tech Stack

This project was modernized from Angular 9 to a current Angular stack:

- **Angular 17** with standalone components, `bootstrapApplication`, and the new control flow syntax (`@if` / `@for`)
- **TypeScript 5.4** and **RxJS 7.8**
- **esbuild-based** application builder (`@angular-devkit/build-angular:application`)
- **ESLint** (`@angular-eslint`) for linting (replacing TSLint)
- **Karma + Jasmine** for unit tests
- **Playwright** for end-to-end tests (replacing Protractor)
- **GitHub Actions** for CI/CD (replacing Travis CI)

## Offline Support

This app uses [Workbox](https://workboxjs.org/) to generate a service worker as part of the build step to load quickly and work offline.
This app uses [`@angular/service-worker`](https://angular.dev/ecosystem/service-workers) to generate a service worker as part of the production build so it loads quickly and works offline. The worker is configured in `ngsw-config.json` and registered via `provideServiceWorker()` in `src/app/app.config.ts`.

## Manifest

Expand Down Expand Up @@ -73,16 +84,21 @@ Feel free to send me feedback on [twitter](https://twitter.com/hdjirdeh) or [fil

## Build process

Note: This project has been ejected (with AOT + production settings) in order to customize Webpack configurations.
Requires **Node.js 18.13+ or 20.9+** and npm.

- Clone or download the repo
- `npm install`
- `npm start` to run the application with webpack-dev-server or `npm build` to kick off a fresh build and update the output directory (`dist/`)
- `npm start` to run the dev server at `http://localhost:4200`
- `npm run build` to produce an optimized production build in `dist/angular-hnpwa/`

### Other scripts

- `npm run lint` &mdash; run ESLint over the TypeScript and HTML templates
- `npm test` &mdash; run unit tests in watch mode (Karma + Jasmine)
- `npm run test:ci` &mdash; run unit tests once in headless Chrome
- `npm run e2e` &mdash; run the Playwright end-to-end tests (boots the dev server automatically)

Note: Any Service Worker changes will not be reflected when you run the application locally in development. To test service worker changes:
- `npm build`
- `npm run precache` to generate the service worker file
- `npm run static-serve` to load the application along with the service worker asset using [live-server](https://github.com/tapio/live-server)
The service worker is only enabled in production builds. To exercise it locally, build the app and serve `dist/angular-hnpwa/browser` with any static file server.

## Contributors

Expand Down
87 changes: 33 additions & 54 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"builder": "@angular-devkit/build-angular:application",
"options": {
"aot": true,
"outputPath": "dist/angular-hnpwa",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
"src/manifest.json",
"src/manifest.webmanifest"
],
"styles": [
Expand All @@ -36,21 +37,6 @@
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": true,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
Expand All @@ -62,35 +48,45 @@
"maximumWarning": "6kb"
}
],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
"outputHashing": "all",
"serviceWorker": "ngsw-config.json"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "angular-hnpwa:build"
},
"configurations": {
"production": {
"browserTarget": "angular-hnpwa:build:production"
"buildTarget": "angular-hnpwa:build:production"
},
"development": {
"buildTarget": "angular-hnpwa:build:development"
}
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular-hnpwa:build"
"buildTarget": "angular-hnpwa:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
Expand All @@ -103,32 +99,15 @@
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "angular-hnpwa:serve"
},
"configurations": {
"production": {
"devServerTarget": "angular-hnpwa:serve:production"
}
}
}
}
}
},
"defaultProject": "angular-hnpwa"
}
}
21 changes: 21 additions & 0 deletions e2e/app.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test, expect } from '@playwright/test';

test.describe('angular2-hn', () => {
test('redirects to the news feed and renders the header navigation', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveURL(/\/news\/1$/);

const header = page.locator('app-header');
await expect(header).toBeVisible();
await expect(header.getByRole('link', { name: 'new' })).toBeVisible();
await expect(header.getByRole('link', { name: 'show' })).toBeVisible();
await expect(header.getByRole('link', { name: 'ask' })).toBeVisible();
await expect(header.getByRole('link', { name: 'jobs' })).toBeVisible();
});

test('navigates to the jobs feed', async ({ page }) => {
await page.goto('/');
await page.locator('app-header').getByRole('link', { name: 'jobs' }).click();
await expect(page).toHaveURL(/\/jobs\/1$/);
});
});
Loading
Loading