@ng-doc/builder 22.0.0 → 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/engine/builders/helpers/get-playground-metadata.js +7 -1
- package/engine/builders/helpers/get-playground-metadata.js.map +1 -1
- package/engine/builders/page/demo-assets.builder.js +2 -0
- package/engine/builders/page/demo-assets.builder.js.map +1 -1
- package/engine/builders/shared/content.builder.js +2 -0
- package/engine/builders/shared/content.builder.js.map +1 -1
- package/engine/builders/shared/page-wrapper.builder.js +2 -0
- package/engine/builders/shared/page-wrapper.builder.js.map +1 -1
- package/generator/bootstrap/bin.js +27 -3
- package/generator/bootstrap/cli.js +27 -3
- package/generator/bootstrap/index.js +17 -3
- package/generator/build-metafile.json +529 -221
- package/generator/build-provenance.json +6 -4
- package/generator/compiler/index.d.ts +20 -0
- package/generator/compiler/index.js +1875 -625
- package/generator/compiler/plan.d.ts +24 -0
- package/generator/content/code-snippet.d.ts +20 -0
- package/generator/content/content-compiler.d.ts +5 -1
- package/generator/content/format-cache.d.ts +108 -0
- package/generator/content/highlight-cache.d.ts +12 -36
- package/generator/content/html-pipeline.d.ts +10 -1
- package/generator/content/html-pool.d.ts +8 -2
- package/generator/content/html-worker.js +622 -39
- package/generator/contracts.d.ts +39 -0
- package/generator/discovery/index.d.ts +2 -0
- package/generator/kernel/flags.d.ts +2 -0
- package/generator/outputs/index.d.ts +13 -0
- package/generator/semantic/angular.d.ts +21 -1
- package/generator/semantic/formatting.d.ts +27 -0
- package/generator/semantic/fragments.d.ts +11 -2
- package/generator/templates/api/class-declaration.html.nunj +5 -3
- package/generator/templates/demo-app.ts.nunj +37 -0
- package/generator/templates/demo-routes.ts.nunj +23 -0
- package/generator/templates/index.ts.nunj +5 -0
- package/generator/templates/page-wrapper.ts.nunj +6 -1
- package/generator/templates/page.ts.nunj +9 -2
- package/generator/templates/routes.ts.nunj +7 -3
- package/generator/templates/symbol/members.html.nunj +6 -4
- package/generator/templates/symbol/page.html.nunj +1 -1
- package/generator/vite/application-builder.js +6 -1
- package/generator/vite/application.d.ts +14 -0
- package/generator/vite/demo-application.d.ts +34 -0
- package/generator/vite/dev-server-builder.js +6 -1
- package/generator/vite/host-updates.d.ts +69 -2
- package/generator/vite/index.js +490 -130
- package/generator/vite/lifecycle.d.ts +1 -0
- package/generator/vite/prerender-entry.js +72 -2
- package/generator/vite/prerender-runtime.d.ts +13 -0
- package/generator/vite/prerender.d.ts +2 -0
- package/helpers/format-cache.d.ts +34 -0
- package/helpers/format-cache.js +26 -0
- package/helpers/format-cache.js.map +1 -0
- package/helpers/format-code.js +9 -2
- package/helpers/format-code.js.map +1 -1
- package/helpers/to-template-string.d.ts +5 -1
- package/helpers/to-template-string.js +7 -2
- package/helpers/to-template-string.js.map +1 -1
- package/interfaces/code-block-options.d.ts +5 -0
- package/interfaces/configuration.d.ts +84 -1
- package/package.json +5 -4
- package/parsers/parse-code-block-params.d.ts +10 -1
- package/parsers/parse-code-block-params.js +29 -3
- package/parsers/parse-code-block-params.js.map +1 -1
- package/schematics/collection.json +1 -1
- package/schematics/migrate-to-vite/analyze.js +46 -3
- package/schematics/migrate-to-vite/analyze.js.map +1 -1
- package/schematics/migrate-to-vite/setup/workspace.d.ts +5 -0
- package/schematics/migrate-to-vite/setup/workspace.js +20 -0
- package/schematics/migrate-to-vite/setup/workspace.js.map +1 -1
- package/schematics/migrate-to-vite/testing/fixtures.d.ts +12 -0
- package/schematics/migrate-to-vite/testing/fixtures.js +27 -0
- package/schematics/migrate-to-vite/testing/fixtures.js.map +1 -1
- package/schematics/vite-setup/index.d.ts +9 -5
- package/schematics/vite-setup/index.js +11 -5
- package/schematics/vite-setup/index.js.map +1 -1
- package/schematics/vite-setup/schema.json +1 -1
- package/templates/api/class-declaration.html.nunj +5 -3
- package/templates/demo-app.ts.nunj +37 -0
- package/templates/demo-routes.ts.nunj +23 -0
- package/templates/index.ts.nunj +5 -0
- package/templates/page-wrapper.ts.nunj +6 -1
- package/templates/page.ts.nunj +9 -2
- package/templates/routes.ts.nunj +7 -3
- package/templates/symbol/members.html.nunj +6 -4
- package/templates/symbol/page.html.nunj +1 -1
package/generator/contracts.d.ts
CHANGED
|
@@ -151,6 +151,12 @@ export interface RemoteKeywordSnapshot {
|
|
|
151
151
|
/** Optional upstream validator is evidence, never an assumed lifetime guarantee. */
|
|
152
152
|
validator?: string;
|
|
153
153
|
}
|
|
154
|
+
/** A Shiki (TextMate) language registration of `shiki.langs`: a grammar as plain JSON. */
|
|
155
|
+
export interface ShikiLanguage {
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly scopeName: string;
|
|
158
|
+
readonly [key: string]: JsonValue;
|
|
159
|
+
}
|
|
154
160
|
export interface GeneratorConfiguration {
|
|
155
161
|
projectId: ProjectId;
|
|
156
162
|
workspaceRoot: FilePath;
|
|
@@ -166,10 +172,35 @@ export interface GeneratorConfiguration {
|
|
|
166
172
|
inlineStyleLanguage: 'CSS' | 'SCSS' | 'SASS' | 'LESS';
|
|
167
173
|
anchorHeadings: Array<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'>;
|
|
168
174
|
headerTemplate?: FilePath;
|
|
175
|
+
/**
|
|
176
|
+
* `false` when `api.protectedMembers` is `false`: API templates then list only public class
|
|
177
|
+
* members. Absent otherwise, so every other configuration keeps its digest.
|
|
178
|
+
*/
|
|
179
|
+
apiProtectedMembers?: false;
|
|
180
|
+
/**
|
|
181
|
+
* The demo pages (`isolatedDemos`, `demoApplication`, `demoProviders`). Absent when none of them
|
|
182
|
+
* is set, so every other configuration keeps its digest, and so is every default field:
|
|
183
|
+
* - `pages`: `all` builds a demo page for every demo, `none` for none; absent, only the guides
|
|
184
|
+
* with an isolated demo get them.
|
|
185
|
+
* - `isolated`: `true` when `isolatedDemos` is: every `demo` action shows an iframe by default.
|
|
186
|
+
* - `path`: the URL path of the demo pages, absent for `demo-preview`.
|
|
187
|
+
* - `providers`: the module that `demoProviders` imports.
|
|
188
|
+
*/
|
|
189
|
+
demoApplication?: {
|
|
190
|
+
pages?: 'all' | 'none';
|
|
191
|
+
isolated?: true;
|
|
192
|
+
path?: string;
|
|
193
|
+
providers?: FilePath;
|
|
194
|
+
};
|
|
169
195
|
themes: {
|
|
170
196
|
light: string;
|
|
171
197
|
dark: string;
|
|
172
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* The Shiki language registrations of `shiki.langs`, flattened, as plain JSON in configuration
|
|
201
|
+
* order. Absent without any, so every other configuration keeps its digest.
|
|
202
|
+
*/
|
|
203
|
+
shikiLangs?: ShikiLanguage[];
|
|
173
204
|
repo?: {
|
|
174
205
|
url: string;
|
|
175
206
|
platform: 'github' | 'gitlab';
|
|
@@ -608,6 +639,14 @@ export interface RouteRecord {
|
|
|
608
639
|
* for the root list), which its route carries as `ngDocApiListSegment` data.
|
|
609
640
|
*/
|
|
610
641
|
apiListSegment?: string;
|
|
642
|
+
/**
|
|
643
|
+
* The guide's demo routes module (relative to the output root), when the guide has demo pages.
|
|
644
|
+
* The aggregate lists it in `demo-app.ts`. Optional and only written for such guides, so a cache
|
|
645
|
+
* written without it stays valid: every other route record is unchanged.
|
|
646
|
+
*/
|
|
647
|
+
demoModulePath?: string;
|
|
648
|
+
/** The names of the guide's demo pages, in the order of `demos`, with `demoModulePath`. */
|
|
649
|
+
demoNames?: string[];
|
|
611
650
|
}
|
|
612
651
|
export interface ApiListRecord {
|
|
613
652
|
apiEntryId: EntryId;
|
|
@@ -69,6 +69,8 @@ export declare function createDiscoveryServices(options?: DiscoveryOptions): {
|
|
|
69
69
|
values: GuideValueReader;
|
|
70
70
|
runtime: DiscoveryServiceImpl;
|
|
71
71
|
};
|
|
72
|
+
/** The default URL path of the demo pages. */
|
|
73
|
+
export declare const DEFAULT_DEMO_PATH = "demo-preview";
|
|
72
74
|
/**
|
|
73
75
|
* The `evaluated` digest of every entry of a discovery snapshot: the refresh source of the
|
|
74
76
|
* `evaluated` dependencies a generation recorded against it.
|
|
@@ -34,7 +34,9 @@ export declare const TRACKED_PROGRAM_REUSE_FLAG = "NGDOC_TRACKED_PROGRAM_REUSE";
|
|
|
34
34
|
export declare const FAST_START_FLAG = "NGDOC_FAST_START";
|
|
35
35
|
export declare const PARALLEL_WRITES_FLAG = "NGDOC_PARALLEL_WRITES";
|
|
36
36
|
export declare const HIGHLIGHT_CACHE_FLAG = "NGDOC_HIGHLIGHT_CACHE";
|
|
37
|
+
export declare const FORMAT_CACHE_FLAG = "NGDOC_FORMAT_CACHE";
|
|
37
38
|
export declare const PARALLEL_RENDER_FLAG = "NGDOC_PARALLEL_RENDER";
|
|
39
|
+
export declare const USED_GRAMMARS_FLAG = "NGDOC_USED_GRAMMARS";
|
|
38
40
|
/** The registry, in the order the engine applies the switches. */
|
|
39
41
|
export declare const FLAGS: readonly FlagDefinition[];
|
|
40
42
|
/**
|
|
@@ -56,10 +56,23 @@ export declare class OutputAssemblerImpl implements OutputAssembler {
|
|
|
56
56
|
assemblePage(request: PageAssemblyRequest): PageAssembly;
|
|
57
57
|
aggregate(request: AggregateRequest): AggregateAssembly;
|
|
58
58
|
private guide;
|
|
59
|
+
/**
|
|
60
|
+
* Writes the guide's demo routes module (one route per demo, under the page's own providers)
|
|
61
|
+
* when the guide gets demo pages: with `pages: 'all'`, or by default when its content has a demo
|
|
62
|
+
* with `isolated: true`. Returns its path and the names of the demos that got a page, or
|
|
63
|
+
* undefined.
|
|
64
|
+
*/
|
|
65
|
+
private demoRoutes;
|
|
59
66
|
private apiEntry;
|
|
60
67
|
private apiDeclaration;
|
|
61
68
|
private apiLists;
|
|
62
69
|
private addContentSource;
|
|
70
|
+
/**
|
|
71
|
+
* Writes `demo-app.ts`, the routes of the demo application (one lazy route per guide with demo
|
|
72
|
+
* pages, under its categories' providers) and its `demoProviders` import, when a guide has demo
|
|
73
|
+
* pages. Returns whether it did.
|
|
74
|
+
*/
|
|
75
|
+
private demoApplication;
|
|
63
76
|
private renderOutput;
|
|
64
77
|
}
|
|
65
78
|
export declare function createOutputAssembler(options?: {
|
|
@@ -2,8 +2,28 @@ import { type ObjectLiteralExpression, type SourceFile, Node, ts } from 'ts-morp
|
|
|
2
2
|
import type { GeneratorConfiguration, GuideSemantics, JsonValue } from '../contracts.js';
|
|
3
3
|
import { type TrackedFiles } from './dependencies.js';
|
|
4
4
|
import type { JsDoc } from './rendering.js';
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Resolves wrappers and local constants without executing source code.
|
|
7
|
+
* @param node
|
|
8
|
+
* @param seen
|
|
9
|
+
*/
|
|
6
10
|
export declare function unwrap(node: Node | undefined, seen?: Set<Node<ts.Node>>): Node | undefined;
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param source
|
|
14
|
+
*/
|
|
7
15
|
export declare function entryObject(source: SourceFile): ObjectLiteralExpression;
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param node
|
|
19
|
+
*/
|
|
8
20
|
export declare function literal(node: Node | undefined): JsonValue;
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param source
|
|
24
|
+
* @param files
|
|
25
|
+
* @param config
|
|
26
|
+
* @param docs
|
|
27
|
+
* @param values
|
|
28
|
+
*/
|
|
9
29
|
export declare function guideSemantics(source: SourceFile, files: TrackedFiles, config: GeneratorConfiguration, docs: JsDoc, values?: JsonValue): GuideSemantics;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Options } from 'prettier';
|
|
2
|
+
/**
|
|
3
|
+
* The cache `formatCode` consults: `FormatCodeCache` of `helpers/format-cache.ts`, restated here
|
|
4
|
+
* because the published declarations reach this module and cannot import from outside the
|
|
5
|
+
* generator. TypeScript checks that the two agree where `formatting` installs it.
|
|
6
|
+
*/
|
|
7
|
+
export interface GenerationFormatCache {
|
|
8
|
+
config(configDirectory: string | undefined, resolve: () => Options | null): Options | null;
|
|
9
|
+
format(input: {
|
|
10
|
+
code: string;
|
|
11
|
+
parser: string;
|
|
12
|
+
config: Options | null;
|
|
13
|
+
}, format: () => string): string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Makes `cache` the one {@link formatting} installs (undefined: none). A runtime compiles one
|
|
17
|
+
* generation at a time; the compiler sets it when it plans a generation and clears it when the
|
|
18
|
+
* compile ends, so a later generation never sees an earlier one's resolved configuration.
|
|
19
|
+
* @param cache The generation's cache (`content/format-cache.ts`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function useFormatCache(cache: GenerationFormatCache | undefined): void;
|
|
22
|
+
/**
|
|
23
|
+
* Runs synchronous formatting work (API templates, demo snippets) with the current generation's
|
|
24
|
+
* format cache, if any.
|
|
25
|
+
* @param run The work; `formatCode` calls it makes consult the cache.
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatting<T>(run: () => T): T;
|
|
@@ -22,7 +22,16 @@ export interface FragmentContext extends QueryContext {
|
|
|
22
22
|
/** The API template root (the option, or the shipped templates). */
|
|
23
23
|
templateRoot(): string;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* The recorder scope of a fragment request: its phase kind and its unit key.
|
|
27
|
+
* @param request
|
|
28
|
+
*/
|
|
26
29
|
export declare function fragmentScope(request: SemanticFragmentRequest): [FootprintScopeKind, string];
|
|
27
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Renders one fragment request against the snapshot, recording its reads in `files`.
|
|
32
|
+
* @param state
|
|
33
|
+
* @param request
|
|
34
|
+
* @param files
|
|
35
|
+
* @param context
|
|
36
|
+
*/
|
|
28
37
|
export declare function renderFragment(state: Snapshot, request: SemanticFragmentRequest, files: TrackedFiles, context: FragmentContext): SemanticFragment;
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
{% import "helpers/modifier.html.nunj" as modifierMacro %}
|
|
8
8
|
|
|
9
9
|
{% block overview %}
|
|
10
|
+
{#- The new engine sets `hideProtectedMembers` for `api.protectedMembers: false`; the legacy engine never does. #}
|
|
11
|
+
{%- set memberScopes = ["public"] if hideProtectedMembers else ["public", "protected"] -%}
|
|
10
12
|
{%- set constructors = declaration.getConstructors() | filterByScope("public") | excludeByJsDocTags('internal') -%}
|
|
11
|
-
{%- set allProperties = declaration | getClassProperties | excludeByJsDocTags('internal') | filterByScope(
|
|
12
|
-
{%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(
|
|
13
|
-
{%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(
|
|
13
|
+
{%- set allProperties = declaration | getClassProperties | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
14
|
+
{%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
15
|
+
{%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
14
16
|
|
|
15
17
|
{%- set staticProperties = allProperties | filterByStatic(true) -%}
|
|
16
18
|
{%- set staticAccessors = allAccessors | filterByStatic(true) -%}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import {Routes} from '@angular/router';
|
|
3
|
+
{%- for category in categories %}
|
|
4
|
+
import {{ category[0] }} from '{{ category[1] }}';
|
|
5
|
+
{%- endfor %}
|
|
6
|
+
|
|
7
|
+
// The URL path of the demo pages, which the Vite host serves the demo application's page under.
|
|
8
|
+
export const NG_DOC_DEMO_PATH = '{{ demoPath }}';
|
|
9
|
+
|
|
10
|
+
// Every demo page, which a build without prerendering writes as a copy of the demo application's
|
|
11
|
+
// page, so that static hosts serve it without a fallback.
|
|
12
|
+
export const NG_DOC_DEMO_PAGES: string[] = {{ demoPages | dump }};
|
|
13
|
+
|
|
14
|
+
// The routes of the demo application: one lazy route per guide with demo pages, under the
|
|
15
|
+
// providers of its categories, as on the documentation pages.
|
|
16
|
+
export const NG_DOC_DEMO_ROUTES: Routes = [
|
|
17
|
+
{%- for page in pages %}
|
|
18
|
+
{
|
|
19
|
+
path: '{{ page.path | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}',
|
|
20
|
+
{%- if page.categories.length %}
|
|
21
|
+
providers: [
|
|
22
|
+
{%- for id in page.categories %}
|
|
23
|
+
...({{ id }}.providers ?? []),
|
|
24
|
+
{%- endfor %}
|
|
25
|
+
],
|
|
26
|
+
{%- endif %}
|
|
27
|
+
loadChildren: () => import('{{ page.importPath }}'),
|
|
28
|
+
},
|
|
29
|
+
{%- endfor %}
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// The module of `demoProviders`, which only the demo application imports.
|
|
33
|
+
{%- if providersImport %}
|
|
34
|
+
export const NG_DOC_DEMO_PROVIDERS = () => import('{{ providersImport }}');
|
|
35
|
+
{%- else %}
|
|
36
|
+
export const NG_DOC_DEMO_PROVIDERS = undefined;
|
|
37
|
+
{%- endif %}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import {Routes} from '@angular/router';
|
|
3
|
+
import {NgDocDemoHostComponent} from '@ng-doc/app/demo-app';
|
|
4
|
+
import entry from '{{ entryImport }}';
|
|
5
|
+
|
|
6
|
+
// The demo pages of one guide: one route per demo, under the page's providers, as the demos have
|
|
7
|
+
// them on the page.
|
|
8
|
+
const routes: Routes = [{
|
|
9
|
+
path: '',
|
|
10
|
+
providers: [...(entry.providers ?? [])],
|
|
11
|
+
children: [
|
|
12
|
+
{%- for demo in demos %}
|
|
13
|
+
{
|
|
14
|
+
path: '{{ demo.name }}',
|
|
15
|
+
component: NgDocDemoHostComponent,
|
|
16
|
+
title: `{{ demo.title | toTemplateString }}`,
|
|
17
|
+
data: {ngDocDemo: entry.demos?.['{{ demo.name }}']},
|
|
18
|
+
},
|
|
19
|
+
{%- endfor %}
|
|
20
|
+
],
|
|
21
|
+
}];
|
|
22
|
+
|
|
23
|
+
export default routes;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export * from './context';
|
|
2
2
|
export * from './routes';
|
|
3
|
+
{% if demoApplication -%}
|
|
4
|
+
// Keeps the demo application's modules, the demo routes and the module of `demoProviders`, in the
|
|
5
|
+
// application's Angular compilation, which compiles them: only the demo application imports them.
|
|
6
|
+
import type {} from './demo-app';
|
|
7
|
+
{% endif -%}
|
|
@@ -42,7 +42,11 @@ const routes: Routes = [{
|
|
|
42
42
|
{%- endif %}
|
|
43
43
|
path: '',
|
|
44
44
|
component: PageWrapperComponent,
|
|
45
|
-
title:
|
|
45
|
+
{#- No title: the route inherits the page's title from the route that loads it, and a second
|
|
46
|
+
copy of it would repeat it in a title strategy that joins the titles of the route. #}
|
|
47
|
+
data: {
|
|
48
|
+
ngDocPageType: '{{ pageType }}',
|
|
49
|
+
},
|
|
46
50
|
children: [
|
|
47
51
|
{%- for importPath, entry in entries %}
|
|
48
52
|
{
|
|
@@ -51,6 +55,7 @@ const routes: Routes = [{
|
|
|
51
55
|
title: `{{ entry.title | toTemplateString }}`,
|
|
52
56
|
data: {
|
|
53
57
|
icon: '{{ entry.entry.icon }}',
|
|
58
|
+
ngDocTitle: 'tab',
|
|
54
59
|
}
|
|
55
60
|
},
|
|
56
61
|
{%- endfor %}
|
|
@@ -64,11 +64,20 @@ export class PageComponent extends NgDocRootPage {
|
|
|
64
64
|
{%- if demoAssetsPath %}
|
|
65
65
|
readonly demoAssets: NgDocDemoAssets = demoAssets;
|
|
66
66
|
{%- endif %}
|
|
67
|
+
{#- The demo pages of the guide (Vite engine), and whether its demos are isolated by default. #}
|
|
68
|
+
{%- if demoRoute %}
|
|
69
|
+
readonly demoRoute: string = '{{ demoRoute | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}';
|
|
70
|
+
{%- if isolatedDemos %}
|
|
71
|
+
readonly isolatedDemos: boolean = true;
|
|
72
|
+
{%- endif %}
|
|
73
|
+
{%- endif %}
|
|
67
74
|
|
|
68
75
|
constructor() {
|
|
69
76
|
super();
|
|
70
77
|
}
|
|
71
78
|
}
|
|
79
|
+
{#- The route has no title of its own: it inherits the title of its tab route, so a page's own
|
|
80
|
+
`route.title` is the only title here and wins over the generated one. #}
|
|
72
81
|
{%- if contentSourceImport %}
|
|
73
82
|
|
|
74
83
|
// The router shows the page only once its content has loaded, so the previous page stays on
|
|
@@ -79,7 +88,6 @@ const routes: Routes = [ɵwithNgDocContent({
|
|
|
79
88
|
{%- endif %}
|
|
80
89
|
path: '',
|
|
81
90
|
component: PageComponent,
|
|
82
|
-
title: `{{ metadata.title | toTemplateString }}`,
|
|
83
91
|
}, pageContentSource)]
|
|
84
92
|
{%- else %}
|
|
85
93
|
|
|
@@ -89,7 +97,6 @@ const routes: Routes = [{
|
|
|
89
97
|
{%- endif %}
|
|
90
98
|
path: '',
|
|
91
99
|
component: PageComponent,
|
|
92
|
-
title: `{{ metadata.title | toTemplateString }}`,
|
|
93
100
|
}]
|
|
94
101
|
{%- endif %}
|
|
95
102
|
|
|
@@ -20,9 +20,13 @@ export const NG_DOC_ROUTING: Routes = [
|
|
|
20
20
|
{
|
|
21
21
|
path: '{{ entry.item.route | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}',
|
|
22
22
|
title: `{{ entry.item.title | toTemplateString }}`,
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
{
|
|
23
|
+
{#- `ngDocTitle` names what the title belongs to, so a title strategy can compose the titles of
|
|
24
|
+
the route without repeating one (`provideNgDocTitle`). #}
|
|
25
|
+
data: {ngDocTitle: '{{ 'category' if entry.item.isCategory else 'page' }}'
|
|
26
|
+
{%- if entry.item.apiListSegment is defined -%}
|
|
27
|
+
, ngDocApiListSegment: '{{ entry.item.apiListSegment | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}'
|
|
28
|
+
{%- endif -%}
|
|
29
|
+
},
|
|
26
30
|
{%- if entry.item.isCategory %}
|
|
27
31
|
providers: [
|
|
28
32
|
...({{ entry.item.id }}.providers ?? []),
|
|
@@ -197,12 +197,14 @@
|
|
|
197
197
|
</tr>
|
|
198
198
|
{%- endmacro %}
|
|
199
199
|
|
|
200
|
-
{
|
|
200
|
+
{#- `hideProtected` (the `api.protectedMembers: false` option) lists public class members only. #}
|
|
201
|
+
{% macro render(declaration, hideProtected=false) %}
|
|
202
|
+
{%- set memberScopes = ["public"] if hideProtected else ["public", "protected"] -%}
|
|
201
203
|
{%- if Node.isClassDeclaration(declaration) %}
|
|
202
204
|
{%- set constructors = declaration.getConstructors() | filterByScope("public") | excludeByJsDocTags('internal') -%}
|
|
203
|
-
{%- set allProperties = declaration | getClassProperties | excludeByJsDocTags('internal') | filterByScope(
|
|
204
|
-
{%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(
|
|
205
|
-
{%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(
|
|
205
|
+
{%- set allProperties = declaration | getClassProperties | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
206
|
+
{%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
207
|
+
{%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(memberScopes) | filterUselessMembers | sortByNodesName -%}
|
|
206
208
|
{%- set staticProperties = allProperties | filterByStatic(true) -%}
|
|
207
209
|
{%- set properties = allProperties | filterByStatic(false) -%}
|
|
208
210
|
{%- set staticAccessors = allAccessors | filterByStatic(true) | groupAccessors -%}
|
|
@@ -7,6 +7,7 @@ import { Observable } from "rxjs";
|
|
|
7
7
|
import { createServer as viteCreateServer } from "vite";
|
|
8
8
|
|
|
9
9
|
// libs/builder/generator/vite/production.ts
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
10
11
|
import { rm, writeFile } from "node:fs/promises";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import { build as viteBuild } from "vite";
|
|
@@ -75,7 +76,8 @@ function prerenderNgDoc(options) {
|
|
|
75
76
|
serverEntry: options.serverEntry,
|
|
76
77
|
routes: [...options.routes ?? []],
|
|
77
78
|
discoverRoutes: options.discoverRoutes ?? true,
|
|
78
|
-
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs }
|
|
79
|
+
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
|
|
80
|
+
...options.demoServerEntry ? { demoServerEntry: options.demoServerEntry } : {}
|
|
79
81
|
};
|
|
80
82
|
if (request.routeTimeoutMs !== void 0 && (!Number.isSafeInteger(request.routeTimeoutMs) || request.routeTimeoutMs < 1)) {
|
|
81
83
|
return Promise.reject(
|
|
@@ -256,11 +258,13 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
|
|
|
256
258
|
}
|
|
257
259
|
const prerender = options.prerender ?? (options.skipBuild ? true : ssr === true);
|
|
258
260
|
if (!prerender) return { browser, ...ssr ? { server: serverEntry } : {} };
|
|
261
|
+
const demoServerEntry = path.join(serverDirectory, "demo-server.mjs");
|
|
259
262
|
const report = await timed(
|
|
260
263
|
"Prerendered the routes",
|
|
261
264
|
() => dependencies.prerender({
|
|
262
265
|
browserDir: browser,
|
|
263
266
|
serverEntry,
|
|
267
|
+
...existsSync(demoServerEntry) && existsSync(path.join(browser, "ng-doc-demo.html")) ? { demoServerEntry } : {},
|
|
264
268
|
routes: options.routes ?? [],
|
|
265
269
|
discoverRoutes: options.discoverRoutes ?? true,
|
|
266
270
|
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
|
|
@@ -273,6 +277,7 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
|
|
|
273
277
|
`
|
|
274
278
|
);
|
|
275
279
|
log(`Prerendered ${report.routes.length} route(s) into ${browser}`);
|
|
280
|
+
for (const { route, message: message2 } of report.warnings ?? []) log(`Warning: ${route}: ${message2}`);
|
|
276
281
|
if (report.errors.length) {
|
|
277
282
|
log(`The application logged ${report.errors.length} error(s) while prerendering:`);
|
|
278
283
|
for (const { route, message: message2 } of report.errors.slice(0, 10)) log(` ${route}: ${message2}`);
|
|
@@ -41,6 +41,14 @@ export interface NgDocViteApplicationOptions {
|
|
|
41
41
|
export declare const NG_DOC_BROWSER_ENTRY = "/@ng-doc-application/browser.js";
|
|
42
42
|
/** The module id of the server entry; `buildNgDocViteApplication` builds it into `server.mjs`. */
|
|
43
43
|
export declare const NG_DOC_SERVER_ENTRY = "/@ng-doc-application/server.js";
|
|
44
|
+
/** The URL of the demo application's browser entry, which its page loads. */
|
|
45
|
+
export declare const NG_DOC_DEMO_ENTRY = "/@ng-doc-application/demo.js";
|
|
46
|
+
/** The module id of the demo application's server entry, built next to `server.mjs`. */
|
|
47
|
+
export declare const NG_DOC_DEMO_SERVER_ENTRY = "/@ng-doc-application/demo-server.js";
|
|
48
|
+
/** The page of the demo application, next to index.html in the build output. */
|
|
49
|
+
export declare const NG_DOC_DEMO_PAGE = "ng-doc-demo.html";
|
|
50
|
+
/** The name of the demo application's server entry in the server build (`demo-server.mjs`). */
|
|
51
|
+
export declare const NG_DOC_DEMO_SERVER_NAME = "demo-server";
|
|
44
52
|
interface ResolvedAsset {
|
|
45
53
|
input: string;
|
|
46
54
|
output: string;
|
|
@@ -77,6 +85,12 @@ export declare function matchAssets(assets: readonly ResolvedAsset[], base: stri
|
|
|
77
85
|
file: string;
|
|
78
86
|
relative: string;
|
|
79
87
|
}>;
|
|
88
|
+
/**
|
|
89
|
+
* The page of the demo application: the application's index.html with the demo application's
|
|
90
|
+
* root as its body, so it has the same head (base href, fonts, icons, theme script).
|
|
91
|
+
* @param index - The content of index.html.
|
|
92
|
+
*/
|
|
93
|
+
export declare function demoDocument(index: string): string;
|
|
80
94
|
/** Sets `<base href>` to an absolute Vite base, or adds it at the start of `<head>`. */
|
|
81
95
|
export declare function withBase(html: string, base: string): string;
|
|
82
96
|
/**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The generated module of the demo application, in the output root (`demo-app.ts.nunj`). */
|
|
2
|
+
export declare const DEMO_APPLICATION_MODULE = "demo-app.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The demo application of a generation: its generated module and the URL path of its pages.
|
|
5
|
+
* The NgDoc plugin publishes it to the application plugin (`NgDocDemoApplicationApi`).
|
|
6
|
+
*/
|
|
7
|
+
export interface NgDocDemoApplication {
|
|
8
|
+
/** The generated module (absolute), which exports the routes and the providers import. */
|
|
9
|
+
readonly module: string;
|
|
10
|
+
/** The URL path of the demo pages, relative to the base href, without slashes around it. */
|
|
11
|
+
readonly path: string;
|
|
12
|
+
/** Every demo page, relative to the base href (`<path>/<page route>/<demo name>`). */
|
|
13
|
+
readonly pages: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What the NgDoc plugin offers the application plugin through `api.ngDocDemoApplication`.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export interface NgDocDemoApplicationApi {
|
|
20
|
+
readonly schemaVersion: 1;
|
|
21
|
+
/**
|
|
22
|
+
* The demo application of the build's generation, generating first if the build has not yet: a
|
|
23
|
+
* build adds the demo page's input before it starts, when there is one.
|
|
24
|
+
*/
|
|
25
|
+
resolve(): Promise<NgDocDemoApplication | undefined>;
|
|
26
|
+
/** The demo application of the current generation of the development server, if any. */
|
|
27
|
+
current(): Promise<NgDocDemoApplication | undefined>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reads the demo application from a committed output root: a generation writes `demo-app.ts`
|
|
31
|
+
* only when a guide has demo pages, and that module names the URL path of the pages.
|
|
32
|
+
* @param outputRoot - The output root of the published generation.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readDemoApplication(outputRoot: string): Promise<NgDocDemoApplication | undefined>;
|
|
@@ -7,6 +7,7 @@ import { Observable } from "rxjs";
|
|
|
7
7
|
import { createServer as viteCreateServer } from "vite";
|
|
8
8
|
|
|
9
9
|
// libs/builder/generator/vite/production.ts
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
10
11
|
import { rm, writeFile } from "node:fs/promises";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import { build as viteBuild } from "vite";
|
|
@@ -75,7 +76,8 @@ function prerenderNgDoc(options) {
|
|
|
75
76
|
serverEntry: options.serverEntry,
|
|
76
77
|
routes: [...options.routes ?? []],
|
|
77
78
|
discoverRoutes: options.discoverRoutes ?? true,
|
|
78
|
-
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs }
|
|
79
|
+
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
|
|
80
|
+
...options.demoServerEntry ? { demoServerEntry: options.demoServerEntry } : {}
|
|
79
81
|
};
|
|
80
82
|
if (request.routeTimeoutMs !== void 0 && (!Number.isSafeInteger(request.routeTimeoutMs) || request.routeTimeoutMs < 1)) {
|
|
81
83
|
return Promise.reject(
|
|
@@ -256,11 +258,13 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
|
|
|
256
258
|
}
|
|
257
259
|
const prerender = options.prerender ?? (options.skipBuild ? true : ssr === true);
|
|
258
260
|
if (!prerender) return { browser, ...ssr ? { server: serverEntry } : {} };
|
|
261
|
+
const demoServerEntry = path.join(serverDirectory, "demo-server.mjs");
|
|
259
262
|
const report = await timed(
|
|
260
263
|
"Prerendered the routes",
|
|
261
264
|
() => dependencies.prerender({
|
|
262
265
|
browserDir: browser,
|
|
263
266
|
serverEntry,
|
|
267
|
+
...existsSync(demoServerEntry) && existsSync(path.join(browser, "ng-doc-demo.html")) ? { demoServerEntry } : {},
|
|
264
268
|
routes: options.routes ?? [],
|
|
265
269
|
discoverRoutes: options.discoverRoutes ?? true,
|
|
266
270
|
...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
|
|
@@ -273,6 +277,7 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
|
|
|
273
277
|
`
|
|
274
278
|
);
|
|
275
279
|
log(`Prerendered ${report.routes.length} route(s) into ${browser}`);
|
|
280
|
+
for (const { route, message: message2 } of report.warnings ?? []) log(`Warning: ${route}: ${message2}`);
|
|
276
281
|
if (report.errors.length) {
|
|
277
282
|
log(`The application logged ${report.errors.length} error(s) while prerendering:`);
|
|
278
283
|
for (const { route, message: message2 } of report.errors.slice(0, 10)) log(` ${route}: ${message2}`);
|
|
@@ -15,13 +15,53 @@ export interface InitialCompilationInventory {
|
|
|
15
15
|
}>;
|
|
16
16
|
isCurrent(): boolean;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* What the host does for the coordinator when a burst ends (see {@link HostUpdateCoordinator}).
|
|
20
|
+
*/
|
|
21
|
+
export interface HostUpdateRecovery {
|
|
22
|
+
/** Logs a warning: the summary of the host update timeouts a burst held back. */
|
|
23
|
+
warn(message: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Reports `file` again as its watcher would have: a generated output whose report never
|
|
26
|
+
* completed its generation's expectation (lost, or its hook timed out and threw).
|
|
27
|
+
*/
|
|
28
|
+
replay(file: string, type: 'create' | 'update' | 'delete'): void;
|
|
29
|
+
/** Re-observes every recorded input before the next generation (`BuildSession.rescan`). */
|
|
30
|
+
rescan(): void;
|
|
31
|
+
}
|
|
32
|
+
/** How long a host update waits for the publication's host obligations to drain. */
|
|
33
|
+
export declare const HOST_COMPLETION_TIMEOUT_MS = 30000;
|
|
34
|
+
/** Hot updates within {@link HOST_BURST_WINDOW_MS} that open a burst of file events. */
|
|
35
|
+
export declare const HOST_BURST_EVENTS = 1000;
|
|
36
|
+
/** The window in which {@link HOST_BURST_EVENTS} hot updates open a burst. */
|
|
37
|
+
export declare const HOST_BURST_WINDOW_MS = 1000;
|
|
38
|
+
/** A burst ends after this long without a hot update or a host update timeout. */
|
|
39
|
+
export declare const HOST_BURST_QUIET_MS = 2000;
|
|
18
40
|
export declare function observePhysicalState(file: string, read?: () => string | Promise<string>): Promise<PhysicalState>;
|
|
19
41
|
/** A synchronous metadata-only stamp preserves the event-time identity without starting I/O queues. */
|
|
20
42
|
export declare function observePhysicalVersion(file: string): string | undefined;
|
|
21
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Coordinates committed generator versions with Vite's client hot-update pipeline.
|
|
45
|
+
*
|
|
46
|
+
* Bursts: thousands of file events at once (a script or a checkout writing many files) flood
|
|
47
|
+
* Vite's hot-update pipeline and the watcher. Host updates then wait longer than
|
|
48
|
+
* {@link HOST_COMPLETION_TIMEOUT_MS}, a hook that timed out leaves its generation's obligation
|
|
49
|
+
* open (nothing reports that output again, so no reload is ever sent), and chokidar may lose
|
|
50
|
+
* events without any signal. So a burst is tracked: it opens with {@link HOST_BURST_EVENTS} hot
|
|
51
|
+
* updates of files outside the output root (once the first generation is published) within
|
|
52
|
+
* {@link HOST_BURST_WINDOW_MS}, or with a host update timeout, and ends after
|
|
53
|
+
* {@link HOST_BURST_QUIET_MS} without either. Only its first timeout is reported as it happens;
|
|
54
|
+
* the others are summed up in one warning when it ends. Then it takes one more pass: every
|
|
55
|
+
* generated output whose report never completed its expectation, and whose file is still what
|
|
56
|
+
* its generation wrote, is reported again (`HostUpdateRecovery.replay`), and every recorded
|
|
57
|
+
* input is re-observed (`HostUpdateRecovery.rescan`), so the newest edit is published even if
|
|
58
|
+
* its event was lost or its generation failed under the load. A replayed report is matched like
|
|
59
|
+
* any other: one that arrives twice is a repeat, so an edit still reloads once.
|
|
60
|
+
*/
|
|
22
61
|
export declare class HostUpdateCoordinator {
|
|
23
62
|
private readonly notify;
|
|
24
63
|
private readonly fail;
|
|
64
|
+
private readonly recovery?;
|
|
25
65
|
private outputRoot?;
|
|
26
66
|
private manifest?;
|
|
27
67
|
private announcedManifest?;
|
|
@@ -43,7 +83,10 @@ export declare class HostUpdateCoordinator {
|
|
|
43
83
|
private pendingGeneration?;
|
|
44
84
|
private sequence;
|
|
45
85
|
private disposed;
|
|
46
|
-
|
|
86
|
+
private burst?;
|
|
87
|
+
/** The start and the hot updates of the current burst-detection window. */
|
|
88
|
+
private window;
|
|
89
|
+
constructor(notify: () => void, fail: (error: Error) => void, recovery?: HostUpdateRecovery | undefined);
|
|
47
90
|
seed(outputRoot: string, manifest: OutputManifest): void;
|
|
48
91
|
initialCompilationInventory(): InitialCompilationInventory;
|
|
49
92
|
started(generation: number, changes: FileChange[]): void;
|
|
@@ -141,5 +184,29 @@ export declare class HostUpdateCoordinator {
|
|
|
141
184
|
private delta;
|
|
142
185
|
private prune;
|
|
143
186
|
private withTimeout;
|
|
187
|
+
/** Counts a hot update towards a burst of file events (see the class description). */
|
|
188
|
+
private hotUpdate;
|
|
189
|
+
/**
|
|
190
|
+
* A host update did not settle in time. The first of a burst is reported as it happens (as
|
|
191
|
+
* every timeout was before); the others only count towards the burst's summary, so a flood of
|
|
192
|
+
* file events logs two lines instead of one per event.
|
|
193
|
+
* @param failure The timeout, thrown to its waiter as well.
|
|
194
|
+
*/
|
|
195
|
+
private timedOut;
|
|
196
|
+
private openBurst;
|
|
197
|
+
/**
|
|
198
|
+
* One timer per burst, re-armed for the rest of the quiet period when activity moved `last`.
|
|
199
|
+
* @param burst The open burst.
|
|
200
|
+
* @param delay When to check it next.
|
|
201
|
+
*/
|
|
202
|
+
private armBurst;
|
|
203
|
+
/**
|
|
204
|
+
* The pass after a burst: the summary of its held-back timeouts, then every open output
|
|
205
|
+
* obligation whose file is still what its generation wrote is reported again, in path order,
|
|
206
|
+
* and every recorded input is re-observed once no generation is in flight. An output a newer generation rewrote since is left
|
|
207
|
+
* to that generation's own report, which supersedes the older expectation.
|
|
208
|
+
* @param burst The burst that ended.
|
|
209
|
+
*/
|
|
210
|
+
private recover;
|
|
144
211
|
}
|
|
145
212
|
export {};
|