xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -0,0 +1,376 @@
1
+ /**
2
+ * Static usage analysis for optional link features.
3
+ *
4
+ * The bundled AST is the last place where the compiler can see *all* of a
5
+ * program: imported modules and `.html` assets are already merged into one
6
+ * `SourceFile`, and asset loaders have inlined external stylesheets. This module
7
+ * extracts the facts a {@link LinkFeature} needs to decide whether it may be
8
+ * used, without pretending to be a full data-flow analysis:
9
+ *
10
+ * - every string literal / template chunk (HTML tags, CSS property values),
11
+ * - identifiers and property names (`Audio`, `decodeAudioData`, …),
12
+ * - every `getContext(...)` kind that can be resolved statically,
13
+ * - whether HTML/CSS can be built from data the compiler cannot see.
14
+ *
15
+ * Constants are resolved through a small, name-based table (`const` only,
16
+ * invalidated by shadowing or reassignment). Anything that cannot be resolved is
17
+ * treated as dynamic, which makes features conservative: a false positive costs
18
+ * size, a false negative could disable a capability at run time.
19
+ */
20
+
21
+ import {
22
+ SyntaxKind,
23
+ type BinaryExpression,
24
+ type CallExpression,
25
+ type Expression,
26
+ type Identifier,
27
+ type NoSubstitutionTemplateLiteral,
28
+ type Parameter,
29
+ type PrefixUnaryExpression,
30
+ type PrivateIdentifier,
31
+ type PropertyAccessExpression,
32
+ type SourceFileNode,
33
+ type StringLiteral,
34
+ type TemplateLiteral,
35
+ type VariableStatement,
36
+ type ElementAccessExpression,
37
+ type AssignmentExpression,
38
+ type PostfixUnaryExpression,
39
+ type NumericLiteral,
40
+ type BigIntLiteral,
41
+ type BooleanLiteral,
42
+ type Node,
43
+ } from "../ast/nodes.js";
44
+ import { walk } from "../ast/visitor.js";
45
+ import type { ProgramUsage } from "../extensions/link-feature.js";
46
+
47
+ /** Operators that make a `BinaryExpression`-shaped node an assignment. */
48
+ const ASSIGNMENT_OPERATORS = new Set<string>([
49
+ "=",
50
+ "+=",
51
+ "-=",
52
+ "*=",
53
+ "/=",
54
+ "%=",
55
+ "**=",
56
+ "<<=",
57
+ ">>=",
58
+ ">>>=",
59
+ "&=",
60
+ "|=",
61
+ "^=",
62
+ "&&=",
63
+ "||=",
64
+ "??=",
65
+ ]);
66
+
67
+ /** Property names whose assigned value is parsed as markup or CSS. */
68
+ const MARKUP_PROPERTIES = new Set(["innerHTML", "outerHTML", "cssText"]);
69
+
70
+ /** A call site with a statically known method/function name. */
71
+ interface CallSite {
72
+ readonly name: string;
73
+ readonly arguments: readonly Expression[];
74
+ }
75
+
76
+ /** An assignment whose left side is a statically known property name. */
77
+ interface PropertyAssignmentSite {
78
+ readonly name: string;
79
+ readonly right: Expression;
80
+ }
81
+
82
+ /** A wrapper expression (`as`, `satisfies`, `!`, parentheses) around a value. */
83
+ interface WrappedExpression {
84
+ readonly expression: Expression;
85
+ }
86
+
87
+ /**
88
+ * The property/method name of `a.b` or `a["b"]`, or the identifier of a bare
89
+ * call (`loadHTML(...)`). Returns undefined for a computed access.
90
+ */
91
+ function memberName(expression: Expression): string | undefined {
92
+ if (expression.kind === SyntaxKind.PropertyAccessExpression) {
93
+ return (expression as PropertyAccessExpression).name.text;
94
+ }
95
+ if (expression.kind === SyntaxKind.ElementAccessExpression) {
96
+ const argument = (expression as ElementAccessExpression).argumentExpression;
97
+ if (argument.kind === SyntaxKind.StringLiteral) return (argument as StringLiteral).value;
98
+ return undefined;
99
+ }
100
+ if (expression.kind === SyntaxKind.Identifier) return (expression as Identifier).text;
101
+ return undefined;
102
+ }
103
+
104
+ /** The assignment-shaped view of a `BinaryExpression`, when it is one. */
105
+ function asAssignment(node: Node): AssignmentExpression | undefined {
106
+ if (node.kind !== SyntaxKind.BinaryExpression) return undefined;
107
+ const operator = (node as BinaryExpression).operator;
108
+ return ASSIGNMENT_OPERATORS.has(String(operator)) ? (node as unknown as AssignmentExpression) : undefined;
109
+ }
110
+
111
+ /** True for literals that stringify to text which can never contain markup. */
112
+ function isPrimitiveLiteral(expression: Expression): boolean {
113
+ switch (expression.kind) {
114
+ case SyntaxKind.NumericLiteral:
115
+ case SyntaxKind.BigIntLiteral:
116
+ case SyntaxKind.TrueKeyword:
117
+ case SyntaxKind.FalseKeyword:
118
+ case SyntaxKind.NullKeyword:
119
+ case SyntaxKind.UndefinedKeyword:
120
+ return true;
121
+ default:
122
+ return false;
123
+ }
124
+ }
125
+
126
+ /** Convert a primitive literal to the text it contributes to a template. */
127
+ function primitiveText(expression: Expression): string | undefined {
128
+ switch (expression.kind) {
129
+ case SyntaxKind.NumericLiteral:
130
+ return String((expression as NumericLiteral).value);
131
+ case SyntaxKind.BigIntLiteral:
132
+ return (expression as BigIntLiteral).value.toString();
133
+ case SyntaxKind.TrueKeyword:
134
+ case SyntaxKind.FalseKeyword:
135
+ return (expression as BooleanLiteral).value ? "true" : "false";
136
+ case SyntaxKind.NullKeyword:
137
+ return "null";
138
+ case SyntaxKind.UndefinedKeyword:
139
+ return "undefined";
140
+ default:
141
+ return undefined;
142
+ }
143
+ }
144
+
145
+ /** Collect the usage facts of one bundled program. */
146
+ export function collectUsage(sourceFile: SourceFileNode): ProgramUsage {
147
+ const strings = new Set<string>();
148
+ const identifiers = new Set<string>();
149
+ const constants = new Map<string, Expression>();
150
+ const ambiguous = new Set<string>();
151
+ const assigned = new Set<string>();
152
+ const calls: CallSite[] = [];
153
+ const assignments: PropertyAssignmentSite[] = [];
154
+ let createsStyleElement = false;
155
+ let unresolvedTextContent = false;
156
+
157
+ const forget = (name: string): void => {
158
+ if (constants.delete(name)) ambiguous.add(name);
159
+ };
160
+
161
+ walk(sourceFile, (node) => {
162
+ switch (node.kind) {
163
+ case SyntaxKind.StringLiteral:
164
+ strings.add((node as StringLiteral).value);
165
+ break;
166
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
167
+ strings.add((node as NoSubstitutionTemplateLiteral).value);
168
+ break;
169
+ case SyntaxKind.TemplateLiteral: {
170
+ const template = node as TemplateLiteral;
171
+ strings.add(template.head);
172
+ for (const span of template.spans) strings.add(span.literal);
173
+ break;
174
+ }
175
+ case SyntaxKind.Identifier:
176
+ identifiers.add((node as Identifier).text);
177
+ break;
178
+ case SyntaxKind.PrivateIdentifier:
179
+ identifiers.add((node as PrivateIdentifier).text);
180
+ break;
181
+ case SyntaxKind.VariableStatement: {
182
+ const statement = node as VariableStatement;
183
+ if (statement.declarationList.declarationKind !== "const") break;
184
+ for (const declaration of statement.declarationList.declarations) {
185
+ if (declaration.name.kind !== SyntaxKind.Identifier || declaration.initializer === undefined) continue;
186
+ const name = declaration.name.text;
187
+ /* A second declaration of the same name makes the initializer
188
+ * ambiguous: shadowing is not tracked, so refuse to resolve it. */
189
+ if (constants.has(name) || ambiguous.has(name)) {
190
+ forget(name);
191
+ continue;
192
+ }
193
+ constants.set(name, declaration.initializer);
194
+ }
195
+ break;
196
+ }
197
+ case SyntaxKind.Parameter: {
198
+ /* A parameter shadows any const of the same name; resolution only looks
199
+ * at the name, so mark it ambiguous. */
200
+ const parameter = node as Parameter;
201
+ if (parameter.name.kind === SyntaxKind.Identifier) ambiguous.add(parameter.name.text);
202
+ break;
203
+ }
204
+ case SyntaxKind.PrefixUnaryExpression: {
205
+ const unary = node as PrefixUnaryExpression;
206
+ if (unary.operator === "++" || unary.operator === "--") {
207
+ if (unary.operand.kind === SyntaxKind.Identifier) assigned.add(unary.operand.text);
208
+ }
209
+ break;
210
+ }
211
+ case SyntaxKind.PostfixUnaryExpression: {
212
+ const unary = node as PostfixUnaryExpression;
213
+ if (unary.operand.kind === SyntaxKind.Identifier) assigned.add(unary.operand.text);
214
+ break;
215
+ }
216
+ case SyntaxKind.BinaryExpression: {
217
+ const assignment = asAssignment(node);
218
+ if (assignment === undefined) break;
219
+ if (assignment.left.kind === SyntaxKind.Identifier) assigned.add(assignment.left.text);
220
+ const name = memberName(assignment.left);
221
+ if (name !== undefined) assignments.push({ name, right: assignment.right });
222
+ break;
223
+ }
224
+ case SyntaxKind.CallExpression: {
225
+ const call = node as CallExpression;
226
+ const name = memberName(call.expression);
227
+ if (name !== undefined) calls.push({ name, arguments: call.arguments });
228
+ break;
229
+ }
230
+ default:
231
+ break;
232
+ }
233
+ });
234
+
235
+ const resolve = (expression: Expression, stack: Set<string> = new Set()): string | undefined => {
236
+ const primitive = primitiveText(expression);
237
+ if (primitive !== undefined) return primitive;
238
+ switch (expression.kind) {
239
+ case SyntaxKind.StringLiteral:
240
+ return (expression as StringLiteral).value;
241
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
242
+ return (expression as NoSubstitutionTemplateLiteral).value;
243
+ case SyntaxKind.TemplateLiteral: {
244
+ const template = expression as TemplateLiteral;
245
+ let text = template.head;
246
+ for (const span of template.spans) {
247
+ const value = resolve(span.expression, stack);
248
+ if (value === undefined) {
249
+ if (!isPrimitiveLiteral(span.expression)) return undefined;
250
+ } else {
251
+ text += value;
252
+ }
253
+ text += span.literal;
254
+ }
255
+ return text;
256
+ }
257
+ case SyntaxKind.Identifier: {
258
+ const name = (expression as Identifier).text;
259
+ if (assigned.has(name) || ambiguous.has(name)) return undefined;
260
+ const initializer = constants.get(name);
261
+ if (initializer === undefined || stack.has(name)) return undefined;
262
+ stack.add(name);
263
+ const value = resolve(initializer, stack);
264
+ stack.delete(name);
265
+ return value;
266
+ }
267
+ case SyntaxKind.ParenthesizedExpression:
268
+ case SyntaxKind.AsExpression:
269
+ case SyntaxKind.SatisfiesExpression:
270
+ case SyntaxKind.NonNullExpression: {
271
+ return resolve((expression as unknown as WrappedExpression).expression, stack);
272
+ }
273
+ case SyntaxKind.BinaryExpression: {
274
+ const binary = expression as BinaryExpression;
275
+ if (ASSIGNMENT_OPERATORS.has(String(binary.operator)) || binary.operator !== "+") return undefined;
276
+ const left = resolve(binary.left, stack);
277
+ const right = resolve(binary.right, stack);
278
+ return left === undefined || right === undefined ? undefined : left + right;
279
+ }
280
+ default:
281
+ return undefined;
282
+ }
283
+ };
284
+
285
+ let dynamicMarkup = false;
286
+ const getContextKinds: (string | undefined)[] = [];
287
+
288
+ const sink = (expression: Expression | undefined): void => {
289
+ if (expression === undefined) {
290
+ dynamicMarkup = true;
291
+ return;
292
+ }
293
+ const text = resolve(expression);
294
+ if (text === undefined) dynamicMarkup = true;
295
+ else strings.add(text);
296
+ };
297
+
298
+ const sinkAll = (expressions: readonly Expression[]): void => {
299
+ for (const expression of expressions) {
300
+ const text = resolve(expression);
301
+ if (text === undefined) dynamicMarkup = true;
302
+ else strings.add(text);
303
+ }
304
+ };
305
+
306
+ for (const site of calls) {
307
+ switch (site.name) {
308
+ case "getContext": {
309
+ const kind = site.arguments[0] === undefined ? undefined : resolve(site.arguments[0]);
310
+ getContextKinds.push(kind === undefined ? undefined : kind.toLowerCase());
311
+ break;
312
+ }
313
+ case "createElement": {
314
+ const tag = site.arguments[0] === undefined ? undefined : resolve(site.arguments[0]);
315
+ if (tag === undefined) dynamicMarkup = true;
316
+ else if (tag.toLowerCase() === "style") createsStyleElement = true;
317
+ break;
318
+ }
319
+ case "createElementNS": {
320
+ const tag = site.arguments[1] === undefined ? undefined : resolve(site.arguments[1]);
321
+ if (tag === undefined) dynamicMarkup = true;
322
+ else if (tag.toLowerCase() === "style") createsStyleElement = true;
323
+ break;
324
+ }
325
+ case "loadHTML":
326
+ case "parseFromString":
327
+ case "write":
328
+ case "writeln":
329
+ sink(site.arguments[0]);
330
+ break;
331
+ case "insertAdjacentHTML":
332
+ sink(site.arguments[1]);
333
+ break;
334
+ case "setAttribute": {
335
+ const attribute = site.arguments[0] === undefined ? undefined : resolve(site.arguments[0]);
336
+ if (attribute === undefined) dynamicMarkup = true;
337
+ else if (attribute.toLowerCase() === "style") sink(site.arguments[1]);
338
+ break;
339
+ }
340
+ case "setProperty": {
341
+ /* The property *name* decides whether a CSS feature is used; the value
342
+ * cannot introduce a new property. A computed name is therefore the
343
+ * only reason to be conservative here. */
344
+ if (site.arguments[0] === undefined || resolve(site.arguments[0]) === undefined) dynamicMarkup = true;
345
+ break;
346
+ }
347
+ case "insertRule":
348
+ case "addRule":
349
+ case "replaceSync":
350
+ sinkAll(site.arguments);
351
+ break;
352
+ default:
353
+ break;
354
+ }
355
+ }
356
+
357
+ for (const assignment of assignments) {
358
+ if (MARKUP_PROPERTIES.has(assignment.name)) {
359
+ sink(assignment.right);
360
+ } else if (assignment.name === "textContent" || assignment.name === "innerText") {
361
+ if (resolve(assignment.right) === undefined) unresolvedTextContent = true;
362
+ }
363
+ }
364
+
365
+ /* A `<style>` element created at run time is usually filled through
366
+ * `textContent`; if that value is not a literal, CSS (and therefore
367
+ * `mix-blend-mode`) may come from data the compiler cannot see. */
368
+ if (createsStyleElement && unresolvedTextContent) dynamicMarkup = true;
369
+
370
+ return {
371
+ strings: [...strings],
372
+ identifiers,
373
+ getContext: getContextKinds,
374
+ dynamicMarkup,
375
+ };
376
+ }
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.45";
12
+ export const COMPILER_VERSION = "0.5.52";
@@ -13,9 +13,8 @@
13
13
  import type { Extension } from "./registry.js";
14
14
  import { nodeExtension } from "./node/index.js";
15
15
  import { guiExtension } from "./gui/index.js";
16
- import { mediaExtension } from "./media/index.js";
17
16
 
18
17
  /** Every extension bundled with xbintsc, in registration order. */
19
18
  export function bundledExtensions(): readonly Extension[] {
20
- return [nodeExtension, guiExtension, mediaExtension];
19
+ return [nodeExtension, guiExtension];
21
20
  }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Optional link features of the `gui` extension.
3
+ *
4
+ * The GUI engine itself is always linked with `--ext gui`; these are the two
5
+ * heavy halves a program may never touch:
6
+ *
7
+ * - `media` — the FFmpeg decoder backend (`runtime/ext_media/`, `media.a`).
8
+ * The DOM media surface lives in `gui.a`; only decoding is optional, and an
9
+ * element without the backend still exists and fires `error`.
10
+ * - `gpu` — glslang (+ SPIRV-Cross on macOS), the GLSL→SPIR-V translator used
11
+ * by WebGL *and* by the `mix-blend-mode` / `backdrop-filter` offscreen
12
+ * compositor. The engine's own shaders are precompiled, so a DOM page
13
+ * never needs it.
14
+ *
15
+ * Detection is static: the compiler sees imported `.html`/`.css` assets and all
16
+ * bundled modules, so it can recognise `<video>`/`<audio>`, `getContext("webgl")`
17
+ * and `mix-blend-mode`/`backdrop-filter`. When markup can be built at run time
18
+ * the usage collector sets `dynamicMarkup`, which conservatively enables both
19
+ * features; users who know better can shrink the binary with `--no-feature`.
20
+ */
21
+
22
+ import { join } from "node:path";
23
+ import type { LinkFeature, ProgramUsage } from "../link-feature.js";
24
+ import { findRuntimeDir, platformSlug } from "../../driver/paths.js";
25
+ import { findRuntimeLibrary, runtimeLibDir } from "../../driver/runtime-lib.js";
26
+
27
+ /** `<video …>` / `<audio …>` in an HTML string or asset. */
28
+ const MEDIA_TAG = /<(?:video|audio)\b/i;
29
+ /** A literal tag name handed to `createElement("video")`. */
30
+ const MEDIA_TAG_NAME = /^(?:video|audio)$/i;
31
+ /** Anything that needs the GLSL translator. */
32
+ const GPU_TOKEN = /webgl|mix-blend-mode|mixBlendMode|backdrop-filter|backdropFilter/i;
33
+
34
+ /** True when the program may create or drive a media element. */
35
+ export function hasMediaUsage(usage: ProgramUsage): boolean {
36
+ if (usage.dynamicMarkup) return true;
37
+ for (const name of ["Audio", "HTMLMediaElement", "HTMLVideoElement", "HTMLAudioElement", "decodeAudioData"]) {
38
+ if (usage.identifiers.has(name)) return true;
39
+ }
40
+ return usage.strings.some((value) => MEDIA_TAG.test(value) || MEDIA_TAG_NAME.test(value));
41
+ }
42
+
43
+ /** True when the program may compile GLSL at run time (WebGL or blending). */
44
+ export function hasGpuUsage(usage: ProgramUsage): boolean {
45
+ if (usage.dynamicMarkup) return true;
46
+ if (usage.identifiers.has("mixBlendMode") || usage.identifiers.has("backdropFilter")) return true;
47
+ if (usage.getContext.some((kind) => kind === undefined || /webgl/i.test(kind))) return true;
48
+ return usage.strings.some((value) => GPU_TOKEN.test(value));
49
+ }
50
+
51
+ /** True when the program may ask for a WebGL context (not just blending). */
52
+ export function hasWebglUsage(usage: ProgramUsage): boolean {
53
+ if (usage.dynamicMarkup) return true;
54
+ if (usage.getContext.some((kind) => kind === undefined || /webgl/i.test(kind))) return true;
55
+ return usage.strings.some((value) => /webgl/i.test(value));
56
+ }
57
+
58
+ /** True when the program may draw a bitmap or SVG. */
59
+ export function hasImageUsage(usage: ProgramUsage): boolean {
60
+ if (usage.dynamicMarkup) return true;
61
+ for (const name of ["Image", "ImageBitmap", "createImageBitmap", "drawImage"]) {
62
+ if (usage.identifiers.has(name)) return true;
63
+ }
64
+ return usage.strings.some((value) =>
65
+ /<img\b|<svg\b|background-image|url\(|\.svg\b|drawImage|createImageBitmap/i.test(value),
66
+ );
67
+ }
68
+
69
+ /** True when the program may need full OpenType shaping. Non-Latin text needs it
70
+ * for shaping, and an author `@font-face` often points at a CFF/OTF font that
71
+ * the default stb_truetype backend cannot outline. */
72
+ export function hasShapingUsage(usage: ProgramUsage): boolean {
73
+ if (usage.dynamicMarkup) return true;
74
+ return usage.strings.some((value) => {
75
+ if (/@font-face\b/i.test(value)) return true;
76
+ for (const character of value) {
77
+ if ((character.codePointAt(0) ?? 0) > 0x7f) return true;
78
+ }
79
+ return /\\u\{[0-9a-fA-F]+\}|\\u[0-9a-fA-F]{4}|&#(?:x[0-9a-fA-F]+|\d+);/.test(value);
80
+ });
81
+ }
82
+
83
+ /** Absolute path to the per-platform prebuilt media archive. */
84
+ function mediaArchive(): string {
85
+ return join(runtimeLibDir(), process.platform === "win32" ? "media.lib" : "media.a");
86
+ }
87
+
88
+ export const guiLinkFeatures: readonly LinkFeature[] = [
89
+ {
90
+ name: "media",
91
+ description: "FFmpeg decoding for <video>/<audio>",
92
+ /**
93
+ * The registration bridge is a tiny C file compiled and linked only when
94
+ * the feature is active. Its constructor installs the decoder table into
95
+ * the engine and references the backend entry points, which is what makes
96
+ * the linker extract `media.a` — an archive whose only entry point is a
97
+ * constructor would otherwise never be pulled in.
98
+ */
99
+ runtimeSources: () => [join(findRuntimeDir(), "ext_media", "media_bridge.c")],
100
+ nativeObjects: () => {
101
+ const archive = findRuntimeLibrary("media");
102
+ if (archive === undefined) {
103
+ throw new Error(
104
+ `media native library not found at ${mediaArchive()} (media.a or media.lib).\n` +
105
+ `The optional @xbintsc/${platformSlug()} package ships it: reinstall xbintsc ` +
106
+ `without --omit=optional, or build it with \`npm run media\` (see doc/gui/build.md).`,
107
+ );
108
+ }
109
+ return [archive];
110
+ },
111
+ /** `media.a` is C++ and FFmpeg itself needs libm; the GUI extension already
112
+ * contributes the C++ runtime and the OS frameworks. On Windows the FFmpeg
113
+ * objects reference the Win32 crypto/COM services (`bcrypt`, `ole32`) and
114
+ * `user32`. */
115
+ linkerFlags: () => (process.platform === "win32" ? ["-lbcrypt", "-lole32", "-luser32"] : ["-lm"]),
116
+ used: hasMediaUsage,
117
+ },
118
+ {
119
+ name: "gpu",
120
+ description: "GLSL translation (WebGL, mix-blend-mode and backdrop-filter) via glslang",
121
+ /**
122
+ * `gpu_bridge.c` is linked only when the feature is active. It calls into
123
+ * `gpu_feature.cpp` inside `gui.a`, which installs the shader-translation
124
+ * table and references `compile_glsl`/`cross_compile_msl`; those references
125
+ * are what pull the glslang members out of `gui.a`. Without the bridge the
126
+ * engine's shader paths see a null table and fail gracefully (blend falls
127
+ * back, WebGL reports a compile log).
128
+ */
129
+ runtimeSources: () => [join(findRuntimeDir(), "ext_gui", "gpu_bridge.c")],
130
+ used: hasGpuUsage,
131
+ },
132
+ {
133
+ name: "images",
134
+ description: "stb_image/NanoSVG decoding for <img>, CSS backgrounds and inline <svg>",
135
+ /**
136
+ * `image_bridge.c` installs `XtImageApi` and references the decoders; a page
137
+ * with no `<img>`/SVG/`background-image`/`drawImage` links neither
138
+ * stb_image nor NanoSVG.
139
+ */
140
+ runtimeSources: () => [join(findRuntimeDir(), "ext_gui", "image_bridge.c")],
141
+ used: hasImageUsage,
142
+ },
143
+ {
144
+ name: "shaping",
145
+ description: "HarfBuzz/FreeType shaping for complex scripts and CFF/OTF fonts",
146
+ /**
147
+ * `text_bridge.c` installs `TextApi` and references `text_hb.cpp`, pulling
148
+ * HarfBuzz and FreeType out of `gui.a`. A Latin-only page keeps the faster
149
+ * stb_truetype path and links neither library.
150
+ */
151
+ runtimeSources: () => [join(findRuntimeDir(), "ext_gui", "text_bridge.c")],
152
+ used: hasShapingUsage,
153
+ },
154
+ {
155
+ name: "webgl",
156
+ description: "WebGL context and DOM API (requires gpu)",
157
+ requires: ["gpu"],
158
+ /**
159
+ * `webgl_bridge.c` installs `XtWebglApi` and references the WebGL context
160
+ * and DOM bindings, pulling them out of `gui.a`. A page that never asks for
161
+ * WebGL therefore links none of `webgl_*`/`dom_api_webgl*`.
162
+ */
163
+ runtimeSources: () => [join(findRuntimeDir(), "ext_gui", "webgl_bridge.c")],
164
+ used: hasWebglUsage,
165
+ },
166
+ ];
@@ -27,6 +27,7 @@ import { platformSlug } from "../../driver/paths.js";
27
27
  import { findRuntimeLibrary, runtimeLibDir } from "../../driver/runtime-lib.js";
28
28
  import { loadHtmlAsset } from "./html.js";
29
29
  import { loadScriptAsset } from "./three.js";
30
+ import { guiLinkFeatures } from "./features.js";
30
31
 
31
32
  /** Top-level `gui` module bindings. */
32
33
  const guiModule: ExtensionModule = {
@@ -141,9 +142,8 @@ export const guiExtension: Extension = {
141
142
  },
142
143
  /**
143
144
  * System libraries the archive itself depends on. The vendored third-party
144
- * libraries (GPU backend, HarfBuzz, FreeType, ...) are linked *into* `gui.a`
145
- * by its build script, so only OS frameworks remain here. TODO(gui): confirm
146
- * the exact set once the GPU backend is chosen (see doc/gui.md).
145
+ * libraries (GPU backend, stb_truetype, stb_image/NanoSVG, ...) are linked
146
+ * *into* `gui.a` by its build script, so only OS frameworks remain here.
147
147
  */
148
148
  linkerFlags: () => {
149
149
  if (process.platform === "darwin") {
@@ -162,7 +162,7 @@ export const guiExtension: Extension = {
162
162
  "-framework", "GameController",
163
163
  "-framework", "Metal",
164
164
  "-framework", "QuartzCore",
165
- /* HarfBuzz's CoreText shaper (macOS system fonts). */
165
+ /* Cocoa/system text and graphics services used by SDL. */
166
166
  "-framework", "CoreText",
167
167
  "-framework", "CoreGraphics",
168
168
  "-framework", "CoreFoundation",
@@ -216,4 +216,10 @@ export const guiExtension: Extension = {
216
216
  globalValues: () => browserGlobals,
217
217
  /** Rewrite imported `.html` assets and patch three.js's shader unroller. */
218
218
  assetLoaders: () => ({ ".html": loadHtmlAsset, ".js": loadScriptAsset, ".mjs": loadScriptAsset }),
219
+ /**
220
+ * FFmpeg and glslang are optional halves of the engine, selected from the
221
+ * program (see `features.ts`). A program that only renders HTML/CSS never
222
+ * links either.
223
+ */
224
+ linkFeatures: () => guiLinkFeatures,
219
225
  };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Optional link features: runtime capabilities an extension can contribute
3
+ * without making every program pay for them.
4
+ *
5
+ * A feature is *not* an extension: the extension is the compile-time unit
6
+ * (`--ext gui`), while a feature is a heavy optional half of it (`FFmpeg`,
7
+ * `glslang`). The driver decides which features a program may use from a static
8
+ * scan of the bundled AST (see `src/driver/usage.ts`) and then links the
9
+ * feature's registration object and archives only for those.
10
+ *
11
+ * The registration object is the "strong reference": it is a C translation unit
12
+ * linked directly only when the feature is active. Its constructor installs the
13
+ * feature's dispatch table into the core, and its references force the linker to
14
+ * extract the archive members that implement the feature. This is the same
15
+ * mechanism the former `media` extension used for `media_bridge.c`.
16
+ */
17
+
18
+ /** Facts about a bundled program that features use to decide whether they are needed. */
19
+ export interface ProgramUsage {
20
+ /**
21
+ * Every string literal value in the bundle, including the HTML/CSS of
22
+ * imported `.html` assets and the static chunks of template literals.
23
+ */
24
+ readonly strings: readonly string[];
25
+ /** Identifiers and property names referenced anywhere in the bundle. */
26
+ readonly identifiers: ReadonlySet<string>;
27
+ /**
28
+ * One entry per `getContext(...)` call: the context kind when the argument
29
+ * could be resolved to a literal, or `undefined` when it is decided at run
30
+ * time (which must be treated conservatively as "might be WebGL").
31
+ */
32
+ readonly getContext: readonly (string | undefined)[];
33
+ /**
34
+ * True when the program can build HTML/CSS from data the compiler cannot
35
+ * see (`innerHTML = fetched`, `loadHTML(buildPage())`, …). Features must
36
+ * assume they may be used when this is set.
37
+ */
38
+ readonly dynamicMarkup: boolean;
39
+ }
40
+
41
+ /**
42
+ * One optional capability an extension can contribute.
43
+ *
44
+ * `used()` is only consulted when the providing extension is enabled. When it
45
+ * returns true (or the user forces the feature) the driver compiles
46
+ * `runtimeSources()` and links `nativeObjects()`, which makes the implementations
47
+ * reachable. Nothing else about the feature exists at compile time.
48
+ */
49
+ export interface LinkFeature {
50
+ /** Stable name used by `--feature` / `--no-feature`, e.g. `media`. */
51
+ readonly name: string;
52
+ /** One-line description for diagnostics and `--verbose`. */
53
+ readonly description: string;
54
+ /**
55
+ * C sources compiled and linked only when the feature is active. At least one
56
+ * of them must reference the feature's implementation so the linker extracts
57
+ * it from its archive.
58
+ */
59
+ runtimeSources?(): readonly string[];
60
+ /**
61
+ * Pre-built objects/archives linked only when the feature is active. Throwing
62
+ * here means "the feature is unavailable on this host"; the driver turns that
63
+ * into a warning for auto-detected features and an error for forced ones.
64
+ */
65
+ nativeObjects?(): readonly string[];
66
+ /** Extra linker flags contributed only when the feature is active. */
67
+ linkerFlags?(): readonly string[];
68
+ /** Other link features this one needs (activated with it). */
69
+ readonly requires?: readonly string[];
70
+ /** Whether the program may use this feature. */
71
+ used(usage: ProgramUsage): boolean;
72
+ }