@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
@@ -776,6 +776,329 @@ var init_helpers2 = __esm({
776
776
  }
777
777
  });
778
778
 
779
+ // libs/utils/html/plugins/keyword-positions.ts
780
+ function scanInlineCode(text) {
781
+ if (WHOLE_KEYWORD.test(text)) {
782
+ return { whole: true, position: () => REFERENCE };
783
+ }
784
+ if (HTML_START.test(text) || !(CODE_PUNCTUATION.test(text) || DECLARATION_START.test(text))) {
785
+ return void 0;
786
+ }
787
+ const scan = scanTypeScript(text);
788
+ return isProse(text, scan.masked) ? void 0 : scan;
789
+ }
790
+ function scanCodeBlock(text, mode) {
791
+ return mode === "html" ? scanHtml(text) : scanTypeScript(text);
792
+ }
793
+ function scanTypeScript(text) {
794
+ const masked = maskTypeScript(text);
795
+ const locals = localBindings(text, masked);
796
+ return {
797
+ whole: false,
798
+ masked,
799
+ position(start, end, key) {
800
+ if (masked[start] === TEMPLATE) {
801
+ const before = text[start - 1] ?? "";
802
+ const element = key.includes("-") && (before === "<" || before === "/" && text[start - 2] === "<");
803
+ const controlFlow = CONTROL_FLOW.test(key) && !WORD_CHARACTER.test(before);
804
+ return element || controlFlow ? REFERENCE : NOT_A_REFERENCE;
805
+ }
806
+ if (masked[start] !== CODE) {
807
+ return NOT_A_REFERENCE;
808
+ }
809
+ const previous = previousSignificant(text, start, masked);
810
+ const next = nextSignificant(text, end, masked);
811
+ const nextCharacter = text[next] ?? "";
812
+ if (SYNTAX_WORDS.has(key) || RESERVED_WORDS.has(key) && nextCharacter !== "(") {
813
+ return NOT_A_REFERENCE;
814
+ }
815
+ if (text[previous] === "." && text[previous - 1] !== ".") {
816
+ const ownerEnd = text[previous - 1] === "?" ? previous - 1 : previous;
817
+ const owner = text.slice(0, ownerEnd).match(IDENTIFIER_BEFORE)?.[0];
818
+ const word = text.slice(start, end);
819
+ return owner && !/[.#]/.test(word) ? { link: true, keyword: `${owner}.${key.toLowerCase()}` } : NOT_A_REFERENCE;
820
+ }
821
+ if (isDeclaredName(text, masked, previous, next)) {
822
+ return NOT_A_REFERENCE;
823
+ }
824
+ return locals.has(key) ? NOT_A_REFERENCE : REFERENCE;
825
+ }
826
+ };
827
+ }
828
+ function isDeclaredName(text, masked, previous, next) {
829
+ const after = text[next];
830
+ const afterNext = text[nextSignificant(text, next + 1, masked)];
831
+ if (after === ":" && text[next + 1] !== ":") {
832
+ const ternary = text[previous] === "?" && text[previous + 1] !== ".";
833
+ return !ternary && previousWord(text, previous) !== "case";
834
+ }
835
+ if ((after === "?" || after === "!") && afterNext === ":") {
836
+ return true;
837
+ }
838
+ if (after === "?" && (afterNext === ")" || afterNext === ",")) {
839
+ return true;
840
+ }
841
+ if (after === "=" && text[next + 1] !== "=" && text[next + 1] !== ">") {
842
+ return !TYPE_CONTEXT.includes(text[previous] ?? "") && !TYPE_WORDS.includes(previousWord(text, previous));
843
+ }
844
+ return false;
845
+ }
846
+ function scanHtml(text) {
847
+ const masked = maskHtml(text);
848
+ return {
849
+ whole: false,
850
+ position: (start) => masked[start] === CODE ? REFERENCE : NOT_A_REFERENCE
851
+ };
852
+ }
853
+ function maskTypeScript(text) {
854
+ const masked = new Uint8Array(text.length);
855
+ const templates = [];
856
+ let depth = 0;
857
+ let index = 0;
858
+ const string = (quote) => {
859
+ const start = index++;
860
+ while (index < text.length && text[index] !== quote && text[index] !== "\n") {
861
+ index += text[index] === "\\" ? 2 : 1;
862
+ }
863
+ masked.fill(STRING, start, Math.min(++index, text.length));
864
+ };
865
+ const template = () => {
866
+ const start = index++;
867
+ while (index < text.length && text[index] !== "`") {
868
+ if (text[index] === "\\") {
869
+ index += 2;
870
+ } else if (text[index] === "$" && text[index + 1] === "{") {
871
+ masked.fill(TEMPLATE, start, index);
872
+ templates.push(depth);
873
+ index += 2;
874
+ return;
875
+ } else {
876
+ index++;
877
+ }
878
+ }
879
+ masked.fill(TEMPLATE, start, Math.min(++index, text.length));
880
+ };
881
+ while (index < text.length) {
882
+ const character = text[index];
883
+ if (character === "/" && text[index + 1] === "/") {
884
+ const end = text.indexOf("\n", index);
885
+ const stop = end < 0 ? text.length : end;
886
+ masked.fill(COMMENT, index, stop);
887
+ index = stop;
888
+ } else if (character === "/" && text[index + 1] === "*") {
889
+ const end = text.indexOf("*/", index + 2);
890
+ const stop = end < 0 ? text.length : end + 2;
891
+ masked.fill(COMMENT, index, stop);
892
+ index = stop;
893
+ } else if (character === "'" || character === '"') {
894
+ string(character);
895
+ } else if (character === "`") {
896
+ template();
897
+ } else if (character === "{") {
898
+ depth++;
899
+ index++;
900
+ } else if (character === "}") {
901
+ if (templates.length && templates[templates.length - 1] === depth) {
902
+ templates.pop();
903
+ template();
904
+ } else {
905
+ depth--;
906
+ index++;
907
+ }
908
+ } else {
909
+ index++;
910
+ }
911
+ }
912
+ return masked;
913
+ }
914
+ function maskHtml(text) {
915
+ const masked = new Uint8Array(text.length);
916
+ let index = 0;
917
+ while (index < text.length) {
918
+ if (text.startsWith("<!--", index)) {
919
+ const end = text.indexOf("-->", index + 4);
920
+ const stop = end < 0 ? text.length : end + 3;
921
+ masked.fill(COMMENT, index, stop);
922
+ index = stop;
923
+ } else if (text[index] === "<" && /[A-Za-z/]/.test(text[index + 1] ?? "")) {
924
+ index = maskTag(text, index + 1, masked);
925
+ } else {
926
+ index++;
927
+ }
928
+ }
929
+ return masked;
930
+ }
931
+ function maskTag(text, index, masked) {
932
+ while (index < text.length && text[index] !== ">") {
933
+ if (text[index] !== "=") {
934
+ index++;
935
+ continue;
936
+ }
937
+ index++;
938
+ while (text[index] === " " || text[index] === " " || text[index] === "\n") index++;
939
+ const quote = text[index];
940
+ const start = index;
941
+ if (quote === '"' || quote === "'") {
942
+ const end = text.indexOf(quote, index + 1);
943
+ index = end < 0 ? text.length : end + 1;
944
+ } else {
945
+ while (index < text.length && !/[\s>]/.test(text[index] ?? "")) index++;
946
+ }
947
+ masked.fill(STRING, start, index);
948
+ }
949
+ return index + 1;
950
+ }
951
+ function localBindings(text, masked) {
952
+ const names = /* @__PURE__ */ new Set();
953
+ for (const match of text.matchAll(/\b(?:const|let|var)\s+([\p{L}_$][\p{L}\w$]*)/gu)) {
954
+ if (!masked[match.index]) names.add(match[1] ?? "");
955
+ }
956
+ for (const match of text.matchAll(
957
+ /\bimport\s+(?:type\s+)?([^;]*?)\s+from\s+(['"])\.{1,2}\/[^'"]*\2/gu
958
+ )) {
959
+ if (masked[match.index]) continue;
960
+ for (const name of (match[1] ?? "").matchAll(
961
+ /([\p{L}_$][\p{L}\w$]*)(?:\s+as\s+([\p{L}_$][\p{L}\w$]*))?/gu
962
+ )) {
963
+ const local = name[2] ?? name[1] ?? "";
964
+ if (local !== "type" && local !== "as") names.add(local);
965
+ }
966
+ }
967
+ return names;
968
+ }
969
+ function isProse(text, masked) {
970
+ for (const match of text.matchAll(/[\p{L}_$][\p{L}\w$]*(?: [\p{L}_$][\p{L}\w$]*){2,}/gu)) {
971
+ const words = match[0].split(" ");
972
+ const plain = words.filter((word) => !SYNTAX_WORDS.has(word) && !RESERVED_WORDS.has(word));
973
+ if (!masked[match.index] && plain.length >= 3) {
974
+ return true;
975
+ }
976
+ }
977
+ return false;
978
+ }
979
+ function previousSignificant(text, index, masked) {
980
+ let position = index - 1;
981
+ while (position >= 0 && insignificant(text, position, masked)) position--;
982
+ return position;
983
+ }
984
+ function nextSignificant(text, index, masked) {
985
+ let position = index;
986
+ while (position < text.length && insignificant(text, position, masked)) position++;
987
+ return position;
988
+ }
989
+ function insignificant(text, index, masked) {
990
+ return masked[index] === COMMENT || /\s/.test(text[index] ?? "");
991
+ }
992
+ function previousWord(text, index) {
993
+ let start = index;
994
+ while (start >= 0 && WORD_CHARACTER.test(text[start] ?? "")) start--;
995
+ return text.slice(start + 1, index + 1);
996
+ }
997
+ 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;
998
+ var init_keyword_positions = __esm({
999
+ "libs/utils/html/plugins/keyword-positions.ts"() {
1000
+ "use strict";
1001
+ WORD_CHARACTER = /[*\p{L}\w$@-]/u;
1002
+ IDENTIFIER_BEFORE = /[\p{L}\w$@-]+$/u;
1003
+ WHOLE_KEYWORD = /^\s*[*\p{L}\w$@-]+(?:[.#][\p{L}\w-]+)?(?:\?[\w=&]+)?\s*$/u;
1004
+ CODE_PUNCTUATION = /[(){}[\]<>|:=@]/;
1005
+ DECLARATION_START = /^\s*(?:export|import|class|interface|type|const|let|var|function|enum|abstract|declare)\s/;
1006
+ HTML_START = /^\s*<\/?[A-Za-z]/;
1007
+ SYNTAX_WORDS = /* @__PURE__ */ new Set([
1008
+ "await",
1009
+ "break",
1010
+ "case",
1011
+ "catch",
1012
+ "continue",
1013
+ "debugger",
1014
+ "delete",
1015
+ "do",
1016
+ "else",
1017
+ "false",
1018
+ "finally",
1019
+ "for",
1020
+ "function",
1021
+ "if",
1022
+ "instanceof",
1023
+ "new",
1024
+ "null",
1025
+ "return",
1026
+ "super",
1027
+ "switch",
1028
+ "this",
1029
+ "throw",
1030
+ "true",
1031
+ "try",
1032
+ "typeof",
1033
+ "void",
1034
+ "while",
1035
+ "with",
1036
+ "yield"
1037
+ ]);
1038
+ RESERVED_WORDS = /* @__PURE__ */ new Set([
1039
+ "abstract",
1040
+ "accessor",
1041
+ "any",
1042
+ "as",
1043
+ "asserts",
1044
+ "async",
1045
+ "bigint",
1046
+ "boolean",
1047
+ "class",
1048
+ "const",
1049
+ "constructor",
1050
+ "declare",
1051
+ "default",
1052
+ "enum",
1053
+ "export",
1054
+ "extends",
1055
+ "from",
1056
+ "get",
1057
+ "implements",
1058
+ "import",
1059
+ "in",
1060
+ "infer",
1061
+ "interface",
1062
+ "is",
1063
+ "keyof",
1064
+ "let",
1065
+ "module",
1066
+ "namespace",
1067
+ "never",
1068
+ "number",
1069
+ "object",
1070
+ "of",
1071
+ "out",
1072
+ "override",
1073
+ "package",
1074
+ "private",
1075
+ "protected",
1076
+ "public",
1077
+ "readonly",
1078
+ "require",
1079
+ "satisfies",
1080
+ "set",
1081
+ "static",
1082
+ "string",
1083
+ "symbol",
1084
+ "type",
1085
+ "undefined",
1086
+ "unique",
1087
+ "unknown",
1088
+ "var"
1089
+ ]);
1090
+ TYPE_CONTEXT = [":", "|", "&", "<"];
1091
+ TYPE_WORDS = ["type", "extends", "keyof", "typeof"];
1092
+ CODE = 0;
1093
+ STRING = 1;
1094
+ COMMENT = 2;
1095
+ TEMPLATE = 3;
1096
+ CONTROL_FLOW = /^@[a-z]+$/;
1097
+ NOT_A_REFERENCE = Object.freeze({ link: false });
1098
+ REFERENCE = Object.freeze({ link: true });
1099
+ }
1100
+ });
1101
+
779
1102
  // libs/utils/html/plugins/keywords.plugin.ts
780
1103
  import { isElement as isElement5 } from "hast-util-is-element";
781
1104
  import { toString } from "hast-util-to-string";
@@ -787,23 +1110,50 @@ function keywordsPlugin(config) {
787
1110
  }
788
1111
  const isInlineCode = !isElement5(ancestors[ancestors.length - 1], "pre");
789
1112
  const lang = asArray(node.properties?.["className"] ?? []).find((className) => className.startsWith("language-"))?.replace("language-", "") ?? "";
790
- if (isInlineCode || LANGUAGES.includes(lang)) {
791
- visitParents(node, "text", (node2, ancestors2) => {
792
- if (hasLinkAncestor(ancestors2)) {
793
- return;
794
- }
795
- const parent = ancestors2[ancestors2.length - 1];
796
- const index = parent.children.indexOf(node2);
797
- const nodes = getNodes(node2, parent, isInlineCode, config, lang);
798
- Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
799
- return [SKIP, index + nodes.length];
800
- });
1113
+ if (!isInlineCode && !LANGUAGES.includes(lang)) {
1114
+ return;
801
1115
  }
1116
+ const offsets = textOffsets(node);
1117
+ const text = Array.from(offsets.keys()).map((textNode) => textNode.value).join("");
1118
+ const scan = isInlineCode ? scanInlineCode(text) : scanCodeBlock(text, HTML_LANGUAGES.includes(lang) ? "html" : "typescript");
1119
+ if (!scan) {
1120
+ return;
1121
+ }
1122
+ visitParents(node, "text", (node2, ancestors2) => {
1123
+ if (hasLinkAncestor(ancestors2)) {
1124
+ return;
1125
+ }
1126
+ const parent = ancestors2[ancestors2.length - 1];
1127
+ const index = parent.children.indexOf(node2);
1128
+ const nodes = getNodes(
1129
+ node2,
1130
+ parent,
1131
+ isInlineCode,
1132
+ config,
1133
+ lang,
1134
+ scan,
1135
+ offsets.get(node2) ?? 0
1136
+ );
1137
+ Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
1138
+ return [SKIP, index + nodes.length];
1139
+ });
1140
+ });
1141
+ }
1142
+ function textOffsets(node) {
1143
+ const offsets = /* @__PURE__ */ new Map();
1144
+ let offset = 0;
1145
+ visitParents(node, "text", (text) => {
1146
+ offsets.set(text, offset);
1147
+ offset += text.value.length;
802
1148
  });
1149
+ return offsets;
803
1150
  }
804
- function getNodes(node, parent, isInlineCode, config, language) {
1151
+ function getNodes(node, parent, isInlineCode, config, language, scan, offset) {
805
1152
  const { addUsedKeyword, getKeyword } = config;
1153
+ let start = offset;
806
1154
  return toString(node).split(SPLIT_REGEXP).map((word) => {
1155
+ const wordStart = start;
1156
+ start += word.length;
807
1157
  const match = word.match(MATCH_KEYWORD_REGEXP);
808
1158
  if (!match) {
809
1159
  return { type: "text", value: word };
@@ -814,8 +1164,13 @@ function getNodes(node, parent, isInlineCode, config, language) {
814
1164
  anchor = "",
815
1165
  queryParams = ""
816
1166
  } = match.groups;
817
- const usedKeyword = `${key}${delimiter}${anchor?.toLowerCase()}`;
818
- const rootKeyword = getKeyword?.(key);
1167
+ const position = scan.position(wordStart, wordStart + word.length, key);
1168
+ if (!position.link) {
1169
+ return { type: "text", value: word };
1170
+ }
1171
+ const isMember = position.keyword !== void 0;
1172
+ const usedKeyword = position.keyword ?? `${key}${delimiter}${anchor?.toLowerCase()}`;
1173
+ const rootKeyword = isMember ? void 0 : getKeyword?.(key);
819
1174
  const keyword = getKeyword?.(usedKeyword);
820
1175
  const isGuideKeyword = key.startsWith("*");
821
1176
  const isLanguageAllowed = !keyword?.languages && ALWAYS_ALLOWED_LANGUAGES.includes(language) || !!keyword?.languages?.includes(language);
@@ -828,7 +1183,7 @@ function getNodes(node, parent, isInlineCode, config, language) {
828
1183
  addUsedKeyword?.(usedKeyword);
829
1184
  const notFoundGuideKeyword = isGuideKeyword && !keyword;
830
1185
  const notFoundApiAnchorKeyword = !!rootKeyword && !!anchor && !keyword;
831
- if (getKeyword && isInlineCode && (notFoundGuideKeyword || notFoundApiAnchorKeyword)) {
1186
+ if (getKeyword && scan.whole && (notFoundGuideKeyword || notFoundApiAnchorKeyword)) {
832
1187
  throw new Error(`Route with keyword "${word}" is missing.`);
833
1188
  }
834
1189
  if (parent.properties) {
@@ -844,7 +1199,7 @@ function getNodes(node, parent, isInlineCode, config, language) {
844
1199
  }
845
1200
  }
846
1201
  return keyword ? createLinkNode(
847
- isInlineCode ? keyword.title : word,
1202
+ isInlineCode && !isMember ? keyword.title : word,
848
1203
  keyword.path,
849
1204
  keyword.type,
850
1205
  keyword.description
@@ -864,14 +1219,16 @@ function createLinkNode(text, href, type, description) {
864
1219
  children: [{ type: "text", value: text }]
865
1220
  };
866
1221
  }
867
- var ALWAYS_ALLOWED_LANGUAGES, LANGUAGES, SPLIT_REGEXP, MATCH_KEYWORD_REGEXP;
1222
+ var ALWAYS_ALLOWED_LANGUAGES, HTML_LANGUAGES, LANGUAGES, SPLIT_REGEXP, MATCH_KEYWORD_REGEXP;
868
1223
  var init_keywords_plugin = __esm({
869
1224
  "libs/utils/html/plugins/keywords.plugin.ts"() {
870
1225
  "use strict";
871
1226
  init_core();
872
1227
  init_helpers2();
1228
+ init_keyword_positions();
873
1229
  ALWAYS_ALLOWED_LANGUAGES = ["typescript", "ts", "angular-ts"];
874
- LANGUAGES = ["typescript", "ts", "angular-ts", ...KEYWORD_ALLOWED_LANGUAGES];
1230
+ HTML_LANGUAGES = [...KEYWORD_ALLOWED_LANGUAGES];
1231
+ LANGUAGES = [...ALWAYS_ALLOWED_LANGUAGES, ...HTML_LANGUAGES];
875
1232
  SPLIT_REGEXP = /([*\p{L}\w$@-]+(?:[.#][\p{L}\w-]+)?(?:\?[\w=&]+)?)/u;
876
1233
  MATCH_KEYWORD_REGEXP = /(?<key>[*\p{L}\w$@-]+)((?<delimiter>[.#])(?<anchor>[\p{L}\w-]+))?(?<queryParams>\?[\w=&]+)?/u;
877
1234
  }
@@ -929,13 +1286,14 @@ var init_add_heading_anchors_plugin = __esm({
929
1286
  });
930
1287
 
931
1288
  // libs/utils/html/plugins/cached-shiki.plugin.ts
932
- import rehypeShiki from "@shikijs/rehype";
933
1289
  import { toString as toString2 } from "hast-util-to-string";
934
1290
  import { visit as visit2 } from "unist-util-visit";
935
1291
  function cachedShikiPlugin(settings) {
936
- const { cache, themes, identity, defaultLanguage } = settings;
1292
+ const { cache, themes, languages, identity, defaultLanguage } = settings;
937
1293
  const id = `${themes.light}
938
- ${themes.dark}`;
1294
+ ${themes.dark}
1295
+ ${languages ?? ""}
1296
+ ${settings.grammars ?? "all"}`;
939
1297
  return async (tree) => {
940
1298
  const blocks = [];
941
1299
  visit2(tree, "element", (node, index, parent) => {
@@ -952,11 +1310,18 @@ ${themes.dark}`;
952
1310
  blocks.push({
953
1311
  node,
954
1312
  parent,
955
- key: cache.key({ options: identity, themes, lang, meta, code })
1313
+ key: cache.key({
1314
+ options: identity,
1315
+ themes,
1316
+ ...languages === void 0 ? {} : { languages },
1317
+ lang,
1318
+ meta,
1319
+ code
1320
+ })
956
1321
  });
957
1322
  });
958
1323
  let highlighter;
959
- const load = async () => highlighter ??= await highlighterFor(id, settings.options);
1324
+ const load = async () => highlighter ??= await highlighterFor(id, settings.transformer);
960
1325
  if (cache.loads?.(themes) !== true) await load();
961
1326
  const values = [];
962
1327
  for (const block of blocks) {
@@ -977,13 +1342,11 @@ ${themes.dark}`;
977
1342
  });
978
1343
  };
979
1344
  }
980
- function highlighterFor(id, options) {
1345
+ function highlighterFor(id, transformer) {
981
1346
  const known = highlighters.get(id);
982
1347
  if (known) return known;
983
1348
  const created = (async () => {
984
- const transform = rehypeShiki(
985
- options()
986
- );
1349
+ const transform = transformer();
987
1350
  await transform({ type: "root", children: [] });
988
1351
  return transform;
989
1352
  })();
@@ -1001,11 +1364,18 @@ async function highlight(transform, node) {
1001
1364
  function fragment(value) {
1002
1365
  try {
1003
1366
  const parsed = JSON.parse(value);
1004
- return Array.isArray(parsed) && parsed.length > 0 && parsed.every((item) => isNode(item) && item.type === "element") ? parsed : void 0;
1367
+ return Array.isArray(parsed) && parsed.length > 0 && parsed.every(isReplacement) ? parsed : void 0;
1005
1368
  } catch {
1006
1369
  return void 0;
1007
1370
  }
1008
1371
  }
1372
+ function isReplacement(value) {
1373
+ if (isNode(value)) return value.type === "element";
1374
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1375
+ const node = value;
1376
+ const children = node["children"];
1377
+ return node["type"] === "root" && Array.isArray(children) && children.length > 0 && children.every((child) => isNode(child) && child.type === "element");
1378
+ }
1009
1379
  function isNode(value) {
1010
1380
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1011
1381
  const node = value;
@@ -1198,11 +1568,158 @@ var init_table_wrapper = __esm({
1198
1568
  }
1199
1569
  });
1200
1570
 
1571
+ // libs/utils/html/shiki-grammars.ts
1572
+ import {
1573
+ bundledLanguages,
1574
+ createHighlighter
1575
+ } from "shiki";
1576
+ import { visit as visit7 } from "unist-util-visit";
1577
+ function shikiGrammars() {
1578
+ grammars ??= ShikiGrammars.create().catch((error) => {
1579
+ grammars = void 0;
1580
+ throw error;
1581
+ });
1582
+ return grammars;
1583
+ }
1584
+ function createGrammarlessHighlighter(themes) {
1585
+ return createHighlighter({ themes, langs: [] });
1586
+ }
1587
+ function blockLanguages(tree, defaultLanguage) {
1588
+ const languages = /* @__PURE__ */ new Set();
1589
+ visit7(tree, "element", (node) => {
1590
+ if (node.tagName !== "pre") return;
1591
+ const head = node.children[0];
1592
+ if (!head || head.type !== "element" || head.tagName !== "code" || !head.properties) return;
1593
+ const classes = head.properties["className"];
1594
+ const languageClass = Array.isArray(classes) ? classes.find((item) => typeof item === "string" && item.startsWith(languagePrefix2)) : void 0;
1595
+ languages.add(
1596
+ typeof languageClass === "string" ? languageClass.slice(languagePrefix2.length) : defaultLanguage
1597
+ );
1598
+ });
1599
+ return languages;
1600
+ }
1601
+ function includedScopes(registration) {
1602
+ const scopes = /* @__PURE__ */ new Set();
1603
+ const walk = (value) => {
1604
+ if (Array.isArray(value)) value.forEach(walk);
1605
+ else if (value && typeof value === "object")
1606
+ for (const [key, item] of Object.entries(value)) {
1607
+ if (key === "include" && typeof item === "string") {
1608
+ if (!item.startsWith("#") && !item.startsWith("$")) scopes.add(item.split("#")[0]);
1609
+ } else walk(item);
1610
+ }
1611
+ };
1612
+ walk(registration);
1613
+ return scopes;
1614
+ }
1615
+ function prefixes(scope) {
1616
+ const parts = scope.split(".");
1617
+ return parts.map((_, index) => parts.slice(0, index + 1).join("."));
1618
+ }
1619
+ var ShikiGrammars, grammars, languagePrefix2;
1620
+ var init_shiki_grammars = __esm({
1621
+ "libs/utils/html/shiki-grammars.ts"() {
1622
+ "use strict";
1623
+ ShikiGrammars = class _ShikiGrammars {
1624
+ constructor(registrations) {
1625
+ this.byName = /* @__PURE__ */ new Map();
1626
+ this.byScope = /* @__PURE__ */ new Map();
1627
+ this.injectors = /* @__PURE__ */ new Map();
1628
+ this.order = /* @__PURE__ */ new Map();
1629
+ this.closures = /* @__PURE__ */ new Map();
1630
+ this.loaded = /* @__PURE__ */ new WeakMap();
1631
+ const mention = (name) => {
1632
+ if (!this.order.has(name)) this.order.set(name, this.order.size);
1633
+ };
1634
+ for (const registration of registrations) {
1635
+ mention(registration.name);
1636
+ for (const embedded of registration.embeddedLangs ?? []) mention(embedded);
1637
+ if (this.byScope.has(registration.scopeName)) continue;
1638
+ this.byScope.set(registration.scopeName, registration);
1639
+ this.byName.set(registration.name, registration);
1640
+ for (const alias of registration.aliases ?? []) this.byName.set(alias, registration);
1641
+ for (const target of registration.injectTo ?? []) {
1642
+ const list = this.injectors.get(target) ?? [];
1643
+ list.push(registration);
1644
+ this.injectors.set(target, list);
1645
+ }
1646
+ }
1647
+ }
1648
+ /**
1649
+ * The bundled grammars, in the order `@shikijs/rehype` registers them: every bundled language
1650
+ * name, each with the registrations of its module.
1651
+ */
1652
+ static async create() {
1653
+ const modules = await Promise.all(
1654
+ Object.keys(bundledLanguages).map(
1655
+ (name) => bundledLanguages[name]()
1656
+ )
1657
+ );
1658
+ return new _ShikiGrammars(modules.flatMap((module) => module.default));
1659
+ }
1660
+ /**
1661
+ * The grammars that a block of `language` can reach, in registration order; empty for a
1662
+ * language that no bundled grammar names (it falls back to plain text, as before).
1663
+ * @param language
1664
+ */
1665
+ closure(language) {
1666
+ const known = this.closures.get(language);
1667
+ if (known) return known;
1668
+ const set = /* @__PURE__ */ new Set();
1669
+ const queue = [this.byName.get(language)];
1670
+ while (queue.length) {
1671
+ const registration = queue.pop();
1672
+ if (!registration || set.has(registration)) continue;
1673
+ set.add(registration);
1674
+ for (const name of [
1675
+ ...registration.embeddedLangs ?? [],
1676
+ ...registration.embeddedLangsLazy ?? []
1677
+ ])
1678
+ queue.push(this.byName.get(name));
1679
+ for (const scope of includedScopes(registration)) queue.push(this.byScope.get(scope));
1680
+ for (const prefix of prefixes(registration.scopeName))
1681
+ queue.push(...this.injectors.get(prefix) ?? []);
1682
+ for (const target of registration.injectTo ?? [])
1683
+ queue.push(...this.injectors.get(target) ?? []);
1684
+ }
1685
+ const closure = [...set].sort(
1686
+ (left, right) => this.order.get(left.name) - this.order.get(right.name)
1687
+ );
1688
+ this.closures.set(language, closure);
1689
+ return closure;
1690
+ }
1691
+ /**
1692
+ * Loads the closures of `languages` that `highlighter` does not hold yet.
1693
+ * @param highlighter
1694
+ * @param languages
1695
+ */
1696
+ load(highlighter, languages) {
1697
+ let loaded = this.loaded.get(highlighter);
1698
+ if (!loaded) this.loaded.set(highlighter, loaded = /* @__PURE__ */ new Set());
1699
+ const missing = /* @__PURE__ */ new Set();
1700
+ for (const language of languages)
1701
+ for (const registration of this.closure(language))
1702
+ if (!loaded.has(registration.name)) missing.add(registration);
1703
+ if (!missing.size) return;
1704
+ const batch = [...missing].sort(
1705
+ (left, right) => this.order.get(left.name) - this.order.get(right.name)
1706
+ );
1707
+ highlighter.loadLanguageSync(batch);
1708
+ for (const registration of batch) loaded.add(registration.name);
1709
+ }
1710
+ };
1711
+ languagePrefix2 = "language-";
1712
+ }
1713
+ });
1714
+
1201
1715
  // libs/utils/html/process-html.ts
1202
- import rehypeShiki2 from "@shikijs/rehype";
1716
+ import rehypeShiki from "@shikijs/rehype";
1717
+ import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
1718
+ import { createHash as createHash2 } from "node:crypto";
1203
1719
  import rehypeMinifyWhitespace2 from "rehype-minify-whitespace";
1204
1720
  import rehypeParse3 from "rehype-parse";
1205
1721
  import rehypeStringify3 from "rehype-stringify";
1722
+ import { bundledLanguages as bundledLanguages2, createHighlighter as createHighlighter2 } from "shiki";
1206
1723
  import { unified as unified3 } from "unified";
1207
1724
  async function processHtml(html, config) {
1208
1725
  const anchors = /* @__PURE__ */ new Set();
@@ -1210,6 +1727,8 @@ async function processHtml(html, config) {
1210
1727
  light: config.lightTheme ?? "github-light",
1211
1728
  dark: config.darkTheme ?? "ayu-dark"
1212
1729
  };
1730
+ const langs = config.langs?.length ? config.langs : void 0;
1731
+ const used = !langs && config.grammars === "used";
1213
1732
  try {
1214
1733
  const content = await unified3().use(rehypeParse3, { fragment: true }).use(rehypeStringify3).use(mermaidPlugin).use(
1215
1734
  config.highlight ? [
@@ -1218,12 +1737,16 @@ async function processHtml(html, config) {
1218
1737
  {
1219
1738
  cache: config.highlight,
1220
1739
  themes,
1740
+ ...langs ? { languages: languagesId(langs) } : {},
1741
+ ...used ? { grammars: "used" } : {},
1221
1742
  identity: HIGHLIGHT_IDENTITY,
1222
1743
  defaultLanguage: DEFAULT_LANGUAGE,
1223
- options: () => shikiOptions(themes)
1744
+ transformer: () => langs ? withLanguages(themes, langs) : used ? withUsedGrammars(themes) : rehypeShiki(
1745
+ shikiOptions(themes)
1746
+ )
1224
1747
  }
1225
1748
  ]
1226
- ] : [[rehypeShiki2, shikiOptions(themes)]]
1749
+ ] : langs ? [[() => withLanguages(themes, langs)]] : used ? [[() => withUsedGrammars(themes)]] : [[rehypeShiki, shikiOptions(themes)]]
1227
1750
  ).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());
1228
1751
  return { content, anchors: Array.from(anchors) };
1229
1752
  } catch (error) {
@@ -1249,7 +1772,59 @@ function shikiOptions(themes) {
1249
1772
  function shikiTheme(name) {
1250
1773
  return name === NG_DOC_SYNTAX_THEME_NAME ? ngDocSyntaxTheme() : name;
1251
1774
  }
1252
- var DEFAULT_LANGUAGE, HIGHLIGHT_IDENTITY;
1775
+ function languagesId(langs) {
1776
+ let id = languageIds.get(langs);
1777
+ if (id === void 0) {
1778
+ id = createHash2("sha256").update(JSON.stringify(langs)).digest("hex");
1779
+ languageIds.set(langs, id);
1780
+ }
1781
+ return id;
1782
+ }
1783
+ function withLanguages(themes, langs) {
1784
+ const id = `${themes.light}
1785
+ ${themes.dark}
1786
+ ${languagesId(langs)}`;
1787
+ return async (tree) => {
1788
+ let highlighter = languageHighlighters.get(id);
1789
+ if (!highlighter) {
1790
+ highlighter = createHighlighter2({
1791
+ themes: [shikiTheme(themes.light), shikiTheme(themes.dark)],
1792
+ // Later registrations of a name replace earlier ones, so the given languages win.
1793
+ langs: [...Object.keys(bundledLanguages2), ...langs]
1794
+ });
1795
+ languageHighlighters.set(id, highlighter);
1796
+ const created = highlighter;
1797
+ created.catch(() => {
1798
+ if (languageHighlighters.get(id) === created) languageHighlighters.delete(id);
1799
+ });
1800
+ }
1801
+ const transform = rehypeShikiFromHighlighter(await highlighter, shikiOptions(themes));
1802
+ await transform(tree);
1803
+ };
1804
+ }
1805
+ function withUsedGrammars(themes) {
1806
+ const id = `${themes.light}
1807
+ ${themes.dark}`;
1808
+ return async (tree) => {
1809
+ let highlighter = usedHighlighters.get(id);
1810
+ if (!highlighter) {
1811
+ highlighter = createGrammarlessHighlighter([
1812
+ shikiTheme(themes.light),
1813
+ shikiTheme(themes.dark)
1814
+ ]);
1815
+ usedHighlighters.set(id, highlighter);
1816
+ const created = highlighter;
1817
+ created.catch(() => {
1818
+ if (usedHighlighters.get(id) === created) usedHighlighters.delete(id);
1819
+ });
1820
+ }
1821
+ const [grammars2, ready] = await Promise.all([shikiGrammars(), highlighter]);
1822
+ grammars2.load(ready, blockLanguages(tree, DEFAULT_LANGUAGE));
1823
+ const transform = rehypeShikiFromHighlighter(ready, shikiOptions(themes));
1824
+ await transform(tree);
1825
+ };
1826
+ }
1827
+ var DEFAULT_LANGUAGE, HIGHLIGHT_IDENTITY, languageHighlighters, languageIds, usedHighlighters;
1253
1828
  var init_process_html = __esm({
1254
1829
  "libs/utils/html/process-html.ts"() {
1255
1830
  "use strict";
@@ -1261,24 +1836,28 @@ var init_process_html = __esm({
1261
1836
  init_mermaid_plugin();
1262
1837
  init_slugger_plugin();
1263
1838
  init_table_wrapper();
1839
+ init_shiki_grammars();
1264
1840
  DEFAULT_LANGUAGE = "ts";
1265
1841
  HIGHLIGHT_IDENTITY = {
1266
- format: 1,
1842
+ format: 2,
1267
1843
  defaultLanguage: DEFAULT_LANGUAGE,
1268
1844
  fallbackLanguage: "text",
1269
1845
  addLanguageClass: true,
1270
1846
  parseMetaString: "json-without-backslashes",
1271
1847
  tokenizeTimeLimit: 0
1272
1848
  };
1849
+ languageHighlighters = /* @__PURE__ */ new Map();
1850
+ languageIds = /* @__PURE__ */ new WeakMap();
1851
+ usedHighlighters = /* @__PURE__ */ new Map();
1273
1852
  }
1274
1853
  });
1275
1854
 
1276
1855
  // libs/utils/html/plugins/remove-not-indexable-content.plugin.ts
1277
1856
  import { filter } from "unist-util-filter";
1278
- import { visit as visit7 } from "unist-util-visit";
1857
+ import { visit as visit8 } from "unist-util-visit";
1279
1858
  function removeNotIndexableContentPlugin() {
1280
1859
  return (tree) => {
1281
- visit7(tree, "element", (node) => {
1860
+ visit8(tree, "element", (node) => {
1282
1861
  if (isHeading(node) && isHiddenSpan(node.children[0])) {
1283
1862
  node.children = node.children.slice(1);
1284
1863
  }
@@ -1364,13 +1943,13 @@ import { parseFile } from "@orama/plugin-parsedoc";
1364
1943
  async function renderDocuments(task, highlight2, aborted = notAborted) {
1365
1944
  const results = [];
1366
1945
  for (const document of task.documents) {
1367
- const result = await renderDocument(document, task.themes, highlight2, aborted);
1946
+ const result = await renderDocument(document, task, highlight2, aborted);
1368
1947
  results.push(result);
1369
1948
  if (!("html" in result)) break;
1370
1949
  }
1371
1950
  return results;
1372
1951
  }
1373
- async function renderDocument(document, themes, highlight2, aborted) {
1952
+ async function renderDocument(document, { themes, langs, grammars: grammars2 }, highlight2, aborted) {
1374
1953
  if (aborted()) return { aborted: true, mismatches: 0 };
1375
1954
  const utilities = await htmlUtilities();
1376
1955
  if (aborted()) return { aborted: true, mismatches: 0 };
@@ -1380,6 +1959,8 @@ async function renderDocument(document, themes, highlight2, aborted) {
1380
1959
  ...document.route === void 0 ? {} : { route: document.route },
1381
1960
  lightTheme: themes.light,
1382
1961
  darkTheme: themes.dark,
1962
+ ...langs ? { langs } : {},
1963
+ grammars: grammars2 ?? "used",
1383
1964
  ...call ? { highlight: call } : {}
1384
1965
  });
1385
1966
  const mismatches = call?.mismatches.length ?? 0;
@@ -1554,7 +2135,9 @@ function serveHtmlThread(port) {
1554
2135
  await renderDocuments(
1555
2136
  {
1556
2137
  documents: [{ html: WARM_DOCUMENT }],
1557
- themes: job["themes"]
2138
+ themes: job["themes"],
2139
+ ...job["langs"] ? { langs: job["langs"] } : {},
2140
+ ...job["grammars"] === "all" ? { grammars: "all" } : {}
1558
2141
  },
1559
2142
  () => job["cache"] === true ? cache : void 0
1560
2143
  );
@@ -1583,7 +2166,7 @@ var init_html_pipeline = __esm({
1583
2166
  init_protocol();
1584
2167
  htmlUtilities = () => Promise.resolve().then(() => (init_utils(), utils_exports));
1585
2168
  notAborted = () => false;
1586
- WARM_DOCUMENT = '<pre><code class="language-ts">const warm = 1;</code></pre>';
2169
+ WARM_DOCUMENT = ["ts", "angular-ts", "angular-html"].map((language) => `<pre><code class="language-${language}">warm</code></pre>`).join("");
1587
2170
  }
1588
2171
  });
1589
2172