@ng-doc/builder 22.0.0 → 22.2.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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/engine/builders/helpers/get-playground-metadata.js +7 -1
  3. package/engine/builders/helpers/get-playground-metadata.js.map +1 -1
  4. package/engine/builders/page/demo-assets.builder.js +2 -0
  5. package/engine/builders/page/demo-assets.builder.js.map +1 -1
  6. package/engine/builders/shared/content.builder.js +2 -0
  7. package/engine/builders/shared/content.builder.js.map +1 -1
  8. package/engine/builders/shared/page-wrapper.builder.js +2 -0
  9. package/engine/builders/shared/page-wrapper.builder.js.map +1 -1
  10. package/generator/bootstrap/bin.js +174 -107
  11. package/generator/bootstrap/cli.js +174 -107
  12. package/generator/bootstrap/index.js +139 -99
  13. package/generator/bootstrap/process-tree.d.ts +5 -4
  14. package/generator/build-metafile.json +647 -240
  15. package/generator/build-provenance.json +6 -4
  16. package/generator/compiler/index.d.ts +20 -0
  17. package/generator/compiler/index.js +2400 -945
  18. package/generator/compiler/plan.d.ts +24 -0
  19. package/generator/content/code-snippet.d.ts +20 -0
  20. package/generator/content/content-compiler.d.ts +5 -1
  21. package/generator/content/format-cache.d.ts +108 -0
  22. package/generator/content/highlight-cache.d.ts +12 -36
  23. package/generator/content/html-pipeline.d.ts +10 -1
  24. package/generator/content/html-pool.d.ts +8 -2
  25. package/generator/content/html-worker.js +630 -39
  26. package/generator/contracts.d.ts +57 -0
  27. package/generator/discovery/index.d.ts +2 -0
  28. package/generator/kernel/flags.d.ts +5 -0
  29. package/generator/outputs/index.d.ts +29 -0
  30. package/generator/semantic/angular.d.ts +21 -1
  31. package/generator/semantic/formatting.d.ts +27 -0
  32. package/generator/semantic/fragments.d.ts +11 -2
  33. package/generator/templates/api/class-declaration.html.nunj +5 -3
  34. package/generator/templates/demo-app.ts.nunj +37 -0
  35. package/generator/templates/demo-routes.ts.nunj +43 -0
  36. package/generator/templates/index.ts.nunj +5 -0
  37. package/generator/templates/page-wrapper.ts.nunj +6 -1
  38. package/generator/templates/page.ts.nunj +13 -2
  39. package/generator/templates/routes.ts.nunj +7 -3
  40. package/generator/templates/symbol/members.html.nunj +6 -4
  41. package/generator/templates/symbol/page.html.nunj +1 -1
  42. package/generator/vite/angular/analog-provenance.json +1 -1
  43. package/generator/vite/angular-composition.d.ts +25 -1
  44. package/generator/vite/application-builder.js +6 -1
  45. package/generator/vite/application.d.ts +14 -0
  46. package/generator/vite/content-updates.d.ts +62 -0
  47. package/generator/vite/demo-application.d.ts +34 -0
  48. package/generator/vite/dev-server-builder.js +6 -1
  49. package/generator/vite/host-updates.d.ts +153 -6
  50. package/generator/vite/index.js +985 -247
  51. package/generator/vite/lifecycle.d.ts +40 -2
  52. package/generator/vite/prerender-entry.js +72 -2
  53. package/generator/vite/prerender-runtime.d.ts +13 -0
  54. package/generator/vite/prerender.d.ts +2 -0
  55. package/helpers/format-cache.d.ts +34 -0
  56. package/helpers/format-cache.js +26 -0
  57. package/helpers/format-cache.js.map +1 -0
  58. package/helpers/format-code.js +9 -2
  59. package/helpers/format-code.js.map +1 -1
  60. package/helpers/playground/get-playground-inputs.js +6 -3
  61. package/helpers/playground/get-playground-inputs.js.map +1 -1
  62. package/helpers/playground/playground-options.d.ts +28 -0
  63. package/helpers/playground/playground-options.js +122 -0
  64. package/helpers/playground/playground-options.js.map +1 -0
  65. package/helpers/playground/written-union-order.d.ts +33 -0
  66. package/helpers/playground/written-union-order.js +106 -0
  67. package/helpers/playground/written-union-order.js.map +1 -0
  68. package/helpers/to-template-string.d.ts +5 -1
  69. package/helpers/to-template-string.js +7 -2
  70. package/helpers/to-template-string.js.map +1 -1
  71. package/interfaces/code-block-options.d.ts +5 -0
  72. package/interfaces/configuration.d.ts +92 -1
  73. package/package.json +6 -5
  74. package/parsers/parse-code-block-params.d.ts +10 -1
  75. package/parsers/parse-code-block-params.js +29 -3
  76. package/parsers/parse-code-block-params.js.map +1 -1
  77. package/schematics/collection.json +1 -1
  78. package/schematics/migrate-to-vite/analyze.js +46 -3
  79. package/schematics/migrate-to-vite/analyze.js.map +1 -1
  80. package/schematics/migrate-to-vite/setup/workspace.d.ts +5 -0
  81. package/schematics/migrate-to-vite/setup/workspace.js +20 -0
  82. package/schematics/migrate-to-vite/setup/workspace.js.map +1 -1
  83. package/schematics/migrate-to-vite/testing/fixtures.d.ts +12 -0
  84. package/schematics/migrate-to-vite/testing/fixtures.js +27 -0
  85. package/schematics/migrate-to-vite/testing/fixtures.js.map +1 -1
  86. package/schematics/vite-setup/index.d.ts +9 -5
  87. package/schematics/vite-setup/index.js +11 -5
  88. package/schematics/vite-setup/index.js.map +1 -1
  89. package/schematics/vite-setup/schema.json +1 -1
  90. package/templates/api/class-declaration.html.nunj +5 -3
  91. package/templates/demo-app.ts.nunj +37 -0
  92. package/templates/demo-routes.ts.nunj +43 -0
  93. package/templates/index.ts.nunj +5 -0
  94. package/templates/page-wrapper.ts.nunj +6 -1
  95. package/templates/page.ts.nunj +13 -2
  96. package/templates/routes.ts.nunj +7 -3
  97. package/templates/symbol/members.html.nunj +6 -4
  98. package/templates/symbol/page.html.nunj +1 -1
@@ -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,43 @@ 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`, `isolatedPlaygrounds`, `demoApplication`, `demoProviders`).
182
+ * Absent when none of them is set, so every other configuration keeps its digest, and so is every
183
+ * default field:
184
+ * - `pages`: `all` builds a demo page for every demo, `none` for none; absent, only the guides
185
+ * with an isolated demo get them.
186
+ * - `isolated`: `true` when `isolatedDemos` is: every `demo` action shows an iframe by default.
187
+ * - `playgroundPages`: `all` builds the playground pages of every playground
188
+ * (`isolatedPlaygrounds`, or `demoApplication` set); absent, only the guides with an isolated playground get them, and
189
+ * no guide does when `pages` is `none`.
190
+ * - `isolatedPlaygrounds`: `true` when `isolatedPlaygrounds` is: every playground shows its demos
191
+ * in iframes by default.
192
+ * - `path`: the URL path of the demo pages, absent for `demo-preview`.
193
+ * - `providers`: the module that `demoProviders` imports.
194
+ */
195
+ demoApplication?: {
196
+ pages?: 'all' | 'none';
197
+ isolated?: true;
198
+ playgroundPages?: 'all';
199
+ isolatedPlaygrounds?: true;
200
+ path?: string;
201
+ providers?: FilePath;
202
+ };
169
203
  themes: {
170
204
  light: string;
171
205
  dark: string;
172
206
  };
207
+ /**
208
+ * The Shiki language registrations of `shiki.langs`, flattened, as plain JSON in configuration
209
+ * order. Absent without any, so every other configuration keeps its digest.
210
+ */
211
+ shikiLangs?: ShikiLanguage[];
173
212
  repo?: {
174
213
  url: string;
175
214
  platform: 'github' | 'gitlab';
@@ -327,6 +366,11 @@ export interface PlaygroundDescriptor {
327
366
  /** Existing NgDocPlaygroundProperties shape with JSON-safe values, aliases and model inputs. */
328
367
  properties: Record<string, JsonValue>;
329
368
  content: Record<string, string>;
369
+ /**
370
+ * `true` when the playground's configuration in `ng-doc.page.ts` sets `isolated: true`, so its
371
+ * guide gets playground pages. Absent otherwise, so every other guide keeps its semantics.
372
+ */
373
+ isolated?: true;
330
374
  }
331
375
  export interface GuideSemantics {
332
376
  demos: Record<string, DemoAsset[]>;
@@ -608,6 +652,19 @@ export interface RouteRecord {
608
652
  * for the root list), which its route carries as `ngDocApiListSegment` data.
609
653
  */
610
654
  apiListSegment?: string;
655
+ /**
656
+ * The guide's demo routes module (relative to the output root), when the guide has demo pages.
657
+ * The aggregate lists it in `demo-app.ts`. Optional and only written for such guides, so a cache
658
+ * written without it stays valid: every other route record is unchanged.
659
+ */
660
+ demoModulePath?: string;
661
+ /** The names of the guide's demo pages, in the order of `demos`, with `demoModulePath`. */
662
+ demoNames?: string[];
663
+ /**
664
+ * The guide's playground pages relative to its demo route (`playground/<id>/<target>`), with
665
+ * `demoModulePath`. Only written for a guide that has them.
666
+ */
667
+ playgroundPages?: string[];
611
668
  }
612
669
  export interface ApiListRecord {
613
670
  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.
@@ -29,12 +29,17 @@ export declare const INCREMENTAL_PROGRAM_FLAG = "NGDOC_INCREMENTAL_PROGRAM";
29
29
  export declare const SHAPE_CLOSURE_FLAG = "NGDOC_SHAPE_CLOSURE";
30
30
  export declare const ANGULAR_SHARED_PASS_FLAG = "NGDOC_ANGULAR_SHARED_PASS";
31
31
  export declare const ANGULAR_STRUCTURAL_PASS_FLAG = "NGDOC_ANGULAR_STRUCTURAL_PASS";
32
+ export declare const ANGULAR_DESCRIPTION_PASS_FLAG = "NGDOC_ANGULAR_DESCRIPTION_PASS";
33
+ export declare const VITE_CONTENT_UPDATE_FLAG = "NGDOC_VITE_CONTENT_UPDATE";
32
34
  export declare const VITE_BUILD_HANDOFF_FLAG = "NGDOC_VITE_BUILD_HANDOFF";
35
+ export declare const VITE_MODULELESS_SETTLE_FLAG = "NGDOC_VITE_MODULELESS_SETTLE";
33
36
  export declare const TRACKED_PROGRAM_REUSE_FLAG = "NGDOC_TRACKED_PROGRAM_REUSE";
34
37
  export declare const FAST_START_FLAG = "NGDOC_FAST_START";
35
38
  export declare const PARALLEL_WRITES_FLAG = "NGDOC_PARALLEL_WRITES";
36
39
  export declare const HIGHLIGHT_CACHE_FLAG = "NGDOC_HIGHLIGHT_CACHE";
40
+ export declare const FORMAT_CACHE_FLAG = "NGDOC_FORMAT_CACHE";
37
41
  export declare const PARALLEL_RENDER_FLAG = "NGDOC_PARALLEL_RENDER";
42
+ export declare const USED_GRAMMARS_FLAG = "NGDOC_USED_GRAMMARS";
38
43
  /** The registry, in the order the engine applies the switches. */
39
44
  export declare const FLAGS: readonly FlagDefinition[];
40
45
  /**
@@ -56,10 +56,39 @@ 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 when the guide gets demo or playground pages, and
61
+ * returns its path, the names of the demos that got a page and the playground pages (relative
62
+ * to the guide's demo route), or undefined.
63
+ *
64
+ * - Demo pages: with `pages: 'all'`, by default when its content has a demo with
65
+ * `isolated: true`, and whenever the guide gets playground pages: the page then has a demo
66
+ * route, and the runtime offers every demo of such a page its demo page.
67
+ * - Playground pages (`playground/<id>/<target>`, one per target of every playground of the
68
+ * guide, under the page's providers and its generated playground classes): with
69
+ * `playgroundPages: 'all'`, or by default when a `playground` action of its content or a
70
+ * playground of its configuration has `isolated: true`.
71
+ */
72
+ private demoRoutes;
73
+ /**
74
+ * The playground pages of a guide, when it gets them (see `demoRoutes`): one per target of each
75
+ * playground, in the order of the guide's playgrounds and of `playgroundTargets`. A playground
76
+ * whose ID is no URL segment gets none, with a warning.
77
+ * @param request - The guide's assembly request.
78
+ * @param slots - The guide's content.
79
+ * @param diagnostics - Receives the warnings.
80
+ */
81
+ private playgroundRoutes;
59
82
  private apiEntry;
60
83
  private apiDeclaration;
61
84
  private apiLists;
62
85
  private addContentSource;
86
+ /**
87
+ * Writes `demo-app.ts`, the routes of the demo application (one lazy route per guide with demo
88
+ * or playground pages, under its categories' providers), every such page (`NG_DOC_DEMO_PAGES`)
89
+ * and its `demoProviders` import, when a guide has demo pages. Returns whether it did.
90
+ */
91
+ private demoApplication;
63
92
  private renderOutput;
64
93
  }
65
94
  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
- /** Resolves wrappers and local constants without executing source code. */
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
- /** The recorder scope of a fragment request: its phase kind and its unit key. */
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
- /** Renders one fragment request against the snapshot, recording its reads in `files`. */
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(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
12
- {%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
13
- {%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
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,43 @@
1
+ // @ts-nocheck
2
+ import {Routes} from '@angular/router';
3
+ {%- if demos.length or not playgrounds.length %}
4
+ import {NgDocDemoHostComponent} from '@ng-doc/app/demo-app';
5
+ {%- endif %}
6
+ {%- if playgrounds.length %}
7
+ import {NgDocPlaygroundHostComponent} from '@ng-doc/app/components/playground';
8
+ {%- endif %}
9
+ import entry from '{{ entryImport }}';
10
+ {%- if playgrounds.length %}
11
+ import {PLAYGROUND_PROVIDERS} from '{{ playgroundsImport }}';
12
+ {%- endif %}
13
+
14
+ // The demo pages of one guide: one route per demo, under the page's providers, as the demos have
15
+ // them on the page.
16
+ {%- if playgrounds.length %}
17
+ // Its playground pages: one route per target of each playground, which finds the generated
18
+ // playground classes of the page through PLAYGROUND_PROVIDERS, as the page does.
19
+ {%- endif %}
20
+ const routes: Routes = [{
21
+ path: '',
22
+ providers: [{% if playgrounds.length %}PLAYGROUND_PROVIDERS, {% endif %}...(entry.providers ?? [])],
23
+ children: [
24
+ {%- for demo in demos %}
25
+ {
26
+ path: '{{ demo.name }}',
27
+ component: NgDocDemoHostComponent,
28
+ title: `{{ demo.title | toTemplateString }}`,
29
+ data: {ngDocDemo: entry.demos?.['{{ demo.name }}']},
30
+ },
31
+ {%- endfor %}
32
+ {%- for playground in playgrounds %}
33
+ {
34
+ path: '{{ playground.path }}',
35
+ component: NgDocPlaygroundHostComponent,
36
+ title: `{{ playground.title | toTemplateString }}`,
37
+ data: {ngDocPlayground: {{ playground.data | dump }}},
38
+ },
39
+ {%- endfor %}
40
+ ],
41
+ }];
42
+
43
+ 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: `{{ metadata.title | toTemplateString }}`,
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,24 @@ export class PageComponent extends NgDocRootPage {
64
64
  {%- if demoAssetsPath %}
65
65
  readonly demoAssets: NgDocDemoAssets = demoAssets;
66
66
  {%- endif %}
67
+ {#- The demo and playground pages of the guide (Vite engine), and whether its demos and
68
+ playgrounds are isolated by default. #}
69
+ {%- if demoRoute %}
70
+ readonly demoRoute: string = '{{ demoRoute | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}';
71
+ {%- if isolatedDemos %}
72
+ readonly isolatedDemos: boolean = true;
73
+ {%- endif %}
74
+ {%- if isolatedPlaygrounds %}
75
+ readonly isolatedPlaygrounds: boolean = true;
76
+ {%- endif %}
77
+ {%- endif %}
67
78
 
68
79
  constructor() {
69
80
  super();
70
81
  }
71
82
  }
83
+ {#- The route has no title of its own: it inherits the title of its tab route, so a page's own
84
+ `route.title` is the only title here and wins over the generated one. #}
72
85
  {%- if contentSourceImport %}
73
86
 
74
87
  // The router shows the page only once its content has loaded, so the previous page stays on
@@ -79,7 +92,6 @@ const routes: Routes = [ɵwithNgDocContent({
79
92
  {%- endif %}
80
93
  path: '',
81
94
  component: PageComponent,
82
- title: `{{ metadata.title | toTemplateString }}`,
83
95
  }, pageContentSource)]
84
96
  {%- else %}
85
97
 
@@ -89,7 +101,6 @@ const routes: Routes = [{
89
101
  {%- endif %}
90
102
  path: '',
91
103
  component: PageComponent,
92
- title: `{{ metadata.title | toTemplateString }}`,
93
104
  }]
94
105
  {%- endif %}
95
106
 
@@ -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
- {%- if entry.item.apiListSegment is defined %}
24
- data: {ngDocApiListSegment: '{{ entry.item.apiListSegment | replace("\\", "\\\\") | replace("'", "\\'") | replace("\n", "\\n") }}'},
25
- {%- endif %}
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
- {% macro render(declaration) %}
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(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
204
- {%- set allMethods = declaration | getClassMethods | excludeByJsDocTags('internal') | filterByScope(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
205
- {%- set allAccessors = declaration | getClassAccessors | excludeByJsDocTags('internal') | filterByScope(["public", "protected"]) | filterUselessMembers | sortByNodesName -%}
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 -%}
@@ -69,7 +69,7 @@
69
69
  {{ overloadMacro.render(overloads) }}
70
70
  {% endif %}
71
71
  {% else %}
72
- {{ membersMacro.render(declaration) }}
72
+ {{ membersMacro.render(declaration, hideProtectedMembers) }}
73
73
  {% endif %}
74
74
 
75
75
  {% include "parts/examples.html.nunj" %}
@@ -858,7 +858,7 @@
858
858
  },
859
859
  {
860
860
  "id": "vite",
861
- "owner": "peer vite@8.3.2"
861
+ "owner": "peer vite@8.3.3"
862
862
  }
863
863
  ],
864
864
  "encounteredStaticExternals": [
@@ -10,6 +10,17 @@ import { type HostUpdateTicket, type InitialCompilationInventory } from './host-
10
10
  * @returns Whether only global style sheets depend on the file.
11
11
  */
12
12
  export declare function globalStyleSheet(context: Pick<HmrContext, 'file' | 'modules'>): boolean;
13
+ /**
14
+ * Whether a hot update's hooks hand Vite anything to do once they return: a module to update, or
15
+ * (for an HTML file without modules) the page reload Vite sends itself. Only such a hot update
16
+ * has to wait for the newest publication before it returns; a module-less one settles once it is
17
+ * acknowledged (`HostUpdateCoordinator.settle`).
18
+ * @param file - The hot update's file.
19
+ * @param modules - The modules Vite updates after this hook: what the hook returned, or the
20
+ * context's modules when it returned nothing.
21
+ * @returns Whether Vite updates or reloads anything for the hot update.
22
+ */
23
+ export declare function handsViteModules(file: string, modules: readonly unknown[]): boolean;
13
24
  /**
14
25
  * The hooks NgDoc's patched Analog compiler exposes as `api.ngDocHost` (the
15
26
  * `generation-claimed-filesystem-*` changes): a claimed add or unlink event invalidates the
@@ -31,6 +42,15 @@ export interface AngularCompositionBridge {
31
42
  start(file: string, read: () => string | Promise<string>): HostUpdateTicket;
32
43
  /** Other TypeScript outputs to compile in this ready ticket's pass (see compilerCompanions). */
33
44
  companions?(ticket: HostUpdateTicket): string[];
45
+ /**
46
+ * Whether the generation of this committed source update still has a generated TypeScript
47
+ * update to compile (see HostUpdateCoordinator.generationPass).
48
+ */
49
+ generationPass?(ticket: HostUpdateTicket): boolean;
50
+ /** Whether `file` is a description module the generator watches. */
51
+ descriptionModule?(file: string): boolean;
52
+ /** Whether `file` is a generated output. */
53
+ generated?(file: string): boolean;
34
54
  /** A mark of the diagnostics recorded so far (see HostUpdateCoordinator.diagnosticMark). */
35
55
  diagnosticMark?(): number;
36
56
  /**
@@ -41,7 +61,11 @@ export interface AngularCompositionBridge {
41
61
  acknowledge(ticket: HostUpdateTicket, resourceWitness: boolean, compilerPass?: boolean, recoverBefore?: number): Promise<void>;
42
62
  diagnostic(ticket: HostUpdateTicket, error: Error): void;
43
63
  committed(ticket: HostUpdateTicket): Promise<void>;
44
- settle(ticket: HostUpdateTicket): Promise<void>;
64
+ /**
65
+ * Settles the hot update (see HostUpdateCoordinator.settle). `modules` is false when its hooks
66
+ * hand Vite nothing to update ({@link handsViteModules}); omitted, it is true.
67
+ */
68
+ settle(ticket: HostUpdateTicket, modules?: boolean): Promise<void>;
45
69
  fail(error: Error): void;
46
70
  }
47
71
  export interface AngularComposition {
@@ -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,62 @@
1
+ import type { HtmlTagDescriptor, ViteDevServer, WebSocketClient } from 'vite';
2
+ import type { AnnouncedChange } from './host-updates.js';
3
+ /** The custom HMR event that carries a content update to the NgDoc client of a page. */
4
+ export declare const CONTENT_UPDATE_EVENT = "ng-doc:content-update";
5
+ /** The custom HMR event a page's NgDoc client sends once it listens for content updates. */
6
+ export declare const CONTENT_CLIENT_EVENT = "ng-doc:content-client";
7
+ /** The version of the content update messages; a client announces the version it reads. */
8
+ export declare const CONTENT_UPDATE_VERSION = 1;
9
+ /** A content update as the server sends it to a client. */
10
+ export interface ContentUpdateMessage {
11
+ readonly version: typeof CONTENT_UPDATE_VERSION;
12
+ /** The URLs to import the changed content modules from, each with a fresh timestamp. */
13
+ readonly modules: readonly string[];
14
+ /** Whether the search index changed. */
15
+ readonly search: boolean;
16
+ }
17
+ /** What a content-only publication changed. */
18
+ export interface ContentChanges {
19
+ /** The changed content modules (`*.content.mjs`), absolute, in path order. */
20
+ readonly modules: readonly string[];
21
+ /** Whether the search index changed. */
22
+ readonly search: boolean;
23
+ }
24
+ /**
25
+ * The page's NgDoc development client, a module script Vite serves with a hot context. It imports
26
+ * the changed content modules again and hands their payloads to the NgDoc runtime as a cancelable
27
+ * window event (`@ng-doc/app`'s `ɵNG_DOC_CONTENT_UPDATE_EVENT`, the same name). The runtime
28
+ * cancels it once it applied them; otherwise (no NgDoc runtime on the page, a payload it cannot
29
+ * read) and on any failure the client reloads the page, as it did before. Updates apply one at a
30
+ * time, in the order they arrive.
31
+ */
32
+ export declare const CONTENT_UPDATE_CLIENT: string;
33
+ /**
34
+ * The tag that adds the NgDoc development client to a page the development server serves.
35
+ * Vite turns an inline module script into a module of its own, with a hot context.
36
+ * @param html The page.
37
+ * @returns The tag, or none when the page carries the client or the switch is off.
38
+ */
39
+ export declare function contentUpdateClientTags(html: string): HtmlTagDescriptor[];
40
+ /** Whether content updates are on (`NGDOC_VITE_CONTENT_UPDATE`). */
41
+ export declare function contentUpdatesEnabled(): boolean;
42
+ /**
43
+ * What a reload announces, when it changes only content: every changed file is an existing
44
+ * content module (`*.content.mjs`) whose bytes changed, the search index or the keyword map
45
+ * (which no page reads). Anything else (a TypeScript module, a created or deleted file, another
46
+ * asset, the API lists) takes a full reload.
47
+ * @param changes The files that differ from the last announcement.
48
+ * @param assetDirectory The asset directory, relative to the output root.
49
+ * @returns The content changes, or `undefined` for a full reload.
50
+ */
51
+ export declare function contentOnly(changes: readonly AnnouncedChange[] | undefined, assetDirectory: string): ContentChanges | undefined;
52
+ /**
53
+ * Sends a content update. Each client that announced its NgDoc client gets the changed content
54
+ * modules it may have loaded (those in the client module graph: a module nobody imported loads
55
+ * afresh anyway); every other client reloads, as before. Without any client the reload is sent
56
+ * as Vite buffers it, so a page that connects later reloads.
57
+ * @param server The development server.
58
+ * @param clients The clients that announced the NgDoc client.
59
+ * @param changes The content changes.
60
+ * @param timestamp A timestamp no earlier update used, so the browser imports new modules.
61
+ */
62
+ export declare function sendContentUpdate(server: ViteDevServer, clients: WeakSet<WebSocketClient>, changes: ContentChanges, timestamp: number): void;