@ng-doc/app 22.0.0-beta.2 → 22.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -29
- package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs +363 -0
- package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs.map +1 -0
- package/fesm2022/ng-doc-app-classes-content-controller.mjs +299 -0
- package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -0
- package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +51 -35
- package/fesm2022/ng-doc-app-classes-default-search-engine.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes-root-page.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes-search-engine.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes.mjs +2 -0
- package/fesm2022/ng-doc-app-classes.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-api-list.mjs +173 -69
- package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs +13 -10
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-code.mjs +152 -25
- package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-command-palette.mjs +498 -0
- package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-copy-button.mjs +38 -21
- package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs +36 -29
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-frame.mjs +96 -0
- package/fesm2022/ng-doc-app-components-demo-frame.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-demo-pane.mjs +39 -36
- package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo.mjs +175 -44
- package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +12 -9
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +79 -0
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs +54 -21
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-image-viewer.mjs +183 -89
- package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-kind-icon.mjs +42 -25
- package/fesm2022/ng-doc-app-components-kind-icon.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-link.mjs +12 -9
- package/fesm2022/ng-doc-app-components-link.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-members.mjs +30 -0
- package/fesm2022/ng-doc-app-components-members.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs +61 -20
- package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-navbar.mjs +39 -49
- package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-header.mjs +73 -10
- package/fesm2022/ng-doc-app-components-page-header.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-link.mjs +123 -31
- package/fesm2022/ng-doc-app-components-page-link.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-navigation.mjs +17 -19
- package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs +56 -35
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page.mjs +168 -24
- package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-playground.mjs +477 -286
- package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-root.mjs +48 -31
- package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search-result.mjs +22 -21
- package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search.mjs +64 -33
- package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-sidebar.mjs +86 -61
- package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-tabs.mjs +35 -11
- package/fesm2022/ng-doc-app-components-tabs.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs +33 -21
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-toc.mjs +267 -90
- package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components.mjs +4 -1
- package/fesm2022/ng-doc-app-components.mjs.map +1 -1
- package/fesm2022/ng-doc-app-defaults-default-processors.mjs +2 -1
- package/fesm2022/ng-doc-app-defaults-default-processors.mjs.map +1 -1
- package/fesm2022/ng-doc-app-demo-app.mjs +176 -0
- package/fesm2022/ng-doc-app-demo-app.mjs.map +1 -0
- package/fesm2022/ng-doc-app-directives-code-highlighter.mjs +11 -5
- package/fesm2022/ng-doc-app-directives-code-highlighter.mjs.map +1 -1
- package/fesm2022/ng-doc-app-directives-members-filter.mjs +276 -0
- package/fesm2022/ng-doc-app-directives-members-filter.mjs.map +1 -0
- package/fesm2022/ng-doc-app-directives-route-active.mjs +34 -30
- package/fesm2022/ng-doc-app-directives-route-active.mjs.map +1 -1
- package/fesm2022/ng-doc-app-directives.mjs +1 -0
- package/fesm2022/ng-doc-app-directives.mjs.map +1 -1
- package/fesm2022/ng-doc-app-helpers.mjs +307 -2
- package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs +6 -4
- package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-extract-value.mjs +6 -4
- package/fesm2022/ng-doc-app-pipes-extract-value.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs +7 -4
- package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs +8 -5
- package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-page-processor.mjs +86 -23
- package/fesm2022/ng-doc-app-processors-page-processor.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs +26 -25
- package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-processors.mjs +42 -10
- package/fesm2022/ng-doc-app-processors-processors.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +31 -2
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-title.mjs +129 -0
- package/fesm2022/ng-doc-app-providers-title.mjs.map +1 -0
- package/fesm2022/ng-doc-app-providers.mjs +1 -0
- package/fesm2022/ng-doc-app-providers.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs +122 -0
- package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-content-state.mjs +57 -0
- package/fesm2022/ng-doc-app-services-content-state.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-fullscreen-route.mjs +74 -0
- package/fesm2022/ng-doc-app-services-fullscreen-route.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-highlighter.mjs +90 -17
- package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-route-preloader.mjs +384 -0
- package/fesm2022/ng-doc-app-services-route-preloader.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-shortcuts.mjs +214 -0
- package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-sidebar.mjs +59 -25
- package/fesm2022/ng-doc-app-services-sidebar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-store.mjs +8 -8
- package/fesm2022/ng-doc-app-services-store.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-theme.mjs +32 -12
- package/fesm2022/ng-doc-app-services-theme.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services.mjs +143 -0
- package/fesm2022/ng-doc-app-services.mjs.map +1 -1
- package/fesm2022/ng-doc-app-tokens.mjs +7 -28
- package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs +18 -9
- package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-number-control.mjs +12 -9
- package/fesm2022/ng-doc-app-type-controls-number-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-string-control.mjs +12 -9
- package/fesm2022/ng-doc-app-type-controls-string-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +59 -18
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
- package/package.json +82 -28
- package/styles/global.css +1128 -201
- package/styles/main.css +1128 -201
- package/styles/themes/dark.css +88 -56
- package/types/ng-doc-app-classes-content-anchor-controller.d.ts +54 -0
- package/types/ng-doc-app-classes-content-controller.d.ts +94 -0
- package/types/ng-doc-app-classes-default-search-engine.d.ts +9 -1
- package/types/ng-doc-app-classes-root-page.d.ts +14 -1
- package/types/ng-doc-app-classes-search-engine.d.ts +5 -0
- package/types/ng-doc-app-classes.d.ts +2 -0
- package/types/ng-doc-app-components-api-list.d.ts +69 -20
- package/types/ng-doc-app-components-breadcrumb.d.ts +8 -3
- package/types/ng-doc-app-components-code.d.ts +46 -12
- package/types/ng-doc-app-components-command-palette.d.ts +147 -0
- package/types/ng-doc-app-components-copy-button.d.ts +18 -6
- package/types/ng-doc-app-components-demo-displayer.d.ts +23 -10
- package/types/ng-doc-app-components-demo-frame.d.ts +48 -0
- package/types/ng-doc-app-components-demo-pane.d.ts +19 -15
- package/types/ng-doc-app-components-demo.d.ts +80 -15
- package/types/ng-doc-app-components-fullscreen-button.d.ts +4 -2
- package/types/ng-doc-app-components-fullscreen-toggle.d.ts +24 -0
- package/types/ng-doc-app-components-heading-anchor.d.ts +26 -7
- package/types/ng-doc-app-components-image-viewer.d.ts +71 -7
- package/types/ng-doc-app-components-kind-icon.d.ts +28 -7
- package/types/ng-doc-app-components-link.d.ts +6 -2
- package/types/ng-doc-app-components-members.d.ts +13 -0
- package/types/ng-doc-app-components-mermaid-viewer.d.ts +21 -6
- package/types/ng-doc-app-components-navbar.d.ts +25 -15
- package/types/ng-doc-app-components-page-header.d.ts +28 -4
- package/types/ng-doc-app-components-page-link.d.ts +34 -15
- package/types/ng-doc-app-components-page-navigation.d.ts +10 -5
- package/types/ng-doc-app-components-page-wrapper.d.ts +35 -11
- package/types/ng-doc-app-components-page.d.ts +57 -6
- package/types/ng-doc-app-components-playground.d.ts +167 -78
- package/types/ng-doc-app-components-root.d.ts +28 -7
- package/types/ng-doc-app-components-search-result.d.ts +14 -6
- package/types/ng-doc-app-components-search.d.ts +19 -7
- package/types/ng-doc-app-components-tabs.d.ts +24 -5
- package/types/ng-doc-app-components-theme-toggle.d.ts +18 -5
- package/types/ng-doc-app-components-toc.d.ts +76 -29
- package/types/ng-doc-app-components.d.ts +4 -1
- package/types/ng-doc-app-demo-app.d.ts +89 -0
- package/types/ng-doc-app-directives-code-highlighter.d.ts +8 -4
- package/types/ng-doc-app-directives-members-filter.d.ts +61 -0
- package/types/ng-doc-app-directives-route-active.d.ts +21 -7
- package/types/ng-doc-app-directives.d.ts +1 -0
- package/types/ng-doc-app-helpers.d.ts +142 -3
- package/types/ng-doc-app-interfaces.d.ts +62 -31
- package/types/ng-doc-app-pipes-decode-uri-component.d.ts +3 -0
- package/types/ng-doc-app-pipes-extract-value.d.ts +3 -0
- package/types/ng-doc-app-pipes-filter-by-text.d.ts +4 -0
- package/types/ng-doc-app-pipes-sanitize-html.d.ts +4 -2
- package/types/ng-doc-app-processors-page-processor.d.ts +39 -10
- package/types/ng-doc-app-processors-processors-tooltip.d.ts +11 -10
- package/types/ng-doc-app-processors-processors.d.ts +10 -1
- package/types/ng-doc-app-providers-ng-doc-app.d.ts +18 -1
- package/types/ng-doc-app-providers-title.d.ts +59 -0
- package/types/ng-doc-app-providers.d.ts +1 -0
- package/types/ng-doc-app-services-content-scroll-intent.d.ts +52 -0
- package/types/ng-doc-app-services-content-state.d.ts +40 -0
- package/types/ng-doc-app-services-fullscreen-route.d.ts +35 -0
- package/types/ng-doc-app-services-highlighter.d.ts +47 -5
- package/types/ng-doc-app-services-route-preloader.d.ts +121 -0
- package/types/ng-doc-app-services-shortcuts.d.ts +96 -0
- package/types/ng-doc-app-services-sidebar.d.ts +24 -4
- package/types/ng-doc-app-services-store.d.ts +24 -1
- package/types/ng-doc-app-services-theme.d.ts +19 -3
- package/types/ng-doc-app-services.d.ts +77 -0
- package/types/ng-doc-app-tokens.d.ts +10 -30
- package/types/ng-doc-app-type-controls-boolean-control.d.ts +11 -6
- package/types/ng-doc-app-type-controls-number-control.d.ts +5 -3
- package/types/ng-doc-app-type-controls-string-control.d.ts +5 -3
- package/types/ng-doc-app-type-controls-type-alias-control.d.ts +35 -6
- package/fesm2022/ng-doc-app-components-search-dialog.mjs +0 -78
- package/fesm2022/ng-doc-app-components-search-dialog.mjs.map +0 -1
- package/types/ng-doc-app-components-search-dialog.d.ts +0 -28
|
@@ -1,29 +1,105 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject,
|
|
2
|
+
import { signal, inject, isDevMode, Service } from '@angular/core';
|
|
3
3
|
import { NG_DOC_SHIKI_THEME } from '@ng-doc/app/tokens';
|
|
4
|
+
import { ngDocSyntaxTheme } from '@ng-doc/core/constants/syntax-theme';
|
|
4
5
|
import { createHighlighterCore } from 'shiki/core';
|
|
6
|
+
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
|
|
5
7
|
import getWasm from 'shiki/wasm';
|
|
6
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Highlights code with Shiki. `provideNgDocApp` initializes it before the application renders.
|
|
11
|
+
*/
|
|
7
12
|
class NgDocHighlighterService {
|
|
8
13
|
constructor() {
|
|
14
|
+
this.ownsHighlighter = false;
|
|
15
|
+
this.destroyed = false;
|
|
16
|
+
this.readyState = signal(false, /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "readyState" }] : /* istanbul ignore next */ []));
|
|
9
18
|
this.theme = inject(NG_DOC_SHIKI_THEME);
|
|
19
|
+
/**
|
|
20
|
+
* Whether `highlight()` can highlight: `true` once `initialize()` has finished, and
|
|
21
|
+
* `false` again after the service is destroyed.
|
|
22
|
+
*/
|
|
23
|
+
this.ready = this.readyState.asReadonly();
|
|
10
24
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Loads Shiki with the built-in themes (`github-light`, `ayu-dark` and NgDoc's `css-variables`)
|
|
27
|
+
* and the given ones, and the given languages. Concurrent and repeated calls share one
|
|
28
|
+
* initialization; a failed one can be retried.
|
|
29
|
+
* @param config - Custom Shiki themes and languages to load.
|
|
30
|
+
*/
|
|
31
|
+
initialize(config) {
|
|
32
|
+
if (this.destroyed) {
|
|
33
|
+
return Promise.reject(new Error('NgDoc highlighter has been destroyed.'));
|
|
34
|
+
}
|
|
35
|
+
if (this.initialization) {
|
|
36
|
+
return this.initialization;
|
|
37
|
+
}
|
|
38
|
+
if (config?.theme && isDevMode()) {
|
|
39
|
+
console.warn('[NgDoc] `shiki.theme` of `provideNgDocApp` has no effect. Set the theme names in ' +
|
|
40
|
+
'`shiki.themes` of `ng-doc.config.ts`: NgDoc uses them for the code blocks and the ' +
|
|
41
|
+
'code that the browser highlights.');
|
|
14
42
|
}
|
|
15
|
-
|
|
43
|
+
// Only immutable built-in themes and languages are shared across SSR applications. Custom
|
|
44
|
+
// ones may reuse names with other definitions, or be asynchronous getters.
|
|
45
|
+
this.ownsHighlighter = Boolean(config?.themes?.length || config?.langs?.length);
|
|
46
|
+
const creation = this.ownsHighlighter
|
|
47
|
+
? NgDocHighlighterService.create(config?.themes, config?.langs)
|
|
48
|
+
: (NgDocHighlighterService.defaultInitialization ??= NgDocHighlighterService.create().catch((error) => {
|
|
49
|
+
NgDocHighlighterService.defaultInitialization = undefined;
|
|
50
|
+
throw error;
|
|
51
|
+
}));
|
|
52
|
+
this.initialization = creation
|
|
53
|
+
.then((highlighter) => {
|
|
54
|
+
if (this.destroyed) {
|
|
55
|
+
if (this.ownsHighlighter) {
|
|
56
|
+
highlighter.dispose();
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.highlighter = highlighter;
|
|
61
|
+
this.readyState.set(true);
|
|
62
|
+
})
|
|
63
|
+
.catch((error) => {
|
|
64
|
+
this.initialization = undefined;
|
|
65
|
+
throw error;
|
|
66
|
+
});
|
|
67
|
+
return this.initialization;
|
|
68
|
+
}
|
|
69
|
+
/** Disposes the highlighter this service created for custom themes or languages. */
|
|
70
|
+
ngOnDestroy() {
|
|
71
|
+
this.destroyed = true;
|
|
72
|
+
this.readyState.set(false);
|
|
73
|
+
if (this.ownsHighlighter) {
|
|
74
|
+
this.highlighter?.dispose();
|
|
75
|
+
}
|
|
76
|
+
this.highlighter = undefined;
|
|
77
|
+
}
|
|
78
|
+
static async create(themes = [], langs = []) {
|
|
79
|
+
return createHighlighterCore({
|
|
16
80
|
themes: [
|
|
17
81
|
import('shiki/themes/github-light.mjs'),
|
|
18
82
|
import('shiki/themes/ayu-dark.mjs'),
|
|
19
|
-
|
|
83
|
+
// A new copy: Shiki changes the theme object it loads.
|
|
84
|
+
ngDocSyntaxTheme(),
|
|
85
|
+
...themes,
|
|
20
86
|
],
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
87
|
+
// Later registrations of a name replace earlier ones, so the given languages win.
|
|
88
|
+
langs: [import('shiki/langs/angular-html.mjs'), ...langs],
|
|
89
|
+
// The Oniguruma engine, as at build time, so that the browser tokenizes code as the code
|
|
90
|
+
// blocks were tokenized.
|
|
91
|
+
engine: createOnigurumaEngine(getWasm),
|
|
92
|
+
});
|
|
24
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Returns the code as highlighted HTML, or an empty string before `initialize()` has
|
|
96
|
+
* finished. It reads `ready()`, so a `computed` over it updates once Shiki is loaded.
|
|
97
|
+
* @param code - Angular HTML to highlight.
|
|
98
|
+
*/
|
|
25
99
|
highlight(code) {
|
|
26
|
-
|
|
100
|
+
if (!this.readyState())
|
|
101
|
+
return '';
|
|
102
|
+
return (this.highlighter?.codeToHtml(code, {
|
|
27
103
|
lang: 'angular-html',
|
|
28
104
|
themes: {
|
|
29
105
|
light: this.theme.light || 'github-light',
|
|
@@ -31,14 +107,11 @@ class NgDocHighlighterService {
|
|
|
31
107
|
},
|
|
32
108
|
}) ?? '');
|
|
33
109
|
}
|
|
34
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
35
|
-
static { this.ɵprov = i0.ɵɵ
|
|
110
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocHighlighterService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
111
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocHighlighterService }); }
|
|
36
112
|
}
|
|
37
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
38
|
-
type:
|
|
39
|
-
args: [{
|
|
40
|
-
providedIn: 'root',
|
|
41
|
-
}]
|
|
113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocHighlighterService, decorators: [{
|
|
114
|
+
type: Service
|
|
42
115
|
}] });
|
|
43
116
|
|
|
44
117
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-doc-app-services-highlighter.mjs","sources":["../../../../libs/app/services/highlighter/highlighter.service.ts","../../../../libs/app/services/highlighter/ng-doc-app-services-highlighter.ts"],"sourcesContent":["import { inject,
|
|
1
|
+
{"version":3,"file":"ng-doc-app-services-highlighter.mjs","sources":["../../../../libs/app/services/highlighter/highlighter.service.ts","../../../../libs/app/services/highlighter/ng-doc-app-services-highlighter.ts"],"sourcesContent":["import { inject, isDevMode, OnDestroy, Service, Signal, signal } from '@angular/core';\nimport { NG_DOC_SHIKI_THEME } from '@ng-doc/app/tokens';\nimport { ngDocSyntaxTheme } from '@ng-doc/core/constants/syntax-theme';\nimport {\n type HighlighterCore,\n type LanguageInput,\n type ThemeInput,\n createHighlighterCore,\n} from 'shiki/core';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport getWasm from 'shiki/wasm';\n\n/** Shiki configuration of the code highlighter. */\nexport interface NgDocHighlighterConfig {\n /**\n * Themes sources.\n */\n themes?: ThemeInput[];\n /**\n * Shiki languages to load besides `angular-html`, the language of playground code, and the\n * languages it embeds. A language named like one of them replaces it, so register here the\n * languages of `shiki.langs` in `ng-doc.config.ts` that playground code uses, for example a newer\n * Angular template grammar.\n */\n langs?: LanguageInput[];\n /**\n * Has no effect, and is reported in development mode.\n *\n * The theme names come from `shiki.themes` in `ng-doc.config.ts`: NgDoc highlights code blocks\n * with them when it builds the site, and passes them to the application (`NG_DOC_SHIKI_THEME`),\n * so the code that the browser highlights matches the code blocks.\n * @deprecated Set the theme names in `shiki.themes` of `ng-doc.config.ts` instead, and load\n * other themes with `themes`. This field will be removed in a future major release.\n */\n theme?: {\n light: string;\n dark: string;\n };\n}\n\n/**\n * Highlights code with Shiki. `provideNgDocApp` initializes it before the application renders.\n */\n@Service()\nexport class NgDocHighlighterService implements OnDestroy {\n private static defaultInitialization?: Promise<HighlighterCore>;\n\n private highlighter?: HighlighterCore;\n private initialization?: Promise<void>;\n private ownsHighlighter = false;\n private destroyed = false;\n private readonly readyState = signal(false);\n\n protected readonly theme = inject(NG_DOC_SHIKI_THEME);\n\n /**\n * Whether `highlight()` can highlight: `true` once `initialize()` has finished, and\n * `false` again after the service is destroyed.\n */\n readonly ready: Signal<boolean> = this.readyState.asReadonly();\n\n /**\n * Loads Shiki with the built-in themes (`github-light`, `ayu-dark` and NgDoc's `css-variables`)\n * and the given ones, and the given languages. Concurrent and repeated calls share one\n * initialization; a failed one can be retried.\n * @param config - Custom Shiki themes and languages to load.\n */\n initialize(config?: NgDocHighlighterConfig): Promise<void> {\n if (this.destroyed) {\n return Promise.reject(new Error('NgDoc highlighter has been destroyed.'));\n }\n if (this.initialization) {\n return this.initialization;\n }\n if (config?.theme && isDevMode()) {\n console.warn(\n '[NgDoc] `shiki.theme` of `provideNgDocApp` has no effect. Set the theme names in ' +\n '`shiki.themes` of `ng-doc.config.ts`: NgDoc uses them for the code blocks and the ' +\n 'code that the browser highlights.',\n );\n }\n\n // Only immutable built-in themes and languages are shared across SSR applications. Custom\n // ones may reuse names with other definitions, or be asynchronous getters.\n this.ownsHighlighter = Boolean(config?.themes?.length || config?.langs?.length);\n const creation = this.ownsHighlighter\n ? NgDocHighlighterService.create(config?.themes, config?.langs)\n : (NgDocHighlighterService.defaultInitialization ??= NgDocHighlighterService.create().catch(\n (error: unknown) => {\n NgDocHighlighterService.defaultInitialization = undefined;\n throw error;\n },\n ));\n this.initialization = creation\n .then((highlighter) => {\n if (this.destroyed) {\n if (this.ownsHighlighter) {\n highlighter.dispose();\n }\n return;\n }\n this.highlighter = highlighter;\n this.readyState.set(true);\n })\n .catch((error: unknown) => {\n this.initialization = undefined;\n throw error;\n });\n return this.initialization;\n }\n\n /** Disposes the highlighter this service created for custom themes or languages. */\n ngOnDestroy(): void {\n this.destroyed = true;\n this.readyState.set(false);\n if (this.ownsHighlighter) {\n this.highlighter?.dispose();\n }\n this.highlighter = undefined;\n }\n\n private static async create(\n themes: ThemeInput[] = [],\n langs: LanguageInput[] = [],\n ): Promise<HighlighterCore> {\n return createHighlighterCore({\n themes: [\n import('shiki/themes/github-light.mjs'),\n import('shiki/themes/ayu-dark.mjs'),\n // A new copy: Shiki changes the theme object it loads.\n ngDocSyntaxTheme(),\n ...themes,\n ],\n // Later registrations of a name replace earlier ones, so the given languages win.\n langs: [import('shiki/langs/angular-html.mjs'), ...langs],\n // The Oniguruma engine, as at build time, so that the browser tokenizes code as the code\n // blocks were tokenized.\n engine: createOnigurumaEngine(getWasm),\n });\n }\n\n /**\n * Returns the code as highlighted HTML, or an empty string before `initialize()` has\n * finished. It reads `ready()`, so a `computed` over it updates once Shiki is loaded.\n * @param code - Angular HTML to highlight.\n */\n highlight(code: string): string {\n if (!this.readyState()) return '';\n\n return (\n this.highlighter?.codeToHtml(code, {\n lang: 'angular-html',\n themes: {\n light: this.theme.light || 'github-light',\n dark: this.theme.dark || 'ayu-dark',\n },\n }) ?? ''\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, DOCUMENT, Injector, NgZone, PLATFORM_ID, DestroyRef, Service } from '@angular/core';
|
|
3
|
+
import { of, catchError } from 'rxjs';
|
|
4
|
+
import { LocationStrategy, isPlatformBrowser } from '@angular/common';
|
|
5
|
+
import { Router, RouterPreloader, PRIMARY_OUTLET } from '@angular/router';
|
|
6
|
+
import { ɵngDocRouteContentSources as _ngDocRouteContentSources, ɵpreloadNgDocContent as _preloadNgDocContent, ɵngDocRouteUrl as _ngDocRouteUrl } from '@ng-doc/app/helpers';
|
|
7
|
+
import { NG_DOC_ROUTE_PREFIX } from '@ng-doc/app/tokens';
|
|
8
|
+
|
|
9
|
+
/** The events that tell that the reader is about to open a link. */
|
|
10
|
+
const INTENT_EVENTS = ['pointerover', 'mouseover', 'focusin', 'touchstart'];
|
|
11
|
+
/** How long the browser may stay busy before an idle preload runs anyway, in milliseconds. */
|
|
12
|
+
const IDLE_TIMEOUT = 2000;
|
|
13
|
+
/** The delay of an idle preload where the browser has no `requestIdleCallback`. */
|
|
14
|
+
const IDLE_FALLBACK_DELAY = 200;
|
|
15
|
+
/**
|
|
16
|
+
* Preloads the pages that the reader is about to open, so that opening them shows them at once.
|
|
17
|
+
*
|
|
18
|
+
* It preloads a page when the reader points at, focuses or touches a link to it, anywhere in the
|
|
19
|
+
* document: the sidebar, the previous and next page links, links in the page content, search
|
|
20
|
+
* results and the table of contents. It also preloads the previous and next pages of a guide once
|
|
21
|
+
* the browser is idle after the guide has rendered. A preload loads the code of the page's route
|
|
22
|
+
* and its content, once per page. Nothing is preloaded on the server, when the reader asked the
|
|
23
|
+
* browser to save data, or on a 2G connection.
|
|
24
|
+
*
|
|
25
|
+
* The router loads the code through its preloading strategy, so preloading works only when the
|
|
26
|
+
* application sets `NgDocPreloadingStrategy` as that strategy:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* provideRouter(routes, withPreloading(NgDocPreloadingStrategy));
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class NgDocRoutePreloader {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.document = inject(DOCUMENT);
|
|
35
|
+
this.injector = inject(Injector);
|
|
36
|
+
this.ngZone = inject(NgZone);
|
|
37
|
+
this.router = inject(Router);
|
|
38
|
+
this.locationStrategy = inject(LocationStrategy);
|
|
39
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
40
|
+
/** The path segments under which the documentation's routes live (`routePrefix`). */
|
|
41
|
+
this.prefix = (inject(NG_DOC_ROUTE_PREFIX, { optional: true }) ?? '')
|
|
42
|
+
.split('/')
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
/** The pages that were asked for, by path, so each page is preloaded once. */
|
|
45
|
+
this.requested = new Set();
|
|
46
|
+
/** The paths, as URL segments, whose routes are being loaded. */
|
|
47
|
+
this.loading = new Map();
|
|
48
|
+
this.enabled = false;
|
|
49
|
+
this.destroyed = false;
|
|
50
|
+
this.intent = (event) => {
|
|
51
|
+
const target = event.target;
|
|
52
|
+
const anchor = typeof Element !== 'undefined' && target instanceof Element
|
|
53
|
+
? target.closest('a[href]')
|
|
54
|
+
: null;
|
|
55
|
+
// Pointer events fire for every element under the pointer, so a link is checked only when
|
|
56
|
+
// the pointer reaches it.
|
|
57
|
+
if (!anchor || (anchor === this.lastAnchor && event.type !== 'focusin'))
|
|
58
|
+
return;
|
|
59
|
+
this.lastAnchor = anchor;
|
|
60
|
+
const url = this.linkUrl(anchor);
|
|
61
|
+
if (url !== undefined)
|
|
62
|
+
this.preload(url);
|
|
63
|
+
};
|
|
64
|
+
inject(DestroyRef).onDestroy(() => this.destroy());
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Turns preloading on and starts listening for links the reader is about to open. Called by
|
|
68
|
+
* `NgDocPreloadingStrategy` when the router creates it; does nothing on the server.
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
enable() {
|
|
72
|
+
if (this.enabled || this.destroyed || !this.browser)
|
|
73
|
+
return;
|
|
74
|
+
this.enabled = true;
|
|
75
|
+
const view = this.document.defaultView;
|
|
76
|
+
if (!view)
|
|
77
|
+
return;
|
|
78
|
+
// A link the reader points at must not run change detection in a zone application.
|
|
79
|
+
this.ngZone.runOutsideAngular(() => {
|
|
80
|
+
INTENT_EVENTS.forEach((type) => this.document.addEventListener(type, this.intent, { capture: true, passive: true }));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Preloads the code and the content of the page at a URL of the application. A page is
|
|
85
|
+
* preloaded once; the current page is not preloaded.
|
|
86
|
+
* @param url - The URL, relative to the application's base, for example `/docs/get-started`.
|
|
87
|
+
* @returns Whether a preload started.
|
|
88
|
+
*/
|
|
89
|
+
preload(url) {
|
|
90
|
+
if (!this.active())
|
|
91
|
+
return false;
|
|
92
|
+
let segments;
|
|
93
|
+
try {
|
|
94
|
+
segments = urlSegments(typeof url === 'string' ? this.router.parseUrl(url) : url);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
// Only the documentation's own pages are preloaded, never the rest of the application.
|
|
100
|
+
if (this.prefix.some((part, index) => segments[index] !== part))
|
|
101
|
+
return false;
|
|
102
|
+
const key = segments.join('/');
|
|
103
|
+
if (this.requested.has(key) || key === urlSegments(this.currentUrl()).join('/'))
|
|
104
|
+
return false;
|
|
105
|
+
this.requested.add(key);
|
|
106
|
+
this.loading.set(key, segments);
|
|
107
|
+
this.ngZone.runOutsideAngular(() => {
|
|
108
|
+
this.injector
|
|
109
|
+
.get(RouterPreloader)
|
|
110
|
+
.preload()
|
|
111
|
+
.subscribe({ complete: () => this.loaded(key, segments) });
|
|
112
|
+
});
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Preloads pages once the browser is idle. A later call replaces the pages of an earlier one
|
|
117
|
+
* that has not run yet.
|
|
118
|
+
* @param urls - The URLs of the pages; missing ones are skipped.
|
|
119
|
+
*/
|
|
120
|
+
preloadWhenIdle(urls) {
|
|
121
|
+
this.cancelIdle?.();
|
|
122
|
+
this.cancelIdle = undefined;
|
|
123
|
+
const view = this.document.defaultView;
|
|
124
|
+
const pages = urls.filter((url) => !!url);
|
|
125
|
+
if (!this.active() || !view || !pages.length)
|
|
126
|
+
return;
|
|
127
|
+
const run = () => {
|
|
128
|
+
this.cancelIdle = undefined;
|
|
129
|
+
pages.forEach((url) => this.preload(url));
|
|
130
|
+
};
|
|
131
|
+
this.ngZone.runOutsideAngular(() => {
|
|
132
|
+
if (typeof view.requestIdleCallback === 'function') {
|
|
133
|
+
const handle = view.requestIdleCallback(run, { timeout: IDLE_TIMEOUT });
|
|
134
|
+
this.cancelIdle = () => view.cancelIdleCallback(handle);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const handle = view.setTimeout(run, IDLE_FALLBACK_DELAY);
|
|
138
|
+
this.cancelIdle = () => view.clearTimeout(handle);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether the router should load a lazy route now: the route leads to a page that is being
|
|
144
|
+
* preloaded.
|
|
145
|
+
* @param route - A lazy route that the router has not loaded yet.
|
|
146
|
+
* @returns Whether to load it.
|
|
147
|
+
* @internal
|
|
148
|
+
*/
|
|
149
|
+
shouldPreload(route) {
|
|
150
|
+
if (!this.enabled || this.destroyed)
|
|
151
|
+
return false;
|
|
152
|
+
for (const segments of this.loading.values()) {
|
|
153
|
+
if (routesOnPath(this.router.config, segments).has(route))
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
loaded(key, segments) {
|
|
159
|
+
this.loading.delete(key);
|
|
160
|
+
if (this.destroyed)
|
|
161
|
+
return;
|
|
162
|
+
const routes = routesOnPath(this.router.config, segments);
|
|
163
|
+
// The strategy turns a failed load into no load, so the router's own preloading survives
|
|
164
|
+
// it. A route that is still not loaded failed: a later intent may try again.
|
|
165
|
+
if ([...routes].some((route) => route.loadChildren && !loadedRoutes(route))) {
|
|
166
|
+
this.requested.delete(key);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// The routes of the page have loaded, so the content sources of their components are known.
|
|
170
|
+
for (const route of routes) {
|
|
171
|
+
for (const source of _ngDocRouteContentSources(route)) {
|
|
172
|
+
_preloadNgDocContent(source).catch(() => undefined);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
active() {
|
|
177
|
+
return this.enabled && !this.destroyed && !constrainedConnection(this.document);
|
|
178
|
+
}
|
|
179
|
+
currentUrl() {
|
|
180
|
+
return this.router.parseUrl(this.router.url);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns the application URL of a link, or `undefined` for a link that leaves the
|
|
184
|
+
* application, opens elsewhere, downloads a file or points into the current page.
|
|
185
|
+
*
|
|
186
|
+
* The link is read through the location strategy: with `PathLocationStrategy` the route is the
|
|
187
|
+
* link's path, and with `HashLocationStrategy` (`withHashLocation()`) it is the link's fragment
|
|
188
|
+
* (`#/docs/page`), so a fragment that is not a route (`#usage`) is an anchor on the page.
|
|
189
|
+
* @param anchor - The link.
|
|
190
|
+
* @returns The URL relative to the application's base, with its query.
|
|
191
|
+
*/
|
|
192
|
+
linkUrl(anchor) {
|
|
193
|
+
const view = this.document.defaultView;
|
|
194
|
+
const href = anchor.getAttribute('href');
|
|
195
|
+
const target = anchor.getAttribute('target');
|
|
196
|
+
if (!view || !href || anchor.hasAttribute('download') || (target && target !== '_self')) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
let url;
|
|
200
|
+
try {
|
|
201
|
+
url = new URL(href, this.document.baseURI);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
if (url.origin !== view.location.origin)
|
|
207
|
+
return undefined;
|
|
208
|
+
if (this.hashLocation()) {
|
|
209
|
+
// The route lives in the fragment of this document; another document leaves the
|
|
210
|
+
// application.
|
|
211
|
+
if (url.pathname !== view.location.pathname || !url.hash.startsWith('#/'))
|
|
212
|
+
return undefined;
|
|
213
|
+
url = new URL(url.hash.slice(1).replace(/^\/*/, '/'), view.location.origin);
|
|
214
|
+
}
|
|
215
|
+
else if (href.startsWith('#')) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
// The current page: an anchor on it, or a link to itself.
|
|
219
|
+
if (url.pathname === _ngDocRouteUrl(this.locationStrategy, view.location.href).pathname) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const base = this.basePath();
|
|
223
|
+
if (!`${url.pathname}/`.startsWith(base))
|
|
224
|
+
return undefined;
|
|
225
|
+
return `/${url.pathname.slice(base.length)}${url.search}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Whether the location strategy keeps the route in the document's fragment, as
|
|
229
|
+
* `HashLocationStrategy` does.
|
|
230
|
+
*/
|
|
231
|
+
hashLocation() {
|
|
232
|
+
return this.locationStrategy.prepareExternalUrl('/').startsWith('#');
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* The base path of the application's routes, ending with a slash: the base href, which a
|
|
236
|
+
* fragment route carries only when `APP_BASE_HREF` sets one.
|
|
237
|
+
*/
|
|
238
|
+
basePath() {
|
|
239
|
+
const base = this.locationStrategy.getBaseHref() || '/';
|
|
240
|
+
const path = this.hashLocation()
|
|
241
|
+
? base.replace(/^\/*/, '/')
|
|
242
|
+
: new URL(base, this.document.baseURI).pathname;
|
|
243
|
+
return path.endsWith('/') ? path : `${path}/`;
|
|
244
|
+
}
|
|
245
|
+
destroy() {
|
|
246
|
+
this.destroyed = true;
|
|
247
|
+
this.cancelIdle?.();
|
|
248
|
+
this.cancelIdle = undefined;
|
|
249
|
+
this.loading.clear();
|
|
250
|
+
INTENT_EVENTS.forEach((type) => this.document.removeEventListener(type, this.intent, { capture: true }));
|
|
251
|
+
}
|
|
252
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocRoutePreloader, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
253
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocRoutePreloader }); }
|
|
254
|
+
}
|
|
255
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocRoutePreloader, decorators: [{
|
|
256
|
+
type: Service
|
|
257
|
+
}], ctorParameters: () => [] });
|
|
258
|
+
/**
|
|
259
|
+
* Whether the reader asked the browser to save data or the connection is 2G, where preloading
|
|
260
|
+
* would compete with what the reader opens.
|
|
261
|
+
* @param document - The document of the application.
|
|
262
|
+
* @returns Whether to skip preloading.
|
|
263
|
+
*/
|
|
264
|
+
function constrainedConnection(document) {
|
|
265
|
+
const navigator = document.defaultView?.navigator;
|
|
266
|
+
const connection = navigator?.connection;
|
|
267
|
+
return (!!connection &&
|
|
268
|
+
(connection.saveData === true || /(^|-)2g$/.test(connection.effectiveType ?? '')));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Returns the path segments of the primary outlet of a URL.
|
|
272
|
+
* @param url - The URL.
|
|
273
|
+
* @returns The segments.
|
|
274
|
+
*/
|
|
275
|
+
function urlSegments(url) {
|
|
276
|
+
return url.root.children[PRIMARY_OUTLET]?.segments.map(({ path }) => path) ?? [];
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Returns the routes that can match a path: those whose own path matches its start, through
|
|
280
|
+
* the children the router has loaded so far. A lazy route whose children have not loaded yet
|
|
281
|
+
* may lead anywhere below its path.
|
|
282
|
+
* @param routes - The routes to search.
|
|
283
|
+
* @param segments - The path segments.
|
|
284
|
+
* @param found - Collects the routes.
|
|
285
|
+
* @returns The routes.
|
|
286
|
+
*/
|
|
287
|
+
function routesOnPath(routes, segments, found = new Set()) {
|
|
288
|
+
for (const route of routes) {
|
|
289
|
+
if ((route.outlet ?? PRIMARY_OUTLET) !== PRIMARY_OUTLET ||
|
|
290
|
+
route.matcher ||
|
|
291
|
+
route.redirectTo !== undefined ||
|
|
292
|
+
// A guarded route may not match for this reader; only opening the page may decide.
|
|
293
|
+
route.canMatch?.length) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const consumed = matchedSegments(route.path ?? '', segments);
|
|
297
|
+
if (consumed === undefined)
|
|
298
|
+
continue;
|
|
299
|
+
const rest = segments.slice(consumed);
|
|
300
|
+
const children = route.children ?? loadedRoutes(route);
|
|
301
|
+
// A route without children matches only the whole remaining path.
|
|
302
|
+
const leaf = !route.children && !route.loadChildren;
|
|
303
|
+
if ((route.pathMatch === 'full' || leaf) && rest.length)
|
|
304
|
+
continue;
|
|
305
|
+
found.add(route);
|
|
306
|
+
if (children)
|
|
307
|
+
routesOnPath(children, rest, found);
|
|
308
|
+
}
|
|
309
|
+
return found;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Returns how many segments a route path matches at the start of a path.
|
|
313
|
+
* @param path - The route path.
|
|
314
|
+
* @param segments - The path segments.
|
|
315
|
+
* @returns The number of matched segments, or `undefined` when the route does not match.
|
|
316
|
+
*/
|
|
317
|
+
function matchedSegments(path, segments) {
|
|
318
|
+
if (!path)
|
|
319
|
+
return 0;
|
|
320
|
+
const parts = path.split('/');
|
|
321
|
+
for (let index = 0; index < parts.length; index++) {
|
|
322
|
+
const part = parts[index];
|
|
323
|
+
if (part === '**')
|
|
324
|
+
return segments.length;
|
|
325
|
+
if (index >= segments.length)
|
|
326
|
+
return undefined;
|
|
327
|
+
if (!part.startsWith(':') && part !== segments[index])
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
return parts.length;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Returns the children that the router loaded for a lazy route. The router keeps them on the
|
|
334
|
+
* route itself and has no public API for them; its own preloader reads the same property.
|
|
335
|
+
* Without it, preloading stops at the first lazy route.
|
|
336
|
+
* @param route - The route.
|
|
337
|
+
* @returns The loaded children, if any.
|
|
338
|
+
*/
|
|
339
|
+
function loadedRoutes(route) {
|
|
340
|
+
return route._loadedRoutes;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* The router preloading strategy of NgDoc: it loads a lazy route only when the reader is about
|
|
345
|
+
* to open one of its pages, as `NgDocRoutePreloader` decides, instead of every route at once.
|
|
346
|
+
*
|
|
347
|
+
* Set it as the router's preloading strategy to preload pages on intent:
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* provideRouter(routes, withPreloading(NgDocPreloadingStrategy));
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
class NgDocPreloadingStrategy {
|
|
354
|
+
constructor() {
|
|
355
|
+
this.preloader = inject(NgDocRoutePreloader);
|
|
356
|
+
// The router creates its preloading strategy at start-up, so this is where preloading on
|
|
357
|
+
// intent starts.
|
|
358
|
+
this.preloader.enable();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Loads a lazy route when it leads to a page that is being preloaded.
|
|
362
|
+
* @param route - A lazy route that the router has not loaded yet.
|
|
363
|
+
* @param load - Loads the route.
|
|
364
|
+
* @returns The load, or an observable of `null` when the route is not loaded now.
|
|
365
|
+
*/
|
|
366
|
+
preload(route, load) {
|
|
367
|
+
if (!this.preloader.shouldPreload(route))
|
|
368
|
+
return of(null);
|
|
369
|
+
// A failed preload is not an error: opening the page loads the route again and reports it.
|
|
370
|
+
return load().pipe(catchError(() => of(null)));
|
|
371
|
+
}
|
|
372
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPreloadingStrategy, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
373
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocPreloadingStrategy }); }
|
|
374
|
+
}
|
|
375
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPreloadingStrategy, decorators: [{
|
|
376
|
+
type: Service
|
|
377
|
+
}], ctorParameters: () => [] });
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Generated bundle index. Do not edit.
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
export { NgDocPreloadingStrategy, NgDocRoutePreloader };
|
|
384
|
+
//# sourceMappingURL=ng-doc-app-services-route-preloader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-doc-app-services-route-preloader.mjs","sources":["../../../../libs/app/services/route-preloader/route-preloader.service.ts","../../../../libs/app/services/route-preloader/preloading-strategy.ts","../../../../libs/app/services/route-preloader/ng-doc-app-services-route-preloader.ts"],"sourcesContent":["import { isPlatformBrowser, LocationStrategy } from '@angular/common';\nimport {\n DestroyRef,\n DOCUMENT,\n inject,\n Injector,\n NgZone,\n PLATFORM_ID,\n Service,\n} from '@angular/core';\nimport { PRIMARY_OUTLET, Route, Router, RouterPreloader, UrlTree } from '@angular/router';\nimport {\n ɵngDocRouteContentSources,\n ɵngDocRouteUrl,\n ɵpreloadNgDocContent,\n} from '@ng-doc/app/helpers';\nimport { NG_DOC_ROUTE_PREFIX } from '@ng-doc/app/tokens';\n\n/** The events that tell that the reader is about to open a link. */\nconst INTENT_EVENTS = ['pointerover', 'mouseover', 'focusin', 'touchstart'] as const;\n\n/** How long the browser may stay busy before an idle preload runs anyway, in milliseconds. */\nconst IDLE_TIMEOUT = 2000;\n/** The delay of an idle preload where the browser has no `requestIdleCallback`. */\nconst IDLE_FALLBACK_DELAY = 200;\n\n/** The parts of the Network Information API that preloading reads. */\ninterface NgDocNetworkInformation {\n readonly saveData?: boolean;\n readonly effectiveType?: string;\n}\n\n/**\n * Preloads the pages that the reader is about to open, so that opening them shows them at once.\n *\n * It preloads a page when the reader points at, focuses or touches a link to it, anywhere in the\n * document: the sidebar, the previous and next page links, links in the page content, search\n * results and the table of contents. It also preloads the previous and next pages of a guide once\n * the browser is idle after the guide has rendered. A preload loads the code of the page's route\n * and its content, once per page. Nothing is preloaded on the server, when the reader asked the\n * browser to save data, or on a 2G connection.\n *\n * The router loads the code through its preloading strategy, so preloading works only when the\n * application sets `NgDocPreloadingStrategy` as that strategy:\n *\n * ```ts\n * provideRouter(routes, withPreloading(NgDocPreloadingStrategy));\n * ```\n */\n@Service()\nexport class NgDocRoutePreloader {\n private readonly document = inject(DOCUMENT);\n private readonly injector = inject(Injector);\n private readonly ngZone = inject(NgZone);\n private readonly router = inject(Router);\n private readonly locationStrategy = inject(LocationStrategy);\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n /** The path segments under which the documentation's routes live (`routePrefix`). */\n private readonly prefix = (inject(NG_DOC_ROUTE_PREFIX, { optional: true }) ?? '')\n .split('/')\n .filter(Boolean);\n /** The pages that were asked for, by path, so each page is preloaded once. */\n private readonly requested = new Set<string>();\n /** The paths, as URL segments, whose routes are being loaded. */\n private readonly loading = new Map<string, readonly string[]>();\n private enabled = false;\n private destroyed = false;\n private lastAnchor?: Element;\n private cancelIdle?: () => void;\n\n private readonly intent = (event: Event): void => {\n const target = event.target;\n const anchor =\n typeof Element !== 'undefined' && target instanceof Element\n ? target.closest('a[href]')\n : null;\n\n // Pointer events fire for every element under the pointer, so a link is checked only when\n // the pointer reaches it.\n if (!anchor || (anchor === this.lastAnchor && event.type !== 'focusin')) return;\n this.lastAnchor = anchor;\n\n const url = this.linkUrl(anchor);\n\n if (url !== undefined) this.preload(url);\n };\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.destroy());\n }\n\n /**\n * Turns preloading on and starts listening for links the reader is about to open. Called by\n * `NgDocPreloadingStrategy` when the router creates it; does nothing on the server.\n * @internal\n */\n enable(): void {\n if (this.enabled || this.destroyed || !this.browser) return;\n this.enabled = true;\n\n const view = this.document.defaultView;\n\n if (!view) return;\n // A link the reader points at must not run change detection in a zone application.\n this.ngZone.runOutsideAngular(() => {\n INTENT_EVENTS.forEach((type: string) =>\n this.document.addEventListener(type, this.intent, { capture: true, passive: true }),\n );\n });\n }\n\n /**\n * Preloads the code and the content of the page at a URL of the application. A page is\n * preloaded once; the current page is not preloaded.\n * @param url - The URL, relative to the application's base, for example `/docs/get-started`.\n * @returns Whether a preload started.\n */\n preload(url: string | UrlTree): boolean {\n if (!this.active()) return false;\n\n let segments: string[];\n\n try {\n segments = urlSegments(typeof url === 'string' ? this.router.parseUrl(url) : url);\n } catch {\n return false;\n }\n\n // Only the documentation's own pages are preloaded, never the rest of the application.\n if (this.prefix.some((part, index) => segments[index] !== part)) return false;\n\n const key = segments.join('/');\n\n if (this.requested.has(key) || key === urlSegments(this.currentUrl()).join('/')) return false;\n this.requested.add(key);\n this.loading.set(key, segments);\n\n this.ngZone.runOutsideAngular(() => {\n this.injector\n .get(RouterPreloader)\n .preload()\n .subscribe({ complete: () => this.loaded(key, segments) });\n });\n\n return true;\n }\n\n /**\n * Preloads pages once the browser is idle. A later call replaces the pages of an earlier one\n * that has not run yet.\n * @param urls - The URLs of the pages; missing ones are skipped.\n */\n preloadWhenIdle(urls: ReadonlyArray<string | undefined>): void {\n this.cancelIdle?.();\n this.cancelIdle = undefined;\n\n const view = this.document.defaultView;\n const pages = urls.filter((url): url is string => !!url);\n\n if (!this.active() || !view || !pages.length) return;\n\n const run = (): void => {\n this.cancelIdle = undefined;\n pages.forEach((url: string) => this.preload(url));\n };\n\n this.ngZone.runOutsideAngular(() => {\n if (typeof view.requestIdleCallback === 'function') {\n const handle = view.requestIdleCallback(run, { timeout: IDLE_TIMEOUT });\n\n this.cancelIdle = () => view.cancelIdleCallback(handle);\n } else {\n const handle = view.setTimeout(run, IDLE_FALLBACK_DELAY);\n\n this.cancelIdle = () => view.clearTimeout(handle);\n }\n });\n }\n\n /**\n * Whether the router should load a lazy route now: the route leads to a page that is being\n * preloaded.\n * @param route - A lazy route that the router has not loaded yet.\n * @returns Whether to load it.\n * @internal\n */\n shouldPreload(route: Route): boolean {\n if (!this.enabled || this.destroyed) return false;\n for (const segments of this.loading.values()) {\n if (routesOnPath(this.router.config, segments).has(route)) return true;\n }\n return false;\n }\n\n private loaded(key: string, segments: readonly string[]): void {\n this.loading.delete(key);\n if (this.destroyed) return;\n\n const routes = routesOnPath(this.router.config, segments);\n\n // The strategy turns a failed load into no load, so the router's own preloading survives\n // it. A route that is still not loaded failed: a later intent may try again.\n if ([...routes].some((route) => route.loadChildren && !loadedRoutes(route))) {\n this.requested.delete(key);\n return;\n }\n // The routes of the page have loaded, so the content sources of their components are known.\n for (const route of routes) {\n for (const source of ɵngDocRouteContentSources(route)) {\n ɵpreloadNgDocContent(source).catch(() => undefined);\n }\n }\n }\n\n private active(): boolean {\n return this.enabled && !this.destroyed && !constrainedConnection(this.document);\n }\n\n private currentUrl(): UrlTree {\n return this.router.parseUrl(this.router.url);\n }\n\n /**\n * Returns the application URL of a link, or `undefined` for a link that leaves the\n * application, opens elsewhere, downloads a file or points into the current page.\n *\n * The link is read through the location strategy: with `PathLocationStrategy` the route is the\n * link's path, and with `HashLocationStrategy` (`withHashLocation()`) it is the link's fragment\n * (`#/docs/page`), so a fragment that is not a route (`#usage`) is an anchor on the page.\n * @param anchor - The link.\n * @returns The URL relative to the application's base, with its query.\n */\n private linkUrl(anchor: Element): string | undefined {\n const view = this.document.defaultView;\n const href = anchor.getAttribute('href');\n const target = anchor.getAttribute('target');\n\n if (!view || !href || anchor.hasAttribute('download') || (target && target !== '_self')) {\n return undefined;\n }\n\n let url: URL;\n\n try {\n url = new URL(href, this.document.baseURI);\n } catch {\n return undefined;\n }\n\n if (url.origin !== view.location.origin) return undefined;\n\n if (this.hashLocation()) {\n // The route lives in the fragment of this document; another document leaves the\n // application.\n if (url.pathname !== view.location.pathname || !url.hash.startsWith('#/')) return undefined;\n\n url = new URL(url.hash.slice(1).replace(/^\\/*/, '/'), view.location.origin);\n } else if (href.startsWith('#')) {\n return undefined;\n }\n\n // The current page: an anchor on it, or a link to itself.\n if (url.pathname === ɵngDocRouteUrl(this.locationStrategy, view.location.href).pathname) {\n return undefined;\n }\n\n const base = this.basePath();\n\n if (!`${url.pathname}/`.startsWith(base)) return undefined;\n\n return `/${url.pathname.slice(base.length)}${url.search}`;\n }\n\n /**\n * Whether the location strategy keeps the route in the document's fragment, as\n * `HashLocationStrategy` does.\n */\n private hashLocation(): boolean {\n return this.locationStrategy.prepareExternalUrl('/').startsWith('#');\n }\n\n /**\n * The base path of the application's routes, ending with a slash: the base href, which a\n * fragment route carries only when `APP_BASE_HREF` sets one.\n */\n private basePath(): string {\n const base = this.locationStrategy.getBaseHref() || '/';\n const path = this.hashLocation()\n ? base.replace(/^\\/*/, '/')\n : new URL(base, this.document.baseURI).pathname;\n\n return path.endsWith('/') ? path : `${path}/`;\n }\n\n private destroy(): void {\n this.destroyed = true;\n this.cancelIdle?.();\n this.cancelIdle = undefined;\n this.loading.clear();\n INTENT_EVENTS.forEach((type: string) =>\n this.document.removeEventListener(type, this.intent, { capture: true }),\n );\n }\n}\n\n/**\n * Whether the reader asked the browser to save data or the connection is 2G, where preloading\n * would compete with what the reader opens.\n * @param document - The document of the application.\n * @returns Whether to skip preloading.\n */\nfunction constrainedConnection(document: Document): boolean {\n const navigator = document.defaultView?.navigator as\n | (Navigator & { connection?: NgDocNetworkInformation })\n | undefined;\n const connection = navigator?.connection;\n\n return (\n !!connection &&\n (connection.saveData === true || /(^|-)2g$/.test(connection.effectiveType ?? ''))\n );\n}\n\n/**\n * Returns the path segments of the primary outlet of a URL.\n * @param url - The URL.\n * @returns The segments.\n */\nfunction urlSegments(url: UrlTree): string[] {\n return url.root.children[PRIMARY_OUTLET]?.segments.map(({ path }) => path) ?? [];\n}\n\n/**\n * Returns the routes that can match a path: those whose own path matches its start, through\n * the children the router has loaded so far. A lazy route whose children have not loaded yet\n * may lead anywhere below its path.\n * @param routes - The routes to search.\n * @param segments - The path segments.\n * @param found - Collects the routes.\n * @returns The routes.\n */\nfunction routesOnPath(\n routes: readonly Route[],\n segments: readonly string[],\n found: Set<Route> = new Set(),\n): Set<Route> {\n for (const route of routes) {\n if (\n (route.outlet ?? PRIMARY_OUTLET) !== PRIMARY_OUTLET ||\n route.matcher ||\n route.redirectTo !== undefined ||\n // A guarded route may not match for this reader; only opening the page may decide.\n route.canMatch?.length\n ) {\n continue;\n }\n\n const consumed = matchedSegments(route.path ?? '', segments);\n\n if (consumed === undefined) continue;\n\n const rest = segments.slice(consumed);\n const children = route.children ?? loadedRoutes(route);\n // A route without children matches only the whole remaining path.\n const leaf = !route.children && !route.loadChildren;\n\n if ((route.pathMatch === 'full' || leaf) && rest.length) continue;\n\n found.add(route);\n if (children) routesOnPath(children, rest, found);\n }\n\n return found;\n}\n\n/**\n * Returns how many segments a route path matches at the start of a path.\n * @param path - The route path.\n * @param segments - The path segments.\n * @returns The number of matched segments, or `undefined` when the route does not match.\n */\nfunction matchedSegments(path: string, segments: readonly string[]): number | undefined {\n if (!path) return 0;\n\n const parts = path.split('/');\n\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n\n if (part === '**') return segments.length;\n if (index >= segments.length) return undefined;\n if (!part.startsWith(':') && part !== segments[index]) return undefined;\n }\n\n return parts.length;\n}\n\n/**\n * Returns the children that the router loaded for a lazy route. The router keeps them on the\n * route itself and has no public API for them; its own preloader reads the same property.\n * Without it, preloading stops at the first lazy route.\n * @param route - The route.\n * @returns The loaded children, if any.\n */\nfunction loadedRoutes(route: Route): readonly Route[] | undefined {\n return (route as Route & { _loadedRoutes?: Route[] })._loadedRoutes;\n}\n","import { inject, Service } from '@angular/core';\nimport { PreloadingStrategy, Route } from '@angular/router';\nimport { catchError, Observable, of } from 'rxjs';\n\nimport { NgDocRoutePreloader } from './route-preloader.service';\n\n/**\n * The router preloading strategy of NgDoc: it loads a lazy route only when the reader is about\n * to open one of its pages, as `NgDocRoutePreloader` decides, instead of every route at once.\n *\n * Set it as the router's preloading strategy to preload pages on intent:\n *\n * ```ts\n * provideRouter(routes, withPreloading(NgDocPreloadingStrategy));\n * ```\n */\n@Service()\nexport class NgDocPreloadingStrategy implements PreloadingStrategy {\n private readonly preloader = inject(NgDocRoutePreloader);\n\n constructor() {\n // The router creates its preloading strategy at start-up, so this is where preloading on\n // intent starts.\n this.preloader.enable();\n }\n\n /**\n * Loads a lazy route when it leads to a page that is being preloaded.\n * @param route - A lazy route that the router has not loaded yet.\n * @param load - Loads the route.\n * @returns The load, or an observable of `null` when the route is not loaded now.\n */\n preload(route: Route, load: () => Observable<unknown>): Observable<unknown> {\n if (!this.preloader.shouldPreload(route)) return of(null);\n\n // A failed preload is not an error: opening the page loads the route again and reports it.\n return load().pipe(catchError(() => of(null)));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["ɵngDocRouteContentSources","ɵpreloadNgDocContent","ɵngDocRouteUrl"],"mappings":"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"}
|