@microsoft/fast-element 2.10.3 → 3.0.0-rc.1

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 (351) hide show
  1. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +1 -1
  2. package/ARCHITECTURE_OVERVIEW.md +2 -2
  3. package/CHANGELOG.json +31 -1
  4. package/CHANGELOG.md +10 -2
  5. package/DECLARATIVE_DESIGN.md +806 -0
  6. package/DECLARATIVE_HTML.md +470 -0
  7. package/DECLARATIVE_MIGRATION.md +215 -0
  8. package/DECLARATIVE_RENDERING.md +530 -0
  9. package/DECLARATIVE_RENDERING_LIFECYCLE.md +288 -0
  10. package/DECLARATIVE_SCHEMA_OBSERVER_MAP.md +489 -0
  11. package/DESIGN.md +143 -34
  12. package/MIGRATION.md +387 -0
  13. package/README.md +208 -1
  14. package/SIZES.md +25 -0
  15. package/api-extractor.arrays.json +15 -0
  16. package/api-extractor.context.json +1 -0
  17. package/api-extractor.declarative.json +15 -0
  18. package/api-extractor.di.json +1 -0
  19. package/api-extractor.hydration.json +15 -0
  20. package/api-extractor.styles.json +15 -0
  21. package/dist/arrays/arrays.api.json +2621 -0
  22. package/dist/context/context.api.json +1 -1
  23. package/dist/declarative/declarative.api.json +7844 -0
  24. package/dist/di/di.api.json +2 -2
  25. package/dist/dts/array-observer.d.ts +2 -0
  26. package/dist/dts/arrays.d.ts +2 -0
  27. package/dist/dts/attr.d.ts +1 -0
  28. package/dist/dts/binding/signal.d.ts +6 -6
  29. package/dist/dts/binding/two-way.d.ts +1 -0
  30. package/dist/dts/binding.d.ts +7 -0
  31. package/dist/dts/components/attributes.d.ts +2 -5
  32. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  33. package/dist/dts/components/element-controller.d.ts +80 -114
  34. package/dist/dts/components/element-hydration.d.ts +1 -1
  35. package/dist/dts/components/enable-hydration.d.ts +34 -0
  36. package/dist/dts/components/fast-definitions.d.ts +91 -42
  37. package/dist/dts/components/fast-element.d.ts +5 -8
  38. package/dist/dts/components/hydration-tracker.d.ts +40 -0
  39. package/dist/dts/components/hydration.d.ts +18 -53
  40. package/dist/dts/components/schema.d.ts +205 -0
  41. package/dist/dts/context.d.ts +6 -6
  42. package/dist/dts/css.d.ts +3 -0
  43. package/dist/dts/debug.d.ts +5 -1
  44. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  45. package/dist/dts/declarative/debug.d.ts +5 -0
  46. package/dist/dts/declarative/index.d.ts +13 -0
  47. package/dist/dts/declarative/interfaces.d.ts +9 -0
  48. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  49. package/dist/dts/declarative/observer-map.d.ts +89 -0
  50. package/dist/dts/declarative/runtime.d.ts +5 -0
  51. package/dist/dts/declarative/syntax.d.ts +21 -0
  52. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  53. package/dist/dts/declarative/template-parser.d.ts +98 -0
  54. package/dist/dts/declarative/template.d.ts +9 -0
  55. package/dist/dts/declarative/utilities.d.ts +312 -0
  56. package/dist/dts/di/di.d.ts +1 -1
  57. package/dist/dts/directives/children.d.ts +2 -0
  58. package/dist/dts/directives/node-observation.d.ts +2 -0
  59. package/dist/dts/directives/ref.d.ts +2 -0
  60. package/dist/dts/directives/repeat.d.ts +4 -0
  61. package/dist/dts/directives/slotted.d.ts +2 -0
  62. package/dist/dts/directives/when.d.ts +3 -0
  63. package/dist/dts/dom-policy.d.ts +1 -1
  64. package/dist/dts/html.d.ts +5 -0
  65. package/dist/dts/hydration/runtime.d.ts +7 -0
  66. package/dist/dts/hydration/target-builder.d.ts +15 -12
  67. package/dist/dts/hydration.d.ts +14 -0
  68. package/dist/dts/index.d.ts +38 -42
  69. package/dist/dts/index.debug.d.ts +0 -1
  70. package/dist/dts/index.rollup.debug.d.ts +0 -1
  71. package/dist/dts/interfaces.d.ts +1 -49
  72. package/dist/dts/observable.d.ts +3 -6
  73. package/dist/dts/observation/arrays.d.ts +1 -1
  74. package/dist/dts/observation/update-queue.d.ts +1 -1
  75. package/dist/dts/platform.d.ts +25 -4
  76. package/dist/dts/render.d.ts +7 -0
  77. package/dist/dts/schema.d.ts +1 -0
  78. package/dist/dts/state/exports.d.ts +1 -1
  79. package/dist/dts/state/state.d.ts +2 -2
  80. package/dist/dts/styles/css-directive.d.ts +5 -12
  81. package/dist/dts/styles/css.d.ts +5 -7
  82. package/dist/dts/styles/element-styles.d.ts +0 -10
  83. package/dist/dts/styles.d.ts +6 -0
  84. package/dist/dts/templating/children.d.ts +1 -1
  85. package/dist/dts/templating/html-binding-directive.d.ts +10 -0
  86. package/dist/dts/templating/html-directive.d.ts +17 -0
  87. package/dist/dts/templating/hydration-view.d.ts +109 -0
  88. package/dist/dts/templating/ref.d.ts +1 -1
  89. package/dist/dts/templating/render.d.ts +8 -2
  90. package/dist/dts/templating/repeat.d.ts +2 -2
  91. package/dist/dts/templating/slotted.d.ts +1 -1
  92. package/dist/dts/templating/template.d.ts +17 -9
  93. package/dist/dts/templating/view.d.ts +25 -102
  94. package/dist/dts/templating/when.d.ts +1 -1
  95. package/dist/dts/templating.d.ts +10 -0
  96. package/dist/dts/testing/exports.d.ts +2 -2
  97. package/dist/dts/tsdoc-metadata.json +1 -1
  98. package/dist/dts/updates.d.ts +1 -0
  99. package/dist/dts/volatile.d.ts +2 -0
  100. package/dist/esm/array-observer.js +1 -0
  101. package/dist/esm/arrays.js +1 -0
  102. package/dist/esm/attr.js +1 -0
  103. package/dist/esm/binding/normalize.js +1 -1
  104. package/dist/esm/binding/signal.js +4 -4
  105. package/dist/esm/binding/two-way.js +2 -1
  106. package/dist/esm/binding.js +4 -0
  107. package/dist/esm/components/attributes.js +8 -5
  108. package/dist/esm/components/definition-schema-transforms.js +23 -0
  109. package/dist/esm/components/element-controller.js +200 -269
  110. package/dist/esm/components/element-hydration.js +1 -1
  111. package/dist/esm/components/enable-hydration.js +100 -0
  112. package/dist/esm/components/fast-definitions.js +211 -49
  113. package/dist/esm/components/fast-element.js +18 -27
  114. package/dist/esm/components/hydration-tracker.js +93 -0
  115. package/dist/esm/components/hydration.js +62 -144
  116. package/dist/esm/components/schema.js +253 -0
  117. package/dist/esm/context.js +6 -6
  118. package/dist/esm/css.js +3 -0
  119. package/dist/esm/debug.js +26 -26
  120. package/dist/esm/declarative/attribute-map.js +121 -0
  121. package/dist/esm/declarative/debug.js +5 -0
  122. package/dist/esm/declarative/index.js +3 -0
  123. package/dist/esm/declarative/interfaces.js +10 -0
  124. package/dist/esm/declarative/observer-map-utilities.js +562 -0
  125. package/dist/esm/declarative/observer-map.js +216 -0
  126. package/dist/esm/declarative/runtime.js +14 -0
  127. package/dist/esm/declarative/syntax.js +36 -0
  128. package/dist/esm/declarative/template-bridge.js +170 -0
  129. package/dist/esm/declarative/template-parser.js +306 -0
  130. package/dist/esm/declarative/template.js +142 -0
  131. package/dist/esm/declarative/utilities.js +834 -0
  132. package/dist/esm/di/di.js +6 -8
  133. package/dist/esm/directives/children.js +1 -0
  134. package/dist/esm/directives/node-observation.js +1 -0
  135. package/dist/esm/directives/ref.js +1 -0
  136. package/dist/esm/directives/repeat.js +1 -0
  137. package/dist/esm/directives/slotted.js +1 -0
  138. package/dist/esm/directives/when.js +1 -0
  139. package/dist/esm/dom-policy.js +2 -2
  140. package/dist/esm/dom.js +1 -1
  141. package/dist/esm/html.js +2 -0
  142. package/dist/esm/hydration/runtime.js +33 -0
  143. package/dist/esm/hydration/target-builder.js +97 -90
  144. package/dist/esm/hydration.js +4 -0
  145. package/dist/esm/index.debug.js +2 -1
  146. package/dist/esm/index.js +34 -29
  147. package/dist/esm/index.rollup.debug.js +3 -2
  148. package/dist/esm/index.rollup.js +1 -1
  149. package/dist/esm/interfaces.js +1 -45
  150. package/dist/esm/observable.js +1 -4
  151. package/dist/esm/observation/arrays.js +1 -1
  152. package/dist/esm/observation/observable.js +5 -5
  153. package/dist/esm/observation/update-queue.js +47 -58
  154. package/dist/esm/platform.js +31 -30
  155. package/dist/esm/render.js +1 -0
  156. package/dist/esm/schema.js +1 -0
  157. package/dist/esm/state/exports.js +1 -1
  158. package/dist/esm/styles/css-directive.js +1 -2
  159. package/dist/esm/styles/css.js +15 -56
  160. package/dist/esm/styles/element-styles.js +69 -15
  161. package/dist/esm/styles.js +2 -0
  162. package/dist/esm/templating/html-binding-directive.js +24 -10
  163. package/dist/esm/templating/hydration-view.js +235 -0
  164. package/dist/esm/templating/render.js +13 -2
  165. package/dist/esm/templating/repeat.js +36 -34
  166. package/dist/esm/templating/template.js +7 -7
  167. package/dist/esm/templating/view.js +24 -233
  168. package/dist/esm/templating.js +7 -0
  169. package/dist/esm/testing/exports.js +2 -2
  170. package/dist/esm/updates.js +1 -0
  171. package/dist/esm/volatile.js +1 -0
  172. package/dist/fast-element.api.json +9017 -6996
  173. package/dist/fast-element.d.ts +3557 -796
  174. package/dist/fast-element.debug.js +5093 -4419
  175. package/dist/fast-element.debug.min.js +2 -2
  176. package/dist/fast-element.js +5398 -4655
  177. package/dist/fast-element.min.js +2 -2
  178. package/dist/fast-element.untrimmed.d.ts +881 -481
  179. package/dist/hydration/hydration.api.json +5237 -0
  180. package/dist/styles/styles.api.json +2672 -0
  181. package/docs/api-report.api.md +344 -167
  182. package/docs/arrays/api-report.api.md +114 -0
  183. package/docs/declarative/api-report.api.md +397 -0
  184. package/docs/hydration/api-report.api.md +285 -0
  185. package/docs/styles/api-report.api.md +135 -0
  186. package/package.json +149 -40
  187. package/playwright.declarative.config.ts +26 -0
  188. package/playwright.declarative.webui.config.ts +20 -0
  189. package/scripts/declarative/build-fixtures-with-webui.js +135 -0
  190. package/scripts/declarative/build-fixtures.js +49 -0
  191. package/scripts/declarative/build-fixtures.utilities.js +101 -0
  192. package/scripts/measure-sizes.js +219 -0
  193. package/scripts/run-api-extractor.js +39 -20
  194. package/test/declarative/fixtures/README.md +72 -0
  195. package/test/declarative/fixtures/WRITING_FIXTURES.md +330 -0
  196. package/test/declarative/fixtures/bindings/README.md +12 -0
  197. package/test/declarative/fixtures/bindings/attribute/entry.html +13 -0
  198. package/test/declarative/fixtures/bindings/attribute/fast-build.config.json +6 -0
  199. package/test/declarative/fixtures/bindings/attribute/index.html +25 -0
  200. package/test/declarative/fixtures/bindings/attribute/main.ts +41 -0
  201. package/test/declarative/fixtures/bindings/attribute/state.json +8 -0
  202. package/test/declarative/fixtures/bindings/attribute/templates.html +11 -0
  203. package/test/declarative/fixtures/bindings/content/entry.html +12 -0
  204. package/test/declarative/fixtures/bindings/content/fast-build.config.json +6 -0
  205. package/test/declarative/fixtures/bindings/content/index.html +19 -0
  206. package/test/declarative/fixtures/bindings/content/main.ts +27 -0
  207. package/test/declarative/fixtures/bindings/content/state.json +4 -0
  208. package/test/declarative/fixtures/bindings/content/templates.html +6 -0
  209. package/test/declarative/fixtures/bindings/dot-syntax/entry.html +11 -0
  210. package/test/declarative/fixtures/bindings/dot-syntax/fast-build.config.json +6 -0
  211. package/test/declarative/fixtures/bindings/dot-syntax/index.html +47 -0
  212. package/test/declarative/fixtures/bindings/dot-syntax/main.ts +59 -0
  213. package/test/declarative/fixtures/bindings/dot-syntax/state.json +16 -0
  214. package/test/declarative/fixtures/bindings/dot-syntax/templates.html +17 -0
  215. package/test/declarative/fixtures/bindings/event/entry.html +11 -0
  216. package/test/declarative/fixtures/bindings/event/fast-build.config.json +6 -0
  217. package/test/declarative/fixtures/bindings/event/index.html +43 -0
  218. package/test/declarative/fixtures/bindings/event/main.ts +43 -0
  219. package/test/declarative/fixtures/bindings/event/state.json +3 -0
  220. package/test/declarative/fixtures/bindings/event/templates.html +18 -0
  221. package/test/declarative/fixtures/bindings/host/entry.html +40 -0
  222. package/test/declarative/fixtures/bindings/host/fast-build.config.json +6 -0
  223. package/test/declarative/fixtures/bindings/host/index.html +96 -0
  224. package/test/declarative/fixtures/bindings/host/main.ts +222 -0
  225. package/test/declarative/fixtures/bindings/host/state.json +9 -0
  226. package/test/declarative/fixtures/bindings/host/templates.html +55 -0
  227. package/test/declarative/fixtures/directives/README.md +12 -0
  228. package/test/declarative/fixtures/directives/children/entry.html +11 -0
  229. package/test/declarative/fixtures/directives/children/fast-build.config.json +6 -0
  230. package/test/declarative/fixtures/directives/children/index.html +15 -0
  231. package/test/declarative/fixtures/directives/children/main.ts +22 -0
  232. package/test/declarative/fixtures/directives/children/state.json +3 -0
  233. package/test/declarative/fixtures/directives/children/templates.html +3 -0
  234. package/test/declarative/fixtures/directives/ref/entry.html +11 -0
  235. package/test/declarative/fixtures/directives/ref/fast-build.config.json +6 -0
  236. package/test/declarative/fixtures/directives/ref/index.html +15 -0
  237. package/test/declarative/fixtures/directives/ref/main.ts +17 -0
  238. package/test/declarative/fixtures/directives/ref/state.json +1 -0
  239. package/test/declarative/fixtures/directives/ref/templates.html +3 -0
  240. package/test/declarative/fixtures/directives/repeat/entry.html +21 -0
  241. package/test/declarative/fixtures/directives/repeat/fast-build.config.json +6 -0
  242. package/test/declarative/fixtures/directives/repeat/index.html +133 -0
  243. package/test/declarative/fixtures/directives/repeat/main.ts +110 -0
  244. package/test/declarative/fixtures/directives/repeat/sprites.svg +8 -0
  245. package/test/declarative/fixtures/directives/repeat/state.json +10 -0
  246. package/test/declarative/fixtures/directives/repeat/templates.html +75 -0
  247. package/test/declarative/fixtures/directives/slotted/entry.html +17 -0
  248. package/test/declarative/fixtures/directives/slotted/fast-build.config.json +6 -0
  249. package/test/declarative/fixtures/directives/slotted/index.html +27 -0
  250. package/test/declarative/fixtures/directives/slotted/main.ts +29 -0
  251. package/test/declarative/fixtures/directives/slotted/state.json +1 -0
  252. package/test/declarative/fixtures/directives/slotted/templates.html +7 -0
  253. package/test/declarative/fixtures/directives/when/entry.html +51 -0
  254. package/test/declarative/fixtures/directives/when/fast-build.config.json +6 -0
  255. package/test/declarative/fixtures/directives/when/index.html +136 -0
  256. package/test/declarative/fixtures/directives/when/main.ts +172 -0
  257. package/test/declarative/fixtures/directives/when/state.json +12 -0
  258. package/test/declarative/fixtures/directives/when/templates.html +75 -0
  259. package/test/declarative/fixtures/ecosystem/README.md +11 -0
  260. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/entry.html +12 -0
  261. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/fast-build.config.json +6 -0
  262. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/index.html +20 -0
  263. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/main.ts +68 -0
  264. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/state.json +4 -0
  265. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/templates.html +7 -0
  266. package/test/declarative/fixtures/ecosystem/errors/entry.html +12 -0
  267. package/test/declarative/fixtures/ecosystem/errors/fast-build.config.json +6 -0
  268. package/test/declarative/fixtures/ecosystem/errors/index.html +20 -0
  269. package/test/declarative/fixtures/ecosystem/errors/main.ts +17 -0
  270. package/test/declarative/fixtures/ecosystem/errors/state.json +1 -0
  271. package/test/declarative/fixtures/ecosystem/errors/templates.html +7 -0
  272. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/entry.html +17 -0
  273. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/fast-build.config.json +6 -0
  274. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/index.html +56 -0
  275. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/main.ts +134 -0
  276. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/state.json +12 -0
  277. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/templates.html +34 -0
  278. package/test/declarative/fixtures/ecosystem/performance-metrics/entry.html +25 -0
  279. package/test/declarative/fixtures/ecosystem/performance-metrics/fast-build.config.json +6 -0
  280. package/test/declarative/fixtures/ecosystem/performance-metrics/fast-card.css +10 -0
  281. package/test/declarative/fixtures/ecosystem/performance-metrics/index.html +181 -0
  282. package/test/declarative/fixtures/ecosystem/performance-metrics/main.ts +58 -0
  283. package/test/declarative/fixtures/ecosystem/performance-metrics/state.json +6 -0
  284. package/test/declarative/fixtures/ecosystem/performance-metrics/templates.html +15 -0
  285. package/test/declarative/fixtures/extensions/README.md +15 -0
  286. package/test/declarative/fixtures/extensions/attribute-map/entry.html +14 -0
  287. package/test/declarative/fixtures/extensions/attribute-map/fast-build.config.json +6 -0
  288. package/test/declarative/fixtures/extensions/attribute-map/index.html +31 -0
  289. package/test/declarative/fixtures/extensions/attribute-map/main.ts +40 -0
  290. package/test/declarative/fixtures/extensions/attribute-map/state.json +4 -0
  291. package/test/declarative/fixtures/extensions/attribute-map/templates.html +14 -0
  292. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/entry.html +12 -0
  293. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/fast-build.config.json +7 -0
  294. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/index.html +25 -0
  295. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/main.ts +31 -0
  296. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/state.json +5 -0
  297. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/templates.html +11 -0
  298. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/entry.html +13 -0
  299. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/fast-build.config.json +7 -0
  300. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/index.html +23 -0
  301. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/main.ts +37 -0
  302. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/state.json +1 -0
  303. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/templates.html +9 -0
  304. package/test/declarative/fixtures/extensions/observer-map/entry.html +15 -0
  305. package/test/declarative/fixtures/extensions/observer-map/fast-build.config.json +6 -0
  306. package/test/declarative/fixtures/extensions/observer-map/index.html +442 -0
  307. package/test/declarative/fixtures/extensions/observer-map/main.ts +482 -0
  308. package/test/declarative/fixtures/extensions/observer-map/state.json +158 -0
  309. package/test/declarative/fixtures/extensions/observer-map/templates.html +172 -0
  310. package/test/declarative/fixtures/extensions/observer-map-config-object/entry.html +16 -0
  311. package/test/declarative/fixtures/extensions/observer-map-config-object/fast-build.config.json +6 -0
  312. package/test/declarative/fixtures/extensions/observer-map-config-object/index.html +27 -0
  313. package/test/declarative/fixtures/extensions/observer-map-config-object/main.ts +53 -0
  314. package/test/declarative/fixtures/extensions/observer-map-config-object/state.json +9 -0
  315. package/test/declarative/fixtures/extensions/observer-map-config-object/templates.html +12 -0
  316. package/test/declarative/fixtures/extensions/observer-map-deep-merge/README.md +98 -0
  317. package/test/declarative/fixtures/extensions/observer-map-deep-merge/entry.html +156 -0
  318. package/test/declarative/fixtures/extensions/observer-map-deep-merge/fast-build.config.json +6 -0
  319. package/test/declarative/fixtures/extensions/observer-map-deep-merge/index.html +376 -0
  320. package/test/declarative/fixtures/extensions/observer-map-deep-merge/main.ts +366 -0
  321. package/test/declarative/fixtures/extensions/observer-map-deep-merge/state.json +69 -0
  322. package/test/declarative/fixtures/extensions/observer-map-deep-merge/templates.html +91 -0
  323. package/test/declarative/fixtures/extensions/observer-map-properties/entry.html +14 -0
  324. package/test/declarative/fixtures/extensions/observer-map-properties/fast-build.config.json +6 -0
  325. package/test/declarative/fixtures/extensions/observer-map-properties/index.html +110 -0
  326. package/test/declarative/fixtures/extensions/observer-map-properties/main.ts +175 -0
  327. package/test/declarative/fixtures/extensions/observer-map-properties/state.json +29 -0
  328. package/test/declarative/fixtures/extensions/observer-map-properties/templates.html +55 -0
  329. package/test/declarative/fixtures/scenarios/README.md +7 -0
  330. package/test/declarative/fixtures/scenarios/nested-elements/entry.html +16 -0
  331. package/test/declarative/fixtures/scenarios/nested-elements/fast-build.config.json +6 -0
  332. package/test/declarative/fixtures/scenarios/nested-elements/index.html +126 -0
  333. package/test/declarative/fixtures/scenarios/nested-elements/main.ts +214 -0
  334. package/test/declarative/fixtures/scenarios/nested-elements/state.json +10 -0
  335. package/test/declarative/fixtures/scenarios/nested-elements/templates.html +54 -0
  336. package/test/declarative/index.html +12 -0
  337. package/test/declarative/vite.config.ts +55 -0
  338. package/test/declarative-main.ts +6 -0
  339. package/test/extension-subpaths-main.ts +9 -0
  340. package/test/main.ts +38 -33
  341. package/test/pure-declarative-main.ts +1 -0
  342. package/dist/dts/components/install-hydration.d.ts +0 -1
  343. package/dist/dts/pending-task.d.ts +0 -32
  344. package/dist/dts/polyfills.d.ts +0 -0
  345. package/dist/dts/styles/css-binding-directive.d.ts +0 -60
  346. package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
  347. package/dist/esm/components/install-hydration.js +0 -3
  348. package/dist/esm/pending-task.js +0 -28
  349. package/dist/esm/polyfills.js +0 -60
  350. package/dist/esm/styles/css-binding-directive.js +0 -76
  351. package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
@@ -0,0 +1,20 @@
1
+ import { defineConfig, devices } from "@playwright/test";
2
+
3
+ process.env.FAST_WEBUI_INTEGRATION = "true";
4
+
5
+ export default defineConfig({
6
+ testDir: "./test/declarative/fixtures",
7
+ testMatch: "**/*.spec.ts",
8
+ retries: 3,
9
+ projects: [
10
+ {
11
+ name: "chromium",
12
+ use: { ...devices["Desktop Chrome"] },
13
+ },
14
+ ],
15
+ webServer: {
16
+ command: "npm run test-server:declarative:webui",
17
+ port: 5174,
18
+ reuseExistingServer: true,
19
+ },
20
+ });
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Build FAST HTML fixtures with @microsoft/webui.
5
+ *
6
+ * For each fixture directory this script:
7
+ * 1. Extracts <f-template> components from templates.html into individual
8
+ * component files (webui uses filename-based component discovery).
9
+ * 2. Builds the fixture with `webui build --plugin=fast`.
10
+ * 3. Renders the compiled protocol with the fixture's state.json.
11
+ * 4. Writes the rendered index.html into temp/integrations/webui/fixtures/
12
+ * alongside a copy of main.ts and any extra assets so that Playwright
13
+ * tests can run against the webui-rendered output via Vite.
14
+ */
15
+
16
+ import {
17
+ copyFileSync,
18
+ existsSync,
19
+ mkdirSync,
20
+ readdirSync,
21
+ readFileSync,
22
+ rmSync,
23
+ writeFileSync,
24
+ } from "node:fs";
25
+ import { dirname, join, resolve } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import { build, render } from "@microsoft/webui";
28
+ import {
29
+ convertToWebuiSyntax,
30
+ discoverFixtures,
31
+ extractFTemplates,
32
+ } from "./build-fixtures.utilities.js";
33
+
34
+ const __dirname = dirname(fileURLToPath(import.meta.url));
35
+ const fixturesDir = resolve(__dirname, "../../test/declarative/fixtures");
36
+ const fixtures = discoverFixtures(fixturesDir);
37
+ const outBase = resolve(__dirname, "../../temp/integrations/webui/fixtures");
38
+
39
+ // Files produced by the build or only needed for the build step.
40
+ const buildOnlyFiles = new Set([
41
+ "entry.html",
42
+ "fast-build.config.json",
43
+ "templates.html",
44
+ "state.json",
45
+ "index.html",
46
+ ]);
47
+
48
+ /**
49
+ * Build a single fixture with webui and write the rendered output plus
50
+ * supporting files into the output directory.
51
+ */
52
+ function buildFixture(fixtureName) {
53
+ const fixtureDir = join(fixturesDir, fixtureName);
54
+ const buildDir = join(outBase, ".build", fixtureName);
55
+ const buildOutDir = join(buildDir, "out");
56
+ const fixtureOutDir = join(outBase, fixtureName);
57
+
58
+ mkdirSync(buildDir, { recursive: true });
59
+ mkdirSync(buildOutDir, { recursive: true });
60
+ mkdirSync(fixtureOutDir, { recursive: true });
61
+
62
+ // Extract individual component HTML files for webui discovery,
63
+ // converting FAST directives to webui syntax.
64
+ const templatesHtml = readFileSync(join(fixtureDir, "templates.html"), "utf8");
65
+ const templates = extractFTemplates(templatesHtml);
66
+
67
+ for (const { name, content } of templates) {
68
+ writeFileSync(join(buildDir, `${name}.html`), convertToWebuiSyntax(content));
69
+ }
70
+
71
+ // Prepare entry.html without script tags for webui build
72
+ const entryHtml = readFileSync(join(fixtureDir, "entry.html"), "utf8");
73
+ const cleanedEntry = entryHtml
74
+ .split("\n")
75
+ .filter(line => !line.includes("<script"))
76
+ .join("\n");
77
+ writeFileSync(join(buildDir, "entry.html"), cleanedEntry);
78
+
79
+ // Build protocol
80
+ const buildResult = build({
81
+ appDir: buildDir,
82
+ entry: "entry.html",
83
+ plugin: "fast",
84
+ outDir: buildOutDir,
85
+ });
86
+
87
+ if (!buildResult.protocol || buildResult.protocol.length === 0) {
88
+ throw new Error(`Build produced empty protocol for "${fixtureName}"`);
89
+ }
90
+
91
+ // Render with state
92
+ const state = JSON.parse(readFileSync(join(fixtureDir, "state.json"), "utf8"));
93
+ let html = render(buildResult.protocol, state, {
94
+ plugin: "fast",
95
+ entry: "entry.html",
96
+ });
97
+
98
+ // Inject <script type="module" src="./main.ts"> before </body>
99
+ html = html.replace(
100
+ "</body>",
101
+ '<script type="module" src="./main.ts"></script>\n</body>',
102
+ );
103
+
104
+ writeFileSync(join(fixtureOutDir, "index.html"), html);
105
+
106
+ // Copy main.ts and any extra assets (CSS, SVG, etc.) from the
107
+ // original fixture directory so Vite can serve them.
108
+ for (const entry of readdirSync(fixtureDir)) {
109
+ if (buildOnlyFiles.has(entry) || entry.endsWith(".spec.ts")) {
110
+ continue;
111
+ }
112
+ copyFileSync(join(fixtureDir, entry), join(fixtureOutDir, entry));
113
+ }
114
+
115
+ process.stdout.write(`Fixture "${fixtureName}" built successfully.\n`);
116
+ }
117
+
118
+ function main() {
119
+ // Clean previous build output
120
+ if (existsSync(outBase)) {
121
+ rmSync(outBase, { recursive: true });
122
+ }
123
+
124
+ for (const fixtureName of fixtures) {
125
+ buildFixture(fixtureName);
126
+ }
127
+
128
+ // Clean up intermediate build directory
129
+ const buildTmp = join(outBase, ".build");
130
+ if (existsSync(buildTmp)) {
131
+ rmSync(buildTmp, { recursive: true });
132
+ }
133
+ }
134
+
135
+ main();
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import { readFileSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { discoverFixtures } from "./build-fixtures.utilities.js";
8
+
9
+ // Pass-through CLI arguments (forwarded to every fast-build invocation).
10
+ const passthroughArgs = process.argv.slice(2);
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const fixturesDir = resolve(__dirname, "../../test/declarative/fixtures");
14
+ const fixtures = discoverFixtures(fixturesDir);
15
+
16
+ const require = createRequire(import.meta.url);
17
+ const fastBin = require.resolve("@microsoft/fast-build/bin/fast.js");
18
+
19
+ for (const fixtureName of fixtures) {
20
+ const fixtureDir = resolve(__dirname, "../../test/declarative/fixtures", fixtureName);
21
+ const configFile = join(fixtureDir, "fast-build.config.json");
22
+ const templatesFile = join(fixtureDir, "templates.html");
23
+ const outputFile = join(fixtureDir, "index.html");
24
+
25
+ // Step 1: render shadow DOM via fast-build CLI using config file
26
+ execFileSync(
27
+ process.execPath,
28
+ [fastBin, "build", `--config=${configFile}`, ...passthroughArgs],
29
+ { stdio: "inherit" },
30
+ );
31
+
32
+ // Step 2: inject <f-template> declarations from templates.html before <script>
33
+ const fTemplates = readFileSync(templatesFile, "utf8").trim();
34
+ const rawHtml = readFileSync(outputFile, "utf8");
35
+
36
+ if (!rawHtml.includes('<script type="module"')) {
37
+ throw new Error(
38
+ `Fixture "${fixtureName}": could not find '<script type="module"' in "${outputFile}". Template injection failed.`,
39
+ );
40
+ }
41
+
42
+ const html = rawHtml.replace(
43
+ /(\s*)<script type="module"/,
44
+ `\n${fTemplates}\n$1<script type="module"`,
45
+ );
46
+
47
+ writeFileSync(outputFile, html);
48
+ process.stdout.write(`Fixture "${fixtureName}" built successfully.\n`);
49
+ }
@@ -0,0 +1,101 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * Auto-discover fixture directories that contain the required build files
6
+ * (entry.html, templates.html, state.json, and fast-build.config.json).
7
+ *
8
+ * Fixtures are organized in category subdirectories (e.g., bindings/attribute,
9
+ * directives/repeat). The function scans two levels deep: category directories
10
+ * at the top level, and fixture directories within each category.
11
+ *
12
+ * Each fixture directory must contain a `fast-build.config.json` that
13
+ * configures the `@microsoft/fast-build` CLI. At minimum the config
14
+ * should specify `entry`, `state`, `output`, and `templates`. Fixture-
15
+ * specific options such as `attribute-name-strategy` can also be included.
16
+ *
17
+ * @param {string} fixturesDir - Absolute path to the fixtures directory.
18
+ * @returns {string[]} Sorted array of fixture paths relative to fixturesDir
19
+ * (e.g., "bindings/attribute", "directives/repeat").
20
+ */
21
+ export function discoverFixtures(fixturesDir) {
22
+ const fixtures = [];
23
+
24
+ for (const category of readdirSync(fixturesDir, { withFileTypes: true })) {
25
+ if (!category.isDirectory()) continue;
26
+
27
+ const categoryDir = join(fixturesDir, category.name);
28
+
29
+ for (const fixture of readdirSync(categoryDir, { withFileTypes: true })) {
30
+ if (!fixture.isDirectory()) continue;
31
+
32
+ const dir = join(categoryDir, fixture.name);
33
+ if (
34
+ existsSync(join(dir, "entry.html")) &&
35
+ existsSync(join(dir, "templates.html")) &&
36
+ existsSync(join(dir, "state.json")) &&
37
+ existsSync(join(dir, "fast-build.config.json"))
38
+ ) {
39
+ fixtures.push(`${category.name}/${fixture.name}`);
40
+ }
41
+ }
42
+ }
43
+
44
+ return fixtures.sort((a, b) => a.localeCompare(b));
45
+ }
46
+
47
+ /**
48
+ * Extract `<f-template name="X">` elements from an HTML string.
49
+ * Returns an array of `{ name, content }` objects where `content`
50
+ * is the inner HTML of the `<template>` child. When the `<template>`
51
+ * element carries host-binding attributes (e.g. `@click`, `?disabled`),
52
+ * those are preserved by wrapping the content in a `<template>` tag.
53
+ * @param {string} html - Raw HTML containing `<f-template>` elements.
54
+ * @returns {{ name: string, content: string }[]}
55
+ */
56
+ export function extractFTemplates(html) {
57
+ const results = [];
58
+ const regex =
59
+ /<f-template\s+name="([^"]+)">\s*<template([^>]*)>([\s\S]*?)<\/template>\s*<\/f-template>/g;
60
+ let match;
61
+ while ((match = regex.exec(html)) !== null) {
62
+ const name = match[1];
63
+ const attrs = match[2].trim();
64
+ const inner = match[3].trim();
65
+
66
+ if (attrs.length > 0) {
67
+ results.push({
68
+ name,
69
+ content: `<template ${attrs}>${inner}</template>`,
70
+ });
71
+ } else {
72
+ results.push({ name, content: inner });
73
+ }
74
+ }
75
+ return results;
76
+ }
77
+
78
+ /**
79
+ * Convert FAST template directives to webui syntax.
80
+ *
81
+ * - `<f-when value="{{expr}}">` → `<if condition="expr">`
82
+ * - `</f-when>` → `</if>`
83
+ * - `<f-repeat value="{{item in list}}" ...>` → `<for each="item in list" ...>`
84
+ * - `</f-repeat>` → `</for>`
85
+ *
86
+ * @param {string} html - Template HTML using FAST directive syntax.
87
+ * @returns {string} Template HTML using webui directive syntax.
88
+ */
89
+ export function convertToWebuiSyntax(html) {
90
+ return html
91
+ .replace(
92
+ /<f-when\s+value="{{([\s\S]*?)}}"\s*>/g,
93
+ (_, expr) => `<if condition="${expr.trim()}">`,
94
+ )
95
+ .replace(/<\/f-when>/g, "</if>")
96
+ .replace(
97
+ /<f-repeat\s+value="{{([\s\S]*?)}}"([^>]*)>/g,
98
+ (_, expr, rest) => `<for each="${expr.trim()}"${rest}>`,
99
+ )
100
+ .replace(/<\/f-repeat>/g, "</for>");
101
+ }
@@ -0,0 +1,219 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { brotliCompressSync, gzipSync } from "node:zlib";
5
+ import { build } from "esbuild";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const packageRoot = path.resolve(__dirname, "..");
10
+ const rootImportPath = "@microsoft/fast-element";
11
+ const fastElementImportPath = "@microsoft/fast-element/fast-element.js";
12
+ const updatesImportPath = "@microsoft/fast-element/updates.js";
13
+ const observableImportPath = "@microsoft/fast-element/observable.js";
14
+ const attrImportPath = "@microsoft/fast-element/attr.js";
15
+ const childrenImportPath = "@microsoft/fast-element/children.js";
16
+ const refImportPath = "@microsoft/fast-element/ref.js";
17
+ const slottedImportPath = "@microsoft/fast-element/slotted.js";
18
+ const volatileImportPath = "@microsoft/fast-element/volatile.js";
19
+ const whenImportPath = "@microsoft/fast-element/when.js";
20
+ const htmlImportPath = "@microsoft/fast-element/html.js";
21
+ const repeatImportPath = "@microsoft/fast-element/repeat.js";
22
+ const cssImportPath = "@microsoft/fast-element/css.js";
23
+ const declarativeImportPath = "@microsoft/fast-element/declarative.js";
24
+ const hydrationImportPath = "@microsoft/fast-element/hydration.js";
25
+ const arrayObserverImportPath = "@microsoft/fast-element/array-observer.js";
26
+ const observerMapImportPath = "@microsoft/fast-element/observer-map.js";
27
+ const attributeMapImportPath = "@microsoft/fast-element/attribute-map.js";
28
+
29
+ const namedExports = [{ name: "FASTElement", importPath: fastElementImportPath }];
30
+
31
+ const measuredExports = [
32
+ {
33
+ name: "Updates",
34
+ export: "Updates",
35
+ importPath: updatesImportPath,
36
+ },
37
+ {
38
+ name: "Observable",
39
+ export: "Observable",
40
+ importPath: observableImportPath,
41
+ },
42
+ {
43
+ name: "observable",
44
+ export: "observable",
45
+ importPath: observableImportPath,
46
+ },
47
+ {
48
+ name: "attr",
49
+ export: "attr",
50
+ importPath: attrImportPath,
51
+ },
52
+ {
53
+ name: "children",
54
+ export: "children",
55
+ importPath: childrenImportPath,
56
+ },
57
+ {
58
+ name: "ref",
59
+ export: "ref",
60
+ importPath: refImportPath,
61
+ },
62
+ {
63
+ name: "slotted",
64
+ export: "slotted",
65
+ importPath: slottedImportPath,
66
+ },
67
+ {
68
+ name: "volatile",
69
+ export: "volatile",
70
+ importPath: volatileImportPath,
71
+ },
72
+ {
73
+ name: "when",
74
+ export: "when",
75
+ importPath: whenImportPath,
76
+ },
77
+ {
78
+ name: "html",
79
+ export: "html",
80
+ importPath: htmlImportPath,
81
+ },
82
+ {
83
+ name: "repeat",
84
+ export: "repeat",
85
+ importPath: repeatImportPath,
86
+ },
87
+ {
88
+ name: "css",
89
+ export: "css",
90
+ importPath: cssImportPath,
91
+ },
92
+ {
93
+ name: "enableHydration",
94
+ export: "enableHydration",
95
+ importPath: hydrationImportPath,
96
+ },
97
+ {
98
+ name: "declarativeTemplate",
99
+ export: "declarativeTemplate",
100
+ importPath: declarativeImportPath,
101
+ },
102
+ {
103
+ name: "ArrayObserver",
104
+ export: "ArrayObserver",
105
+ importPath: arrayObserverImportPath,
106
+ },
107
+ {
108
+ name: "observerMap",
109
+ export: "observerMap",
110
+ importPath: observerMapImportPath,
111
+ },
112
+ {
113
+ name: "attributeMap",
114
+ export: "attributeMap",
115
+ importPath: attributeMapImportPath,
116
+ },
117
+ ];
118
+
119
+ function formatExportLabel(name, importPath) {
120
+ return `${name} (${importPath})`;
121
+ }
122
+
123
+ function formatBytes(bytes) {
124
+ if (bytes < 1024) return `${bytes} B`;
125
+ return `${(bytes / 1024).toFixed(2)} KB`;
126
+ }
127
+
128
+ function measureBuffer(buffer) {
129
+ const gzip = gzipSync(buffer);
130
+ const brotli = brotliCompressSync(buffer);
131
+ return {
132
+ minified: buffer.length,
133
+ gzip: gzip.length,
134
+ brotli: brotli.length,
135
+ };
136
+ }
137
+
138
+ async function measureExport(exportName, importPath = rootImportPath) {
139
+ const contents = `import { ${exportName} } from "${importPath}";
140
+ export { ${exportName} };
141
+ `;
142
+
143
+ const result = await build({
144
+ stdin: {
145
+ contents,
146
+ resolveDir: packageRoot,
147
+ loader: "ts",
148
+ },
149
+ bundle: true,
150
+ minify: true,
151
+ format: "esm",
152
+ write: false,
153
+ treeShaking: true,
154
+ });
155
+
156
+ const code = Buffer.from(result.outputFiles[0].contents);
157
+ return measureBuffer(code);
158
+ }
159
+
160
+ async function measureCore() {
161
+ const coreBundle = path.resolve(packageRoot, "dist/fast-element.min.js");
162
+ const buffer = readFileSync(coreBundle);
163
+ return measureBuffer(buffer);
164
+ }
165
+
166
+ async function main() {
167
+ const results = [];
168
+
169
+ // Measure CDN rollup bundle
170
+ const core = await measureCore();
171
+ results.push({ name: "CDN Rollup Bundle", ...core });
172
+
173
+ // Measure each named export in parallel
174
+ const exportResults = await Promise.all(
175
+ namedExports.map(async ({ name, importPath }) => {
176
+ const sizes = await measureExport(name, importPath);
177
+ return { name: formatExportLabel(name, importPath), ...sizes };
178
+ }),
179
+ );
180
+ results.push(...exportResults);
181
+
182
+ // Measure each root export in parallel
183
+ const measuredResults = await Promise.all(
184
+ measuredExports.map(
185
+ async ({ name, export: exportName, importPath = rootImportPath }) => {
186
+ const sizes = await measureExport(exportName, importPath);
187
+ return { name: formatExportLabel(name, importPath), ...sizes };
188
+ },
189
+ ),
190
+ );
191
+ results.push(...measuredResults);
192
+
193
+ // Generate markdown table
194
+ const lines = [
195
+ "# Export Sizes",
196
+ "",
197
+ "Bundle sizes for `@microsoft/fast-element` exports.",
198
+ "",
199
+ "| Export | Minified | Gzip | Brotli |",
200
+ "|--------|----------|------|--------|",
201
+ ];
202
+
203
+ for (const { name, minified, gzip, brotli } of results) {
204
+ lines.push(
205
+ `| ${name} | ${formatBytes(minified)} | ${formatBytes(gzip)} | ${formatBytes(brotli)} |`,
206
+ );
207
+ }
208
+
209
+ lines.push("");
210
+
211
+ const outputPath = path.resolve(packageRoot, "SIZES.md");
212
+ writeFileSync(outputPath, lines.join("\n"));
213
+ console.log(`Bundle sizes written to ${outputPath}`);
214
+ }
215
+
216
+ main().catch(err => {
217
+ console.error(err);
218
+ process.exit(1);
219
+ });
@@ -1,7 +1,7 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import { fileURLToPath } from 'url';
4
- import { Extractor, ExtractorConfig } from '@microsoft/api-extractor';
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { Extractor, ExtractorConfig } from "@microsoft/api-extractor";
5
5
 
6
6
  const __filename = fileURLToPath(import.meta.url);
7
7
  const __dirname = path.dirname(__filename);
@@ -10,17 +10,37 @@ export const exportPaths = [
10
10
  {
11
11
  path: "./context.js",
12
12
  docsFolder: "context",
13
- configPath: path.resolve(__dirname, "../api-extractor.context.json")
13
+ configPath: path.resolve(__dirname, "../api-extractor.context.json"),
14
+ },
15
+ {
16
+ path: "./declarative.js",
17
+ docsFolder: "declarative",
18
+ configPath: path.resolve(__dirname, "../api-extractor.declarative.json"),
14
19
  },
15
20
  {
16
21
  path: "./di.js",
17
22
  docsFolder: "di",
18
- configPath: path.resolve(__dirname, "../api-extractor.di.json")
19
- }
23
+ configPath: path.resolve(__dirname, "../api-extractor.di.json"),
24
+ },
25
+ {
26
+ path: "./hydration.js",
27
+ docsFolder: "hydration",
28
+ configPath: path.resolve(__dirname, "../api-extractor.hydration.json"),
29
+ },
30
+ {
31
+ path: "./styles.js",
32
+ docsFolder: "styles",
33
+ configPath: path.resolve(__dirname, "../api-extractor.styles.json"),
34
+ },
35
+ {
36
+ path: "./arrays.js",
37
+ docsFolder: "arrays",
38
+ configPath: path.resolve(__dirname, "../api-extractor.arrays.json"),
39
+ },
20
40
  ];
21
41
 
22
- (function() {
23
- exportPaths.forEach((exportPath) => {
42
+ (function () {
43
+ exportPaths.forEach(exportPath => {
24
44
  const docsFolder = path.resolve(__dirname, `../docs/${exportPath.docsFolder}`);
25
45
  // Create folders in the docs directory
26
46
  if (!fs.existsSync(docsFolder)) {
@@ -31,20 +51,19 @@ export const exportPaths = [
31
51
  const extractorConfig = ExtractorConfig.loadFileAndPrepare(exportPath.configPath);
32
52
 
33
53
  // Invoke API Extractor
34
- const extractorResult = Extractor.invoke(
35
- extractorConfig,
36
- {
37
- // Equivalent to the "--local" command-line parameter
38
- localBuild: process.argv[2] !== "ci",
39
- }
40
- );
54
+ const extractorResult = Extractor.invoke(extractorConfig, {
55
+ // Equivalent to the "--local" command-line parameter
56
+ localBuild: process.argv[2] !== "ci",
57
+ });
41
58
 
42
59
  if (extractorResult.succeeded) {
43
- console.log(`API Extractor completed successfully for ${exportPath.configPath}`);
44
- } else {
60
+ console.log(
61
+ `API Extractor completed successfully for ${exportPath.configPath}`,
62
+ );
63
+ } else {
45
64
  console.error(
46
- `API Extractor completed with ${extractorResult.errorCount} errors` +
47
- ` and ${extractorResult.warningCount} warnings for ${exportPath.configPath}`
65
+ `API Extractor completed with ${extractorResult.errorCount} errors` +
66
+ ` and ${extractorResult.warningCount} warnings for ${exportPath.configPath}`,
48
67
  );
49
68
  }
50
69
  });
@@ -0,0 +1,72 @@
1
+ # Fixtures
2
+
3
+ Fixtures are organized into category subdirectories:
4
+
5
+ | Category | Description |
6
+ |---|---|
7
+ | [`bindings/`](./bindings/README.md) | Various binding types (attribute, content, event, dot-syntax, host). |
8
+ | [`scenarios/`](./scenarios/README.md) | Complex scenarios with edge cases involving multiple features. |
9
+ | [`directives/`](./directives/README.md) | Attribute and element directives (`f-repeat`, `f-when`, `f-ref`, etc.). |
10
+ | [`extensions/`](./extensions/README.md) | Additional functionality that augments the core behavior (attribute maps, observer maps). |
11
+ | [`ecosystem/`](./ecosystem/README.md) | Other ecosystem APIs (errors, lifecycle, performance). |
12
+
13
+ Each fixture contains the following contents:
14
+
15
+ ```
16
+ <category>/<fixture-name>/
17
+ ├── <fixture-name>.spec.ts - Playwright tests
18
+ ├── entry.html - Entry template with root custom elements (input to build-fixtures.js)
19
+ ├── fast-build.config.json - Build configuration for @microsoft/fast-build
20
+ ├── index.html - Pre-rendered HTML page served by Vite (generated by build-fixtures.js)
21
+ ├── main.ts - Component definition and setup
22
+ ├── state.json - Initial state passed to the server-side renderer
23
+ └── templates.html - Declarative f-template definitions
24
+ ```
25
+
26
+ Fixtures are auto-discovered by the Vite config in `../vite.config.ts`. To add a new fixture, create a new directory within the appropriate category with the files above — no other changes are needed.
27
+
28
+ ## Hydration readiness
29
+
30
+ Fixtures that exercise prerendered Declarative Shadow DOM should enable
31
+ hydration explicitly and set a global flag in the `hydrationComplete()` callback
32
+ so Playwright tests can wait for the element to be fully interactive before
33
+ asserting. The standard pattern is:
34
+
35
+ 1. **In `main.ts`**, call `enableHydration()` before elements connect:
36
+
37
+ ```ts
38
+ import { enableHydration } from "@microsoft/fast-element/hydration.js";
39
+
40
+ enableHydration({
41
+ hydrationComplete() {
42
+ (window as any).hydrationCompleted = true;
43
+ },
44
+ });
45
+ ```
46
+
47
+ Per-element callbacks belong on `declarativeTemplate(callbacks)`. Map
48
+ behavior belongs in define extensions such as `attributeMap()` and
49
+ `observerMap()`.
50
+
51
+ 2. **In the spec file**, create the wait **before** navigation so the listener is registered before the page starts loading:
52
+
53
+ ```ts
54
+ const hydrationCompleted = page.waitForFunction(
55
+ () => (window as any).hydrationCompleted === true,
56
+ );
57
+ await page.goto("/fixtures/<fixture-name>/");
58
+ await hydrationCompleted;
59
+ ```
60
+
61
+ Place this around every `page.goto()` call — including inside `test.beforeEach` hooks — to prevent assertions from running before hydration finishes.
62
+
63
+ ## Entry HTML attribute guidelines
64
+
65
+ The `entry.html` file defines the root custom elements that the server-side renderer processes. When writing attribute bindings on root elements, follow these conventions:
66
+
67
+ - **Same-name complex bindings are unnecessary.** Attributes like `list="{{list}}"` where `list` is an array or object are redundant — non-primitive values are automatically stripped from the rendered opening tag, and state propagation already provides the value to the element's template. Omit these attributes.
68
+ - **Rename bindings should be avoided.** Prefer naming state properties to match the attribute name the element expects. When a rename such as `foo="{{bar}}"` is removed, ensure `state.json` includes a property named `foo` with the contents of `bar` so the value is available via state propagation.
69
+ - **Property bindings (`:attr`) for renames are acceptable** when multiple instances of the same element in a fixture need different values for the same property (e.g., `:items="{{emptyItems}}"` and `:items="{{singleItem}}"` on separate elements).
70
+ - **Primitive same-name bindings are fine.** Attributes like `type="{{type}}"` where the value is a string, number, or boolean are rendered onto the opening tag and may be needed for `@attr`-based element initialization.
71
+
72
+ For a complete guide on creating fixtures — including how to write each file, component patterns, and spec examples — see [WRITING_FIXTURES.md](./WRITING_FIXTURES.md).