@ng-doc/builder 22.0.0 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/engine/builders/helpers/get-playground-metadata.js +7 -1
  2. package/engine/builders/helpers/get-playground-metadata.js.map +1 -1
  3. package/engine/builders/page/demo-assets.builder.js +2 -0
  4. package/engine/builders/page/demo-assets.builder.js.map +1 -1
  5. package/engine/builders/shared/content.builder.js +2 -0
  6. package/engine/builders/shared/content.builder.js.map +1 -1
  7. package/engine/builders/shared/page-wrapper.builder.js +2 -0
  8. package/engine/builders/shared/page-wrapper.builder.js.map +1 -1
  9. package/generator/bootstrap/bin.js +27 -3
  10. package/generator/bootstrap/cli.js +27 -3
  11. package/generator/bootstrap/index.js +17 -3
  12. package/generator/build-metafile.json +529 -221
  13. package/generator/build-provenance.json +6 -4
  14. package/generator/compiler/index.d.ts +20 -0
  15. package/generator/compiler/index.js +1875 -625
  16. package/generator/compiler/plan.d.ts +24 -0
  17. package/generator/content/code-snippet.d.ts +20 -0
  18. package/generator/content/content-compiler.d.ts +5 -1
  19. package/generator/content/format-cache.d.ts +108 -0
  20. package/generator/content/highlight-cache.d.ts +12 -36
  21. package/generator/content/html-pipeline.d.ts +10 -1
  22. package/generator/content/html-pool.d.ts +8 -2
  23. package/generator/content/html-worker.js +622 -39
  24. package/generator/contracts.d.ts +39 -0
  25. package/generator/discovery/index.d.ts +2 -0
  26. package/generator/kernel/flags.d.ts +2 -0
  27. package/generator/outputs/index.d.ts +13 -0
  28. package/generator/semantic/angular.d.ts +21 -1
  29. package/generator/semantic/formatting.d.ts +27 -0
  30. package/generator/semantic/fragments.d.ts +11 -2
  31. package/generator/templates/api/class-declaration.html.nunj +5 -3
  32. package/generator/templates/demo-app.ts.nunj +37 -0
  33. package/generator/templates/demo-routes.ts.nunj +23 -0
  34. package/generator/templates/index.ts.nunj +5 -0
  35. package/generator/templates/page-wrapper.ts.nunj +6 -1
  36. package/generator/templates/page.ts.nunj +9 -2
  37. package/generator/templates/routes.ts.nunj +7 -3
  38. package/generator/templates/symbol/members.html.nunj +6 -4
  39. package/generator/templates/symbol/page.html.nunj +1 -1
  40. package/generator/vite/application-builder.js +6 -1
  41. package/generator/vite/application.d.ts +14 -0
  42. package/generator/vite/demo-application.d.ts +34 -0
  43. package/generator/vite/dev-server-builder.js +6 -1
  44. package/generator/vite/host-updates.d.ts +69 -2
  45. package/generator/vite/index.js +490 -130
  46. package/generator/vite/lifecycle.d.ts +1 -0
  47. package/generator/vite/prerender-entry.js +72 -2
  48. package/generator/vite/prerender-runtime.d.ts +13 -0
  49. package/generator/vite/prerender.d.ts +2 -0
  50. package/helpers/format-cache.d.ts +34 -0
  51. package/helpers/format-cache.js +26 -0
  52. package/helpers/format-cache.js.map +1 -0
  53. package/helpers/format-code.js +9 -2
  54. package/helpers/format-code.js.map +1 -1
  55. package/helpers/to-template-string.d.ts +5 -1
  56. package/helpers/to-template-string.js +7 -2
  57. package/helpers/to-template-string.js.map +1 -1
  58. package/interfaces/code-block-options.d.ts +5 -0
  59. package/interfaces/configuration.d.ts +84 -1
  60. package/package.json +5 -4
  61. package/parsers/parse-code-block-params.d.ts +10 -1
  62. package/parsers/parse-code-block-params.js +29 -3
  63. package/parsers/parse-code-block-params.js.map +1 -1
  64. package/schematics/collection.json +1 -1
  65. package/schematics/migrate-to-vite/analyze.js +46 -3
  66. package/schematics/migrate-to-vite/analyze.js.map +1 -1
  67. package/schematics/migrate-to-vite/setup/workspace.d.ts +5 -0
  68. package/schematics/migrate-to-vite/setup/workspace.js +20 -0
  69. package/schematics/migrate-to-vite/setup/workspace.js.map +1 -1
  70. package/schematics/migrate-to-vite/testing/fixtures.d.ts +12 -0
  71. package/schematics/migrate-to-vite/testing/fixtures.js +27 -0
  72. package/schematics/migrate-to-vite/testing/fixtures.js.map +1 -1
  73. package/schematics/vite-setup/index.d.ts +9 -5
  74. package/schematics/vite-setup/index.js +11 -5
  75. package/schematics/vite-setup/index.js.map +1 -1
  76. package/schematics/vite-setup/schema.json +1 -1
  77. package/templates/api/class-declaration.html.nunj +5 -3
  78. package/templates/demo-app.ts.nunj +37 -0
  79. package/templates/demo-routes.ts.nunj +23 -0
  80. package/templates/index.ts.nunj +5 -0
  81. package/templates/page-wrapper.ts.nunj +6 -1
  82. package/templates/page.ts.nunj +9 -2
  83. package/templates/routes.ts.nunj +7 -3
  84. package/templates/symbol/members.html.nunj +6 -4
  85. package/templates/symbol/page.html.nunj +1 -1
@@ -886,6 +886,329 @@ var init_helpers2 = __esm({
886
886
  }
887
887
  });
888
888
 
889
+ // libs/utils/html/plugins/keyword-positions.ts
890
+ function scanInlineCode(text) {
891
+ if (WHOLE_KEYWORD.test(text)) {
892
+ return { whole: true, position: () => REFERENCE };
893
+ }
894
+ if (HTML_START.test(text) || !(CODE_PUNCTUATION.test(text) || DECLARATION_START.test(text))) {
895
+ return void 0;
896
+ }
897
+ const scan = scanTypeScript(text);
898
+ return isProse(text, scan.masked) ? void 0 : scan;
899
+ }
900
+ function scanCodeBlock(text, mode) {
901
+ return mode === "html" ? scanHtml(text) : scanTypeScript(text);
902
+ }
903
+ function scanTypeScript(text) {
904
+ const masked = maskTypeScript(text);
905
+ const locals = localBindings(text, masked);
906
+ return {
907
+ whole: false,
908
+ masked,
909
+ position(start, end, key) {
910
+ if (masked[start] === TEMPLATE) {
911
+ const before = text[start - 1] ?? "";
912
+ const element = key.includes("-") && (before === "<" || before === "/" && text[start - 2] === "<");
913
+ const controlFlow = CONTROL_FLOW.test(key) && !WORD_CHARACTER.test(before);
914
+ return element || controlFlow ? REFERENCE : NOT_A_REFERENCE;
915
+ }
916
+ if (masked[start] !== CODE) {
917
+ return NOT_A_REFERENCE;
918
+ }
919
+ const previous = previousSignificant(text, start, masked);
920
+ const next = nextSignificant(text, end, masked);
921
+ const nextCharacter = text[next] ?? "";
922
+ if (SYNTAX_WORDS.has(key) || RESERVED_WORDS.has(key) && nextCharacter !== "(") {
923
+ return NOT_A_REFERENCE;
924
+ }
925
+ if (text[previous] === "." && text[previous - 1] !== ".") {
926
+ const ownerEnd = text[previous - 1] === "?" ? previous - 1 : previous;
927
+ const owner = text.slice(0, ownerEnd).match(IDENTIFIER_BEFORE)?.[0];
928
+ const word = text.slice(start, end);
929
+ return owner && !/[.#]/.test(word) ? { link: true, keyword: `${owner}.${key.toLowerCase()}` } : NOT_A_REFERENCE;
930
+ }
931
+ if (isDeclaredName(text, masked, previous, next)) {
932
+ return NOT_A_REFERENCE;
933
+ }
934
+ return locals.has(key) ? NOT_A_REFERENCE : REFERENCE;
935
+ }
936
+ };
937
+ }
938
+ function isDeclaredName(text, masked, previous, next) {
939
+ const after = text[next];
940
+ const afterNext = text[nextSignificant(text, next + 1, masked)];
941
+ if (after === ":" && text[next + 1] !== ":") {
942
+ const ternary = text[previous] === "?" && text[previous + 1] !== ".";
943
+ return !ternary && previousWord(text, previous) !== "case";
944
+ }
945
+ if ((after === "?" || after === "!") && afterNext === ":") {
946
+ return true;
947
+ }
948
+ if (after === "?" && (afterNext === ")" || afterNext === ",")) {
949
+ return true;
950
+ }
951
+ if (after === "=" && text[next + 1] !== "=" && text[next + 1] !== ">") {
952
+ return !TYPE_CONTEXT.includes(text[previous] ?? "") && !TYPE_WORDS.includes(previousWord(text, previous));
953
+ }
954
+ return false;
955
+ }
956
+ function scanHtml(text) {
957
+ const masked = maskHtml(text);
958
+ return {
959
+ whole: false,
960
+ position: (start) => masked[start] === CODE ? REFERENCE : NOT_A_REFERENCE
961
+ };
962
+ }
963
+ function maskTypeScript(text) {
964
+ const masked = new Uint8Array(text.length);
965
+ const templates = [];
966
+ let depth = 0;
967
+ let index = 0;
968
+ const string2 = (quote) => {
969
+ const start = index++;
970
+ while (index < text.length && text[index] !== quote && text[index] !== "\n") {
971
+ index += text[index] === "\\" ? 2 : 1;
972
+ }
973
+ masked.fill(STRING, start, Math.min(++index, text.length));
974
+ };
975
+ const template = () => {
976
+ const start = index++;
977
+ while (index < text.length && text[index] !== "`") {
978
+ if (text[index] === "\\") {
979
+ index += 2;
980
+ } else if (text[index] === "$" && text[index + 1] === "{") {
981
+ masked.fill(TEMPLATE, start, index);
982
+ templates.push(depth);
983
+ index += 2;
984
+ return;
985
+ } else {
986
+ index++;
987
+ }
988
+ }
989
+ masked.fill(TEMPLATE, start, Math.min(++index, text.length));
990
+ };
991
+ while (index < text.length) {
992
+ const character = text[index];
993
+ if (character === "/" && text[index + 1] === "/") {
994
+ const end = text.indexOf("\n", index);
995
+ const stop = end < 0 ? text.length : end;
996
+ masked.fill(COMMENT, index, stop);
997
+ index = stop;
998
+ } else if (character === "/" && text[index + 1] === "*") {
999
+ const end = text.indexOf("*/", index + 2);
1000
+ const stop = end < 0 ? text.length : end + 2;
1001
+ masked.fill(COMMENT, index, stop);
1002
+ index = stop;
1003
+ } else if (character === "'" || character === '"') {
1004
+ string2(character);
1005
+ } else if (character === "`") {
1006
+ template();
1007
+ } else if (character === "{") {
1008
+ depth++;
1009
+ index++;
1010
+ } else if (character === "}") {
1011
+ if (templates.length && templates[templates.length - 1] === depth) {
1012
+ templates.pop();
1013
+ template();
1014
+ } else {
1015
+ depth--;
1016
+ index++;
1017
+ }
1018
+ } else {
1019
+ index++;
1020
+ }
1021
+ }
1022
+ return masked;
1023
+ }
1024
+ function maskHtml(text) {
1025
+ const masked = new Uint8Array(text.length);
1026
+ let index = 0;
1027
+ while (index < text.length) {
1028
+ if (text.startsWith("<!--", index)) {
1029
+ const end = text.indexOf("-->", index + 4);
1030
+ const stop = end < 0 ? text.length : end + 3;
1031
+ masked.fill(COMMENT, index, stop);
1032
+ index = stop;
1033
+ } else if (text[index] === "<" && /[A-Za-z/]/.test(text[index + 1] ?? "")) {
1034
+ index = maskTag(text, index + 1, masked);
1035
+ } else {
1036
+ index++;
1037
+ }
1038
+ }
1039
+ return masked;
1040
+ }
1041
+ function maskTag(text, index, masked) {
1042
+ while (index < text.length && text[index] !== ">") {
1043
+ if (text[index] !== "=") {
1044
+ index++;
1045
+ continue;
1046
+ }
1047
+ index++;
1048
+ while (text[index] === " " || text[index] === " " || text[index] === "\n") index++;
1049
+ const quote = text[index];
1050
+ const start = index;
1051
+ if (quote === '"' || quote === "'") {
1052
+ const end = text.indexOf(quote, index + 1);
1053
+ index = end < 0 ? text.length : end + 1;
1054
+ } else {
1055
+ while (index < text.length && !/[\s>]/.test(text[index] ?? "")) index++;
1056
+ }
1057
+ masked.fill(STRING, start, index);
1058
+ }
1059
+ return index + 1;
1060
+ }
1061
+ function localBindings(text, masked) {
1062
+ const names = /* @__PURE__ */ new Set();
1063
+ for (const match of text.matchAll(/\b(?:const|let|var)\s+([\p{L}_$][\p{L}\w$]*)/gu)) {
1064
+ if (!masked[match.index]) names.add(match[1] ?? "");
1065
+ }
1066
+ for (const match of text.matchAll(
1067
+ /\bimport\s+(?:type\s+)?([^;]*?)\s+from\s+(['"])\.{1,2}\/[^'"]*\2/gu
1068
+ )) {
1069
+ if (masked[match.index]) continue;
1070
+ for (const name of (match[1] ?? "").matchAll(
1071
+ /([\p{L}_$][\p{L}\w$]*)(?:\s+as\s+([\p{L}_$][\p{L}\w$]*))?/gu
1072
+ )) {
1073
+ const local = name[2] ?? name[1] ?? "";
1074
+ if (local !== "type" && local !== "as") names.add(local);
1075
+ }
1076
+ }
1077
+ return names;
1078
+ }
1079
+ function isProse(text, masked) {
1080
+ for (const match of text.matchAll(/[\p{L}_$][\p{L}\w$]*(?: [\p{L}_$][\p{L}\w$]*){2,}/gu)) {
1081
+ const words2 = match[0].split(" ");
1082
+ const plain = words2.filter((word) => !SYNTAX_WORDS.has(word) && !RESERVED_WORDS.has(word));
1083
+ if (!masked[match.index] && plain.length >= 3) {
1084
+ return true;
1085
+ }
1086
+ }
1087
+ return false;
1088
+ }
1089
+ function previousSignificant(text, index, masked) {
1090
+ let position = index - 1;
1091
+ while (position >= 0 && insignificant(text, position, masked)) position--;
1092
+ return position;
1093
+ }
1094
+ function nextSignificant(text, index, masked) {
1095
+ let position = index;
1096
+ while (position < text.length && insignificant(text, position, masked)) position++;
1097
+ return position;
1098
+ }
1099
+ function insignificant(text, index, masked) {
1100
+ return masked[index] === COMMENT || /\s/.test(text[index] ?? "");
1101
+ }
1102
+ function previousWord(text, index) {
1103
+ let start = index;
1104
+ while (start >= 0 && WORD_CHARACTER.test(text[start] ?? "")) start--;
1105
+ return text.slice(start + 1, index + 1);
1106
+ }
1107
+ var WORD_CHARACTER, IDENTIFIER_BEFORE, WHOLE_KEYWORD, CODE_PUNCTUATION, DECLARATION_START, HTML_START, SYNTAX_WORDS, RESERVED_WORDS, TYPE_CONTEXT, TYPE_WORDS, CODE, STRING, COMMENT, TEMPLATE, CONTROL_FLOW, NOT_A_REFERENCE, REFERENCE;
1108
+ var init_keyword_positions = __esm({
1109
+ "libs/utils/html/plugins/keyword-positions.ts"() {
1110
+ "use strict";
1111
+ WORD_CHARACTER = /[*\p{L}\w$@-]/u;
1112
+ IDENTIFIER_BEFORE = /[\p{L}\w$@-]+$/u;
1113
+ WHOLE_KEYWORD = /^\s*[*\p{L}\w$@-]+(?:[.#][\p{L}\w-]+)?(?:\?[\w=&]+)?\s*$/u;
1114
+ CODE_PUNCTUATION = /[(){}[\]<>|:=@]/;
1115
+ DECLARATION_START = /^\s*(?:export|import|class|interface|type|const|let|var|function|enum|abstract|declare)\s/;
1116
+ HTML_START = /^\s*<\/?[A-Za-z]/;
1117
+ SYNTAX_WORDS = /* @__PURE__ */ new Set([
1118
+ "await",
1119
+ "break",
1120
+ "case",
1121
+ "catch",
1122
+ "continue",
1123
+ "debugger",
1124
+ "delete",
1125
+ "do",
1126
+ "else",
1127
+ "false",
1128
+ "finally",
1129
+ "for",
1130
+ "function",
1131
+ "if",
1132
+ "instanceof",
1133
+ "new",
1134
+ "null",
1135
+ "return",
1136
+ "super",
1137
+ "switch",
1138
+ "this",
1139
+ "throw",
1140
+ "true",
1141
+ "try",
1142
+ "typeof",
1143
+ "void",
1144
+ "while",
1145
+ "with",
1146
+ "yield"
1147
+ ]);
1148
+ RESERVED_WORDS = /* @__PURE__ */ new Set([
1149
+ "abstract",
1150
+ "accessor",
1151
+ "any",
1152
+ "as",
1153
+ "asserts",
1154
+ "async",
1155
+ "bigint",
1156
+ "boolean",
1157
+ "class",
1158
+ "const",
1159
+ "constructor",
1160
+ "declare",
1161
+ "default",
1162
+ "enum",
1163
+ "export",
1164
+ "extends",
1165
+ "from",
1166
+ "get",
1167
+ "implements",
1168
+ "import",
1169
+ "in",
1170
+ "infer",
1171
+ "interface",
1172
+ "is",
1173
+ "keyof",
1174
+ "let",
1175
+ "module",
1176
+ "namespace",
1177
+ "never",
1178
+ "number",
1179
+ "object",
1180
+ "of",
1181
+ "out",
1182
+ "override",
1183
+ "package",
1184
+ "private",
1185
+ "protected",
1186
+ "public",
1187
+ "readonly",
1188
+ "require",
1189
+ "satisfies",
1190
+ "set",
1191
+ "static",
1192
+ "string",
1193
+ "symbol",
1194
+ "type",
1195
+ "undefined",
1196
+ "unique",
1197
+ "unknown",
1198
+ "var"
1199
+ ]);
1200
+ TYPE_CONTEXT = [":", "|", "&", "<"];
1201
+ TYPE_WORDS = ["type", "extends", "keyof", "typeof"];
1202
+ CODE = 0;
1203
+ STRING = 1;
1204
+ COMMENT = 2;
1205
+ TEMPLATE = 3;
1206
+ CONTROL_FLOW = /^@[a-z]+$/;
1207
+ NOT_A_REFERENCE = Object.freeze({ link: false });
1208
+ REFERENCE = Object.freeze({ link: true });
1209
+ }
1210
+ });
1211
+
889
1212
  // libs/utils/html/plugins/keywords.plugin.ts
890
1213
  import { isElement as isElement5 } from "hast-util-is-element";
891
1214
  import { toString } from "hast-util-to-string";
@@ -897,23 +1220,50 @@ function keywordsPlugin(config) {
897
1220
  }
898
1221
  const isInlineCode = !isElement5(ancestors[ancestors.length - 1], "pre");
899
1222
  const lang = asArray(node.properties?.["className"] ?? []).find((className) => className.startsWith("language-"))?.replace("language-", "") ?? "";
900
- if (isInlineCode || LANGUAGES.includes(lang)) {
901
- visitParents(node, "text", (node2, ancestors2) => {
902
- if (hasLinkAncestor(ancestors2)) {
903
- return;
904
- }
905
- const parent = ancestors2[ancestors2.length - 1];
906
- const index = parent.children.indexOf(node2);
907
- const nodes = getNodes(node2, parent, isInlineCode, config, lang);
908
- Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
909
- return [SKIP, index + nodes.length];
910
- });
1223
+ if (!isInlineCode && !LANGUAGES.includes(lang)) {
1224
+ return;
1225
+ }
1226
+ const offsets = textOffsets(node);
1227
+ const text = Array.from(offsets.keys()).map((textNode) => textNode.value).join("");
1228
+ const scan = isInlineCode ? scanInlineCode(text) : scanCodeBlock(text, HTML_LANGUAGES.includes(lang) ? "html" : "typescript");
1229
+ if (!scan) {
1230
+ return;
911
1231
  }
1232
+ visitParents(node, "text", (node2, ancestors2) => {
1233
+ if (hasLinkAncestor(ancestors2)) {
1234
+ return;
1235
+ }
1236
+ const parent = ancestors2[ancestors2.length - 1];
1237
+ const index = parent.children.indexOf(node2);
1238
+ const nodes = getNodes(
1239
+ node2,
1240
+ parent,
1241
+ isInlineCode,
1242
+ config,
1243
+ lang,
1244
+ scan,
1245
+ offsets.get(node2) ?? 0
1246
+ );
1247
+ Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
1248
+ return [SKIP, index + nodes.length];
1249
+ });
1250
+ });
1251
+ }
1252
+ function textOffsets(node) {
1253
+ const offsets = /* @__PURE__ */ new Map();
1254
+ let offset = 0;
1255
+ visitParents(node, "text", (text) => {
1256
+ offsets.set(text, offset);
1257
+ offset += text.value.length;
912
1258
  });
1259
+ return offsets;
913
1260
  }
914
- function getNodes(node, parent, isInlineCode, config, language) {
1261
+ function getNodes(node, parent, isInlineCode, config, language, scan, offset) {
915
1262
  const { addUsedKeyword, getKeyword } = config;
1263
+ let start = offset;
916
1264
  return toString(node).split(SPLIT_REGEXP).map((word) => {
1265
+ const wordStart = start;
1266
+ start += word.length;
917
1267
  const match = word.match(MATCH_KEYWORD_REGEXP);
918
1268
  if (!match) {
919
1269
  return { type: "text", value: word };
@@ -924,8 +1274,13 @@ function getNodes(node, parent, isInlineCode, config, language) {
924
1274
  anchor = "",
925
1275
  queryParams = ""
926
1276
  } = match.groups;
927
- const usedKeyword = `${key}${delimiter}${anchor?.toLowerCase()}`;
928
- const rootKeyword2 = getKeyword?.(key);
1277
+ const position = scan.position(wordStart, wordStart + word.length, key);
1278
+ if (!position.link) {
1279
+ return { type: "text", value: word };
1280
+ }
1281
+ const isMember = position.keyword !== void 0;
1282
+ const usedKeyword = position.keyword ?? `${key}${delimiter}${anchor?.toLowerCase()}`;
1283
+ const rootKeyword2 = isMember ? void 0 : getKeyword?.(key);
929
1284
  const keyword = getKeyword?.(usedKeyword);
930
1285
  const isGuideKeyword = key.startsWith("*");
931
1286
  const isLanguageAllowed = !keyword?.languages && ALWAYS_ALLOWED_LANGUAGES.includes(language) || !!keyword?.languages?.includes(language);
@@ -938,7 +1293,7 @@ function getNodes(node, parent, isInlineCode, config, language) {
938
1293
  addUsedKeyword?.(usedKeyword);
939
1294
  const notFoundGuideKeyword = isGuideKeyword && !keyword;
940
1295
  const notFoundApiAnchorKeyword = !!rootKeyword2 && !!anchor && !keyword;
941
- if (getKeyword && isInlineCode && (notFoundGuideKeyword || notFoundApiAnchorKeyword)) {
1296
+ if (getKeyword && scan.whole && (notFoundGuideKeyword || notFoundApiAnchorKeyword)) {
942
1297
  throw new Error(`Route with keyword "${word}" is missing.`);
943
1298
  }
944
1299
  if (parent.properties) {
@@ -954,7 +1309,7 @@ function getNodes(node, parent, isInlineCode, config, language) {
954
1309
  }
955
1310
  }
956
1311
  return keyword ? createLinkNode(
957
- isInlineCode ? keyword.title : word,
1312
+ isInlineCode && !isMember ? keyword.title : word,
958
1313
  keyword.path,
959
1314
  keyword.type,
960
1315
  keyword.description
@@ -974,14 +1329,16 @@ function createLinkNode(text, href, type, description) {
974
1329
  children: [{ type: "text", value: text }]
975
1330
  };
976
1331
  }
977
- var ALWAYS_ALLOWED_LANGUAGES, LANGUAGES, SPLIT_REGEXP, MATCH_KEYWORD_REGEXP;
1332
+ var ALWAYS_ALLOWED_LANGUAGES, HTML_LANGUAGES, LANGUAGES, SPLIT_REGEXP, MATCH_KEYWORD_REGEXP;
978
1333
  var init_keywords_plugin = __esm({
979
1334
  "libs/utils/html/plugins/keywords.plugin.ts"() {
980
1335
  "use strict";
981
1336
  init_core();
982
1337
  init_helpers2();
1338
+ init_keyword_positions();
983
1339
  ALWAYS_ALLOWED_LANGUAGES = ["typescript", "ts", "angular-ts"];
984
- LANGUAGES = ["typescript", "ts", "angular-ts", ...KEYWORD_ALLOWED_LANGUAGES];
1340
+ HTML_LANGUAGES = [...KEYWORD_ALLOWED_LANGUAGES];
1341
+ LANGUAGES = [...ALWAYS_ALLOWED_LANGUAGES, ...HTML_LANGUAGES];
985
1342
  SPLIT_REGEXP = /([*\p{L}\w$@-]+(?:[.#][\p{L}\w-]+)?(?:\?[\w=&]+)?)/u;
986
1343
  MATCH_KEYWORD_REGEXP = /(?<key>[*\p{L}\w$@-]+)((?<delimiter>[.#])(?<anchor>[\p{L}\w-]+))?(?<queryParams>\?[\w=&]+)?/u;
987
1344
  }
@@ -1039,13 +1396,14 @@ var init_add_heading_anchors_plugin = __esm({
1039
1396
  });
1040
1397
 
1041
1398
  // libs/utils/html/plugins/cached-shiki.plugin.ts
1042
- import rehypeShiki from "@shikijs/rehype";
1043
1399
  import { toString as toString2 } from "hast-util-to-string";
1044
1400
  import { visit as visit2 } from "unist-util-visit";
1045
1401
  function cachedShikiPlugin(settings2) {
1046
- const { cache, themes, identity, defaultLanguage } = settings2;
1402
+ const { cache, themes, languages, identity, defaultLanguage } = settings2;
1047
1403
  const id = `${themes.light}
1048
- ${themes.dark}`;
1404
+ ${themes.dark}
1405
+ ${languages ?? ""}
1406
+ ${settings2.grammars ?? "all"}`;
1049
1407
  return async (tree2) => {
1050
1408
  const blocks = [];
1051
1409
  visit2(tree2, "element", (node, index, parent) => {
@@ -1062,11 +1420,18 @@ ${themes.dark}`;
1062
1420
  blocks.push({
1063
1421
  node,
1064
1422
  parent,
1065
- key: cache.key({ options: identity, themes, lang, meta, code })
1423
+ key: cache.key({
1424
+ options: identity,
1425
+ themes,
1426
+ ...languages === void 0 ? {} : { languages },
1427
+ lang,
1428
+ meta,
1429
+ code
1430
+ })
1066
1431
  });
1067
1432
  });
1068
1433
  let highlighter;
1069
- const load = async () => highlighter ??= await highlighterFor(id, settings2.options);
1434
+ const load = async () => highlighter ??= await highlighterFor(id, settings2.transformer);
1070
1435
  if (cache.loads?.(themes) !== true) await load();
1071
1436
  const values = [];
1072
1437
  for (const block of blocks) {
@@ -1087,13 +1452,11 @@ ${themes.dark}`;
1087
1452
  });
1088
1453
  };
1089
1454
  }
1090
- function highlighterFor(id, options) {
1455
+ function highlighterFor(id, transformer) {
1091
1456
  const known3 = highlighters.get(id);
1092
1457
  if (known3) return known3;
1093
1458
  const created = (async () => {
1094
- const transform = rehypeShiki(
1095
- options()
1096
- );
1459
+ const transform = transformer();
1097
1460
  await transform({ type: "root", children: [] });
1098
1461
  return transform;
1099
1462
  })();
@@ -1111,11 +1474,18 @@ async function highlight(transform, node) {
1111
1474
  function fragment(value) {
1112
1475
  try {
1113
1476
  const parsed = JSON.parse(value);
1114
- return Array.isArray(parsed) && parsed.length > 0 && parsed.every((item) => isNode(item) && item.type === "element") ? parsed : void 0;
1477
+ return Array.isArray(parsed) && parsed.length > 0 && parsed.every(isReplacement) ? parsed : void 0;
1115
1478
  } catch {
1116
1479
  return void 0;
1117
1480
  }
1118
1481
  }
1482
+ function isReplacement(value) {
1483
+ if (isNode(value)) return value.type === "element";
1484
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1485
+ const node = value;
1486
+ const children = node["children"];
1487
+ return node["type"] === "root" && Array.isArray(children) && children.length > 0 && children.every((child) => isNode(child) && child.type === "element");
1488
+ }
1119
1489
  function isNode(value) {
1120
1490
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1121
1491
  const node = value;
@@ -1308,11 +1678,158 @@ var init_table_wrapper = __esm({
1308
1678
  }
1309
1679
  });
1310
1680
 
1681
+ // libs/utils/html/shiki-grammars.ts
1682
+ import {
1683
+ bundledLanguages,
1684
+ createHighlighter
1685
+ } from "shiki";
1686
+ import { visit as visit7 } from "unist-util-visit";
1687
+ function shikiGrammars() {
1688
+ grammars ??= ShikiGrammars.create().catch((error) => {
1689
+ grammars = void 0;
1690
+ throw error;
1691
+ });
1692
+ return grammars;
1693
+ }
1694
+ function createGrammarlessHighlighter(themes) {
1695
+ return createHighlighter({ themes, langs: [] });
1696
+ }
1697
+ function blockLanguages(tree2, defaultLanguage) {
1698
+ const languages = /* @__PURE__ */ new Set();
1699
+ visit7(tree2, "element", (node) => {
1700
+ if (node.tagName !== "pre") return;
1701
+ const head = node.children[0];
1702
+ if (!head || head.type !== "element" || head.tagName !== "code" || !head.properties) return;
1703
+ const classes = head.properties["className"];
1704
+ const languageClass = Array.isArray(classes) ? classes.find((item) => typeof item === "string" && item.startsWith(languagePrefix2)) : void 0;
1705
+ languages.add(
1706
+ typeof languageClass === "string" ? languageClass.slice(languagePrefix2.length) : defaultLanguage
1707
+ );
1708
+ });
1709
+ return languages;
1710
+ }
1711
+ function includedScopes(registration) {
1712
+ const scopes = /* @__PURE__ */ new Set();
1713
+ const walk = (value) => {
1714
+ if (Array.isArray(value)) value.forEach(walk);
1715
+ else if (value && typeof value === "object")
1716
+ for (const [key, item] of Object.entries(value)) {
1717
+ if (key === "include" && typeof item === "string") {
1718
+ if (!item.startsWith("#") && !item.startsWith("$")) scopes.add(item.split("#")[0]);
1719
+ } else walk(item);
1720
+ }
1721
+ };
1722
+ walk(registration);
1723
+ return scopes;
1724
+ }
1725
+ function prefixes(scope) {
1726
+ const parts = scope.split(".");
1727
+ return parts.map((_, index) => parts.slice(0, index + 1).join("."));
1728
+ }
1729
+ var ShikiGrammars, grammars, languagePrefix2;
1730
+ var init_shiki_grammars = __esm({
1731
+ "libs/utils/html/shiki-grammars.ts"() {
1732
+ "use strict";
1733
+ ShikiGrammars = class _ShikiGrammars {
1734
+ constructor(registrations) {
1735
+ this.byName = /* @__PURE__ */ new Map();
1736
+ this.byScope = /* @__PURE__ */ new Map();
1737
+ this.injectors = /* @__PURE__ */ new Map();
1738
+ this.order = /* @__PURE__ */ new Map();
1739
+ this.closures = /* @__PURE__ */ new Map();
1740
+ this.loaded = /* @__PURE__ */ new WeakMap();
1741
+ const mention = (name) => {
1742
+ if (!this.order.has(name)) this.order.set(name, this.order.size);
1743
+ };
1744
+ for (const registration of registrations) {
1745
+ mention(registration.name);
1746
+ for (const embedded of registration.embeddedLangs ?? []) mention(embedded);
1747
+ if (this.byScope.has(registration.scopeName)) continue;
1748
+ this.byScope.set(registration.scopeName, registration);
1749
+ this.byName.set(registration.name, registration);
1750
+ for (const alias of registration.aliases ?? []) this.byName.set(alias, registration);
1751
+ for (const target of registration.injectTo ?? []) {
1752
+ const list = this.injectors.get(target) ?? [];
1753
+ list.push(registration);
1754
+ this.injectors.set(target, list);
1755
+ }
1756
+ }
1757
+ }
1758
+ /**
1759
+ * The bundled grammars, in the order `@shikijs/rehype` registers them: every bundled language
1760
+ * name, each with the registrations of its module.
1761
+ */
1762
+ static async create() {
1763
+ const modules = await Promise.all(
1764
+ Object.keys(bundledLanguages).map(
1765
+ (name) => bundledLanguages[name]()
1766
+ )
1767
+ );
1768
+ return new _ShikiGrammars(modules.flatMap((module) => module.default));
1769
+ }
1770
+ /**
1771
+ * The grammars that a block of `language` can reach, in registration order; empty for a
1772
+ * language that no bundled grammar names (it falls back to plain text, as before).
1773
+ * @param language
1774
+ */
1775
+ closure(language) {
1776
+ const known3 = this.closures.get(language);
1777
+ if (known3) return known3;
1778
+ const set = /* @__PURE__ */ new Set();
1779
+ const queue = [this.byName.get(language)];
1780
+ while (queue.length) {
1781
+ const registration = queue.pop();
1782
+ if (!registration || set.has(registration)) continue;
1783
+ set.add(registration);
1784
+ for (const name of [
1785
+ ...registration.embeddedLangs ?? [],
1786
+ ...registration.embeddedLangsLazy ?? []
1787
+ ])
1788
+ queue.push(this.byName.get(name));
1789
+ for (const scope of includedScopes(registration)) queue.push(this.byScope.get(scope));
1790
+ for (const prefix of prefixes(registration.scopeName))
1791
+ queue.push(...this.injectors.get(prefix) ?? []);
1792
+ for (const target of registration.injectTo ?? [])
1793
+ queue.push(...this.injectors.get(target) ?? []);
1794
+ }
1795
+ const closure = [...set].sort(
1796
+ (left, right) => this.order.get(left.name) - this.order.get(right.name)
1797
+ );
1798
+ this.closures.set(language, closure);
1799
+ return closure;
1800
+ }
1801
+ /**
1802
+ * Loads the closures of `languages` that `highlighter` does not hold yet.
1803
+ * @param highlighter
1804
+ * @param languages
1805
+ */
1806
+ load(highlighter, languages) {
1807
+ let loaded = this.loaded.get(highlighter);
1808
+ if (!loaded) this.loaded.set(highlighter, loaded = /* @__PURE__ */ new Set());
1809
+ const missing = /* @__PURE__ */ new Set();
1810
+ for (const language of languages)
1811
+ for (const registration of this.closure(language))
1812
+ if (!loaded.has(registration.name)) missing.add(registration);
1813
+ if (!missing.size) return;
1814
+ const batch = [...missing].sort(
1815
+ (left, right) => this.order.get(left.name) - this.order.get(right.name)
1816
+ );
1817
+ highlighter.loadLanguageSync(batch);
1818
+ for (const registration of batch) loaded.add(registration.name);
1819
+ }
1820
+ };
1821
+ languagePrefix2 = "language-";
1822
+ }
1823
+ });
1824
+
1311
1825
  // libs/utils/html/process-html.ts
1312
- import rehypeShiki2 from "@shikijs/rehype";
1826
+ import rehypeShiki from "@shikijs/rehype";
1827
+ import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
1828
+ import { createHash as createHash2 } from "node:crypto";
1313
1829
  import rehypeMinifyWhitespace2 from "rehype-minify-whitespace";
1314
1830
  import rehypeParse3 from "rehype-parse";
1315
1831
  import rehypeStringify3 from "rehype-stringify";
1832
+ import { bundledLanguages as bundledLanguages2, createHighlighter as createHighlighter2 } from "shiki";
1316
1833
  import { unified as unified3 } from "unified";
1317
1834
  async function processHtml(html, config) {
1318
1835
  const anchors = /* @__PURE__ */ new Set();
@@ -1320,6 +1837,8 @@ async function processHtml(html, config) {
1320
1837
  light: config.lightTheme ?? "github-light",
1321
1838
  dark: config.darkTheme ?? "ayu-dark"
1322
1839
  };
1840
+ const langs = config.langs?.length ? config.langs : void 0;
1841
+ const used = !langs && config.grammars === "used";
1323
1842
  try {
1324
1843
  const content = await unified3().use(rehypeParse3, { fragment: true }).use(rehypeStringify3).use(mermaidPlugin).use(
1325
1844
  config.highlight ? [
@@ -1328,12 +1847,16 @@ async function processHtml(html, config) {
1328
1847
  {
1329
1848
  cache: config.highlight,
1330
1849
  themes,
1850
+ ...langs ? { languages: languagesId(langs) } : {},
1851
+ ...used ? { grammars: "used" } : {},
1331
1852
  identity: HIGHLIGHT_IDENTITY,
1332
1853
  defaultLanguage: DEFAULT_LANGUAGE,
1333
- options: () => shikiOptions(themes)
1854
+ transformer: () => langs ? withLanguages(themes, langs) : used ? withUsedGrammars(themes) : rehypeShiki(
1855
+ shikiOptions(themes)
1856
+ )
1334
1857
  }
1335
1858
  ]
1336
- ] : [[rehypeShiki2, shikiOptions(themes)]]
1859
+ ] : langs ? [[() => withLanguages(themes, langs)]] : used ? [[() => withUsedGrammars(themes)]] : [[rehypeShiki, shikiOptions(themes)]]
1337
1860
  ).use(highlightCodeLines).use(table_wrapper_default).use(sluggerPlugin, anchors.add.bind(anchors), config.headings).use(rehypeMinifyWhitespace2).use(addHeadingAnchors, config.route).use(markElementsPlugin).process(html).then((file) => file.toString());
1338
1861
  return { content, anchors: Array.from(anchors) };
1339
1862
  } catch (error) {
@@ -1359,7 +1882,59 @@ function shikiOptions(themes) {
1359
1882
  function shikiTheme(name) {
1360
1883
  return name === NG_DOC_SYNTAX_THEME_NAME ? ngDocSyntaxTheme() : name;
1361
1884
  }
1362
- var DEFAULT_LANGUAGE, HIGHLIGHT_IDENTITY;
1885
+ function languagesId(langs) {
1886
+ let id = languageIds.get(langs);
1887
+ if (id === void 0) {
1888
+ id = createHash2("sha256").update(JSON.stringify(langs)).digest("hex");
1889
+ languageIds.set(langs, id);
1890
+ }
1891
+ return id;
1892
+ }
1893
+ function withLanguages(themes, langs) {
1894
+ const id = `${themes.light}
1895
+ ${themes.dark}
1896
+ ${languagesId(langs)}`;
1897
+ return async (tree2) => {
1898
+ let highlighter = languageHighlighters.get(id);
1899
+ if (!highlighter) {
1900
+ highlighter = createHighlighter2({
1901
+ themes: [shikiTheme(themes.light), shikiTheme(themes.dark)],
1902
+ // Later registrations of a name replace earlier ones, so the given languages win.
1903
+ langs: [...Object.keys(bundledLanguages2), ...langs]
1904
+ });
1905
+ languageHighlighters.set(id, highlighter);
1906
+ const created = highlighter;
1907
+ created.catch(() => {
1908
+ if (languageHighlighters.get(id) === created) languageHighlighters.delete(id);
1909
+ });
1910
+ }
1911
+ const transform = rehypeShikiFromHighlighter(await highlighter, shikiOptions(themes));
1912
+ await transform(tree2);
1913
+ };
1914
+ }
1915
+ function withUsedGrammars(themes) {
1916
+ const id = `${themes.light}
1917
+ ${themes.dark}`;
1918
+ return async (tree2) => {
1919
+ let highlighter = usedHighlighters.get(id);
1920
+ if (!highlighter) {
1921
+ highlighter = createGrammarlessHighlighter([
1922
+ shikiTheme(themes.light),
1923
+ shikiTheme(themes.dark)
1924
+ ]);
1925
+ usedHighlighters.set(id, highlighter);
1926
+ const created = highlighter;
1927
+ created.catch(() => {
1928
+ if (usedHighlighters.get(id) === created) usedHighlighters.delete(id);
1929
+ });
1930
+ }
1931
+ const [grammars2, ready] = await Promise.all([shikiGrammars(), highlighter]);
1932
+ grammars2.load(ready, blockLanguages(tree2, DEFAULT_LANGUAGE));
1933
+ const transform = rehypeShikiFromHighlighter(ready, shikiOptions(themes));
1934
+ await transform(tree2);
1935
+ };
1936
+ }
1937
+ var DEFAULT_LANGUAGE, HIGHLIGHT_IDENTITY, languageHighlighters, languageIds, usedHighlighters;
1363
1938
  var init_process_html = __esm({
1364
1939
  "libs/utils/html/process-html.ts"() {
1365
1940
  "use strict";
@@ -1371,24 +1946,28 @@ var init_process_html = __esm({
1371
1946
  init_mermaid_plugin();
1372
1947
  init_slugger_plugin();
1373
1948
  init_table_wrapper();
1949
+ init_shiki_grammars();
1374
1950
  DEFAULT_LANGUAGE = "ts";
1375
1951
  HIGHLIGHT_IDENTITY = {
1376
- format: 1,
1952
+ format: 2,
1377
1953
  defaultLanguage: DEFAULT_LANGUAGE,
1378
1954
  fallbackLanguage: "text",
1379
1955
  addLanguageClass: true,
1380
1956
  parseMetaString: "json-without-backslashes",
1381
1957
  tokenizeTimeLimit: 0
1382
1958
  };
1959
+ languageHighlighters = /* @__PURE__ */ new Map();
1960
+ languageIds = /* @__PURE__ */ new WeakMap();
1961
+ usedHighlighters = /* @__PURE__ */ new Map();
1383
1962
  }
1384
1963
  });
1385
1964
 
1386
1965
  // libs/utils/html/plugins/remove-not-indexable-content.plugin.ts
1387
1966
  import { filter } from "unist-util-filter";
1388
- import { visit as visit7 } from "unist-util-visit";
1967
+ import { visit as visit8 } from "unist-util-visit";
1389
1968
  function removeNotIndexableContentPlugin() {
1390
1969
  return (tree2) => {
1391
- visit7(tree2, "element", (node) => {
1970
+ visit8(tree2, "element", (node) => {
1392
1971
  if (isHeading(node) && isHiddenSpan(node.children[0])) {
1393
1972
  node.children = node.children.slice(1);
1394
1973
  }
@@ -1469,8 +2048,7 @@ var init_utils = __esm({
1469
2048
  }
1470
2049
  });
1471
2050
 
1472
- // libs/builder/generator/content/highlight-cache.ts
1473
- init_core();
2051
+ // libs/builder/generator/content/format-cache.ts
1474
2052
  import { randomUUID as randomUUID3 } from "node:crypto";
1475
2053
  import { readFileSync as readFileSync5 } from "node:fs";
1476
2054
  import { mkdir as mkdir3, rename as rename3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
@@ -1525,24 +2103,24 @@ function sha256Hex(value) {
1525
2103
  function canonicalJson(value) {
1526
2104
  return canonical(value, /* @__PURE__ */ new Set()) ?? "null";
1527
2105
  }
1528
- function canonical(value, path17) {
2106
+ function canonical(value, path18) {
1529
2107
  if (value !== null && typeof value === "object" && typeof value.toJSON === "function")
1530
2108
  value = value.toJSON();
1531
2109
  if (value === null || typeof value !== "object") return JSON.stringify(value);
1532
- if (path17.has(value)) throw new TypeError("Converting circular structure to canonical JSON");
1533
- path17.add(value);
2110
+ if (path18.has(value)) throw new TypeError("Converting circular structure to canonical JSON");
2111
+ path18.add(value);
1534
2112
  let text;
1535
2113
  if (Array.isArray(value)) {
1536
- text = `[${Array.from(value, (item) => canonical(item, path17) ?? "null").join(",")}]`;
2114
+ text = `[${Array.from(value, (item) => canonical(item, path18) ?? "null").join(",")}]`;
1537
2115
  } else {
1538
2116
  const fields = [];
1539
2117
  for (const key of Object.keys(value).sort(compareCodeUnits)) {
1540
- const item = canonical(value[key], path17);
2118
+ const item = canonical(value[key], path18);
1541
2119
  if (item !== void 0) fields.push(`${JSON.stringify(key)}:${item}`);
1542
2120
  }
1543
2121
  text = `{${fields.join(",")}}`;
1544
2122
  }
1545
- path17.delete(value);
2123
+ path18.delete(value);
1546
2124
  return text;
1547
2125
  }
1548
2126
  function canonicalJsonStrict(value) {
@@ -1649,16 +2227,16 @@ var ObservationRecorder = class {
1649
2227
  * file does not exist; a read error propagates.
1650
2228
  */
1651
2229
  readFile(file) {
1652
- const path17 = this.normalize(file);
1653
- let bytes = this.bytes?.get(path17);
2230
+ const path18 = this.normalize(file);
2231
+ let bytes = this.bytes?.get(path18);
1654
2232
  const exists = bytes !== void 0 || existsSync(file);
1655
- this.add({ kind: "existence", path: path17, exists });
2233
+ this.add({ kind: "existence", path: path18, exists });
1656
2234
  if (!exists) return void 0;
1657
2235
  if (!bytes) {
1658
2236
  bytes = readFileSync(file);
1659
- this.bytes?.set(path17, bytes);
2237
+ this.bytes?.set(path18, bytes);
1660
2238
  }
1661
- this.add({ kind: "content", path: path17, digest: bytesDigest(bytes) });
2239
+ this.add({ kind: "content", path: path18, digest: bytesDigest(bytes) });
1662
2240
  return bytes;
1663
2241
  }
1664
2242
  /** {@link readFile} decoded as UTF-8; a missing file throws like `readFileSync`. */
@@ -1673,12 +2251,12 @@ var ObservationRecorder = class {
1673
2251
  return this.ordered.map(cloneDependency);
1674
2252
  }
1675
2253
  };
1676
- function readText(path17) {
1677
- const bytes = readFileSync(path17);
2254
+ function readText(path18) {
2255
+ const bytes = readFileSync(path18);
1678
2256
  return { text: bytes.toString("utf8"), digest: bytesDigest(bytes) };
1679
2257
  }
1680
- async function readTextFile(path17) {
1681
- const bytes = await readFile(path17);
2258
+ async function readTextFile(path18) {
2259
+ const bytes = await readFile(path18);
1682
2260
  return { text: bytes.toString("utf8"), digest: bytesDigest(bytes) };
1683
2261
  }
1684
2262
  function cloneDependency(dependency) {
@@ -2134,9 +2712,9 @@ var ObservationStamps = class {
2134
2712
  result.changed = { path: dependency.path, kind: "content" };
2135
2713
  return result;
2136
2714
  }
2137
- const current = stampOf(dependency.path);
2715
+ const current2 = stampOf(dependency.path);
2138
2716
  const recorded = this.stamps.get(dependency.path);
2139
- if (current && recorded && sameStamp(current, recorded)) {
2717
+ if (current2 && recorded && sameStamp(current2, recorded)) {
2140
2718
  result.stamped += 1;
2141
2719
  continue;
2142
2720
  }
@@ -2144,7 +2722,7 @@ var ObservationStamps = class {
2144
2722
  this.stamps.delete(dependency.path);
2145
2723
  let digest3;
2146
2724
  try {
2147
- digest3 = current ? this.digestOf(dependency.path) : void 0;
2725
+ digest3 = current2 ? this.digestOf(dependency.path) : void 0;
2148
2726
  } catch {
2149
2727
  digest3 = void 0;
2150
2728
  }
@@ -2153,7 +2731,7 @@ var ObservationStamps = class {
2153
2731
  return result;
2154
2732
  }
2155
2733
  const after = stampOf(dependency.path);
2156
- if (current && after && sameStamp(current, after) && stampSettledBefore(after, startedNs)) {
2734
+ if (current2 && after && sameStamp(current2, after) && stampSettledBefore(after, startedNs)) {
2157
2735
  this.stamps.set(dependency.path, after);
2158
2736
  }
2159
2737
  }
@@ -2298,8 +2876,8 @@ var DirectoryListings = class _DirectoryListings {
2298
2876
  result.changed = listing.path;
2299
2877
  return result;
2300
2878
  }
2301
- const current = directoryStamp(listing.path);
2302
- if (current && listing.stamp && sameStamp(current, listing.stamp)) {
2879
+ const current2 = directoryStamp(listing.path);
2880
+ if (current2 && listing.stamp && sameStamp(current2, listing.stamp)) {
2303
2881
  result.stamped += 1;
2304
2882
  continue;
2305
2883
  }
@@ -2310,7 +2888,7 @@ var DirectoryListings = class _DirectoryListings {
2310
2888
  return result;
2311
2889
  }
2312
2890
  const after = directoryStamp(listing.path);
2313
- if (current && after && sameStamp(current, after) && stampSettledBefore(after, startedNs)) {
2891
+ if (current2 && after && sameStamp(current2, after) && stampSettledBefore(after, startedNs)) {
2314
2892
  listing.stamp = after;
2315
2893
  }
2316
2894
  }
@@ -2612,9 +3190,9 @@ var ReasonCollector = class {
2612
3190
  }
2613
3191
  add(ownerId, reason, detail) {
2614
3192
  const key = `${ownerId}\0${reason}`;
2615
- const current = this.reasons.get(key) ?? { ownerId, reason, details: /* @__PURE__ */ new Set() };
2616
- current.details.add(detail);
2617
- this.reasons.set(key, current);
3193
+ const current2 = this.reasons.get(key) ?? { ownerId, reason, details: /* @__PURE__ */ new Set() };
3194
+ current2.details.add(detail);
3195
+ this.reasons.set(key, current2);
2618
3196
  }
2619
3197
  values() {
2620
3198
  return [...this.reasons.values()].map(({ ownerId, reason, details }) => ({
@@ -3002,9 +3580,11 @@ function isRoute(value) {
3002
3580
  "metadata",
3003
3581
  "category",
3004
3582
  "modulePath",
3005
- "apiListSegment"
3583
+ "apiListSegment",
3584
+ "demoModulePath",
3585
+ "demoNames"
3006
3586
  ]
3007
- ) && [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"));
3587
+ ) && [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.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"));
3008
3588
  }
3009
3589
  function isApiList(value) {
3010
3590
  return isRecord(value) && exact(
@@ -3100,18 +3680,18 @@ function validDescriptorPlan(artifact) {
3100
3680
  const closures = new Map(descriptors.map((descriptor) => [descriptor.id, descriptor.closureIds]));
3101
3681
  const visiting = /* @__PURE__ */ new Set();
3102
3682
  const visited2 = /* @__PURE__ */ new Set();
3103
- const visit8 = (id) => {
3683
+ const visit9 = (id) => {
3104
3684
  if (visiting.has(id)) return false;
3105
3685
  if (visited2.has(id)) return true;
3106
3686
  visiting.add(id);
3107
3687
  const valid = (closures.get(id) ?? []).every(
3108
- (dependency) => ids.has(dependency) && visit8(dependency)
3688
+ (dependency) => ids.has(dependency) && visit9(dependency)
3109
3689
  );
3110
3690
  visiting.delete(id);
3111
3691
  visited2.add(id);
3112
3692
  return valid;
3113
3693
  };
3114
- return [...ids].every(visit8);
3694
+ return [...ids].every(visit9);
3115
3695
  }
3116
3696
  function absoluteNormalizedPath(value) {
3117
3697
  if (!string(value) || value.includes("\0") || value.includes("\\")) return false;
@@ -3358,7 +3938,9 @@ var TRACKED_PROGRAM_REUSE_FLAG = "NGDOC_TRACKED_PROGRAM_REUSE";
3358
3938
  var FAST_START_FLAG = "NGDOC_FAST_START";
3359
3939
  var PARALLEL_WRITES_FLAG = "NGDOC_PARALLEL_WRITES";
3360
3940
  var HIGHLIGHT_CACHE_FLAG = "NGDOC_HIGHLIGHT_CACHE";
3941
+ var FORMAT_CACHE_FLAG = "NGDOC_FORMAT_CACHE";
3361
3942
  var PARALLEL_RENDER_FLAG = "NGDOC_PARALLEL_RENDER";
3943
+ var USED_GRAMMARS_FLAG = "NGDOC_USED_GRAMMARS";
3362
3944
  var FLAGS = Object.freeze(
3363
3945
  [
3364
3946
  {
@@ -3436,6 +4018,16 @@ var FLAGS = Object.freeze(
3436
4018
  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.",
3437
4019
  verify: true
3438
4020
  },
4021
+ {
4022
+ name: FORMAT_CACHE_FLAG,
4023
+ 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.",
4024
+ verify: true
4025
+ },
4026
+ {
4027
+ name: USED_GRAMMARS_FLAG,
4028
+ 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.",
4029
+ verify: false
4030
+ },
3439
4031
  {
3440
4032
  name: PARALLEL_RENDER_FLAG,
3441
4033
  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.",
@@ -3481,6 +4073,10 @@ var RUNTIME_PACKAGES = [
3481
4073
  ["@shikijs/rehype"],
3482
4074
  ["shiki", "@shikijs/rehype"],
3483
4075
  ["@shikijs/core", "shiki"],
4076
+ ["@shikijs/langs", "shiki"],
4077
+ ["@shikijs/themes", "shiki"],
4078
+ ["@shikijs/engine-oniguruma", "shiki"],
4079
+ ["@shikijs/vscode-textmate", "@shikijs/core"],
3484
4080
  ["marked"],
3485
4081
  ["gray-matter"],
3486
4082
  ["nunjucks"],
@@ -3519,10 +4115,10 @@ function packageVersions(list, from) {
3519
4115
  return versions;
3520
4116
  }
3521
4117
  function packageDirectory(name, directory) {
3522
- for (let current = directory; ; current = path4.dirname(current)) {
3523
- const candidate = path4.join(current, "node_modules", name);
4118
+ for (let current2 = directory; ; current2 = path4.dirname(current2)) {
4119
+ const candidate = path4.join(current2, "node_modules", name);
3524
4120
  if (existsSync3(path4.join(candidate, "package.json"))) return candidate;
3525
- if (path4.dirname(current) === current) return void 0;
4121
+ if (path4.dirname(current2) === current2) return void 0;
3526
4122
  }
3527
4123
  }
3528
4124
  function packageVersion(directory) {
@@ -3535,15 +4131,28 @@ function packageVersion(directory) {
3535
4131
  }
3536
4132
  }
3537
4133
 
3538
- // libs/builder/generator/content/highlight-cache.ts
3539
- var HIGHLIGHT_CACHE_MISMATCH = "CONTENT_HIGHLIGHT_CACHE_MISMATCH";
4134
+ // libs/builder/generator/content/format-cache.ts
4135
+ var FORMAT_CACHE_MISMATCH = "CONTENT_FORMAT_CACHE_MISMATCH";
3540
4136
  var PACK_VERSION = 1;
3541
- var HIGHLIGHT_CACHE_LIMIT = 64 * 1024 * 1024;
4137
+ var SYNTAX_ERROR = "!";
4138
+ function encode(format) {
4139
+ try {
4140
+ return `=${format()}`;
4141
+ } catch (error) {
4142
+ if (error instanceof Error && error.name === "SyntaxError" && "loc" in error)
4143
+ return SYNTAX_ERROR;
4144
+ throw error;
4145
+ }
4146
+ }
4147
+ function decode(entry) {
4148
+ if (entry === SYNTAX_ERROR)
4149
+ throw new SyntaxError("Prettier rejected this code before (format cache).");
4150
+ return entry.slice(1);
4151
+ }
4152
+ var PRETTIER_PACKAGES = ["prettier", "@prettier/sync"];
4153
+ var FORMAT_CACHE_LIMIT = 16 * 1024 * 1024;
3542
4154
  var memory = /* @__PURE__ */ new Map();
3543
4155
  var memoryBytes = 0;
3544
- var journal = [];
3545
- var epoch = 0;
3546
- var proven = /* @__PURE__ */ new Set();
3547
4156
  var packs = /* @__PURE__ */ new Map();
3548
4157
  var sizeOf = (key, value) => key.length + value.length;
3549
4158
  function remember(key, value) {
@@ -3551,11 +4160,213 @@ function remember(key, value) {
3551
4160
  if (previous !== void 0) memoryBytes -= sizeOf(key, previous);
3552
4161
  memory.set(key, value);
3553
4162
  memoryBytes += sizeOf(key, value);
4163
+ }
4164
+ function formatCacheSwitch(options) {
4165
+ if (options.formatCache === false) return "off";
4166
+ const value = readFlag(FORMAT_CACHE_FLAG).value;
4167
+ return value === "off" ? "off" : options.formatCache === "verify" ? "verify" : value;
4168
+ }
4169
+ function createFormatSession(options, request, configuration) {
4170
+ const mode = formatCacheSwitch(options);
4171
+ if (mode === "off" || options.incrementalReuse === false) return void 0;
4172
+ const pack = request.mode === "development" && configuration.cacheEnabled ? path5.join(configuration.cacheRoot, `${digestOf(options.projectId)}.format.json`) : void 0;
4173
+ return new FormatSession(mode === "verify", pack);
4174
+ }
4175
+ var FormatSession = class {
4176
+ constructor(verify, pack) {
4177
+ this.verify = verify;
4178
+ this.pack = pack;
4179
+ /** The keys this generation hit or formatted. */
4180
+ this.used = /* @__PURE__ */ new Set();
4181
+ /** The resolved configuration per directory (`''` for the process's working directory). */
4182
+ this.configs = /* @__PURE__ */ new Map();
4183
+ /** Each resolved configuration's identity in the keys; undefined: not cacheable. */
4184
+ this.identities = /* @__PURE__ */ new Map();
4185
+ this.loaded = false;
4186
+ /** An entry was replaced (a `verify` repair): the pack must be written. */
4187
+ this.replaced = false;
4188
+ this.mismatchCount = 0;
4189
+ const engine = runtimePackages();
4190
+ this.context = digestOf({
4191
+ version: PACK_VERSION,
4192
+ engine: Object.fromEntries(PRETTIER_PACKAGES.map((name) => [name, engine[name] ?? null]))
4193
+ });
4194
+ if (memoryBytes > FORMAT_CACHE_LIMIT) {
4195
+ memory.clear();
4196
+ memoryBytes = 0;
4197
+ packs.clear();
4198
+ }
4199
+ }
4200
+ /** How many `verify` hits and configurations differed from the cached ones. */
4201
+ get mismatches() {
4202
+ return this.mismatchCount;
4203
+ }
4204
+ config(configDirectory, resolve10) {
4205
+ const directory = configDirectory ?? "";
4206
+ if (this.configs.has(directory)) {
4207
+ const known3 = this.configs.get(directory);
4208
+ if (!this.verify) return known3;
4209
+ const fresh = resolve10();
4210
+ if (canonicalJsonStrict(fresh) === canonicalJsonStrict(known3)) return known3;
4211
+ this.mismatchCount += 1;
4212
+ return this.remember(directory, configDirectory, fresh);
4213
+ }
4214
+ return this.remember(directory, configDirectory, resolve10());
4215
+ }
4216
+ format(input, format) {
4217
+ const identity = this.identities.get(input.config);
4218
+ if (identity === void 0) return format();
4219
+ this.load();
4220
+ const key = digestOf({
4221
+ context: this.context,
4222
+ parser: input.parser,
4223
+ config: identity,
4224
+ code: input.code
4225
+ });
4226
+ const known3 = memory.get(key);
4227
+ const usable = known3 !== void 0 && (known3 === SYNTAX_ERROR || known3.startsWith("="));
4228
+ if (usable && !this.verify) {
4229
+ this.used.add(key);
4230
+ return decode(known3);
4231
+ }
4232
+ const value = encode(format);
4233
+ if (known3 !== void 0 && known3 !== value) {
4234
+ if (usable) this.mismatchCount += 1;
4235
+ this.replaced = true;
4236
+ }
4237
+ if (known3 !== value) remember(key, value);
4238
+ this.used.add(key);
4239
+ return decode(value);
4240
+ }
4241
+ /**
4242
+ * Writes the pack after the artifact cache: the entries this generation hit or formatted, plus
4243
+ * those the pack held before unless the generation rendered every content (`complete`), or the
4244
+ * result would exceed {@link FORMAT_CACHE_LIMIT}. Best effort.
4245
+ * @param complete
4246
+ */
4247
+ async save(complete) {
4248
+ if (!this.pack) return;
4249
+ if (!this.loaded) return;
4250
+ const file = this.pack;
4251
+ const last = packs.get(file);
4252
+ const previous = !complete && last?.context === this.context ? last.keys : [];
4253
+ let selected = new Set([...previous, ...this.used].filter((key) => memory.has(key)));
4254
+ const size = (keys2) => {
4255
+ let total = 0;
4256
+ for (const key of keys2) total += sizeOf(key, memory.get(key));
4257
+ return total;
4258
+ };
4259
+ if (size(selected) > FORMAT_CACHE_LIMIT)
4260
+ selected = new Set([...this.used].filter((key) => memory.has(key)));
4261
+ const keys = [...selected].sort(compareCodeUnits);
4262
+ if (!this.replaced && last?.context === this.context && last.keys.length === keys.length && last.keys.every((key, index) => key === keys[index]) && last.content === currentContent2(file))
4263
+ return;
4264
+ const content = {
4265
+ version: PACK_VERSION,
4266
+ context: this.context,
4267
+ entries: Object.fromEntries(keys.map((key) => [key, memory.get(key)]))
4268
+ };
4269
+ const text = JSON.stringify(content);
4270
+ const temporary = `${file}.${randomUUID3()}.tmp`;
4271
+ try {
4272
+ await mkdir3(path5.dirname(file), { recursive: true });
4273
+ await sweepTemporaries(file);
4274
+ await writeFile3(temporary, text, { flag: "wx" });
4275
+ await retryingRename(rename3)(temporary, file);
4276
+ packs.set(file, { context: this.context, keys, content: contentDigest(text) });
4277
+ this.replaced = false;
4278
+ } catch {
4279
+ packs.delete(file);
4280
+ } finally {
4281
+ await rm3(temporary, { force: true }).catch(() => void 0);
4282
+ }
4283
+ }
4284
+ remember(directory, configDirectory, config) {
4285
+ const frozen = config && Object.isFrozen(config) ? config : config && Object.freeze(config);
4286
+ this.configs.set(directory, frozen);
4287
+ if (!this.identities.has(frozen))
4288
+ this.identities.set(frozen, identityOf(frozen, configDirectory));
4289
+ return frozen;
4290
+ }
4291
+ /** Reads the pack once per runtime and context; an unusable pack is not read. */
4292
+ load() {
4293
+ if (this.loaded) return;
4294
+ this.loaded = true;
4295
+ if (!this.pack || packs.get(this.pack)?.context === this.context) return;
4296
+ try {
4297
+ const bytes = readFileSync5(this.pack);
4298
+ const value = JSON.parse(bytes.toString("utf8"));
4299
+ if (!isPackFile(value) || value.context !== this.context) return;
4300
+ const keys = Object.keys(value.entries).sort(compareCodeUnits);
4301
+ for (const key of keys) if (!memory.has(key)) remember(key, value.entries[key]);
4302
+ packs.set(this.pack, { context: this.context, keys, content: bytesDigest(bytes) });
4303
+ } catch {
4304
+ }
4305
+ }
4306
+ };
4307
+ function identityOf(config, configDirectory) {
4308
+ const json = canonicalJsonStrict(config);
4309
+ if (json === void 0) return void 0;
4310
+ const plugins = Array.isArray(config?.plugins) ? config.plugins : [];
4311
+ const versions = {};
4312
+ for (const plugin of plugins) {
4313
+ if (typeof plugin !== "string" || plugin.startsWith(".") || path5.isAbsolute(plugin))
4314
+ return void 0;
4315
+ const version = packageVersions([[plugin]], configDirectory ?? process.cwd())[plugin];
4316
+ if (!version) return void 0;
4317
+ versions[plugin] = version;
4318
+ }
4319
+ return digestOf({ config: json, plugins: versions });
4320
+ }
4321
+ function currentContent2(file) {
4322
+ try {
4323
+ return bytesDigest(readFileSync5(file));
4324
+ } catch {
4325
+ return void 0;
4326
+ }
4327
+ }
4328
+ function isPackFile(value) {
4329
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
4330
+ const file = value;
4331
+ const entries = file["entries"];
4332
+ return file["version"] === PACK_VERSION && typeof file["context"] === "string" && !!entries && typeof entries === "object" && !Array.isArray(entries) && Object.values(entries).every((entry) => typeof entry === "string");
4333
+ }
4334
+
4335
+ // libs/builder/generator/content/highlight-cache.ts
4336
+ init_core();
4337
+ import { randomUUID as randomUUID4 } from "node:crypto";
4338
+ import { readFileSync as readFileSync6 } from "node:fs";
4339
+ import { mkdir as mkdir4, rename as rename4, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
4340
+ import path6 from "node:path";
4341
+ var SHIKI_PACKAGES = [
4342
+ "shiki",
4343
+ "@shikijs/core",
4344
+ "@shikijs/langs",
4345
+ "@shikijs/themes",
4346
+ "@shikijs/engine-oniguruma",
4347
+ "@shikijs/vscode-textmate",
4348
+ "@shikijs/rehype"
4349
+ ];
4350
+ var HIGHLIGHT_CACHE_MISMATCH = "CONTENT_HIGHLIGHT_CACHE_MISMATCH";
4351
+ var PACK_VERSION2 = 1;
4352
+ var HIGHLIGHT_CACHE_LIMIT = 64 * 1024 * 1024;
4353
+ var memory2 = /* @__PURE__ */ new Map();
4354
+ var memoryBytes2 = 0;
4355
+ var journal = [];
4356
+ var epoch = 0;
4357
+ var proven = /* @__PURE__ */ new Set();
4358
+ var packs2 = /* @__PURE__ */ new Map();
4359
+ var sizeOf2 = (key, value) => key.length + value.length;
4360
+ function remember2(key, value) {
4361
+ const previous = memory2.get(key);
4362
+ if (previous !== void 0) memoryBytes2 -= sizeOf2(key, previous);
4363
+ memory2.set(key, value);
4364
+ memoryBytes2 += sizeOf2(key, value);
3554
4365
  journal.push([key, value]);
3555
4366
  }
3556
4367
  function forget() {
3557
- memory.clear();
3558
- memoryBytes = 0;
4368
+ memory2.clear();
4369
+ memoryBytes2 = 0;
3559
4370
  journal.length = 0;
3560
4371
  epoch += 1;
3561
4372
  }
@@ -3567,14 +4378,22 @@ function highlightCacheSwitch(options) {
3567
4378
  const value = readFlag(HIGHLIGHT_CACHE_FLAG).value;
3568
4379
  return value === "off" ? "off" : options.highlightCache === "verify" ? "verify" : value;
3569
4380
  }
4381
+ function grammarsSwitch(options) {
4382
+ return options.usedGrammars === false || readFlag(USED_GRAMMARS_FLAG).value === "off" ? "all" : "used";
4383
+ }
3570
4384
  function createHighlightSession(options, request, configuration) {
3571
4385
  const mode = highlightCacheSwitch(options);
3572
4386
  if (mode === "off" || options.incrementalReuse === false) return void 0;
3573
- const pack = request.mode === "development" && configuration.cacheEnabled ? path5.join(configuration.cacheRoot, `${digestOf(options.projectId)}.highlight.json`) : void 0;
3574
- return new HighlightSession(configuration.themes, mode === "verify", pack);
4387
+ const pack = request.mode === "development" && configuration.cacheEnabled ? path6.join(configuration.cacheRoot, `${digestOf(options.projectId)}.highlight.json`) : void 0;
4388
+ return new HighlightSession(
4389
+ configuration.themes,
4390
+ mode === "verify",
4391
+ pack,
4392
+ configuration.shikiLangs
4393
+ );
3575
4394
  }
3576
4395
  var HighlightSession = class {
3577
- constructor(themes, verify, pack) {
4396
+ constructor(themes, verify, pack, langs) {
3578
4397
  this.themes = themes;
3579
4398
  this.verify = verify;
3580
4399
  this.pack = pack;
@@ -3585,17 +4404,15 @@ var HighlightSession = class {
3585
4404
  this.replaced = false;
3586
4405
  const engine = runtimePackages();
3587
4406
  this.context = digestOf({
3588
- version: PACK_VERSION,
3589
- engine: {
3590
- shiki: engine["shiki"] ?? null,
3591
- "@shikijs/core": engine["@shikijs/core"] ?? null,
3592
- "@shikijs/rehype": engine["@shikijs/rehype"] ?? null
3593
- },
3594
- themes: { light: themeIdentity(themes.light), dark: themeIdentity(themes.dark) }
4407
+ version: PACK_VERSION2,
4408
+ engine: Object.fromEntries(SHIKI_PACKAGES.map((name) => [name, engine[name] ?? null])),
4409
+ themes: { light: themeIdentity(themes.light), dark: themeIdentity(themes.dark) },
4410
+ // The configured languages: a block highlights with them, and they must load.
4411
+ ...langs?.length ? { langs: digestOf(langs) } : {}
3595
4412
  });
3596
- if (memoryBytes > HIGHLIGHT_CACHE_LIMIT) {
4413
+ if (memoryBytes2 > HIGHLIGHT_CACHE_LIMIT) {
3597
4414
  forget();
3598
- packs.clear();
4415
+ packs2.clear();
3599
4416
  }
3600
4417
  }
3601
4418
  /** The cache of one `processHtml` call. */
@@ -3606,13 +4423,13 @@ var HighlightSession = class {
3606
4423
  return {
3607
4424
  key: (block) => digestOf({ context, block }),
3608
4425
  get: (key) => {
3609
- const value = memory.get(key);
4426
+ const value = memory2.get(key);
3610
4427
  if (value !== void 0) used.add(key);
3611
4428
  return value;
3612
4429
  },
3613
4430
  set: (key, value) => {
3614
- if (memory.has(key)) this.replaced = true;
3615
- remember(key, value);
4431
+ if (memory2.has(key)) this.replaced = true;
4432
+ remember2(key, value);
3616
4433
  used.add(key);
3617
4434
  proven.add(context);
3618
4435
  },
@@ -3633,7 +4450,7 @@ var HighlightSession = class {
3633
4450
  }
3634
4451
  /** The entry this runtime holds for `key`, without counting it as used. */
3635
4452
  peek(key) {
3636
- return memory.get(key);
4453
+ return memory2.get(key);
3637
4454
  }
3638
4455
  /**
3639
4456
  * Takes in what one document's highlighting recorded out of plan order (a render thread, or the
@@ -3647,32 +4464,32 @@ var HighlightSession = class {
3647
4464
  const fresh = new Map(record.fresh);
3648
4465
  let mismatches = 0;
3649
4466
  for (const key of record.mismatched) {
3650
- const known3 = memory.get(key);
4467
+ const known3 = memory2.get(key);
3651
4468
  if (known3 !== void 0 && known3 !== fresh.get(key)) mismatches += 1;
3652
4469
  }
3653
4470
  for (const [key, value] of record.fresh) {
3654
- const known3 = memory.get(key);
4471
+ const known3 = memory2.get(key);
3655
4472
  if (known3 === value) continue;
3656
4473
  if (known3 !== void 0) this.replaced = true;
3657
- remember(key, value);
4474
+ remember2(key, value);
3658
4475
  proven.add(this.context);
3659
4476
  }
3660
- for (const key of record.used) if (memory.has(key)) this.used.add(key);
4477
+ for (const key of record.used) if (memory2.has(key)) this.used.add(key);
3661
4478
  return mismatches;
3662
4479
  }
3663
4480
  /** Reads the pack once per runtime and context; an unusable pack is not read. */
3664
4481
  load() {
3665
4482
  if (this.loaded || !this.pack) return;
3666
4483
  this.loaded = true;
3667
- if (packs.get(this.pack)?.context === this.context) return;
4484
+ if (packs2.get(this.pack)?.context === this.context) return;
3668
4485
  try {
3669
- const bytes = readFileSync5(this.pack);
4486
+ const bytes = readFileSync6(this.pack);
3670
4487
  const value = JSON.parse(bytes.toString("utf8"));
3671
- if (!isPackFile(value) || value.context !== this.context) return;
4488
+ if (!isPackFile2(value) || value.context !== this.context) return;
3672
4489
  const keys = Object.keys(value.entries).sort(compareCodeUnits);
3673
- for (const key of keys) if (!memory.has(key)) remember(key, value.entries[key]);
4490
+ for (const key of keys) if (!memory2.has(key)) remember2(key, value.entries[key]);
3674
4491
  if (keys.length) proven.add(this.context);
3675
- packs.set(this.pack, { context: this.context, keys, content: bytesDigest(bytes) });
4492
+ packs2.set(this.pack, { context: this.context, keys, content: bytesDigest(bytes) });
3676
4493
  } catch {
3677
4494
  }
3678
4495
  }
@@ -3685,55 +4502,55 @@ var HighlightSession = class {
3685
4502
  async save(complete) {
3686
4503
  if (!this.pack) return;
3687
4504
  const file = this.pack;
3688
- const last = packs.get(file);
4505
+ const last = packs2.get(file);
3689
4506
  const previous = !complete && last?.context === this.context ? last.keys : [];
3690
- let selected = new Set([...previous, ...this.used].filter((key) => memory.has(key)));
4507
+ let selected = new Set([...previous, ...this.used].filter((key) => memory2.has(key)));
3691
4508
  const size = (keys2) => {
3692
4509
  let total = 0;
3693
- for (const key of keys2) total += sizeOf(key, memory.get(key));
4510
+ for (const key of keys2) total += sizeOf2(key, memory2.get(key));
3694
4511
  return total;
3695
4512
  };
3696
4513
  if (size(selected) > HIGHLIGHT_CACHE_LIMIT)
3697
- selected = new Set([...this.used].filter((key) => memory.has(key)));
4514
+ selected = new Set([...this.used].filter((key) => memory2.has(key)));
3698
4515
  const keys = [...selected].sort(compareCodeUnits);
3699
- if (!this.replaced && last?.context === this.context && last.keys.length === keys.length && last.keys.every((key, index) => key === keys[index]) && last.content === currentContent2(file))
4516
+ if (!this.replaced && last?.context === this.context && last.keys.length === keys.length && last.keys.every((key, index) => key === keys[index]) && last.content === currentContent3(file))
3700
4517
  return;
3701
4518
  const content = {
3702
- version: PACK_VERSION,
4519
+ version: PACK_VERSION2,
3703
4520
  context: this.context,
3704
- entries: Object.fromEntries(keys.map((key) => [key, memory.get(key)]))
4521
+ entries: Object.fromEntries(keys.map((key) => [key, memory2.get(key)]))
3705
4522
  };
3706
4523
  const text = JSON.stringify(content);
3707
- const temporary = `${file}.${randomUUID3()}.tmp`;
4524
+ const temporary = `${file}.${randomUUID4()}.tmp`;
3708
4525
  try {
3709
- await mkdir3(path5.dirname(file), { recursive: true });
4526
+ await mkdir4(path6.dirname(file), { recursive: true });
3710
4527
  await sweepTemporaries(file);
3711
- await writeFile3(temporary, text, { flag: "wx" });
3712
- await retryingRename(rename3)(temporary, file);
3713
- packs.set(file, { context: this.context, keys, content: contentDigest(text) });
4528
+ await writeFile4(temporary, text, { flag: "wx" });
4529
+ await retryingRename(rename4)(temporary, file);
4530
+ packs2.set(file, { context: this.context, keys, content: contentDigest(text) });
3714
4531
  this.replaced = false;
3715
4532
  } catch {
3716
- packs.delete(file);
4533
+ packs2.delete(file);
3717
4534
  } finally {
3718
- await rm3(temporary, { force: true }).catch(() => void 0);
4535
+ await rm4(temporary, { force: true }).catch(() => void 0);
3719
4536
  }
3720
4537
  }
3721
4538
  };
3722
4539
  function themeIdentity(name) {
3723
4540
  return name === NG_DOC_SYNTAX_THEME_NAME ? { ngDoc: digestOf(ngDocSyntaxTheme()) } : name;
3724
4541
  }
3725
- function currentContent2(file) {
4542
+ function currentContent3(file) {
3726
4543
  try {
3727
- return bytesDigest(readFileSync5(file));
4544
+ return bytesDigest(readFileSync6(file));
3728
4545
  } catch {
3729
4546
  return void 0;
3730
4547
  }
3731
4548
  }
3732
- function isPackFile(value) {
4549
+ function isPackFile2(value) {
3733
4550
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
3734
4551
  const file = value;
3735
4552
  const entries = file["entries"];
3736
- return file["version"] === PACK_VERSION && typeof file["context"] === "string" && !!entries && typeof entries === "object" && !Array.isArray(entries) && Object.values(entries).every((entry) => typeof entry === "string");
4553
+ return file["version"] === PACK_VERSION2 && typeof file["context"] === "string" && !!entries && typeof entries === "object" && !Array.isArray(entries) && Object.values(entries).every((entry) => typeof entry === "string");
3737
4554
  }
3738
4555
 
3739
4556
  // libs/builder/generator/content/html-pool.ts
@@ -3743,7 +4560,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
3743
4560
  import { Worker } from "node:worker_threads";
3744
4561
 
3745
4562
  // libs/builder/generator/worker/protocol.ts
3746
- function encode(value) {
4563
+ function encode2(value) {
3747
4564
  const result = JSON.stringify(
3748
4565
  value,
3749
4566
  function(key, item) {
@@ -3756,7 +4573,7 @@ function encode(value) {
3756
4573
  if (result === void 0) throw new Error("Worker transport requires a JSON value");
3757
4574
  return result;
3758
4575
  }
3759
- function decode(value) {
4576
+ function decode2(value) {
3760
4577
  if (typeof value !== "string") throw new Error("Worker message must be JSON text");
3761
4578
  const parsed = JSON.parse(value);
3762
4579
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -3769,7 +4586,7 @@ function decode(value) {
3769
4586
  import matter from "gray-matter";
3770
4587
  import { Marked } from "marked";
3771
4588
  import { existsSync as existsSync4 } from "node:fs";
3772
- import path7 from "node:path";
4589
+ import path8 from "node:path";
3773
4590
 
3774
4591
  // libs/builder/helpers/keyword-heading-title.ts
3775
4592
  var LEADING_EMOJI = new RegExp("^(?:\\p{Regional_Indicator}{2}|(?:\\p{Emoji_Presentation}|\\p{Extended_Pictographic}\uFE0F)(?:\\p{Emoji_Modifier}|\uFE0F|\u200D\\p{Extended_Pictographic}\uFE0F?)*)\\s*", "u");
@@ -3818,7 +4635,16 @@ function param(key, newKeyName) {
3818
4635
 
3819
4636
  // libs/builder/parsers/parse-code-block-params.ts
3820
4637
  var CODE_BLOCK_LANGUAGE = /[\p{L}\p{N}+#-]+/u;
3821
- function parseCodeBlockParams(options) {
4638
+ var SNIPPET_ID = /[a-zA-Z0-9-]+/;
4639
+ var LINE_RANGE = /^L(\d+)(?:(-)(?:L(\d+))?)?$/;
4640
+ function fileFragment(fragment2) {
4641
+ const range = LINE_RANGE.exec(fragment2);
4642
+ if (!range) return { snippet: fragment2 };
4643
+ const fileLineStart = Number(range[1]) - 1;
4644
+ if (!range[2]) return { fileLineStart, fileLineEnd: fileLineStart + 1 };
4645
+ return { fileLineStart, fileLineEnd: range[3] === void 0 ? void 0 : Number(range[3]) };
4646
+ }
4647
+ function parseCodeBlockParams(options, parserOptions = {}) {
3822
4648
  const parser2 = P4.createLanguage({
3823
4649
  language: () => P4.regexp(CODE_BLOCK_LANGUAGE).skip(P4.optWhitespace).map((language) => ({ language })),
3824
4650
  // File Parsers
@@ -3829,7 +4655,7 @@ function parseCodeBlockParams(options) {
3829
4655
  (fileLineStart, fileLineEnd) => ({ fileLineStart, fileLineEnd })
3830
4656
  ),
3831
4657
  fileLineStart: (p) => P4.string("#").then(p["fileLineNumber"]).map((num) => num - 1).map((fileLineStart) => ({ fileLineStart, fileLineEnd: fileLineStart + 1 })),
3832
- lineParams: (p) => p["fileLineRange"].or(p["fileLineStart"]).fallback({}),
4658
+ lineParams: (p) => (parserOptions.snippets ? P4.string("#").then(P4.regexp(SNIPPET_ID)).map(fileFragment) : p["fileLineRange"].or(p["fileLineStart"])).fallback({}),
3833
4659
  highlightedLinesRange: () => P4.seqMap(
3834
4660
  number.skip(P4.string("-")),
3835
4661
  number,
@@ -3845,7 +4671,11 @@ function parseCodeBlockParams(options) {
3845
4671
  active: () => P4.string("active").map(() => ({ active: true })),
3846
4672
  file: (p) => P4.seq(
3847
4673
  p["filePath"],
3848
- p["lineParams"].map(({ fileLineStart, fileLineEnd }) => ({ fileLineStart, fileLineEnd }))
4674
+ p["lineParams"].map(({ fileLineStart, fileLineEnd, snippet: snippet2 }) => ({
4675
+ fileLineStart,
4676
+ fileLineEnd,
4677
+ ...snippet2 === void 0 ? {} : { snippet: snippet2 }
4678
+ }))
3849
4679
  ).map(([file, lineParams]) => ({ ...file, ...lineParams })),
3850
4680
  highlightedLines: (p) => p["highlightedLinesRange"].or(number).sepBy(P4.string(",").then(P4.optWhitespace)).wrap(P4.string("{"), P4.string("}")).map((a) => Array.from(new Set(a.flat()))).map((highlightedLines) => ({ highlightedLines })),
3851
4681
  // Combined Parsers
@@ -3960,11 +4790,62 @@ function footprintOf(result) {
3960
4790
  }
3961
4791
 
3962
4792
  // libs/builder/generator/kernel/paths.ts
3963
- import path6 from "node:path";
3964
- function hostPath(file, rules = path6) {
4793
+ import path7 from "node:path";
4794
+ function hostPath(file, rules = path7) {
3965
4795
  return rules.resolve(file).replace(/\\/g, "/");
3966
4796
  }
3967
4797
 
4798
+ // libs/builder/parsers/parse-snippet.ts
4799
+ import P5 from "parsimmon";
4800
+ function parseSnippet(string2) {
4801
+ const parser2 = P5.createLanguage({
4802
+ keyword: () => P5.string("snippet"),
4803
+ id: () => P5.string("#").then(P5.regexp(/[a-zA-Z0-9-]+/)).fallback(null).map((id) => ({ id })),
4804
+ lang: () => P5.string(":").then(P5.regexp(/[a-zA-Z0-9-]+/)).fallback(void 0).map((lang) => ({ lang })),
4805
+ icon: () => param("icon"),
4806
+ title: () => paramValue().map((title) => ({ title })),
4807
+ opened: () => P5.string("opened").result(true).fallback(void 0).map((opened) => ({ opened })),
4808
+ snippet: ({ keyword, id, lang, icon, title, opened }) => keyword.then(
4809
+ P5.seq(
4810
+ id,
4811
+ lang,
4812
+ P5.whitespace.then(title.or(icon).or(opened).sepBy(P5.whitespace)).fallback([])
4813
+ ).map(([id2, lang2, rest]) => ({ ...id2, ...lang2, ...Object.assign({}, ...rest) }))
4814
+ ),
4815
+ snippetFromFile: ({ lang, icon, title, opened }) => P5.seq(
4816
+ param("snippet-from-file", "fromFile"),
4817
+ lang,
4818
+ P5.whitespace.then(title.or(icon).or(opened).sepBy(P5.whitespace)).fallback([])
4819
+ ).map(([id, lang2, rest]) => ({ ...id, ...lang2, ...Object.assign({}, ...rest) })),
4820
+ anySnippet: ({ snippet: snippet2, snippetFromFile }) => snippetFromFile.or(snippet2)
4821
+ });
4822
+ const result = parser2.anySnippet.parse(string2);
4823
+ return result.status ? result.value : void 0;
4824
+ }
4825
+
4826
+ // libs/builder/generator/content/code-snippet.ts
4827
+ var MARKER = /^.*((\/\/|<!--|\/\*)\s*)(snippet.*?(?=(-->|\*\/)?))\s*(-->|\*\/)?$/;
4828
+ function snippetRegion(text, id) {
4829
+ const lines = text.split("\n").map((line) => line.replace(/\r$/, ""));
4830
+ const markers = lines.map((line) => {
4831
+ const match = MARKER.exec(line);
4832
+ return match ? parseSnippet(match[3].trim())?.id ?? null : void 0;
4833
+ });
4834
+ const start = markers.indexOf(id);
4835
+ if (start < 0) return { status: "unknown" };
4836
+ const end = markers.indexOf(id, start + 1);
4837
+ if (end < 0) return { status: "unclosed" };
4838
+ const region = removeLinesFromCode(
4839
+ lines.slice(start + 1, end).filter((_, index) => markers[start + 1 + index] === void 0).join("\n")
4840
+ ).split("\n");
4841
+ while (region.length && !region[0].trim()) region.shift();
4842
+ while (region.length && !region[region.length - 1].trim()) region.pop();
4843
+ const indent = Math.min(
4844
+ ...region.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length)
4845
+ );
4846
+ return { status: "found", code: region.map((line) => line.slice(indent)).join("\n") };
4847
+ }
4848
+
3968
4849
  // libs/builder/generator/content/html-pipeline.ts
3969
4850
  import { parseFile } from "@orama/plugin-parsedoc";
3970
4851
  var htmlUtilities = () => Promise.resolve().then(() => (init_utils(), utils_exports));
@@ -3972,13 +4853,13 @@ var notAborted = () => false;
3972
4853
  async function renderDocuments(task, highlight2, aborted = notAborted) {
3973
4854
  const results = [];
3974
4855
  for (const document of task.documents) {
3975
- const result = await renderDocument(document, task.themes, highlight2, aborted);
4856
+ const result = await renderDocument(document, task, highlight2, aborted);
3976
4857
  results.push(result);
3977
4858
  if (!("html" in result)) break;
3978
4859
  }
3979
4860
  return results;
3980
4861
  }
3981
- async function renderDocument(document, themes, highlight2, aborted) {
4862
+ async function renderDocument(document, { themes, langs, grammars: grammars2 }, highlight2, aborted) {
3982
4863
  if (aborted()) return { aborted: true, mismatches: 0 };
3983
4864
  const utilities = await htmlUtilities();
3984
4865
  if (aborted()) return { aborted: true, mismatches: 0 };
@@ -3988,6 +4869,8 @@ async function renderDocument(document, themes, highlight2, aborted) {
3988
4869
  ...document.route === void 0 ? {} : { route: document.route },
3989
4870
  lightTheme: themes.light,
3990
4871
  darkTheme: themes.dark,
4872
+ ...langs ? { langs } : {},
4873
+ grammars: grammars2 ?? "used",
3991
4874
  ...call ? { highlight: call } : {}
3992
4875
  });
3993
4876
  const mismatches = call?.mismatches.length ?? 0;
@@ -4106,6 +4989,7 @@ function recordingHighlight(settings2, themes, store) {
4106
4989
  }
4107
4990
  };
4108
4991
  }
4992
+ var WARM_DOCUMENT = ["ts", "angular-ts", "angular-html"].map((language) => `<pre><code class="language-${language}">warm</code></pre>`).join("");
4109
4993
 
4110
4994
  // libs/builder/generator/content/content-compiler.ts
4111
4995
  var PARALLEL_RENDER_MISMATCH = "CONTENT_PARALLEL_MISMATCH";
@@ -4120,10 +5004,11 @@ var GeneratorContentCompiler = class {
4120
5004
  * is highlighted (`./highlight-cache`).
4121
5005
  * @param back - Where the HTML pipeline runs; without it, in this thread, at once.
4122
5006
  */
4123
- constructor(services, highlight2, back) {
5007
+ constructor(services, highlight2, back, grammars2 = "used") {
4124
5008
  this.services = services;
4125
5009
  this.highlight = highlight2;
4126
5010
  this.back = back;
5011
+ this.grammars = grammars2;
4127
5012
  }
4128
5013
  /**
4129
5014
  * Whether this call records its footprint. The semantic service's effective recorder state
@@ -4222,8 +5107,8 @@ var GeneratorContentCompiler = class {
4222
5107
  );
4223
5108
  return { dependencies, diagnostics };
4224
5109
  }
4225
- const current = await this.descriptorFields(request, dependencies, diagnostics, signal);
4226
- if (!current) {
5110
+ const current2 = await this.descriptorFields(request, dependencies, diagnostics, signal);
5111
+ if (!current2) {
4227
5112
  if (!diagnostics.some((item) => item.code === "CONTENT_ABORTED")) {
4228
5113
  diagnostics.push(
4229
5114
  this.diag(
@@ -4235,7 +5120,7 @@ var GeneratorContentCompiler = class {
4235
5120
  }
4236
5121
  return { dependencies, diagnostics };
4237
5122
  }
4238
- if (!this.matchesDescriptor(request, descriptor, current)) {
5123
+ if (!this.matchesDescriptor(request, descriptor, current2)) {
4239
5124
  diagnostics.push(
4240
5125
  this.diag(
4241
5126
  "CONTENT_DESCRIPTOR_STALE",
@@ -4365,11 +5250,11 @@ var GeneratorContentCompiler = class {
4365
5250
  return `Descriptor closure for "${request.id}" is invalid.`;
4366
5251
  return void 0;
4367
5252
  }
4368
- matchesDescriptor(request, descriptor, current) {
5253
+ matchesDescriptor(request, descriptor, current2) {
4369
5254
  return /^[a-f0-9]{64}$/.test(descriptor.requestDigest) && expectedContentId(request, descriptor.ownerId) === request.id && Number.isSafeInteger(descriptor.ordinal) && descriptor.ordinal >= 0 && sameValue2(
4370
5255
  descriptor.closureIds,
4371
5256
  request.kind === "header" ? [] : [`${descriptor.ownerId}:header`]
4372
- ) && sameValue2(omitDescriptorPublication(descriptor), current);
5257
+ ) && sameValue2(omitDescriptorPublication(descriptor), current2);
4373
5258
  }
4374
5259
  matchesCompiledDependencies(descriptor, dependencies) {
4375
5260
  return descriptor.dependencies.every((expected) => {
@@ -4405,7 +5290,7 @@ var GeneratorContentCompiler = class {
4405
5290
  entryId: request.entry.id,
4406
5291
  source: { path: request.markdown },
4407
5292
  text: parsed.content,
4408
- scope: path7.dirname(request.markdown),
5293
+ scope: path8.dirname(request.markdown),
4409
5294
  kind: "guide",
4410
5295
  values: {
4411
5296
  title: request.entry.title,
@@ -4423,7 +5308,7 @@ var GeneratorContentCompiler = class {
4423
5308
  const processed = await this.process(
4424
5309
  this.markdownToHtml(
4425
5310
  rendered.value,
4426
- path7.dirname(request.markdown),
5311
+ path8.dirname(request.markdown),
4427
5312
  dependencies,
4428
5313
  diagnostics
4429
5314
  ),
@@ -4704,7 +5589,13 @@ var GeneratorContentCompiler = class {
4704
5589
  * `compileStaged` that the front is done: everything after the pipeline is pure.
4705
5590
  */
4706
5591
  render(documents, signal, staged) {
4707
- const task = { documents, themes: this.services.configuration.themes };
5592
+ const { themes, shikiLangs } = this.services.configuration;
5593
+ const task = {
5594
+ documents,
5595
+ themes,
5596
+ ...shikiLangs ? { langs: shikiLangs } : {},
5597
+ ...this.grammars === "all" ? { grammars: "all" } : {}
5598
+ };
4708
5599
  const rendered = this.back ? this.back.render(task, signal) : renderDocuments(
4709
5600
  task,
4710
5601
  () => this.highlight?.call(),
@@ -4954,16 +5845,27 @@ var GeneratorContentCompiler = class {
4954
5845
  markdownToHtml(markdown, context, dependencies, diagnostics) {
4955
5846
  const renderer = {
4956
5847
  code: (code, language) => {
4957
- const options = parseCodeBlockParams(language?.trim() || "typescript");
5848
+ const options = parseCodeBlockParams(language?.trim() || "typescript", { snippets: true });
4958
5849
  if (options.file) {
4959
- const source = hostPath(path7.resolve(context, options.file));
5850
+ const source = hostPath(path8.resolve(context, options.file));
4960
5851
  const fullSource = this.readPhysicalFile(
4961
5852
  source,
4962
5853
  dependencies,
4963
5854
  diagnostics,
4964
5855
  "CONTENT_SNIPPET_READ"
4965
5856
  );
4966
- if (fullSource !== void 0) {
5857
+ if (fullSource !== void 0 && options.snippet !== void 0) {
5858
+ const region = snippetRegion(fullSource, options.snippet);
5859
+ if (region.status === "found") code = region.code;
5860
+ else
5861
+ diagnostics.push(
5862
+ this.diag(
5863
+ "CONTENT_SNIPPET_UNKNOWN",
5864
+ region.status === "unknown" ? `Snippet "${options.snippet}" is not in ${options.file}: no "snippet#${options.snippet}" marker.` : `Snippet "${options.snippet}" in ${options.file} has no closing "snippet#${options.snippet}" marker.`,
5865
+ source
5866
+ )
5867
+ );
5868
+ } else if (fullSource !== void 0) {
4967
5869
  try {
4968
5870
  const sourceCode = fullSource.split("\n").slice(options.fileLineStart, options.fileLineEnd).join("\n").trim();
4969
5871
  code = removeLinesFromCode(sourceCode);
@@ -5152,7 +6054,7 @@ var HtmlThread = class {
5152
6054
  receive(message2) {
5153
6055
  let reply;
5154
6056
  try {
5155
- reply = decode(message2);
6057
+ reply = decode2(message2);
5156
6058
  } catch (error) {
5157
6059
  this.fail(error);
5158
6060
  return;
@@ -5202,7 +6104,7 @@ var HtmlPool = class {
5202
6104
  while (this.threads.length < this.size) {
5203
6105
  const thread = new HtmlThread(this.entry, () => this.changed());
5204
6106
  this.threads.push(thread);
5205
- if (warm) thread.post(encode({ type: "warm", ...warm }));
6107
+ if (warm) thread.post(encode2({ type: "warm", ...warm }));
5206
6108
  }
5207
6109
  this.changed();
5208
6110
  }
@@ -5358,7 +6260,7 @@ var RenderBack = class {
5358
6260
  signal,
5359
6261
  (thread, id) => [
5360
6262
  ...this.highlightSync(thread, settings2),
5361
- encode({
6263
+ encode2({
5362
6264
  type: "render",
5363
6265
  id,
5364
6266
  task,
@@ -5392,9 +6294,9 @@ var RenderBack = class {
5392
6294
  pool,
5393
6295
  signal,
5394
6296
  (thread, id) => {
5395
- const messages = [encode({ type: "link", id, task, keywords: set })];
6297
+ const messages = [encode2({ type: "link", id, task, keywords: set })];
5396
6298
  if (thread.keywords !== keywords) {
5397
- messages.unshift(encode({ type: "keywords", id: set, keywords }));
6299
+ messages.unshift(encode2({ type: "keywords", id: set, keywords }));
5398
6300
  thread.keywords = keywords;
5399
6301
  }
5400
6302
  return messages;
@@ -5508,7 +6410,7 @@ var RenderBack = class {
5508
6410
  const from = reset ? 0 : thread.highlight.cursor;
5509
6411
  thread.highlight = { epoch: epoch2, cursor: entries.length };
5510
6412
  return [
5511
- encode({
6413
+ encode2({
5512
6414
  type: "highlight",
5513
6415
  reset,
5514
6416
  context: settings2.context,
@@ -5605,7 +6507,7 @@ import fg2 from "fast-glob";
5605
6507
  import matter2 from "gray-matter";
5606
6508
  import { existsSync as existsSync6, realpathSync as realpathSync2 } from "node:fs";
5607
6509
  import { createRequire } from "node:module";
5608
- import path8 from "node:path";
6510
+ import path9 from "node:path";
5609
6511
  import { types as nodeTypes } from "node:util";
5610
6512
  import vm from "node:vm";
5611
6513
  import nunjucks from "nunjucks";
@@ -5936,7 +6838,7 @@ var TrackingLoader = class extends nunjucks.Loader {
5936
6838
  * error escapes later as an uncaught exception. Thrown from the loader, it fails this render
5937
6839
  * synchronously, and each including template prefixes its own path to the message.
5938
6840
  */
5939
- compile(path17, text) {
6841
+ compile(path18, text) {
5940
6842
  const environment = this.environment();
5941
6843
  try {
5942
6844
  return {
@@ -5947,13 +6849,13 @@ var TrackingLoader = class extends nunjucks.Loader {
5947
6849
  text,
5948
6850
  environment.asyncFilters,
5949
6851
  environment.extensionsList,
5950
- path17,
6852
+ path18,
5951
6853
  environment.opts
5952
6854
  )
5953
6855
  )()
5954
6856
  };
5955
6857
  } catch (error) {
5956
- throw nunjucksLib._prettifyError(path17, environment.opts.dev, error);
6858
+ throw nunjucksLib._prettifyError(path18, environment.opts.dev, error);
5957
6859
  }
5958
6860
  }
5959
6861
  };
@@ -6083,8 +6985,8 @@ function locateConfig(request, workspaceRoot, dependencies) {
6083
6985
  dependencies.observeFile(explicit);
6084
6986
  return existsSync6(explicit) ? explicit : void 0;
6085
6987
  }
6086
- let directory = path8.dirname(normalizeAbsolute(request.defaults.docsRoot, workspaceRoot));
6087
- const root = path8.parse(directory).root;
6988
+ let directory = path9.dirname(normalizeAbsolute(request.defaults.docsRoot, workspaceRoot));
6989
+ const root = path9.parse(directory).root;
6088
6990
  while (true) {
6089
6991
  for (const name of [
6090
6992
  "ng-doc.config.ts",
@@ -6092,12 +6994,12 @@ function locateConfig(request, workspaceRoot, dependencies) {
6092
6994
  "ng-doc.config.mjs",
6093
6995
  "ng-doc.config.cjs"
6094
6996
  ]) {
6095
- const candidate = path8.join(directory, name);
6997
+ const candidate = path9.join(directory, name);
6096
6998
  if (dependencies.observeFile(candidate)) return normalizePath2(candidate);
6097
6999
  }
6098
7000
  if (directory === workspaceRoot || directory === root || !isWithin(workspaceRoot, directory))
6099
7001
  break;
6100
- directory = path8.dirname(directory);
7002
+ directory = path9.dirname(directory);
6101
7003
  }
6102
7004
  return void 0;
6103
7005
  }
@@ -6158,7 +7060,10 @@ async function evaluateModules(workspaceRoot, tsConfig, configFile, descriptions
6158
7060
  treeShaking: true,
6159
7061
  tsconfig: normalizeAbsolute(tsConfig, workspaceRoot),
6160
7062
  logLevel: "silent",
6161
- plugins: [descriptionSanitizer(modules, dependencies, probes)]
7063
+ plugins: [
7064
+ ...configFile ? [browserModuleImports(configFile, dependencies)] : [],
7065
+ descriptionSanitizer(modules, dependencies, probes)
7066
+ ]
6162
7067
  });
6163
7068
  throwIfAborted(signal);
6164
7069
  const inputs = result.metafile?.inputs ?? {};
@@ -6218,6 +7123,35 @@ async function evaluateModules(workspaceRoot, tsConfig, configFile, descriptions
6218
7123
  throw error;
6219
7124
  }
6220
7125
  }
7126
+ var BROWSER_MODULE = "ng-doc-browser-module:";
7127
+ function browserModuleImports(configFile, dependencies) {
7128
+ const config = normalizePath2(path9.resolve(configFile));
7129
+ return {
7130
+ name: "ng-doc-browser-module-imports",
7131
+ setup(build2) {
7132
+ build2.onResolve({ filter: /.*/ }, async (args) => {
7133
+ if (args.kind !== "dynamic-import" || args.pluginData?.ngDocBrowserModule || !path9.isAbsolute(args.importer) || normalizePath2(path9.resolve(args.importer)) !== config) {
7134
+ return void 0;
7135
+ }
7136
+ if (path9.isAbsolute(args.path) || /^\.{1,2}\//.test(args.path)) {
7137
+ observeResolutionCandidates(path9.resolve(args.resolveDir, args.path), dependencies);
7138
+ }
7139
+ const resolved = await build2.resolve(args.path, {
7140
+ kind: "dynamic-import",
7141
+ importer: args.importer,
7142
+ resolveDir: args.resolveDir,
7143
+ pluginData: { ngDocBrowserModule: true }
7144
+ });
7145
+ if (resolved.errors.length || !resolved.path || resolved.external) {
7146
+ return { path: args.path, external: true };
7147
+ }
7148
+ const file = normalizeAbsolute(resolved.path);
7149
+ dependencies.add({ kind: "existence", path: file, exists: true });
7150
+ return { path: `${BROWSER_MODULE}${encodeURIComponent(file)}`, external: true };
7151
+ });
7152
+ }
7153
+ };
7154
+ }
6221
7155
  function descriptionSanitizer(modules, dependencies, probes) {
6222
7156
  const kinds = new Map(modules.map((item) => [normalizePath2(item.source), item.kind]));
6223
7157
  const importers = /* @__PURE__ */ new Map();
@@ -6225,10 +7159,10 @@ function descriptionSanitizer(modules, dependencies, probes) {
6225
7159
  name: "ng-doc-description-sanitizer",
6226
7160
  setup(build2) {
6227
7161
  build2.onResolve({ filter: /.*/ }, (args) => {
6228
- if (!path8.isAbsolute(args.path) && !/^\.{1,2}\//.test(args.path)) return void 0;
6229
- const target = path8.resolve(args.resolveDir, args.path);
7162
+ if (!path9.isAbsolute(args.path) && !/^\.{1,2}\//.test(args.path)) return void 0;
7163
+ const target = path9.resolve(args.resolveDir, args.path);
6230
7164
  const candidates = observeResolutionCandidates(target, dependencies);
6231
- if (path8.isAbsolute(args.importer)) {
7165
+ if (path9.isAbsolute(args.importer)) {
6232
7166
  let importer = importers.get(args.importer);
6233
7167
  if (importer === void 0) {
6234
7168
  importer = normalizeAbsolute(args.importer);
@@ -6248,7 +7182,7 @@ function descriptionSanitizer(modules, dependencies, probes) {
6248
7182
  kinds.get(normalizePath2(args.path)) ?? kindFor(args.path)
6249
7183
  ),
6250
7184
  loader: "ts",
6251
- resolveDir: path8.dirname(args.path)
7185
+ resolveDir: path9.dirname(args.path)
6252
7186
  };
6253
7187
  });
6254
7188
  build2.onLoad({ filter: /\.(?:[cm]?[jt]s|[jt]sx|json)$/, namespace: "file" }, (args) => {
@@ -6257,8 +7191,8 @@ function descriptionSanitizer(modules, dependencies, probes) {
6257
7191
  if (!contents) return void 0;
6258
7192
  return {
6259
7193
  contents,
6260
- loader: BUNDLE_LOADERS[path8.extname(args.path).toLowerCase()] ?? "js",
6261
- resolveDir: path8.dirname(args.path)
7194
+ loader: BUNDLE_LOADERS[path9.extname(args.path).toLowerCase()] ?? "js",
7195
+ resolveDir: path9.dirname(args.path)
6262
7196
  };
6263
7197
  });
6264
7198
  }
@@ -6289,7 +7223,7 @@ function sanitizeDescription(source, kind) {
6289
7223
  (declaration) => ts.isIdentifier(declaration.name) && declaration.name.text === exportName
6290
7224
  )?.initializer : void 0;
6291
7225
  const transformer = (context) => {
6292
- const visit8 = (node) => {
7226
+ const visit9 = (node) => {
6293
7227
  if (node === root && ts.isObjectLiteralExpression(node)) {
6294
7228
  const properties = node.properties.flatMap((property) => {
6295
7229
  const name = propertyName(property);
@@ -6337,9 +7271,9 @@ function sanitizeDescription(source, kind) {
6337
7271
  });
6338
7272
  return context.factory.updateObjectLiteralExpression(node, properties);
6339
7273
  }
6340
- return ts.visitEachChild(node, visit8, context);
7274
+ return ts.visitEachChild(node, visit9, context);
6341
7275
  };
6342
- return (node) => ts.visitNode(node, visit8);
7276
+ return (node) => ts.visitNode(node, visit9);
6343
7277
  };
6344
7278
  const transformed = ts.transform(sourceFile, [transformer]);
6345
7279
  try {
@@ -6381,7 +7315,7 @@ var EvaluationScope = class {
6381
7315
  this.timers = /* @__PURE__ */ new Set();
6382
7316
  this.disposed = false;
6383
7317
  this.networkAbort = new AbortController();
6384
- const filename = path8.join(workspaceRoot, ".ng-doc-discovery-evaluator.cjs");
7318
+ const filename = path9.join(workspaceRoot, ".ng-doc-discovery-evaluator.cjs");
6385
7319
  const module = { exports: {} };
6386
7320
  const nativeRequire = createRequire(filename);
6387
7321
  const schedule = (callback, delay, ...args) => {
@@ -6464,7 +7398,7 @@ var EvaluationScope = class {
6464
7398
  evaluate(code, timeoutMs) {
6465
7399
  this.assertActive();
6466
7400
  new vm.Script(code, {
6467
- filename: path8.join(this.workspaceRoot, ".ng-doc-discovery-evaluator.cjs")
7401
+ filename: path9.join(this.workspaceRoot, ".ng-doc-discovery-evaluator.cjs")
6468
7402
  }).runInContext(this.context, { timeout: timeoutMs });
6469
7403
  return this.context.module.exports;
6470
7404
  }
@@ -6497,6 +7431,7 @@ async function normalizeConfiguration(request, workspaceRoot, configFile, raw, d
6497
7431
  const globalKeywords = normalizeKeywords(
6498
7432
  validGlobalKeywords(raw.keywords?.keywords ?? {}, configFile ?? workspaceRoot, diagnostics)
6499
7433
  );
7434
+ const shikiLangs = shikiLanguages(raw.shiki?.langs, configFile ?? workspaceRoot, diagnostics);
6500
7435
  const remoteKeywords = [];
6501
7436
  const executables = [];
6502
7437
  const loaders = raw.keywords?.loaders ?? [];
@@ -6543,12 +7478,16 @@ async function normalizeConfiguration(request, workspaceRoot, configFile, raw, d
6543
7478
  );
6544
7479
  const headerTemplate = raw.guide?.headerTemplate ? normalizeAbsolute(raw.guide.headerTemplate, workspaceRoot) : void 0;
6545
7480
  if (headerTemplate) dependencies.observeFile(headerTemplate);
7481
+ const demoApplication = demoApplicationSettings(raw, configFile ?? workspaceRoot, diagnostics);
7482
+ if (diagnostics.some((item) => item.severity === "error")) {
7483
+ return { globalKeywords, remoteKeywords, diagnostics };
7484
+ }
6546
7485
  const withoutDigest = {
6547
7486
  projectId: request.projectId,
6548
7487
  workspaceRoot,
6549
7488
  docsRoots,
6550
7489
  tsConfig: normalizeAbsolute(raw.tsConfig ?? request.defaults.tsConfig, workspaceRoot),
6551
- outputRoot: raw.outDir === void 0 ? normalizeAbsolute(request.defaults.outputRoot, workspaceRoot) : normalizeAbsolute(path8.join(workspaceRoot, raw.outDir, "ng-doc", request.projectId)),
7490
+ outputRoot: raw.outDir === void 0 ? normalizeAbsolute(request.defaults.outputRoot, workspaceRoot) : normalizeAbsolute(path9.join(workspaceRoot, raw.outDir, "ng-doc", request.projectId)),
6552
7491
  cacheRoot: normalizeAbsolute(request.defaults.cacheRoot, workspaceRoot),
6553
7492
  routePrefix: normalizeRoute(raw.routePrefix ?? ""),
6554
7493
  guideDirectory: "guides",
@@ -6557,10 +7496,13 @@ async function normalizeConfiguration(request, workspaceRoot, configFile, raw, d
6557
7496
  inlineStyleLanguage: options.inlineStyleLanguage,
6558
7497
  anchorHeadings: raw.guide?.anchorHeadings ?? options.defaultAnchorHeadings,
6559
7498
  headerTemplate,
7499
+ ...raw.api?.protectedMembers === false ? { apiProtectedMembers: false } : {},
7500
+ ...demoApplication ? { demoApplication } : {},
6560
7501
  themes: {
6561
7502
  light: raw.shiki?.themes?.light ?? options.defaultThemes.light,
6562
7503
  dark: raw.shiki?.themes?.dark ?? options.defaultThemes.dark
6563
7504
  },
7505
+ ...shikiLangs.length ? { shikiLangs } : {},
6564
7506
  repo: raw.repoConfig ? {
6565
7507
  url: raw.repoConfig.url,
6566
7508
  platform: raw.repoConfig.platform ?? "github",
@@ -6581,12 +7523,87 @@ async function normalizeConfiguration(request, workspaceRoot, configFile, raw, d
6581
7523
  };
6582
7524
  return { configuration, globalKeywords, remoteKeywords, diagnostics };
6583
7525
  }
7526
+ var DEFAULT_DEMO_PATH = "demo-preview";
7527
+ var DEMO_PATH_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
7528
+ function demoApplicationSettings(raw, source, diagnostics) {
7529
+ const invalid = (code, message2) => {
7530
+ diagnostics.push({
7531
+ code,
7532
+ severity: "error",
7533
+ stage: "evaluation",
7534
+ message: message2,
7535
+ source: { path: source }
7536
+ });
7537
+ return void 0;
7538
+ };
7539
+ const { isolatedDemos, demoApplication, demoProviders } = raw;
7540
+ if (isolatedDemos !== void 0 && typeof isolatedDemos !== "boolean") {
7541
+ return invalid("DISCOVERY_DEMO_APPLICATION_INVALID", "isolatedDemos must be a boolean.");
7542
+ }
7543
+ const configured = demoApplication !== null && typeof demoApplication === "object";
7544
+ if (demoApplication !== void 0 && typeof demoApplication !== "boolean" && !configured) {
7545
+ return invalid(
7546
+ "DISCOVERY_DEMO_APPLICATION_INVALID",
7547
+ "demoApplication must be a boolean or an object."
7548
+ );
7549
+ }
7550
+ let demoPath;
7551
+ if (configured && demoApplication.path !== void 0) {
7552
+ const value = demoApplication.path;
7553
+ const segments = typeof value === "string" ? value.split("/").filter(Boolean) : [];
7554
+ if (!segments.length || segments.some((segment) => !DEMO_PATH_SEGMENT.test(segment))) {
7555
+ return invalid(
7556
+ "DISCOVERY_DEMO_PATH_INVALID",
7557
+ `demoApplication.path must be one or more URL segments of letters, digits, '-', '_' and '.', each starting with a letter or a digit; got ${JSON.stringify(value)}.`
7558
+ );
7559
+ }
7560
+ demoPath = segments.join("/");
7561
+ }
7562
+ const providers = demoProviders === void 0 ? void 0 : demoProvidersModule(demoProviders, invalid);
7563
+ if (demoProviders !== void 0 && !providers) return void 0;
7564
+ if (demoApplication === false && isolatedDemos === true) {
7565
+ diagnostics.push({
7566
+ code: "DISCOVERY_DEMO_APPLICATION_OFF",
7567
+ severity: "warning",
7568
+ stage: "evaluation",
7569
+ message: "isolatedDemos has no effect while demoApplication is false: demos render in the page.",
7570
+ source: { path: source }
7571
+ });
7572
+ }
7573
+ const pages = demoApplication === false ? "none" : isolatedDemos === true || demoApplication === true || configured ? "all" : void 0;
7574
+ const settings2 = {
7575
+ ...pages ? { pages } : {},
7576
+ ...isolatedDemos === true && pages !== "none" ? { isolated: true } : {},
7577
+ ...demoPath && demoPath !== DEFAULT_DEMO_PATH ? { path: demoPath } : {},
7578
+ ...providers ? { providers } : {}
7579
+ };
7580
+ return Object.keys(settings2).length ? settings2 : void 0;
7581
+ }
7582
+ function demoProvidersModule(value, invalid) {
7583
+ const usage = "write it as `demoProviders: () => import('./demo.providers')` in ng-doc.config.ts";
7584
+ if (typeof value !== "function") {
7585
+ return invalid(
7586
+ "DISCOVERY_DEMO_PROVIDERS_INVALID",
7587
+ `demoProviders must be a function: ${usage}.`
7588
+ );
7589
+ }
7590
+ const compact = Function.prototype.toString.call(value).replace(/\s+/g, "");
7591
+ const marker = `(["'\`])${BROWSER_MODULE}([^"'\`]+)\\1`;
7592
+ const match = new RegExp(`^(?:async)?\\(\\)=>\\(?import\\(${marker}\\)\\)?$`).exec(compact) ?? new RegExp(`^(?:async)?\\(\\)=>\\{returnimport\\(${marker}\\);?\\}$`).exec(compact);
7593
+ if (!match) {
7594
+ return invalid(
7595
+ "DISCOVERY_DEMO_PROVIDERS_IMPORT",
7596
+ compact.includes(BROWSER_MODULE) ? `demoProviders must only import its module: ${usage}.` : `demoProviders must import a file by a literal path that resolves from ng-doc.config.ts: ${usage}.`
7597
+ );
7598
+ }
7599
+ return decodeURIComponent(match[2]);
7600
+ }
6584
7601
  function describeEntries(projectId, workspaceRoot, routePrefix, modules, closures, allDependencies, diagnostics, tags) {
6585
7602
  const moduleByObject = /* @__PURE__ */ new Map();
6586
7603
  for (const item of modules) moduleByObject.set(item.value, item);
6587
7604
  const idBySource = /* @__PURE__ */ new Map();
6588
7605
  for (const item of modules) {
6589
- const relative3 = path8.relative(workspaceRoot, item.source);
7606
+ const relative3 = path9.relative(workspaceRoot, item.source);
6590
7607
  if (!isWithin(workspaceRoot, item.source)) {
6591
7608
  diagnostics.push({
6592
7609
  code: "DISCOVERY_SOURCE_OUTSIDE_WORKSPACE",
@@ -6699,7 +7716,7 @@ function describeEntries(projectId, workspaceRoot, routePrefix, modules, closure
6699
7716
  liveEntries.set(id, entry);
6700
7717
  if (module.kind === "guide") {
6701
7718
  const markdown = asArray2(entry.mdFile).map(
6702
- (file) => normalizeAbsolute(String(file), path8.dirname(module.source))
7719
+ (file) => normalizeAbsolute(String(file), path9.dirname(module.source))
6703
7720
  );
6704
7721
  for (const file of markdown) allDependencies.observeFile(file);
6705
7722
  return {
@@ -6772,7 +7789,7 @@ function guideKeywords(mdFile, source, dependencies) {
6772
7789
  const keywords = /* @__PURE__ */ new Set();
6773
7790
  for (const file of asArray2(mdFile)) {
6774
7791
  try {
6775
- const text = dependencies.readFile(normalizeAbsolute(String(file), path8.dirname(source)));
7792
+ const text = dependencies.readFile(normalizeAbsolute(String(file), path9.dirname(source)));
6776
7793
  const keyword = text ? matter2(text.toString("utf8")).data.keyword : void 0;
6777
7794
  if (typeof keyword === "string" && keyword) keywords.add(`*${keyword}`);
6778
7795
  } catch {
@@ -6823,6 +7840,58 @@ function validGlobalKeywords(value, source, diagnostics) {
6823
7840
  }
6824
7841
  return valid;
6825
7842
  }
7843
+ function shikiLanguages(value, source, diagnostics) {
7844
+ if (value === void 0) return [];
7845
+ const invalid = (message2) => {
7846
+ diagnostics.push({
7847
+ code: "DISCOVERY_SHIKI_LANGUAGE_INVALID",
7848
+ severity: "error",
7849
+ stage: "evaluation",
7850
+ message: message2,
7851
+ source: { path: source }
7852
+ });
7853
+ return [];
7854
+ };
7855
+ if (!Array.isArray(value))
7856
+ return invalid("shiki.langs must be an array of Shiki language registrations.");
7857
+ const languages = [];
7858
+ for (const [index, entry] of value.entries()) {
7859
+ for (const item of Array.isArray(entry) ? entry : [entry]) {
7860
+ const json = plainJson(item, 0);
7861
+ if (!json || typeof json !== "object" || Array.isArray(json) || typeof json["name"] !== "string" || !json["name"] || typeof json["scopeName"] !== "string" || !json["scopeName"])
7862
+ return invalid(
7863
+ `shiki.langs[${index}] is not a Shiki language registration: plain JSON data with a name and a scopeName. Import the grammar instead of passing a function or a promise.`
7864
+ );
7865
+ languages.push(json);
7866
+ }
7867
+ }
7868
+ return languages;
7869
+ }
7870
+ function plainJson(value, depth) {
7871
+ if (depth > 256) return void 0;
7872
+ if (value === null || typeof value === "boolean" || typeof value === "string") return value;
7873
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
7874
+ if (Array.isArray(value)) {
7875
+ const copy2 = [];
7876
+ for (const item of value) {
7877
+ const json = plainJson(item, depth + 1);
7878
+ if (json === void 0) return void 0;
7879
+ copy2.push(json);
7880
+ }
7881
+ return copy2;
7882
+ }
7883
+ if (typeof value !== "object") return void 0;
7884
+ const prototype = Object.getPrototypeOf(value);
7885
+ if (prototype !== null && Object.getPrototypeOf(prototype) !== null || Object.prototype.toString.call(value) !== "[object Object]")
7886
+ return void 0;
7887
+ const copy = {};
7888
+ for (const [key, item] of Object.entries(value)) {
7889
+ const json = plainJson(item, depth + 1);
7890
+ if (json === void 0) return void 0;
7891
+ copy[key] = json;
7892
+ }
7893
+ return copy;
7894
+ }
6826
7895
  function normalizeKeywords(value) {
6827
7896
  return Object.entries(value).sort(([left], [right]) => compareCodeUnits(left, right)).map(([key, keyword]) => ({
6828
7897
  key,
@@ -6834,12 +7903,12 @@ function normalizeKeywords(value) {
6834
7903
  }));
6835
7904
  }
6836
7905
  function observeResolutionCandidates(target, dependencies) {
6837
- const extension = path8.extname(target);
7906
+ const extension = path9.extname(target);
6838
7907
  const candidates = [".ts", ".tsx", ".js", ".mjs", ".cjs", ".json"].includes(extension) ? [target] : [
6839
7908
  target,
6840
7909
  ...[".ts", ".tsx", ".js", ".mjs", ".cjs", ".json"].map((suffix) => `${target}${suffix}`),
6841
7910
  ...[".ts", ".tsx", ".js", ".mjs", ".cjs"].map(
6842
- (suffix) => path8.join(target, `index${suffix}`)
7911
+ (suffix) => path9.join(target, `index${suffix}`)
6843
7912
  )
6844
7913
  ];
6845
7914
  for (const candidate of candidates)
@@ -6851,7 +7920,7 @@ function observeResolutionCandidates(target, dependencies) {
6851
7920
  return candidates.map(normalizePath2);
6852
7921
  }
6853
7922
  function kindFor(file) {
6854
- const basename2 = path8.basename(file);
7923
+ const basename2 = path9.basename(file);
6855
7924
  const kind = ENTRY_KIND.get(basename2);
6856
7925
  if (!kind) throw new Error(`Unsupported description filename: ${file}`);
6857
7926
  return kind;
@@ -6861,12 +7930,12 @@ function entryRoute(kind, entry, source) {
6861
7930
  if (kind === "guide") {
6862
7931
  if (typeof route === "object" && route !== null)
6863
7932
  return normalizeRoute(
6864
- String(route.path ?? path8.basename(path8.dirname(source)))
7933
+ String(route.path ?? path9.basename(path9.dirname(source)))
6865
7934
  );
6866
- return normalizeRoute(String(route ?? path8.basename(path8.dirname(source))));
7935
+ return normalizeRoute(String(route ?? path9.basename(path9.dirname(source))));
6867
7936
  }
6868
7937
  if (kind === "api") return normalizeRoute(String(route ?? "api"));
6869
- return normalizeRoute(String(route ?? path8.basename(path8.dirname(source))));
7938
+ return normalizeRoute(String(route ?? path9.basename(path9.dirname(source))));
6870
7939
  }
6871
7940
  function stableId(projectId, kind, relativeSource) {
6872
7941
  return `${projectId}:${kind}:${sha256Hex(relativeSource).slice(0, 16)}`;
@@ -6879,23 +7948,23 @@ function digestDependencies(dependencies) {
6879
7948
  );
6880
7949
  }
6881
7950
  function normalizeAbsolute(value, base) {
6882
- const absolute = path8.isAbsolute(value) ? path8.resolve(value) : path8.resolve(base ?? process.cwd(), value);
7951
+ const absolute = path9.isAbsolute(value) ? path9.resolve(value) : path9.resolve(base ?? process.cwd(), value);
6883
7952
  if (existsSync6(absolute)) return normalizePath2(realpathSync2.native(absolute));
6884
- const existingParent = nearestExistingParent(path8.dirname(absolute));
7953
+ const existingParent = nearestExistingParent(path9.dirname(absolute));
6885
7954
  const canonicalParent = realpathSync2.native(existingParent);
6886
- return normalizePath2(path8.join(canonicalParent, path8.relative(existingParent, absolute)));
7955
+ return normalizePath2(path9.join(canonicalParent, path9.relative(existingParent, absolute)));
6887
7956
  }
6888
7957
  function nearestExistingParent(value) {
6889
- let current = value;
6890
- while (!existsSync6(current)) {
6891
- const parent = path8.dirname(current);
6892
- if (parent === current) return current;
6893
- current = parent;
7958
+ let current2 = value;
7959
+ while (!existsSync6(current2)) {
7960
+ const parent = path9.dirname(current2);
7961
+ if (parent === current2) return current2;
7962
+ current2 = parent;
6894
7963
  }
6895
- return current;
7964
+ return current2;
6896
7965
  }
6897
7966
  function normalizePath2(value) {
6898
- return value.replaceAll(path8.sep, "/");
7967
+ return value.replaceAll(path9.sep, "/");
6899
7968
  }
6900
7969
  function normalizeRoute(value) {
6901
7970
  return value.replaceAll("\\", "/").split("/").filter(Boolean).join("/");
@@ -6979,7 +8048,7 @@ var FUNCTION_KEYS = /* @__PURE__ */ new Set([
6979
8048
  function canonicalValue(root) {
6980
8049
  const seen = /* @__PURE__ */ new Map();
6981
8050
  let functions = false;
6982
- const visit8 = (value2, at) => {
8051
+ const visit9 = (value2, at) => {
6983
8052
  if (value2 === null || typeof value2 === "string" || typeof value2 === "boolean") return value2;
6984
8053
  if (typeof value2 === "number")
6985
8054
  return Number.isFinite(value2) ? value2 : { $number: String(value2) };
@@ -7013,11 +8082,11 @@ function canonicalValue(root) {
7013
8082
  if (typeof key === "symbol" || skipped.has(key)) continue;
7014
8083
  const descriptor = Object.getOwnPropertyDescriptor(object, key);
7015
8084
  if (!("value" in descriptor)) throw new Unattested(`accessor at ${at}.${key}`);
7016
- result.push([key, visit8(descriptor.value, `${at}.${key}`)]);
8085
+ result.push([key, visit9(descriptor.value, `${at}.${key}`)]);
7017
8086
  }
7018
8087
  return result.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
7019
8088
  };
7020
- const value = visit8(root, "$");
8089
+ const value = visit9(root, "$");
7021
8090
  return { value, functions };
7022
8091
  }
7023
8092
  function sortDependencies(dependencies) {
@@ -7035,8 +8104,8 @@ function dependenciesForEntry(all, source, related = []) {
7035
8104
  return all.filter((item) => "path" in item && paths.has(item.path));
7036
8105
  }
7037
8106
  function isWithin(parent, child) {
7038
- const relative3 = path8.relative(parent, child);
7039
- return relative3 !== ".." && !relative3.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relative3);
8107
+ const relative3 = path9.relative(parent, child);
8108
+ return relative3 !== ".." && !relative3.startsWith(`..${path9.sep}`) && !path9.isAbsolute(relative3);
7040
8109
  }
7041
8110
  function throwIfAborted(signal) {
7042
8111
  if (signal.aborted) throw signal.reason ?? new Error("Discovery was aborted.");
@@ -7065,27 +8134,27 @@ function awaitWithLimits(value, signal, timeoutMs) {
7065
8134
  });
7066
8135
  }
7067
8136
  function jsonData(value, omitOptional = true) {
7068
- const active = /* @__PURE__ */ new Set();
7069
- function visit8(input) {
8137
+ const active2 = /* @__PURE__ */ new Set();
8138
+ function visit9(input) {
7070
8139
  if (input === null || typeof input === "string" || typeof input === "boolean") return input;
7071
8140
  if (typeof input === "number" && Number.isFinite(input)) return input;
7072
- if (typeof input !== "object" || input === null || active.has(input)) {
8141
+ if (typeof input !== "object" || input === null || active2.has(input)) {
7073
8142
  throw new TypeError("Discovery produced a non-JSON value.");
7074
8143
  }
7075
- active.add(input);
8144
+ active2.add(input);
7076
8145
  try {
7077
- if (Array.isArray(input)) return Array.from(input, visit8);
8146
+ if (Array.isArray(input)) return Array.from(input, visit9);
7078
8147
  if (Object.prototype.toString.call(input) !== "[object Object]") {
7079
8148
  throw new TypeError("Discovery produced a non-JSON object.");
7080
8149
  }
7081
8150
  return Object.fromEntries(
7082
- Object.entries(input).filter(([, item]) => !omitOptional || item !== void 0).map(([key, item]) => [key, visit8(item)])
8151
+ Object.entries(input).filter(([, item]) => !omitOptional || item !== void 0).map(([key, item]) => [key, visit9(item)])
7083
8152
  );
7084
8153
  } finally {
7085
- active.delete(input);
8154
+ active2.delete(input);
7086
8155
  }
7087
8156
  }
7088
- return visit8(value);
8157
+ return visit9(value);
7089
8158
  }
7090
8159
  function failure(dependencies, diagnostics) {
7091
8160
  return { dependencies: dependencies.all(), diagnostics };
@@ -7107,7 +8176,7 @@ function isBuildFailure(error) {
7107
8176
  }
7108
8177
 
7109
8178
  // libs/builder/generator/outputs/index.ts
7110
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
8179
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
7111
8180
  import { dirname, isAbsolute, posix, relative, resolve, sep } from "node:path";
7112
8181
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7113
8182
  import { Environment, Loader } from "nunjucks";
@@ -7142,8 +8211,43 @@ var STRUCTURAL_TEMPLATES = [
7142
8211
  "api-list.ts.nunj",
7143
8212
  "routes.ts.nunj",
7144
8213
  "context.ts.nunj",
7145
- "index.ts.nunj"
8214
+ "index.ts.nunj",
8215
+ "demo-routes.ts.nunj",
8216
+ "demo-app.ts.nunj"
7146
8217
  ];
8218
+ var DEFAULT_DEMO_PATH2 = "demo-preview";
8219
+ var DEMO_NAME = /^[A-Za-z0-9_$-]+$/;
8220
+ var DEMO_ACTION = /<ng-doc-demo\b[^>]*>\s*<div\b[^>]*\bid="options"[^>]*>([^<]*)<\/div>/g;
8221
+ function demoSettings(configuration) {
8222
+ const settings2 = configuration.demoApplication;
8223
+ return {
8224
+ pages: settings2?.pages ?? "isolated",
8225
+ isolated: settings2?.isolated === true,
8226
+ path: settings2?.path ?? DEFAULT_DEMO_PATH2,
8227
+ ...settings2?.providers ? { providers: settings2.providers } : {}
8228
+ };
8229
+ }
8230
+ function decodeHtmlText(value) {
8231
+ return value.replace(
8232
+ /&(?:#x([0-9a-f]+)|#([0-9]+)|(amp|lt|gt|quot|apos));/gi,
8233
+ (all, hex, dec, name) => {
8234
+ if (hex) return String.fromCodePoint(parseInt(hex, 16));
8235
+ if (dec) return String.fromCodePoint(parseInt(dec, 10));
8236
+ return { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" }[String(name).toLowerCase()] ?? all;
8237
+ }
8238
+ );
8239
+ }
8240
+ function hasIsolatedDemo(html) {
8241
+ for (const match of html?.matchAll(DEMO_ACTION) ?? []) {
8242
+ try {
8243
+ if (JSON.parse(decodeHtmlText(match[1]))?.isolated === true) {
8244
+ return true;
8245
+ }
8246
+ } catch {
8247
+ }
8248
+ }
8249
+ return false;
8250
+ }
7147
8251
  function pageAssemblyKey(request, templates) {
7148
8252
  const text = canonicalJsonStrict({
7149
8253
  schema: "page-assembly-key-v1",
@@ -7172,7 +8276,7 @@ function moduleSpecifier(fromDirectory, target) {
7172
8276
  return specifier.startsWith(".") ? specifier : `./${specifier}`;
7173
8277
  }
7174
8278
  function templateString(value) {
7175
- return String(value ?? "").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/{/g, "\\{").replace(/}/g, "\\}");
8279
+ return String(value ?? "").replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/{/g, "\\{").replace(/}/g, "\\}");
7176
8280
  }
7177
8281
  function escapeAttribute2(value) {
7178
8282
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -7205,24 +8309,24 @@ function safeRelative(value) {
7205
8309
  function joinOutput(...parts) {
7206
8310
  return safeRelative(posix.join(...parts.map(normalize)));
7207
8311
  }
7208
- function output(path17, role, content) {
7209
- return { path: path17, role, encoding: "utf8", content, digest: sha256Hex(content) };
8312
+ function output(path18, role, content) {
8313
+ return { path: path18, role, encoding: "utf8", content, digest: sha256Hex(content) };
7210
8314
  }
7211
8315
  function addOutput(outputs, candidate, diagnostics, ownerId) {
7212
- const path17 = safeRelative(candidate.path);
7213
- if (!path17) {
8316
+ const path18 = safeRelative(candidate.path);
8317
+ if (!path18) {
7214
8318
  diagnostics.push(
7215
8319
  diagnostic2("OUTPUT_PATH_INVALID", `Unsafe generated output path: ${candidate.path}`, ownerId)
7216
8320
  );
7217
8321
  return;
7218
8322
  }
7219
- if (outputs.has(path17)) {
8323
+ if (outputs.has(path18)) {
7220
8324
  diagnostics.push(
7221
- diagnostic2("OUTPUT_COLLISION", `Generated output collision: ${path17}`, ownerId)
8325
+ diagnostic2("OUTPUT_COLLISION", `Generated output collision: ${path18}`, ownerId)
7222
8326
  );
7223
8327
  return;
7224
8328
  }
7225
- outputs.set(path17, { ...candidate, path: path17 });
8329
+ outputs.set(path18, { ...candidate, path: path18 });
7226
8330
  }
7227
8331
  function isWithin2(parent, child) {
7228
8332
  const value = relative(resolve(parent), resolve(child));
@@ -7271,29 +8375,29 @@ function apiListType(declaration) {
7271
8375
  return kind ? `${kind[0].toUpperCase()}${kind.slice(1)}` : "";
7272
8376
  }
7273
8377
  function strictJson(value) {
7274
- const active = /* @__PURE__ */ new Set();
7275
- const visit8 = (item) => {
8378
+ const active2 = /* @__PURE__ */ new Set();
8379
+ const visit9 = (item) => {
7276
8380
  if (item === null || typeof item === "string" || typeof item === "boolean") return;
7277
8381
  if (typeof item === "number" && Number.isFinite(item) && !Object.is(item, -0)) return;
7278
- if (typeof item !== "object" || item === null || active.has(item)) {
8382
+ if (typeof item !== "object" || item === null || active2.has(item)) {
7279
8383
  throw new TypeError("Aggregate JSON must be a finite, acyclic JSON tree.");
7280
8384
  }
7281
- active.add(item);
8385
+ active2.add(item);
7282
8386
  try {
7283
8387
  if (Array.isArray(item)) {
7284
8388
  if (Object.keys(item).length !== item.length)
7285
8389
  throw new TypeError("Aggregate JSON arrays cannot be sparse.");
7286
- item.forEach(visit8);
8390
+ item.forEach(visit9);
7287
8391
  } else {
7288
8392
  if (Object.prototype.toString.call(item) !== "[object Object]")
7289
8393
  throw new TypeError("Aggregate JSON contains an unsupported object.");
7290
- for (const child of Object.values(item)) visit8(child);
8394
+ for (const child of Object.values(item)) visit9(child);
7291
8395
  }
7292
8396
  } finally {
7293
- active.delete(item);
8397
+ active2.delete(item);
7294
8398
  }
7295
8399
  };
7296
- visit8(value);
8400
+ visit9(value);
7297
8401
  return JSON.stringify(value);
7298
8402
  }
7299
8403
  function tree(values, diagnostics) {
@@ -7348,27 +8452,27 @@ function pruneEmptyCategories(nodes) {
7348
8452
  }
7349
8453
  function validateRoutePaths(nodes, diagnostics, parentPath = "", seen = /* @__PURE__ */ new Map()) {
7350
8454
  for (const node of nodes) {
7351
- const path17 = posix.join(parentPath, normalize(node.item.route)).replace(/^\/+|\/+$/g, "");
7352
- const owner = seen.get(path17);
8455
+ const path18 = posix.join(parentPath, normalize(node.item.route)).replace(/^\/+|\/+$/g, "");
8456
+ const owner = seen.get(path18);
7353
8457
  if (owner && owner !== node.item.id) {
7354
8458
  diagnostics.push(
7355
8459
  diagnostic2(
7356
8460
  "OUTPUT_ROUTE_PATH_COLLISION",
7357
- `Routes ${owner} and ${node.item.id} resolve to the same public path: ${path17}`,
8461
+ `Routes ${owner} and ${node.item.id} resolve to the same public path: ${path18}`,
7358
8462
  node.item.id
7359
8463
  )
7360
8464
  );
7361
8465
  } else {
7362
- seen.set(path17, node.item.id);
8466
+ seen.set(path18, node.item.id);
7363
8467
  }
7364
- validateRoutePaths(node.children, diagnostics, path17, seen);
8468
+ validateRoutePaths(node.children, diagnostics, path18, seen);
7365
8469
  }
7366
8470
  }
7367
8471
  function validateOutputOwnership(artifacts, aggregate, diagnostics) {
7368
8472
  const owners = /* @__PURE__ */ new Map();
7369
8473
  const observe2 = (candidate, ownerId) => {
7370
- const path17 = safeRelative(candidate.path);
7371
- if (!path17) {
8474
+ const path18 = safeRelative(candidate.path);
8475
+ if (!path18) {
7372
8476
  diagnostics.push(
7373
8477
  diagnostic2(
7374
8478
  "OUTPUT_PATH_INVALID",
@@ -7378,18 +8482,18 @@ function validateOutputOwnership(artifacts, aggregate, diagnostics) {
7378
8482
  );
7379
8483
  return;
7380
8484
  }
7381
- const previous = owners.get(path17);
8485
+ const previous = owners.get(path18);
7382
8486
  if (previous) {
7383
8487
  diagnostics.push(
7384
8488
  diagnostic2(
7385
8489
  "OUTPUT_CANDIDATE_COLLISION",
7386
- `Artifacts ${previous} and ${ownerId} both own generated output ${path17}.`,
8490
+ `Artifacts ${previous} and ${ownerId} both own generated output ${path18}.`,
7387
8491
  ownerId
7388
8492
  )
7389
8493
  );
7390
8494
  return;
7391
8495
  }
7392
- owners.set(path17, ownerId);
8496
+ owners.set(path18, ownerId);
7393
8497
  };
7394
8498
  for (const artifact of artifacts) {
7395
8499
  for (const candidate of artifact.outputs) observe2(candidate, artifact.id);
@@ -7398,11 +8502,11 @@ function validateOutputOwnership(artifacts, aggregate, diagnostics) {
7398
8502
  }
7399
8503
  function keywordMap(keywords) {
7400
8504
  return Object.fromEntries(
7401
- keywords.map(({ key, title, path: path17, type, languages, description }) => [
8505
+ keywords.map(({ key, title, path: path18, type, languages, description }) => [
7402
8506
  key,
7403
8507
  {
7404
8508
  title,
7405
- path: path17,
8509
+ path: path18,
7406
8510
  ...type === void 0 ? {} : { type },
7407
8511
  ...languages === void 0 ? {} : { languages },
7408
8512
  ...description === void 0 ? {} : { description }
@@ -7420,7 +8524,7 @@ var StructuralLoader = class extends Loader {
7420
8524
  if (!isWithin2(this.root, source) || !existsSync7(source)) {
7421
8525
  throw new Error(`Structural template not found: ${source}`);
7422
8526
  }
7423
- return { src: readFileSync6(source, "utf8"), path: source, noCache: true };
8527
+ return { src: readFileSync7(source, "utf8"), path: source, noCache: true };
7424
8528
  }
7425
8529
  };
7426
8530
  function render2(root, name, context) {
@@ -7431,7 +8535,7 @@ function render2(root, name, context) {
7431
8535
  environment.addFilter("toTemplateString", templateString);
7432
8536
  environment.addFilter(
7433
8537
  "createImportPath",
7434
- (current, target) => relativeImport(current, target)
8538
+ (current2, target) => relativeImport(current2, target)
7435
8539
  );
7436
8540
  environment.addFilter("dump", (value) => strictJson(value));
7437
8541
  environment.addFilter(
@@ -7628,7 +8732,7 @@ function validateDescriptorPartition(request, diagnostics) {
7628
8732
  const markdown = descriptors.filter((descriptor) => descriptor.role === "guide-tab").map(
7629
8733
  (descriptor) => descriptor.locator.kind === "guide-tab" ? descriptor.locator.markdown : ""
7630
8734
  );
7631
- if (markdown.length !== guide.markdown.length || new Set(markdown).size !== markdown.length || markdown.some((path17, index) => path17 !== guide.markdown[index]) || descriptors.some((descriptor) => descriptor.role === "api-tab"))
8735
+ if (markdown.length !== guide.markdown.length || new Set(markdown).size !== markdown.length || markdown.some((path18, index) => path18 !== guide.markdown[index]) || descriptors.some((descriptor) => descriptor.role === "api-tab"))
7632
8736
  diagnostics.push(
7633
8737
  diagnostic2(
7634
8738
  "OUTPUT_DESCRIPTOR_MEMBERSHIP",
@@ -7640,18 +8744,18 @@ function validateDescriptorPartition(request, diagnostics) {
7640
8744
  const closures = new Map(descriptors.map((descriptor) => [descriptor.id, descriptor.closureIds]));
7641
8745
  const visiting = /* @__PURE__ */ new Set();
7642
8746
  const visited2 = /* @__PURE__ */ new Set();
7643
- const visit8 = (id) => {
8747
+ const visit9 = (id) => {
7644
8748
  if (visiting.has(id)) return false;
7645
8749
  if (visited2.has(id)) return true;
7646
8750
  visiting.add(id);
7647
8751
  const ok = (closures.get(id) ?? []).every(
7648
- (dependency) => ids.has(dependency) && visit8(dependency)
8752
+ (dependency) => ids.has(dependency) && visit9(dependency)
7649
8753
  );
7650
8754
  visiting.delete(id);
7651
8755
  visited2.add(id);
7652
8756
  return ok;
7653
8757
  };
7654
- if (![...ids].every(visit8))
8758
+ if (![...ids].every(visit9))
7655
8759
  diagnostics.push(
7656
8760
  diagnostic2(
7657
8761
  "OUTPUT_DESCRIPTOR_CLOSURE",
@@ -7706,15 +8810,15 @@ var OutputAssemblerImpl = class {
7706
8810
  const dependencies = [];
7707
8811
  const diagnostics = [];
7708
8812
  for (const name of STRUCTURAL_TEMPLATES) {
7709
- const path17 = hostPath(resolve(this.templateRoot, name));
8813
+ const path18 = hostPath(resolve(this.templateRoot, name));
7710
8814
  try {
7711
- dependencies.push({ kind: "content", path: path17, digest: (await readTextFile(path17)).digest });
8815
+ dependencies.push({ kind: "content", path: path18, digest: (await readTextFile(path18)).digest });
7712
8816
  } catch (error) {
7713
- dependencies.push({ kind: "existence", path: path17, exists: false });
8817
+ dependencies.push({ kind: "existence", path: path18, exists: false });
7714
8818
  diagnostics.push(
7715
8819
  diagnostic2(
7716
8820
  "OUTPUT_TEMPLATE_READ",
7717
- `Cannot read structural template ${path17}: ${String(error)}`
8821
+ `Cannot read structural template ${path18}: ${String(error)}`
7718
8822
  )
7719
8823
  );
7720
8824
  }
@@ -7791,7 +8895,9 @@ var OutputAssemblerImpl = class {
7791
8895
  // Every API list the site has, so the search palette requests no other one.
7792
8896
  apiLists: request.entries.filter((entry) => entry.kind === "api").map((entry) => apiAssetRoute(entry, request.configuration))
7793
8897
  });
7794
- this.renderOutput(outputs, diagnostics, "index.ts", "angular", "index.ts.nunj", {});
8898
+ this.renderOutput(outputs, diagnostics, "index.ts", "angular", "index.ts.nunj", {
8899
+ demoApplication: this.demoApplication(request, records, outputs, diagnostics)
8900
+ });
7795
8901
  const searchPath = joinOutput(request.configuration.assetDirectory, "indexes.json");
7796
8902
  const keywordPath = joinOutput(request.configuration.assetDirectory, "keywords.json");
7797
8903
  if (!searchPath || !keywordPath) {
@@ -7855,6 +8961,8 @@ var OutputAssemblerImpl = class {
7855
8961
  const wrapperPath = joinOutput(directory, "page.ts");
7856
8962
  const demoPath = joinOutput(directory, "demo-assets.ts");
7857
8963
  const playgroundPath = joinOutput(directory, "playgrounds.ts");
8964
+ const demoPages = this.demoRoutes(request, directory, [header2, ...tabs], outputs, diagnostics);
8965
+ const demoSettingsOfPage = demoSettings(request.configuration);
7858
8966
  const tabEntries = {};
7859
8967
  for (const tab of tabs) {
7860
8968
  const tabPath = joinOutput(directory, tab.route || "index", "page.ts");
@@ -7884,6 +8992,10 @@ var OutputAssemblerImpl = class {
7884
8992
  pageType: "guide",
7885
8993
  entryPath: request.entry.runtimeImport.source,
7886
8994
  entryHasImports: request.entry.hasImports,
8995
+ ...demoPages ? {
8996
+ demoRoute: `${demoSettingsOfPage.path}/${request.entry.absoluteRoute}`,
8997
+ isolatedDemos: demoSettingsOfPage.isolated
8998
+ } : {},
7887
8999
  demoAssetsPath: resolve(request.configuration.outputRoot, demoPath),
7888
9000
  playgroundsPath: resolve(request.configuration.outputRoot, playgroundPath)
7889
9001
  },
@@ -7979,8 +9091,55 @@ var OutputAssemblerImpl = class {
7979
9091
  ...request.entry.order === void 0 ? {} : { order: request.entry.order },
7980
9092
  ...request.entry.hidden === void 0 ? {} : { hidden: request.entry.hidden },
7981
9093
  ...request.metadata ? { metadata: request.metadata } : {},
7982
- modulePath: wrapperPath
9094
+ modulePath: wrapperPath,
9095
+ ...demoPages ? { demoModulePath: demoPages.path, demoNames: demoPages.names } : {}
9096
+ });
9097
+ }
9098
+ /**
9099
+ * Writes the guide's demo routes module (one route per demo, under the page's own providers)
9100
+ * when the guide gets demo pages: with `pages: 'all'`, or by default when its content has a demo
9101
+ * with `isolated: true`. Returns its path and the names of the demos that got a page, or
9102
+ * undefined.
9103
+ */
9104
+ demoRoutes(request, directory, slots, outputs, diagnostics) {
9105
+ const settings2 = demoSettings(request.configuration);
9106
+ const names = Object.keys(request.semantics?.demos ?? {});
9107
+ if (settings2.pages === "none" || !names.length || settings2.pages === "isolated" && !slots.some((slot) => hasIsolatedDemo(slot.linked?.html))) {
9108
+ return void 0;
9109
+ }
9110
+ const demos = names.filter((name) => {
9111
+ if (DEMO_NAME.test(name)) return true;
9112
+ diagnostics.push({
9113
+ ...diagnostic2(
9114
+ "OUTPUT_DEMO_NAME_INVALID",
9115
+ `Demo ${JSON.stringify(name)} has no demo page: a demo name must be letters, digits, '_', '$' or '-' to be its page's URL segment.`,
9116
+ request.entry.id
9117
+ ),
9118
+ severity: "warning"
9119
+ });
9120
+ return false;
7983
9121
  });
9122
+ const path18 = joinOutput(directory, "demo-routes.ts");
9123
+ if (!path18 || !demos.length) return void 0;
9124
+ this.renderOutput(
9125
+ outputs,
9126
+ diagnostics,
9127
+ path18,
9128
+ "angular",
9129
+ "demo-routes.ts.nunj",
9130
+ {
9131
+ entryImport: moduleSpecifier(
9132
+ dirname(resolve(request.configuration.outputRoot, path18)),
9133
+ request.entry.runtimeImport.source
9134
+ ),
9135
+ demos: demos.map((name) => ({
9136
+ name,
9137
+ title: `${request.entry.title}: ${name}`
9138
+ }))
9139
+ },
9140
+ request.entry.id
9141
+ );
9142
+ return outputs.has(path18) ? { path: path18, names: demos } : void 0;
7984
9143
  }
7985
9144
  apiEntry(request, outputs, routes, diagnostics) {
7986
9145
  const assetRoute = apiAssetRoute(request.entry, request.configuration);
@@ -8146,18 +9305,18 @@ var OutputAssemblerImpl = class {
8146
9305
  ...record.signature ? { signature: record.signature } : {}
8147
9306
  }))
8148
9307
  }));
8149
- const path17 = joinOutput(
9308
+ const path18 = joinOutput(
8150
9309
  request.configuration.assetDirectory,
8151
9310
  apiAssetRoute(entry, request.configuration),
8152
9311
  "api-list.json"
8153
9312
  );
8154
- if (!path17) {
9313
+ if (!path18) {
8155
9314
  diagnostics.push(
8156
9315
  diagnostic2("OUTPUT_PATH_INVALID", `Unsafe API asset route: ${entry.route}`)
8157
9316
  );
8158
9317
  continue;
8159
9318
  }
8160
- addOutput(outputs, output(path17, "api-list", strictJson(lists)), diagnostics, entry.id);
9319
+ addOutput(outputs, output(path18, "api-list", strictJson(lists)), diagnostics, entry.id);
8161
9320
  }
8162
9321
  }
8163
9322
  addContentSource(outputs, diagnostics, componentPath, slot, configuration, ownerId) {
@@ -8210,11 +9369,48 @@ export default source;
8210
9369
  ownerId
8211
9370
  );
8212
9371
  }
8213
- renderOutput(outputs, diagnostics, path17, role, template, context, ownerId) {
8214
- const safe = safeRelative(path17);
9372
+ /**
9373
+ * Writes `demo-app.ts`, the routes of the demo application (one lazy route per guide with demo
9374
+ * pages, under its categories' providers) and its `demoProviders` import, when a guide has demo
9375
+ * pages. Returns whether it did.
9376
+ */
9377
+ demoApplication(request, records, outputs, diagnostics) {
9378
+ const settings2 = demoSettings(request.configuration);
9379
+ const outputRoot = resolve(request.configuration.outputRoot);
9380
+ const entries = new Map(request.entries.map((entry) => [entry.id, entry]));
9381
+ const categories = /* @__PURE__ */ new Map();
9382
+ const pages = records.filter((record) => record.demoModulePath && entries.get(record.id)?.kind === "guide").map((record) => {
9383
+ const chain = [];
9384
+ for (let parent = entries.get(record.id)?.parentId; parent !== void 0; parent = entries.get(parent)?.parentId) {
9385
+ const category = entries.get(parent);
9386
+ if (category?.kind !== "category") break;
9387
+ const identifier = categoryIdentifier(request.configuration.projectId, category.id);
9388
+ categories.set(identifier, moduleSpecifier(outputRoot, category.runtimeImport.source));
9389
+ chain.unshift(identifier);
9390
+ }
9391
+ const route = `${settings2.path}/${entries.get(record.id).absoluteRoute}`;
9392
+ return {
9393
+ path: route,
9394
+ demos: (record.demoNames ?? []).map((name) => `${route}/${name}`),
9395
+ categories: chain,
9396
+ importPath: moduleSpecifier(outputRoot, resolve(outputRoot, record.demoModulePath))
9397
+ };
9398
+ }).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
9399
+ if (!pages.length) return false;
9400
+ this.renderOutput(outputs, diagnostics, "demo-app.ts", "routes", "demo-app.ts.nunj", {
9401
+ demoPath: settings2.path,
9402
+ demoPages: pages.flatMap((page) => page.demos),
9403
+ pages,
9404
+ categories: [...categories].sort(([left], [right]) => left < right ? -1 : 1),
9405
+ ...settings2.providers ? { providersImport: moduleSpecifier(outputRoot, settings2.providers) } : {}
9406
+ });
9407
+ return true;
9408
+ }
9409
+ renderOutput(outputs, diagnostics, path18, role, template, context, ownerId) {
9410
+ const safe = safeRelative(path18);
8215
9411
  if (!safe) {
8216
9412
  diagnostics.push(
8217
- diagnostic2("OUTPUT_PATH_INVALID", `Unsafe generated output path: ${path17}`, ownerId)
9413
+ diagnostic2("OUTPUT_PATH_INVALID", `Unsafe generated output path: ${path18}`, ownerId)
8218
9414
  );
8219
9415
  return;
8220
9416
  }
@@ -8240,6 +9436,30 @@ function createOutputAssembler(options = {}) {
8240
9436
  return new OutputAssemblerImpl(options);
8241
9437
  }
8242
9438
 
9439
+ // libs/builder/helpers/format-cache.ts
9440
+ var active;
9441
+ function withFormatCodeCache(cache, run) {
9442
+ const previous = active;
9443
+ active = cache;
9444
+ try {
9445
+ return run();
9446
+ } finally {
9447
+ active = previous;
9448
+ }
9449
+ }
9450
+ function activeFormatCodeCache() {
9451
+ return active;
9452
+ }
9453
+
9454
+ // libs/builder/generator/semantic/formatting.ts
9455
+ var current;
9456
+ function useFormatCache(cache) {
9457
+ current = cache;
9458
+ }
9459
+ function formatting(run) {
9460
+ return withFormatCodeCache(current, run);
9461
+ }
9462
+
8243
9463
  // libs/builder/generator/semantic/semantic-service.ts
8244
9464
  import { dirname as dirname9, resolve as resolve9 } from "node:path";
8245
9465
  import { setImmediate as setImmediate2 } from "node:timers";
@@ -8306,7 +9526,7 @@ function getComponentInputs(cls) {
8306
9526
 
8307
9527
  // libs/builder/helpers/angular/get-component-source-files.ts
8308
9528
  init_core();
8309
- import path9 from "path";
9529
+ import path10 from "path";
8310
9530
 
8311
9531
  // libs/builder/helpers/angular/get-component-decorator.ts
8312
9532
  import { Node as Node4 } from "ts-morph";
@@ -8348,12 +9568,12 @@ function getComponentSourceFiles(cls) {
8348
9568
  const decoratorData = getComponentDecorator(cls);
8349
9569
  if (decoratorData) {
8350
9570
  const filePath = cls.getSourceFile().getFilePath();
8351
- const fileDir = path9.dirname(filePath);
9571
+ const fileDir = path10.dirname(filePath);
8352
9572
  return asArray(
8353
9573
  filePath,
8354
- decoratorData.templateUrl ? path9.join(fileDir, decoratorData.templateUrl) : [],
9574
+ decoratorData.templateUrl ? path10.join(fileDir, decoratorData.templateUrl) : [],
8355
9575
  asArray(decoratorData.styleUrls, decoratorData.styleUrl).map(
8356
- (styleUrl) => path9.join(fileDir, styleUrl)
9576
+ (styleUrl) => path10.join(fileDir, styleUrl)
8357
9577
  )
8358
9578
  );
8359
9579
  }
@@ -8626,15 +9846,18 @@ function collect(node, into, seen) {
8626
9846
  }
8627
9847
  }
8628
9848
  if (Node13.isEnumDeclaration(declaration)) {
8629
- declaration.getMembers().forEach((member) => into.push(member.getType().compilerType));
9849
+ declaration.getMembers().forEach((member) => into.push(regularType(member.getType().compilerType)));
8630
9850
  return;
8631
9851
  }
8632
9852
  }
8633
9853
  into.push(node.getType().compilerType);
8634
9854
  }
9855
+ function regularType(type) {
9856
+ return type.regularType ?? type;
9857
+ }
8635
9858
 
8636
9859
  // libs/builder/helpers/snippets-from-asset.ts
8637
- import path11 from "path";
9860
+ import path12 from "path";
8638
9861
 
8639
9862
  // libs/builder/helpers/process-legacy-snippets.ts
8640
9863
  init_core();
@@ -8687,35 +9910,7 @@ function removeSnippetsInCode(code) {
8687
9910
  // libs/builder/helpers/process-snippets.ts
8688
9911
  init_core();
8689
9912
  import * as fs from "fs";
8690
- import path10 from "path";
8691
-
8692
- // libs/builder/parsers/parse-snippet.ts
8693
- import P5 from "parsimmon";
8694
- function parseSnippet(string2) {
8695
- const parser2 = P5.createLanguage({
8696
- keyword: () => P5.string("snippet"),
8697
- id: () => P5.string("#").then(P5.regexp(/[a-zA-Z0-9-]+/)).fallback(null).map((id) => ({ id })),
8698
- lang: () => P5.string(":").then(P5.regexp(/[a-zA-Z0-9-]+/)).fallback(void 0).map((lang) => ({ lang })),
8699
- icon: () => param("icon"),
8700
- title: () => paramValue().map((title) => ({ title })),
8701
- opened: () => P5.string("opened").result(true).fallback(void 0).map((opened) => ({ opened })),
8702
- snippet: ({ keyword, id, lang, icon, title, opened }) => keyword.then(
8703
- P5.seq(
8704
- id,
8705
- lang,
8706
- P5.whitespace.then(title.or(icon).or(opened).sepBy(P5.whitespace)).fallback([])
8707
- ).map(([id2, lang2, rest]) => ({ ...id2, ...lang2, ...Object.assign({}, ...rest) }))
8708
- ),
8709
- snippetFromFile: ({ lang, icon, title, opened }) => P5.seq(
8710
- param("snippet-from-file", "fromFile"),
8711
- lang,
8712
- P5.whitespace.then(title.or(icon).or(opened).sepBy(P5.whitespace)).fallback([])
8713
- ).map(([id, lang2, rest]) => ({ ...id, ...lang2, ...Object.assign({}, ...rest) })),
8714
- anySnippet: ({ snippet: snippet2, snippetFromFile }) => snippetFromFile.or(snippet2)
8715
- });
8716
- const result = parser2.anySnippet.parse(string2);
8717
- return result.status ? result.value : void 0;
8718
- }
9913
+ import path11 from "path";
8719
9914
 
8720
9915
  // libs/builder/helpers/format-code.ts
8721
9916
  import prettierSync from "@prettier/sync";
@@ -8724,16 +9919,19 @@ function formatCode(code, codeType = "TypeScript", configDirectory) {
8724
9919
  try {
8725
9920
  if (codeType) {
8726
9921
  const parser2 = getPrettierParserFromCodeType(codeType);
8727
- const config = prettierSync.resolveConfig(
9922
+ const cache = activeFormatCodeCache();
9923
+ const resolve10 = () => prettierSync.resolveConfig(
8728
9924
  // Prettier searches from a file's parent, so use a synthetic path inside the directory.
8729
9925
  configDirectory ? join(configDirectory, "__ng_doc_format__.ts") : process.cwd(),
8730
9926
  { editorconfig: true, useCache: false }
8731
9927
  );
8732
- return prettierSync.format(code, {
9928
+ const config = cache ? cache.config(configDirectory, resolve10) : resolve10();
9929
+ const format = () => prettierSync.format(code, {
8733
9930
  ...config,
8734
9931
  parser: parser2,
8735
9932
  embeddedLanguageFormatting: "auto"
8736
9933
  }).trim();
9934
+ return cache && typeof parser2 === "string" ? cache.format({ code, parser: parser2, config }, format) : format();
8737
9935
  }
8738
9936
  return code.trim();
8739
9937
  } catch (e) {
@@ -8806,9 +10004,9 @@ function processSnippets(code, basePath, configDirectory) {
8806
10004
  const { title, lang, icon, opened, fromFile } = config;
8807
10005
  const language = lang || (isHTMLComment ? "angular-html" : "angular-ts");
8808
10006
  if (fromFile) {
8809
- const snippetCode = fs.readFileSync(path10.join(basePath, fromFile), "utf-8");
10007
+ const snippetCode = fs.readFileSync(path11.join(basePath, fromFile), "utf-8");
8810
10008
  result.push({
8811
- title: title ?? path10.basename(fromFile),
10009
+ title: title ?? path11.basename(fromFile),
8812
10010
  lang: language,
8813
10011
  icon,
8814
10012
  opened,
@@ -8857,7 +10055,7 @@ function removeSnippets(code) {
8857
10055
  // libs/builder/helpers/snippets-from-asset.ts
8858
10056
  function snippetsFromAsset(asset, inlineStylesType, configDirectory) {
8859
10057
  const snippets = processLegacySnippets(asset.code).concat(
8860
- processSnippets(asset.code, path11.dirname(asset.filePath), configDirectory)
10058
+ processSnippets(asset.code, path12.dirname(asset.filePath), configDirectory)
8861
10059
  );
8862
10060
  const codeType = codeTypeFromExt(asset.filePath);
8863
10061
  const isStylesFile = ["CSS", "SCSS", "LESS", "SASS"].includes(codeType.toUpperCase());
@@ -8915,17 +10113,17 @@ var TrackedFiles = class extends ObservationRecorder {
8915
10113
  super(initial, { normalize: normalize2, observer });
8916
10114
  }
8917
10115
  read(file) {
8918
- const path17 = normalize2(file);
8919
- this.add({ kind: "existence", path: path17, exists: existsSync8(path17) });
10116
+ const path18 = normalize2(file);
10117
+ this.add({ kind: "existence", path: path18, exists: existsSync8(path18) });
8920
10118
  try {
8921
- const { text, digest: digest3 } = readText(path17);
8922
- this.add({ kind: "content", path: path17, digest: digest3 });
10119
+ const { text, digest: digest3 } = readText(path18);
10120
+ this.add({ kind: "content", path: path18, digest: digest3 });
8923
10121
  return text;
8924
10122
  } catch (error) {
8925
10123
  throw new SemanticFailure(
8926
10124
  "SEMANTIC_INPUT_READ",
8927
10125
  error instanceof Error ? error.message : String(error),
8928
- { path: path17 }
10126
+ { path: path18 }
8929
10127
  );
8930
10128
  }
8931
10129
  }
@@ -8964,8 +10162,8 @@ function recordDerivedQuery(identity, query) {
8964
10162
  top()?.recordDerived(identity);
8965
10163
  return suspendRecording(query);
8966
10164
  }
8967
- function recordLookup(path17) {
8968
- top()?.record(path17, "lookup");
10165
+ function recordLookup(path18) {
10166
+ top()?.record(path18, "lookup");
8969
10167
  }
8970
10168
  var allocator = ts3.objectAllocator;
8971
10169
  var SymbolObject = allocator.getSymbolConstructor();
@@ -8973,9 +10171,9 @@ var TypeObject = allocator.getTypeConstructor();
8973
10171
  var SignatureObject = allocator.getSignatureConstructor();
8974
10172
  var visited = /* @__PURE__ */ new WeakMap();
8975
10173
  function fileOf(node) {
8976
- let current = node;
8977
- while (current && current.kind !== ts3.SyntaxKind.SourceFile) current = current.parent;
8978
- return current?.fileName;
10174
+ let current2 = node;
10175
+ while (current2 && current2.kind !== ts3.SyntaxKind.SourceFile) current2 = current2.parent;
10176
+ return current2?.fileName;
8979
10177
  }
8980
10178
  function moduleSourceFile(specifier, context) {
8981
10179
  const declaration = context.getSymbolAtLocation(specifier)?.declarations?.find((item) => ts3.isSourceFile(item));
@@ -8987,17 +10185,17 @@ function starClosure(source, context) {
8987
10185
  const cached = context.stars.get(source.fileName);
8988
10186
  if (cached) return cached;
8989
10187
  const files = /* @__PURE__ */ new Set();
8990
- const visit8 = (file) => {
10188
+ const visit9 = (file) => {
8991
10189
  for (const statement of file.statements) {
8992
10190
  if (!ts3.isExportDeclaration(statement) || statement.exportClause || !statement.moduleSpecifier)
8993
10191
  continue;
8994
10192
  const target = moduleSourceFile(statement.moduleSpecifier, context);
8995
10193
  if (!target || files.has(target.fileName)) continue;
8996
10194
  files.add(target.fileName);
8997
- visit8(target);
10195
+ visit9(target);
8998
10196
  }
8999
10197
  };
9000
- visit8(source);
10198
+ visit9(source);
9001
10199
  const closure = [...files];
9002
10200
  context.stars.set(source.fileName, closure);
9003
10201
  return closure;
@@ -9523,7 +10721,7 @@ function structural(type) {
9523
10721
  }
9524
10722
  function reorder(root, print, checker, restore) {
9525
10723
  const seen = /* @__PURE__ */ new Set();
9526
- const visit8 = (type, level) => {
10724
+ const visit9 = (type, level) => {
9527
10725
  if (!type || seen.has(type)) return;
9528
10726
  seen.add(type);
9529
10727
  const parts = type;
@@ -9533,16 +10731,16 @@ function reorder(root, print, checker, restore) {
9533
10731
  ...parts.resolvedTypeArguments ?? [],
9534
10732
  ...parts.aliasTypeArguments ?? []
9535
10733
  ])
9536
- visit8(inner, level);
10734
+ visit9(inner, level);
9537
10735
  if (level < DEPTH && structural(type))
9538
10736
  suspendRecording(() => {
9539
10737
  for (const property of checker.getPropertiesOfType(type))
9540
- visit8(checker.getTypeOfSymbol(property), level + 1);
10738
+ visit9(checker.getTypeOfSymbol(property), level + 1);
9541
10739
  for (const kind of [ts4.SignatureKind.Call, ts4.SignatureKind.Construct])
9542
10740
  for (const signature2 of checker.getSignaturesOfType(type, kind)) {
9543
10741
  for (const parameter2 of signature2.getParameters())
9544
- visit8(checker.getTypeOfSymbol(parameter2), level + 1);
9545
- visit8(checker.getReturnTypeOfSignature(signature2), level + 1);
10742
+ visit9(checker.getTypeOfSymbol(parameter2), level + 1);
10743
+ visit9(checker.getReturnTypeOfSignature(signature2), level + 1);
9546
10744
  }
9547
10745
  });
9548
10746
  if (type.flags & (ts4.TypeFlags.Union | ts4.TypeFlags.Intersection) && parts.types) {
@@ -9553,7 +10751,7 @@ function reorder(root, print, checker, restore) {
9553
10751
  }
9554
10752
  }
9555
10753
  };
9556
- visit8(root, 0);
10754
+ visit9(root, 0);
9557
10755
  }
9558
10756
 
9559
10757
  // libs/builder/generator/semantic/angular.ts
@@ -9568,6 +10766,18 @@ function canonicalMembers(type) {
9568
10766
  )
9569
10767
  ).map((member) => wrappers.get(member));
9570
10768
  }
10769
+ function playgroundOption(member) {
10770
+ if (member.isEnumLiteral()) {
10771
+ const value = member.getLiteralValue();
10772
+ const name = member.getSymbol()?.getName();
10773
+ if (name && (typeof value === "string" || typeof value === "number"))
10774
+ return { label: name, value };
10775
+ }
10776
+ return member.getText(
10777
+ void 0,
10778
+ TypeFormatFlags2.NoTruncation | TypeFormatFlags2.UseSingleQuotesForStringLiteralType
10779
+ );
10780
+ }
9571
10781
  function unwrap(node, seen = /* @__PURE__ */ new Set()) {
9572
10782
  if (!node || seen.has(node)) return void 0;
9573
10783
  seen.add(node);
@@ -9616,31 +10826,49 @@ function literal(node) {
9616
10826
  "Computed controls require the evaluated readGuideValues factory port"
9617
10827
  );
9618
10828
  }
9619
- function controlsToProperties(controls) {
9620
- return Object.fromEntries(
9621
- Object.entries(controls).map(([name, value]) => {
9622
- if (typeof value === "string") {
9623
- return [name, { inputName: name, type: value, isManual: true }];
9624
- }
9625
- if (!value || typeof value !== "object" || Array.isArray(value)) {
9626
- throw new SemanticFailure("SEMANTIC_CONTROLS_SHAPE", `Invalid control ${name}`);
9627
- }
9628
- const type = value.type;
9629
- if (typeof type !== "string") {
9630
- throw new SemanticFailure("SEMANTIC_CONTROLS_SHAPE", `Invalid control ${name}`);
9631
- }
9632
- return [
9633
- name,
9634
- {
9635
- inputName: typeof value.alias === "string" ? value.alias : name,
9636
- type,
9637
- ...typeof value.description === "string" ? { description: value.description } : {},
9638
- ...Array.isArray(value.options) && value.options.every((item) => typeof item === "string") ? { options: value.options } : {},
9639
- isManual: true
9640
- }
9641
- ];
9642
- })
9643
- );
10829
+ function displayFields(value) {
10830
+ return {
10831
+ ...typeof value.label === "string" ? { label: value.label } : {},
10832
+ ...typeof value.group === "string" ? { group: value.group } : {},
10833
+ ...typeof value.order === "number" && Number.isFinite(value.order) ? { order: value.order } : {}
10834
+ };
10835
+ }
10836
+ function applyControls(properties, controls) {
10837
+ for (const [name, value] of Object.entries(controls)) {
10838
+ if (typeof value === "string") {
10839
+ properties[name] = { inputName: name, type: value, isManual: true };
10840
+ continue;
10841
+ }
10842
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
10843
+ throw new SemanticFailure("SEMANTIC_CONTROLS_SHAPE", `Invalid control ${name}`);
10844
+ }
10845
+ const type = value.type;
10846
+ if (type === void 0) {
10847
+ const detected = properties[name];
10848
+ if (!detected)
10849
+ throw new SemanticFailure(
10850
+ "SEMANTIC_CONTROLS_SHAPE",
10851
+ `Control ${name} has no type, and the target has no input ${name} to keep the control of`
10852
+ );
10853
+ properties[name] = {
10854
+ ...detected,
10855
+ ...typeof value.description === "string" ? { description: value.description } : {},
10856
+ ...displayFields(value)
10857
+ };
10858
+ continue;
10859
+ }
10860
+ if (typeof type !== "string") {
10861
+ throw new SemanticFailure("SEMANTIC_CONTROLS_SHAPE", `Invalid control ${name}`);
10862
+ }
10863
+ properties[name] = {
10864
+ inputName: typeof value.alias === "string" ? value.alias : name,
10865
+ type,
10866
+ ...typeof value.description === "string" ? { description: value.description } : {},
10867
+ ...Array.isArray(value.options) && value.options.every((item) => typeof item === "string") ? { options: value.options } : {},
10868
+ isManual: true,
10869
+ ...displayFields(value)
10870
+ };
10871
+ }
9644
10872
  }
9645
10873
  function assets(target, files, config) {
9646
10874
  return getComponentSourceFiles(target).flatMap((file) => {
@@ -9663,7 +10891,9 @@ function assets(target, files, config) {
9663
10891
  isEmpty: !code,
9664
10892
  lang: title.replace("TypeScript", "angular-ts").replace("HTML", "angular-html")
9665
10893
  };
9666
- const snippets = snippetsFromAsset(asset, config.inlineStyleLanguage, config.workspaceRoot);
10894
+ const snippets = formatting(
10895
+ () => snippetsFromAsset(asset, config.inlineStyleLanguage, config.workspaceRoot)
10896
+ );
9667
10897
  return (snippets.length ? snippets : [asset]).map((item) => ({
9668
10898
  title: item.title,
9669
10899
  source,
@@ -9750,12 +10980,7 @@ function guideSemantics(source, files, config, docs, values) {
9750
10980
  ),
9751
10981
  description: Node16.isParameterDeclaration(input) ? docs.getJsDocParam(target.getMethodOrThrow("transform"), input.getName()) : docs.getJsDocDescription(input),
9752
10982
  // In the written order: the checker's (stable) order sorts literals by value.
9753
- options: writtenUnionOrder(input, type, canonicalMembers(type)).map(
9754
- (part) => part.getText(
9755
- void 0,
9756
- TypeFormatFlags2.NoTruncation | TypeFormatFlags2.UseSingleQuotesForStringLiteralType
9757
- )
9758
- )
10983
+ options: writtenUnionOrder(input, type, canonicalMembers(type)).map(playgroundOption)
9759
10984
  };
9760
10985
  }
9761
10986
  const controls = evaluated?.playgrounds?.[id]?.controls ?? (expression.getProperty("controls") ? literal(expression.getProperty("controls")) : {});
@@ -9764,7 +10989,7 @@ function guideSemantics(source, files, config, docs, values) {
9764
10989
  "SEMANTIC_CONTROLS_SHAPE",
9765
10990
  `Controls for ${id} must be a JSON object`
9766
10991
  );
9767
- Object.assign(properties, controlsToProperties(controls));
10992
+ applyControls(properties, controls);
9768
10993
  for (const [key, value] of Object.entries(properties)) {
9769
10994
  if (!value || typeof value !== "object" || typeof value.type !== "string")
9770
10995
  throw new SemanticFailure("SEMANTIC_CONTROLS_SHAPE", `Invalid control ${id}.${key}`);
@@ -10056,9 +11281,9 @@ function enumerateApi(state, entryId, files, diagnostics, context) {
10056
11281
  const result = [];
10057
11282
  const seen = /* @__PURE__ */ new Set();
10058
11283
  for (const scope of entry.scopes)
10059
- for (const path17 of state.scopes.get(`${entry.id}:${scope.id}`) ?? []) {
10060
- recordLookup(path17);
10061
- const source = state.project.getSourceFileOrThrow(path17);
11284
+ for (const path18 of state.scopes.get(`${entry.id}:${scope.id}`) ?? []) {
11285
+ recordLookup(path18);
11286
+ const source = state.project.getSourceFileOrThrow(path18);
10062
11287
  for (const [name, nodes] of source.getExportedDeclarations()) {
10063
11288
  const candidates = nodes.filter(supported);
10064
11289
  if (!candidates.length) {
@@ -10067,7 +11292,7 @@ function enumerateApi(state, entryId, files, diagnostics, context) {
10067
11292
  severity: "info",
10068
11293
  stage: "semantic",
10069
11294
  message: `Skipped unsupported exported declaration ${name}`,
10070
- source: { path: path17 }
11295
+ source: { path: path18 }
10071
11296
  });
10072
11297
  continue;
10073
11298
  }
@@ -10173,16 +11398,16 @@ var DerivedClassIndex = class {
10173
11398
  referenceNames(expression.expression);
10174
11399
  }
10175
11400
  };
10176
- const visit8 = (node) => {
11401
+ const visit9 = (node) => {
10177
11402
  if (ts6.isClassDeclaration(node)) {
10178
11403
  for (const clause of node.heritageClauses ?? [])
10179
11404
  if (clause.token === ts6.SyntaxKind.ExtendsKeyword)
10180
11405
  clause.types.forEach((type) => referenceNames(type.expression));
10181
11406
  }
10182
- ts6.forEachChild(node, visit8);
11407
+ ts6.forEachChild(node, visit9);
10183
11408
  };
10184
11409
  for (const source of program.getSourceFiles())
10185
- if (source.text.includes("extends")) visit8(source);
11410
+ if (source.text.includes("extends")) visit9(source);
10186
11411
  }
10187
11412
  /** False only when no reference search can find a class deriving from `declaration`. */
10188
11413
  mayHaveDerivedClasses(declaration) {
@@ -10275,7 +11500,7 @@ import { Project as Project3, ts as ts9 } from "ts-morph";
10275
11500
 
10276
11501
  // libs/builder/generator/semantic/owned-roots.ts
10277
11502
  import { minimatch as minimatch2 } from "minimatch";
10278
- import { randomUUID as randomUUID4 } from "node:crypto";
11503
+ import { randomUUID as randomUUID5 } from "node:crypto";
10279
11504
  import { realpathSync as realpathSync3 } from "node:fs";
10280
11505
  import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join2, parse as parse3, posix as posix3 } from "node:path";
10281
11506
  import { Project as Project2 } from "ts-morph";
@@ -10291,23 +11516,23 @@ var OwnedRoots = class {
10291
11516
  }
10292
11517
  /** The owned root containing a path, if any. */
10293
11518
  describe(file) {
10294
- const path17 = normalize2(file);
10295
- return this.roots.find((root) => path17 === root || path17.startsWith(`${root}/`));
11519
+ const path18 = normalize2(file);
11520
+ return this.roots.find((root) => path18 === root || path18.startsWith(`${root}/`));
10296
11521
  }
10297
11522
  /**
10298
11523
  * A non-owned path that is a symlink into an owned root stays unresolved: it is read through
10299
11524
  * its own path like any other input instead of resolving to a hidden owned file.
10300
11525
  */
10301
- realpath(path17, resolve10) {
10302
- const real2 = resolve10(path17);
10303
- return this.has(real2) && !this.has(path17) ? path17 : real2;
11526
+ realpath(path18, resolve10) {
11527
+ const real2 = resolve10(path18);
11528
+ return this.has(real2) && !this.has(path18) ? path18 : real2;
10304
11529
  }
10305
11530
  /** Notes a raw text read of the program's file system (see {@link contentDigest}). */
10306
11531
  recordRead(file, text) {
10307
- const path17 = normalize2(file);
11532
+ const path18 = normalize2(file);
10308
11533
  if (text.charCodeAt(0) === 65279)
10309
- this.marked.set(path17, { text: text.slice(1), digest: contentDigest(text) });
10310
- else this.marked.delete(path17);
11534
+ this.marked.set(path18, { text: text.slice(1), digest: contentDigest(text) });
11535
+ else this.marked.delete(path18);
10311
11536
  }
10312
11537
  /**
10313
11538
  * The content digest of a program file whose text is `text`. TypeScript drops a leading byte
@@ -10326,8 +11551,8 @@ var OwnedRoots = class {
10326
11551
  fileExists: (file) => !this.has(file) && host.fileExists(file),
10327
11552
  readFile: (file) => this.has(file) ? void 0 : host.readFile(file),
10328
11553
  directoryExists: (directory) => !this.has(directory) && (host.directoryExists?.(directory) ?? true),
10329
- getDirectories: (path17) => this.has(path17) ? [] : (host.getDirectories?.(path17) ?? []).filter((name) => !this.has(join2(path17, name))),
10330
- ...host.realpath ? { realpath: (path17) => this.realpath(path17, (value) => host.realpath(value)) } : {}
11554
+ getDirectories: (path18) => this.has(path18) ? [] : (host.getDirectories?.(path18) ?? []).filter((name) => !this.has(join2(path18, name))),
11555
+ ...host.realpath ? { realpath: (path18) => this.realpath(path18, (value) => host.realpath(value)) } : {}
10331
11556
  };
10332
11557
  }
10333
11558
  /** The real ts-morph file system with owned paths absent. Writes are never issued by semantic. */
@@ -10336,13 +11561,13 @@ var OwnedRoots = class {
10336
11561
  return new OwnedRootsFileSystem(realFileSystem(), this);
10337
11562
  }
10338
11563
  };
10339
- function canonical2(path17) {
11564
+ function canonical2(path18) {
10340
11565
  const missing = [];
10341
- for (let cursor = normalize2(path17); ; cursor = dirname3(cursor)) {
11566
+ for (let cursor = normalize2(path18); ; cursor = dirname3(cursor)) {
10342
11567
  try {
10343
11568
  return normalize2(join2(realpathSync3(cursor), ...missing));
10344
11569
  } catch {
10345
- if (dirname3(cursor) === cursor) return normalize2(path17);
11570
+ if (dirname3(cursor) === cursor) return normalize2(path18);
10346
11571
  missing.unshift(basename(cursor));
10347
11572
  }
10348
11573
  }
@@ -10352,7 +11577,7 @@ var notFound;
10352
11577
  function missingFileError(fileSystem) {
10353
11578
  const probe2 = join2(
10354
11579
  parse3(process.cwd()).root,
10355
- `ngdoc-owned-root-probe-${randomUUID4()}`,
11580
+ `ngdoc-owned-root-probe-${randomUUID5()}`,
10356
11581
  "missing.ts"
10357
11582
  );
10358
11583
  let observed;
@@ -10370,8 +11595,8 @@ function missingFileError(fileSystem) {
10370
11595
  }
10371
11596
  return type;
10372
11597
  }
10373
- function fileNotFound(path17) {
10374
- return new notFound(path17);
11598
+ function fileNotFound(path18) {
11599
+ return new notFound(path18);
10375
11600
  }
10376
11601
  function realFileSystem() {
10377
11602
  return real ??= new Project2({ useInMemoryFileSystem: false }).getFileSystem();
@@ -10384,11 +11609,11 @@ var OwnedRootsFileSystem = class {
10384
11609
  isCaseSensitive() {
10385
11610
  return this.real.isCaseSensitive();
10386
11611
  }
10387
- delete(path17) {
10388
- return this.real.delete(path17);
11612
+ delete(path18) {
11613
+ return this.real.delete(path18);
10389
11614
  }
10390
- deleteSync(path17) {
10391
- this.real.deleteSync(path17);
11615
+ deleteSync(path18) {
11616
+ this.real.deleteSync(path18);
10392
11617
  }
10393
11618
  readDirSync(dirPath) {
10394
11619
  if (this.owned.has(dirPath)) return [];
@@ -10444,8 +11669,8 @@ var OwnedRootsFileSystem = class {
10444
11669
  directoryExistsSync(dirPath) {
10445
11670
  return !this.owned.has(dirPath) && this.real.directoryExistsSync(dirPath);
10446
11671
  }
10447
- realpathSync(path17) {
10448
- return this.owned.realpath(path17, (value) => this.real.realpathSync(value));
11672
+ realpathSync(path18) {
11673
+ return this.owned.realpath(path18, (value) => this.real.realpathSync(value));
10449
11674
  }
10450
11675
  getCurrentDirectory() {
10451
11676
  return this.real.getCurrentDirectory();
@@ -10483,8 +11708,8 @@ function ownedRootReach(workspaceRoot, include, exclude, roots) {
10483
11708
  const base = normalize2(workspaceRoot);
10484
11709
  const relative3 = (pattern) => posix3.normalize(isAbsolute2(pattern) ? posix3.relative(base, normalize2(pattern)) : pattern);
10485
11710
  const excludes = exclude.map(relative3);
10486
- const excluded = (path17) => {
10487
- const parts = path17.split("/");
11711
+ const excluded = (path18) => {
11712
+ const parts = path18.split("/");
10488
11713
  return parts.some((_, index) => {
10489
11714
  const prefix = parts.slice(0, index + 1).join("/");
10490
11715
  return excludes.some((pattern) => minimatch2(prefix, pattern, { dot: true }));
@@ -10537,8 +11762,8 @@ function staticBase(spec) {
10537
11762
  if (index < 0) return { base: spec, rest: [] };
10538
11763
  return { base: parts.slice(0, index).join("/") || "/", rest: parts.slice(index) };
10539
11764
  }
10540
- function inside(root, path17) {
10541
- return path17 === root || path17.startsWith(root === "/" ? "/" : `${root}/`);
11765
+ function inside(root, path18) {
11766
+ return path18 === root || path18.startsWith(root === "/" ? "/" : `${root}/`);
10542
11767
  }
10543
11768
  function literal2(character) {
10544
11769
  if (character === "!" || character === "\\") return "?";
@@ -10582,7 +11807,7 @@ function segmentPattern(segment, caseInsensitive) {
10582
11807
  async function tsconfigMembershipDependencies(parsed, configFile, owned, specs = tsconfigSpecs(parsed, configFile)) {
10583
11808
  const directory = normalize2(dirname4(configFile));
10584
11809
  const caseInsensitive = !ts8.sys.useCaseSensitiveFileNames;
10585
- const key = (path17) => caseInsensitive ? path17.toLowerCase() : path17;
11810
+ const key = (path18) => caseInsensitive ? path18.toLowerCase() : path18;
10586
11811
  const extensions = [
10587
11812
  "ts",
10588
11813
  "tsx",
@@ -10594,10 +11819,10 @@ async function tsconfigMembershipDependencies(parsed, configFile, owned, specs =
10594
11819
  const dependencies = [];
10595
11820
  const reproduced = /* @__PURE__ */ new Set();
10596
11821
  for (const file of specs.files) {
10597
- const path17 = normalize2(resolve4(directory, file));
10598
- if (owned.has(path17)) continue;
10599
- dependencies.push({ kind: "existence", path: path17, exists: existsSync9(path17) });
10600
- reproduced.add(key(path17));
11822
+ const path18 = normalize2(resolve4(directory, file));
11823
+ if (owned.has(path18)) continue;
11824
+ dependencies.push({ kind: "existence", path: path18, exists: existsSync9(path18) });
11825
+ reproduced.add(key(path18));
10601
11826
  }
10602
11827
  const globs = /* @__PURE__ */ new Map();
10603
11828
  for (const spec of specs.include) {
@@ -10685,11 +11910,11 @@ async function tsconfigMembershipDependencies(parsed, configFile, owned, specs =
10685
11910
  }
10686
11911
 
10687
11912
  // libs/builder/generator/semantic/program-builder.ts
10688
- function ownedOverlap(kind, path17, owned) {
11913
+ function ownedOverlap(kind, path18, owned) {
10689
11914
  return new SemanticFailure(
10690
11915
  "SEMANTIC_OWNED_ROOT_OVERLAP",
10691
- `${kind} ${path17} lies under a generator-owned output or cache root (${owned.describe(path17)}). Generated output is never documented; configure outputRoot/cacheRoot outside documented sources.`,
10692
- { path: path17 }
11916
+ `${kind} ${path18} lies under a generator-owned output or cache root (${owned.describe(path18)}). Generated output is never documented; configure outputRoot/cacheRoot outside documented sources.`,
11917
+ { path: path18 }
10693
11918
  );
10694
11919
  }
10695
11920
  var FORMATTER_CONFIGURATION_FILES = [
@@ -10717,9 +11942,9 @@ var FORMATTER_CONFIGURATION_FILES = [
10717
11942
  function trackFormatterConfiguration(workspaceRoot, files) {
10718
11943
  for (let directory = workspaceRoot; ; directory = dirname5(directory)) {
10719
11944
  for (const name of FORMATTER_CONFIGURATION_FILES) {
10720
- const path17 = normalize2(resolve5(directory, name));
10721
- files.add({ kind: "existence", path: path17, exists: existsSync10(path17) });
10722
- if (existsSync10(path17)) files.read(path17);
11945
+ const path18 = normalize2(resolve5(directory, name));
11946
+ files.add({ kind: "existence", path: path18, exists: existsSync10(path18) });
11947
+ if (existsSync10(path18)) files.read(path18);
10723
11948
  }
10724
11949
  if (dirname5(directory) === directory) break;
10725
11950
  }
@@ -10831,9 +12056,9 @@ function syntaxFailure(program) {
10831
12056
  import { existsSync as existsSync11 } from "node:fs";
10832
12057
  import { dirname as dirname6, resolve as resolve6 } from "node:path";
10833
12058
  import { ts as ts10 } from "ts-morph";
10834
- function realpathOf(path17) {
12059
+ function realpathOf(path18) {
10835
12060
  try {
10836
- return ts10.sys.realpath ? ts10.sys.realpath(path17) : path17;
12061
+ return ts10.sys.realpath ? ts10.sys.realpath(path18) : path18;
10837
12062
  } catch {
10838
12063
  return void 0;
10839
12064
  }
@@ -10842,9 +12067,9 @@ function trackingHost(files, owned, probes) {
10842
12067
  return owned.resolutionHost({
10843
12068
  ...ts10.sys,
10844
12069
  fileExists: (file) => {
10845
- const path17 = normalize2(file);
10846
- const exists = existsSync11(path17);
10847
- files.add({ kind: "existence", path: path17, exists });
12070
+ const path18 = normalize2(file);
12071
+ const exists = existsSync11(path18);
12072
+ files.add({ kind: "existence", path: path18, exists });
10848
12073
  return exists;
10849
12074
  },
10850
12075
  readFile: (file) => {
@@ -10859,9 +12084,9 @@ function trackingHost(files, owned, probes) {
10859
12084
  if (!exists) probes?.missing(normalize2(directory));
10860
12085
  return exists;
10861
12086
  },
10862
- realpath: (path17) => {
10863
- const real2 = realpathOf(path17) ?? path17;
10864
- const from = normalize2(path17);
12087
+ realpath: (path18) => {
12088
+ const real2 = realpathOf(path18) ?? path18;
12089
+ const from = normalize2(path18);
10865
12090
  const to = normalize2(real2);
10866
12091
  if (probes && from !== to) probes.realpath(from, to);
10867
12092
  return real2;
@@ -10869,29 +12094,29 @@ function trackingHost(files, owned, probes) {
10869
12094
  });
10870
12095
  }
10871
12096
  function trackSourceFile(source, options, host, files, owned, imports) {
10872
- const path17 = normalize2(source.fileName);
10873
- files.add({ kind: "existence", path: path17, exists: true });
10874
- files.add({ kind: "content", path: path17, digest: owned.contentDigest(path17, source.text) });
12097
+ const path18 = normalize2(source.fileName);
12098
+ files.add({ kind: "existence", path: path18, exists: true });
12099
+ files.add({ kind: "content", path: path18, digest: owned.contentDigest(path18, source.text) });
10875
12100
  const references2 = ts10.preProcessFile(source.text);
10876
12101
  for (const item of references2.importedFiles) {
10877
- const resolved = ts10.resolveModuleName(item.fileName, path17, options, host).resolvedModule;
12102
+ const resolved = ts10.resolveModuleName(item.fileName, path18, options, host).resolvedModule;
10878
12103
  imports?.push(resolution("module", item.fileName, resolved?.resolvedFileName));
10879
12104
  }
10880
12105
  for (const item of references2.typeReferenceDirectives) {
10881
12106
  const resolved = ts10.resolveTypeReferenceDirective(
10882
12107
  item.fileName,
10883
- path17,
12108
+ path18,
10884
12109
  options,
10885
12110
  host
10886
12111
  ).resolvedTypeReferenceDirective;
10887
12112
  imports?.push(resolution("type-reference", item.fileName, resolved?.resolvedFileName));
10888
12113
  }
10889
12114
  for (const item of references2.referencedFiles) {
10890
- const path18 = normalize2(resolve6(dirname6(source.fileName), item.fileName));
10891
- if (owned.has(path18)) continue;
10892
- const exists = existsSync11(path18);
10893
- files.add({ kind: "existence", path: path18, exists });
10894
- imports?.push(resolution("reference", item.fileName, exists ? path18 : void 0));
12115
+ const path19 = normalize2(resolve6(dirname6(source.fileName), item.fileName));
12116
+ if (owned.has(path19)) continue;
12117
+ const exists = existsSync11(path19);
12118
+ files.add({ kind: "existence", path: path19, exists });
12119
+ imports?.push(resolution("reference", item.fileName, exists ? path19 : void 0));
10895
12120
  }
10896
12121
  }
10897
12122
  function resolution(kind, specifier, resolved) {
@@ -10914,14 +12139,14 @@ function semanticDefinition(configuration, files, project) {
10914
12139
  reason: "Whole TypeScript program, configuration, resolution libraries and source membership conservatively invalidate semantic consumers"
10915
12140
  };
10916
12141
  }
10917
- function rawDigest(path17) {
12142
+ function rawDigest(path18) {
10918
12143
  try {
10919
- return readText(path17).digest;
12144
+ return readText(path18).digest;
10920
12145
  } catch (error) {
10921
12146
  throw new SemanticFailure(
10922
12147
  "SEMANTIC_RETRACK",
10923
- `${path17} cannot be read again: ${error instanceof Error ? error.message : String(error)}`,
10924
- { path: path17 }
12148
+ `${path18} cannot be read again: ${error instanceof Error ? error.message : String(error)}`,
12149
+ { path: path18 }
10925
12150
  );
10926
12151
  }
10927
12152
  }
@@ -10961,9 +12186,9 @@ var ProgramObservations = class {
10961
12186
  const options = project.getCompilerOptions();
10962
12187
  const host = trackingHost(this.aggregate, owned, this.probeSink);
10963
12188
  for (const source of project.getProgram().compilerObject.getSourceFiles()) {
10964
- const path17 = normalize2(source.fileName);
10965
- if (owned.has(path17)) continue;
10966
- const segment = this.open(path17);
12189
+ const path18 = normalize2(source.fileName);
12190
+ if (owned.has(path18)) continue;
12191
+ const segment = this.open(path18);
10967
12192
  try {
10968
12193
  trackSourceFile(source, options, host, this.aggregate, owned, segment.imports);
10969
12194
  } finally {
@@ -10990,8 +12215,8 @@ var ProgramObservations = class {
10990
12215
  return [...this.importerSegments.keys()];
10991
12216
  }
10992
12217
  /** What tracking one program file observed, or undefined when it is not a tracked importer. */
10993
- importer(path17) {
10994
- const segment = this.importerSegments.get(normalize2(path17));
12218
+ importer(path18) {
12219
+ const segment = this.importerSegments.get(normalize2(path18));
10995
12220
  return segment ? {
10996
12221
  path: segment.owner,
10997
12222
  dependencies: segment.dependencies,
@@ -11001,7 +12226,7 @@ var ProgramObservations = class {
11001
12226
  } : void 0;
11002
12227
  }
11003
12228
  /** The tracked program files with a reference that resolved to this file, in program order. */
11004
- importersOf(path17) {
12229
+ importersOf(path18) {
11005
12230
  if (!this.reverse) {
11006
12231
  this.reverse = /* @__PURE__ */ new Map();
11007
12232
  for (const [importer, segment] of this.importerSegments)
@@ -11012,7 +12237,7 @@ var ProgramObservations = class {
11012
12237
  this.reverse.set(item, list);
11013
12238
  }
11014
12239
  }
11015
- return [...this.reverse.get(normalize2(path17)) ?? []];
12240
+ return [...this.reverse.get(normalize2(path18)) ?? []];
11016
12241
  }
11017
12242
  /**
11018
12243
  * The aggregate replayed from the attribution log: every observation (without the definition),
@@ -11062,14 +12287,14 @@ var ProgramObservations = class {
11062
12287
  const options = project.getCompilerOptions();
11063
12288
  const retracked = [];
11064
12289
  const targets = new Set([...paths].map(normalize2));
11065
- for (const path17 of targets) {
11066
- const segment = this.importerSegments.get(path17);
11067
- const source = segment ? program.getSourceFile(path17) : void 0;
12290
+ for (const path18 of targets) {
12291
+ const segment = this.importerSegments.get(path18);
12292
+ const source = segment ? program.getSourceFile(path18) : void 0;
11068
12293
  if (!segment || !source)
11069
12294
  throw new SemanticFailure(
11070
12295
  "SEMANTIC_RETRACK",
11071
- `${path17} is not a tracked file of the current program`,
11072
- { path: path17 }
12296
+ `${path18} is not a tracked file of the current program`,
12297
+ { path: path18 }
11073
12298
  );
11074
12299
  segment.dependencies = [];
11075
12300
  segment.missing = [];
@@ -11139,16 +12364,16 @@ var ProgramObservations = class {
11139
12364
  this.importerSegments.clear();
11140
12365
  const tracked = [];
11141
12366
  for (const source of program.getSourceFiles()) {
11142
- const path17 = normalize2(source.fileName);
11143
- if (owned.has(path17)) continue;
11144
- const kept2 = previous.get(path17);
11145
- if (kept2 && keep(path17)) {
12367
+ const path18 = normalize2(source.fileName);
12368
+ if (owned.has(path18)) continue;
12369
+ const kept2 = previous.get(path18);
12370
+ if (kept2 && keep(path18)) {
11146
12371
  segments.push(kept2);
11147
- this.importerSegments.set(path17, kept2);
12372
+ this.importerSegments.set(path18, kept2);
11148
12373
  continue;
11149
12374
  }
11150
12375
  const segment = {
11151
- owner: path17,
12376
+ owner: path18,
11152
12377
  dependencies: [],
11153
12378
  missing: [],
11154
12379
  realpaths: [],
@@ -11162,7 +12387,7 @@ var ProgramObservations = class {
11162
12387
  trackSourceFile(source, options, host, files2, owned, segment.imports);
11163
12388
  tracked.push(...segment.dependencies);
11164
12389
  segments.push(segment);
11165
- this.importerSegments.set(path17, segment);
12390
+ this.importerSegments.set(path18, segment);
11166
12391
  }
11167
12392
  segments.push(...tail);
11168
12393
  this.segments.splice(0, this.segments.length, ...segments);
@@ -11183,16 +12408,16 @@ var ProgramObservations = class {
11183
12408
  this.segment().dependencies.push(dependency);
11184
12409
  });
11185
12410
  }
11186
- open(path17) {
12411
+ open(path18) {
11187
12412
  const segment = {
11188
- owner: path17,
12413
+ owner: path18,
11189
12414
  dependencies: [],
11190
12415
  missing: [],
11191
12416
  realpaths: [],
11192
12417
  imports: []
11193
12418
  };
11194
12419
  this.segments.push(segment);
11195
- this.importerSegments.set(path17, segment);
12420
+ this.importerSegments.set(path18, segment);
11196
12421
  this.current = segment;
11197
12422
  return segment;
11198
12423
  }
@@ -11283,7 +12508,7 @@ function watchProgram(project, observations, owned, tsConfig, observedAtNs) {
11283
12508
  const published = observations.files.all();
11284
12509
  for (const dependency of published)
11285
12510
  if (dependency.kind === "glob")
11286
- listings.recordGlob(dependency, observedAtNs, (path17) => owned.has(path17));
12511
+ listings.recordGlob(dependency, observedAtNs, (path18) => owned.has(path18));
11287
12512
  const stamps = new ObservationStamps((file) => readText(file).digest);
11288
12513
  stamps.record([...published, ...hidden.all()], observedAtNs);
11289
12514
  const probes = observations.probes;
@@ -11301,16 +12526,16 @@ function watchFacts(watch) {
11301
12526
  return {
11302
12527
  hidden: watch.hidden.all(),
11303
12528
  missingDirectories: [...watch.missingDirectories],
11304
- realpaths: watch.realpaths.map(([path17, real2]) => [path17, real2]),
12529
+ realpaths: watch.realpaths.map(([path18, real2]) => [path18, real2]),
11305
12530
  directories
11306
12531
  };
11307
12532
  }
11308
12533
  function watchFactsChange(facts) {
11309
12534
  for (const directory of facts.missingDirectories)
11310
12535
  if (ts12.sys.directoryExists(directory)) return `directory appeared: ${directory}`;
11311
- for (const [path17, real2] of facts.realpaths) {
11312
- const current = realpathOf(path17);
11313
- if (current === void 0 || normalize2(current) !== real2) return `symlink retargeted: ${path17}`;
12536
+ for (const [path18, real2] of facts.realpaths) {
12537
+ const current2 = realpathOf(path18);
12538
+ if (current2 === void 0 || normalize2(current2) !== real2) return `symlink retargeted: ${path18}`;
11314
12539
  }
11315
12540
  for (const directory of facts.directories)
11316
12541
  if (!directoryListed(directory)) return `directory listing changed: ${directory.path}`;
@@ -11361,9 +12586,9 @@ function assessPatch(previous, changes) {
11361
12586
  rehashed += sweep2.rehashed;
11362
12587
  if (sweep2.changed) edited.add(dependency.path);
11363
12588
  }
11364
- for (const path17 of edited)
11365
- if (!importers.has(path17))
11366
- return { kind: "full", reason: `patched path is not a program file: ${path17}` };
12589
+ for (const path18 of edited)
12590
+ if (!importers.has(path18))
12591
+ return { kind: "full", reason: `patched path is not a program file: ${path18}` };
11367
12592
  if (edited.size > MAX_PATCHED_FILES)
11368
12593
  return {
11369
12594
  kind: "full",
@@ -11373,8 +12598,8 @@ function assessPatch(previous, changes) {
11373
12598
  (dependency) => !(dependency.kind === "content" && importers.has(dependency.path))
11374
12599
  );
11375
12600
  const unexplained = changes.filter((change) => {
11376
- const path17 = normalize2(change.path);
11377
- return !importers.has(path17) || !existsSync12(path17);
12601
+ const path18 = normalize2(change.path);
12602
+ return !importers.has(path18) || !existsSync12(path18);
11378
12603
  });
11379
12604
  const verified = holds2(watch, rest, unexplained);
11380
12605
  if (typeof verified === "string") return { kind: "full", reason: verified };
@@ -11383,7 +12608,7 @@ function assessPatch(previous, changes) {
11383
12608
  stamped: verified.stamped + stamped,
11384
12609
  rehashed: verified.rehashed + rehashed
11385
12610
  };
11386
- return edited.size ? { kind: "patch", files: order.filter((path17) => edited.has(path17)), counters: counters2 } : { kind: "reuse", counters: counters2 };
12611
+ return edited.size ? { kind: "patch", files: order.filter((path18) => edited.has(path18)), counters: counters2 } : { kind: "reuse", counters: counters2 };
11387
12612
  }
11388
12613
  function assessRestructure(previous, changes, discovery) {
11389
12614
  const { watch, mirror } = previous;
@@ -11398,42 +12623,42 @@ function assessRestructure(previous, changes, discovery) {
11398
12623
  const deleted = /* @__PURE__ */ new Set();
11399
12624
  let stamped = 0;
11400
12625
  let rehashed = 0;
11401
- for (const path17 of layout.importers) {
11402
- const own = observations.importer(path17).dependencies.filter(
11403
- (dependency) => dependency.kind === "content" && dependency.path === path17
12626
+ for (const path18 of layout.importers) {
12627
+ const own = observations.importer(path18).dependencies.filter(
12628
+ (dependency) => dependency.kind === "content" && dependency.path === path18
11404
12629
  );
11405
12630
  const sweep2 = watch.stamps.sweep(own);
11406
12631
  stamped += sweep2.stamped;
11407
12632
  rehashed += sweep2.rehashed;
11408
12633
  if (!sweep2.changed) continue;
11409
- if (existsSync12(path17)) edited.add(path17);
11410
- else deleted.add(path17);
12634
+ if (existsSync12(path18)) edited.add(path18);
12635
+ else deleted.add(path18);
11411
12636
  }
11412
- for (const path17 of edited)
11413
- if (!importers.has(path17))
11414
- return { kind: "full", reason: `patched path is not a program file: ${path17}` };
12637
+ for (const path18 of edited)
12638
+ if (!importers.has(path18))
12639
+ return { kind: "full", reason: `patched path is not a program file: ${path18}` };
11415
12640
  if (edited.size + deleted.size > MAX_PATCHED_FILES)
11416
12641
  return {
11417
12642
  kind: "full",
11418
12643
  reason: `${edited.size + deleted.size} program files changed (at most ${MAX_PATCHED_FILES} are patched)`
11419
12644
  };
11420
12645
  const retracked = /* @__PURE__ */ new Set([...edited, ...deleted]);
11421
- const kept2 = layout.importers.filter((path17) => !retracked.has(path17));
12646
+ const kept2 = layout.importers.filter((path18) => !retracked.has(path18));
11422
12647
  const rest = [
11423
12648
  // The program configuration (entry modules are read again with the membership).
11424
12649
  ...layout.membership.filter(
11425
12650
  (dependency) => (dependency.kind === "content" || dependency.kind === "existence") && !entries.has(normalize2(dependency.path))
11426
12651
  ),
11427
- ...kept2.flatMap((path17) => observations.importer(path17).dependencies),
12652
+ ...kept2.flatMap((path18) => observations.importer(path18).dependencies),
11428
12653
  ...layout.tail,
11429
12654
  ...watch.hidden.all()
11430
12655
  ];
11431
12656
  const missing = [
11432
- ...kept2.flatMap((path17) => observations.importer(path17).missing),
12657
+ ...kept2.flatMap((path18) => observations.importer(path18).missing),
11433
12658
  ...layout.tailMissing
11434
12659
  ];
11435
12660
  const realpaths = [
11436
- ...kept2.flatMap((path17) => observations.importer(path17).realpaths),
12661
+ ...kept2.flatMap((path18) => observations.importer(path18).realpaths),
11437
12662
  ...layout.tailRealpaths
11438
12663
  ];
11439
12664
  const unexplained = changes.filter((change) => !edited.has(normalize2(change.path)));
@@ -11445,7 +12670,7 @@ function assessRestructure(previous, changes, discovery) {
11445
12670
  if (typeof verified === "string") return { kind: "full", reason: verified };
11446
12671
  return {
11447
12672
  kind: "patch",
11448
- files: layout.importers.filter((path17) => edited.has(path17)),
12673
+ files: layout.importers.filter((path18) => edited.has(path18)),
11449
12674
  counters: {
11450
12675
  ...verified,
11451
12676
  stamped: verified.stamped + stamped,
@@ -11464,7 +12689,7 @@ function keyConfiguration(key) {
11464
12689
  function entryPaths(key) {
11465
12690
  try {
11466
12691
  const [, entries] = JSON.parse(key);
11467
- return entries.map(([, , path17]) => normalize2(path17));
12692
+ return entries.map(([, , path18]) => normalize2(path18));
11468
12693
  } catch {
11469
12694
  return [];
11470
12695
  }
@@ -11484,9 +12709,9 @@ function holds2(watch, observations, changes, probes = {
11484
12709
  if (sweep2.changed) return `${sweep2.changed.kind} observation changed: ${sweep2.changed.path}`;
11485
12710
  for (const directory of probes.missing)
11486
12711
  if (ts12.sys.directoryExists(directory)) return `directory appeared: ${directory}`;
11487
- for (const [path17, real2] of probes.realpaths) {
11488
- const current = realpathOf(path17);
11489
- if (current === void 0 || normalize2(current) !== real2) return `symlink retargeted: ${path17}`;
12712
+ for (const [path18, real2] of probes.realpaths) {
12713
+ const current2 = realpathOf(path18);
12714
+ if (current2 === void 0 || normalize2(current2) !== real2) return `symlink retargeted: ${path18}`;
11490
12715
  }
11491
12716
  const directories = probes.listings.sweep();
11492
12717
  if (directories.changed) return `directory listing changed: ${directories.changed}`;
@@ -11514,29 +12739,29 @@ async function patchProgram(previous, assessment, discovery) {
11514
12739
  const order = before.getSourceFiles().map((source) => source.fileName);
11515
12740
  const observedAtNs = observationClockNs();
11516
12741
  const plans = [];
11517
- for (const path17 of files) {
11518
- const source = project.getSourceFile(path17);
11519
- const importer = mirror.observations.importer(path17);
12742
+ for (const path18 of files) {
12743
+ const source = project.getSourceFile(path18);
12744
+ const importer = mirror.observations.importer(path18);
11520
12745
  if (!source || !importer)
11521
- return { kind: "refused", reason: `${path17} is not a file of the retained program` };
12746
+ return { kind: "refused", reason: `${path18} is not a file of the retained program` };
11522
12747
  const compiled = source.compilerNode;
11523
12748
  if (compiled.isDeclarationFile || before.isSourceFileDefaultLibrary(compiled) || before.isSourceFileFromExternalLibrary(compiled))
11524
- return { kind: "refused", reason: `${path17} is a declaration or library file` };
12749
+ return { kind: "refused", reason: `${path18} is a declaration or library file` };
11525
12750
  let raw;
11526
12751
  try {
11527
- raw = readText(path17);
12752
+ raw = readText(path18);
11528
12753
  } catch (error) {
11529
12754
  return {
11530
12755
  kind: "refused",
11531
- reason: `${path17} cannot be read: ${error instanceof Error ? error.message : String(error)}`
12756
+ reason: `${path18} cannot be read: ${error instanceof Error ? error.message : String(error)}`
11532
12757
  };
11533
12758
  }
11534
12759
  const text = raw.text.charCodeAt(0) === 65279 ? raw.text.slice(1) : raw.text;
11535
- const reason = patchable(path17, compiled.text, "before") ?? patchable(path17, text, "after");
11536
- if (reason) return { kind: "refused", reason: `${path17}: ${reason}` };
11537
- if (references(path17, compiled.text) !== references(path17, text))
11538
- return discovery ? restructureProgram(previous, files, discovery) : { kind: "refused", reason: `${path17}: its imports, directives or pragmas changed` };
11539
- plans.push({ path: path17, source, raw, text, before: comparable(importer) });
12760
+ const reason = patchable(path18, compiled.text, "before") ?? patchable(path18, text, "after");
12761
+ if (reason) return { kind: "refused", reason: `${path18}: ${reason}` };
12762
+ if (references(path18, compiled.text) !== references(path18, text))
12763
+ return discovery ? restructureProgram(previous, files, discovery) : { kind: "refused", reason: `${path18}: its imports, directives or pragmas changed` };
12764
+ plans.push({ path: path18, source, raw, text, before: comparable(importer) });
11540
12765
  }
11541
12766
  for (const plan of plans) mirror.appliedSinceBase.set(plan.path, plan.raw.digest);
11542
12767
  mirror.version += 1;
@@ -11602,7 +12827,7 @@ async function restructureProgram(previous, edited, discovery) {
11602
12827
  if (!sameList(parentMost(roots), parentMost(mirror.roots)))
11603
12828
  return { kind: "refused", reason: "the members' parent-most folders changed" };
11604
12829
  const importers = new Set(mirror.observations.importers());
11605
- const deleted = [...importers].filter((path17) => !existsSync12(path17));
12830
+ const deleted = [...importers].filter((path18) => !existsSync12(path18));
11606
12831
  const added = roots.filter((root) => !importers.has(normalize2(root)));
11607
12832
  if (edited.length + deleted.length + added.length > MAX_PATCHED_FILES)
11608
12833
  return {
@@ -11611,50 +12836,50 @@ async function restructureProgram(previous, edited, discovery) {
11611
12836
  };
11612
12837
  const compiled = project.getProgram().compilerObject;
11613
12838
  const plans = [];
11614
- for (const path17 of edited) {
11615
- const source = project.getSourceFile(path17);
11616
- const importer = mirror.observations.importer(path17);
12839
+ for (const path18 of edited) {
12840
+ const source = project.getSourceFile(path18);
12841
+ const importer = mirror.observations.importer(path18);
11617
12842
  if (!source || !importer)
11618
- return { kind: "refused", reason: `${path17} is not a file of the retained program` };
12843
+ return { kind: "refused", reason: `${path18} is not a file of the retained program` };
11619
12844
  if (source.compilerNode.isDeclarationFile || compiled.isSourceFileDefaultLibrary(source.compilerNode) || compiled.isSourceFileFromExternalLibrary(source.compilerNode))
11620
- return { kind: "refused", reason: `${path17} is a declaration or library file` };
12845
+ return { kind: "refused", reason: `${path18} is a declaration or library file` };
11621
12846
  let raw;
11622
12847
  try {
11623
- raw = readText(path17);
12848
+ raw = readText(path18);
11624
12849
  } catch (error) {
11625
12850
  return {
11626
12851
  kind: "refused",
11627
- reason: `${path17} cannot be read: ${error instanceof Error ? error.message : String(error)}`
12852
+ reason: `${path18} cannot be read: ${error instanceof Error ? error.message : String(error)}`
11628
12853
  };
11629
12854
  }
11630
12855
  const text = raw.text.charCodeAt(0) === 65279 ? raw.text.slice(1) : raw.text;
11631
- const reason = patchable(path17, source.compilerNode.text, "before") ?? patchable(path17, text, "after");
11632
- if (reason) return { kind: "refused", reason: `${path17}: ${reason}` };
11633
- plans.push({ path: path17, source, raw, text, before: comparable(importer) });
12856
+ const reason = patchable(path18, source.compilerNode.text, "before") ?? patchable(path18, text, "after");
12857
+ if (reason) return { kind: "refused", reason: `${path18}: ${reason}` };
12858
+ plans.push({ path: path18, source, raw, text, before: comparable(importer) });
11634
12859
  }
11635
- for (const path17 of deleted) {
11636
- const source = project.getSourceFile(path17);
11637
- const reason = source ? patchable(path17, source.compilerNode.text, "before") : void 0;
11638
- if (reason) return { kind: "refused", reason: `deleted ${path17}: ${reason}` };
12860
+ for (const path18 of deleted) {
12861
+ const source = project.getSourceFile(path18);
12862
+ const reason = source ? patchable(path18, source.compilerNode.text, "before") : void 0;
12863
+ if (reason) return { kind: "refused", reason: `deleted ${path18}: ${reason}` };
11639
12864
  }
11640
- for (const path17 of added) {
12865
+ for (const path18 of added) {
11641
12866
  let text;
11642
12867
  try {
11643
- text = readText(path17).text;
12868
+ text = readText(path18).text;
11644
12869
  } catch (error) {
11645
12870
  return {
11646
12871
  kind: "refused",
11647
- reason: `${path17} cannot be read: ${error instanceof Error ? error.message : String(error)}`
12872
+ reason: `${path18} cannot be read: ${error instanceof Error ? error.message : String(error)}`
11648
12873
  };
11649
12874
  }
11650
- const reason = patchable(path17, text.charCodeAt(0) === 65279 ? text.slice(1) : text, "after");
11651
- if (reason) return { kind: "refused", reason: `new ${path17}: ${reason}` };
12875
+ const reason = patchable(path18, text.charCodeAt(0) === 65279 ? text.slice(1) : text, "after");
12876
+ if (reason) return { kind: "refused", reason: `new ${path18}: ${reason}` };
11652
12877
  }
11653
12878
  for (const plan of plans) mirror.appliedSinceBase.set(plan.path, plan.raw.digest);
11654
12879
  mirror.version += 1;
11655
12880
  let after;
11656
12881
  const unchanged = new Set(
11657
- [...importers].filter((path17) => !plans.some((plan) => plan.path === path17) && existsSync12(path17))
12882
+ [...importers].filter((path18) => !plans.some((plan) => plan.path === path18) && existsSync12(path18))
11658
12883
  );
11659
12884
  withPinnedRoots(
11660
12885
  wrapper,
@@ -11664,13 +12889,13 @@ async function restructureProgram(previous, edited, discovery) {
11664
12889
  owned.recordRead(plan.path, plan.raw.text);
11665
12890
  if (plan.source.getFullText() !== plan.text) replaceSource(factory, plan.source, plan.text);
11666
12891
  }
11667
- for (const path17 of deleted) cache.get(path17)?.forget();
12892
+ for (const path18 of deleted) cache.get(path18)?.forget();
11668
12893
  for (const root of added) project.addSourceFileAtPath(root);
11669
12894
  project.resolveSourceFileDependencies();
11670
12895
  const files2 = new Set(
11671
12896
  project.getProgram().compilerObject.getSourceFiles().map((source) => source.fileName)
11672
12897
  );
11673
- for (const path17 of [...cache.paths()]) if (!files2.has(path17)) cache.get(path17)?.forget();
12898
+ for (const path18 of [...cache.paths()]) if (!files2.has(path18)) cache.get(path18)?.forget();
11674
12899
  after = project.getProgram().compilerObject;
11675
12900
  },
11676
12901
  (host) => reusingResolutions(host, compiled, unchanged, project.getCompilerOptions())
@@ -11678,7 +12903,7 @@ async function restructureProgram(previous, edited, discovery) {
11678
12903
  if (!sameList(after.getRootFileNames(), roots))
11679
12904
  return { kind: "refused", reason: "the restructured program has other root files" };
11680
12905
  const files = new Set(after.getSourceFiles().map((source) => normalize2(source.fileName)));
11681
- const gone = deleted.find((path17) => files.has(path17));
12906
+ const gone = deleted.find((path18) => files.has(path18));
11682
12907
  if (gone) return { kind: "refused", reason: `the deleted ${gone} is still a program file` };
11683
12908
  const failure2 = syntaxFailure(after);
11684
12909
  if (failure2) return { kind: "refused", reason: `syntax errors: ${failure2.message}` };
@@ -11687,7 +12912,7 @@ async function restructureProgram(previous, edited, discovery) {
11687
12912
  project,
11688
12913
  owned,
11689
12914
  membership,
11690
- (path17) => !retracked.has(path17) && existsSync12(path17)
12915
+ (path18) => !retracked.has(path18) && existsSync12(path18)
11691
12916
  );
11692
12917
  const watch = mirror.watch;
11693
12918
  watch.stamps.record(tracked, observedAtNs);
@@ -11697,7 +12922,7 @@ async function restructureProgram(previous, edited, discovery) {
11697
12922
  const listings = watch.listings.withoutGlobs();
11698
12923
  for (const dependency of mirror.observations.files.all())
11699
12924
  if (dependency.kind === "glob")
11700
- listings.recordGlob(dependency, observedAtNs, (path17) => owned.has(path17));
12925
+ listings.recordGlob(dependency, observedAtNs, (path18) => owned.has(path18));
11701
12926
  watch.listings = listings;
11702
12927
  mirror.roots = roots;
11703
12928
  previous.scopes.clear();
@@ -11759,7 +12984,7 @@ function sourceCache(project) {
11759
12984
  return void 0;
11760
12985
  return {
11761
12986
  paths: () => [...factory.getSourceFilePaths()],
11762
- get: (path17) => factory.getSourceFileFromCacheFromFilePath(fileSystem.getStandardizedAbsolutePath(path17)),
12987
+ get: (path18) => factory.getSourceFileFromCacheFromFilePath(fileSystem.getStandardizedAbsolutePath(path18)),
11763
12988
  fileSystem: { globSync: (patterns) => fileSystem.globSync(patterns) }
11764
12989
  };
11765
12990
  }
@@ -11789,11 +13014,11 @@ function withPinnedRoots(program, roots, action, hostFor = (host) => host) {
11789
13014
  }
11790
13015
  var TYPESCRIPT_SOURCE = /\.(?:[cm]?ts|tsx)$/;
11791
13016
  var DECLARATION = /\.d\.(?:[^/]*\.)?[cm]?ts$/;
11792
- function patchable(path17, text, when) {
11793
- if (!TYPESCRIPT_SOURCE.test(path17) || DECLARATION.test(path17))
13017
+ function patchable(path18, text, when) {
13018
+ if (!TYPESCRIPT_SOURCE.test(path18) || DECLARATION.test(path18))
11794
13019
  return "not a TypeScript source file";
11795
- if (path17.includes("/node_modules/")) return "a library file";
11796
- const source = ts12.createSourceFile(path17, text, ts12.ScriptTarget.Latest, false);
13020
+ if (path18.includes("/node_modules/")) return "a library file";
13021
+ const source = ts12.createSourceFile(path18, text, ts12.ScriptTarget.Latest, false);
11797
13022
  if (!ts12.isExternalModule(source)) return `a script ${when} the edit`;
11798
13023
  for (const statement of source.statements) {
11799
13024
  if (ts12.isModuleDeclaration(statement) && (ts12.isStringLiteral(statement.name) || (statement.flags & ts12.NodeFlags.GlobalAugmentation) !== 0))
@@ -11802,9 +13027,9 @@ function patchable(path17, text, when) {
11802
13027
  }
11803
13028
  return void 0;
11804
13029
  }
11805
- function references(path17, text) {
13030
+ function references(path18, text) {
11806
13031
  const info = ts12.preProcessFile(text, true, true);
11807
- const pragmas = ts12.createSourceFile(path17, text, ts12.ScriptTarget.Latest, false).pragmas;
13032
+ const pragmas = ts12.createSourceFile(path18, text, ts12.ScriptTarget.Latest, false).pragmas;
11808
13033
  const argumentsOf = (value) => Array.isArray(value) ? value.map(argumentsOf) : value?.arguments ?? null;
11809
13034
  return JSON.stringify([
11810
13035
  [...pragmas ?? /* @__PURE__ */ new Map()].map(([name, value]) => [name, argumentsOf(value)]),
@@ -12585,12 +13810,12 @@ function createJsDoc(markdown) {
12585
13810
  const original = comment?.getStructure().tags ?? [];
12586
13811
  if (!comment || !comment.getText().includes("```")) return original;
12587
13812
  const ranges = [];
12588
- const visit8 = (node2) => {
13813
+ const visit9 = (node2) => {
12589
13814
  if (node2 instanceof DocExcerpt && node2.excerptKind === ExcerptKind.FencedCode_Code)
12590
13815
  ranges.push(node2.content.getContainingTextRange());
12591
- node2.getChildNodes().forEach(visit8);
13816
+ node2.getChildNodes().forEach(visit9);
12592
13817
  };
12593
- visit8(new TSDocParser().parseString(comment.getText().replace(/@/g, "a")).docComment);
13818
+ visit9(new TSDocParser().parseString(comment.getText().replace(/@/g, "a")).docComment);
12594
13819
  const nativeTags = comment.getTags();
12595
13820
  const validTags = nativeTags.filter((tag) => {
12596
13821
  const start = tag.getStart() - comment.getStart();
@@ -12631,10 +13856,10 @@ function createJsDoc(markdown) {
12631
13856
  }
12632
13857
  var { compiler: nunjucksCompiler2, lib: nunjucksLib2 } = nunjucks2;
12633
13858
  var compiledTemplates = /* @__PURE__ */ new Map();
12634
- function compiledSource(path17, text, environment) {
13859
+ function compiledSource(path18, text, environment) {
12635
13860
  const cacheable = !environment.extensionsList.length;
12636
13861
  const key = JSON.stringify([text, environment.asyncFilters, environment.opts]);
12637
- const cached = compiledTemplates.get(path17);
13862
+ const cached = compiledTemplates.get(path18);
12638
13863
  if (cacheable && cached?.key === key) return { type: "code", obj: cached.code };
12639
13864
  let code;
12640
13865
  try {
@@ -12644,15 +13869,15 @@ function compiledSource(path17, text, environment) {
12644
13869
  text,
12645
13870
  environment.asyncFilters,
12646
13871
  environment.extensionsList,
12647
- path17,
13872
+ path18,
12648
13873
  environment.opts
12649
13874
  )
12650
13875
  )();
12651
13876
  } catch (error) {
12652
- compiledTemplates.delete(path17);
12653
- throw nunjucksLib2._prettifyError(path17, environment.opts.dev, error);
13877
+ compiledTemplates.delete(path18);
13878
+ throw nunjucksLib2._prettifyError(path18, environment.opts.dev, error);
12654
13879
  }
12655
- if (cacheable) compiledTemplates.set(path17, { key, code });
13880
+ if (cacheable) compiledTemplates.set(path18, { key, code });
12656
13881
  return { type: "code", obj: code };
12657
13882
  }
12658
13883
  function signalMember(node) {
@@ -12666,10 +13891,10 @@ function signalMember(node) {
12666
13891
  function renderApiTemplate(template, context, root, files, markdown, configDirectory) {
12667
13892
  class TrackedLoader extends Loader2 {
12668
13893
  getSource(name) {
12669
- const path17 = hostPath(resolve7(root, name));
12670
- const text = files.read(path17);
12671
- const src = compiledSource(path17, text, environment);
12672
- return { src, path: path17, noCache: true };
13894
+ const path18 = hostPath(resolve7(root, name));
13895
+ const text = files.read(path18);
13896
+ const src = compiledSource(path18, text, environment);
13897
+ return { src, path: path18, noCache: true };
12673
13898
  }
12674
13899
  }
12675
13900
  const docs = createJsDoc(markdown);
@@ -12736,17 +13961,17 @@ function requestedDeclaration(state, request, files, context) {
12736
13961
  const [file, name, extra] = request.declarationPath.split("#");
12737
13962
  if (!file || !name || extra)
12738
13963
  throw new SemanticFailure("SEMANTIC_DECLARATION_PATH", "Expected path/to/file.ts#exportName");
12739
- const path17 = normalize2(resolve8(state.discovery.configuration.workspaceRoot, file));
12740
- if (state.owned.has(path17)) throw ownedOverlap("API declaration path", path17, state.owned);
12741
- files.read(path17);
12742
- recordLookup(path17);
12743
- let source = state.project.getSourceFile(path17);
13964
+ const path18 = normalize2(resolve8(state.discovery.configuration.workspaceRoot, file));
13965
+ if (state.owned.has(path18)) throw ownedOverlap("API declaration path", path18, state.owned);
13966
+ files.read(path18);
13967
+ recordLookup(path18);
13968
+ let source = state.project.getSourceFile(path18);
12744
13969
  const added = !source;
12745
13970
  if (!source) {
12746
13971
  state.mutated = true;
12747
13972
  const project = state.project;
12748
- source = withRootsAppended(project, path17, () => {
12749
- const added2 = project.addSourceFileAtPath(path17);
13973
+ source = withRootsAppended(project, path18, () => {
13974
+ const added2 = project.addSourceFileAtPath(path18);
12750
13975
  project.resolveSourceFileDependencies();
12751
13976
  return added2;
12752
13977
  });
@@ -12759,7 +13984,7 @@ function requestedDeclaration(state, request, files, context) {
12759
13984
  throw new SemanticFailure(
12760
13985
  "SEMANTIC_DECLARATION_MISSING",
12761
13986
  `Cannot resolve exported declaration ${request.declarationPath}`,
12762
- { path: path17 }
13987
+ { path: path18 }
12763
13988
  );
12764
13989
  return { node: declaration };
12765
13990
  }
@@ -12793,25 +14018,29 @@ function renderFragment(state, request, files, context) {
12793
14018
  const html = withIndexedDerivedClasses(
12794
14019
  node,
12795
14020
  state.project,
12796
- () => renderApiTemplate(
12797
- template,
12798
- {
12799
- declaration: node,
12800
- docNode: documented,
12801
- templateName: kind,
12802
- scope,
12803
- ...request.kind === "api" ? {
12804
- hideDescription: true,
12805
- hideSeeAlso: true,
12806
- hideUsageNotes: true,
12807
- hideRemarks: true,
12808
- hideExamples: true
12809
- } : {}
12810
- },
12811
- context.templateRoot(),
12812
- files,
12813
- (text) => context.markdown(text, source, files),
12814
- state.discovery.configuration.workspaceRoot
14021
+ () => formatting(
14022
+ () => renderApiTemplate(
14023
+ template,
14024
+ {
14025
+ declaration: node,
14026
+ docNode: documented,
14027
+ templateName: kind,
14028
+ scope,
14029
+ // The legacy engine passes no such flag, so the shared templates keep protected members.
14030
+ hideProtectedMembers: state.discovery.configuration.apiProtectedMembers === false,
14031
+ ...request.kind === "api" ? {
14032
+ hideDescription: true,
14033
+ hideSeeAlso: true,
14034
+ hideUsageNotes: true,
14035
+ hideRemarks: true,
14036
+ hideExamples: true
14037
+ } : {}
14038
+ },
14039
+ context.templateRoot(),
14040
+ files,
14041
+ (text) => context.markdown(text, source, files),
14042
+ state.discovery.configuration.workspaceRoot
14043
+ )
12815
14044
  )
12816
14045
  );
12817
14046
  return {
@@ -12834,10 +14063,10 @@ function trackWholeProgram(state, files, context) {
12834
14063
  }
12835
14064
  for (const dependency of state.tracked.dependencies) files.add(dependency);
12836
14065
  }
12837
- function withRootsAppended(project, path17, add) {
14066
+ function withRootsAppended(project, path18, add) {
12838
14067
  const program = project.getProgram();
12839
14068
  if (typeof program._reset !== "function") return add();
12840
- const roots = [...project.getProgram().compilerObject.getRootFileNames(), path17];
14069
+ const roots = [...project.getProgram().compilerObject.getRootFileNames(), path18];
12841
14070
  let added;
12842
14071
  withPinnedRoots(program, roots, () => {
12843
14072
  added = add();
@@ -13036,8 +14265,8 @@ var ProgramIndex = class {
13036
14265
  let value = this.stars.get(file);
13037
14266
  if (value === void 0) {
13038
14267
  const found = /* @__PURE__ */ new Set();
13039
- const visit8 = (current) => {
13040
- const source = this.program.getSourceFile(current);
14268
+ const visit9 = (current2) => {
14269
+ const source = this.program.getSourceFile(current2);
13041
14270
  if (!source) return;
13042
14271
  for (const statement of source.statements) {
13043
14272
  if (!ts13.isExportDeclaration(statement) || statement.exportClause || !statement.moduleSpecifier || !ts13.isStringLiteralLike(statement.moduleSpecifier))
@@ -13045,11 +14274,11 @@ var ProgramIndex = class {
13045
14274
  for (const target of this.moduleFiles(statement.moduleSpecifier, source)) {
13046
14275
  if (found.has(target)) continue;
13047
14276
  found.add(target);
13048
- visit8(target);
14277
+ visit9(target);
13049
14278
  }
13050
14279
  }
13051
14280
  };
13052
- visit8(file);
14281
+ visit9(file);
13053
14282
  value = [...found].sort(compare);
13054
14283
  this.stars.set(file, value);
13055
14284
  }
@@ -13347,12 +14576,12 @@ function isolatedShape(source, options) {
13347
14576
  });
13348
14577
  if (emitted.diagnostics?.length) return void 0;
13349
14578
  const enums = [];
13350
- const visit8 = (node) => {
14579
+ const visit9 = (node) => {
13351
14580
  if (ts13.isEnumDeclaration(node))
13352
14581
  enums.push(printer.printNode(ts13.EmitHint.Unspecified, node, source));
13353
- else ts13.forEachChild(node, visit8);
14582
+ else ts13.forEachChild(node, visit9);
13354
14583
  };
13355
- visit8(source);
14584
+ visit9(source);
13356
14585
  return hash2("dts-shape-v1", "dts", emitted.outputText, enums);
13357
14586
  }
13358
14587
  function printedToken(source) {
@@ -14099,19 +15328,19 @@ async function patchMismatch(patched, result, discovery, signal) {
14099
15328
 
14100
15329
  // libs/builder/generator/compiler/assemble.ts
14101
15330
  import { existsSync as existsSync16 } from "node:fs";
14102
- import path16 from "node:path";
15331
+ import path17 from "node:path";
14103
15332
 
14104
15333
  // libs/builder/generator/compiler/plan.ts
14105
- import { randomUUID as randomUUID7 } from "node:crypto";
15334
+ import { randomUUID as randomUUID8 } from "node:crypto";
14106
15335
  import { existsSync as existsSync13 } from "node:fs";
14107
- import { mkdir as mkdir5, readFile as readFile4, rename as rename6, rm as rm6, writeFile as writeFile6 } from "node:fs/promises";
14108
- import path14 from "node:path";
15336
+ import { mkdir as mkdir6, readFile as readFile4, rename as rename7, rm as rm7, writeFile as writeFile7 } from "node:fs/promises";
15337
+ import path15 from "node:path";
14109
15338
 
14110
15339
  // libs/builder/generator/compiler/fast-start.ts
14111
- import { randomUUID as randomUUID5 } from "node:crypto";
14112
- import { readFileSync as readFileSync8 } from "node:fs";
14113
- import { mkdir as mkdir4, rename as rename4, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
14114
- import path12 from "node:path";
15340
+ import { randomUUID as randomUUID6 } from "node:crypto";
15341
+ import { readFileSync as readFileSync9 } from "node:fs";
15342
+ import { mkdir as mkdir5, rename as rename5, rm as rm5, writeFile as writeFile5 } from "node:fs/promises";
15343
+ import path13 from "node:path";
14115
15344
  import { ts as ts15 } from "ts-morph";
14116
15345
  var FAST_START_MISMATCH = "COMPILATION_FAST_START_MISMATCH";
14117
15346
  var RECORD_VERSION = 1;
@@ -14133,7 +15362,7 @@ function fastStartMode(options, request) {
14133
15362
  function fastStartPath(options, request, configuration) {
14134
15363
  if (request.mode !== "development" || options.incrementalReuse === false || fastStartSwitch(options) === "off" || !configuration.cacheEnabled)
14135
15364
  return void 0;
14136
- return path12.join(configuration.cacheRoot, `${hash(options.projectId)}.fast-start.json`);
15365
+ return path13.join(configuration.cacheRoot, `${hash(options.projectId)}.fast-start.json`);
14137
15366
  }
14138
15367
  function engineSwitches() {
14139
15368
  return FLAGS.filter((flag) => flag.name !== FAST_START_FLAG).map((flag) => [
@@ -14183,22 +15412,22 @@ async function saveFastStart(file, input) {
14183
15412
  const text = JSON.stringify(record);
14184
15413
  const digest3 = contentDigest(text);
14185
15414
  if (known2.get(file) === digest3 && currentDigest(file) === digest3) return;
14186
- const temporary = `${file}.${randomUUID5()}.tmp`;
15415
+ const temporary = `${file}.${randomUUID6()}.tmp`;
14187
15416
  try {
14188
- await mkdir4(path12.dirname(file), { recursive: true });
15417
+ await mkdir5(path13.dirname(file), { recursive: true });
14189
15418
  await sweepTemporaries(file);
14190
- await writeFile4(temporary, text, { flag: "wx" });
14191
- await retryingRename(rename4)(temporary, file);
15419
+ await writeFile5(temporary, text, { flag: "wx" });
15420
+ await retryingRename(rename5)(temporary, file);
14192
15421
  known2.set(file, digest3);
14193
15422
  } catch {
14194
15423
  known2.delete(file);
14195
15424
  } finally {
14196
- await rm4(temporary, { force: true }).catch(() => void 0);
15425
+ await rm5(temporary, { force: true }).catch(() => void 0);
14197
15426
  }
14198
15427
  }
14199
15428
  function currentDigest(file) {
14200
15429
  try {
14201
- return bytesDigest(readFileSync8(file));
15430
+ return bytesDigest(readFileSync9(file));
14202
15431
  } catch {
14203
15432
  return void 0;
14204
15433
  }
@@ -14207,7 +15436,7 @@ async function restoreFastStart(input) {
14207
15436
  const { file, options, found, signal } = input;
14208
15437
  let record;
14209
15438
  try {
14210
- const bytes = readFileSync8(file);
15439
+ const bytes = readFileSync9(file);
14211
15440
  const value = JSON.parse(bytes.toString("utf8"));
14212
15441
  if (!isRecord2(value)) return { reason: "the start record is invalid" };
14213
15442
  record = value;
@@ -14265,7 +15494,7 @@ function physical(dependencies) {
14265
15494
  );
14266
15495
  }
14267
15496
  function normalize3(value) {
14268
- return path12.resolve(value).replace(/\\/g, "/");
15497
+ return path13.resolve(value).replace(/\\/g, "/");
14269
15498
  }
14270
15499
  function members(values, roots) {
14271
15500
  return JSON.stringify(
@@ -14281,8 +15510,8 @@ async function firstChange(inputs, ownedRoots) {
14281
15510
  for (let index2 = next++; index2 < inputs.length && !found; index2 = next++) {
14282
15511
  const input2 = inputs[index2];
14283
15512
  const refreshed = await refresher.refresh([input2], []);
14284
- const current = refreshed.dependencies[0];
14285
- const same = !refreshed.diagnostics.some((item) => item.severity === "error") && (input2.kind === "content" ? current?.kind === "content" && current.digest === input2.digest : input2.kind === "existence" ? current?.kind === "existence" && current.exists === input2.exists : current?.kind === "glob" && members(current.members, ownedRoots) === members(input2.members, ownedRoots));
15513
+ const current2 = refreshed.dependencies[0];
15514
+ const same = !refreshed.diagnostics.some((item) => item.severity === "error") && (input2.kind === "content" ? current2?.kind === "content" && current2.digest === input2.digest : input2.kind === "existence" ? current2?.kind === "existence" && current2.exists === input2.exists : current2?.kind === "glob" && members(current2.members, ownedRoots) === members(input2.members, ownedRoots));
14286
15515
  if (!same) {
14287
15516
  changed[index2] = true;
14288
15517
  found = true;
@@ -14323,12 +15552,12 @@ function isDependency2(value) {
14323
15552
  }
14324
15553
 
14325
15554
  // libs/builder/generator/compiler/memo.ts
14326
- import { randomUUID as randomUUID6 } from "node:crypto";
14327
- import { readFileSync as readFileSync9 } from "node:fs";
14328
- import { rename as rename5, rm as rm5, writeFile as writeFile5 } from "node:fs/promises";
14329
- import path13 from "node:path";
15555
+ import { randomUUID as randomUUID7 } from "node:crypto";
15556
+ import { readFileSync as readFileSync10 } from "node:fs";
15557
+ import { rename as rename6, rm as rm6, writeFile as writeFile6 } from "node:fs/promises";
15558
+ import path14 from "node:path";
14330
15559
  var MEMO_SCHEMA_VERSION = 2;
14331
- var memoPath = (root, projectId) => path13.join(root, `${hash(projectId)}.compiler-memo.json`);
15560
+ var memoPath = (root, projectId) => path14.join(root, `${hash(projectId)}.compiler-memo.json`);
14332
15561
  function createMemoState(enabled, root, options) {
14333
15562
  let loaded;
14334
15563
  const empty2 = () => ({
@@ -14345,7 +15574,7 @@ function createMemoState(enabled, root, options) {
14345
15574
  loaded = empty2();
14346
15575
  if (!enabled) return loaded;
14347
15576
  try {
14348
- const value = JSON.parse(readFileSync9(memoPath(root, options.projectId), "utf8"));
15577
+ const value = JSON.parse(readFileSync10(memoPath(root, options.projectId), "utf8"));
14349
15578
  if (isMemoFile(value) && value.compilerVersion === options.compilerVersion && value.toolchainDigest === options.toolchainDigest)
14350
15579
  loaded = value;
14351
15580
  } catch {
@@ -14382,13 +15611,13 @@ function createMemoState(enabled, root, options) {
14382
15611
  async save(projectId) {
14383
15612
  if (!enabled) return;
14384
15613
  const file = memoPath(root, projectId);
14385
- const temporary = `${file}.${randomUUID6()}.tmp`;
15614
+ const temporary = `${file}.${randomUUID7()}.tmp`;
14386
15615
  try {
14387
- await writeFile5(temporary, JSON.stringify(next), { flag: "wx" });
14388
- await retryingRename(rename5)(temporary, file);
15616
+ await writeFile6(temporary, JSON.stringify(next), { flag: "wx" });
15617
+ await retryingRename(rename6)(temporary, file);
14389
15618
  } catch {
14390
15619
  } finally {
14391
- await rm5(temporary, { force: true }).catch(() => void 0);
15620
+ await rm6(temporary, { force: true }).catch(() => void 0);
14392
15621
  }
14393
15622
  }
14394
15623
  };
@@ -14478,14 +15707,20 @@ async function planGeneration(services, input) {
14478
15707
  setup(outputDependencyResult.diagnostics, outputDependencyResult.dependencies);
14479
15708
  const outputDependencies = outputDependencyResult.dependencies;
14480
15709
  const highlight2 = createHighlightSession(options, request, configuration);
15710
+ const format = createFormatSession(options, request, configuration);
15711
+ useFormatCache(format);
15712
+ const grammars2 = grammarsSwitch(options);
14481
15713
  const back = createRenderBack(options, highlight2, {
14482
15714
  themes: configuration.themes,
15715
+ ...configuration.shikiLangs ? { langs: configuration.shikiLangs } : {},
15716
+ ...grammars2 === "all" ? { grammars: grammars2 } : {},
14483
15717
  cache: highlight2 !== void 0
14484
15718
  });
14485
15719
  const compiler = new GeneratorContentCompiler(
14486
15720
  { configuration, semantic, templates: discovery.templates },
14487
15721
  highlight2,
14488
- back
15722
+ back,
15723
+ grammars2
14489
15724
  );
14490
15725
  return {
14491
15726
  ...services,
@@ -14508,6 +15743,7 @@ async function planGeneration(services, input) {
14508
15743
  memo,
14509
15744
  compiler,
14510
15745
  ...highlight2 ? { highlight: highlight2 } : {},
15746
+ ...format ? { format } : {},
14511
15747
  ...back ? { back } : {},
14512
15748
  scopedSemantic
14513
15749
  };
@@ -14529,7 +15765,7 @@ function planContent(plan, units) {
14529
15765
  plan.records.global("plan", { diagnostics, dependencies: [] });
14530
15766
  }
14531
15767
  var RESTORE_CONCURRENCY = 32;
14532
- var indexPath = (root, projectId) => path14.join(root, `${hash(projectId)}.compilation-index.json`);
15768
+ var indexPath = (root, projectId) => path15.join(root, `${hash(projectId)}.compilation-index.json`);
14533
15769
  async function restoreIndex(root, projectId, diagnostics) {
14534
15770
  try {
14535
15771
  const keys = JSON.parse(await readFile4(indexPath(root, projectId), "utf8"));
@@ -14572,18 +15808,18 @@ async function restoreIndex(root, projectId, diagnostics) {
14572
15808
  }
14573
15809
  }
14574
15810
  async function saveIndex(root, projectId, artifacts) {
14575
- await mkdir5(root, { recursive: true });
15811
+ await mkdir6(root, { recursive: true });
14576
15812
  const file = indexPath(root, projectId);
14577
- const temporary = `${file}.${randomUUID7()}.tmp`;
15813
+ const temporary = `${file}.${randomUUID8()}.tmp`;
14578
15814
  try {
14579
- await writeFile6(
15815
+ await writeFile7(
14580
15816
  temporary,
14581
15817
  JSON.stringify(artifacts.map(({ identity, fingerprint }) => ({ identity, fingerprint }))),
14582
15818
  { flag: "wx" }
14583
15819
  );
14584
- await retryingRename(rename6)(temporary, file);
15820
+ await retryingRename(rename7)(temporary, file);
14585
15821
  } finally {
14586
- await rm6(temporary, { force: true });
15822
+ await rm7(temporary, { force: true });
14587
15823
  }
14588
15824
  }
14589
15825
 
@@ -14595,9 +15831,9 @@ import { existsSync as existsSync15, statSync as statSync3 } from "node:fs";
14595
15831
 
14596
15832
  // libs/builder/generator/graph/unit-index.ts
14597
15833
  import { existsSync as existsSync14, realpathSync as realpathSync4 } from "node:fs";
14598
- import path15 from "node:path";
15834
+ import path16 from "node:path";
14599
15835
  function normalizePath3(file) {
14600
- return file.charCodeAt(0) === 47 && !file.includes("/.") && !file.includes("//") && !file.includes("\\") && !file.endsWith("/") ? file : path15.resolve(file).replace(/\\/g, "/");
15836
+ return file.charCodeAt(0) === 47 && !file.includes("/.") && !file.includes("//") && !file.includes("\\") && !file.endsWith("/") ? file : path16.resolve(file).replace(/\\/g, "/");
14601
15837
  }
14602
15838
  function rootKeyword(used) {
14603
15839
  const match = /^[^.#]+/.exec(used);
@@ -14613,7 +15849,7 @@ var WINDOWS_DRIVE = /^[A-Za-z]:$/;
14613
15849
  var WINDOWS_DRIVE_ROOT = /^[A-Za-z]:\/$/;
14614
15850
  function parentDirectory(file) {
14615
15851
  if (WINDOWS_DRIVE_ROOT.test(file)) return file;
14616
- const parent = path15.posix.dirname(file);
15852
+ const parent = path16.posix.dirname(file);
14617
15853
  return WINDOWS_DRIVE.test(parent) ? `${parent}/` : parent;
14618
15854
  }
14619
15855
  function createCanonicalAliases(ports = {}) {
@@ -14635,7 +15871,7 @@ function createCanonicalAliases(ports = {}) {
14635
15871
  }
14636
15872
  } else {
14637
15873
  const parent = parentDirectory(directory);
14638
- resolved = parent === directory ? directory : path15.posix.join(physicalDirectory(parent), path15.posix.basename(directory));
15874
+ resolved = parent === directory ? directory : path16.posix.join(physicalDirectory(parent), path16.posix.basename(directory));
14639
15875
  }
14640
15876
  directories.set(directory, resolved);
14641
15877
  return resolved;
@@ -14646,7 +15882,7 @@ function createCanonicalAliases(ports = {}) {
14646
15882
  if (cached) return cached;
14647
15883
  const normalized = normalize4(file);
14648
15884
  const directory = parentDirectory(normalized);
14649
- const physical2 = directory === normalized ? normalized : `${physicalDirectory(directory).replace(/\/$/, "")}/${path15.posix.basename(normalized)}`;
15885
+ const physical2 = directory === normalized ? normalized : `${physicalDirectory(directory).replace(/\/$/, "")}/${path16.posix.basename(normalized)}`;
14650
15886
  const aliases = physical2 === normalized ? [normalized] : [normalized, physical2];
14651
15887
  files.set(file, aliases);
14652
15888
  return aliases;
@@ -15171,8 +16407,8 @@ function discoveryChanges(index, fresh, structural2 = false) {
15171
16407
  entries.add(entry.id);
15172
16408
  continue;
15173
16409
  }
15174
- const current = indexEntry(entry);
15175
- if (current.masked !== retained.masked || current.evaluated !== retained.evaluated || current.markdown.size !== retained.markdown.size || [...current.markdown].some(([file, digest3]) => retained.markdown.get(file) !== digest3))
16410
+ const current2 = indexEntry(entry);
16411
+ if (current2.masked !== retained.masked || current2.evaluated !== retained.evaluated || current2.markdown.size !== retained.markdown.size || [...current2.markdown].some(([file, digest3]) => retained.markdown.get(file) !== digest3))
15176
16412
  entries.add(entry.id);
15177
16413
  }
15178
16414
  const ids = new Set(fresh.entries.map((entry) => entry.id));
@@ -15201,9 +16437,9 @@ var discoveryFacts = (found, templates) => ({
15201
16437
  remoteKeywords: JSON.stringify(found.remoteKeywords),
15202
16438
  templates: JSON.stringify(templates)
15203
16439
  });
15204
- function explained(path17, read) {
15205
- if (read.has(path17)) return true;
15206
- const prefix = `${path17}/`;
16440
+ function explained(path18, read) {
16441
+ if (read.has(path18)) return true;
16442
+ const prefix = `${path18}/`;
15207
16443
  for (const item of read) if (item.startsWith(prefix)) return true;
15208
16444
  return false;
15209
16445
  }
@@ -15216,8 +16452,8 @@ function sweep(build2, index) {
15216
16452
  else if (observed === "present") {
15217
16453
  same = existsSync15(file) && stampOf(file) === void 0;
15218
16454
  } else {
15219
- const current = stampOf(file);
15220
- same = current !== void 0 && sameStamp(current, observed);
16455
+ const current2 = stampOf(file);
16456
+ same = current2 !== void 0 && sameStamp(current2, observed);
15221
16457
  }
15222
16458
  if (!same && index.hits(file).length) changed.push({ kind: "update", path: file });
15223
16459
  }
@@ -15518,7 +16754,7 @@ var TargetedGeneration = class _TargetedGeneration {
15518
16754
  for (const phase of ["discovery", "semantic"])
15519
16755
  for (const contribution of plan.records.globalContributions(phase))
15520
16756
  add(contribution.dependencies);
15521
- return paths.filter((path17) => !explained(path17, read));
16757
+ return paths.filter((path18) => !explained(path18, read));
15522
16758
  }
15523
16759
  /**
15524
16760
  * Compares an API entry's fresh enumeration with its retained units: the entry's own unit holds
@@ -15578,11 +16814,11 @@ var TargetedGeneration = class _TargetedGeneration {
15578
16814
  if (!recorded.size) return false;
15579
16815
  const refreshed = await refresher.refresh([...recorded.values()], []);
15580
16816
  if (refreshed.diagnostics.some((item) => item.severity === "error")) return true;
15581
- const current = new Map(
16817
+ const current2 = new Map(
15582
16818
  refreshed.dependencies.map((dependency) => [dependencyIdentity(dependency), dependency])
15583
16819
  );
15584
16820
  for (const [identity, dependency] of recorded) {
15585
- const now = current.get(identity);
16821
+ const now = current2.get(identity);
15586
16822
  if (!now || !("digest" in now) || !("digest" in dependency) || now.digest !== dependency.digest)
15587
16823
  return true;
15588
16824
  }
@@ -15698,8 +16934,8 @@ var TargetedGeneration = class _TargetedGeneration {
15698
16934
  closure(units, keywords) {
15699
16935
  const index = this.committed.index;
15700
16936
  const started = performance.now();
15701
- const current = new Set(units.map((unit) => unit.id));
15702
- this.removedOwners = [...index.owners.keys()].filter((id) => !current.has(id)).sort();
16937
+ const current2 = new Set(units.map((unit) => unit.id));
16938
+ this.removedOwners = [...index.owners.keys()].filter((id) => !current2.has(id)).sort();
15703
16939
  if (this.keywordFacts) {
15704
16940
  const keys = /* @__PURE__ */ new Set();
15705
16941
  for (const id of [...this.candidates, ...this.removedOwners])
@@ -15719,7 +16955,7 @@ var TargetedGeneration = class _TargetedGeneration {
15719
16955
  this.facts.keysFrom = "all";
15720
16956
  }
15721
16957
  const consumers = [...index.consumersOf(this.keys)].filter(
15722
- (id) => !this.candidates.has(id) && current.has(id)
16958
+ (id) => !this.candidates.has(id) && current2.has(id)
15723
16959
  );
15724
16960
  this.linked = /* @__PURE__ */ new Set([...this.candidates, ...consumers]);
15725
16961
  this.facts.keys = this.keys;
@@ -15747,7 +16983,7 @@ var TargetedGeneration = class _TargetedGeneration {
15747
16983
  if (dependency.kind === "content" || dependency.kind === "existence")
15748
16984
  read.add(normalizePath3(dependency.path));
15749
16985
  }
15750
- const missing = this.unknown.find((path17) => !explained(path17, read));
16986
+ const missing = this.unknown.find((path18) => !explained(path18, read));
15751
16987
  if (missing) throw new TargetedFallback(`unknown path not read by this generation: ${missing}`);
15752
16988
  }
15753
16989
  /** The units a targeted generation linked and assembled (for the report). */
@@ -15960,7 +17196,7 @@ function scopedSemanticSwitch(options) {
15960
17196
  function scopedSemanticMode(options, request) {
15961
17197
  return request.mode === "development" ? scopedSemanticSwitch(options) : "off";
15962
17198
  }
15963
- function closureRebuildReason(previous, current) {
17199
+ function closureRebuildReason(previous, current2) {
15964
17200
  const closures = (artifact) => {
15965
17201
  const values = /* @__PURE__ */ new Map();
15966
17202
  for (const dependency of [
@@ -15971,9 +17207,9 @@ function closureRebuildReason(previous, current) {
15971
17207
  return values;
15972
17208
  };
15973
17209
  const before = closures(previous);
15974
- const after = closures(current);
17210
+ const after = closures(current2);
15975
17211
  const key = [.../* @__PURE__ */ new Set([...before.keys(), ...after.keys()])].sort().find((item) => before.get(item) !== after.get(item));
15976
- return key === void 0 ? void 0 : { ownerId: current.id, reason: "semantic", detail: `semantic closure changed: ${key}` };
17212
+ return key === void 0 ? void 0 : { ownerId: current2.id, reason: "semantic", detail: `semantic closure changed: ${key}` };
15977
17213
  }
15978
17214
  async function verifyClosureReuse(plan, unit, descriptor, reused) {
15979
17215
  if (plan.scopedSemantic === "off") return void 0;
@@ -16003,12 +17239,12 @@ async function verifyClosureReuse(plan, unit, descriptor, reused) {
16003
17239
  };
16004
17240
  }
16005
17241
  function valueAt(value, at) {
16006
- let current = value;
17242
+ let current2 = value;
16007
17243
  for (const [, key, index] of at.matchAll(/\.([^.[]+)|\[(\d+)\]/g)) {
16008
- if (current === null || typeof current !== "object") return void 0;
16009
- current = current[key ?? Number(index)];
17244
+ if (current2 === null || typeof current2 !== "object") return void 0;
17245
+ current2 = current2[key ?? Number(index)];
16010
17246
  }
16011
- return current !== null && typeof current === "object" ? JSON.stringify(current) : current;
17247
+ return current2 !== null && typeof current2 === "object" ? JSON.stringify(current2) : current2;
16012
17248
  }
16013
17249
 
16014
17250
  // libs/builder/generator/compiler/assemble.ts
@@ -16223,7 +17459,7 @@ function candidateSnapshot(plan, artifacts, keywordPlan, site) {
16223
17459
  for (const output2 of artifact.outputs) {
16224
17460
  if (plan.previousById.get(artifact.id)?.outputs.some(
16225
17461
  (previousOutput) => previousOutput.path === output2.path && previousOutput.role === output2.role
16226
- ) && !existsSync16(path16.resolve(configuration.outputRoot, output2.path))) {
17462
+ ) && !existsSync16(path17.resolve(configuration.outputRoot, output2.path))) {
16227
17463
  records.reason({
16228
17464
  ownerId: artifact.id,
16229
17465
  reason: "output-missing",
@@ -17083,8 +18319,8 @@ async function renderFront(plan, unit, scope, rendered) {
17083
18319
  function matchesDescriptorContent(descriptor, ir) {
17084
18320
  return ir.schemaVersion === GENERATOR_SCHEMA_VERSION && ir.id === descriptor.id && ir.role === descriptor.role && ir.title === descriptor.title && ir.route === descriptor.route && ir.absoluteRoute === descriptor.absoluteRoute && sameValue(ir.searchBreadcrumbs ?? [], descriptor.searchBreadcrumbs) && ir.icon === descriptor.icon;
17085
18321
  }
17086
- function sameDescriptorPlan(previous, current) {
17087
- return !!previous && sameValue(previous, current);
18322
+ function sameDescriptorPlan(previous, current2) {
18323
+ return !!previous && sameValue(previous, current2);
17088
18324
  }
17089
18325
 
17090
18326
  // libs/builder/generator/compiler/retention.ts
@@ -17329,7 +18565,7 @@ function createCompilationService(options) {
17329
18565
  };
17330
18566
  const targetedResults = /* @__PURE__ */ new WeakSet();
17331
18567
  let disposed = false;
17332
- let active = false;
18568
+ let active2 = false;
17333
18569
  let activeController;
17334
18570
  let activeSettlement;
17335
18571
  return {
@@ -17337,7 +18573,7 @@ function createCompilationService(options) {
17337
18573
  async compile(request, signal, context) {
17338
18574
  let records = new GenerationRecords();
17339
18575
  const result = (candidate) => fold(records, candidate);
17340
- if (disposed || signal.aborted || active) {
18576
+ if (disposed || signal.aborted || active2) {
17341
18577
  records.global("failure", {
17342
18578
  diagnostics: [
17343
18579
  diagnostic(
@@ -17349,7 +18585,7 @@ function createCompilationService(options) {
17349
18585
  });
17350
18586
  return result();
17351
18587
  }
17352
- active = true;
18588
+ active2 = true;
17353
18589
  const releaseThreads = holdHtmlPool();
17354
18590
  guideValues = /* @__PURE__ */ new Map();
17355
18591
  const startedNs = BigInt(Date.now()) * 1000000n;
@@ -17478,6 +18714,8 @@ function createCompilationService(options) {
17478
18714
  if (!scratch)
17479
18715
  prestartRenderThreads(options, request, found.entries.length, {
17480
18716
  themes: found.configuration.themes,
18717
+ ...found.configuration.shikiLangs ? { langs: found.configuration.shikiLangs } : {},
18718
+ ...grammarsSwitch(options) === "all" ? { grammars: "all" } : {},
17481
18719
  cache: highlightCacheSwitch(options) !== "off"
17482
18720
  });
17483
18721
  await synchronize(true);
@@ -17663,8 +18901,9 @@ function createCompilationService(options) {
17663
18901
  ...reportError !== void 0 ? { error: reportError } : {}
17664
18902
  });
17665
18903
  signal.removeEventListener("abort", abort);
18904
+ useFormatCache(void 0);
17666
18905
  releaseThreads();
17667
- active = false;
18906
+ active2 = false;
17668
18907
  activeController = void 0;
17669
18908
  settle();
17670
18909
  activeSettlement = void 0;
@@ -17726,6 +18965,17 @@ async function runPhases(plan, found, scope, cache = true, progress) {
17726
18965
  scope?.explain(artifacts, units);
17727
18966
  progress?.phase("aggregate");
17728
18967
  const site = aggregateUnits(plan, artifacts, keywordPlan);
18968
+ if (plan.format?.mismatches)
18969
+ records.global("aggregate", {
18970
+ diagnostics: [
18971
+ diagnostic(
18972
+ FORMAT_CACHE_MISMATCH,
18973
+ `${plan.format.mismatches} cached formatted signature(s), snippet(s) or formatter configuration(s) differ from formatting them again; the fresh formatting is used.`,
18974
+ "warning"
18975
+ )
18976
+ ],
18977
+ dependencies: []
18978
+ });
17729
18979
  if (signal.aborted || records.failed()) return done();
17730
18980
  const candidate = candidateSnapshot(plan, artifacts, keywordPlan, site);
17731
18981
  if (records.failed()) return done();
@@ -17736,9 +18986,9 @@ async function runPhases(plan, found, scope, cache = true, progress) {
17736
18986
  artifacts,
17737
18987
  scope ? (artifact) => artifact === plan.previousById.get(artifact.id) : void 0
17738
18988
  );
17739
- await plan.highlight?.save(
17740
- !scope && units.every((unit) => unit.record.render.every((step) => step.projection !== "reuse"))
17741
- );
18989
+ const complete = !scope && units.every((unit) => unit.record.render.every((step) => step.projection !== "reuse"));
18990
+ await plan.highlight?.save(complete);
18991
+ await plan.format?.save(complete);
17742
18992
  }
17743
18993
  progress?.end();
17744
18994
  if (signal.aborted) return done();