@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
@@ -1,5 +1,5 @@
1
1
  // libs/builder/generator/vite/plugin.ts
2
- import path19 from "node:path";
2
+ import path21 from "node:path";
3
3
 
4
4
  // libs/builder/generator/artifacts/index.ts
5
5
  import { randomUUID } from "node:crypto";
@@ -49,24 +49,24 @@ function sha256Hex(value) {
49
49
  function canonicalJson(value) {
50
50
  return canonical(value, /* @__PURE__ */ new Set()) ?? "null";
51
51
  }
52
- function canonical(value, path24) {
52
+ function canonical(value, path26) {
53
53
  if (value !== null && typeof value === "object" && typeof value.toJSON === "function")
54
54
  value = value.toJSON();
55
55
  if (value === null || typeof value !== "object") return JSON.stringify(value);
56
- if (path24.has(value)) throw new TypeError("Converting circular structure to canonical JSON");
57
- path24.add(value);
56
+ if (path26.has(value)) throw new TypeError("Converting circular structure to canonical JSON");
57
+ path26.add(value);
58
58
  let text5;
59
59
  if (Array.isArray(value)) {
60
- text5 = `[${Array.from(value, (item) => canonical(item, path24) ?? "null").join(",")}]`;
60
+ text5 = `[${Array.from(value, (item) => canonical(item, path26) ?? "null").join(",")}]`;
61
61
  } else {
62
62
  const fields = [];
63
63
  for (const key of Object.keys(value).sort(compareCodeUnits)) {
64
- const item = canonical(value[key], path24);
64
+ const item = canonical(value[key], path26);
65
65
  if (item !== void 0) fields.push(`${JSON.stringify(key)}:${item}`);
66
66
  }
67
67
  text5 = `{${fields.join(",")}}`;
68
68
  }
69
- path24.delete(value);
69
+ path26.delete(value);
70
70
  return text5;
71
71
  }
72
72
  function digestOf(value) {
@@ -1608,7 +1608,7 @@ function normalizeRelative(value) {
1608
1608
  return normalized;
1609
1609
  }
1610
1610
  function trustedBase(last, request) {
1611
- const published = (manifest) => isRecord(manifest) && manifest.projectId === last.projectId && manifest.revision === last.revision && manifest.generation === last.generation;
1611
+ const published = (manifest) => isRecord(manifest) && manifest["projectId"] === last.projectId && manifest["revision"] === last.revision && manifest["generation"] === last.generation;
1612
1612
  const base = request.base;
1613
1613
  return isRecord(base) && published(base.manifest) && published(request.previous) && isRecord(base.snapshot) && base.snapshot.projectId === last.projectId && base.snapshot.revision === last.revision;
1614
1614
  }
@@ -1622,17 +1622,17 @@ function sameManifestStamp(left, right) {
1622
1622
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
1623
1623
  }
1624
1624
  function configurationDigest(candidate) {
1625
- return isRecord(candidate) && isRecord(candidate.configuration) ? candidate.configuration.digest : void 0;
1625
+ return isRecord(candidate) && isRecord(candidate["configuration"]) ? candidate["configuration"]["digest"] : void 0;
1626
1626
  }
1627
1627
  function unchangedArtifact(artifact, last) {
1628
- const id = artifact.id;
1629
- const outputs = artifact.outputs;
1630
- if (!string(artifact.revision) || last.revisions.get(id) !== artifact.revision || !Array.isArray(outputs) || outputs.length !== (last.counts.get(id) ?? 0))
1628
+ const id = artifact["id"];
1629
+ const outputs = artifact["outputs"];
1630
+ if (!string(artifact["revision"]) || last.revisions.get(id) !== artifact["revision"] || !Array.isArray(outputs) || outputs.length !== (last.counts.get(id) ?? 0))
1631
1631
  return false;
1632
1632
  return outputs.every((output) => {
1633
- if (!isRecord(output) || !string(output.path)) return false;
1634
- const entry = last.files.get(output.path);
1635
- return entry?.ownerId === id && entry.digest === output.digest && entry.role === output.role;
1633
+ if (!isRecord(output) || !string(output["path"])) return false;
1634
+ const entry = last.files.get(output["path"]);
1635
+ return entry?.ownerId === id && entry.digest === output["digest"] && entry.role === output["role"];
1636
1636
  });
1637
1637
  }
1638
1638
  var EMPTY_SCOPE_FACTS = canonicalJson({ definitions: [], references: [] });
@@ -1815,7 +1815,7 @@ function stringArray(value) {
1815
1815
  function isIdentity(value) {
1816
1816
  if (!isRecord(value) || !exact(value, ["projectId", "entryId", "role"], ["declarationId", "part"]))
1817
1817
  return false;
1818
- return string(value.projectId) && string(value.entryId) && [
1818
+ return string(value["projectId"]) && string(value["entryId"]) && [
1819
1819
  "page-shell",
1820
1820
  "content",
1821
1821
  "demo-assets",
@@ -1823,7 +1823,7 @@ function isIdentity(value) {
1823
1823
  "category",
1824
1824
  "api-list",
1825
1825
  "aggregate"
1826
- ].includes(String(value.role)) && (value.declarationId === void 0 || string(value.declarationId)) && (value.part === void 0 || string(value.part));
1826
+ ].includes(String(value["role"])) && (value["declarationId"] === void 0 || string(value["declarationId"])) && (value["part"] === void 0 || string(value["part"]));
1827
1827
  }
1828
1828
  function isFingerprint(value) {
1829
1829
  return isRecord(value) && exact(value, [
@@ -1833,40 +1833,40 @@ function isFingerprint(value) {
1833
1833
  "configurationDigest",
1834
1834
  "inputDigest",
1835
1835
  "keywordDigest"
1836
- ]) && value.schemaVersion === SCHEMA_VERSION && [
1837
- value.compilerVersion,
1838
- value.toolchainDigest,
1839
- value.configurationDigest,
1840
- value.inputDigest,
1841
- value.keywordDigest
1836
+ ]) && value["schemaVersion"] === SCHEMA_VERSION && [
1837
+ value["compilerVersion"],
1838
+ value["toolchainDigest"],
1839
+ value["configurationDigest"],
1840
+ value["inputDigest"],
1841
+ value["keywordDigest"]
1842
1842
  ].every(string);
1843
1843
  }
1844
1844
  function isDependency(value) {
1845
- if (!isRecord(value) || !string(value.kind)) return false;
1846
- if (value.kind === "content")
1847
- return exact(value, ["kind", "path", "digest"]) && string(value.path) && string(value.digest);
1848
- if (value.kind === "existence")
1849
- return exact(value, ["kind", "path", "exists"]) && string(value.path) && typeof value.exists === "boolean";
1850
- if (value.kind === "glob")
1851
- return exact(value, ["kind", "root", "include", "exclude", "members"]) && string(value.root) && stringArray(value.include) && stringArray(value.exclude) && stringArray(value.members);
1852
- if (value.kind === "semantic")
1853
- return exact(value, ["kind", "scopeId", "digest", "files", "reason"]) && string(value.scopeId) && string(value.digest) && stringArray(value.files) && string(value.reason);
1854
- if (value.kind === "semantic-reference")
1855
- return exact(value, ["kind", "scopeId", "digest", "reason"]) && string(value.scopeId) && string(value.digest) && string(value.reason);
1856
- if (value.kind === "semantic-closure")
1857
- return exact(value, ["kind", "scopeId", "key", "digest"]) && string(value.scopeId) && string(value.key) && string(value.digest);
1858
- if (value.kind === "evaluated")
1859
- return exact(value, ["kind", "entryId", "digest"]) && string(value.entryId) && string(value.digest);
1860
- return value.kind === "keyword" && exact(value, ["kind", "key", "digest"]) && string(value.key) && string(value.digest);
1845
+ if (!isRecord(value) || !string(value["kind"])) return false;
1846
+ if (value["kind"] === "content")
1847
+ return exact(value, ["kind", "path", "digest"]) && string(value["path"]) && string(value["digest"]);
1848
+ if (value["kind"] === "existence")
1849
+ return exact(value, ["kind", "path", "exists"]) && string(value["path"]) && typeof value["exists"] === "boolean";
1850
+ if (value["kind"] === "glob")
1851
+ return exact(value, ["kind", "root", "include", "exclude", "members"]) && string(value["root"]) && stringArray(value["include"]) && stringArray(value["exclude"]) && stringArray(value["members"]);
1852
+ if (value["kind"] === "semantic")
1853
+ return exact(value, ["kind", "scopeId", "digest", "files", "reason"]) && string(value["scopeId"]) && string(value["digest"]) && stringArray(value["files"]) && string(value["reason"]);
1854
+ if (value["kind"] === "semantic-reference")
1855
+ return exact(value, ["kind", "scopeId", "digest", "reason"]) && string(value["scopeId"]) && string(value["digest"]) && string(value["reason"]);
1856
+ if (value["kind"] === "semantic-closure")
1857
+ return exact(value, ["kind", "scopeId", "key", "digest"]) && string(value["scopeId"]) && string(value["key"]) && string(value["digest"]);
1858
+ if (value["kind"] === "evaluated")
1859
+ return exact(value, ["kind", "entryId", "digest"]) && string(value["entryId"]) && string(value["digest"]);
1860
+ return value["kind"] === "keyword" && exact(value, ["kind", "key", "digest"]) && string(value["key"]) && string(value["digest"]);
1861
1861
  }
1862
1862
  function isKeyword(value) {
1863
- return isRecord(value) && exact(value, ["key", "title", "path"], ["type", "languages", "description"]) && string(value.key) && string(value.title) && string(value.path) && (value.type === void 0 || value.type === "link") && (value.languages === void 0 || stringArray(value.languages)) && [value.description, value.signature].every((item) => item === void 0 || string(item));
1863
+ return isRecord(value) && exact(value, ["key", "title", "path"], ["type", "languages", "description"]) && string(value["key"]) && string(value["title"]) && string(value["path"]) && (value["type"] === void 0 || value["type"] === "link") && (value["languages"] === void 0 || stringArray(value["languages"])) && [value["description"], value["signature"]].every((item) => item === void 0 || string(item));
1864
1864
  }
1865
1865
  function isSource(value) {
1866
- return isRecord(value) && exact(value, ["path"], ["line", "column"]) && string(value.path) && (value.line === void 0 || finite(value.line)) && (value.column === void 0 || finite(value.column));
1866
+ return isRecord(value) && exact(value, ["path"], ["line", "column"]) && string(value["path"]) && (value["line"] === void 0 || finite(value["line"])) && (value["column"] === void 0 || finite(value["column"]));
1867
1867
  }
1868
1868
  function isDiagnostic(value) {
1869
- return isRecord(value) && exact(value, ["code", "severity", "message", "stage"], ["source", "ownerId", "related"]) && string(value.code) && ["error", "warning", "info"].includes(String(value.severity)) && string(value.message) && [
1869
+ return isRecord(value) && exact(value, ["code", "severity", "message", "stage"], ["source", "ownerId", "related"]) && string(value["code"]) && ["error", "warning", "info"].includes(String(value["severity"])) && string(value["message"]) && [
1870
1870
  "discovery",
1871
1871
  "evaluation",
1872
1872
  "semantic",
@@ -1875,8 +1875,8 @@ function isDiagnostic(value) {
1875
1875
  "cache",
1876
1876
  "commit",
1877
1877
  "host"
1878
- ].includes(String(value.stage)) && (value.source === void 0 || isSource(value.source)) && (value.ownerId === void 0 || string(value.ownerId)) && (value.related === void 0 || Array.isArray(value.related) && value.related.every(
1879
- (item) => isRecord(item) && exact(item, ["message", "source"]) && string(item.message) && isSource(item.source)
1878
+ ].includes(String(value["stage"])) && (value["source"] === void 0 || isSource(value["source"])) && (value["ownerId"] === void 0 || string(value["ownerId"])) && (value["related"] === void 0 || Array.isArray(value["related"]) && value["related"].every(
1879
+ (item) => isRecord(item) && exact(item, ["message", "source"]) && string(item["message"]) && isSource(item["source"])
1880
1880
  ));
1881
1881
  }
1882
1882
  function isSearch(value) {
@@ -1884,17 +1884,17 @@ function isSearch(value) {
1884
1884
  value,
1885
1885
  ["breadcrumbs", "pageType", "title", "section", "route", "content"],
1886
1886
  ["fragment", "kind", "signature", "description"]
1887
- ) && stringArray(value.breadcrumbs) && ["guide", "api"].includes(String(value.pageType)) && [value.title, value.section, value.route, value.content].every(string) && [value.fragment, value.kind, value.signature, value.description].every(
1887
+ ) && stringArray(value["breadcrumbs"]) && ["guide", "api"].includes(String(value["pageType"])) && [value["title"], value["section"], value["route"], value["content"]].every(string) && [value["fragment"], value["kind"], value["signature"], value["description"]].every(
1888
1888
  (item) => item === void 0 || string(item)
1889
1889
  );
1890
1890
  }
1891
1891
  function isAnchor(value) {
1892
- return isRecord(value) && exact(value, ["anchorId", "anchor", "title", "type"], ["scope"]) && [value.anchorId, value.anchor, value.title].every(string) && ["heading", "member"].includes(String(value.type)) && (value.scope === void 0 || isRecord(value.scope) && exact(value.scope, ["key", "title"]) && string(value.scope.key) && string(value.scope.title));
1892
+ return isRecord(value) && exact(value, ["anchorId", "anchor", "title", "type"], ["scope"]) && [value["anchorId"], value["anchor"], value["title"]].every(string) && ["heading", "member"].includes(String(value["type"])) && (value["scope"] === void 0 || isRecord(value["scope"]) && exact(value["scope"], ["key", "title"]) && string(value["scope"]["key"]) && string(value["scope"]["title"]));
1893
1893
  }
1894
1894
  function isLinkedContent(value) {
1895
- if (!isRecord(value) || !exact(value, ["ir", "html", "searchRecords", "keywordDigest"]) || !string(value.html) || !string(value.keywordDigest) || !Array.isArray(value.searchRecords) || !value.searchRecords.every(isSearch) || !isRecord(value.ir))
1895
+ if (!isRecord(value) || !exact(value, ["ir", "html", "searchRecords", "keywordDigest"]) || !string(value["html"]) || !string(value["keywordDigest"]) || !Array.isArray(value["searchRecords"]) || !value["searchRecords"].every(isSearch) || !isRecord(value["ir"]))
1896
1896
  return false;
1897
- const ir = value.ir;
1897
+ const ir = value["ir"];
1898
1898
  return exact(
1899
1899
  ir,
1900
1900
  [
@@ -1913,7 +1913,9 @@ function isLinkedContent(value) {
1913
1913
  "diagnostics"
1914
1914
  ],
1915
1915
  ["icon", "searchBreadcrumbs"]
1916
- ) && ir.schemaVersion === SCHEMA_VERSION && [ir.id, ir.entryId, ir.title, ir.route, ir.absoluteRoute, ir.html].every(string) && ["guide-tab", "api-tab", "header", "demo-assets"].includes(String(ir.role)) && (ir.searchBreadcrumbs === void 0 || stringArray(ir.searchBreadcrumbs)) && (ir.icon === void 0 || string(ir.icon)) && Array.isArray(ir.anchors) && ir.anchors.every(isAnchor) && Array.isArray(ir.exportedKeywords) && ir.exportedKeywords.every(isKeyword) && stringArray(ir.usedKeywords) && Array.isArray(ir.dependencies) && ir.dependencies.every(isDependency) && Array.isArray(ir.diagnostics) && ir.diagnostics.every(isDiagnostic);
1916
+ ) && ir["schemaVersion"] === SCHEMA_VERSION && [ir["id"], ir["entryId"], ir["title"], ir["route"], ir["absoluteRoute"], ir["html"]].every(
1917
+ string
1918
+ ) && ["guide-tab", "api-tab", "header", "demo-assets"].includes(String(ir["role"])) && (ir["searchBreadcrumbs"] === void 0 || stringArray(ir["searchBreadcrumbs"])) && (ir["icon"] === void 0 || string(ir["icon"])) && Array.isArray(ir["anchors"]) && ir["anchors"].every(isAnchor) && Array.isArray(ir["exportedKeywords"]) && ir["exportedKeywords"].every(isKeyword) && stringArray(ir["usedKeywords"]) && Array.isArray(ir["dependencies"]) && ir["dependencies"].every(isDependency) && Array.isArray(ir["diagnostics"]) && ir["diagnostics"].every(isDiagnostic);
1917
1919
  }
1918
1920
  function isRoute(value) {
1919
1921
  return isRecord(value) && exact(
@@ -1927,23 +1929,31 @@ function isRoute(value) {
1927
1929
  "metadata",
1928
1930
  "category",
1929
1931
  "modulePath",
1930
- "apiListSegment"
1932
+ "apiListSegment",
1933
+ "demoModulePath",
1934
+ "demoNames",
1935
+ "playgroundPages"
1931
1936
  ]
1932
- ) && [value.id, value.path, value.title].every(string) && (value.parentId === void 0 || string(value.parentId)) && (value.order === void 0 || finite(value.order)) && (value.hidden === void 0 || typeof value.hidden === "boolean") && (value.icon === void 0 || string(value.icon)) && (value.modulePath === void 0 || string(value.modulePath)) && (value.apiListSegment === void 0 || string(value.apiListSegment)) && (value.metadata === void 0 || isRecord(value.metadata) && exact(value.metadata, ["description", "tags"]) && string(value.metadata.description) && isRecord(value.metadata.tags) && Object.values(value.metadata.tags).every(stringArray)) && (value.category === void 0 || isRecord(value.category) && exact(value.category, ["runtimeImport"], ["expandable", "expanded"]) && isRecord(value.category.runtimeImport) && exact(value.category.runtimeImport, ["source", "exportName"]) && string(value.category.runtimeImport.source) && string(value.category.runtimeImport.exportName) && (value.category.expandable === void 0 || typeof value.category.expandable === "boolean") && (value.category.expanded === void 0 || typeof value.category.expanded === "boolean"));
1937
+ ) && [value["id"], value["path"], value["title"]].every(string) && (value["parentId"] === void 0 || string(value["parentId"])) && (value["order"] === void 0 || finite(value["order"])) && (value["hidden"] === void 0 || typeof value["hidden"] === "boolean") && (value["icon"] === void 0 || string(value["icon"])) && (value["modulePath"] === void 0 || string(value["modulePath"])) && (value["apiListSegment"] === void 0 || string(value["apiListSegment"])) && (value["demoModulePath"] === void 0 || string(value["demoModulePath"])) && (value["demoNames"] === void 0 || stringArray(value["demoNames"])) && (value["playgroundPages"] === void 0 || stringArray(value["playgroundPages"])) && (value["metadata"] === void 0 || isRecord(value["metadata"]) && exact(value["metadata"], ["description", "tags"]) && string(value["metadata"]["description"]) && isRecord(value["metadata"]["tags"]) && Object.values(value["metadata"]["tags"]).every(stringArray)) && (value["category"] === void 0 || isRecord(value["category"]) && exact(value["category"], ["runtimeImport"], ["expandable", "expanded"]) && isRecord(value["category"]["runtimeImport"]) && exact(value["category"]["runtimeImport"], ["source", "exportName"]) && string(value["category"]["runtimeImport"]["source"]) && string(value["category"]["runtimeImport"]["exportName"]) && (value["category"]["expandable"] === void 0 || typeof value["category"]["expandable"] === "boolean") && (value["category"]["expanded"] === void 0 || typeof value["category"]["expanded"] === "boolean"));
1933
1938
  }
1934
1939
  function isApiList(value) {
1935
1940
  return isRecord(value) && exact(
1936
1941
  value,
1937
1942
  ["apiEntryId", "scopeId", "scopeTitle", "name", "type", "route"],
1938
1943
  ["description", "signature"]
1939
- ) && [value.apiEntryId, value.scopeId, value.scopeTitle, value.name, value.type, value.route].every(
1940
- string
1941
- ) && [value.description, value.signature].every((item) => item === void 0 || string(item));
1944
+ ) && [
1945
+ value["apiEntryId"],
1946
+ value["scopeId"],
1947
+ value["scopeTitle"],
1948
+ value["name"],
1949
+ value["type"],
1950
+ value["route"]
1951
+ ].every(string) && [value["description"], value["signature"]].every((item) => item === void 0 || string(item));
1942
1952
  }
1943
1953
  function isFileOutput(value) {
1944
- return isRecord(value) && exact(value, ["path", "role", "encoding", "content", "digest"]) && string(value.path) && ["angular", "content", "asset", "routes", "context", "search", "api-list"].includes(
1945
- String(value.role)
1946
- ) && ["utf8", "base64"].includes(String(value.encoding)) && string(value.content) && string(value.digest);
1954
+ return isRecord(value) && exact(value, ["path", "role", "encoding", "content", "digest"]) && string(value["path"]) && ["angular", "content", "asset", "routes", "context", "search", "api-list"].includes(
1955
+ String(value["role"])
1956
+ ) && ["utf8", "base64"].includes(String(value["encoding"])) && string(value["content"]) && string(value["digest"]);
1947
1957
  }
1948
1958
  function isContentDescriptor(value) {
1949
1959
  if (!isRecord(value) || !exact(
@@ -1967,23 +1977,23 @@ function isContentDescriptor(value) {
1967
1977
  ["icon", "keyword"]
1968
1978
  ))
1969
1979
  return false;
1970
- if (value.schemaVersion !== SCHEMA_VERSION || ![
1971
- value.id,
1972
- value.ownerId,
1973
- value.title,
1974
- value.route,
1975
- value.absoluteRoute,
1976
- value.inputDigest,
1977
- value.requestDigest
1978
- ].every(string) || ![value.id, value.ownerId, value.inputDigest, value.requestDigest].every(
1980
+ if (value["schemaVersion"] !== SCHEMA_VERSION || ![
1981
+ value["id"],
1982
+ value["ownerId"],
1983
+ value["title"],
1984
+ value["route"],
1985
+ value["absoluteRoute"],
1986
+ value["inputDigest"],
1987
+ value["requestDigest"]
1988
+ ].every(string) || ![value["id"], value["ownerId"], value["inputDigest"], value["requestDigest"]].every(
1979
1989
  (item) => string(item) && item.length > 0 && !item.includes("\0")
1980
- ) || !finite(value.ordinal) || value.ordinal < 0 || !Number.isInteger(value.ordinal) || !["guide-tab", "api-tab", "header"].includes(String(value.role)) || !stringArray(value.searchBreadcrumbs) || !Array.isArray(value.dependencies) || !value.dependencies.every((dependency) => isPhysicalDescriptorDependency(dependency)) || new Set(value.dependencies.map((dependency) => canonicalJson(dependency))).size !== value.dependencies.length || !stringArray(value.closureIds) || value.icon !== void 0 && !string(value.icon) || value.keyword !== void 0 && !string(value.keyword) || !isRecord(value.locator))
1990
+ ) || !finite(value["ordinal"]) || value["ordinal"] < 0 || !Number.isInteger(value["ordinal"]) || !["guide-tab", "api-tab", "header"].includes(String(value["role"])) || !stringArray(value["searchBreadcrumbs"]) || !Array.isArray(value["dependencies"]) || !value["dependencies"].every((dependency) => isPhysicalDescriptorDependency(dependency)) || new Set(value["dependencies"].map((dependency) => canonicalJson(dependency))).size !== value["dependencies"].length || !stringArray(value["closureIds"]) || value["icon"] !== void 0 && !string(value["icon"]) || value["keyword"] !== void 0 && !string(value["keyword"]) || !isRecord(value["locator"]))
1981
1991
  return false;
1982
- const locator = value.locator;
1983
- return value.role === "header" ? exact(locator, ["kind"]) && locator.kind === "header" : value.role === "guide-tab" ? exact(locator, ["kind", "markdown"]) && locator.kind === "guide-tab" && string(locator.markdown) : exact(locator, ["kind", "declarationId"]) && locator.kind === "api-tab" && string(locator.declarationId);
1992
+ const locator = value["locator"];
1993
+ return value["role"] === "header" ? exact(locator, ["kind"]) && locator["kind"] === "header" : value["role"] === "guide-tab" ? exact(locator, ["kind", "markdown"]) && locator["kind"] === "guide-tab" && string(locator["markdown"]) : exact(locator, ["kind", "declarationId"]) && locator["kind"] === "api-tab" && string(locator["declarationId"]);
1984
1994
  }
1985
1995
  function isPhysicalDescriptorDependency(value) {
1986
- return isRecord(value) && (value.kind === "content" && exact(value, ["kind", "path", "digest"]) && absoluteNormalizedPath(value.path) && string(value.digest) || value.kind === "existence" && exact(value, ["kind", "path", "exists"]) && absoluteNormalizedPath(value.path) && typeof value.exists === "boolean");
1996
+ return isRecord(value) && (value["kind"] === "content" && exact(value, ["kind", "path", "digest"]) && absoluteNormalizedPath(value["path"]) && string(value["digest"]) || value["kind"] === "existence" && exact(value, ["kind", "path", "exists"]) && absoluteNormalizedPath(value["path"]) && typeof value["exists"] === "boolean");
1987
1997
  }
1988
1998
  function validDescriptorPlan(artifact) {
1989
1999
  const descriptors = artifact.contentDescriptors;
@@ -2068,7 +2078,7 @@ function isPageArtifact(value) {
2068
2078
  "diagnostics"
2069
2079
  ],
2070
2080
  ["contentDescriptors"]
2071
- ) && string(value.id) && isIdentity(value.identity) && string(value.revision) && isFingerprint(value.fingerprint) && Array.isArray(value.dependencies) && value.dependencies.every(isDependency) && Array.isArray(value.content) && value.content.every(isLinkedContent) && Array.isArray(value.exportedKeywords) && value.exportedKeywords.every(isKeyword) && stringArray(value.usedKeywords) && Array.isArray(value.searchRecords) && value.searchRecords.every(isSearch) && Array.isArray(value.routes) && value.routes.every(isRoute) && Array.isArray(value.apiList) && value.apiList.every(isApiList) && Array.isArray(value.outputs) && value.outputs.every((output) => isFileOutput(output) && validOutputDigest(output)) && Array.isArray(value.diagnostics) && value.diagnostics.every(isDiagnostic) && validDescriptorPlan(value);
2081
+ ) && string(value["id"]) && isIdentity(value["identity"]) && string(value["revision"]) && isFingerprint(value["fingerprint"]) && Array.isArray(value["dependencies"]) && value["dependencies"].every(isDependency) && Array.isArray(value["content"]) && value["content"].every(isLinkedContent) && Array.isArray(value["exportedKeywords"]) && value["exportedKeywords"].every(isKeyword) && stringArray(value["usedKeywords"]) && Array.isArray(value["searchRecords"]) && value["searchRecords"].every(isSearch) && Array.isArray(value["routes"]) && value["routes"].every(isRoute) && Array.isArray(value["apiList"]) && value["apiList"].every(isApiList) && Array.isArray(value["outputs"]) && value["outputs"].every((output) => isFileOutput(output) && validOutputDigest(output)) && Array.isArray(value["diagnostics"]) && value["diagnostics"].every(isDiagnostic) && validDescriptorPlan(value);
2072
2082
  }
2073
2083
  function isJsonTree(value, ancestors = /* @__PURE__ */ new WeakSet()) {
2074
2084
  if (value === null || string(value) || typeof value === "boolean") return true;
@@ -2103,19 +2113,19 @@ function validOutputDigest(output) {
2103
2113
  }
2104
2114
  }
2105
2115
  function isPublishedGeneratorConfiguration(value) {
2106
- return isJsonTree(value) && isRecord(value) && exact(value, ["outputRoot", "cacheRoot", "assetDirectory", "themes", "digest"]) && [value.outputRoot, value.cacheRoot].every(
2116
+ return isJsonTree(value) && isRecord(value) && exact(value, ["outputRoot", "cacheRoot", "assetDirectory", "themes", "digest"]) && [value["outputRoot"], value["cacheRoot"]].every(
2107
2117
  (item) => string(item) && path2.isAbsolute(item) && path2.resolve(item).replace(/\\/g, "/") === item && !item.includes("\0")
2108
- ) && string(value.assetDirectory) && !path2.win32.parse(value.assetDirectory).root && safeRelative(value.assetDirectory) && isRecord(value.themes) && exact(value.themes, ["light", "dark"]) && string(value.themes.light) && string(value.themes.dark) && string(value.digest);
2118
+ ) && string(value["assetDirectory"]) && !path2.win32.parse(value["assetDirectory"]).root && safeRelative(value["assetDirectory"]) && isRecord(value["themes"]) && exact(value["themes"], ["light", "dark"]) && string(value["themes"]["light"]) && string(value["themes"]["dark"]) && string(value["digest"]);
2109
2119
  }
2110
2120
  function isArtifactSnapshotShape(value) {
2111
2121
  return isJsonTree(value) && isRecord(value) && exact(
2112
2122
  value,
2113
2123
  ["projectId", "revision", "artifacts", "globalKeywords", "remoteKeywords"],
2114
2124
  ["configuration"]
2115
- ) && (value.configuration === void 0 || isPublishedGeneratorConfiguration(value.configuration)) && string(value.projectId) && string(value.revision) && Array.isArray(value.artifacts) && value.artifacts.every(isPageArtifact) && Array.isArray(value.globalKeywords) && value.globalKeywords.every(isKeyword) && Array.isArray(value.remoteKeywords) && value.remoteKeywords.every(isRemoteKeywords);
2125
+ ) && (value["configuration"] === void 0 || isPublishedGeneratorConfiguration(value["configuration"])) && string(value["projectId"]) && string(value["revision"]) && Array.isArray(value["artifacts"]) && value["artifacts"].every(isPageArtifact) && Array.isArray(value["globalKeywords"]) && value["globalKeywords"].every(isKeyword) && Array.isArray(value["remoteKeywords"]) && value["remoteKeywords"].every(isRemoteKeywords);
2116
2126
  }
2117
2127
  function isRemoteKeywords(remote) {
2118
- return isRecord(remote) && exact(remote, ["loaderId", "digest", "keywords"], ["validator"]) && string(remote.loaderId) && string(remote.digest) && Array.isArray(remote.keywords) && remote.keywords.every(isKeyword) && (remote.validator === void 0 || string(remote.validator));
2128
+ return isRecord(remote) && exact(remote, ["loaderId", "digest", "keywords"], ["validator"]) && string(remote["loaderId"]) && string(remote["digest"]) && Array.isArray(remote["keywords"]) && remote["keywords"].every(isKeyword) && (remote["validator"] === void 0 || string(remote["validator"]));
2119
2129
  }
2120
2130
  function isSnapshotEnvelope(value) {
2121
2131
  if (!isRecord(value) || Object.getPrototypeOf(value) !== Object.prototype) return false;
@@ -2126,7 +2136,7 @@ function isSnapshotEnvelope(value) {
2126
2136
  return descriptor?.enumerable === true && "value" in descriptor;
2127
2137
  }))
2128
2138
  return false;
2129
- const artifacts = value.artifacts;
2139
+ const artifacts = value["artifacts"];
2130
2140
  if (!Array.isArray(artifacts) || Object.getPrototypeOf(artifacts) !== Array.prototype || Reflect.ownKeys(artifacts).length !== artifacts.length + 1)
2131
2141
  return false;
2132
2142
  for (let index = 0; index < artifacts.length; index += 1)
@@ -2135,14 +2145,14 @@ function isSnapshotEnvelope(value) {
2135
2145
  value,
2136
2146
  ["projectId", "revision", "artifacts", "globalKeywords", "remoteKeywords"],
2137
2147
  ["configuration"]
2138
- ) && (value.configuration === void 0 || isPublishedGeneratorConfiguration(value.configuration)) && string(value.projectId) && string(value.revision) && isJsonTree(value.globalKeywords) && Array.isArray(value.globalKeywords) && value.globalKeywords.every(isKeyword) && isJsonTree(value.remoteKeywords) && Array.isArray(value.remoteKeywords) && value.remoteKeywords.every(isRemoteKeywords);
2148
+ ) && (value["configuration"] === void 0 || isPublishedGeneratorConfiguration(value["configuration"])) && string(value["projectId"]) && string(value["revision"]) && isJsonTree(value["globalKeywords"]) && Array.isArray(value["globalKeywords"]) && value["globalKeywords"].every(isKeyword) && isJsonTree(value["remoteKeywords"]) && Array.isArray(value["remoteKeywords"]) && value["remoteKeywords"].every(isRemoteKeywords);
2139
2149
  }
2140
2150
  function isOutputManifest(value) {
2141
- return isJsonTree(value) && isRecord(value) && exact(value, ["schemaVersion", "projectId", "generation", "revision", "files"]) && value.schemaVersion === OUTPUT_SCHEMA_VERSION && string(value.projectId) && finite(value.generation) && string(value.revision) && Array.isArray(value.files) && value.files.every(
2142
- (file) => isRecord(file) && exact(file, ["path", "ownerId", "digest", "role"]) && string(file.path) && string(file.ownerId) && string(file.digest) && safeRelative(file.path) && ["angular", "content", "asset", "routes", "context", "search", "api-list"].includes(
2143
- String(file.role)
2151
+ return isJsonTree(value) && isRecord(value) && exact(value, ["schemaVersion", "projectId", "generation", "revision", "files"]) && value["schemaVersion"] === OUTPUT_SCHEMA_VERSION && string(value["projectId"]) && finite(value["generation"]) && string(value["revision"]) && Array.isArray(value["files"]) && value["files"].every(
2152
+ (file) => isRecord(file) && exact(file, ["path", "ownerId", "digest", "role"]) && string(file["path"]) && string(file["ownerId"]) && string(file["digest"]) && safeRelative(file["path"]) && ["angular", "content", "asset", "routes", "context", "search", "api-list"].includes(
2153
+ String(file["role"])
2144
2154
  )
2145
- ) && new Set(value.files.map((file) => file.path)).size === value.files.length;
2155
+ ) && new Set(value["files"].map((file) => file.path)).size === value["files"].length;
2146
2156
  }
2147
2157
  function safeRelative(value) {
2148
2158
  try {
@@ -2169,12 +2179,17 @@ var INCREMENTAL_PROGRAM_FLAG = "NGDOC_INCREMENTAL_PROGRAM";
2169
2179
  var SHAPE_CLOSURE_FLAG = "NGDOC_SHAPE_CLOSURE";
2170
2180
  var ANGULAR_SHARED_PASS_FLAG = "NGDOC_ANGULAR_SHARED_PASS";
2171
2181
  var ANGULAR_STRUCTURAL_PASS_FLAG = "NGDOC_ANGULAR_STRUCTURAL_PASS";
2182
+ var ANGULAR_DESCRIPTION_PASS_FLAG = "NGDOC_ANGULAR_DESCRIPTION_PASS";
2183
+ var VITE_CONTENT_UPDATE_FLAG = "NGDOC_VITE_CONTENT_UPDATE";
2172
2184
  var VITE_BUILD_HANDOFF_FLAG = "NGDOC_VITE_BUILD_HANDOFF";
2185
+ var VITE_MODULELESS_SETTLE_FLAG = "NGDOC_VITE_MODULELESS_SETTLE";
2173
2186
  var TRACKED_PROGRAM_REUSE_FLAG = "NGDOC_TRACKED_PROGRAM_REUSE";
2174
2187
  var FAST_START_FLAG = "NGDOC_FAST_START";
2175
2188
  var PARALLEL_WRITES_FLAG = "NGDOC_PARALLEL_WRITES";
2176
2189
  var HIGHLIGHT_CACHE_FLAG = "NGDOC_HIGHLIGHT_CACHE";
2190
+ var FORMAT_CACHE_FLAG = "NGDOC_FORMAT_CACHE";
2177
2191
  var PARALLEL_RENDER_FLAG = "NGDOC_PARALLEL_RENDER";
2192
+ var USED_GRAMMARS_FLAG = "NGDOC_USED_GRAMMARS";
2178
2193
  var FLAGS = Object.freeze(
2179
2194
  [
2180
2195
  {
@@ -2242,6 +2257,21 @@ var FLAGS = Object.freeze(
2242
2257
  description: "In the Vite host, the generated modules and description modules a generation creates or deletes join that generation's Angular compiler pass instead of starting a pass of their own.",
2243
2258
  verify: false
2244
2259
  },
2260
+ {
2261
+ name: ANGULAR_DESCRIPTION_PASS_FLAG,
2262
+ description: "In the Vite host, an edited description module joins the Angular compiler pass of the generation it starts, when that generation rewrites generated TypeScript modules, instead of taking a pass of its own first.",
2263
+ verify: false
2264
+ },
2265
+ {
2266
+ name: VITE_CONTENT_UPDATE_FLAG,
2267
+ description: "In the Vite host, a generation that changes only content modules and the search index updates the open pages in place instead of reloading them.",
2268
+ verify: false
2269
+ },
2270
+ {
2271
+ name: VITE_MODULELESS_SETTLE_FLAG,
2272
+ description: "In the Vite host, a hot update that hands Vite no module to update settles once it is acknowledged, instead of waiting for the newest generation to be published.",
2273
+ verify: false
2274
+ },
2245
2275
  {
2246
2276
  name: VITE_BUILD_HANDOFF_FLAG,
2247
2277
  description: "The server build of the Vite production pipeline publishes the browser build's generation instead of generating again.",
@@ -2252,6 +2282,16 @@ var FLAGS = Object.freeze(
2252
2282
  description: "Code blocks are highlighted with one Shiki highlighter per runtime and cached by their text, language, meta, themes and Shiki version; development keeps the cache beside the artifact cache.",
2253
2283
  verify: true
2254
2284
  },
2285
+ {
2286
+ name: FORMAT_CACHE_FLAG,
2287
+ description: "The Prettier configuration is resolved once per generation, and formatted signatures and snippets are cached by their code, parser, configuration and Prettier version; development keeps the cache beside the artifact cache.",
2288
+ verify: true
2289
+ },
2290
+ {
2291
+ name: USED_GRAMMARS_FLAG,
2292
+ description: "Code blocks are highlighted by a highlighter that loads only the Shiki grammars their languages reach, instead of every bundled grammar; the HTML is the same.",
2293
+ verify: false
2294
+ },
2255
2295
  {
2256
2296
  name: PARALLEL_RENDER_FLAG,
2257
2297
  description: "A large generation runs the HTML processing of its pages (highlighting, anchors, keyword links, search records) on up to four worker threads, while the main thread prepares the next pages in order.",
@@ -2333,11 +2373,11 @@ async function changedInputs(inputs, ownedRoots) {
2333
2373
  const refresher = createDependencyRefresher();
2334
2374
  const changes = /* @__PURE__ */ new Map();
2335
2375
  const add = (kind, file) => {
2336
- const path24 = normalize(file);
2337
- if (owned(path24, roots)) return;
2338
- const existing = changes.get(path24);
2376
+ const path26 = normalize(file);
2377
+ if (owned(path26, roots)) return;
2378
+ const existing = changes.get(path26);
2339
2379
  if (!existing || CHANGE_RANK[kind] > CHANGE_RANK[existing.kind])
2340
- changes.set(path24, { kind, path: path24 });
2380
+ changes.set(path26, { kind, path: path26 });
2341
2381
  };
2342
2382
  for (let offset = 0; offset < inputs.length; offset += CONCURRENCY) {
2343
2383
  await Promise.all(
@@ -2835,29 +2875,29 @@ function projectWatchInputs(dependencies) {
2835
2875
  const scopes = /* @__PURE__ */ new Map();
2836
2876
  const references = [];
2837
2877
  for (const dependency of dependencies) {
2838
- if (!isRecord2(dependency) || typeof dependency.kind !== "string")
2878
+ if (!isRecord2(dependency) || typeof dependency["kind"] !== "string")
2839
2879
  throw new Error("Invalid dependency");
2840
- switch (dependency.kind) {
2880
+ switch (dependency["kind"]) {
2841
2881
  case "content": {
2842
2882
  if (!hasExactKeys(dependency, ["digest", "kind", "path"]))
2843
2883
  throw new Error("Invalid content dependency");
2844
- files.add(path5(dependency.path, "content path"));
2845
- string2(dependency.digest, "content digest");
2884
+ files.add(path5(dependency["path"], "content path"));
2885
+ string2(dependency["digest"], "content digest");
2846
2886
  break;
2847
2887
  }
2848
2888
  case "existence": {
2849
- if (!hasExactKeys(dependency, ["exists", "kind", "path"]) || typeof dependency.exists !== "boolean")
2889
+ if (!hasExactKeys(dependency, ["exists", "kind", "path"]) || typeof dependency["exists"] !== "boolean")
2850
2890
  throw new Error("Invalid existence dependency");
2851
- files.add(path5(dependency.path, "existence path"));
2891
+ files.add(path5(dependency["path"], "existence path"));
2852
2892
  break;
2853
2893
  }
2854
2894
  case "glob": {
2855
2895
  if (!hasExactKeys(dependency, ["exclude", "include", "kind", "members", "root"]))
2856
2896
  throw new Error("Invalid glob dependency");
2857
- const root = path5(dependency.root, "glob root");
2858
- const include = strings(dependency.include, "glob include");
2859
- const exclude = strings(dependency.exclude, "glob exclude");
2860
- paths(dependency.members, "glob members").forEach((member) => files.add(member));
2897
+ const root = path5(dependency["root"], "glob root");
2898
+ const include = strings(dependency["include"], "glob include");
2899
+ const exclude = strings(dependency["exclude"], "glob exclude");
2900
+ paths(dependency["members"], "glob members").forEach((member) => files.add(member));
2861
2901
  const glob2 = { root, include, exclude };
2862
2902
  globs.set(JSON.stringify(glob2), glob2);
2863
2903
  break;
@@ -2865,10 +2905,10 @@ function projectWatchInputs(dependencies) {
2865
2905
  case "semantic": {
2866
2906
  if (!hasExactKeys(dependency, ["digest", "files", "kind", "reason", "scopeId"]))
2867
2907
  throw new Error("Invalid semantic dependency");
2868
- const scopeId = string2(dependency.scopeId, "semantic scope ID");
2869
- const digest = string2(dependency.digest, "semantic digest");
2870
- string2(dependency.reason, "semantic reason");
2871
- const scopeFiles = paths(dependency.files, "semantic files");
2908
+ const scopeId = string2(dependency["scopeId"], "semantic scope ID");
2909
+ const digest = string2(dependency["digest"], "semantic digest");
2910
+ string2(dependency["reason"], "semantic reason");
2911
+ const scopeFiles = paths(dependency["files"], "semantic files");
2872
2912
  scopeFiles.forEach((file) => files.add(file));
2873
2913
  const registered = scopes.get(scopeId) ?? [];
2874
2914
  registered.push({ digest });
@@ -2879,17 +2919,17 @@ function projectWatchInputs(dependencies) {
2879
2919
  if (!hasExactKeys(dependency, ["digest", "kind", "reason", "scopeId"]))
2880
2920
  throw new Error("Invalid semantic-reference dependency");
2881
2921
  references.push({
2882
- scopeId: string2(dependency.scopeId, "semantic-reference scope ID"),
2883
- digest: string2(dependency.digest, "semantic-reference digest")
2922
+ scopeId: string2(dependency["scopeId"], "semantic-reference scope ID"),
2923
+ digest: string2(dependency["digest"], "semantic-reference digest")
2884
2924
  });
2885
- string2(dependency.reason, "semantic-reference reason");
2925
+ string2(dependency["reason"], "semantic-reference reason");
2886
2926
  break;
2887
2927
  }
2888
2928
  case "keyword": {
2889
2929
  if (!hasExactKeys(dependency, ["digest", "key", "kind"]))
2890
2930
  throw new Error("Invalid keyword dependency");
2891
- string2(dependency.key, "keyword key");
2892
- string2(dependency.digest, "keyword digest");
2931
+ string2(dependency["key"], "keyword key");
2932
+ string2(dependency["digest"], "keyword digest");
2893
2933
  break;
2894
2934
  }
2895
2935
  // The non-physical kinds are validated but are never watch inputs: they name no path, and
@@ -2897,16 +2937,16 @@ function projectWatchInputs(dependencies) {
2897
2937
  case "semantic-closure": {
2898
2938
  if (!hasExactKeys(dependency, ["digest", "key", "kind", "scopeId"]))
2899
2939
  throw new Error("Invalid semantic-closure dependency");
2900
- string2(dependency.scopeId, "semantic-closure scope ID");
2901
- string2(dependency.key, "semantic-closure key");
2902
- string2(dependency.digest, "semantic-closure digest");
2940
+ string2(dependency["scopeId"], "semantic-closure scope ID");
2941
+ string2(dependency["key"], "semantic-closure key");
2942
+ string2(dependency["digest"], "semantic-closure digest");
2903
2943
  break;
2904
2944
  }
2905
2945
  case "evaluated": {
2906
2946
  if (!hasExactKeys(dependency, ["digest", "entryId", "kind"]))
2907
2947
  throw new Error("Invalid evaluated dependency");
2908
- string2(dependency.entryId, "evaluated entry ID");
2909
- string2(dependency.digest, "evaluated digest");
2948
+ string2(dependency["entryId"], "evaluated entry ID");
2949
+ string2(dependency["digest"], "evaluated digest");
2910
2950
  break;
2911
2951
  }
2912
2952
  default:
@@ -3270,7 +3310,7 @@ var GeneratorBuildSession = class {
3270
3310
  const pending = state.pending;
3271
3311
  if (queued) {
3272
3312
  const changes = new Map(queued.changes.map((change) => [change.path, change]));
3273
- pending.changes.forEach((change, path24) => changes.set(path24, change));
3313
+ pending.changes.forEach((change, path26) => changes.set(path26, change));
3274
3314
  queued.changes = [...changes.values()];
3275
3315
  queued.origin = queued.changes.length ? "filesystem" : "reconcile";
3276
3316
  queued.waiters.push(...pending.waiters);
@@ -4911,7 +4951,7 @@ function createCandidateOutputCommitter(options = {}, host = {}) {
4911
4951
 
4912
4952
  // libs/builder/generator/bootstrap/constants.ts
4913
4953
  var GENERATOR_COMPILER_VERSION = "0.0.1";
4914
- var GENERATOR_TOOLCHAIN_DIGEST = "a837a4b43736f6b95b5565f442aa62d2f2175fb549d7a44138d2955d465c9ac3";
4954
+ var GENERATOR_TOOLCHAIN_DIGEST = "495b74972f5c2d99ad311bd24b03e2560f77343e401abd7c4d8185e4fee96764";
4915
4955
 
4916
4956
  // libs/builder/generator/bootstrap/index.ts
4917
4957
  var PERSISTENT_WORKER_ENV = PERSISTENT_WORKER_FLAG;
@@ -5062,6 +5102,9 @@ var COMPILER_TYPESCRIPT = /\.[cm]?ts(?![a-z])/;
5062
5102
  var JAVASCRIPT_MODULE = /\.[cm]?jsx?$/;
5063
5103
  var COMPILER_RESOURCE = /\.(?:html?|css|less|sass|scss)$/;
5064
5104
  var HOST_COMPLETION_TIMEOUT_MS = 3e4;
5105
+ var HOST_BURST_EVENTS = 1e3;
5106
+ var HOST_BURST_WINDOW_MS = 1e3;
5107
+ var HOST_BURST_QUIET_MS = 2e3;
5065
5108
  function deferred2(resolved = false) {
5066
5109
  let settled = false;
5067
5110
  let settle = () => {
@@ -5130,9 +5173,17 @@ function observePhysicalVersion(file) {
5130
5173
  }
5131
5174
  }
5132
5175
  var HostUpdateCoordinator = class {
5133
- constructor(notify, fail) {
5176
+ /**
5177
+ * @param notify Announces the newest settled publication to the browser. `changes` lists the
5178
+ * generated files that differ from the last announcement, when nothing else asks for a full
5179
+ * reload; without it, or when the host cannot apply the changes in place, the page reloads.
5180
+ * @param fail Reports a host error.
5181
+ * @param recovery What the host does after a burst.
5182
+ */
5183
+ constructor(notify, fail, recovery) {
5134
5184
  this.notify = notify;
5135
5185
  this.fail = fail;
5186
+ this.recovery = recovery;
5136
5187
  this.attempts = /* @__PURE__ */ new Map();
5137
5188
  this.pendingSources = /* @__PURE__ */ new Map();
5138
5189
  this.claimedSources = /* @__PURE__ */ new Map();
@@ -5147,6 +5198,15 @@ var HostUpdateCoordinator = class {
5147
5198
  this.hostDrain = deferred2(true);
5148
5199
  this.sequence = 0;
5149
5200
  this.disposed = false;
5201
+ /** The start and the hot updates of the current burst-detection window. */
5202
+ this.window = { start: 0, events: 0 };
5203
+ /** Whether a module-less host update settles without waiting for the current publication. */
5204
+ this.modulelessSettle = !flagOff(VITE_MODULELESS_SETTLE_FLAG);
5205
+ /**
5206
+ * Whether the pending reload must reload the page whatever its outputs: an Angular source was
5207
+ * compiled for it, or it repairs an earlier failure (the browser shows the error overlay).
5208
+ */
5209
+ this.pendingFull = false;
5150
5210
  }
5151
5211
  seed(outputRoot, manifest) {
5152
5212
  this.outputRoot = normalize3(outputRoot);
@@ -5239,6 +5299,7 @@ var HostUpdateCoordinator = class {
5239
5299
  }
5240
5300
  const filePath = normalize3(file);
5241
5301
  const generated = !!this.outputRoot && relativeWithin(this.outputRoot, filePath) !== void 0;
5302
+ if (this.outputRoot && !generated) this.hotUpdate();
5242
5303
  const token = {
5243
5304
  id: ++this.sequence,
5244
5305
  path: filePath,
@@ -5320,7 +5381,8 @@ var HostUpdateCoordinator = class {
5320
5381
  * generated output that a generation claimed. Its host hooks settle only once the newest
5321
5382
  * generation is published and every host obligation is drained, and by then `flush()` has sent
5322
5383
  * that generation's reload (or its outputs equal the last announced ones). A native reload for
5323
- * the same output would be a second one.
5384
+ * the same output would be a second one. A module-less one settles earlier (see
5385
+ * {@link settle}), but Vite has nothing of it to reload either way.
5324
5386
  */
5325
5387
  announces(read) {
5326
5388
  const token = this.tokensByEvent.get(read);
@@ -5345,6 +5407,25 @@ var HostUpdateCoordinator = class {
5345
5407
  )
5346
5408
  ).map((expected) => expected.path).sort();
5347
5409
  }
5410
+ /**
5411
+ * Whether the generation that claimed this TypeScript source update committed and still has a
5412
+ * generated TypeScript module update whose Angular pass has not completed: that pass is still to
5413
+ * come, and the source can join it (`ngDocCompanionFiles`) instead of taking a pass of its own.
5414
+ * False for anything else: a generated output, a source no generation claimed, a generation
5415
+ * that failed, was cancelled or rewrote no TypeScript module, or whose passes all completed.
5416
+ * Call it after `committed(ticket)`, which waits for the generation's result.
5417
+ * @param ticket The source's hot update ticket.
5418
+ */
5419
+ generationPass(ticket) {
5420
+ const token = ticket.token;
5421
+ const attempt = token?.attempt;
5422
+ if (this.disposed || !token || token.generated || !token.isClaimed || token.sourceKind !== "typescript" || token.type !== "update" || !attempt?.resultSeen || attempt.resultStatus !== "success") {
5423
+ return false;
5424
+ }
5425
+ return [...attempt.expected.values()].some(
5426
+ (expected) => expected.kind === "update" && !expected.completed && COMPILER_TYPESCRIPT.test(expected.path) && !JAVASCRIPT_MODULE.test(expected.path)
5427
+ );
5428
+ }
5348
5429
  /**
5349
5430
  * Whether `file` is a generated output: a path below the output root of the published
5350
5431
  * generation. Nothing is generated before the first publication or after disposal.
@@ -5386,10 +5467,28 @@ var HostUpdateCoordinator = class {
5386
5467
  this.diagnosticChanged = deferred2();
5387
5468
  }
5388
5469
  }
5389
- /** Waits for the current accepted publication and all peer host acknowledgments. */
5390
- async settle(ticket) {
5470
+ /**
5471
+ * Waits for the current accepted publication and all peer host acknowledgments.
5472
+ *
5473
+ * A module-less hot update does not wait: once its hooks return, Vite has no module to update
5474
+ * and sends nothing, and its acknowledgment has already settled what it obliged (the reload,
5475
+ * sent by `flush()` once every obligation is settled, is this coordinator's own). Waiting made
5476
+ * it follow every generation that started meanwhile, so under sustained churn it settled only
5477
+ * when the churn stopped, and each wait for the host obligations to drain could time out
5478
+ * (`NGDOC_VITE_HOST_TIMEOUT`) while every single obligation was completed in time. Nothing
5479
+ * else changes: obligations, the reload and what is published are the same, so the switch off
5480
+ * (`NGDOC_VITE_MODULELESS_SETTLE=0`) only makes it wait as before.
5481
+ * @param ticket The hot update's ticket, after its acknowledgment.
5482
+ * @param modules Whether its hooks hand Vite any module to update (or a page reload Vite sends
5483
+ * itself, for an HTML file without modules). Only the caller, which sees Vite's context, knows.
5484
+ */
5485
+ async settle(ticket, modules = true) {
5391
5486
  const token = ticket.token;
5392
5487
  if (!token || this.disposed) return;
5488
+ if (!modules && this.modulelessSettle) {
5489
+ this.settleModuleless(token);
5490
+ return;
5491
+ }
5393
5492
  if (!token.generated && token.isClaimed && token.sourceKind === "typescript" && token.type === "update") {
5394
5493
  await this.awaitWithoutDiagnostic(token.attempt.publication.promise);
5395
5494
  if (token.attempt.publicationStatus === "skipped" || token.attempt.publicationStatus === "cancelled") {
@@ -5405,9 +5504,14 @@ var HostUpdateCoordinator = class {
5405
5504
  if (!token?.attempt || this.disposed) return;
5406
5505
  await token.attempt.result.promise;
5407
5506
  }
5408
- async complete(ticket) {
5507
+ /**
5508
+ * Acknowledges a hot update that takes no compiler pass, then settles it.
5509
+ * @param ticket The hot update's ticket.
5510
+ * @param modules See {@link settle}.
5511
+ */
5512
+ async complete(ticket, modules = true) {
5409
5513
  await this.acknowledge(ticket);
5410
- await this.settle(ticket);
5514
+ await this.settle(ticket, modules);
5411
5515
  }
5412
5516
  published(result, recovered) {
5413
5517
  const attempt = this.attempts.get(result.generation);
@@ -5419,6 +5523,7 @@ var HostUpdateCoordinator = class {
5419
5523
  this.publishedGeneration = result.generation;
5420
5524
  this.publication(result.generation, "success");
5421
5525
  if (recovered || delta.changed || compilerSource || this.pendingGeneration !== void 0) {
5526
+ if (recovered || compilerSource) this.pendingFull = true;
5422
5527
  this.pendingGeneration = result.generation;
5423
5528
  this.pendingManifest = structuredClone(result.manifest);
5424
5529
  this.flush();
@@ -5427,8 +5532,11 @@ var HostUpdateCoordinator = class {
5427
5532
  }
5428
5533
  dispose() {
5429
5534
  this.disposed = true;
5535
+ if (this.burst?.timer) clearTimeout(this.burst.timer);
5536
+ this.burst = void 0;
5430
5537
  this.pendingGeneration = void 0;
5431
5538
  this.pendingManifest = void 0;
5539
+ this.pendingFull = false;
5432
5540
  for (const attempt of this.attempts.values()) {
5433
5541
  attempt.result.resolve();
5434
5542
  attempt.shapes.resolve();
@@ -5622,8 +5730,10 @@ var HostUpdateCoordinator = class {
5622
5730
  return;
5623
5731
  if (this.latestAttempt && this.pendingGeneration < this.latestAttempt.generation) return;
5624
5732
  this.pendingGeneration = void 0;
5733
+ const changes = this.pendingFull || !this.pendingManifest || !this.outputRoot ? void 0 : this.changes(this.outputRoot, this.announcedManifest, this.pendingManifest);
5734
+ this.pendingFull = false;
5625
5735
  try {
5626
- this.notify();
5736
+ this.notify(changes?.length ? changes : void 0);
5627
5737
  if (this.pendingManifest) this.announcedManifest = this.pendingManifest;
5628
5738
  this.pendingManifest = void 0;
5629
5739
  } catch (error2) {
@@ -5631,10 +5741,29 @@ var HostUpdateCoordinator = class {
5631
5741
  }
5632
5742
  }
5633
5743
  queueCurrentReload() {
5744
+ this.pendingFull = true;
5634
5745
  this.pendingGeneration = this.publishedGeneration ?? this.manifest?.generation ?? this.pendingGeneration ?? 0;
5635
5746
  this.pendingManifest = structuredClone(this.manifest ?? this.announcedManifest);
5636
5747
  this.flush();
5637
5748
  }
5749
+ /**
5750
+ * Settles a module-less hot update without waiting (see {@link settle}).
5751
+ * @param token The acknowledged hot update.
5752
+ */
5753
+ settleModuleless(token) {
5754
+ const failure3 = this.diagnosticError();
5755
+ if (failure3) throw failure3;
5756
+ const attempt = token.attempt;
5757
+ if (!attempt || token.generated || !token.isClaimed || token.sourceKind !== "typescript" || token.type !== "update") {
5758
+ return;
5759
+ }
5760
+ void attempt.publication.promise.then(() => {
5761
+ if (this.disposed || this.diagnosticError()) return;
5762
+ if (attempt.publicationStatus === "skipped" || attempt.publicationStatus === "cancelled") {
5763
+ this.queueCurrentReload();
5764
+ }
5765
+ });
5766
+ }
5638
5767
  async awaitCurrentPublication(initial) {
5639
5768
  if (this.diagnosticError()) throw this.diagnosticError();
5640
5769
  let attempt = initial ?? this.latestAttempt;
@@ -5691,6 +5820,28 @@ var HostUpdateCoordinator = class {
5691
5820
  }
5692
5821
  return { changed };
5693
5822
  }
5823
+ /**
5824
+ * The generated files that differ between two manifests, in path order.
5825
+ * @param previousManifest The outputs the browser was last told about.
5826
+ * @param nextManifest The outputs announced now.
5827
+ */
5828
+ changes(root, previousManifest, nextManifest) {
5829
+ const previous = manifestMap(previousManifest);
5830
+ const next2 = manifestMap(nextManifest);
5831
+ const changes = [];
5832
+ for (const relative of [.../* @__PURE__ */ new Set([...previous.keys(), ...next2.keys()])].sort()) {
5833
+ const before = previous.get(relative);
5834
+ const after = next2.get(relative);
5835
+ if (sameFile(before, after)) continue;
5836
+ changes.push({
5837
+ path: relative,
5838
+ file: normalize3(path8.join(root, relative)),
5839
+ ...before ? { before: { digest: before.digest, role: before.role } } : {},
5840
+ ...after ? { after: { digest: after.digest, role: after.role } } : {}
5841
+ });
5842
+ }
5843
+ return changes;
5844
+ }
5694
5845
  prune(generation) {
5695
5846
  for (const [candidate, attempt] of this.attempts) {
5696
5847
  if (candidate < generation && ![...attempt.expected.values()].some((item) => !item.completed)) {
@@ -5713,13 +5864,109 @@ var HostUpdateCoordinator = class {
5713
5864
  })
5714
5865
  ]).catch((error2) => {
5715
5866
  const failure3 = error2 instanceof Error ? error2 : new Error(String(error2));
5716
- this.fail(failure3);
5867
+ this.timedOut(failure3);
5717
5868
  throw failure3;
5718
5869
  });
5719
5870
  } finally {
5720
5871
  if (timeout) clearTimeout(timeout);
5721
5872
  }
5722
5873
  }
5874
+ /** Counts a hot update towards a burst of file events (see the class description). */
5875
+ hotUpdate() {
5876
+ const now = Date.now();
5877
+ if (this.burst) {
5878
+ this.burst.last = now;
5879
+ this.burst.events = true;
5880
+ return;
5881
+ }
5882
+ if (now - this.window.start >= HOST_BURST_WINDOW_MS) this.window = { start: now, events: 0 };
5883
+ if (++this.window.events >= HOST_BURST_EVENTS) this.openBurst(now).events = true;
5884
+ }
5885
+ /**
5886
+ * A host update did not settle in time. The first of a burst is reported as it happens (as
5887
+ * every timeout was before); the others only count towards the burst's summary, so a flood of
5888
+ * file events logs two lines instead of one per event.
5889
+ * @param failure The timeout, thrown to its waiter as well.
5890
+ */
5891
+ timedOut(failure3) {
5892
+ const burst = this.burst ?? this.openBurst(Date.now());
5893
+ burst.last = Date.now();
5894
+ if (burst.timeouts++ === 0) this.fail(failure3);
5895
+ }
5896
+ openBurst(now) {
5897
+ const burst = { events: false, timeouts: 0, last: now };
5898
+ this.burst = burst;
5899
+ this.armBurst(burst, HOST_BURST_QUIET_MS);
5900
+ return burst;
5901
+ }
5902
+ /**
5903
+ * One timer per burst, re-armed for the rest of the quiet period when activity moved `last`.
5904
+ * @param burst The open burst.
5905
+ * @param delay When to check it next.
5906
+ */
5907
+ armBurst(burst, delay) {
5908
+ burst.timer = setTimeout(() => {
5909
+ if (this.burst !== burst || this.disposed) return;
5910
+ const quiet = Date.now() - burst.last;
5911
+ if (quiet < HOST_BURST_QUIET_MS) {
5912
+ this.armBurst(burst, HOST_BURST_QUIET_MS - quiet);
5913
+ return;
5914
+ }
5915
+ this.burst = void 0;
5916
+ this.window = { start: 0, events: 0 };
5917
+ void this.recover(burst);
5918
+ }, delay);
5919
+ burst.timer.unref?.();
5920
+ }
5921
+ /**
5922
+ * The pass after a burst: the summary of its held-back timeouts, then every open output
5923
+ * obligation whose file is still what its generation wrote is reported again, in path order,
5924
+ * and every recorded input is re-observed once no generation is in flight. An output a newer generation rewrote since is left
5925
+ * to that generation's own report, which supersedes the older expectation.
5926
+ * @param burst The burst that ended.
5927
+ */
5928
+ async recover(burst) {
5929
+ if (burst.timeouts > 1) {
5930
+ this.recovery?.warn(
5931
+ `[NGDOC_VITE_HOST_TIMEOUT] ${burst.timeouts - 1} more host update(s) did not settle within ${HOST_COMPLETION_TIMEOUT_MS / 1e3} s while files kept changing. NgDoc checks the generated files and the documentation inputs again.`
5932
+ );
5933
+ }
5934
+ const open2 = /* @__PURE__ */ new Map();
5935
+ for (const attempt of [...this.attempts.values()].sort(
5936
+ (left, right) => right.generation - left.generation
5937
+ )) {
5938
+ if (!attempt.resultSeen) continue;
5939
+ for (const expected of attempt.expected.values()) {
5940
+ if (!expected.completed && !open2.has(expected.path)) open2.set(expected.path, expected);
5941
+ }
5942
+ }
5943
+ for (const [file, expected] of [...open2].sort(([left], [right]) => left < right ? -1 : 1)) {
5944
+ let current;
5945
+ let written;
5946
+ try {
5947
+ [current, written] = await Promise.all([observePhysicalState(file), expected.state]);
5948
+ } catch {
5949
+ continue;
5950
+ }
5951
+ if (this.disposed) return;
5952
+ const unchanged = expected.digest === void 0 ? current.version === "missing" : current.digest === expected.digest && current.version === written.version;
5953
+ if (expected.completed || !unchanged) continue;
5954
+ try {
5955
+ this.recovery?.replay(
5956
+ file,
5957
+ expected.kind === "update" ? "update" : expected.digest === void 0 ? "delete" : "create"
5958
+ );
5959
+ } catch {
5960
+ }
5961
+ }
5962
+ for (let latest = this.latestAttempt; latest && !latest.resultSeen && !this.disposed; latest = this.latestAttempt)
5963
+ await latest.result.promise;
5964
+ if (this.disposed) return;
5965
+ try {
5966
+ this.recovery?.rescan();
5967
+ } catch {
5968
+ }
5969
+ }
5723
5970
  };
5724
5971
 
5725
5972
  // libs/builder/generator/vite/angular-composition.ts
@@ -5746,10 +5993,23 @@ function globalStyleSheet(context) {
5746
5993
  };
5747
5994
  return context.modules.every(global) && virtual;
5748
5995
  }
5996
+ function handsViteModules(file, modules) {
5997
+ return modules.length > 0 || file.endsWith(".html");
5998
+ }
5999
+ function handedModules(context, result) {
6000
+ return Array.isArray(result) ? result : context.modules;
6001
+ }
5749
6002
  function analogHostHooks(compiler) {
5750
6003
  const hooks = compiler?.api?.ngDocHost;
5751
6004
  return hooks && typeof hooks === "object" && typeof hooks.passes === "number" ? hooks : void 0;
5752
6005
  }
6006
+ function released() {
6007
+ let resolve;
6008
+ const promise = new Promise((done) => {
6009
+ resolve = done;
6010
+ });
6011
+ return { promise, resolve };
6012
+ }
5753
6013
  function error(code, message4, cause) {
5754
6014
  return Object.assign(
5755
6015
  new Error(`[${code}] ${message4}`, cause === void 0 ? void 0 : { cause }),
@@ -5831,10 +6091,10 @@ var CompilerAdmission = class {
5831
6091
  this.drained = new Promise((resolve) => {
5832
6092
  this.drainedResolve = resolve;
5833
6093
  });
5834
- let released = false;
6094
+ let released2 = false;
5835
6095
  waiter.resolve(() => {
5836
- if (released) return;
5837
- released = true;
6096
+ if (released2) return;
6097
+ released2 = true;
5838
6098
  this.locked = false;
5839
6099
  this.drainedResolve?.();
5840
6100
  this.drainedResolve = void 0;
@@ -5899,6 +6159,9 @@ function composeAngularPlugins(angularPlugins, componentProbe, bridge) {
5899
6159
  const sharedPasses = !flagOff(ANGULAR_SHARED_PASS_FLAG);
5900
6160
  const compiledStates = /* @__PURE__ */ new Map();
5901
6161
  const hooks = flagOff(ANGULAR_STRUCTURAL_PASS_FLAG) ? void 0 : analogHostHooks(compiler);
6162
+ const descriptionPasses = sharedPasses && !flagOff(ANGULAR_DESCRIPTION_PASS_FLAG);
6163
+ const waitingSources = /* @__PURE__ */ new Map();
6164
+ let admissionReleased = released();
5902
6165
  if (hooks)
5903
6166
  hooks.claimFilesystemChange = (file) => !disposed && bridge.claimsFilesystemChange?.(file) === true;
5904
6167
  const captureInitialCompilation = async () => {
@@ -6038,20 +6301,26 @@ function composeAngularPlugins(angularPlugins, componentProbe, bridge) {
6038
6301
  if (!compilerTrigger) {
6039
6302
  const result2 = await Reflect.apply(originalHot, this, [context]);
6040
6303
  await bridge.acknowledge(ticket, false);
6041
- await bridge.settle(ticket);
6304
+ await bridge.settle(ticket, handsViteModules(context.file, handedModules(context, result2)));
6042
6305
  return result2;
6043
6306
  }
6044
6307
  await preflight();
6045
6308
  const resource = COMPILER_RESOURCE2.test(context.file);
6046
6309
  if (resource) await bridge.committed(ticket);
6047
6310
  else await ticket.ready;
6311
+ if (!resource && descriptionPasses && bridge.descriptionModule?.(context.file)) {
6312
+ await joinGenerationPass(context, ticket);
6313
+ }
6048
6314
  const release = await admission.acquire();
6049
6315
  let result;
6050
6316
  try {
6051
6317
  if (!resource && (await coveredByInitialCompilation(context) || await coveredByCompanionPass(context))) {
6052
6318
  await bridge.acknowledge(ticket, false);
6053
6319
  } else {
6054
- const companions = sharedPasses && !resource ? await captureCompanions(bridge.companions?.(ticket) ?? []) : /* @__PURE__ */ new Map();
6320
+ const companions = sharedPasses && !resource ? await captureCompanions([
6321
+ ...bridge.companions?.(ticket) ?? [],
6322
+ ...takeWaitingSources(context.file)
6323
+ ]) : /* @__PURE__ */ new Map();
6055
6324
  const hotContext = companions.size > 0 ? { ...context, ngDocCompanionFiles: [...companions.keys()].map(analogFileId) } : context;
6056
6325
  const own = companions.size > 0 ? await observePhysicalState(path9.resolve(context.file)).catch(() => void 0) : void 0;
6057
6326
  const mark = companions.size > 0 ? bridge.diagnosticMark?.() : void 0;
@@ -6078,10 +6347,47 @@ function composeAngularPlugins(angularPlugins, componentProbe, bridge) {
6078
6347
  throw cause;
6079
6348
  } finally {
6080
6349
  release();
6350
+ const ended = admissionReleased;
6351
+ admissionReleased = released();
6352
+ ended.resolve();
6081
6353
  }
6082
- await bridge.settle(ticket);
6354
+ await bridge.settle(ticket, handsViteModules(context.file, handedModules(context, result)));
6083
6355
  return result;
6084
6356
  };
6357
+ const joinGenerationPass = async (context, ticket) => {
6358
+ await bridge.committed(ticket);
6359
+ if (disposed || !bridge.generationPass?.(ticket)) return;
6360
+ const file = path9.resolve(context.file);
6361
+ const entry = { taken: false };
6362
+ waitingSources.set(file, entry);
6363
+ const deadline = Date.now() + HOST_COMPLETION_TIMEOUT_MS;
6364
+ try {
6365
+ while (!entry.taken && !disposed && bridge.generationPass(ticket)) {
6366
+ const remaining = deadline - Date.now();
6367
+ if (remaining <= 0) return;
6368
+ let timer2;
6369
+ await Promise.race([
6370
+ admissionReleased.promise,
6371
+ new Promise((resolve) => {
6372
+ timer2 = setTimeout(resolve, remaining);
6373
+ timer2.unref?.();
6374
+ })
6375
+ ]);
6376
+ clearTimeout(timer2);
6377
+ }
6378
+ } finally {
6379
+ if (waitingSources.get(file) === entry) waitingSources.delete(file);
6380
+ }
6381
+ };
6382
+ const takeWaitingSources = (file) => {
6383
+ if (!bridge.generated?.(file)) return [];
6384
+ const taken = [];
6385
+ for (const [source, entry] of waitingSources) {
6386
+ entry.taken = true;
6387
+ taken.push(source);
6388
+ }
6389
+ return taken;
6390
+ };
6085
6391
  const wrappedCompiler = {
6086
6392
  ...compiler,
6087
6393
  buildStart: replaceHandler(
@@ -6335,6 +6641,115 @@ function sameRuntimeConfiguration(left, right) {
6335
6641
  return left.outputRoot === right.outputRoot && left.cacheRoot === right.cacheRoot && left.assetDirectory === right.assetDirectory;
6336
6642
  }
6337
6643
 
6644
+ // libs/builder/generator/vite/content-updates.ts
6645
+ import path11 from "node:path";
6646
+ var CONTENT_UPDATE_EVENT = "ng-doc:content-update";
6647
+ var CONTENT_CLIENT_EVENT = "ng-doc:content-client";
6648
+ var CONTENT_UPDATE_VERSION = 1;
6649
+ var CONTENT_UPDATE_CLIENT = `
6650
+ const hot = import.meta.hot;
6651
+ if (hot) {
6652
+ let queue = Promise.resolve();
6653
+ hot.on(${JSON.stringify(CONTENT_UPDATE_EVENT)}, (update) => {
6654
+ queue = queue.then(async () => {
6655
+ try {
6656
+ const payloads = await Promise.all(
6657
+ update.modules.map(async (url) => (await import(/* @vite-ignore */ url)).default),
6658
+ );
6659
+ const event = new CustomEvent(${JSON.stringify(CONTENT_UPDATE_EVENT)}, {
6660
+ cancelable: true,
6661
+ detail: { payloads, search: update.search },
6662
+ });
6663
+ if (window.dispatchEvent(event)) location.reload();
6664
+ } catch (error) {
6665
+ console.error('[ng-doc] The content update failed; reloading the page.', error);
6666
+ location.reload();
6667
+ }
6668
+ });
6669
+ });
6670
+ hot.send(${JSON.stringify(CONTENT_CLIENT_EVENT)}, { version: ${CONTENT_UPDATE_VERSION} });
6671
+ }
6672
+ `;
6673
+ var CLIENT_MARKER = "data-ng-doc-content-updates";
6674
+ function contentUpdateClientTags(html) {
6675
+ if (!contentUpdatesEnabled() || html.includes(CLIENT_MARKER)) return [];
6676
+ return [
6677
+ {
6678
+ tag: "script",
6679
+ attrs: { type: "module", [CLIENT_MARKER]: "" },
6680
+ children: CONTENT_UPDATE_CLIENT,
6681
+ injectTo: "head"
6682
+ }
6683
+ ];
6684
+ }
6685
+ function contentUpdatesEnabled() {
6686
+ return !flagOff(VITE_CONTENT_UPDATE_FLAG);
6687
+ }
6688
+ function contentOnly(changes, assetDirectory) {
6689
+ if (!changes?.length) return void 0;
6690
+ const keywords = path11.posix.join(assetDirectory.replace(/\\/g, "/"), "keywords.json");
6691
+ const modules = [];
6692
+ let search = false;
6693
+ for (const change of changes) {
6694
+ const { before, after } = change;
6695
+ if (!before || !after || before.role !== after.role) return void 0;
6696
+ if (after.role === "content" && change.path.endsWith(".content.mjs")) {
6697
+ modules.push(change.file);
6698
+ } else if (after.role === "search") {
6699
+ search = true;
6700
+ } else if (!(after.role === "asset" && change.path === keywords)) {
6701
+ return void 0;
6702
+ }
6703
+ }
6704
+ return { modules, search };
6705
+ }
6706
+ function sendContentUpdate(server, clients, changes, timestamp) {
6707
+ const connected = [...server.ws.clients ?? []];
6708
+ if (connected.length === 0) {
6709
+ server.ws.send({ type: "full-reload" });
6710
+ return;
6711
+ }
6712
+ const graph = server.environments["client"]?.moduleGraph;
6713
+ const urls = /* @__PURE__ */ new Set();
6714
+ for (const file of changes.modules) {
6715
+ for (const module of graph?.getModulesByFile(file) ?? []) urls.add(module.url);
6716
+ }
6717
+ const base = server.config.base.endsWith("/") ? server.config.base : `${server.config.base}/`;
6718
+ const message4 = {
6719
+ version: CONTENT_UPDATE_VERSION,
6720
+ modules: [...urls].sort().map(
6721
+ (url) => `${base}${url.replace(/^\//, "")}${url.includes("?") ? "&" : "?"}t=${timestamp}`
6722
+ ),
6723
+ search: changes.search
6724
+ };
6725
+ for (const client of connected) {
6726
+ if (clients.has(client)) client.send(CONTENT_UPDATE_EVENT, message4);
6727
+ else client.send({ type: "full-reload" });
6728
+ }
6729
+ }
6730
+
6731
+ // libs/builder/generator/vite/demo-application.ts
6732
+ import { readFile as readFile5 } from "node:fs/promises";
6733
+ import path12 from "node:path";
6734
+ var DEMO_APPLICATION_MODULE = "demo-app.ts";
6735
+ var DEMO_PATH = /^export const NG_DOC_DEMO_PATH = '([^'\\\n]*)';$/m;
6736
+ var DEMO_PAGES = /^export const NG_DOC_DEMO_PAGES: string\[\] = (.*);$/m;
6737
+ async function readDemoApplication(outputRoot) {
6738
+ const module = path12.join(outputRoot, DEMO_APPLICATION_MODULE);
6739
+ const source = await readFile5(module, "utf8").catch(() => void 0);
6740
+ const demoPath = source === void 0 ? void 0 : DEMO_PATH.exec(source)?.[1];
6741
+ const pages2 = source === void 0 ? void 0 : demoPages(DEMO_PAGES.exec(source)?.[1]);
6742
+ return demoPath && pages2 ? { module, path: demoPath, pages: pages2 } : void 0;
6743
+ }
6744
+ function demoPages(json) {
6745
+ try {
6746
+ const value = json === void 0 ? void 0 : JSON.parse(json);
6747
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
6748
+ } catch {
6749
+ return void 0;
6750
+ }
6751
+ }
6752
+
6338
6753
  // libs/builder/generator/vite/diagnostics.ts
6339
6754
  function diagnosticText(diagnostic4) {
6340
6755
  const source = diagnostic4.source ? ` (${diagnostic4.source.path}${diagnostic4.source.line ? `:${diagnostic4.source.line}` : ""})` : "";
@@ -6350,7 +6765,7 @@ function hostDiagnostic(code, message4) {
6350
6765
  }
6351
6766
 
6352
6767
  // libs/builder/generator/vite/lease.ts
6353
- import path11 from "node:path";
6768
+ import path13 from "node:path";
6354
6769
  var identities = /* @__PURE__ */ new Map();
6355
6770
  var outputRoots = /* @__PURE__ */ new Map();
6356
6771
  function acquireOutputLease(identity2, defaultOutputRoot) {
@@ -6358,7 +6773,7 @@ function acquireOutputLease(identity2, defaultOutputRoot) {
6358
6773
  throw new Error(`[NGDOC_VITE_OUTPUT_LEASE] Output owner is already active for ${identity2}.`);
6359
6774
  }
6360
6775
  const token = Symbol(identity2);
6361
- const initialRoot = canonicalDrive(path11.resolve(defaultOutputRoot).replace(/\\/g, "/"));
6776
+ const initialRoot = canonicalDrive(path13.resolve(defaultOutputRoot).replace(/\\/g, "/"));
6362
6777
  if (outputRoots.has(initialRoot)) {
6363
6778
  throw new Error(
6364
6779
  `[NGDOC_VITE_OUTPUT_LEASE] Default output root is already active: ${initialRoot}.`
@@ -6372,7 +6787,7 @@ function acquireOutputLease(identity2, defaultOutputRoot) {
6372
6787
  return {
6373
6788
  admit(value) {
6374
6789
  if (disposed) throw new Error("[NGDOC_VITE_OUTPUT_LEASE] Output lease is disposed.");
6375
- const normalized = canonicalDrive(path11.resolve(value).replace(/\\/g, "/"));
6790
+ const normalized = canonicalDrive(path13.resolve(value).replace(/\\/g, "/"));
6376
6791
  const owner = outputRoots.get(normalized);
6377
6792
  if (owner && owner !== token) {
6378
6793
  throw new Error(
@@ -6403,11 +6818,11 @@ function acquireOutputLease(identity2, defaultOutputRoot) {
6403
6818
 
6404
6819
  // libs/builder/generator/vite/lifecycle.ts
6405
6820
  import { existsSync as existsSync5 } from "node:fs";
6406
- import path16 from "node:path";
6821
+ import path18 from "node:path";
6407
6822
 
6408
6823
  // libs/builder/generator/vite/assets.ts
6409
- import { readFile as readFile5 } from "node:fs/promises";
6410
- import path12 from "node:path";
6824
+ import { readFile as readFile6 } from "node:fs/promises";
6825
+ import path14 from "node:path";
6411
6826
  var GENERATED_ASSET_PATH = "assets/ng-doc";
6412
6827
  var MIME = /* @__PURE__ */ new Map([
6413
6828
  [".css", "text/css; charset=utf-8"],
@@ -6429,7 +6844,7 @@ function normalizedBase(base) {
6429
6844
  return value.endsWith("/") ? value : `${value}/`;
6430
6845
  }
6431
6846
  function safeRelative2(value) {
6432
- return !!value && !value.includes("\0") && !value.includes("\\") && !path12.posix.isAbsolute(value) && !value.split("/").some((segment) => segment === ".." || segment === "");
6847
+ return !!value && !value.includes("\0") && !value.includes("\\") && !path14.posix.isAbsolute(value) && !value.split("/").some((segment) => segment === ".." || segment === "");
6433
6848
  }
6434
6849
  function inventory(configuration, manifest) {
6435
6850
  const prefix = `${configuration.assetDirectory.replace(/\\/g, "/").replace(/\/$/, "")}/`;
@@ -6446,7 +6861,7 @@ function inventory(configuration, manifest) {
6446
6861
  throw new Error(`[NGDOC_VITE_ASSET_COLLISION] Duplicate generated asset: ${relative}.`);
6447
6862
  }
6448
6863
  result.set(relative, {
6449
- file: path12.join(configuration.outputRoot, output),
6864
+ file: path14.join(configuration.outputRoot, output),
6450
6865
  outputName: outputName2
6451
6866
  });
6452
6867
  }
@@ -6485,12 +6900,12 @@ var GeneratedAssetInventory = class {
6485
6900
  next2();
6486
6901
  return;
6487
6902
  }
6488
- void readFile5(asset.file).then(
6903
+ void readFile6(asset.file).then(
6489
6904
  (source) => {
6490
6905
  response.statusCode = 200;
6491
6906
  response.setHeader(
6492
6907
  "Content-Type",
6493
- MIME.get(path12.extname(relative).toLowerCase()) ?? "application/octet-stream"
6908
+ MIME.get(path14.extname(relative).toLowerCase()) ?? "application/octet-stream"
6494
6909
  );
6495
6910
  response.end(source);
6496
6911
  },
@@ -6513,7 +6928,7 @@ var GeneratedAssetInventory = class {
6513
6928
  context.emitFile({
6514
6929
  type: "asset",
6515
6930
  fileName: asset.outputName,
6516
- source: await readFile5(asset.file)
6931
+ source: await readFile6(asset.file)
6517
6932
  });
6518
6933
  }
6519
6934
  }
@@ -6524,7 +6939,7 @@ var GeneratedAssetInventory = class {
6524
6939
 
6525
6940
  // libs/builder/generator/vite/vite-event-source.ts
6526
6941
  import { existsSync as existsSync4, realpathSync as realpathSync2, statSync as statSync6 } from "node:fs";
6527
- import path15 from "node:path";
6942
+ import path17 from "node:path";
6528
6943
 
6529
6944
  // libs/builder/generator/vite/external-probes.ts
6530
6945
  import { statSync as statSync5 } from "node:fs";
@@ -6605,7 +7020,7 @@ var ExternalProbeWatcher = class {
6605
7020
 
6606
7021
  // libs/builder/generator/vite/missing-paths.ts
6607
7022
  import { existsSync as existsSync3, watch as watchDirectory } from "node:fs";
6608
- import path13 from "node:path";
7023
+ import path15 from "node:path";
6609
7024
  var MAX_MISSING_PATH_DIRECTORIES = 1024;
6610
7025
  var defaultWatch = (directory, listener) => watchDirectory(
6611
7026
  directory,
@@ -6613,9 +7028,9 @@ var defaultWatch = (directory, listener) => watchDirectory(
6613
7028
  (event, name) => listener(event, typeof name === "string" ? name : null)
6614
7029
  );
6615
7030
  function anchorOf(target) {
6616
- let cursor = path13.dirname(target);
7031
+ let cursor = path15.dirname(target);
6617
7032
  while (!existsSync3(cursor)) {
6618
- const parent = path13.dirname(cursor);
7033
+ const parent = path15.dirname(cursor);
6619
7034
  if (parent === cursor) break;
6620
7035
  cursor = parent;
6621
7036
  }
@@ -6702,7 +7117,7 @@ var MissingPathWatcher = class {
6702
7117
  const members2 = [...this.members.get(anchor) ?? []];
6703
7118
  const gone = !existsSync3(anchor);
6704
7119
  for (const target of members2) {
6705
- const entry = path13.relative(anchor, target).split(path13.sep)[0];
7120
+ const entry = path15.relative(anchor, target).split(path15.sep)[0];
6706
7121
  if (!gone && name !== null && name !== entry) continue;
6707
7122
  this.refresh(target);
6708
7123
  }
@@ -6725,9 +7140,9 @@ var MissingPathWatcher = class {
6725
7140
  // libs/builder/generator/vite/watch-inputs.ts
6726
7141
  import { Minimatch } from "minimatch";
6727
7142
  import { access } from "node:fs/promises";
6728
- import path14 from "node:path";
7143
+ import path16 from "node:path";
6729
7144
  function normalize4(value) {
6730
- return path14.resolve(value).replace(/\\/g, "/");
7145
+ return path16.resolve(value).replace(/\\/g, "/");
6731
7146
  }
6732
7147
  function union(left, right) {
6733
7148
  const files = new Set([...left?.files ?? [], ...right?.files ?? []].map(normalize4));
@@ -6749,14 +7164,14 @@ function inputKey(inputs) {
6749
7164
  return JSON.stringify(inputs);
6750
7165
  }
6751
7166
  function absolutePattern(root, pattern) {
6752
- return normalize4(path14.isAbsolute(pattern) ? pattern : path14.join(root, pattern));
7167
+ return normalize4(path16.isAbsolute(pattern) ? pattern : path16.join(root, pattern));
6753
7168
  }
6754
7169
  function patternBase(root, pattern) {
6755
7170
  const absolute2 = absolutePattern(root, pattern);
6756
7171
  const magic = absolute2.search(/[*?{}[\]()!]/);
6757
- if (magic < 0) return path14.dirname(absolute2).replace(/\\/g, "/");
7172
+ if (magic < 0) return path16.dirname(absolute2).replace(/\\/g, "/");
6758
7173
  const slash2 = absolute2.lastIndexOf("/", magic);
6759
- return (slash2 > 0 ? absolute2.slice(0, slash2) : path14.parse(absolute2).root).replace(/\\/g, "/");
7174
+ return (slash2 > 0 ? absolute2.slice(0, slash2) : path16.parse(absolute2).root).replace(/\\/g, "/");
6760
7175
  }
6761
7176
  async function nearestExisting(value) {
6762
7177
  let cursor = normalize4(value);
@@ -6765,7 +7180,7 @@ async function nearestExisting(value) {
6765
7180
  await access(cursor);
6766
7181
  return cursor;
6767
7182
  } catch {
6768
- const parent = path14.dirname(cursor).replace(/\\/g, "/");
7183
+ const parent = path16.dirname(cursor).replace(/\\/g, "/");
6769
7184
  if (parent === cursor) return cursor;
6770
7185
  cursor = parent;
6771
7186
  }
@@ -6775,16 +7190,16 @@ function ancestorOf(target, candidate) {
6775
7190
  return target === candidate || target.startsWith(`${candidate}/`);
6776
7191
  }
6777
7192
  function globPattern(pattern) {
6778
- return path14.sep === "\\" ? pattern.replace(/\\/g, "/") : pattern;
7193
+ return path16.sep === "\\" ? pattern.replace(/\\/g, "/") : pattern;
6779
7194
  }
6780
7195
  function compilePattern(pattern) {
6781
7196
  return {
6782
- absolute: path14.isAbsolute(pattern),
7197
+ absolute: path16.isAbsolute(pattern),
6783
7198
  matcher: new Minimatch(globPattern(pattern), { dot: true })
6784
7199
  };
6785
7200
  }
6786
7201
  function patternMatches(glob2, pattern, changed) {
6787
- const subject = pattern.absolute ? changed : path14.relative(glob2.root, changed).replace(/\\/g, "/");
7202
+ const subject = pattern.absolute ? changed : path16.relative(glob2.root, changed).replace(/\\/g, "/");
6788
7203
  return pattern.matcher.match(subject);
6789
7204
  }
6790
7205
  function globMatches(glob2, changed) {
@@ -6871,7 +7286,7 @@ var WatchInputRegistry = class {
6871
7286
  const directories = /* @__PURE__ */ new Set();
6872
7287
  for (const target of globBases) {
6873
7288
  const existing = await nearestExisting(target);
6874
- if (existing === path14.parse(existing).root.replace(/\\/g, "/")) {
7289
+ if (existing === path16.parse(existing).root.replace(/\\/g, "/")) {
6875
7290
  throw new Error(
6876
7291
  `[NGDOC_VITE_WATCH_ROOT] Refusing an unbounded filesystem-root glob watch for ${target}.`
6877
7292
  );
@@ -6882,7 +7297,7 @@ var WatchInputRegistry = class {
6882
7297
  }
6883
7298
  }
6884
7299
  for (const target of desired) {
6885
- if (target === path14.parse(target).root.replace(/\\/g, "/")) {
7300
+ if (target === path16.parse(target).root.replace(/\\/g, "/")) {
6886
7301
  throw new Error(
6887
7302
  `[NGDOC_VITE_WATCH_ROOT] Refusing an unbounded filesystem-root file watch for ${target}.`
6888
7303
  );
@@ -6941,7 +7356,7 @@ var WatchInputRegistry = class {
6941
7356
 
6942
7357
  // libs/builder/generator/vite/vite-event-source.ts
6943
7358
  function normalize5(value) {
6944
- return canonicalDrive(path15.resolve(value).replace(/\\/g, "/"));
7359
+ return canonicalDrive(path17.resolve(value).replace(/\\/g, "/"));
6945
7360
  }
6946
7361
  function within2(target, root) {
6947
7362
  return target === root || target.startsWith(`${root}/`);
@@ -6962,12 +7377,12 @@ function statVersion(file) {
6962
7377
  }
6963
7378
  }
6964
7379
  function listed(watched, target) {
6965
- return watched.has(target) || (watched.get(path15.dirname(target))?.includes(path15.basename(target)) ?? false);
7380
+ return watched.has(target) || (watched.get(path17.dirname(target))?.includes(path17.basename(target)) ?? false);
6966
7381
  }
6967
7382
  function nearestExistingPath(target) {
6968
7383
  let cursor = target;
6969
7384
  while (!existsSync4(cursor)) {
6970
- const parent = path15.dirname(cursor);
7385
+ const parent = path17.dirname(cursor);
6971
7386
  if (parent === cursor) return cursor;
6972
7387
  cursor = parent;
6973
7388
  }
@@ -6992,7 +7407,7 @@ function workspaceRoots(root) {
6992
7407
  roots.add(normalize5(realpathSync2.native(root)));
6993
7408
  } catch {
6994
7409
  }
6995
- return [...roots].filter((value) => value !== normalize5(path15.parse(value).root));
7410
+ return [...roots].filter((value) => value !== normalize5(path17.parse(value).root));
6996
7411
  }
6997
7412
  function watchesMissingPaths(watcher) {
6998
7413
  const options = watcher.options;
@@ -7444,9 +7859,38 @@ var ViteAdapterLifecycle = class {
7444
7859
  this.registration = Promise.resolve();
7445
7860
  this.disposed = false;
7446
7861
  this.latestStarted = -1;
7862
+ /** Whether a content-only reload updates the open pages in place (`NGDOC_VITE_CONTENT_UPDATE`). */
7863
+ this.contentUpdates = contentUpdatesEnabled();
7864
+ /** The browser clients whose NgDoc client listens for content updates. */
7865
+ this.contentClients = /* @__PURE__ */ new WeakSet();
7866
+ /** The timestamp of the last content update: each one imports the modules anew. */
7867
+ this.contentStamp = 0;
7868
+ /** Records the clients that announce the NgDoc client. */
7869
+ this.contentClient = (data, client) => {
7870
+ if (data?.version === CONTENT_UPDATE_VERSION) {
7871
+ this.contentClients.add(client);
7872
+ }
7873
+ };
7447
7874
  this.hostUpdates = new HostUpdateCoordinator(
7448
- () => this.server?.ws.send({ type: "full-reload" }),
7449
- (error2) => this.fail(error2)
7875
+ (changes) => this.announce(changes),
7876
+ (error2) => this.fail(error2),
7877
+ {
7878
+ warn: (message4) => this.warn(message4),
7879
+ // Exactly the event Vite's watcher emits for the file: Vite invalidates its modules and
7880
+ // runs the hot update hooks, which the coordinator matches like the lost report.
7881
+ replay: (file, type) => {
7882
+ if (this.disposed) return;
7883
+ this.server?.watcher.emit(
7884
+ type === "create" ? "add" : type === "delete" ? "unlink" : "change",
7885
+ file
7886
+ );
7887
+ },
7888
+ rescan: () => {
7889
+ if (this.disposed) return;
7890
+ void this.session?.rescan().catch(() => {
7891
+ });
7892
+ }
7893
+ }
7450
7894
  );
7451
7895
  }
7452
7896
  get failure() {
@@ -7458,9 +7902,40 @@ var ViteAdapterLifecycle = class {
7458
7902
  }
7459
7903
  attachServer(server, source) {
7460
7904
  if (this.disposed) throw new Error("Vite adapter is disposed.");
7905
+ if (this.contentUpdates && this.server !== server) {
7906
+ this.stopContentClients();
7907
+ if (typeof server.ws?.on === "function")
7908
+ server.ws.on(CONTENT_CLIENT_EVENT, this.contentClient);
7909
+ }
7461
7910
  this.server = server;
7462
7911
  this.source = source;
7463
7912
  }
7913
+ /** Stops recording the clients that announce the NgDoc client. */
7914
+ stopContentClients() {
7915
+ try {
7916
+ if (typeof this.server?.ws?.off === "function") {
7917
+ this.server.ws.off(CONTENT_CLIENT_EVENT, this.contentClient);
7918
+ }
7919
+ } catch {
7920
+ }
7921
+ }
7922
+ /**
7923
+ * Tells the browser about the newest settled publication: a content update when it changed only
7924
+ * content (see `contentOnly`), else a full reload.
7925
+ * @param changes The generated files that differ from the last announcement, if the reload may
7926
+ * be applied in place.
7927
+ */
7928
+ announce(changes) {
7929
+ const server = this.server;
7930
+ if (!server) return;
7931
+ const content = this.contentUpdates && this.configuration ? contentOnly(changes, this.configuration.assetDirectory) : void 0;
7932
+ if (!content) {
7933
+ server.ws.send({ type: "full-reload" });
7934
+ return;
7935
+ }
7936
+ this.contentStamp = Math.max(Date.now(), this.contentStamp + 1);
7937
+ sendContentUpdate(server, this.contentClients, content, this.contentStamp);
7938
+ }
7464
7939
  attachWatch(watch) {
7465
7940
  if (this.disposed) throw new Error("Vite adapter is disposed.");
7466
7941
  this.watch = watch;
@@ -7511,13 +7986,13 @@ var ViteAdapterLifecycle = class {
7511
7986
  const read = /* @__PURE__ */ new Set();
7512
7987
  for (const artifact of committed.snapshot.artifacts) {
7513
7988
  for (const dependency of artifact.dependencies) {
7514
- if (dependency.kind === "content" && DESCRIPTION_MODULE.test(path16.basename(dependency.path)))
7515
- read.add(canonicalDrive(path16.resolve(dependency.path)));
7989
+ if (dependency.kind === "content" && DESCRIPTION_MODULE.test(path18.basename(dependency.path)))
7990
+ read.add(canonicalDrive(path18.resolve(dependency.path)));
7516
7991
  }
7517
7992
  }
7518
7993
  committed.read = read;
7519
7994
  }
7520
- return committed.read.has(canonicalDrive(path16.resolve(file)));
7995
+ return committed.read.has(canonicalDrive(path18.resolve(file)));
7521
7996
  }
7522
7997
  hostUpdateStarted(file, type, read) {
7523
7998
  const change = { kind: type, path: file };
@@ -7526,13 +8001,34 @@ var ViteAdapterLifecycle = class {
7526
8001
  if (relevant) this.source.forward(change);
7527
8002
  return ticket;
7528
8003
  }
7529
- hostUpdateCompleted(ticket) {
7530
- return this.hostUpdates.complete(ticket);
8004
+ /**
8005
+ * See {@link HostUpdateCoordinator.complete}.
8006
+ * @param ticket The hot update's ticket.
8007
+ * @param modules Whether its hooks hand Vite any module to update.
8008
+ */
8009
+ hostUpdateCompleted(ticket, modules = true) {
8010
+ return this.hostUpdates.complete(ticket, modules);
7531
8011
  }
7532
8012
  /** The adapter reloads for this settled hot update itself. */
7533
8013
  hostUpdateAnnounced(read) {
7534
8014
  return !this.disposed && this.hostUpdates.announces(read);
7535
8015
  }
8016
+ /** See {@link HostUpdateCoordinator.generationPass}. */
8017
+ hostGenerationPass(ticket) {
8018
+ return !this.disposed && this.hostUpdates.generationPass(ticket);
8019
+ }
8020
+ /**
8021
+ * Whether `file` is a description module (`ng-doc.page.ts`, `ng-doc.category.ts`,
8022
+ * `ng-doc.api.ts`) the generator watches: its edit starts a generation that reads it.
8023
+ * @param file An absolute path.
8024
+ */
8025
+ hostDescriptionModule(file) {
8026
+ return !this.disposed && DESCRIPTION_MODULE.test(path18.basename(file)) && (this.source?.matches({ kind: "update", path: file }) ?? false);
8027
+ }
8028
+ /** Whether `file` is a generated output (see {@link HostUpdateCoordinator.generated}). */
8029
+ hostGenerated(file) {
8030
+ return !this.disposed && this.hostUpdates.generated(file);
8031
+ }
7536
8032
  /** See {@link HostUpdateCoordinator.compilerCompanions}. */
7537
8033
  hostUpdateCompanions(ticket) {
7538
8034
  return this.disposed ? [] : this.hostUpdates.compilerCompanions(ticket);
@@ -7562,7 +8058,7 @@ var ViteAdapterLifecycle = class {
7562
8058
  hostClaimsFilesystemChange(file) {
7563
8059
  if (this.disposed) return false;
7564
8060
  if (this.hostUpdates.generated(file)) return true;
7565
- if (!DESCRIPTION_MODULE.test(path16.basename(file)) || !(this.source?.matches({ kind: "update", path: file }) ?? false))
8061
+ if (!DESCRIPTION_MODULE.test(path18.basename(file)) || !(this.source?.matches({ kind: "update", path: file }) ?? false))
7566
8062
  return false;
7567
8063
  return !(this.committedDescriptionModule(file) && existsSync5(file));
7568
8064
  }
@@ -7580,8 +8076,13 @@ var ViteAdapterLifecycle = class {
7580
8076
  this.hostUpdates.diagnostic(ticket, error2);
7581
8077
  this.report(error2);
7582
8078
  }
7583
- hostUpdateSettled(ticket) {
7584
- return this.hostUpdates.settle(ticket);
8079
+ /**
8080
+ * See {@link HostUpdateCoordinator.settle}.
8081
+ * @param ticket The hot update's ticket.
8082
+ * @param modules Whether its hooks hand Vite any module to update.
8083
+ */
8084
+ hostUpdateSettled(ticket, modules = true) {
8085
+ return this.hostUpdates.settle(ticket, modules);
7585
8086
  }
7586
8087
  hostUpdateCommitted(ticket) {
7587
8088
  return this.hostUpdates.committed(ticket);
@@ -7717,6 +8218,14 @@ var ViteAdapterLifecycle = class {
7717
8218
  } catch {
7718
8219
  }
7719
8220
  }
8221
+ warn(message4) {
8222
+ if (!this.server) return;
8223
+ this.interrupt();
8224
+ try {
8225
+ this.server.config.logger.warn(message4);
8226
+ } catch {
8227
+ }
8228
+ }
7720
8229
  isCurrent(generation) {
7721
8230
  return !this.disposed && generation >= this.latestStarted && (!this.source || this.source.isCurrent(generation));
7722
8231
  }
@@ -7742,6 +8251,7 @@ var ViteAdapterLifecycle = class {
7742
8251
  }
7743
8252
  async disposeAll() {
7744
8253
  this.hostUpdates.dispose();
8254
+ this.stopContentClients();
7745
8255
  const results = await Promise.allSettled([
7746
8256
  this.registration,
7747
8257
  this.watch?.dispose(),
@@ -7758,7 +8268,7 @@ var ViteAdapterLifecycle = class {
7758
8268
  var NG_DOC_SYNTAX_THEME_NAME = "css-variables";
7759
8269
 
7760
8270
  // libs/builder/generator/vite/options.ts
7761
- import path17 from "node:path";
8271
+ import path19 from "node:path";
7762
8272
 
7763
8273
  // libs/builder/generator/progress/settings.ts
7764
8274
  var PROGRESS_SETTINGS = Object.freeze([
@@ -7877,6 +8387,21 @@ var ENGINE_SWITCHES = Object.freeze([
7877
8387
  values: /^(0|false|off|no)$/i,
7878
8388
  effect: "adding or removing a page compiles the application twice"
7879
8389
  },
8390
+ {
8391
+ name: "NGDOC_ANGULAR_DESCRIPTION_PASS",
8392
+ values: /^(0|false|off|no)$/i,
8393
+ effect: "a page title edit compiles the application twice"
8394
+ },
8395
+ {
8396
+ name: "NGDOC_VITE_CONTENT_UPDATE",
8397
+ values: /^(0|false|off|no)$/i,
8398
+ effect: "guide text edits reload the page"
8399
+ },
8400
+ {
8401
+ name: "NGDOC_VITE_MODULELESS_SETTLE",
8402
+ values: /^(0|false|off|no)$/i,
8403
+ effect: "hot updates of files the browser never loaded wait for the newest generation"
8404
+ },
7880
8405
  {
7881
8406
  name: "NGDOC_VITE_BUILD_HANDOFF",
7882
8407
  values: /^(0|false|off|no)$/i,
@@ -7887,6 +8412,16 @@ var ENGINE_SWITCHES = Object.freeze([
7887
8412
  values: /^(0|false|off|no|verify)$/i,
7888
8413
  effect: "every code block is highlighted again"
7889
8414
  },
8415
+ {
8416
+ name: "NGDOC_FORMAT_CACHE",
8417
+ values: /^(0|false|off|no|verify)$/i,
8418
+ effect: "every signature and snippet is formatted again"
8419
+ },
8420
+ {
8421
+ name: "NGDOC_USED_GRAMMARS",
8422
+ values: /^(0|false|off|no)$/i,
8423
+ effect: "every Shiki grammar is loaded before code is highlighted"
8424
+ },
7890
8425
  {
7891
8426
  name: "NGDOC_PARALLEL_RENDER",
7892
8427
  values: /^(0|false|off|no|verify)$/i,
@@ -7942,7 +8477,7 @@ function resolveOptions(options) {
7942
8477
  );
7943
8478
  }
7944
8479
  const angularComponentProbe = text2(options.angularComponentProbe, "angularComponentProbe");
7945
- if (!path17.isAbsolute(angularComponentProbe) || !/\.ts$/.test(angularComponentProbe) || /\.d\.ts$/.test(angularComponentProbe) || angularComponentProbe.includes("?")) {
8480
+ if (!path19.isAbsolute(angularComponentProbe) || !/\.ts$/.test(angularComponentProbe) || /\.d\.ts$/.test(angularComponentProbe) || angularComponentProbe.includes("?")) {
7946
8481
  throw new TypeError(
7947
8482
  "angularComponentProbe must be an absolute application .ts component path."
7948
8483
  );
@@ -7961,8 +8496,8 @@ function resolveOptions(options) {
7961
8496
  if (options.progress !== void 0 && (!isProgressSetting(options.progress) || options.progress === "json")) {
7962
8497
  throw new TypeError("progress must be auto, live, plain, verbose, summary or off.");
7963
8498
  }
7964
- const workspace = path17.resolve(text2(options.generator.workspaceRoot, "workspaceRoot"));
7965
- const defaultRoot = path17.resolve(
8499
+ const workspace = path19.resolve(text2(options.generator.workspaceRoot, "workspaceRoot"));
8500
+ const defaultRoot = path19.resolve(
7966
8501
  text2(options.generator.defaults.outputRoot, "defaults.outputRoot")
7967
8502
  );
7968
8503
  return {
@@ -7972,7 +8507,7 @@ function resolveOptions(options) {
7972
8507
  themeModules,
7973
8508
  leaseIdentity: `${text2(options.generator.projectId, "projectId")}\0${workspace}\0${defaultRoot}`,
7974
8509
  angularPlugins: [...options.angularPlugins],
7975
- angularComponentProbe: path17.normalize(angularComponentProbe),
8510
+ angularComponentProbe: path19.normalize(angularComponentProbe),
7976
8511
  ...options.progress ? { progress: options.progress } : {}
7977
8512
  };
7978
8513
  }
@@ -7989,14 +8524,14 @@ function themeImports(options, configuration) {
7989
8524
  ].sort();
7990
8525
  }
7991
8526
  function within3(root, file) {
7992
- const relative = path17.relative(root, file);
7993
- return relative === "" || !relative.startsWith("..") && !path17.isAbsolute(relative);
8527
+ const relative = path19.relative(root, file);
8528
+ return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
7994
8529
  }
7995
8530
  var CacheRootWatchIgnore = class {
7996
8531
  constructor(options) {
7997
8532
  this.options = options;
7998
8533
  const cacheRoot = options.generator.defaults.cacheRoot;
7999
- this.cacheRoot = typeof cacheRoot === "string" && path17.isAbsolute(cacheRoot) ? canonicalDrive(path17.resolve(cacheRoot)) : void 0;
8534
+ this.cacheRoot = typeof cacheRoot === "string" && path19.isAbsolute(cacheRoot) ? canonicalDrive(path19.resolve(cacheRoot)) : void 0;
8000
8535
  this.active = this.cacheRoot !== void 0;
8001
8536
  }
8002
8537
  get ignoring() {
@@ -8010,11 +8545,11 @@ var CacheRootWatchIgnore = class {
8010
8545
  workspaceRoot,
8011
8546
  defaults.outputRoot,
8012
8547
  defaults.docsRoot,
8013
- typeof defaults.tsConfig === "string" ? path17.dirname(defaults.tsConfig) : void 0
8548
+ typeof defaults.tsConfig === "string" ? path19.dirname(defaults.tsConfig) : void 0
8014
8549
  ]);
8015
8550
  const cacheRoot = this.cacheRoot;
8016
8551
  if (!this.active || !cacheRoot) return void 0;
8017
- return (file) => this.active && typeof file === "string" && within3(cacheRoot, path17.resolve(file));
8552
+ return (file) => this.active && typeof file === "string" && within3(cacheRoot, path19.resolve(file));
8018
8553
  }
8019
8554
  /** Re-checks the finally resolved Vite root (a later plugin can change `root`). */
8020
8555
  confirmRoot(viteRoot) {
@@ -8027,7 +8562,7 @@ var CacheRootWatchIgnore = class {
8027
8562
  admit(configuration) {
8028
8563
  const cacheRoot = this.cacheRoot;
8029
8564
  if (!this.active || !cacheRoot) return false;
8030
- const publishedCache = typeof configuration.cacheRoot === "string" ? canonicalDrive(path17.resolve(configuration.cacheRoot)) : void 0;
8565
+ const publishedCache = typeof configuration.cacheRoot === "string" ? canonicalDrive(path19.resolve(configuration.cacheRoot)) : void 0;
8031
8566
  if (publishedCache === cacheRoot && this.guard([configuration.outputRoot])) return false;
8032
8567
  this.active = false;
8033
8568
  return true;
@@ -8035,7 +8570,7 @@ var CacheRootWatchIgnore = class {
8035
8570
  guard(roots) {
8036
8571
  const cacheRoot = this.cacheRoot;
8037
8572
  if (!this.active || !cacheRoot || // Unvalidated roots are rejected by bootstrap later; never ignore when one is missing.
8038
- roots.some((value) => typeof value !== "string" || !value) || roots.some((value) => within3(cacheRoot, path17.resolve(value)))) {
8573
+ roots.some((value) => typeof value !== "string" || !value) || roots.some((value) => within3(cacheRoot, path19.resolve(value)))) {
8039
8574
  this.active = false;
8040
8575
  }
8041
8576
  return this.active;
@@ -8043,26 +8578,26 @@ var CacheRootWatchIgnore = class {
8043
8578
  };
8044
8579
  function metadataWatchIgnore(options, viteRoot) {
8045
8580
  const { workspaceRoot, defaults } = options.generator;
8046
- if (typeof workspaceRoot !== "string" || !path17.isAbsolute(workspaceRoot)) return void 0;
8047
- const idea = path17.resolve(workspaceRoot, ".idea");
8581
+ if (typeof workspaceRoot !== "string" || !path19.isAbsolute(workspaceRoot)) return void 0;
8582
+ const idea = path19.resolve(workspaceRoot, ".idea");
8048
8583
  const roots = [
8049
8584
  viteRoot,
8050
8585
  defaults.outputRoot,
8051
8586
  defaults.docsRoot,
8052
- typeof defaults.tsConfig === "string" ? path17.dirname(defaults.tsConfig) : void 0
8587
+ typeof defaults.tsConfig === "string" ? path19.dirname(defaults.tsConfig) : void 0
8053
8588
  ];
8054
8589
  const ignoreIdea = !roots.some(
8055
- (value) => typeof value === "string" && value && within3(idea, path17.resolve(value))
8590
+ (value) => typeof value === "string" && value && within3(idea, path19.resolve(value))
8056
8591
  );
8057
8592
  return (file) => {
8058
8593
  if (typeof file !== "string") return false;
8059
- const target = path17.resolve(file);
8060
- return path17.basename(target) === ".DS_Store" || ignoreIdea && within3(idea, target);
8594
+ const target = path19.resolve(file);
8595
+ return path19.basename(target) === ".DS_Store" || ignoreIdea && within3(idea, target);
8061
8596
  };
8062
8597
  }
8063
8598
  function staticViteConfig(options, viteRoot = process.cwd(), cacheWatch = new CacheRootWatchIgnore(options)) {
8064
- const cacheIgnore = cacheWatch.matcher(path17.resolve(viteRoot));
8065
- const metadataIgnore = metadataWatchIgnore(options, path17.resolve(viteRoot));
8599
+ const cacheIgnore = cacheWatch.matcher(path19.resolve(viteRoot));
8600
+ const metadataIgnore = metadataWatchIgnore(options, path19.resolve(viteRoot));
8066
8601
  const ignored = [GENERATED_STAGE_IGNORE];
8067
8602
  if (cacheIgnore) ignored.push(cacheIgnore);
8068
8603
  if (metadataIgnore) ignored.push(metadataIgnore);
@@ -8098,10 +8633,10 @@ function assertThemeModules(options, configuration) {
8098
8633
 
8099
8634
  // libs/builder/generator/vite/output-watch.ts
8100
8635
  import { realpathSync as realpathSync3 } from "node:fs";
8101
- import path18 from "node:path";
8636
+ import path20 from "node:path";
8102
8637
  var OUTPUT_WATCH_ATTACH_TIMEOUT_MS = 1e4;
8103
8638
  function normalize6(value) {
8104
- return canonicalDrive(path18.resolve(value).replace(/\\/g, "/"));
8639
+ return canonicalDrive(path20.resolve(value).replace(/\\/g, "/"));
8105
8640
  }
8106
8641
  function within4(target, root) {
8107
8642
  return target === root || target.startsWith(`${root}/`);
@@ -8133,9 +8668,9 @@ async function watchOutputRoot(watcher, viteRoot, outputRoot, files, options = {
8133
8668
  for (const file of files) {
8134
8669
  let target = `${root}/${file.replace(/\\/g, "/")}`;
8135
8670
  while (target !== root && within4(target, root)) {
8136
- const parent = path18.posix.dirname(target);
8671
+ const parent = path20.posix.dirname(target);
8137
8672
  const listed2 = entries.get(parent) ?? /* @__PURE__ */ new Set();
8138
- listed2.add(path18.posix.basename(target));
8673
+ listed2.add(path20.posix.basename(target));
8139
8674
  entries.set(parent, listed2);
8140
8675
  target = parent;
8141
8676
  }
@@ -9551,7 +10086,7 @@ function hotPayload(value, childToServer = false) {
9551
10086
  case "full-reload":
9552
10087
  return (value.path === void 0 || typeof value.path === "string") && (value.triggeredBy === void 0 || typeof value.triggeredBy === "string");
9553
10088
  case "prune":
9554
- return Array.isArray(value.paths) && value.paths.every((path24) => typeof path24 === "string");
10089
+ return Array.isArray(value.paths) && value.paths.every((path26) => typeof path26 === "string");
9555
10090
  case "update":
9556
10091
  return Array.isArray(value.updates) && value.updates.every(
9557
10092
  (update) => record2(update) && (update.type === "js-update" || update.type === "css-update") && typeof update.path === "string" && typeof update.acceptedPath === "string" && Number.isFinite(update.timestamp)
@@ -10367,10 +10902,10 @@ function getNgDocViteSsrRenderer(plugins, options) {
10367
10902
  }
10368
10903
 
10369
10904
  // libs/builder/generator/vite/theme-index.ts
10370
- import { readFile as readFile6 } from "node:fs/promises";
10905
+ import { readFile as readFile7 } from "node:fs/promises";
10371
10906
  var MARKER = "data-ng-doc-theme-restore";
10372
10907
  async function createThemeIndexTransformer(scriptUrl = new URL("./restore-theme.js", import.meta.url)) {
10373
- const source = await readFile6(scriptUrl, "utf8");
10908
+ const source = await readFile7(scriptUrl, "utf8");
10374
10909
  const tag = `<script ${MARKER}>${source}</script>`;
10375
10910
  return async (content) => {
10376
10911
  if (content.includes(MARKER)) return content;
@@ -10610,6 +11145,15 @@ function createPlugin(options) {
10610
11145
  diagnosticMark() {
10611
11146
  return (active?.lifecycle ?? pendingLifecycle)?.hostDiagnosticMark() ?? 0;
10612
11147
  },
11148
+ generationPass(ticket) {
11149
+ return (active?.lifecycle ?? pendingLifecycle)?.hostGenerationPass(ticket) ?? false;
11150
+ },
11151
+ descriptionModule(file) {
11152
+ return (active?.lifecycle ?? pendingLifecycle)?.hostDescriptionModule(file) ?? false;
11153
+ },
11154
+ generated(file) {
11155
+ return (active?.lifecycle ?? pendingLifecycle)?.hostGenerated(file) ?? false;
11156
+ },
10613
11157
  claimsFilesystemChange(file) {
10614
11158
  return (active?.lifecycle ?? pendingLifecycle)?.hostClaimsFilesystemChange(file) ?? false;
10615
11159
  },
@@ -10627,8 +11171,8 @@ function createPlugin(options) {
10627
11171
  committed(ticket) {
10628
11172
  return (active?.lifecycle ?? pendingLifecycle)?.hostUpdateCommitted(ticket) ?? Promise.resolve();
10629
11173
  },
10630
- settle(ticket) {
10631
- return (active?.lifecycle ?? pendingLifecycle)?.hostUpdateSettled(ticket) ?? Promise.resolve();
11174
+ settle(ticket, modules) {
11175
+ return (active?.lifecycle ?? pendingLifecycle)?.hostUpdateSettled(ticket, modules) ?? Promise.resolve();
10632
11176
  },
10633
11177
  fail(error2) {
10634
11178
  (active?.lifecycle ?? pendingLifecycle)?.hostUpdateFailed(error2);
@@ -10642,6 +11186,20 @@ function createPlugin(options) {
10642
11186
  ngDocSsrRenderer: Object.freeze({
10643
11187
  schemaVersion: 1,
10644
11188
  select: (entry) => ssrRenderer.select(entry)
11189
+ }),
11190
+ ngDocDemoApplication: Object.freeze({
11191
+ schemaVersion: 1,
11192
+ // A build decides its inputs before it starts, so the application plugin asks for the
11193
+ // generation early (its `options` hook); `buildStart` then finds it initialized.
11194
+ async resolve() {
11195
+ if (vite?.command === "build") await initializeBuild();
11196
+ const state = active ?? await initialized;
11197
+ return state ? readDemoApplication(state.configuration.outputRoot) : void 0;
11198
+ },
11199
+ async current() {
11200
+ const configuration = active?.lifecycle.configuration ?? active?.configuration;
11201
+ return configuration ? readDemoApplication(configuration.outputRoot) : void 0;
11202
+ }
10645
11203
  })
10646
11204
  },
10647
11205
  enforce: "pre",
@@ -10663,7 +11221,7 @@ function createPlugin(options) {
10663
11221
  "[NGDOC_VITE_BUILD_WATCH] Vite build watch is unsupported; use the development server."
10664
11222
  );
10665
11223
  }
10666
- return staticViteConfig(resolved, path19.resolve(config.root ?? process.cwd()), cacheWatch);
11224
+ return staticViteConfig(resolved, path21.resolve(config.root ?? process.cwd()), cacheWatch);
10667
11225
  },
10668
11226
  configResolved(config) {
10669
11227
  vite = config;
@@ -10794,7 +11352,14 @@ function createPlugin(options) {
10794
11352
  load(id) {
10795
11353
  return ssrRenderer.controlModule(id) ?? null;
10796
11354
  },
10797
- transformIndexHtml: { order: "pre", handler: transformNgDocIndex },
11355
+ transformIndexHtml: {
11356
+ order: "pre",
11357
+ async handler(html, context) {
11358
+ const transformed = await transformNgDocIndex(html);
11359
+ const tags = context?.server ? contentUpdateClientTags(transformed) : [];
11360
+ return tags.length > 0 ? { html: transformed, tags } : transformed;
11361
+ }
11362
+ },
10798
11363
  async generateBundle(_output, bundle) {
10799
11364
  if (vite?.command === "build" && active) await active.lifecycle.assets.emit(this, bundle);
10800
11365
  },
@@ -10822,7 +11387,10 @@ function createPlugin(options) {
10822
11387
  const ticket = hostUpdates.get(context);
10823
11388
  if (ticket) {
10824
11389
  hostUpdates.delete(context);
10825
- await (active?.lifecycle ?? pendingLifecycle)?.hostUpdateCompleted(ticket);
11390
+ const modules = handsViteModules(context.file, context.modules) || Object.values(devServer?.environments ?? {}).some(
11391
+ (environment) => environment !== this.environment && (environment.moduleGraph.getModulesByFile(context.file)?.size ?? 0) > 0
11392
+ );
11393
+ await (active?.lifecycle ?? pendingLifecycle)?.hostUpdateCompleted(ticket, modules);
10826
11394
  }
10827
11395
  if (!(active?.lifecycle ?? pendingLifecycle)?.hostUpdateAnnounced(context.read)) return;
10828
11396
  const invalidated = /* @__PURE__ */ new Set();
@@ -10844,13 +11412,13 @@ function createPlugin(options) {
10844
11412
  // libs/builder/generator/vite/application.ts
10845
11413
  import { minimatch as minimatch2 } from "minimatch";
10846
11414
  import { existsSync as existsSync6 } from "node:fs";
10847
- import { readdir, readFile as readFile7, stat as stat5 } from "node:fs/promises";
11415
+ import { mkdir as mkdir2, readdir, readFile as readFile8, stat as stat5, writeFile as writeFile2 } from "node:fs/promises";
10848
11416
  import { createRequire as createRequire2 } from "node:module";
10849
- import path22 from "node:path";
11417
+ import path24 from "node:path";
10850
11418
  import { escapePath, glob } from "tinyglobby";
10851
11419
 
10852
11420
  // libs/builder/generator/vite/dev-styles.ts
10853
- import path20 from "node:path";
11421
+ import path22 from "node:path";
10854
11422
  var GATE_TIMINGS = { sheet: 5e3, quiet: 1500, limit: 1e4 };
10855
11423
  function ngDocDevStyleGate(timings = GATE_TIMINGS) {
10856
11424
  return `(() => {
@@ -10909,8 +11477,8 @@ async function ngDocDevStyleTags(server, styles) {
10909
11477
  { tag: "script", children: NG_DOC_DEV_STYLE_GATE, injectTo: "head-prepend" }
10910
11478
  ];
10911
11479
  for (const style of styles) {
10912
- const relative = path20.relative(root, style);
10913
- const url = relative && !relative.startsWith("..") && !path20.isAbsolute(relative) ? `/${relative.split(path20.sep).join("/")}` : `/@fs/${style.split(path20.sep).join("/").replace(/^\/+/, "")}`;
11480
+ const relative = path22.relative(root, style);
11481
+ const url = relative && !relative.startsWith("..") && !path22.isAbsolute(relative) ? `/${relative.split(path22.sep).join("/")}` : `/@fs/${style.split(path22.sep).join("/").replace(/^\/+/, "")}`;
10914
11482
  try {
10915
11483
  const sheet = cssModuleSheet((await environment.transformRequest(url))?.code ?? "");
10916
11484
  if (!sheet) continue;
@@ -10942,7 +11510,7 @@ function cssModuleSheet(code) {
10942
11510
  }
10943
11511
 
10944
11512
  // libs/builder/generator/vite/mime.ts
10945
- import path21 from "node:path";
11513
+ import path23 from "node:path";
10946
11514
  var TYPES = {
10947
11515
  ".aac": "audio/aac",
10948
11516
  ".apng": "image/apng",
@@ -10992,14 +11560,21 @@ var TYPES = {
10992
11560
  ".zip": "application/zip"
10993
11561
  };
10994
11562
  function contentType(file) {
10995
- return TYPES[path21.extname(file).toLowerCase()] ?? "application/octet-stream";
11563
+ return TYPES[path23.extname(file).toLowerCase()] ?? "application/octet-stream";
10996
11564
  }
10997
11565
 
10998
11566
  // libs/builder/generator/vite/application.ts
10999
11567
  var NG_DOC_BROWSER_ENTRY = "/@ng-doc-application/browser.js";
11000
11568
  var NG_DOC_SERVER_ENTRY = "/@ng-doc-application/server.js";
11569
+ var NG_DOC_DEMO_ENTRY = "/@ng-doc-application/demo.js";
11570
+ var NG_DOC_DEMO_SERVER_ENTRY = "/@ng-doc-application/demo-server.js";
11571
+ var NG_DOC_DEMO_PAGE = "ng-doc-demo.html";
11572
+ var NG_DOC_DEMO_SERVER_NAME = "demo-server";
11001
11573
  var BROWSER_ID = "\0ng-doc-application:browser";
11002
11574
  var SERVER_ID = "\0ng-doc-application:server";
11575
+ var DEMO_ID = "\0ng-doc-application:demo";
11576
+ var DEMO_SERVER_ID = "\0ng-doc-application:demo-server";
11577
+ var DEMO_ROOT = "<ng-doc-demo-app></ng-doc-demo-app>";
11003
11578
  var ANGULAR_DEDUPE = [
11004
11579
  "@angular/common",
11005
11580
  "@angular/compiler",
@@ -11091,23 +11666,23 @@ function posix(value) {
11091
11666
  return value.replace(/\\/g, "/");
11092
11667
  }
11093
11668
  function specifier(workspaceRoot, value) {
11094
- const file = path22.resolve(workspaceRoot, value);
11095
- return value.startsWith(".") || path22.isAbsolute(value) || existsSync6(file) ? posix(file) : value;
11669
+ const file = path24.resolve(workspaceRoot, value);
11670
+ return value.startsWith(".") || path24.isAbsolute(value) || existsSync6(file) ? posix(file) : value;
11096
11671
  }
11097
11672
  async function resolveAssets(assets, workspaceRoot, sourceRoot) {
11098
11673
  const resolved = [];
11099
11674
  for (const [index, asset] of assets.entries()) {
11100
11675
  if (typeof asset === "string") {
11101
- const input = path22.resolve(workspaceRoot, text4(asset, `assets[${index}]`));
11676
+ const input = path24.resolve(workspaceRoot, text4(asset, `assets[${index}]`));
11102
11677
  const file = await stat5(input).then(
11103
11678
  (value) => value.isFile(),
11104
11679
  () => {
11105
11680
  throw optionError(`assets[${index}] does not exist: ${asset}`);
11106
11681
  }
11107
11682
  );
11108
- const directory = file ? path22.dirname(input) : input;
11109
- const relative = path22.relative(sourceRoot, directory);
11110
- if (relative.startsWith("..") || path22.isAbsolute(relative)) {
11683
+ const directory = file ? path24.dirname(input) : input;
11684
+ const relative = path24.relative(sourceRoot, directory);
11685
+ if (relative.startsWith("..") || path24.isAbsolute(relative)) {
11111
11686
  throw optionError(
11112
11687
  `assets[${index}] (${asset}) is outside sourceRoot (${sourceRoot}); use a { glob, input, output } entry.`
11113
11688
  );
@@ -11115,18 +11690,18 @@ async function resolveAssets(assets, workspaceRoot, sourceRoot) {
11115
11690
  resolved.push({
11116
11691
  input: directory,
11117
11692
  output: posix(relative),
11118
- glob: file ? path22.basename(input) : "**/*",
11693
+ glob: file ? path24.basename(input) : "**/*",
11119
11694
  ignore: []
11120
11695
  });
11121
11696
  continue;
11122
11697
  }
11123
11698
  if (!asset || typeof asset !== "object") throw optionError(`assets[${index}] is invalid.`);
11124
11699
  const output = posix(asset.output ?? "").replace(/^\/+|\/+$/g, "");
11125
- if (output.split("/").includes("..") || path22.isAbsolute(output)) {
11700
+ if (output.split("/").includes("..") || path24.isAbsolute(output)) {
11126
11701
  throw optionError(`assets[${index}].output must stay inside the output directory.`);
11127
11702
  }
11128
11703
  resolved.push({
11129
- input: path22.resolve(workspaceRoot, text4(asset.input, `assets[${index}].input`)),
11704
+ input: path24.resolve(workspaceRoot, text4(asset.input, `assets[${index}].input`)),
11130
11705
  output,
11131
11706
  glob: text4(asset.glob, `assets[${index}].glob`),
11132
11707
  ignore: [...asset.ignore ?? []]
@@ -11148,7 +11723,7 @@ async function collectAssets(assets) {
11148
11723
  expandDirectories: false
11149
11724
  });
11150
11725
  for (const relative of matches.map(posix).sort()) {
11151
- files.set(outputName(asset, relative), path22.join(asset.input, relative));
11726
+ files.set(outputName(asset, relative), path24.join(asset.input, relative));
11152
11727
  }
11153
11728
  }
11154
11729
  return files;
@@ -11168,14 +11743,14 @@ function matchAssets(assets, base, url) {
11168
11743
  for (const asset of [...assets].reverse()) {
11169
11744
  const relative = asset.output ? requested.startsWith(`${asset.output}/`) ? requested.slice(asset.output.length + 1) : void 0 : requested;
11170
11745
  if (relative && minimatch2(relative, asset.glob, { dot: true }) && !asset.ignore.some((pattern) => minimatch2(relative, pattern, { dot: true }))) {
11171
- candidates.push({ file: path22.join(asset.input, relative), relative });
11746
+ candidates.push({ file: path24.join(asset.input, relative), relative });
11172
11747
  }
11173
11748
  }
11174
11749
  return candidates;
11175
11750
  }
11176
11751
  function resolvable(root, name) {
11177
11752
  try {
11178
- createRequire2(path22.join(root, "package.json")).resolve(`${name}/package.json`);
11753
+ createRequire2(path24.join(root, "package.json")).resolve(`${name}/package.json`);
11179
11754
  return true;
11180
11755
  } catch {
11181
11756
  return false;
@@ -11183,17 +11758,17 @@ function resolvable(root, name) {
11183
11758
  }
11184
11759
  async function coreImports(root) {
11185
11760
  const found = /* @__PURE__ */ new Set();
11186
- const require2 = createRequire2(path22.join(root, "package.json"));
11761
+ const require2 = createRequire2(path24.join(root, "package.json"));
11187
11762
  for (const name of ["@ng-doc/app", "@ng-doc/ui-kit"]) {
11188
11763
  let directory;
11189
11764
  try {
11190
- directory = path22.join(path22.dirname(require2.resolve(`${name}/package.json`)), "fesm2022");
11765
+ directory = path24.join(path24.dirname(require2.resolve(`${name}/package.json`)), "fesm2022");
11191
11766
  } catch {
11192
11767
  continue;
11193
11768
  }
11194
11769
  const files = await readdir(directory).catch(() => []);
11195
11770
  for (const file of files.filter((item) => item.endsWith(".mjs")).sort()) {
11196
- for (const match of (await readFile7(path22.join(directory, file), "utf8")).matchAll(
11771
+ for (const match of (await readFile8(path24.join(directory, file), "utf8")).matchAll(
11197
11772
  CORE_IMPORT
11198
11773
  )) {
11199
11774
  found.add(match[1]);
@@ -11205,6 +11780,20 @@ async function coreImports(root) {
11205
11780
  function escapeAttribute(value) {
11206
11781
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
11207
11782
  }
11783
+ function demoDocument(index) {
11784
+ const body = /<body(\s[^>]*)?>[\s\S]*<\/body>/i;
11785
+ return body.test(index) ? index.replace(
11786
+ body,
11787
+ (_all, attributes) => `<body${attributes ?? ""}>${DEMO_ROOT}</body>`
11788
+ ) : `${index}${DEMO_ROOT}`;
11789
+ }
11790
+ function withDemoInput(input, key, id, next2) {
11791
+ const record3 = typeof input === "string" ? { [path24.parse(input).name]: input } : Array.isArray(input) ? Object.fromEntries(input.map((item) => [path24.parse(item).name, item])) : { ...input ?? {} };
11792
+ if (Object.values(record3).some((item) => item === id || posix(path24.resolve(item)) === id)) {
11793
+ record3[key] = next2;
11794
+ }
11795
+ return record3;
11796
+ }
11208
11797
  function withBase(html, base) {
11209
11798
  if (!base.startsWith("/")) return html;
11210
11799
  const href = escapeAttribute(base);
@@ -11216,30 +11805,38 @@ function withBase(html, base) {
11216
11805
  function createNgDocApplicationPlugin(options) {
11217
11806
  if (!options || typeof options !== "object") throw optionError("options must be an object.");
11218
11807
  const warnings = checkOptionKeys(options);
11219
- const workspaceRoot = path22.resolve(options.workspaceRoot ?? process.cwd());
11220
- const browser = path22.resolve(workspaceRoot, text4(options.browser, "browser"));
11221
- const server = options.server === void 0 ? void 0 : path22.resolve(workspaceRoot, text4(options.server, "server"));
11222
- const sourceRoot = path22.resolve(workspaceRoot, options.sourceRoot ?? path22.dirname(browser));
11808
+ const workspaceRoot = path24.resolve(options.workspaceRoot ?? process.cwd());
11809
+ const browser = path24.resolve(workspaceRoot, text4(options.browser, "browser"));
11810
+ const server = options.server === void 0 ? void 0 : path24.resolve(workspaceRoot, text4(options.server, "server"));
11811
+ const sourceRoot = path24.resolve(workspaceRoot, options.sourceRoot ?? path24.dirname(browser));
11223
11812
  const polyfills = (options.polyfills ?? []).map(
11224
11813
  (item, index) => specifier(workspaceRoot, text4(item, `polyfills[${index}]`))
11225
11814
  );
11226
11815
  const styles = (options.styles ?? []).map(
11227
- (item, index) => posix(path22.resolve(workspaceRoot, text4(item, `styles[${index}]`)))
11816
+ (item, index) => posix(path24.resolve(workspaceRoot, text4(item, `styles[${index}]`)))
11228
11817
  );
11229
11818
  const assetPatterns = [...options.assets ?? []];
11819
+ const zone = (options.polyfills ?? []).some(
11820
+ (item) => item === "zone.js" || item.startsWith("zone.js/")
11821
+ );
11822
+ let viteRoot = process.cwd();
11823
+ let demoApi;
11824
+ let demo;
11825
+ const demoPage = () => posix(path24.join(viteRoot, NG_DOC_DEMO_PAGE));
11826
+ const currentDemo = async () => config?.command === "build" ? demo : await demoApi?.current().catch(() => void 0);
11230
11827
  let assets;
11231
11828
  const resolvedAssets = () => assets ??= resolveAssets(assetPatterns, workspaceRoot, sourceRoot);
11232
11829
  let config;
11233
- const developmentPrebundles = async (viteRoot) => ({
11830
+ const developmentPrebundles = async (viteRoot2) => ({
11234
11831
  include: [
11235
11832
  .../* @__PURE__ */ new Set([
11236
- ...await coreImports(viteRoot),
11237
- ...polyfills.filter((item) => !path22.isAbsolute(item)),
11238
- ...resolvable(viteRoot, "tslib") ? ["tslib"] : []
11833
+ ...await coreImports(viteRoot2),
11834
+ ...polyfills.filter((item) => !path24.isAbsolute(item)),
11835
+ ...resolvable(viteRoot2, "tslib") ? ["tslib"] : []
11239
11836
  ])
11240
11837
  ].sort(),
11241
- entries: [...polyfills.filter((item) => path22.isAbsolute(item)), posix(browser)].map(
11242
- (file) => escapePath(posix(path22.relative(viteRoot, file)))
11838
+ entries: [...polyfills.filter((item) => path24.isAbsolute(item)), posix(browser)].map(
11839
+ (file) => escapePath(posix(path24.relative(viteRoot2, file)))
11243
11840
  )
11244
11841
  });
11245
11842
  const api = Object.freeze({
@@ -11251,6 +11848,7 @@ function createNgDocApplicationPlugin(options) {
11251
11848
  enforce: "pre",
11252
11849
  api: { ngDocApplication: api },
11253
11850
  async config(user, environment) {
11851
+ viteRoot = path24.resolve(user.root ?? process.cwd());
11254
11852
  return {
11255
11853
  resolve: { dedupe: ANGULAR_DEDUPE },
11256
11854
  // Every `vite build` is a production build of the application (the Angular CLI's
@@ -11260,7 +11858,7 @@ function createNgDocApplicationPlugin(options) {
11260
11858
  ["ngDevMode", "ngJitMode"].filter((name) => !(user.define && name in user.define)).map((name) => [name, "false"])
11261
11859
  )
11262
11860
  } : {},
11263
- ...environment.command === "serve" ? { optimizeDeps: await developmentPrebundles(path22.resolve(user.root ?? process.cwd())) } : {},
11861
+ ...environment.command === "serve" ? { optimizeDeps: await developmentPrebundles(viteRoot) } : {},
11264
11862
  // The server bundle is imported by a plain Node process: it must hold every dependency,
11265
11863
  // partially compiled Angular libraries included.
11266
11864
  ...environment.command === "build" && environment.isSsrBuild ? { ssr: { noExternal: true } } : {}
@@ -11268,11 +11866,42 @@ function createNgDocApplicationPlugin(options) {
11268
11866
  },
11269
11867
  configResolved(resolved) {
11270
11868
  config = resolved;
11869
+ viteRoot = resolved.root ?? viteRoot;
11870
+ const apis = (resolved.plugins ?? []).map((plugin) => plugin.api?.ngDocDemoApplication).filter((api2) => api2?.schemaVersion === 1);
11871
+ demoApi = apis.length === 1 ? apis[0] : void 0;
11271
11872
  for (const warning of warnings) resolved.logger.warn(warning);
11272
11873
  },
11874
+ // A build with demo pages builds the demo application too: its page is a second input of the
11875
+ // browser build and its server entry a second input of the server build. Whether there are
11876
+ // demo pages is known once NgDoc has generated, so the NgDoc plugin generates here, before
11877
+ // the build starts, instead of in its `buildStart`.
11878
+ async options(input) {
11879
+ if (config?.command !== "build" || !demoApi) return null;
11880
+ demo = await demoApi.resolve();
11881
+ if (!demo) return null;
11882
+ return {
11883
+ ...input,
11884
+ input: config.build.ssr ? withDemoInput(
11885
+ input.input,
11886
+ NG_DOC_DEMO_SERVER_NAME,
11887
+ NG_DOC_SERVER_ENTRY,
11888
+ NG_DOC_DEMO_SERVER_ENTRY
11889
+ ) : withDemoInput(
11890
+ input.input ?? path24.join(viteRoot, "index.html"),
11891
+ "ng-doc-demo",
11892
+ posix(path24.join(viteRoot, "index.html")),
11893
+ demoPage()
11894
+ )
11895
+ };
11896
+ },
11273
11897
  resolveId(source, importer, resolveOptions2) {
11274
11898
  if (source === NG_DOC_BROWSER_ENTRY) return BROWSER_ID;
11275
- if (importer === BROWSER_ID && config?.command === "build") {
11899
+ if (source === NG_DOC_DEMO_ENTRY) return DEMO_ID;
11900
+ if (source === NG_DOC_DEMO_SERVER_ENTRY) return DEMO_SERVER_ID;
11901
+ if (config?.command === "build" && demo && posix(path24.resolve(viteRoot, source)) === demoPage()) {
11902
+ return demoPage();
11903
+ }
11904
+ if ((importer === BROWSER_ID || importer === DEMO_ID) && config?.command === "build") {
11276
11905
  const { isEntry, custom } = resolveOptions2;
11277
11906
  return this.resolve(source, importer, { isEntry, custom, skipSelf: true }).then(
11278
11907
  (resolved) => resolved && !resolved.external ? { ...resolved, moduleSideEffects: true } : resolved
@@ -11287,6 +11916,25 @@ function createNgDocApplicationPlugin(options) {
11287
11916
  return SERVER_ID;
11288
11917
  },
11289
11918
  load(id) {
11919
+ if (config?.command === "build" && demo && id === demoPage()) {
11920
+ const index = path24.join(viteRoot, "index.html");
11921
+ this.addWatchFile(index);
11922
+ return readFile8(index, "utf8").then(demoDocument);
11923
+ }
11924
+ if (id === DEMO_ID || id === DEMO_SERVER_ID) {
11925
+ return currentDemo().then((current) => {
11926
+ if (!current) {
11927
+ throw new Error(
11928
+ "[NGDOC_VITE_DEMO_APPLICATION] The demo application was requested, but NgDoc generated no demo pages."
11929
+ );
11930
+ }
11931
+ return demoEntry(id === DEMO_SERVER_ID ? "server" : "browser", current.module, {
11932
+ polyfills: id === DEMO_SERVER_ID ? serverPolyfills(options.polyfills ?? []) : polyfills,
11933
+ styles: id === DEMO_SERVER_ID ? [] : styles,
11934
+ zone
11935
+ });
11936
+ });
11937
+ }
11290
11938
  if (id === BROWSER_ID) {
11291
11939
  return [...polyfills, ...styles, posix(browser)].map((item) => `import ${JSON.stringify(item)};`).join("\n");
11292
11940
  }
@@ -11318,6 +11966,7 @@ function createNgDocApplicationPlugin(options) {
11318
11966
  transformIndexHtml: {
11319
11967
  order: "pre",
11320
11968
  async handler(html, context) {
11969
+ const demoPageHtml = html.includes(DEMO_ROOT);
11321
11970
  return {
11322
11971
  html: withBase(html, config?.base ?? "/"),
11323
11972
  tags: [
@@ -11326,7 +11975,10 @@ function createNgDocApplicationPlugin(options) {
11326
11975
  ...context.server ? await ngDocDevStyleTags(context.server, styles) : [],
11327
11976
  {
11328
11977
  tag: "script",
11329
- attrs: { type: "module", src: NG_DOC_BROWSER_ENTRY },
11978
+ attrs: {
11979
+ type: "module",
11980
+ src: demoPageHtml ? NG_DOC_DEMO_ENTRY : NG_DOC_BROWSER_ENTRY
11981
+ },
11330
11982
  injectTo: "body"
11331
11983
  }
11332
11984
  ]
@@ -11335,6 +11987,43 @@ function createNgDocApplicationPlugin(options) {
11335
11987
  },
11336
11988
  configureServer(server2) {
11337
11989
  server2.middlewares.use(assetMiddleware(resolvedAssets, () => server2.config.base));
11990
+ server2.middlewares.use((request, response, next2) => {
11991
+ const accept = request.headers.accept;
11992
+ const url = request.url ?? "/";
11993
+ if (request.method !== "GET" || !(Array.isArray(accept) ? accept.join(",") : accept ?? "").includes("text/html")) {
11994
+ next2();
11995
+ return;
11996
+ }
11997
+ void currentDemo().then(async (current) => {
11998
+ const prefix = `${server2.config.base.replace(/\/?$/, "/")}${current?.path}/`;
11999
+ if (!current || !url.split(/[?#]/, 1)[0].startsWith(prefix)) {
12000
+ next2();
12001
+ return;
12002
+ }
12003
+ const index = await readFile8(path24.join(viteRoot, "index.html"), "utf8");
12004
+ const html = await server2.transformIndexHtml(
12005
+ url,
12006
+ demoDocument(index),
12007
+ request.originalUrl
12008
+ );
12009
+ response.statusCode = 200;
12010
+ response.setHeader("Content-Type", "text/html");
12011
+ response.end(html);
12012
+ }).catch((error2) => next2(error2));
12013
+ });
12014
+ },
12015
+ // Every demo page is a copy of the demo application's page, so a static host without a
12016
+ // fallback serves it, as with hash URLs (which are built without prerendering). Prerendering
12017
+ // replaces each copy with the rendered page.
12018
+ async writeBundle(output) {
12019
+ if (config?.command !== "build" || config.build.ssr || !demo || !output.dir) return;
12020
+ const page = await readFile8(path24.join(output.dir, NG_DOC_DEMO_PAGE), "utf8");
12021
+ for (const route of [...demo.pages].sort()) {
12022
+ if (unsafeSegment(route)) continue;
12023
+ const file = path24.join(output.dir, ...route.split("/"), "index.html");
12024
+ await mkdir2(path24.dirname(file), { recursive: true });
12025
+ await writeFile2(file, page);
12026
+ }
11338
12027
  },
11339
12028
  async generateBundle(_output, bundle) {
11340
12029
  if (config?.command !== "build" || config.build.ssr) return;
@@ -11344,7 +12033,7 @@ function createNgDocApplicationPlugin(options) {
11344
12033
  if (bundle[fileName]) {
11345
12034
  throw new Error(`[NGDOC_VITE_ASSET_COLLISION] Vite bundle already owns ${fileName}.`);
11346
12035
  }
11347
- this.emitFile({ type: "asset", fileName, source: await readFile7(file) });
12036
+ this.emitFile({ type: "asset", fileName, source: await readFile8(file) });
11348
12037
  }
11349
12038
  }
11350
12039
  };
@@ -11354,7 +12043,7 @@ function assetMiddleware(assets, base) {
11354
12043
  void assets().then(
11355
12044
  async (resolved) => {
11356
12045
  for (const match of matchAssets(resolved, base(), request.url ?? "/")) {
11357
- const source = await readFile7(match.file).catch(() => void 0);
12046
+ const source = await readFile8(match.file).catch(() => void 0);
11358
12047
  if (!source) continue;
11359
12048
  response.statusCode = 200;
11360
12049
  response.setHeader("Content-Type", contentType(match.relative));
@@ -11367,6 +12056,50 @@ function assetMiddleware(assets, base) {
11367
12056
  );
11368
12057
  };
11369
12058
  }
12059
+ function demoEntry(platform, module, options) {
12060
+ const imports = [...options.polyfills, ...options.styles].map(
12061
+ (item) => `import ${JSON.stringify(item)};`
12062
+ );
12063
+ const application = [
12064
+ `import { bootstrapApplication } from '@angular/platform-browser';`,
12065
+ `import { NgDocDemoAppComponent, \u0275ngDocDemoApplicationConfig } from '@ng-doc/app/demo-app';`,
12066
+ `import { NG_DOC_DEMO_PROVIDERS, NG_DOC_DEMO_ROUTES } from ${JSON.stringify(posix(module))};`
12067
+ ];
12068
+ if (platform === "browser") {
12069
+ return [
12070
+ ...imports,
12071
+ ...application,
12072
+ // A page the server rendered carries the serialized state (`<script id="ng-state">`, the
12073
+ // default APP_ID); the other pages (the copies of the demo page, the development server's)
12074
+ // start without hydration, as Angular warns (NG0505) when it finds no state.
12075
+ `\u0275ngDocDemoApplicationConfig(NG_DOC_DEMO_ROUTES, NG_DOC_DEMO_PROVIDERS, {`,
12076
+ ` zone: ${options.zone},`,
12077
+ ` hydration: document.getElementById('ng-state') !== null,`,
12078
+ `})`,
12079
+ ` .then((config) => bootstrapApplication(NgDocDemoAppComponent, config))`,
12080
+ ` .catch((error) => console.error(error));`
12081
+ ].join("\n");
12082
+ }
12083
+ return [
12084
+ ...imports,
12085
+ ...application,
12086
+ `import { provideServerRendering } from '@angular/platform-server';`,
12087
+ `export const bootstrap = async (context) =>`,
12088
+ ` bootstrapApplication(`,
12089
+ ` NgDocDemoAppComponent,`,
12090
+ ` await \u0275ngDocDemoApplicationConfig(NG_DOC_DEMO_ROUTES, NG_DOC_DEMO_PROVIDERS, {`,
12091
+ ` zone: ${options.zone},`,
12092
+ ` hydration: true,`,
12093
+ ` providers: [provideServerRendering()],`,
12094
+ ` }),`,
12095
+ ` context,`,
12096
+ ` );`,
12097
+ `export default bootstrap;`,
12098
+ `export { renderApplication } from '@angular/platform-server';`,
12099
+ `export { Router } from '@angular/router';`,
12100
+ `export { runInInjectionContext } from '@angular/core';`
12101
+ ].join("\n");
12102
+ }
11370
12103
 
11371
12104
  // libs/builder/generator/vite/prerender.ts
11372
12105
  import { fork as fork3 } from "node:child_process";
@@ -11385,7 +12118,8 @@ function prerenderNgDoc(options) {
11385
12118
  serverEntry: options.serverEntry,
11386
12119
  routes: [...options.routes ?? []],
11387
12120
  discoverRoutes: options.discoverRoutes ?? true,
11388
- ...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs }
12121
+ ...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
12122
+ ...options.demoServerEntry ? { demoServerEntry: options.demoServerEntry } : {}
11389
12123
  };
11390
12124
  if (request.routeTimeoutMs !== void 0 && (!Number.isSafeInteger(request.routeTimeoutMs) || request.routeTimeoutMs < 1)) {
11391
12125
  return Promise.reject(
@@ -11436,8 +12170,9 @@ function prerenderNgDoc(options) {
11436
12170
  }
11437
12171
 
11438
12172
  // libs/builder/generator/vite/production.ts
11439
- import { rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
11440
- import path23 from "node:path";
12173
+ import { existsSync as existsSync7 } from "node:fs";
12174
+ import { rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
12175
+ import path25 from "node:path";
11441
12176
  import { build as viteBuild } from "vite";
11442
12177
  var defaultDependencies = {
11443
12178
  build: viteBuild,
@@ -11474,11 +12209,11 @@ function applicationProbe() {
11474
12209
  async function buildNgDocViteApplication(options, dependencies = defaultDependencies) {
11475
12210
  assertSupportedViteVersion();
11476
12211
  assertConsistentAngularBuild();
11477
- const configFile = path23.resolve(options.configFile);
11478
- const outputPath = path23.resolve(options.outputPath);
11479
- const browser = path23.join(outputPath, "browser");
11480
- const serverDirectory = path23.join(outputPath, "server");
11481
- const serverEntry = path23.join(serverDirectory, "server.mjs");
12212
+ const configFile = path25.resolve(options.configFile);
12213
+ const outputPath = path25.resolve(options.outputPath);
12214
+ const browser = path25.join(outputPath, "browser");
12215
+ const serverDirectory = path25.join(outputPath, "server");
12216
+ const serverEntry = path25.join(serverDirectory, "server.mjs");
11482
12217
  const mode = options.mode ?? "production";
11483
12218
  const log = options.log ?? (() => {
11484
12219
  });
@@ -11515,7 +12250,7 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
11515
12250
  ssr ??= entry !== void 0;
11516
12251
  if (!ssr) {
11517
12252
  await rm2(serverDirectory, { recursive: true, force: true });
11518
- await rm2(path23.join(outputPath, "prerendered-routes.json"), { force: true });
12253
+ await rm2(path25.join(outputPath, "prerendered-routes.json"), { force: true });
11519
12254
  if (options.prerender === true) throw noServer();
11520
12255
  } else {
11521
12256
  if (!entry) {
@@ -11550,23 +12285,26 @@ async function buildNgDocViteApplication(options, dependencies = defaultDependen
11550
12285
  }
11551
12286
  const prerender = options.prerender ?? (options.skipBuild ? true : ssr === true);
11552
12287
  if (!prerender) return { browser, ...ssr ? { server: serverEntry } : {} };
12288
+ const demoServerEntry = path25.join(serverDirectory, "demo-server.mjs");
11553
12289
  const report = await timed(
11554
12290
  "Prerendered the routes",
11555
12291
  () => dependencies.prerender({
11556
12292
  browserDir: browser,
11557
12293
  serverEntry,
12294
+ ...existsSync7(demoServerEntry) && existsSync7(path25.join(browser, "ng-doc-demo.html")) ? { demoServerEntry } : {},
11558
12295
  routes: options.routes ?? [],
11559
12296
  discoverRoutes: options.discoverRoutes ?? true,
11560
12297
  ...options.routeTimeoutMs === void 0 ? {} : { routeTimeoutMs: options.routeTimeoutMs },
11561
12298
  ...options.signal ? { signal: options.signal } : {}
11562
12299
  })
11563
12300
  );
11564
- await writeFile2(
11565
- path23.join(outputPath, "prerendered-routes.json"),
12301
+ await writeFile3(
12302
+ path25.join(outputPath, "prerendered-routes.json"),
11566
12303
  `${JSON.stringify({ routes: report.routes, excluded: report.excluded }, null, 2)}
11567
12304
  `
11568
12305
  );
11569
12306
  log(`Prerendered ${report.routes.length} route(s) into ${browser}`);
12307
+ for (const { route, message: message4 } of report.warnings ?? []) log(`Warning: ${route}: ${message4}`);
11570
12308
  if (report.errors.length) {
11571
12309
  log(`The application logged ${report.errors.length} error(s) while prerendering:`);
11572
12310
  for (const { route, message: message4 } of report.errors.slice(0, 10)) log(` ${route}: ${message4}`);