@gyral/core 0.3.0 → 0.3.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 (381) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/NOTICE +19 -0
  3. package/README.md +30 -5
  4. package/dist/call.d.ts +32 -0
  5. package/dist/call.js +63 -0
  6. package/dist/call.js.map +1 -0
  7. package/dist/children.d.ts +25 -3
  8. package/dist/children.js +18 -1
  9. package/dist/children.js.map +1 -1
  10. package/dist/cli/gyral-types.d.ts +2 -0
  11. package/dist/cli/gyral-types.js +23 -0
  12. package/dist/cli/gyral-types.js.map +1 -0
  13. package/dist/command.d.ts +54 -12
  14. package/dist/command.js +25 -11
  15. package/dist/command.js.map +1 -1
  16. package/dist/compiler/ast.d.ts +2 -0
  17. package/dist/compiler/ast.js +5 -1
  18. package/dist/compiler/ast.js.map +1 -1
  19. package/dist/compiler/compile.d.ts +5 -2
  20. package/dist/compiler/compile.js +36 -17
  21. package/dist/compiler/compile.js.map +1 -1
  22. package/dist/compiler/component-entries.d.ts +7 -0
  23. package/dist/compiler/component-entries.js +42 -0
  24. package/dist/compiler/component-entries.js.map +1 -0
  25. package/dist/compiler/components.d.ts +4 -0
  26. package/dist/compiler/components.js +182 -0
  27. package/dist/compiler/components.js.map +1 -0
  28. package/dist/compiler/facts.d.ts +31 -0
  29. package/dist/compiler/facts.js +285 -0
  30. package/dist/compiler/facts.js.map +1 -0
  31. package/dist/compiler/features.d.ts +34 -0
  32. package/dist/compiler/features.js +158 -0
  33. package/dist/compiler/features.js.map +1 -0
  34. package/dist/compiler/hooks.d.ts +4 -0
  35. package/dist/compiler/hooks.js +144 -89
  36. package/dist/compiler/hooks.js.map +1 -1
  37. package/dist/compiler/loader-source.d.ts +14 -0
  38. package/dist/compiler/loader-source.js +86 -0
  39. package/dist/compiler/loader-source.js.map +1 -0
  40. package/dist/compiler/locate.d.ts +14 -0
  41. package/dist/compiler/locate.js +78 -0
  42. package/dist/compiler/locate.js.map +1 -0
  43. package/dist/compiler/parse5-check.d.ts +1 -1
  44. package/dist/compiler/parse5-check.js +8 -3
  45. package/dist/compiler/parse5-check.js.map +1 -1
  46. package/dist/compiler/prop-schemas.d.ts +4 -0
  47. package/dist/compiler/prop-schemas.js +108 -0
  48. package/dist/compiler/prop-schemas.js.map +1 -0
  49. package/dist/compiler/scan.d.ts +13 -3
  50. package/dist/compiler/scan.js +21 -13
  51. package/dist/compiler/scan.js.map +1 -1
  52. package/dist/compiler/scope.d.ts +8 -0
  53. package/dist/compiler/scope.js +116 -0
  54. package/dist/compiler/scope.js.map +1 -0
  55. package/dist/compiler/tag-map.d.ts +41 -0
  56. package/dist/compiler/tag-map.js +126 -0
  57. package/dist/compiler/tag-map.js.map +1 -0
  58. package/dist/components-ready.d.ts +7 -0
  59. package/dist/components-ready.js +19 -0
  60. package/dist/components-ready.js.map +1 -0
  61. package/dist/define.d.ts +13 -4
  62. package/dist/define.js +8 -12
  63. package/dist/define.js.map +1 -1
  64. package/dist/devtools-events.d.ts +8 -0
  65. package/dist/devtools-events.js.map +1 -1
  66. package/dist/devtools-off.d.ts +1 -0
  67. package/dist/devtools-off.js +1 -0
  68. package/dist/devtools-off.js.map +1 -1
  69. package/dist/devtools.d.ts +3 -0
  70. package/dist/devtools.js +14 -0
  71. package/dist/devtools.js.map +1 -1
  72. package/dist/drivers-scope.js.map +1 -1
  73. package/dist/element-internals.d.ts +1 -0
  74. package/dist/element-internals.js +11 -0
  75. package/dist/element-internals.js.map +1 -0
  76. package/dist/element-types.d.ts +6 -2
  77. package/dist/element-types.js.map +1 -1
  78. package/dist/element.js +80 -27
  79. package/dist/element.js.map +1 -1
  80. package/dist/error-view.d.ts +10 -0
  81. package/dist/error-view.js +30 -0
  82. package/dist/error-view.js.map +1 -0
  83. package/dist/errors.d.ts +21 -0
  84. package/dist/errors.js +47 -0
  85. package/dist/errors.js.map +1 -0
  86. package/dist/errors.json +289 -0
  87. package/dist/eslint/imports.d.ts +2 -0
  88. package/dist/eslint/imports.js +2 -2
  89. package/dist/eslint/imports.js.map +1 -1
  90. package/dist/eslint/index.d.ts +2 -1
  91. package/dist/eslint/index.js +11 -3
  92. package/dist/eslint/index.js.map +1 -1
  93. package/dist/eslint/template-rule.d.ts +1 -0
  94. package/dist/eslint/template-rule.js +15 -5
  95. package/dist/eslint/template-rule.js.map +1 -1
  96. package/dist/eslint/unused-intent-rule.d.ts +2 -0
  97. package/dist/eslint/unused-intent-rule.js +186 -0
  98. package/dist/eslint/unused-intent-rule.js.map +1 -0
  99. package/dist/features.d.ts +2 -2
  100. package/dist/features.js.map +1 -1
  101. package/dist/focus.d.ts +6 -0
  102. package/dist/focus.js +32 -2
  103. package/dist/focus.js.map +1 -1
  104. package/dist/form-associated.d.ts +46 -0
  105. package/dist/form-associated.js +77 -0
  106. package/dist/form-associated.js.map +1 -0
  107. package/dist/forms.d.ts +42 -30
  108. package/dist/forms.js +85 -41
  109. package/dist/forms.js.map +1 -1
  110. package/dist/frame-lane.d.ts +11 -0
  111. package/dist/frame-lane.js +28 -0
  112. package/dist/frame-lane.js.map +1 -0
  113. package/dist/head.d.ts +61 -0
  114. package/dist/head.js +85 -0
  115. package/dist/head.js.map +1 -0
  116. package/dist/host-model.d.ts +34 -5
  117. package/dist/host-model.js +146 -23
  118. package/dist/host-model.js.map +1 -1
  119. package/dist/hydration-client.js +2 -2
  120. package/dist/hydration-client.js.map +1 -1
  121. package/dist/hydration-loader.d.ts +13 -0
  122. package/dist/hydration-loader.js +58 -0
  123. package/dist/hydration-loader.js.map +1 -0
  124. package/dist/hydration-off.d.ts +8 -0
  125. package/dist/hydration-off.js +25 -0
  126. package/dist/hydration-off.js.map +1 -0
  127. package/dist/hydration.d.ts +0 -12
  128. package/dist/hydration.js +3 -52
  129. package/dist/hydration.js.map +1 -1
  130. package/dist/index.d.ts +20 -24
  131. package/dist/index.js +10 -19
  132. package/dist/index.js.map +1 -1
  133. package/dist/indexeddb-types.d.ts +97 -0
  134. package/dist/indexeddb-types.js +2 -0
  135. package/dist/indexeddb-types.js.map +1 -0
  136. package/dist/indexeddb.d.ts +5 -0
  137. package/dist/indexeddb.js +118 -0
  138. package/dist/indexeddb.js.map +1 -0
  139. package/dist/intent.d.ts +41 -19
  140. package/dist/intent.js +144 -106
  141. package/dist/intent.js.map +1 -1
  142. package/dist/internal/indexeddb-open.d.ts +8 -0
  143. package/dist/internal/indexeddb-open.js +96 -0
  144. package/dist/internal/indexeddb-open.js.map +1 -0
  145. package/dist/internal/indexeddb-transaction.d.ts +4 -0
  146. package/dist/internal/indexeddb-transaction.js +134 -0
  147. package/dist/internal/indexeddb-transaction.js.map +1 -0
  148. package/dist/internal/interpreter.d.ts +12 -4
  149. package/dist/internal/interpreter.js +36 -48
  150. package/dist/internal/interpreter.js.map +1 -1
  151. package/dist/internal/token.d.ts +1 -0
  152. package/dist/internal/token.js +7 -0
  153. package/dist/internal/token.js.map +1 -0
  154. package/dist/internal.d.ts +20 -0
  155. package/dist/internal.js +22 -0
  156. package/dist/internal.js.map +1 -0
  157. package/dist/invoker-fallback-used.d.ts +4 -0
  158. package/dist/invoker-fallback-used.js +10 -0
  159. package/dist/invoker-fallback-used.js.map +1 -0
  160. package/dist/invoker-fallback.d.ts +4 -0
  161. package/dist/invoker-fallback.js +15 -0
  162. package/dist/invoker-fallback.js.map +1 -0
  163. package/dist/light-dom.d.ts +1 -1
  164. package/dist/light-dom.js.map +1 -1
  165. package/dist/prop.d.ts +30 -7
  166. package/dist/prop.js +19 -0
  167. package/dist/prop.js.map +1 -1
  168. package/dist/props.d.ts +8 -3
  169. package/dist/props.js +22 -15
  170. package/dist/props.js.map +1 -1
  171. package/dist/retry.d.ts +15 -0
  172. package/dist/retry.js +48 -0
  173. package/dist/retry.js.map +1 -0
  174. package/dist/scheduler.d.ts +7 -3
  175. package/dist/scheduler.js +38 -56
  176. package/dist/scheduler.js.map +1 -1
  177. package/dist/script-json.d.ts +2 -0
  178. package/dist/script-json.js +13 -0
  179. package/dist/script-json.js.map +1 -0
  180. package/dist/server-component.d.ts +1 -1
  181. package/dist/server-component.js +45 -14
  182. package/dist/server-component.js.map +1 -1
  183. package/dist/server.d.ts +8 -2
  184. package/dist/server.js +11 -2
  185. package/dist/server.js.map +1 -1
  186. package/dist/settled.d.ts +4 -3
  187. package/dist/settled.js +48 -26
  188. package/dist/settled.js.map +1 -1
  189. package/dist/spec-features-used.d.ts +21 -0
  190. package/dist/spec-features-used.js +50 -0
  191. package/dist/spec-features-used.js.map +1 -0
  192. package/dist/spec-features.d.ts +28 -0
  193. package/dist/spec-features.js +42 -0
  194. package/dist/spec-features.js.map +1 -0
  195. package/dist/states.d.ts +3 -5
  196. package/dist/states.js +22 -8
  197. package/dist/states.js.map +1 -1
  198. package/dist/storage.d.ts +69 -0
  199. package/dist/storage.js +158 -0
  200. package/dist/storage.js.map +1 -0
  201. package/dist/store-binding.js +2 -2
  202. package/dist/store-binding.js.map +1 -1
  203. package/dist/store-scope.d.ts +2 -4
  204. package/dist/store-scope.js +6 -16
  205. package/dist/store-scope.js.map +1 -1
  206. package/dist/store.js +36 -8
  207. package/dist/store.js.map +1 -1
  208. package/dist/subscription.d.ts +35 -0
  209. package/dist/subscription.js +74 -0
  210. package/dist/subscription.js.map +1 -0
  211. package/dist/subscriptions.d.ts +26 -0
  212. package/dist/subscriptions.js +98 -0
  213. package/dist/subscriptions.js.map +1 -0
  214. package/dist/transitions.d.ts +4 -5
  215. package/dist/transitions.js +10 -4
  216. package/dist/transitions.js.map +1 -1
  217. package/dist/types.d.ts +124 -27
  218. package/dist/types.js.map +1 -1
  219. package/dist/use-dialogs.d.ts +2 -0
  220. package/dist/use-dialogs.js +9 -0
  221. package/dist/use-dialogs.js.map +1 -0
  222. package/dist/use-disposal.d.ts +2 -0
  223. package/dist/use-disposal.js +9 -0
  224. package/dist/use-disposal.js.map +1 -0
  225. package/dist/use-error-view.d.ts +2 -0
  226. package/dist/use-error-view.js +9 -0
  227. package/dist/use-error-view.js.map +1 -0
  228. package/dist/use-errored.d.ts +2 -0
  229. package/dist/use-errored.js +9 -0
  230. package/dist/use-errored.js.map +1 -0
  231. package/dist/use-form-associated.d.ts +2 -0
  232. package/dist/use-form-associated.js +7 -0
  233. package/dist/use-form-associated.js.map +1 -0
  234. package/dist/use-frame-lane.d.ts +2 -0
  235. package/dist/use-frame-lane.js +10 -0
  236. package/dist/use-frame-lane.js.map +1 -0
  237. package/dist/use-invokers.d.ts +2 -0
  238. package/dist/use-invokers.js +10 -0
  239. package/dist/use-invokers.js.map +1 -0
  240. package/dist/use-states.d.ts +2 -0
  241. package/dist/use-states.js +10 -0
  242. package/dist/use-states.js.map +1 -0
  243. package/dist/use-subscriptions.d.ts +2 -0
  244. package/dist/use-subscriptions.js +10 -0
  245. package/dist/use-subscriptions.js.map +1 -0
  246. package/dist/use-transitions.d.ts +2 -0
  247. package/dist/use-transitions.js +10 -0
  248. package/dist/use-transitions.js.map +1 -0
  249. package/dist/view/attributes.d.ts +5 -0
  250. package/dist/view/attributes.js +5 -0
  251. package/dist/view/attributes.js.map +1 -1
  252. package/dist/view/compiled.d.ts +1 -1
  253. package/dist/view/compiled.js +1 -1
  254. package/dist/view/compiled.js.map +1 -1
  255. package/dist/view/index.d.ts +9 -4
  256. package/dist/view/index.js +9 -3
  257. package/dist/view/index.js.map +1 -1
  258. package/dist/view/loc.d.ts +9 -0
  259. package/dist/view/loc.js +50 -0
  260. package/dist/view/loc.js.map +1 -0
  261. package/dist/view/message.d.ts +17 -0
  262. package/dist/view/message.js +40 -0
  263. package/dist/view/message.js.map +1 -0
  264. package/dist/view/messages.d.ts +43 -0
  265. package/dist/view/messages.js +84 -0
  266. package/dist/view/messages.js.map +1 -0
  267. package/dist/view/normalize/check.d.ts +5 -2
  268. package/dist/view/normalize/check.js +6 -3
  269. package/dist/view/normalize/check.js.map +1 -1
  270. package/dist/view/normalize/emit.d.ts +2 -0
  271. package/dist/view/normalize/emit.js +40 -11
  272. package/dist/view/normalize/emit.js.map +1 -1
  273. package/dist/view/normalize/errors.js +1 -1
  274. package/dist/view/normalize/errors.js.map +1 -1
  275. package/dist/view/normalize/id.d.ts +5 -2
  276. package/dist/view/normalize/id.js +9 -2
  277. package/dist/view/normalize/id.js.map +1 -1
  278. package/dist/view/normalize/normalize.d.ts +7 -4
  279. package/dist/view/normalize/normalize.js +15 -9
  280. package/dist/view/normalize/normalize.js.map +1 -1
  281. package/dist/view/normalize/rules.d.ts +9 -0
  282. package/dist/view/normalize/rules.js +35 -5
  283. package/dist/view/normalize/rules.js.map +1 -1
  284. package/dist/view/normalize/svg.d.ts +14 -0
  285. package/dist/view/normalize/svg.js +40 -0
  286. package/dist/view/normalize/svg.js.map +1 -0
  287. package/dist/view/normalize/tokens.d.ts +5 -1
  288. package/dist/view/normalize/tokens.js.map +1 -1
  289. package/dist/view/normalize/tree.d.ts +8 -2
  290. package/dist/view/normalize/tree.js +58 -13
  291. package/dist/view/normalize/tree.js.map +1 -1
  292. package/dist/view/normalize/types.d.ts +24 -4
  293. package/dist/view/normalize/types.js +4 -1
  294. package/dist/view/normalize/types.js.map +1 -1
  295. package/dist/view/normalize/whitespace.js +4 -3
  296. package/dist/view/normalize/whitespace.js.map +1 -1
  297. package/dist/view/prepare-stub.js +2 -2
  298. package/dist/view/prepare-stub.js.map +1 -1
  299. package/dist/view/prepare.d.ts +5 -2
  300. package/dist/view/prepare.js +9 -3
  301. package/dist/view/prepare.js.map +1 -1
  302. package/dist/view/registry.d.ts +15 -0
  303. package/dist/view/registry.js +10 -2
  304. package/dist/view/registry.js.map +1 -1
  305. package/dist/view/render/adopt-attr.js +7 -1
  306. package/dist/view/render/adopt-attr.js.map +1 -1
  307. package/dist/view/render/adopt.js +21 -4
  308. package/dist/view/render/adopt.js.map +1 -1
  309. package/dist/view/render/attr-parts.d.ts +12 -1
  310. package/dist/view/render/attr-parts.js +38 -5
  311. package/dist/view/render/attr-parts.js.map +1 -1
  312. package/dist/view/render/child-part.js +5 -2
  313. package/dist/view/render/child-part.js.map +1 -1
  314. package/dist/view/render/dispose.d.ts +3 -0
  315. package/dist/view/render/dispose.js +76 -0
  316. package/dist/view/render/dispose.js.map +1 -0
  317. package/dist/view/render/hook-part.d.ts +3 -1
  318. package/dist/view/render/hook-part.js +21 -5
  319. package/dist/view/render/hook-part.js.map +1 -1
  320. package/dist/view/render/hook-run.d.ts +2 -0
  321. package/dist/view/render/hook-run.js +72 -0
  322. package/dist/view/render/hook-run.js.map +1 -0
  323. package/dist/view/render/hooks.d.ts +73 -5
  324. package/dist/view/render/hooks.js +79 -17
  325. package/dist/view/render/hooks.js.map +1 -1
  326. package/dist/view/render/hydrate.js +2 -2
  327. package/dist/view/render/hydrate.js.map +1 -1
  328. package/dist/view/render/keyed.d.ts +6 -0
  329. package/dist/view/render/keyed.js +15 -0
  330. package/dist/view/render/keyed.js.map +1 -0
  331. package/dist/view/render/mismatch.js +5 -3
  332. package/dist/view/render/mismatch.js.map +1 -1
  333. package/dist/view/render/modal.d.ts +3 -0
  334. package/dist/view/render/modal.js +39 -0
  335. package/dist/view/render/modal.js.map +1 -0
  336. package/dist/view/render/raw.d.ts +4 -1
  337. package/dist/view/render/raw.js +6 -2
  338. package/dist/view/render/raw.js.map +1 -1
  339. package/dist/view/render/render.d.ts +4 -1
  340. package/dist/view/render/render.js +17 -12
  341. package/dist/view/render/render.js.map +1 -1
  342. package/dist/view/render/toggles.d.ts +6 -0
  343. package/dist/view/render/toggles.js +6 -0
  344. package/dist/view/render/toggles.js.map +1 -0
  345. package/dist/view/render/warn.d.ts +14 -0
  346. package/dist/view/render/warn.js +45 -0
  347. package/dist/view/render/warn.js.map +1 -1
  348. package/dist/view/server/component.d.ts +6 -3
  349. package/dist/view/server/component.js +29 -7
  350. package/dist/view/server/component.js.map +1 -1
  351. package/dist/view/server/index.d.ts +20 -0
  352. package/dist/view/server/index.js +12 -4
  353. package/dist/view/server/index.js.map +1 -1
  354. package/dist/view/server/tags.d.ts +13 -2
  355. package/dist/view/server/tags.js +13 -2
  356. package/dist/view/server/tags.js.map +1 -1
  357. package/dist/view/server/values.d.ts +2 -0
  358. package/dist/view/server/values.js +8 -0
  359. package/dist/view/server/values.js.map +1 -1
  360. package/dist/view/server/writer.d.ts +9 -4
  361. package/dist/view/server/writer.js +33 -18
  362. package/dist/view/server/writer.js.map +1 -1
  363. package/dist/view/template-element.d.ts +7 -0
  364. package/dist/view/template-element.js +25 -5
  365. package/dist/view/template-element.js.map +1 -1
  366. package/dist/view/template.d.ts +10 -2
  367. package/dist/view/template.js +62 -8
  368. package/dist/view/template.js.map +1 -1
  369. package/dist/view/trusted-html.d.ts +6 -0
  370. package/dist/view/trusted-html.js +34 -0
  371. package/dist/view/trusted-html.js.map +1 -0
  372. package/dist/vite.d.ts +38 -31
  373. package/dist/vite.js +198 -31
  374. package/dist/vite.js.map +1 -1
  375. package/package.json +34 -3
  376. package/dist/hooks/labelled-by.d.ts +0 -13
  377. package/dist/hooks/labelled-by.js +0 -51
  378. package/dist/hooks/labelled-by.js.map +0 -1
  379. package/dist/random.d.ts +0 -13
  380. package/dist/random.js +0 -19
  381. package/dist/random.js.map +0 -1
@@ -3,23 +3,39 @@
3
3
  // `vite build` hook, so the preset stays loadable from any vite.config.ts:
4
4
  // - `transform` (the shell is enforce: 'pre', so positions are the author's own source,
5
5
  // TypeScript included, parsed by Rolldown's `this.parse`): rewrites each `html` call site
6
- // of the configured sources into `compiled(<hoisted template object>, [values…])`,
7
- // dependencies in node_modules included. Template rule violations, and references it
8
- // can't follow, fail the build with a code frame.
9
- // - `generateBundle`: fails when an uncompiled `html` survived in a chunk anyway (re-exports
10
- // across modules, dynamic imports): the guarantee that the `#prepare` stub is never hit.
6
+ // of the configured sources into `compiled(<hoisted template object>, [values…])` (and
7
+ // each `svg` one into `compiledSvg(…)`), dependencies in node_modules included. Template
8
+ // rule violations, and references it can't follow, fail the build with a code frame.
9
+ // - `generateBundle`: fails when an uncompiled `html` or `svg` survived in a chunk anyway
10
+ // (re-exports across modules, dynamic imports): the guarantee that the `#prepare` stub is
11
+ // never hit.
12
+ // - In client environments, `transform` also adds the registration of view transitions, the
13
+ // frame lane and custom states to every module that names their spec field, among the
14
+ // modules that can affect Gyral components (the app's own, and packages that reach a Gyral
15
+ // package), so the `gyral-compiled` condition can leave them out otherwise (features.ts,
16
+ // gyral-c5d.12); `resolveId` and `load` serve those registration modules.
11
17
  import { join, relative } from 'node:path';
12
- import { fileURLToPath } from 'node:url';
18
+ import { langOf } from './ast.js';
13
19
  import { CompileError, compileModule } from './compile.js';
20
+ import { createFeatures } from './features.js';
21
+ import { stripPropSchemas } from './prop-schemas.js';
14
22
  import { loadParse5 } from './parse5-check.js';
23
+ import { VIEW } from './locate.js';
15
24
  import { findUses, htmlImports } from './scan.js';
16
- import { Lines } from './source.js';
17
- /** Core's own view modules: always sources (core-internal templates, tests). */
18
- const VIEW = ['index', 'template'].flatMap((name) => ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))));
19
- /** template.ts's symbol description: the module defining `html`, in any copy of core. */
25
+ import { Lines, splice } from './source.js';
26
+ /** Appends `imports` after the module's last line: no code moves, so source maps hold. */
27
+ const appended = (result, code, imports) => imports === ''
28
+ ? result
29
+ : result === null
30
+ ? { code: code + imports, map: null }
31
+ : { ...result, code: result.code + imports };
32
+ /** Modules naming a template tag, or calling `.value(`: only those can need compiling. */
33
+ const NAMES_TAG = /html|svg|\.value\s*\(/;
34
+ /** A `prop.value(…)` call may be in the module (prop-schemas.ts). */
35
+ const PROP_VALUE = /\.value\s*\(/;
36
+ /** template.ts's symbol description: the module defining `html` and `svg`, in any copy of core. */
20
37
  const DEFINES_HTML = /Symbol\(['"]gyral\.template['"]\)/;
21
38
  const SCRIPT = /\.[cm]?[jt]sx?$/;
22
- const langOf = (id) => /\.[cm]?tsx$/.test(id) ? 'tsx' : /\.[cm]?ts$/.test(id) ? 'ts' : id.endsWith('x') ? 'jsx' : 'js';
23
39
  /**
24
40
  * Whether an environment resolves core's `#view-dev` to its development module: the
25
41
  * `development` condition is listed, or Vite's `development|production` placeholder is and
@@ -31,12 +47,24 @@ function developmentBuild(config) {
31
47
  (!config.isProduction && conditions.includes('development|production')));
32
48
  }
33
49
  const CANT_FOLLOW = `This use of html can't be compiled: the template compiler rewrites only html\`…\` tagged ` +
34
- `templates whose tag is the imported html itself (or ns.html for import * as ns). ` +
50
+ `templates whose tag is the imported html itself (or ns.html for import * as ns; the same ` +
51
+ `for svg). ` +
35
52
  `Aliasing it, passing it around or calling it as a function would reach the runtime ` +
36
53
  `template preparer, which builds made with the Gyral preset leave out. Write html\`…\` at ` +
37
54
  `the call site (view/01-templates.md "Compiled").`;
38
55
  export function createCompiler(settings) {
39
56
  const { sources } = settings;
57
+ const features = createFeatures([
58
+ 'transitions',
59
+ 'frame',
60
+ 'states',
61
+ 'forms',
62
+ 'subscriptions',
63
+ 'dialogs',
64
+ 'disposal',
65
+ 'errorView',
66
+ 'errored',
67
+ ], settings.ownEntry);
40
68
  /** Per environment (client, ssr, …): Vite may build them with one plugin instance. */
41
69
  const states = new WeakMap();
42
70
  const global = {};
@@ -68,6 +96,89 @@ export function createCompiler(settings) {
68
96
  }
69
97
  return parse5.module;
70
98
  }
99
+ /** Compiles the module's template call sites (the transform proper). */
100
+ async function compile(code, id, opts) {
101
+ if (id.startsWith('\0') || !SCRIPT.test(id))
102
+ return null;
103
+ const env = this.environment;
104
+ const state = stateOf(env);
105
+ if (DEFINES_HTML.test(code))
106
+ state.definers.add(id);
107
+ if (state.sourceIds.has(id))
108
+ return null;
109
+ let program;
110
+ try {
111
+ program = this.parse(code, { lang: langOf(id) });
112
+ }
113
+ catch {
114
+ return null; // not ours to report: the bundler fails on it with a better message
115
+ }
116
+ const lines = new Lines(code);
117
+ const file = relative(root, id);
118
+ // Production client builds: property-only prop checks can't run (prop-schemas.ts).
119
+ const client = opts?.ssr !== true && env.config.consumer === 'client';
120
+ const extra = client && !developmentBuild(env.config) && PROP_VALUE.test(code)
121
+ ? stripPropSchemas(program, code)
122
+ : [];
123
+ const unchanged = () => (extra.length === 0 ? null : splice(lines, extra, id));
124
+ const { imports, reexports } = htmlImports(program);
125
+ if (imports.length === 0 && reexports.length === 0)
126
+ return unchanged();
127
+ const isSource = async (specifier) => {
128
+ if (sources.includes(specifier))
129
+ return true;
130
+ const resolved = await this.resolve(specifier, id);
131
+ return resolved !== null && state.sourceIds.has(resolved.id);
132
+ };
133
+ const where = (node) => `${file}:${String(lines.position(node.start).line)}`;
134
+ for (const r of reexports) {
135
+ if (!(await isSource(r.specifier)))
136
+ continue;
137
+ this.warn(`${where(r.node)}: re-exporting html or svg. The template compiler only follows the ` +
138
+ `tags imported straight from ${r.specifier}: templates written with this re-export ` +
139
+ `stay uncompiled and fail the build. Import them from ${r.specifier} instead.`);
140
+ }
141
+ const matched = [];
142
+ for (const binding of imports)
143
+ if (await isSource(binding.specifier))
144
+ matched.push(binding);
145
+ if (matched.length === 0)
146
+ return unchanged();
147
+ const fail = (start, end, message) => this.error({
148
+ message,
149
+ id,
150
+ loc: { file: id, ...lines.position(start) },
151
+ frame: lines.frame(start, end),
152
+ });
153
+ const { sites, leftovers } = findUses(program, matched);
154
+ for (const l of leftovers) {
155
+ if (l.exported)
156
+ this.warn(`${where(l.node)}: re-exporting a template tag; its users stay uncompiled.`);
157
+ else
158
+ fail(l.node.start, l.node.end, CANT_FOLLOW);
159
+ }
160
+ if (sites.length === 0)
161
+ return unchanged();
162
+ const ssr = opts?.ssr === true || env.config.consumer === 'server';
163
+ try {
164
+ return compileModule({
165
+ lines,
166
+ id,
167
+ file,
168
+ sites,
169
+ ssr,
170
+ ids: ssr || developmentBuild(env.config),
171
+ parse5: loadOnce(),
172
+ extra,
173
+ seen: state.seen,
174
+ });
175
+ }
176
+ catch (error) {
177
+ if (error instanceof CompileError)
178
+ return fail(error.start, error.end, error.message);
179
+ throw error;
180
+ }
181
+ }
71
182
  return {
72
183
  configResolved(config) {
73
184
  root = config.root;
@@ -84,82 +195,25 @@ export function createCompiler(settings) {
84
195
  if (resolved !== null)
85
196
  state.sourceIds.add(resolved.id);
86
197
  }
198
+ await features.buildStart((source, importer) => this.resolve(source, importer), root);
87
199
  loadOnce();
88
200
  },
201
+ resolveId(source) {
202
+ return features.resolveId(source);
203
+ },
204
+ load(id) {
205
+ return features.load(id);
206
+ },
89
207
  async transform(code, id, opts) {
90
- if (id.startsWith('\0') || !SCRIPT.test(id))
91
- return null;
92
- const env = this.environment;
93
- const state = stateOf(env);
94
- if (DEFINES_HTML.test(code))
95
- state.definers.add(id);
96
- if (state.sourceIds.has(id))
97
- return null;
98
- let program;
99
- try {
100
- program = this.parse(code, { lang: langOf(id) });
101
- }
102
- catch {
103
- return null; // not ours to report: the bundler fails on it with a better message
104
- }
105
- const { imports, reexports } = htmlImports(program);
106
- if (imports.length === 0 && reexports.length === 0)
107
- return null;
108
- const isSource = async (specifier) => {
109
- if (sources.includes(specifier))
110
- return true;
111
- const resolved = await this.resolve(specifier, id);
112
- return resolved !== null && state.sourceIds.has(resolved.id);
113
- };
114
- const lines = new Lines(code);
115
- const file = relative(root, id);
116
- const where = (node) => `${file}:${String(lines.position(node.start).line)}`;
117
- for (const r of reexports) {
118
- if (!(await isSource(r.specifier)))
119
- continue;
120
- this.warn(`${where(r.node)}: re-exporting html. The template compiler only follows html ` +
121
- `imported straight from ${r.specifier}: templates written with this re-export stay ` +
122
- `uncompiled and fail the build. Import html from ${r.specifier} instead.`);
123
- }
124
- const matched = [];
125
- for (const binding of imports)
126
- if (await isSource(binding.specifier))
127
- matched.push(binding);
128
- if (matched.length === 0)
129
- return null;
130
- const fail = (start, end, message) => this.error({
131
- message,
132
- id,
133
- loc: { file: id, ...lines.position(start) },
134
- frame: lines.frame(start, end),
135
- });
136
- const { sites, leftovers } = findUses(program, matched);
137
- for (const l of leftovers) {
138
- if (l.exported)
139
- this.warn(`${where(l.node)}: re-exporting html; its users stay uncompiled.`);
140
- else
141
- fail(l.node.start, l.node.end, CANT_FOLLOW);
142
- }
143
- if (sites.length === 0)
144
- return null;
145
- const ssr = opts?.ssr === true || env.config.consumer === 'server';
146
- try {
147
- return compileModule({
148
- lines,
149
- id,
150
- file,
151
- sites,
152
- ssr,
153
- ids: ssr || developmentBuild(env.config),
154
- parse5: loadOnce(),
155
- seen: state.seen,
156
- });
157
- }
158
- catch (error) {
159
- if (error instanceof CompileError)
160
- return fail(error.start, error.end, error.message);
161
- throw error;
162
- }
208
+ const client = opts?.ssr !== true && this.environment.config.consumer === 'client';
209
+ const imports = client
210
+ ? await features.inject(code, id, {
211
+ parse: (text, lang) => this.parse(text, { lang }),
212
+ resolve: (source, importer) => this.resolve(source, importer),
213
+ })
214
+ : '';
215
+ const compiled = NAMES_TAG.test(code) ? await compile.call(this, code, id, opts) : null;
216
+ return appended(compiled, code, imports);
163
217
  },
164
218
  generateBundle(_, bundle) {
165
219
  const state = stateOf(this.environment);
@@ -170,13 +224,14 @@ export function createCompiler(settings) {
170
224
  if (chunk.type !== 'chunk')
171
225
  continue;
172
226
  for (const [id, module] of Object.entries(chunk.modules)) {
173
- if (!state.definers.has(id) || !module.renderedExports.includes('html'))
227
+ const tag = ['html', 'svg'].find((name) => module.renderedExports.includes(name));
228
+ if (!state.definers.has(id) || tag === undefined)
174
229
  continue;
175
- this.error(`gyral: an uncompiled html template remains in ${chunk.fileName}: something uses ` +
176
- `html from ${relative(root, id)} in a way the template compiler can't see (a ` +
230
+ this.error(`gyral: an uncompiled ${tag} template remains in ${chunk.fileName}: something uses ` +
231
+ `${tag} from ${relative(root, id)} in a way the template compiler can't see (a ` +
177
232
  `re-export, a dynamic import, or a module it doesn't process). With the ` +
178
233
  `gyral-compiled condition the runtime preparer is left out, so that template ` +
179
- `would throw. Import html straight from '@gyral/core' where you write templates ` +
234
+ `would throw. Import ${tag} straight from '@gyral/core' where you write templates ` +
180
235
  `(view/01-templates.md "Compiled").`);
181
236
  }
182
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/compiler/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["// The template compiler's build hooks (view/01-templates.md \"Compiled\",\n// view/09-template-rules.md), loaded lazily by the plugin shell in ../vite.ts on the first\n// `vite build` hook, so the preset stays loadable from any vite.config.ts:\n// - `transform` (the shell is enforce: 'pre', so positions are the author's own source,\n// TypeScript included, parsed by Rolldown's `this.parse`): rewrites each `html` call site\n// of the configured sources into `compiled(<hoisted template object>, [values…])`,\n// dependencies in node_modules included. Template rule violations, and references it\n// can't follow, fail the build with a code frame.\n// - `generateBundle`: fails when an uncompiled `html` survived in a chunk anyway (re-exports\n// across modules, dynamic imports): the guarantee that the `#prepare` stub is never hit.\nimport { join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Plugin } from 'vite';\nimport type { Node } from './ast.js';\nimport { CompileError, compileModule } from './compile.js';\nimport { loadParse5, type Parse5 } from './parse5-check.js';\nimport { findUses, htmlImports } from './scan.js';\nimport { Lines } from './source.js';\n\ntype Fn<H> = H extends { handler: infer F } ? F : H;\ntype HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;\n\nexport interface CompilerHooks {\n readonly configResolved: HookFn<'configResolved'>;\n readonly buildStart: HookFn<'buildStart'>;\n readonly transform: HookFn<'transform'>;\n readonly generateBundle: HookFn<'generateBundle'>;\n}\n\nexport interface CompilerSettings {\n readonly sources: readonly string[];\n /** The module to load parse5 from; undefined skips the parse5 check. */\n readonly parse5: string | undefined;\n}\n\n/** Core's own view modules: always sources (core-internal templates, tests). */\nconst VIEW = ['index', 'template'].flatMap((name) =>\n ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))),\n);\n/** template.ts's symbol description: the module defining `html`, in any copy of core. */\nconst DEFINES_HTML = /Symbol\\(['\"]gyral\\.template['\"]\\)/;\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\ninterface BuildState {\n readonly sourceIds: Set<string>;\n readonly definers: Set<string>;\n readonly seen: Map<string, { readonly strings: string; readonly loc: string }>;\n}\n\nconst langOf = (id: string): 'js' | 'jsx' | 'ts' | 'tsx' =>\n /\\.[cm]?tsx$/.test(id) ? 'tsx' : /\\.[cm]?ts$/.test(id) ? 'ts' : id.endsWith('x') ? 'jsx' : 'js';\n\n/**\n * Whether an environment resolves core's `#view-dev` to its development module: the\n * `development` condition is listed, or Vite's `development|production` placeholder is and\n * the build isn't a production one. Exactly then the client keeps template ids (01).\n */\nfunction developmentBuild(config: {\n readonly isProduction: boolean;\n readonly resolve: { readonly conditions: readonly string[] };\n}): boolean {\n const { conditions } = config.resolve;\n return (\n conditions.includes('development') ||\n (!config.isProduction && conditions.includes('development|production'))\n );\n}\n\nconst CANT_FOLLOW =\n `This use of html can't be compiled: the template compiler rewrites only html\\`…\\` tagged ` +\n `templates whose tag is the imported html itself (or ns.html for import * as ns). ` +\n `Aliasing it, passing it around or calling it as a function would reach the runtime ` +\n `template preparer, which builds made with the Gyral preset leave out. Write html\\`…\\` at ` +\n `the call site (view/01-templates.md \"Compiled\").`;\n\nexport function createCompiler(settings: CompilerSettings): CompilerHooks {\n const { sources } = settings;\n /** Per environment (client, ssr, …): Vite may build them with one plugin instance. */\n const states = new WeakMap<object, BuildState>();\n const global = {};\n let root = '';\n let treeshake = true;\n let info: (message: string) => void = () => undefined;\n let parse5: { readonly module: Parse5 | undefined } | undefined;\n\n function stateOf(env: object | undefined): BuildState {\n const key = env ?? global;\n let state = states.get(key);\n if (state === undefined) {\n state = { sourceIds: new Set(VIEW), definers: new Set(), seen: new Map() };\n states.set(key, state);\n }\n return state;\n }\n\n /** parse5, loaded once; the notice when it is missing is printed once too. */\n function loadOnce(): Parse5 | undefined {\n const specifier = settings.parse5;\n if (specifier === undefined) return undefined;\n if (parse5 === undefined) {\n parse5 = { module: loadParse5(specifier, root) };\n if (parse5.module === undefined) {\n info(\n `gyral: ${specifier} is not installed, so the template compiler skips its extra ` +\n `rule 7 check (the normalizer's own check still runs). To get it: pnpm add -D ` +\n `parse5 (view/09-template-rules.md).`,\n );\n }\n }\n return parse5.module;\n }\n\n return {\n configResolved(config) {\n root = config.root;\n treeshake = config.build.rolldownOptions.treeshake !== false;\n info = (message) => {\n config.logger.info(message);\n };\n },\n\n async buildStart() {\n states.delete(this.environment);\n const state = stateOf(this.environment);\n for (const source of sources) {\n const resolved = await this.resolve(source, join(root, 'index.html'));\n if (resolved !== null) state.sourceIds.add(resolved.id);\n }\n loadOnce();\n },\n\n async transform(code, id, opts) {\n if (id.startsWith('\\0') || !SCRIPT.test(id)) return null;\n const env = this.environment;\n const state = stateOf(env);\n if (DEFINES_HTML.test(code)) state.definers.add(id);\n if (state.sourceIds.has(id)) return null;\n let program: Node;\n try {\n program = this.parse(code, { lang: langOf(id) }) as unknown as Node;\n } catch {\n return null; // not ours to report: the bundler fails on it with a better message\n }\n const { imports, reexports } = htmlImports(program);\n if (imports.length === 0 && reexports.length === 0) return null;\n const isSource = async (specifier: string): Promise<boolean> => {\n if (sources.includes(specifier)) return true;\n const resolved = await this.resolve(specifier, id);\n return resolved !== null && state.sourceIds.has(resolved.id);\n };\n const lines = new Lines(code);\n const file = relative(root, id);\n const where = (node: Node): string => `${file}:${String(lines.position(node.start).line)}`;\n for (const r of reexports) {\n if (!(await isSource(r.specifier))) continue;\n this.warn(\n `${where(r.node)}: re-exporting html. The template compiler only follows html ` +\n `imported straight from ${r.specifier}: templates written with this re-export stay ` +\n `uncompiled and fail the build. Import html from ${r.specifier} instead.`,\n );\n }\n const matched = [];\n for (const binding of imports) if (await isSource(binding.specifier)) matched.push(binding);\n if (matched.length === 0) return null;\n const fail = (start: number, end: number, message: string): never =>\n this.error({\n message,\n id,\n loc: { file: id, ...lines.position(start) },\n frame: lines.frame(start, end),\n });\n const { sites, leftovers } = findUses(program, matched);\n for (const l of leftovers) {\n if (l.exported)\n this.warn(`${where(l.node)}: re-exporting html; its users stay uncompiled.`);\n else fail(l.node.start, l.node.end, CANT_FOLLOW);\n }\n if (sites.length === 0) return null;\n const ssr = opts?.ssr === true || env.config.consumer === 'server';\n try {\n return compileModule({\n lines,\n id,\n file,\n sites,\n ssr,\n ids: ssr || developmentBuild(env.config),\n parse5: loadOnce(),\n seen: state.seen,\n });\n } catch (error) {\n if (error instanceof CompileError) return fail(error.start, error.end, error.message);\n throw error;\n }\n },\n\n generateBundle(_, bundle) {\n const state = stateOf(this.environment);\n // Without tree-shaking every export is rendered, used or not.\n if (state.definers.size === 0 || !treeshake) return;\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk') continue;\n for (const [id, module] of Object.entries(chunk.modules)) {\n if (!state.definers.has(id) || !module.renderedExports.includes('html')) continue;\n this.error(\n `gyral: an uncompiled html template remains in ${chunk.fileName}: something uses ` +\n `html from ${relative(root, id)} in a way the template compiler can't see (a ` +\n `re-export, a dynamic import, or a module it doesn't process). With the ` +\n `gyral-compiled condition the runtime preparer is left out, so that template ` +\n `would throw. Import html straight from '@gyral/core' where you write templates ` +\n `(view/01-templates.md \"Compiled\").`,\n );\n }\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/compiler/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["// The template compiler's build hooks (view/01-templates.md \"Compiled\",\n// view/09-template-rules.md), loaded lazily by the plugin shell in ../vite.ts on the first\n// `vite build` hook, so the preset stays loadable from any vite.config.ts:\n// - `transform` (the shell is enforce: 'pre', so positions are the author's own source,\n// TypeScript included, parsed by Rolldown's `this.parse`): rewrites each `html` call site\n// of the configured sources into `compiled(<hoisted template object>, [values…])` (and\n// each `svg` one into `compiledSvg(…)`), dependencies in node_modules included. Template\n// rule violations, and references it can't follow, fail the build with a code frame.\n// - `generateBundle`: fails when an uncompiled `html` or `svg` survived in a chunk anyway\n// (re-exports across modules, dynamic imports): the guarantee that the `#prepare` stub is\n// never hit.\n// - In client environments, `transform` also adds the registration of view transitions, the\n// frame lane and custom states to every module that names their spec field, among the\n// modules that can affect Gyral components (the app's own, and packages that reach a Gyral\n// package), so the `gyral-compiled` condition can leave them out otherwise (features.ts,\n// gyral-c5d.12); `resolveId` and `load` serve those registration modules.\nimport { join, relative } from 'node:path';\nimport type { Plugin } from 'vite';\nimport { langOf, type Node } from './ast.js';\nimport { CompileError, compileModule } from './compile.js';\nimport { createFeatures } from './features.js';\nimport { stripPropSchemas } from './prop-schemas.js';\nimport { loadParse5, type Parse5 } from './parse5-check.js';\nimport { VIEW } from './locate.js';\nimport { findUses, htmlImports } from './scan.js';\nimport { Lines, splice, type SourceMap } from './source.js';\n\ntype Fn<H> = H extends { handler: infer F } ? F : H;\ntype HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;\n\nexport interface CompilerHooks {\n readonly configResolved: HookFn<'configResolved'>;\n readonly buildStart: HookFn<'buildStart'>;\n readonly resolveId: HookFn<'resolveId'>;\n readonly load: HookFn<'load'>;\n readonly transform: HookFn<'transform'>;\n readonly generateBundle: HookFn<'generateBundle'>;\n}\n\nexport interface CompilerSettings {\n readonly sources: readonly string[];\n /** The module to load parse5 from; undefined skips the parse5 check. */\n readonly parse5: string | undefined;\n /** The compiled entry of the core copy running the plugin (features.ts). */\n readonly ownEntry: string;\n}\n\ntype TransformContext = ThisParameterType<HookFn<'transform'>>;\ntype TransformOptions = Parameters<HookFn<'transform'>>[2];\n/** What `compile` returns: the rewritten module, or null when nothing changed. */\ntype Transformed = { readonly code: string; readonly map: SourceMap | null } | null;\n\n/** Appends `imports` after the module's last line: no code moves, so source maps hold. */\nconst appended = (result: Transformed, code: string, imports: string): Transformed =>\n imports === ''\n ? result\n : result === null\n ? { code: code + imports, map: null }\n : { ...result, code: result.code + imports };\n\n/** Modules naming a template tag, or calling `.value(`: only those can need compiling. */\nconst NAMES_TAG = /html|svg|\\.value\\s*\\(/;\n/** A `prop.value(…)` call may be in the module (prop-schemas.ts). */\nconst PROP_VALUE = /\\.value\\s*\\(/;\n\n/** template.ts's symbol description: the module defining `html` and `svg`, in any copy of core. */\nconst DEFINES_HTML = /Symbol\\(['\"]gyral\\.template['\"]\\)/;\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\ninterface BuildState {\n readonly sourceIds: Set<string>;\n readonly definers: Set<string>;\n readonly seen: Map<string, { readonly strings: string; readonly loc: string }>;\n}\n\n/**\n * Whether an environment resolves core's `#view-dev` to its development module: the\n * `development` condition is listed, or Vite's `development|production` placeholder is and\n * the build isn't a production one. Exactly then the client keeps template ids (01).\n */\nfunction developmentBuild(config: {\n readonly isProduction: boolean;\n readonly resolve: { readonly conditions: readonly string[] };\n}): boolean {\n const { conditions } = config.resolve;\n return (\n conditions.includes('development') ||\n (!config.isProduction && conditions.includes('development|production'))\n );\n}\n\nconst CANT_FOLLOW =\n `This use of html can't be compiled: the template compiler rewrites only html\\`…\\` tagged ` +\n `templates whose tag is the imported html itself (or ns.html for import * as ns; the same ` +\n `for svg). ` +\n `Aliasing it, passing it around or calling it as a function would reach the runtime ` +\n `template preparer, which builds made with the Gyral preset leave out. Write html\\`…\\` at ` +\n `the call site (view/01-templates.md \"Compiled\").`;\n\nexport function createCompiler(settings: CompilerSettings): CompilerHooks {\n const { sources } = settings;\n const features = createFeatures(\n [\n 'transitions',\n 'frame',\n 'states',\n 'forms',\n 'subscriptions',\n 'dialogs',\n 'disposal',\n 'errorView',\n 'errored',\n ],\n settings.ownEntry,\n );\n /** Per environment (client, ssr, …): Vite may build them with one plugin instance. */\n const states = new WeakMap<object, BuildState>();\n const global = {};\n let root = '';\n let treeshake = true;\n let info: (message: string) => void = () => undefined;\n let parse5: { readonly module: Parse5 | undefined } | undefined;\n\n function stateOf(env: object | undefined): BuildState {\n const key = env ?? global;\n let state = states.get(key);\n if (state === undefined) {\n state = { sourceIds: new Set(VIEW), definers: new Set(), seen: new Map() };\n states.set(key, state);\n }\n return state;\n }\n\n /** parse5, loaded once; the notice when it is missing is printed once too. */\n function loadOnce(): Parse5 | undefined {\n const specifier = settings.parse5;\n if (specifier === undefined) return undefined;\n if (parse5 === undefined) {\n parse5 = { module: loadParse5(specifier, root) };\n if (parse5.module === undefined) {\n info(\n `gyral: ${specifier} is not installed, so the template compiler skips its extra ` +\n `rule 7 check (the normalizer's own check still runs). To get it: pnpm add -D ` +\n `parse5 (view/09-template-rules.md).`,\n );\n }\n }\n return parse5.module;\n }\n\n /** Compiles the module's template call sites (the transform proper). */\n async function compile(\n this: TransformContext,\n code: string,\n id: string,\n opts: TransformOptions,\n ): Promise<Transformed> {\n if (id.startsWith('\\0') || !SCRIPT.test(id)) return null;\n const env = this.environment;\n const state = stateOf(env);\n if (DEFINES_HTML.test(code)) state.definers.add(id);\n if (state.sourceIds.has(id)) return null;\n let program: Node;\n try {\n program = this.parse(code, { lang: langOf(id) }) as unknown as Node;\n } catch {\n return null; // not ours to report: the bundler fails on it with a better message\n }\n const lines = new Lines(code);\n const file = relative(root, id);\n // Production client builds: property-only prop checks can't run (prop-schemas.ts).\n const client = opts?.ssr !== true && env.config.consumer === 'client';\n const extra =\n client && !developmentBuild(env.config) && PROP_VALUE.test(code)\n ? stripPropSchemas(program, code)\n : [];\n const unchanged = (): Transformed => (extra.length === 0 ? null : splice(lines, extra, id));\n const { imports, reexports } = htmlImports(program);\n if (imports.length === 0 && reexports.length === 0) return unchanged();\n const isSource = async (specifier: string): Promise<boolean> => {\n if (sources.includes(specifier)) return true;\n const resolved = await this.resolve(specifier, id);\n return resolved !== null && state.sourceIds.has(resolved.id);\n };\n const where = (node: Node): string => `${file}:${String(lines.position(node.start).line)}`;\n for (const r of reexports) {\n if (!(await isSource(r.specifier))) continue;\n this.warn(\n `${where(r.node)}: re-exporting html or svg. The template compiler only follows the ` +\n `tags imported straight from ${r.specifier}: templates written with this re-export ` +\n `stay uncompiled and fail the build. Import them from ${r.specifier} instead.`,\n );\n }\n const matched = [];\n for (const binding of imports) if (await isSource(binding.specifier)) matched.push(binding);\n if (matched.length === 0) return unchanged();\n const fail = (start: number, end: number, message: string): never =>\n this.error({\n message,\n id,\n loc: { file: id, ...lines.position(start) },\n frame: lines.frame(start, end),\n });\n const { sites, leftovers } = findUses(program, matched);\n for (const l of leftovers) {\n if (l.exported)\n this.warn(`${where(l.node)}: re-exporting a template tag; its users stay uncompiled.`);\n else fail(l.node.start, l.node.end, CANT_FOLLOW);\n }\n if (sites.length === 0) return unchanged();\n const ssr = opts?.ssr === true || env.config.consumer === 'server';\n try {\n return compileModule({\n lines,\n id,\n file,\n sites,\n ssr,\n ids: ssr || developmentBuild(env.config),\n parse5: loadOnce(),\n extra,\n seen: state.seen,\n });\n } catch (error) {\n if (error instanceof CompileError) return fail(error.start, error.end, error.message);\n throw error;\n }\n }\n\n return {\n configResolved(config) {\n root = config.root;\n treeshake = config.build.rolldownOptions.treeshake !== false;\n info = (message) => {\n config.logger.info(message);\n };\n },\n\n async buildStart() {\n states.delete(this.environment);\n const state = stateOf(this.environment);\n for (const source of sources) {\n const resolved = await this.resolve(source, join(root, 'index.html'));\n if (resolved !== null) state.sourceIds.add(resolved.id);\n }\n await features.buildStart((source, importer) => this.resolve(source, importer), root);\n loadOnce();\n },\n\n resolveId(source) {\n return features.resolveId(source);\n },\n\n load(id) {\n return features.load(id);\n },\n\n async transform(code, id, opts) {\n const client = opts?.ssr !== true && this.environment.config.consumer === 'client';\n const imports = client\n ? await features.inject(code, id, {\n parse: (text, lang) => this.parse(text, { lang }),\n resolve: (source, importer) => this.resolve(source, importer),\n })\n : '';\n const compiled = NAMES_TAG.test(code) ? await compile.call(this, code, id, opts) : null;\n return appended(compiled, code, imports);\n },\n\n generateBundle(_, bundle) {\n const state = stateOf(this.environment);\n // Without tree-shaking every export is rendered, used or not.\n if (state.definers.size === 0 || !treeshake) return;\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk') continue;\n for (const [id, module] of Object.entries(chunk.modules)) {\n const tag = ['html', 'svg'].find((name) => module.renderedExports.includes(name));\n if (!state.definers.has(id) || tag === undefined) continue;\n this.error(\n `gyral: an uncompiled ${tag} template remains in ${chunk.fileName}: something uses ` +\n `${tag} from ${relative(root, id)} in a way the template compiler can't see (a ` +\n `re-export, a dynamic import, or a module it doesn't process). With the ` +\n `gyral-compiled condition the runtime preparer is left out, so that template ` +\n `would throw. Import ${tag} straight from '@gyral/core' where you write templates ` +\n `(view/01-templates.md \"Compiled\").`,\n );\n }\n }\n },\n };\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /** How a production build writes the loader (./components.ts). */
2
+ export interface LoaderOptions {
3
+ /** Tags the app's entry defines: wait for registration and watch their shadow roots. */
4
+ readonly known?: readonly string[];
5
+ }
6
+ /**
7
+ * The loader: imports a tag's module the first time that tag is rendered, in the document or in
8
+ * the shadow root of a component it defined (now, or after a mutation, checked once per
9
+ * microtask), then forgets it. It registers itself on `globalThis[Symbol.for('gyral.components')]`
10
+ * so `componentsReady()` (core) can wait until every rendered tag it knows is defined.
11
+ * Shadow roots are observed by the one shared MutationObserver; a removed host's root is
12
+ * dropped from the scan and its registration is collected with it.
13
+ */
14
+ export declare function loaderSource(tags: ReadonlyMap<string, string>, options?: LoaderOptions): string;
@@ -0,0 +1,86 @@
1
+ // The component loader's source (./components.ts): a module that imports a tag's module the
2
+ // first time that tag is in the document. No Node imports, so tests can run it in a browser.
3
+ /**
4
+ * The loader: imports a tag's module the first time that tag is rendered, in the document or in
5
+ * the shadow root of a component it defined (now, or after a mutation, checked once per
6
+ * microtask), then forgets it. It registers itself on `globalThis[Symbol.for('gyral.components')]`
7
+ * so `componentsReady()` (core) can wait until every rendered tag it knows is defined.
8
+ * Shadow roots are observed by the one shared MutationObserver; a removed host's root is
9
+ * dropped from the scan and its registration is collected with it.
10
+ */
11
+ export function loaderSource(tags, options = {}) {
12
+ const entries = [
13
+ ...[...tags].map(([tag, url]) => ` [${JSON.stringify(tag)}, () => import(${JSON.stringify(url)})],`),
14
+ ...(options.known ?? [])
15
+ .filter((tag) => !tags.has(tag))
16
+ .map((tag) => ` [${JSON.stringify(tag)}, () => Promise.resolve()],`),
17
+ ].join('\n');
18
+ const watched = [...new Set([...tags.keys(), ...(options.known ?? [])])].join(',');
19
+ return `// Generated by gyralVitePreset({ components: true }).
20
+ const pending = new Map([
21
+ ${entries}
22
+ ]);
23
+ const known = ${JSON.stringify(watched)};
24
+ const roots = new Set();
25
+ const inflight = new Set();
26
+ let queued = false;
27
+ const observer = new MutationObserver(() => {
28
+ if (!queued) {
29
+ queued = true;
30
+ queueMicrotask(scan);
31
+ }
32
+ });
33
+ function watch(root) {
34
+ for (const host of root.querySelectorAll(known)) {
35
+ const shadow = host.shadowRoot;
36
+ if (shadow !== null && !roots.has(shadow)) {
37
+ roots.add(shadow);
38
+ observer.observe(shadow, { childList: true, subtree: true });
39
+ watch(shadow);
40
+ }
41
+ }
42
+ }
43
+ function scan() {
44
+ queued = false;
45
+ for (const root of roots) if (!root.host.isConnected) roots.delete(root);
46
+ if (known !== '') {
47
+ watch(document);
48
+ for (const root of [...roots]) watch(root);
49
+ }
50
+ for (const [tag, load] of pending) {
51
+ if (customElements.get(tag) !== undefined) {
52
+ pending.delete(tag);
53
+ continue;
54
+ }
55
+ let found = document.querySelector(tag) !== null;
56
+ for (const root of roots) if (!found && root.querySelector(tag) !== null) found = true;
57
+ if (!found) continue;
58
+ pending.delete(tag);
59
+ const done = load()
60
+ .then(() => customElements.whenDefined(tag))
61
+ .then(() => {
62
+ // The upgraded host renders into its shadow root next: look there too.
63
+ if (!queued) {
64
+ queued = true;
65
+ queueMicrotask(scan);
66
+ }
67
+ })
68
+ .finally(() => inflight.delete(done));
69
+ inflight.add(done);
70
+ }
71
+ if (pending.size === 0) observer.disconnect();
72
+ }
73
+ async function ready() {
74
+ for (;;) {
75
+ scan();
76
+ if (inflight.size === 0) return;
77
+ await Promise.allSettled([...inflight]);
78
+ await new Promise((resolve) => setTimeout(resolve));
79
+ }
80
+ }
81
+ globalThis[Symbol.for('gyral.components')] = { ready };
82
+ observer.observe(document.documentElement, { childList: true, subtree: true });
83
+ scan();
84
+ `;
85
+ }
86
+ //# sourceMappingURL=loader-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader-source.js","sourceRoot":"","sources":["../../src/compiler/loader-source.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,6FAA6F;AAQ7F;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAiC,EACjC,UAAyB,EAAE;IAE3B,MAAM,OAAO,GAAG;QACd,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CACd,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CACpF;QACD,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;aACrB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;aAC/B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,6BAA6B,CAAC;KACxE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnF,OAAO;;EAEP,OAAO;;gBAEO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DtC,CAAC;AACF,CAAC","sourcesContent":["// The component loader's source (./components.ts): a module that imports a tag's module the\n// first time that tag is in the document. No Node imports, so tests can run it in a browser.\n\n/** How a production build writes the loader (./components.ts). */\nexport interface LoaderOptions {\n /** Tags the app's entry defines: wait for registration and watch their shadow roots. */\n readonly known?: readonly string[];\n}\n\n/**\n * The loader: imports a tag's module the first time that tag is rendered, in the document or in\n * the shadow root of a component it defined (now, or after a mutation, checked once per\n * microtask), then forgets it. It registers itself on `globalThis[Symbol.for('gyral.components')]`\n * so `componentsReady()` (core) can wait until every rendered tag it knows is defined.\n * Shadow roots are observed by the one shared MutationObserver; a removed host's root is\n * dropped from the scan and its registration is collected with it.\n */\nexport function loaderSource(\n tags: ReadonlyMap<string, string>,\n options: LoaderOptions = {},\n): string {\n const entries = [\n ...[...tags].map(\n ([tag, url]) => ` [${JSON.stringify(tag)}, () => import(${JSON.stringify(url)})],`,\n ),\n ...(options.known ?? [])\n .filter((tag) => !tags.has(tag))\n .map((tag) => ` [${JSON.stringify(tag)}, () => Promise.resolve()],`),\n ].join('\\n');\n const watched = [...new Set([...tags.keys(), ...(options.known ?? [])])].join(',');\n return `// Generated by gyralVitePreset({ components: true }).\nconst pending = new Map([\n${entries}\n]);\nconst known = ${JSON.stringify(watched)};\nconst roots = new Set();\nconst inflight = new Set();\nlet queued = false;\nconst observer = new MutationObserver(() => {\n if (!queued) {\n queued = true;\n queueMicrotask(scan);\n }\n});\nfunction watch(root) {\n for (const host of root.querySelectorAll(known)) {\n const shadow = host.shadowRoot;\n if (shadow !== null && !roots.has(shadow)) {\n roots.add(shadow);\n observer.observe(shadow, { childList: true, subtree: true });\n watch(shadow);\n }\n }\n}\nfunction scan() {\n queued = false;\n for (const root of roots) if (!root.host.isConnected) roots.delete(root);\n if (known !== '') {\n watch(document);\n for (const root of [...roots]) watch(root);\n }\n for (const [tag, load] of pending) {\n if (customElements.get(tag) !== undefined) {\n pending.delete(tag);\n continue;\n }\n let found = document.querySelector(tag) !== null;\n for (const root of roots) if (!found && root.querySelector(tag) !== null) found = true;\n if (!found) continue;\n pending.delete(tag);\n const done = load()\n .then(() => customElements.whenDefined(tag))\n .then(() => {\n // The upgraded host renders into its shadow root next: look there too.\n if (!queued) {\n queued = true;\n queueMicrotask(scan);\n }\n })\n .finally(() => inflight.delete(done));\n inflight.add(done);\n }\n if (pending.size === 0) observer.disconnect();\n}\nasync function ready() {\n for (;;) {\n scan();\n if (inflight.size === 0) return;\n await Promise.allSettled([...inflight]);\n await new Promise((resolve) => setTimeout(resolve));\n }\n}\nglobalThis[Symbol.for('gyral.components')] = { ready };\nobserver.observe(document.documentElement, { childList: true, subtree: true });\nscan();\n`;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { Plugin } from 'vite';
2
+ type Fn<H> = H extends {
3
+ handler: infer F;
4
+ } ? F : H;
5
+ type HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;
6
+ export interface LocatorHooks {
7
+ readonly configResolved: HookFn<'configResolved'>;
8
+ readonly transform: HookFn<'transform'>;
9
+ }
10
+ /** Core's own view modules: always sources (core-internal templates, tests). */
11
+ export declare const VIEW: string[];
12
+ /** `sources`: as the compiler's (module specifiers whose `html` and `svg` are the view layer's tags). */
13
+ export declare function createLocator(sources: readonly string[]): LocatorHooks;
14
+ export {};
@@ -0,0 +1,78 @@
1
+ // Development source locations for runtime templates (view/01-templates.md "Source locations",
2
+ // gyral-g1r.24). `vite serve` (the dev server and Vitest) doesn't compile templates, and stack
3
+ // traces point into Vite's transformed modules, whose lines aren't the author's. So the preset's
4
+ // `gyral:template-locations` plugin (serve only, enforce: 'pre', loaded lazily like the
5
+ // compiler) rewrites each html`…` call site of the configured sources into
6
+ // (html.at?.("src/app.ts:12:5") ?? html)`…`
7
+ // (svg`…` call sites the same way, with svg.at) and the development runtime records that
8
+ // position for the template (view/loc.ts). Nothing else changes and no line moves. Uses the
9
+ // compiler can't follow (an aliased html) stay as they are: the runtime falls back to the stack
10
+ // trace there.
11
+ import { relative } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { langOf, nodeAt } from './ast.js';
14
+ import { findUses, htmlImports } from './scan.js';
15
+ import { Lines, splice } from './source.js';
16
+ /** Core's own view modules: always sources (core-internal templates, tests). */
17
+ export const VIEW = ['index', 'template'].flatMap((name) => ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))));
18
+ const SCRIPT = /\.[cm]?[jt]sx?$/;
19
+ /** `sources`: as the compiler's (module specifiers whose `html` and `svg` are the view layer's tags). */
20
+ export function createLocator(sources) {
21
+ let root = '';
22
+ /** Resolved ids of the sources, per environment (resolution may differ between them). */
23
+ const resolved = new WeakMap();
24
+ return {
25
+ configResolved(config) {
26
+ root = config.root;
27
+ },
28
+ async transform(code, id) {
29
+ if (id.startsWith('\0') || !SCRIPT.test(id) || id.includes('/node_modules/'))
30
+ return null;
31
+ let program;
32
+ try {
33
+ program = this.parse(code, { lang: langOf(id) });
34
+ }
35
+ catch {
36
+ return null; // not ours to report
37
+ }
38
+ const { imports } = htmlImports(program);
39
+ if (imports.length === 0)
40
+ return null;
41
+ let ids = resolved.get(this.environment);
42
+ if (ids === undefined) {
43
+ ids = Promise.all(sources.map((s) => this.resolve(s, id))).then((rs) => new Set([...VIEW, ...rs.flatMap((r) => (r === null ? [] : [r.id]))]));
44
+ resolved.set(this.environment, ids);
45
+ }
46
+ const sourceIds = await ids;
47
+ const matched = [];
48
+ for (const binding of imports) {
49
+ const r = sources.includes(binding.specifier)
50
+ ? null
51
+ : await this.resolve(binding.specifier, id);
52
+ if (sources.includes(binding.specifier) || (r !== null && sourceIds.has(r.id))) {
53
+ matched.push(binding);
54
+ }
55
+ }
56
+ if (matched.length === 0)
57
+ return null;
58
+ const { sites } = findUses(program, matched);
59
+ if (sites.length === 0)
60
+ return null;
61
+ const lines = new Lines(code);
62
+ const file = relative(root, id);
63
+ const edits = [];
64
+ for (const { node } of sites) {
65
+ const tag = nodeAt(node, 'tag');
66
+ if (tag === undefined)
67
+ continue;
68
+ const { line, column } = lines.position(node.start);
69
+ const loc = JSON.stringify(`${file}:${String(line)}:${String(column + 1)}`);
70
+ const name = code.slice(tag.start, tag.end);
71
+ edits.push({ start: tag.start, end: tag.start, text: '(' });
72
+ edits.push({ start: tag.end, end: tag.end, text: `.at?.(${loc}) ?? ${name})` });
73
+ }
74
+ return splice(lines, edits, id);
75
+ },
76
+ };
77
+ }
78
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/compiler/locate.ts"],"names":[],"mappings":"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","sourcesContent":["// Development source locations for runtime templates (view/01-templates.md \"Source locations\",\n// gyral-g1r.24). `vite serve` (the dev server and Vitest) doesn't compile templates, and stack\n// traces point into Vite's transformed modules, whose lines aren't the author's. So the preset's\n// `gyral:template-locations` plugin (serve only, enforce: 'pre', loaded lazily like the\n// compiler) rewrites each html`…` call site of the configured sources into\n// (html.at?.(\"src/app.ts:12:5\") ?? html)`…`\n// (svg`…` call sites the same way, with svg.at) and the development runtime records that\n// position for the template (view/loc.ts). Nothing else changes and no line moves. Uses the\n// compiler can't follow (an aliased html) stay as they are: the runtime falls back to the stack\n// trace there.\nimport { relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Plugin } from 'vite';\nimport { langOf, nodeAt, type Node } from './ast.js';\nimport { findUses, htmlImports } from './scan.js';\nimport { Lines, splice, type Edit } from './source.js';\n\ntype Fn<H> = H extends { handler: infer F } ? F : H;\ntype HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;\n\nexport interface LocatorHooks {\n readonly configResolved: HookFn<'configResolved'>;\n readonly transform: HookFn<'transform'>;\n}\n\n/** Core's own view modules: always sources (core-internal templates, tests). */\nexport const VIEW = ['index', 'template'].flatMap((name) =>\n ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))),\n);\n\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\n/** `sources`: as the compiler's (module specifiers whose `html` and `svg` are the view layer's tags). */\nexport function createLocator(sources: readonly string[]): LocatorHooks {\n let root = '';\n /** Resolved ids of the sources, per environment (resolution may differ between them). */\n const resolved = new WeakMap<object, Promise<Set<string>>>();\n\n return {\n configResolved(config) {\n root = config.root;\n },\n\n async transform(code, id) {\n if (id.startsWith('\\0') || !SCRIPT.test(id) || id.includes('/node_modules/')) return null;\n let program: Node;\n try {\n program = this.parse(code, { lang: langOf(id) }) as unknown as Node;\n } catch {\n return null; // not ours to report\n }\n const { imports } = htmlImports(program);\n if (imports.length === 0) return null;\n let ids = resolved.get(this.environment);\n if (ids === undefined) {\n ids = Promise.all(sources.map((s) => this.resolve(s, id))).then(\n (rs) => new Set([...VIEW, ...rs.flatMap((r) => (r === null ? [] : [r.id]))]),\n );\n resolved.set(this.environment, ids);\n }\n const sourceIds = await ids;\n const matched = [];\n for (const binding of imports) {\n const r = sources.includes(binding.specifier)\n ? null\n : await this.resolve(binding.specifier, id);\n if (sources.includes(binding.specifier) || (r !== null && sourceIds.has(r.id))) {\n matched.push(binding);\n }\n }\n if (matched.length === 0) return null;\n const { sites } = findUses(program, matched);\n if (sites.length === 0) return null;\n const lines = new Lines(code);\n const file = relative(root, id);\n const edits: Edit[] = [];\n for (const { node } of sites) {\n const tag = nodeAt(node, 'tag');\n if (tag === undefined) continue;\n const { line, column } = lines.position(node.start);\n const loc = JSON.stringify(`${file}:${String(line)}:${String(column + 1)}`);\n const name = code.slice(tag.start, tag.end);\n edits.push({ start: tag.start, end: tag.start, text: '(' });\n edits.push({ start: tag.end, end: tag.end, text: `.at?.(${loc}) ?? ${name})` });\n }\n return splice(lines, edits, id);\n },\n };\n}\n"]}
@@ -10,7 +10,7 @@ interface Parse5Node {
10
10
  readonly content?: Parse5Node;
11
11
  }
12
12
  /**
13
- * Loads parse5 (`specifier`) as resolved from @gyral/core, else from the app's `root`;
13
+ * Loads parse5 (`specifier`) as resolved from `@gyral/core`, else from the app's `root`;
14
14
  * undefined when neither has it. `require` (Node loads ES modules with it too) rather than
15
15
  * `import()`: when core runs from source, this module is loaded by a Vite module runner that
16
16
  * is closed by the time the build starts.