diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 000000000..bb54d247d --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', 'dev-dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + }, +}; diff --git a/.gitignore b/.gitignore index f4f46a5fe..bc50ce5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ testem.log # System Files .DS_Store Thumbs.db + +# Vite PWA dev output +/dev-dist diff --git a/.travis.yml b/.travis.yml index ff155b5fa..bdefa24b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "6.9" + - "20.19" branches: only: @@ -8,9 +8,13 @@ branches: before_script: - npm install -g firebase-tools - - npm install -g @angular/cli + +install: + - npm ci script: + - npm run lint + - npm test - npm run build after_success: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 840cf8244..6ff2ec021 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,27 +1,24 @@ # Contributing Thank you for your interest in contributing! Please feel free to put up a PR for any issue or feature request. -Even if you have little to no experience with Angular, I'll be more than happy to help. :) +Even if you have little to no experience with React, I'll be more than happy to help. :) ## Setup 1. Fork the repo 2. Clone your fork 3. Make a branch for your feature or bug fix -4. If you don't have Angular CLI installed: `npm install -g angular-cli@latest` -5. `ng init` -6. Type `n` for each file to not overwrite any file changes -7. Run `npm start` and open `localhost:4200` in a browser -8. Work your magic -9. Run `npm run build` or `npm run static-serve` to kick off a production build and make sure nothing is broken -10. To test service worker changes: +4. `npm install` +5. Run `npm run dev` and open `localhost:4200` in a browser +6. Work your magic +7. Run `npm run lint`, `npm test` and `npm run build` to make sure nothing is broken +8. To test service worker changes: * `npm run build` to kick off a fresh build and update the `dist/` directory - * `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) -11. Add yourself to the [contributor's list](https://github.com/hdjirdeh/angular2-hn#contributors) in the README! -12. Commit your changes and reference the issue you're addressing (for example: `git commit -am 'Commit message. Closes #5'`) -13. Push your branch to your fork -14. Create a pull request from your branch on your fork to `master` on this repo -15. Have your branch get merged in! :star2: + * `npm run preview` to serve the built app together with its generated service worker +9. Add yourself to the [contributor's list](https://github.com/hdjirdeh/angular2-hn#contributors) in the README! +10. Commit your changes and reference the issue you're addressing (for example: `git commit -am 'Commit message. Closes #5'`) +11. Push your branch to your fork +12. Create a pull request from your branch on your fork to `master` on this repo +13. Have your branch get merged in! :star2: If you experience a problem at any point, please don't hesitate to file an issue or send me a message! diff --git a/README.md b/README.md index 67cef9e61..e138a9faf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- A progressive Hacker News client built with Angular + A progressive Hacker News client built with React and TypeScript

@@ -23,7 +23,7 @@ :iphone: **Responsive:** Completely responsive UI that can be installed to your mobile home screen to provide a native feel. -:rocket: **Progressive:** [Lighthouse](https://github.com/GoogleChrome/lighthouse) score of 87/100. +:rocket: **Progressive:** installable PWA with a Workbox service worker generated by `vite-plugin-pwa`.

@@ -43,11 +43,11 @@ ## 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 [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) (Workbox under the hood) to generate a service worker as part of the build step, so the app loads quickly and works offline. ## Manifest -With Chromium based browsers for Android (Chrome, Opera, etc...), Angular 2 HN includes a Web App Manifest that allows you to install to your homescreen. +With Chromium based browsers for Android (Chrome, Opera, etc...), React HN includes a Web App Manifest that allows you to install to your homescreen.

@@ -71,18 +71,25 @@ More to come! Feel free to send me feedback on [twitter](https://twitter.com/hdjirdeh) or [file an issue](https://github.com/hdjirdeh/angular2-hn/issues/new)! Feature requests are always welcome. -## Build process +## Stack + +* [React](https://react.dev/) 18 with TypeScript +* [React Router](https://reactrouter.com/) for routing (feeds, item details, user profiles) +* [Vite](https://vitejs.dev/) for the dev server and production build +* [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) for the service worker and offline support +* [Vitest](https://vitest.dev/) + [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) for tests +* SCSS for styling and theming -Note: This project has been ejected (with AOT + production settings) in order to customize Webpack configurations. +## Build process - 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 run dev` to start the Vite dev server on [localhost:4200](http://localhost:4200) + - `npm run build` to type-check and produce a production build in `dist/` + - `npm run preview` to serve the production build locally (this is the only way to exercise the service worker, which is disabled in development) + - `npm test` to run the unit tests, `npm run lint` to lint -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) +Data comes from the [node-hnapi](https://github.com/cheeaun/node-hnapi) REST API. ## Contributors diff --git a/angular.json b/angular.json deleted file mode 100644 index bae858c47..000000000 --- a/angular.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "angular-hnpwa": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss" - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "aot": true, - "outputPath": "dist/angular-hnpwa", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.app.json", - "assets": [ - "src/favicon.ico", - "src/assets", - "src/manifest.json", - "src/manifest.webmanifest" - ], - "styles": [ - "src/styles.scss" - ], - "scripts": [] - }, - "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", - "maximumWarning": "2mb", - "maximumError": "5mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "6kb" - } - ], - "serviceWorker": true, - "ngswConfigPath": "ngsw-config.json" - } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "angular-hnpwa:build" - }, - "configurations": { - "production": { - "browserTarget": "angular-hnpwa:build:production" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "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", - "assets": [ - "src/favicon.ico", - "src/assets", - "src/manifest.webmanifest" - ], - "styles": [ - "src/styles.scss" - ], - "scripts": [] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, - "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" -} diff --git a/browserslist b/browserslist deleted file mode 100644 index 80848532e..000000000 --- a/browserslist +++ /dev/null @@ -1,12 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -> 0.5% -last 2 versions -Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js deleted file mode 100644 index 73e4e6806..000000000 --- a/e2e/protractor.conf.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-check -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter } = require('jasmine-spec-reporter'); - -/** - * @type { import("protractor").Config } - */ -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - 'browserName': 'chrome' - }, - directConnect: true, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } -}; \ No newline at end of file diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 0897abab0..000000000 --- a/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AppPage } from './app.po'; -import { browser, logging } from 'protractor'; - -describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display welcome message', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('Welcome to angular-hnpwa!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - } as logging.Entry)); - }); -}); diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts deleted file mode 100644 index 5776aa9eb..000000000 --- a/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - navigateTo() { - return browser.get(browser.baseUrl) as Promise; - } - - getTitleText() { - return element(by.css('app-root h1')).getText() as Promise; - } -} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json deleted file mode 100644 index 39b800f78..000000000 --- a/e2e/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/e2e", - "module": "commonjs", - "target": "es5", - "types": [ - "jasmine", - "jasminewd2", - "node" - ] - } -} diff --git a/src/index.html b/index.html similarity index 55% rename from src/index.html rename to index.html index 05e798ea4..d224224c9 100644 --- a/src/index.html +++ b/index.html @@ -2,24 +2,23 @@ - Angular 2 HN - + React HN - + - - + + - + - + - - - + + + @@ -27,36 +26,34 @@ - - + + - + - - - + + + - - - - + + + + - -

- +
- +