You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: ARCHITECTURE.md
+17-22Lines changed: 17 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -206,13 +206,10 @@ app/src
206
206
└─ website.routes.ts
207
207
└─ app.component.ts
208
208
└─ app.routes.ts
209
-
└─ loader-shell.component.ts
210
209
└─ assets
211
210
└─ {images, icons (incl PWA icons), fonts, etc.}
212
211
└─ environments
213
212
└─ {environment files - live, test and local}
214
-
└─ styles
215
-
└─ {SCSS partials and utilities}
216
213
└─ test
217
214
└─ helpers
218
215
└─ {test helpers}
@@ -253,13 +250,13 @@ As things grow you may need to adapt and tweak this structure (e.g. to add anoth
253
250
254
251
|**:brain: Design decision**|
255
252
| :-- |
256
-
| 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). |
253
+
| 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). |
257
254
258
255
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).
259
256
260
-
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).
257
+
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).
261
258
262
-
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).
259
+
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).
263
260
264
261
> [!NOTE]
265
262
>
@@ -273,13 +270,10 @@ For the build-time prerendering of pages, we:
273
270
274
271
- Configure the `prerender` option in `angular.json` to prerender all paths defined in the [`app/prerendered-routes.txt`](./app/prerendered-routes.txt) file.
275
272
- We also set `"discoverRoutes": false` so only the routes we explicitly specify are prerendered.
276
-
- Specify all static paths we want prerendered, in the `prerendered-routes.txt` file.
273
+
- Specify all static paths we want prerendered, in the aforementioned `prerendered-routes.txt` file.
277
274
- Out of the box, we have the website home page (`/`) and the about page (`/about`).
278
-
- Specify the `/loader` path in the `prerendered-routes.txt` file, so that the loader shell is prerendered too.
279
-
- 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.
280
-
- This is used as the default HTML file to serve for all fully dynamic parts of the app.
281
275
282
-
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.
276
+
So, when we run the production build (`pnpm build`) Angular will output separate static HTML files for the prerendered routes.
283
277
284
278
> [!NOTE]
285
279
>
@@ -293,7 +287,7 @@ Given we have a mix of prerendered static and fully dynamic pages, we have to co
293
287
>
294
288
> 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).
295
289
>
296
-
> 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.
290
+
> 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.
297
291
298
292
For the static pages (prerendered), we:
299
293
@@ -305,10 +299,11 @@ For the static pages (prerendered), we:
305
299
306
300
Then, for the rest of the fully dynamic pages, we:
307
301
308
-
- 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.
302
+
- 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.
309
303
- This is known as a "catch-all" rule, and MUST be the last item in the list of rewrite rules.
310
-
- 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.
311
-
- 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.
304
+
- 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.
305
+
- I.e. this will be used by the service worker for all dynamic pages.
306
+
- 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.
312
307
313
308
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.
314
309
@@ -338,9 +333,9 @@ The [`app/src/app/app.component.ts`](./app/src/app/app.component.ts) file contai
338
333
339
334
|**:brain: Design decision**|
340
335
| :-- |
341
-
| 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. |
336
+
| 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. |
342
337
343
-
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.
338
+
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.
344
339
345
340
> [!NOTE]
346
341
>
@@ -414,7 +409,7 @@ To try out the login flow run the app locally and click on the "Login" button.
414
409
>
415
410
> 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.
416
411
>
417
-
> 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).
412
+
> 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.
418
413
419
414
## [`app`] Logging
420
415
@@ -454,9 +449,9 @@ Most of the components, services, etc. provided in the base template have corres
454
449
455
450
|**:brain: Design decision**|
456
451
| :-- |
457
-
| 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). |
452
+
| 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). |
458
453
459
-
The config for linting is in [`app/.eslintrc.json`](./app/.eslintrc.json) and for formatting in [`app/.prettierrc`](./app/.prettierrc).
454
+
The config for linting is in [`app/eslint.config.js`](./app/eslint.config.js) and for formatting in [`app/.prettierrc`](./app/.prettierrc).
460
455
461
456
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.
462
457
@@ -551,9 +546,9 @@ See the files within the [`firebase/test`](./firebase/test/) folder for the secu
551
546
552
547
|**:brain: Design decision**|
553
548
| :-- |
554
-
| 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). |
549
+
| 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). |
555
550
556
-
The config for linting is in [`firebase/.eslintrc.js`](./firebase/.eslintrc.js) and for formatting in [`firebase/.prettierrc`](./firebase/.prettierrc).
551
+
The config for linting is in [`firebase/eslint.config.js`](./firebase/eslint.config.js) and for formatting in [`firebase/.prettierrc`](./firebase/.prettierrc).
557
552
558
553
## Continuous integration (CI) using GitHub Actions
@@ -256,13 +256,13 @@ For apps built on this base template there are two things you need to do to keep
256
256
257
257
### 1. How to update dependencies
258
258
259
-
You can update some or all of the dependencies in the `app` and `firebase` folders using `pnpm` (our chosen pack manager):
259
+
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):
260
260
261
261
For the `app` folder:
262
262
263
263
1. First run `pnpm ng update` and follow the instructions.
264
-
- This will update the Angular specific dependencies (and any other dependencies that support Angular Schematics).
265
-
- 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).
264
+
- This will update the Angular specific dependencies (and any other dependencies that support Angular Schematics for updates).
265
+
- 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.
266
266
1. Then run `pnpm update --interactive --latest` and follow the instructions.
267
267
- You can select the packages you want to update.
268
268
@@ -281,11 +281,15 @@ Make sure to do all this in a branch, test locally and push to GitHub, then open
281
281
>
282
282
> 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).
283
283
284
+
> [!TIP]
285
+
>
286
+
> 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).
287
+
284
288
### 2. How to update your app to a base template release
285
289
286
290
> [!NOTE]
287
291
>
288
-
> 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.
292
+
> 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.
289
293
>
290
294
> Also, we won't usually make releases for simple dependency updates, or content changes.
0 commit comments