From e6e015c0632a5ae591f69bba0dd19f40db37dd1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Stanimirovi=C4=87?= Date: Fri, 28 Jul 2023 22:14:27 +0200 Subject: [PATCH 1/4] docs(router): add section that combines all types of routes (#579) --- .../docs-app/docs/features/routing/content.md | 2 +- .../docs/features/routing/overview.md | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/docs-app/docs/features/routing/content.md b/apps/docs-app/docs/features/routing/content.md index 62c35070b..52a7b097b 100644 --- a/apps/docs-app/docs/features/routing/content.md +++ b/apps/docs-app/docs/features/routing/content.md @@ -138,7 +138,7 @@ The `injectContent()` function can also be used with an object that contains the This can be useful if, for instance, you have blog posts, as well as a portfolio of project markdown files to be used on the site. -```ts +```treeview src/ └── app/ │ └── pages/ diff --git a/apps/docs-app/docs/features/routing/overview.md b/apps/docs-app/docs/features/routing/overview.md index 0a0901e19..39d70e446 100644 --- a/apps/docs-app/docs/features/routing/overview.md +++ b/apps/docs-app/docs/features/routing/overview.md @@ -22,6 +22,12 @@ There are 5 primary types of routes: These routes can be combined in different ways to build to URLs for navigation. +:::note + +In addition to the 5 primary types of routes, Analog also supports [Redirect Routes](/docs/features/routing/metadata#redirect-routes) and [Content Routes](/docs/features/routing/content). + +::: + ## Index Routes Index routes are defined by using the filename as the route path enclosed in parenthesis. @@ -252,7 +258,7 @@ src/ The above example defines `/login` and `/signup` routes with a shared layout. The parent `src/app/pages/(auth).page.ts` file contains the parent page with a router outlet. -## Catch-all routes +## Catch-all Routes Catch-all routes are defined by using the filename as the route path prefixed with 3 periods enclosed in square brackets. @@ -275,3 +281,41 @@ export default class PageNotFoundComponent {} ``` Catch-all routes can also be defined as nested child routes. + +## Putting It All Together + +For the following file structure: + +```treeview +src/ +└── app/ + └── pages/ + ├── (auth)/ + │ ├── login.page.ts + │ └── signup.page.ts + ├── (marketing)/ + │ ├── about.md + │ └── contact.md + ├── products/ + │ ├── (product-list).page.ts + │ ├── [productId].edit.page.ts + │ └── [productId].page.ts + ├── (auth).page.ts + ├── (home).page.ts + ├── [...not-found].md + └── products.page.ts +``` + +The filesystem-based router will generate the following routes: + +| Path | Page | +| ------------------ | ---------------------------------------------------------------- | +| `/` | `(home).page.ts` | +| `/about` | `(marketing)/about.md` | +| `/contact` | `(marketing)/contact.md` | +| `/login` | `(auth)/login.page.ts` (layout: `(auth).page.ts`) | +| `/signup` | `(auth)/signup.page.ts` (layout: `(auth).page.ts`) | +| `/products` | `products/(product-list).page.ts` (layout: `products.page.ts`) | +| `/products/1` | `products/[productId].page.ts` (layout: `products.page.ts`) | +| `/products/1/edit` | `products/[productId].edit.page.ts` (layout: `products.page.ts`) | +| `/unknown-url` | `[...not-found].md` | From ce348f61450d971f8d192931990b5ec6bf88de77 Mon Sep 17 00:00:00 2001 From: Artur Androsovych Date: Fri, 28 Jul 2023 23:15:07 +0300 Subject: [PATCH 2/4] fix(vite-plugin-angular): cache style URLs by matched `styleUrls` expression (#571) --- .../src/lib/component-resolvers.ts | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/packages/vite-plugin-angular/src/lib/component-resolvers.ts b/packages/vite-plugin-angular/src/lib/component-resolvers.ts index 67904c485..057a2aa8d 100644 --- a/packages/vite-plugin-angular/src/lib/component-resolvers.ts +++ b/packages/vite-plugin-angular/src/lib/component-resolvers.ts @@ -8,10 +8,12 @@ export function hasStyleUrls(code: string) { } interface StyleUrlsCacheEntry { - code: string; + matchedStyleUrls: string; styleUrls: string[]; } +const EMPTY_ARRAY: any[] = []; + export class StyleUrlsResolver { // These resolvers may be called multiple times during the same // compilation for the same files. Caching is required because these @@ -20,32 +22,44 @@ export class StyleUrlsResolver { private readonly styleUrlsCache = new Map(); resolve(code: string, id: string): string[] { - const entry = this.styleUrlsCache.get(id); - if (entry?.code === code) { - return entry.styleUrls; - } + const styleUrlsExecArray = styleUrlsRE.exec(code); - const styleUrlsGroup = styleUrlsRE.exec(code); - - if (Array.isArray(styleUrlsGroup) && styleUrlsGroup[0]) { - const styleUrls = styleUrlsGroup[0].replace( - /(styleUrls|\:|\s|\[|\]|"|')/g, - '' - ); - const styleUrlPaths = styleUrls?.split(',') || []; - - const newEntry = { - code, - styleUrls: styleUrlPaths.map((styleUrlPath) => { - return `${styleUrlPath}|${resolve(dirname(id), styleUrlPath)}`; - }), - }; + if (styleUrlsExecArray === null) { + return EMPTY_ARRAY; + } - this.styleUrlsCache.set(id, newEntry); - return newEntry.styleUrls; + // Given the code is the following: + // @Component({ + // styleUrls: [ + // './app.component.scss' + // ] + // }) + // The `matchedStyleUrls` would result in: `styleUrls: [\n './app.component.scss'\n ]`. + const [matchedStyleUrls] = styleUrlsExecArray; + const entry = this.styleUrlsCache.get(id); + // We're using `matchedStyleUrls` as a key because the code may be changing continuously, + // resulting in the resolver being called multiple times. While the code changes, the + // `styleUrls` may remain constant, which means we should always return the previously + // resolved style URLs. + if (entry?.matchedStyleUrls === matchedStyleUrls) { + return entry.styleUrls; } - return []; + // The `styleUrls` property is an array, which means we may have a list of + // CSS files provided there. Let `matchedStyleUrls` be equal to the following: + // "styleUrls: [\n './app.component.scss',\n '../global.scss'\n ]" + const styleUrlPaths = matchedStyleUrls + .replace(/(styleUrls|\:|\s|\[|\]|"|')/g, '') + // The above replace will result in the following: + // "./app.component.scss,../global.scss" + .split(','); + + const styleUrls = styleUrlPaths.map((styleUrlPath) => { + return `${styleUrlPath}|${resolve(dirname(id), styleUrlPath)}`; + }); + + this.styleUrlsCache.set(matchedStyleUrls, { styleUrls, matchedStyleUrls }); + return styleUrls; } } From 0436b6fe7d64d11be7bbf0dd94b6a988118c2087 Mon Sep 17 00:00:00 2001 From: Artur Androsovych Date: Fri, 28 Jul 2023 23:15:21 +0300 Subject: [PATCH 3/4] fix(content): do not run change detection when loading mermaid (#562) --- .../content/src/lib/markdown.component.ts | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/content/src/lib/markdown.component.ts b/packages/content/src/lib/markdown.component.ts index c7bf69ec9..38c92d930 100644 --- a/packages/content/src/lib/markdown.component.ts +++ b/packages/content/src/lib/markdown.component.ts @@ -10,9 +10,10 @@ import { ViewEncapsulation, inject, } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute, Data } from '@angular/router'; -import { Observable, of } from 'rxjs'; +import { Observable, from, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { AnchorNavigationDirective } from './anchor-navigation.directive'; @@ -54,15 +55,7 @@ export default class AnalogMarkdownComponent } } - async loadMermaid(mermaidImport: Promise) { - this.mermaid = await mermaidImport; - this.mermaid.default.initialize({ startOnLoad: false }); - // Explicitly running mermaid as ngAfterViewChecked - // has probably already been called - this.zone.runOutsideAngular(() => this.mermaid?.default.run()); - } - - ngOnInit() { + ngOnInit(): void { this.updateContent(); } @@ -87,4 +80,20 @@ export default class AnalogMarkdownComponent this.contentRenderer.enhance(); this.zone.runOutsideAngular(() => this.mermaid?.default.run()); } + + private loadMermaid(mermaidImport: Promise) { + this.zone.runOutsideAngular(() => + // Wrap into an observable to avoid redundant initialization once + // the markdown component is destroyed before the promise is resolved. + from(mermaidImport) + .pipe(takeUntilDestroyed()) + .subscribe((mermaid) => { + this.mermaid = mermaid; + this.mermaid.default.initialize({ startOnLoad: false }); + // Explicitly running mermaid as ngAfterViewChecked + // has probably already been called + this.mermaid?.default.run(); + }) + ); + } } From afb9188290208943c855f0841feb193165328641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Stanimirovi=C4=87?= Date: Fri, 28 Jul 2023 22:15:35 +0200 Subject: [PATCH 4/4] chore: fix commitlint on Windows (#580) --- .githooks/commit-msg | 2 +- .githooks/pre-commit | 2 +- package.json | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.githooks/commit-msg b/.githooks/commit-msg index 94b4cc74f..27d06982c 100755 --- a/.githooks/commit-msg +++ b/.githooks/commit-msg @@ -1,3 +1,3 @@ #!/bin/sh -npm run hook:commit-msg +pnpm exec commitlint --edit $1 diff --git a/.githooks/pre-commit b/.githooks/pre-commit index cd25e400e..9c668ff95 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,3 +1,3 @@ #!/bin/sh -npm run hook:pre-commit \ No newline at end of file +pnpm exec lint-staged diff --git a/package.json b/package.json index b0556571c..d7734dbbd 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", - "hook:commit-msg": "npx --no -- commitlint --edit $1", - "hook:pre-commit": "lint-staged", "prettify": "prettier --write ." }, "engines": {