Skip to content

Commit

Permalink
Merge pull request #19 from FullStacksDev/upgrades
Browse files Browse the repository at this point in the history
Upgrades
  • Loading branch information
jits authored Jul 15, 2024
2 parents b9a28f0 + a116aea commit fe45a24
Show file tree
Hide file tree
Showing 61 changed files with 14,977 additions and 12,697 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ jobs:
${{ runner.os }}-firebase-emulators-
- name: Set up pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: 8
version: 9

- name: Set up Node.js
uses: actions/setup-node@v4
Expand Down
39 changes: 17 additions & 22 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,10 @@ app/src
└─ website.routes.ts
└─ app.component.ts
└─ app.routes.ts
└─ loader-shell.component.ts
└─ assets
└─ {images, icons (incl PWA icons), fonts, etc.}
└─ environments
└─ {environment files - live, test and local}
└─ styles
└─ {SCSS partials and utilities}
└─ test
└─ helpers
└─ {test helpers}
Expand Down Expand Up @@ -253,13 +250,13 @@ As things grow you may need to adapt and tweak this structure (e.g. to add anoth

| **:brain: Design decision** |
| :-- |
| Out of the box, we _don't_ use server-side rendering (SSR). We _do_ use prerendering for certain pages (configured explicitly), and everything else is fully dynamic (i.e. client-side only, with a minimal loader shell). |
| Out of the box, we _don't_ use server-side rendering (SSR). We _do_ use prerendering for certain pages (configured explicitly), and everything else is fully dynamic (i.e. client-side only). |

Whilst Angular has very good [support for server-side rendering (SSR)](https://angular.dev/guide/ssr) we don't make use of this in the _deployed app_ as we want to be able to run the app wholly from static assets (i.e. no dynamic server required to render any pages).

Instead, we do make use of [build-time prerendering](https://angular.dev/guide/prerendering) for routes we explicitly specify in [`app/prerendered-routes.txt`](./app/prerendered-routes.txt) (currently the website home and about pages) — a static HTML file is built and served for each path specified there (with some additional Firebase Hosting and PWA configuration).
Instead, we do make use of [build-time prerendering](https://angular.dev/guide/prerendering) for routes we explicitly specify in [`app/prerendered-routes.txt`](./app/prerendered-routes.txt) file (currently the website home and about pages) — a static HTML file is built and served for _each_ path specified there (with some additional Firebase Hosting and PWA configuration required to support these).

And then everything else in the app is fully dynamic (i.e. rendered on the client) — a special empty "loader" HTML file (prerendered from the [`LoaderShellComponent`](./app/src/app/loader-shell.component.ts)) is served for all these routes, and we've configured Firebase Hosting and the PWA set-up to serve this loader file for these routes (more details below).
And then everything else in the app is fully dynamic (i.e. rendered on the client) — the special `index.csr.html` generated by the Angular build is used in the Firebase Hosting config and the PWA set-up as the file to serve for all non-prerendered routes (more details below).

> [!NOTE]
>
Expand All @@ -273,13 +270,10 @@ For the build-time prerendering of pages, we:

- Configure the `prerender` option in `angular.json` to prerender all paths defined in the [`app/prerendered-routes.txt`](./app/prerendered-routes.txt) file.
- We also set `"discoverRoutes": false` so only the routes we explicitly specify are prerendered.
- Specify all static paths we want prerendered, in the `prerendered-routes.txt` file.
- Specify all static paths we want prerendered, in the aforementioned `prerendered-routes.txt` file.
- Out of the box, we have the website home page (`/`) and the about page (`/about`).
- Specify the `/loader` path in the `prerendered-routes.txt` file, so that the loader shell is prerendered too.
- The `/loader` route serves an empty shell of the app (using the [`LoaderShellComponent`](./app/src/app/loader-shell.component.ts)), which then loads the full app on the client-side. This route is defined in the [`app.routes.ts`](./app/src/app/app.routes.ts) file.
- This is used as the default HTML file to serve for all fully dynamic parts of the app.

So, when we run the production build (`pnpm build`) Angular will output static HTML files for the prerendered routes (including an HTML file for the loader shell) together with the usual JavaScript, CSS, etc. assets.
So, when we run the production build (`pnpm build`) Angular will output separate static HTML files for the prerendered routes.

> [!NOTE]
>
Expand All @@ -293,7 +287,7 @@ Given we have a mix of prerendered static and fully dynamic pages, we have to co
>
> In a typical single-page app (SPA) without any server side rendering or static page generation, you'd serve a static `index.html` file for all paths requested. This file would usually contain very little UI, and then bootstrap the app and handle routing, data fetching, templating, etc. on the client-side (all handled by your framework).
>
> However, in our case, the `index.html` file is now the static (prerendered) website home page (which still bootstraps the Angular app when it loads client-side), which we wouldn't want to serve for all routes in our app as it contains content for the home page. And we have a mix of static pages and fully dynamic pages that need to work regardless of whether they are requested directly (from the "server" — a static host, Firebase Hosting, in our case) or within the single-page app (client-side). Hence the need for the loader shell and the Firebase Hosting and PWA set-up.
> However, in our case, the `index.html` file is now the static (prerendered) website home page (which still bootstraps the Angular app when it loads client-side), which we wouldn't want to serve for all routes in our app as it contains content for the home page. And we have a mix of static pages and fully dynamic pages that need to work regardless of whether they are requested directly (from Firebase Hosting) or within the single-page app (client-side). As part of the build, Angular outputs a special `index.csr.html` file which we make use of for all routes not covered by the prerendered pages.
For the static pages (prerendered), we:

Expand All @@ -305,10 +299,11 @@ For the static pages (prerendered), we:

Then, for the rest of the fully dynamic pages, we:

- Add an entry in the [`firebase/firebase.json`](./firebase/firebase.json) file (under the `hosting.rewrites` key) to serve the `/loader` path (the default loader shell, mentioned previously) for all paths that aren't explicitly covered by the static pages.
- Add an entry in the [`firebase/firebase.json`](./firebase/firebase.json) file (under the `hosting.rewrites` key) to serve the special `/index.csr.html` file for all paths that aren't explicitly covered by the static (aka prerendered) pages.
- This is known as a "catch-all" rule, and MUST be the last item in the list of rewrite rules.
- Configure the PWA service worker (in the [`app/ngsw-config.json`](./app/ngsw-config.json) file) to use the `"/loader/index.html"` path as the default "index" file to serve for all paths not covered by those defined in the `navigationUrls` key.
- In this same file, we also add `"/loader/index.html"` to the list of prefetched URLs so it can be cached by the service worker.
- Configure the PWA service worker (in the [`app/ngsw-config.json`](./app/ngsw-config.json) file) to use the same `"/index.csr.html"` path as the default "index" file to serve for all paths not covered by those defined in the `navigationUrls` key.
- I.e. this will be used by the service worker for all dynamic pages.
- In this same file, we also add `"/index.csr.html"` to the list of prefetched URLs so it can be cached by the service worker.

Note also: in the [`firebase/firebase.json`](./firebase/firebase.json) file (under the `hosting` key) we set `"cleanUrls": true` and `"trailingSlash": false` to normalize the behavior and ensure our static paths are served correctly.

Expand Down Expand Up @@ -338,9 +333,9 @@ The [`app/src/app/app.component.ts`](./app/src/app/app.component.ts) file contai

| **:brain: Design decision** |
| :-- |
| We use [Angular Material](https://material.angular.io/) for UI components, and [Tailwind CSS](https://tailwindcss.com/) for styling. You can still create your own UI components or add in other libraries, if needed. You can also customize Tailwind CSS as you wish. |
| We use [Angular Material](https://material.angular.io/) (with Material 3) for UI components, and [Tailwind CSS](https://tailwindcss.com/) for styling. You can still create your own UI components or add in other libraries, if needed. You can also customize Tailwind CSS as you wish, by updating the [`app/tailwind.config.js`](./app/tailwind.config.js) config file. |

The [`app/src/styles.scss`](./app/src/styles.scss) file sets up both Angular Material (with a basic theme) and Tailwind CSS styling. Here, we also provide styling overrides to make Angular Material work okay with the Tailwind CSS base styles.
The [`app/src/styles.scss`](./app/src/styles.scss) file sets up both Angular Material (with a basic Material 3 theme with custom background and text colors) and Tailwind CSS styling. Here, we also provide styling overrides to make Angular Material work okay with the Tailwind CSS base styles.

> [!NOTE]
>
Expand Down Expand Up @@ -414,7 +409,7 @@ To try out the login flow run the app locally and click on the "Login" button.
>
> Firebase Authentication does not provide server-side sessions, which is not a problem for us as we don't use server-side rendering (SSR), and for any server-side functionality we use Firebase Functions (which has access to the auth token in each request). All authentication is carried out and managed client-side using the Firebase JavaScript SDK.
>
> This does mean that in the auth guard we need a check to see if we're running server-side (currently only applicable to local development and running the build process), where we then "redirect" to the special `/loader` route (covered in a previous section). Note that this isn't a proper redirect, just something that happens purely server-side to determine what content gets rendered for that route (so it doesn’t actually change the path the user is requesting). Once the page loads in the browser then the usual client-side auth check takes over when the Angular app hydrates (i.e. fully loads up).
> This does mean that in the auth guard we need a check to see if we're running server-side and then short-circuit the logic and return `false`. Note that, currently, this is only applicable to local development, since we don't use SSR in production. Once the page loads in the browser then the usual client-side auth check takes over when the Angular app hydrates (i.e. fully loads up). You may see the error `ERROR RuntimeError: NG04002: Cannot match any routes.` in the dev process output — you can safely ignore this as it will only happen in local development.
## [`app`] Logging

Expand Down Expand Up @@ -454,9 +449,9 @@ Most of the components, services, etc. provided in the base template have corres

| **:brain: Design decision** |
| :-- |
| We use [ESLint](https://eslint.org/) for linting and [Prettier](https://prettier.io/) for code formatting.<br><br>Running `pnpm lint` performs the linting, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |
| We use [ESLint](https://eslint.org/) for linting and [Prettier](https://prettier.io/) for code formatting.<br><br>Running `pnpm lint` performs the linting only, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |

The config for linting is in [`app/.eslintrc.json`](./app/.eslintrc.json) and for formatting in [`app/.prettierrc`](./app/.prettierrc).
The config for linting is in [`app/eslint.config.js`](./app/eslint.config.js) and for formatting in [`app/.prettierrc`](./app/.prettierrc).

We also integrate [`prettier-plugin-tailwindcss`](https://www.npmjs.com/package/prettier-plugin-tailwindcss) to format Tailwind CSS classes in your HTML and JavaScript files.

Expand Down Expand Up @@ -551,9 +546,9 @@ See the files within the [`firebase/test`](./firebase/test/) folder for the secu

| **:brain: Design decision** |
| :-- |
| We use [ESLint](https://eslint.org/) for linting and [Prettier](https://prettier.io/) for code formatting.<br><br>Running `pnpm lint` performs the linting, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |
| We use [ESLint](https://eslint.org/) for linting and [Prettier](https://prettier.io/) for code formatting.<br><br>Running `pnpm lint` performs the linting only, and all Prettier formatting is carried out within VS Code (e.g. when you save a file). |

The config for linting is in [`firebase/.eslintrc.js`](./firebase/.eslintrc.js) and for formatting in [`firebase/.prettierrc`](./firebase/.prettierrc).
The config for linting is in [`firebase/eslint.config.js`](./firebase/eslint.config.js) and for formatting in [`firebase/.prettierrc`](./firebase/.prettierrc).

## Continuous integration (CI) using GitHub Actions

Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ Part of the curated [**FullStacksDev Angular and Firebase tech stack**](https://

An opinionated full-stack starting point for building a web app, using Angular and Firebase. Aiming to be lean and useful enough so you can hit the ground running and focus on stuff that matters. With enough room for you to extend as you need.

You are free to use and customize this template as you want — build a prototype, an internal tool, a side project, or the next million-dollar web app.
You are free to use and customize this template as you want — build a prototype, an internal tool, a side project, or the next big thing.

> [!IMPORTANT]
>
> This is currently in **beta**. We're actively working on it and will be making regular updates — expect big changes and improvements until it gets to a stable release. Feel free to give your feedback and suggestions via the Issues tab.
## Features

Expand Down Expand Up @@ -41,10 +45,10 @@ For more details see the [Architecture and design decisions](./ARCHITECTURE.md)
- [Node.js](https://nodejs.org/en/) v20.x
- [TypeScript](https://www.typescriptlang.org/) v5.4
- [Angular](https://angular.dev/) v17.3
- [Angular Material](https://material.angular.io/) v17.3
- [Angular](https://angular.dev/) v18.1
- [Angular Material](https://material.angular.io/) v18.1
- [Tailwind CSS](https://tailwindcss.com/) v3.4
- [NgRx Signals](https://ngrx.io/guide/signals) v17.2
- [NgRx Signals](https://ngrx.io/guide/signals) v18.0
- [RxFire](https://github.com/FirebaseExtended/rxfire) v6
- [Firebase](https://firebase.google.com/)
- [Hosting](https://firebase.google.com/products/hosting)
Expand Down Expand Up @@ -256,13 +260,13 @@ For apps built on this base template there are two things you need to do to keep

### 1. How to update dependencies

You can update some or all of the dependencies in the `app` and `firebase` folders using `pnpm` (our chosen pack manager):
You can update some or all of the dependencies in the `app` and `firebase` folders using the Angular CLI and `pnpm` (our chosen package manager):

For the `app` folder:

1. First run `pnpm ng update` and follow the instructions.
- This will update the Angular specific dependencies (and any other dependencies that support Angular Schematics).
- You can choose to skip this step, especially if there is a major version of Angular with breaking changes (in which case you could choose to wait until the base template has been updated first).
- This will update the Angular specific dependencies (and any other dependencies that support Angular Schematics for updates).
- You can choose to skip this step, especially if there is a major version of Angular with breaking changes (in which case you could choose to wait until the base template has been updated first). If you do skip this step, makes you don't inadvertently update the version of any Angular etc. packages in the next step.
1. Then run `pnpm update --interactive --latest` and follow the instructions.
- You can select the packages you want to update.

Expand All @@ -281,11 +285,15 @@ Make sure to do all this in a branch, test locally and push to GitHub, then open
>
> Sometimes, especially with major version updates, you may need to delete the `node_modules` folder and `pnpm-lock.yaml` file, and then run `pnpm install` to rebuild the dependency tree and lockfile. This ensures the very latest dependencies (matched to the versions defined in the relevant `package.json`) are used (especially subdependencies).
> [!TIP]
>
> When updating the Angular dependencies using the Angular CLI, you may want to use <https://github.com/cexbrayat/angular-cli-diff> to find any other changes that could be made. Sometimes, the Angular folks will automatically make some of these changes for you (via the `ng update` process), but sometimes they won't. The `angular-cli-diff` is a really useful community project that can help you find any other relevant changes and make them manually. We usually do this ourselves when updating the base template and will provide instruction on what you could update (see the next section).
### 2. How to update your app to a base template release

> [!NOTE]
>
> Once the base template is launched and in a stable version, there are unlikely to be frequent changes, so hopefully you won't need to do this often.
> Once the base template is launched and in a stable version, there are unlikely to be _frequent_ changes, so hopefully you won't need to do this often.
>
> Also, we won't usually make releases for simple dependency updates, or content changes.
Expand Down
2 changes: 1 addition & 1 deletion TEMPLATE_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.0.0-alpha.0
v1.0.0-alpha.1
57 changes: 0 additions & 57 deletions app/.eslintrc.json

This file was deleted.

3 changes: 3 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ testem.log
# System files
.DS_Store
Thumbs.db

# Nx cache etc.
.nx
1 change: 1 addition & 0 deletions app/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm-lock.yaml
Loading

0 comments on commit fe45a24

Please sign in to comment.