@gyral/core 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +1 -1
  3. package/NOTICE +1 -1
  4. package/README.md +9 -7
  5. package/dist/children.d.ts +5 -2
  6. package/dist/children.js +8 -3
  7. package/dist/children.js.map +1 -1
  8. package/dist/command.js +6 -0
  9. package/dist/command.js.map +1 -1
  10. package/dist/compiler/ast.d.ts +21 -0
  11. package/dist/compiler/ast.js +145 -0
  12. package/dist/compiler/ast.js.map +1 -0
  13. package/dist/compiler/compile.d.ts +38 -0
  14. package/dist/compiler/compile.js +129 -0
  15. package/dist/compiler/compile.js.map +1 -0
  16. package/dist/compiler/hooks.d.ts +18 -0
  17. package/dist/compiler/hooks.js +186 -0
  18. package/dist/compiler/hooks.js.map +1 -0
  19. package/dist/compiler/parse5-check.d.ts +21 -0
  20. package/dist/compiler/parse5-check.js +55 -0
  21. package/dist/compiler/parse5-check.js.map +1 -0
  22. package/dist/compiler/scan.d.ts +31 -0
  23. package/dist/compiler/scan.js +152 -0
  24. package/dist/compiler/scan.js.map +1 -0
  25. package/dist/compiler/source.d.ts +34 -0
  26. package/dist/compiler/source.js +114 -0
  27. package/dist/compiler/source.js.map +1 -0
  28. package/dist/define.d.ts +2 -2
  29. package/dist/define.js +35 -334
  30. package/dist/define.js.map +1 -1
  31. package/dist/devtools-events.d.ts +5 -0
  32. package/dist/devtools-events.js.map +1 -1
  33. package/dist/devtools-off.d.ts +1 -0
  34. package/dist/devtools-off.js +1 -0
  35. package/dist/devtools-off.js.map +1 -1
  36. package/dist/devtools.d.ts +1 -0
  37. package/dist/devtools.js +3 -0
  38. package/dist/devtools.js.map +1 -1
  39. package/dist/drivers-scope.d.ts +1 -1
  40. package/dist/drivers-scope.js +1 -8
  41. package/dist/drivers-scope.js.map +1 -1
  42. package/dist/element-types.d.ts +5 -3
  43. package/dist/element-types.js.map +1 -1
  44. package/dist/element.d.ts +3 -0
  45. package/dist/element.js +269 -0
  46. package/dist/element.js.map +1 -0
  47. package/dist/eslint/imports.d.ts +24 -0
  48. package/dist/eslint/imports.js +78 -0
  49. package/dist/eslint/imports.js.map +1 -0
  50. package/dist/eslint/index.d.ts +15 -0
  51. package/dist/eslint/index.js +15 -0
  52. package/dist/eslint/index.js.map +1 -0
  53. package/dist/eslint/locate.d.ts +33 -0
  54. package/dist/eslint/locate.js +132 -0
  55. package/dist/eslint/locate.js.map +1 -0
  56. package/dist/eslint/row-purity-rule.d.ts +2 -0
  57. package/dist/eslint/row-purity-rule.js +103 -0
  58. package/dist/eslint/row-purity-rule.js.map +1 -0
  59. package/dist/eslint/template-rule.d.ts +5 -0
  60. package/dist/eslint/template-rule.js +66 -0
  61. package/dist/eslint/template-rule.js.map +1 -0
  62. package/dist/features.d.ts +39 -0
  63. package/dist/features.js +2 -0
  64. package/dist/features.js.map +1 -0
  65. package/dist/focus.d.ts +9 -2
  66. package/dist/focus.js +17 -2
  67. package/dist/focus.js.map +1 -1
  68. package/dist/hooks/invalid.d.ts +14 -0
  69. package/dist/hooks/invalid.js +72 -0
  70. package/dist/hooks/invalid.js.map +1 -0
  71. package/dist/hooks/labelled-by.d.ts +13 -0
  72. package/dist/hooks/labelled-by.js +51 -0
  73. package/dist/hooks/labelled-by.js.map +1 -0
  74. package/dist/host-model.d.ts +48 -0
  75. package/dist/host-model.js +155 -0
  76. package/dist/host-model.js.map +1 -0
  77. package/dist/hydration-client.d.ts +9 -0
  78. package/dist/hydration-client.js +35 -0
  79. package/dist/hydration-client.js.map +1 -0
  80. package/dist/hydration.d.ts +13 -19
  81. package/dist/hydration.js +45 -45
  82. package/dist/hydration.js.map +1 -1
  83. package/dist/index.d.ts +9 -16
  84. package/dist/index.js +9 -16
  85. package/dist/index.js.map +1 -1
  86. package/dist/intent.d.ts +31 -8
  87. package/dist/intent.js +82 -27
  88. package/dist/intent.js.map +1 -1
  89. package/dist/internal/interpreter.d.ts +7 -1
  90. package/dist/internal/interpreter.js +118 -73
  91. package/dist/internal/interpreter.js.map +1 -1
  92. package/dist/invokers-shim.d.ts +6 -0
  93. package/dist/invokers-shim.js +32 -0
  94. package/dist/invokers-shim.js.map +1 -0
  95. package/dist/invokers.d.ts +3 -5
  96. package/dist/invokers.js +11 -51
  97. package/dist/invokers.js.map +1 -1
  98. package/dist/islands.d.ts +0 -2
  99. package/dist/islands.js +4 -11
  100. package/dist/islands.js.map +1 -1
  101. package/dist/light-dom.d.ts +3 -52
  102. package/dist/light-dom.js +5 -100
  103. package/dist/light-dom.js.map +1 -1
  104. package/dist/prop.d.ts +66 -0
  105. package/dist/prop.js +22 -0
  106. package/dist/prop.js.map +1 -0
  107. package/dist/props.d.ts +52 -21
  108. package/dist/props.js +150 -34
  109. package/dist/props.js.map +1 -1
  110. package/dist/scheduler.d.ts +39 -0
  111. package/dist/scheduler.js +230 -0
  112. package/dist/scheduler.js.map +1 -0
  113. package/dist/server-component.d.ts +10 -0
  114. package/dist/server-component.js +102 -0
  115. package/dist/server-component.js.map +1 -0
  116. package/dist/server-specs.d.ts +6 -0
  117. package/dist/server-specs.js +13 -0
  118. package/dist/server-specs.js.map +1 -0
  119. package/dist/server.d.ts +28 -0
  120. package/dist/server.js +50 -0
  121. package/dist/server.js.map +1 -0
  122. package/dist/settled.d.ts +10 -0
  123. package/dist/settled.js +40 -0
  124. package/dist/settled.js.map +1 -0
  125. package/dist/states.d.ts +3 -3
  126. package/dist/states.js +4 -7
  127. package/dist/states.js.map +1 -1
  128. package/dist/store-binding.d.ts +6 -3
  129. package/dist/store-binding.js +5 -6
  130. package/dist/store-binding.js.map +1 -1
  131. package/dist/store-scope.d.ts +6 -7
  132. package/dist/store-scope.js +11 -26
  133. package/dist/store-scope.js.map +1 -1
  134. package/dist/store.d.ts +3 -0
  135. package/dist/store.js +9 -2
  136. package/dist/store.js.map +1 -1
  137. package/dist/stores-provider.d.ts +2 -6
  138. package/dist/stores-provider.js +21 -55
  139. package/dist/stores-provider.js.map +1 -1
  140. package/dist/transitions.d.ts +5 -6
  141. package/dist/transitions.js +11 -20
  142. package/dist/transitions.js.map +1 -1
  143. package/dist/types.d.ts +19 -23
  144. package/dist/types.js.map +1 -1
  145. package/dist/view/attributes.d.ts +6 -0
  146. package/dist/view/attributes.js +10 -0
  147. package/dist/view/attributes.js.map +1 -0
  148. package/dist/view/compiled.d.ts +1 -0
  149. package/dist/view/compiled.js +6 -0
  150. package/dist/view/compiled.js.map +1 -0
  151. package/dist/view/dev-off.d.ts +1 -0
  152. package/dist/view/dev-off.js +4 -0
  153. package/dist/view/dev-off.js.map +1 -0
  154. package/dist/view/dev.d.ts +1 -0
  155. package/dist/view/dev.js +5 -0
  156. package/dist/view/dev.js.map +1 -0
  157. package/dist/view/flags.d.ts +1 -0
  158. package/dist/view/flags.js +4 -0
  159. package/dist/view/flags.js.map +1 -0
  160. package/dist/view/index.d.ts +21 -0
  161. package/dist/view/index.js +21 -0
  162. package/dist/view/index.js.map +1 -0
  163. package/dist/view/normalize/check.d.ts +20 -0
  164. package/dist/view/normalize/check.js +78 -0
  165. package/dist/view/normalize/check.js.map +1 -0
  166. package/dist/view/normalize/emit.d.ts +13 -0
  167. package/dist/view/normalize/emit.js +136 -0
  168. package/dist/view/normalize/emit.js.map +1 -0
  169. package/dist/view/normalize/entities.d.ts +4 -0
  170. package/dist/view/normalize/entities.js +38 -0
  171. package/dist/view/normalize/entities.js.map +1 -0
  172. package/dist/view/normalize/errors.d.ts +9 -0
  173. package/dist/view/normalize/errors.js +19 -0
  174. package/dist/view/normalize/errors.js.map +1 -0
  175. package/dist/view/normalize/id.d.ts +7 -0
  176. package/dist/view/normalize/id.js +48 -0
  177. package/dist/view/normalize/id.js.map +1 -0
  178. package/dist/view/normalize/normalize.d.ts +15 -0
  179. package/dist/view/normalize/normalize.js +36 -0
  180. package/dist/view/normalize/normalize.js.map +1 -0
  181. package/dist/view/normalize/repairs.d.ts +24 -0
  182. package/dist/view/normalize/repairs.js +166 -0
  183. package/dist/view/normalize/repairs.js.map +1 -0
  184. package/dist/view/normalize/rules.d.ts +30 -0
  185. package/dist/view/normalize/rules.js +90 -0
  186. package/dist/view/normalize/rules.js.map +1 -0
  187. package/dist/view/normalize/shape.d.ts +5 -0
  188. package/dist/view/normalize/shape.js +38 -0
  189. package/dist/view/normalize/shape.js.map +1 -0
  190. package/dist/view/normalize/source.d.ts +13 -0
  191. package/dist/view/normalize/source.js +34 -0
  192. package/dist/view/normalize/source.js.map +1 -0
  193. package/dist/view/normalize/tag-states.d.ts +23 -0
  194. package/dist/view/normalize/tag-states.js +192 -0
  195. package/dist/view/normalize/tag-states.js.map +1 -0
  196. package/dist/view/normalize/tokenizer.d.ts +5 -0
  197. package/dist/view/normalize/tokenizer.js +233 -0
  198. package/dist/view/normalize/tokenizer.js.map +1 -0
  199. package/dist/view/normalize/tokens.d.ts +51 -0
  200. package/dist/view/normalize/tokens.js +24 -0
  201. package/dist/view/normalize/tokens.js.map +1 -0
  202. package/dist/view/normalize/tree.d.ts +59 -0
  203. package/dist/view/normalize/tree.js +190 -0
  204. package/dist/view/normalize/tree.js.map +1 -0
  205. package/dist/view/normalize/types.d.ts +126 -0
  206. package/dist/view/normalize/types.js +26 -0
  207. package/dist/view/normalize/types.js.map +1 -0
  208. package/dist/view/normalize/whitespace.d.ts +2 -0
  209. package/dist/view/normalize/whitespace.js +182 -0
  210. package/dist/view/normalize/whitespace.js.map +1 -0
  211. package/dist/view/prepare-stub.d.ts +3 -0
  212. package/dist/view/prepare-stub.js +7 -0
  213. package/dist/view/prepare-stub.js.map +1 -0
  214. package/dist/view/prepare.d.ts +8 -0
  215. package/dist/view/prepare.js +57 -0
  216. package/dist/view/prepare.js.map +1 -0
  217. package/dist/view/registry.d.ts +63 -0
  218. package/dist/view/registry.js +19 -0
  219. package/dist/view/registry.js.map +1 -0
  220. package/dist/view/render/adopt-attr.d.ts +14 -0
  221. package/dist/view/render/adopt-attr.js +77 -0
  222. package/dist/view/render/adopt-attr.js.map +1 -0
  223. package/dist/view/render/adopt-plan.d.ts +10 -0
  224. package/dist/view/render/adopt-plan.js +26 -0
  225. package/dist/view/render/adopt-plan.js.map +1 -0
  226. package/dist/view/render/adopt.d.ts +3 -0
  227. package/dist/view/render/adopt.js +269 -0
  228. package/dist/view/render/adopt.js.map +1 -0
  229. package/dist/view/render/attr-parts.d.ts +61 -0
  230. package/dist/view/render/attr-parts.js +229 -0
  231. package/dist/view/render/attr-parts.js.map +1 -0
  232. package/dist/view/render/child-part.d.ts +51 -0
  233. package/dist/view/render/child-part.js +170 -0
  234. package/dist/view/render/child-part.js.map +1 -0
  235. package/dist/view/render/dev-check.d.ts +12 -0
  236. package/dist/view/render/dev-check.js +122 -0
  237. package/dist/view/render/dev-check.js.map +1 -0
  238. package/dist/view/render/hook-part.d.ts +3 -0
  239. package/dist/view/render/hook-part.js +20 -0
  240. package/dist/view/render/hook-part.js.map +1 -0
  241. package/dist/view/render/hooks.d.ts +40 -0
  242. package/dist/view/render/hooks.js +46 -0
  243. package/dist/view/render/hooks.js.map +1 -0
  244. package/dist/view/render/hydrate.d.ts +10 -0
  245. package/dist/view/render/hydrate.js +33 -0
  246. package/dist/view/render/hydrate.js.map +1 -0
  247. package/dist/view/render/instance.d.ts +33 -0
  248. package/dist/view/render/instance.js +107 -0
  249. package/dist/view/render/instance.js.map +1 -0
  250. package/dist/view/render/list.d.ts +20 -0
  251. package/dist/view/render/list.js +156 -0
  252. package/dist/view/render/list.js.map +1 -0
  253. package/dist/view/render/mismatch.d.ts +10 -0
  254. package/dist/view/render/mismatch.js +53 -0
  255. package/dist/view/render/mismatch.js.map +1 -0
  256. package/dist/view/render/nodes.d.ts +10 -0
  257. package/dist/view/render/nodes.js +37 -0
  258. package/dist/view/render/nodes.js.map +1 -0
  259. package/dist/view/render/place.d.ts +18 -0
  260. package/dist/view/render/place.js +69 -0
  261. package/dist/view/render/place.js.map +1 -0
  262. package/dist/view/render/plan.d.ts +26 -0
  263. package/dist/view/render/plan.js +124 -0
  264. package/dist/view/render/plan.js.map +1 -0
  265. package/dist/view/render/raw.d.ts +18 -0
  266. package/dist/view/render/raw.js +68 -0
  267. package/dist/view/render/raw.js.map +1 -0
  268. package/dist/view/render/render.d.ts +12 -0
  269. package/dist/view/render/render.js +60 -0
  270. package/dist/view/render/render.js.map +1 -0
  271. package/dist/view/render/reorder.d.ts +4 -0
  272. package/dist/view/render/reorder.js +101 -0
  273. package/dist/view/render/reorder.js.map +1 -0
  274. package/dist/view/render/rows.d.ts +23 -0
  275. package/dist/view/render/rows.js +95 -0
  276. package/dist/view/render/rows.js.map +1 -0
  277. package/dist/view/render/seen.d.ts +11 -0
  278. package/dist/view/render/seen.js +17 -0
  279. package/dist/view/render/seen.js.map +1 -0
  280. package/dist/view/render/values.d.ts +29 -0
  281. package/dist/view/render/values.js +13 -0
  282. package/dist/view/render/values.js.map +1 -0
  283. package/dist/view/render/warn.d.ts +14 -0
  284. package/dist/view/render/warn.js +69 -0
  285. package/dist/view/render/warn.js.map +1 -0
  286. package/dist/view/server/component.d.ts +9 -0
  287. package/dist/view/server/component.js +42 -0
  288. package/dist/view/server/component.js.map +1 -0
  289. package/dist/view/server/escape.d.ts +10 -0
  290. package/dist/view/server/escape.js +34 -0
  291. package/dist/view/server/escape.js.map +1 -0
  292. package/dist/view/server/index.d.ts +28 -0
  293. package/dist/view/server/index.js +72 -0
  294. package/dist/view/server/index.js.map +1 -0
  295. package/dist/view/server/sha256.d.ts +2 -0
  296. package/dist/view/server/sha256.js +75 -0
  297. package/dist/view/server/sha256.js.map +1 -0
  298. package/dist/view/server/tags.d.ts +57 -0
  299. package/dist/view/server/tags.js +121 -0
  300. package/dist/view/server/tags.js.map +1 -0
  301. package/dist/view/server/values.d.ts +16 -0
  302. package/dist/view/server/values.js +86 -0
  303. package/dist/view/server/values.js.map +1 -0
  304. package/dist/view/server/writer.d.ts +16 -0
  305. package/dist/view/server/writer.js +155 -0
  306. package/dist/view/server/writer.js.map +1 -0
  307. package/dist/view/styles.d.ts +25 -0
  308. package/dist/view/styles.js +58 -0
  309. package/dist/view/styles.js.map +1 -0
  310. package/dist/view/template-element.d.ts +3 -0
  311. package/dist/view/template-element.js +25 -0
  312. package/dist/view/template-element.js.map +1 -0
  313. package/dist/view/template.d.ts +21 -0
  314. package/dist/view/template.js +30 -0
  315. package/dist/view/template.js.map +1 -0
  316. package/dist/vite.d.ts +45 -6
  317. package/dist/vite.js +150 -25
  318. package/dist/vite.js.map +1 -1
  319. package/package.json +41 -7
  320. package/dist/accessible-name.d.ts +0 -19
  321. package/dist/accessible-name.js +0 -56
  322. package/dist/accessible-name.js.map +0 -1
  323. package/dist/element-directive.d.ts +0 -20
  324. package/dist/element-directive.js +0 -31
  325. package/dist/element-directive.js.map +0 -1
  326. package/dist/internal/runtime.d.ts +0 -7
  327. package/dist/internal/runtime.js +0 -8
  328. package/dist/internal/runtime.js.map +0 -1
  329. package/dist/invalid.d.ts +0 -21
  330. package/dist/invalid.js +0 -70
  331. package/dist/invalid.js.map +0 -1
  332. package/dist/live-boolean.d.ts +0 -22
  333. package/dist/live-boolean.js +0 -34
  334. package/dist/live-boolean.js.map +0 -1
  335. package/dist/styles.d.ts +0 -12
  336. package/dist/styles.js +0 -11
  337. package/dist/styles.js.map +0 -1
  338. package/dist/textarea.d.ts +0 -26
  339. package/dist/textarea.js +0 -85
  340. package/dist/textarea.js.map +0 -1
@@ -1,9 +1,11 @@
1
- import type { LitElement } from 'lit';
2
1
  import type { DriverOverrides } from './command.js';
3
2
  import type { StoreOverrides } from './store.js';
4
3
  import type { ComponentSpec, Tagged } from './types.js';
5
- /** The custom element class produced by `define()`. */
6
- export interface GyralElement<S, M extends Tagged> extends LitElement {
4
+ /**
5
+ * The custom element `define()` produces: a plain `HTMLElement` (view/05-element.md "Public
6
+ * instance API"). Tests wait for rendering with `settled()`.
7
+ */
8
+ export interface GyralElement<S, M extends Tagged> extends HTMLElement {
7
9
  /** Current model state. */
8
10
  readonly state: S;
9
11
  /** Feeds a message through `update`, as if an intent had produced it. */
@@ -1 +1 @@
1
- {"version":3,"file":"element-types.js","sourceRoot":"","sources":["../src/element-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { LitElement } from 'lit';\nimport type { DriverOverrides } from './command.js';\nimport type { StoreOverrides } from './store.js';\nimport type { ComponentSpec, Tagged } from './types.js';\n\n/** The custom element class produced by `define()`. */\nexport interface GyralElement<S, M extends Tagged> extends LitElement {\n /** Current model state. */\n readonly state: S;\n /** Feeds a message through `update`, as if an intent had produced it. */\n send(msg: M): void;\n /** Per-instance driver substitutions by name (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /**\n * Per-instance store instances by store name (tests, islands). Checked before the nearest\n * `<gyral-stores>` provider and the document default (ADR 0013).\n */\n stores: StoreOverrides;\n /**\n * Messages applied through `update` right after `init`, before the first render. The server\n * uses it to render a rejected form with the same reducer as the JS path (ADR 0008). Ignored\n * when the element resumes from a hydration seed (the seed already contains their effect).\n */\n initialMessages: readonly Tagged[];\n}\n\nexport interface GyralElementClass<S, M extends Tagged, P, O extends Tagged = never> {\n /** Instances expose their declared props as settable properties. */\n new (): GyralElement<S, M> & { -readonly [K in keyof P]: P[K] };\n readonly spec: ComponentSpec<S, M, P>;\n readonly tagName: string;\n /** Type-only: the outputs this component emits (read by `child()`). */\n readonly outputs?: O;\n}\n"]}
1
+ {"version":3,"file":"element-types.js","sourceRoot":"","sources":["../src/element-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DriverOverrides } from './command.js';\nimport type { StoreOverrides } from './store.js';\nimport type { ComponentSpec, Tagged } from './types.js';\n\n/**\n * The custom element `define()` produces: a plain `HTMLElement` (view/05-element.md \"Public\n * instance API\"). Tests wait for rendering with `settled()`.\n */\nexport interface GyralElement<S, M extends Tagged> extends HTMLElement {\n /** Current model state. */\n readonly state: S;\n /** Feeds a message through `update`, as if an intent had produced it. */\n send(msg: M): void;\n /** Per-instance driver substitutions by name (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /**\n * Per-instance store instances by store name (tests, islands). Checked before the nearest\n * `<gyral-stores>` provider and the document default (ADR 0013).\n */\n stores: StoreOverrides;\n /**\n * Messages applied through `update` right after `init`, before the first render. The server\n * uses it to render a rejected form with the same reducer as the JS path (ADR 0008). Ignored\n * when the element resumes from a hydration seed (the seed already contains their effect).\n */\n initialMessages: readonly Tagged[];\n}\n\nexport interface GyralElementClass<S, M extends Tagged, P, O extends Tagged = never> {\n /** Instances expose their declared props as settable properties. */\n new (): GyralElement<S, M> & { -readonly [K in keyof P]: P[K] };\n readonly spec: ComponentSpec<S, M, P>;\n readonly tagName: string;\n /** Type-only: the outputs this component emits (read by `child()`). */\n readonly outputs?: O;\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { ComponentSpec, Tagged } from './types.js';
2
+ /** The element class for `spec` (not yet registered); define() types it for the spec. */
3
+ export declare function elementClass<S, M extends Tagged, P>(tag: string, spec: ComponentSpec<S, M, P>): CustomElementConstructor;
@@ -0,0 +1,269 @@
1
+ // The element define() creates (docs/design-docs/view/05-element.md): a plain HTMLElement
2
+ // subclass. Declared props are prototype accessors over Standard Schema builders; renders go
3
+ // through the global scheduler (view/04-scheduler.md); intents are delegated listeners on the
4
+ // root (ADR 0001). The model side (state, commands, stores) is host-model.ts.
5
+ import { DEVTOOLS_ENABLED, devConnect, devHydrated } from '#devtools';
6
+ import { HostModel } from './host-model.js';
7
+ import { hydrationCode, takeSeed, whenHydrationLoads } from './hydration.js';
8
+ import { DEFAULT_EVENTS, eventsOf, handleIntent, hostDepth, intentNames, listenForIntents, } from './intent.js';
9
+ import { isLight } from './light-dom.js';
10
+ import { features } from './features.js';
11
+ import { afterRender, markDirty, POST_HYDRATED, POST_INIT, POST_STATES, } from './scheduler.js';
12
+ import { stateSync } from './states.js';
13
+ import { render, sheetsFor } from './view/index.js';
14
+ const DEFER = 'defer-hydration';
15
+ /** The element class for `spec` (not yet registered); define() types it for the spec. */
16
+ export function elementClass(tag, spec) {
17
+ const table = (spec.props ?? {});
18
+ const names = Object.keys(table);
19
+ // Registered by the prop builders (features.ts): set whenever a component declares props.
20
+ const props = features.props;
21
+ const attrs = new Map();
22
+ const light = isLight(spec);
23
+ let sheets;
24
+ const parsers = spec.intent;
25
+ const model = spec;
26
+ class Element extends HTMLElement {
27
+ static { this.spec = spec; }
28
+ static { this.tagName = tag; }
29
+ static get observedAttributes() {
30
+ return [...attrs.keys(), DEFER];
31
+ }
32
+ static {
33
+ for (const name of names) {
34
+ const def = table[name];
35
+ const attr = def === undefined ? undefined : props.attributeOf(name, def);
36
+ if (attr !== undefined)
37
+ attrs.set(attr, name);
38
+ Object.defineProperty(this.prototype, name, {
39
+ configurable: true,
40
+ enumerable: true,
41
+ get() {
42
+ return this.#values[name];
43
+ },
44
+ set(value) {
45
+ this.#write(name, props.propertyValue(tag, name, table[name], value));
46
+ },
47
+ });
48
+ }
49
+ }
50
+ #values = {};
51
+ #root;
52
+ #rendered = false;
53
+ /** Something changed while disconnected: render on reconnect. */
54
+ #stale = false;
55
+ #serverRendered = false;
56
+ /** The first render adopts the server's DOM (07): resumed from a seed, root not empty. */
57
+ #hydrating = false;
58
+ /** init's commands for a server-rendered host: started after its first render. */
59
+ #afterInit = [];
60
+ #states;
61
+ #task = {
62
+ tag,
63
+ depth: 0,
64
+ render: () => {
65
+ this.#render();
66
+ },
67
+ };
68
+ #model = new HostModel({
69
+ el: this,
70
+ tag,
71
+ props: () => (names.length > 0 ? props.readProps(this.#values, table) : {}),
72
+ root: () => this.#root,
73
+ invalidate: (onFrame) => {
74
+ this.#invalidate(onFrame);
75
+ },
76
+ }, model);
77
+ constructor() {
78
+ super();
79
+ const self = this;
80
+ self['drivers'] ??= {};
81
+ self['stores'] ??= {};
82
+ self['initialMessages'] ??= [];
83
+ // Upgrade capture: a property set before the class was defined hides the accessor.
84
+ for (const name of names) {
85
+ if (!Object.hasOwn(this, name))
86
+ continue;
87
+ const value = self[name];
88
+ Reflect.deleteProperty(this, name);
89
+ self[name] = value;
90
+ }
91
+ }
92
+ get state() {
93
+ return this.#state();
94
+ }
95
+ #state() {
96
+ if (!this.#model.ready && names.length > 0)
97
+ props.warnMissing(tag, this.#values, table);
98
+ return this.#model.state(this.initialMessages);
99
+ }
100
+ send(msg) {
101
+ this.#model.dispatch(msg);
102
+ }
103
+ connectedCallback() {
104
+ if (this.#root !== undefined) {
105
+ this.#connect();
106
+ return;
107
+ }
108
+ this.#resume();
109
+ this.#begin();
110
+ }
111
+ /** `moveBefore()` keeps everything: no disconnect, no re-resolution (view/03-lists.md). */
112
+ connectedMoveCallback() {
113
+ // Intentionally empty.
114
+ }
115
+ disconnectedCallback() {
116
+ this.#model.disconnect();
117
+ if (DEVTOOLS_ENABLED)
118
+ devConnect(this, tag, false);
119
+ }
120
+ attributeChangedCallback(name, _old, raw) {
121
+ if (name === DEFER) {
122
+ if (raw === null && this.isConnected && this.#root === undefined)
123
+ this.#begin();
124
+ return;
125
+ }
126
+ const prop = attrs.get(name);
127
+ if (prop !== undefined)
128
+ this.#write(prop, props.attributeValue(tag, prop, table[prop], name, raw));
129
+ }
130
+ #write(name, value) {
131
+ if (Object.is(this.#values[name], value))
132
+ return;
133
+ this.#values[name] = value;
134
+ if (this.#model.ready)
135
+ this.#invalidate();
136
+ }
137
+ /** Hydration seeds (ADR 0012): the server's state and the props no attribute carried. */
138
+ #resume() {
139
+ if (this.#model.ready)
140
+ return;
141
+ const seed = takeSeed(this);
142
+ if (seed === undefined)
143
+ return;
144
+ for (const [name, value] of Object.entries(seed.props)) {
145
+ const def = table[name];
146
+ if (def === undefined || this.#values[name] !== undefined)
147
+ continue;
148
+ this.#values[name] = props.propertyValue(tag, name, def, value, 'the seed');
149
+ }
150
+ this.#afterInit = this.#model.resume(seed);
151
+ this.#serverRendered = true;
152
+ }
153
+ /**
154
+ * Starts the host, or waits: for the hydration code (a server-rendered host or an island,
155
+ * view/07-hydration.md "Loading"), then for an island's trigger (released by
156
+ * attributeChangedCallback).
157
+ */
158
+ #begin() {
159
+ const defer = this.hasAttribute(DEFER);
160
+ if (hydrationCode === undefined && (this.#serverRendered || defer)) {
161
+ whenHydrationLoads(() => {
162
+ if (this.isConnected && this.#root === undefined)
163
+ this.#begin();
164
+ });
165
+ }
166
+ else if (defer && hydrationCode !== null)
167
+ hydrationCode?.scheduleIsland(this);
168
+ else
169
+ this.#start();
170
+ }
171
+ #start() {
172
+ const root = light ? this : (this.shadowRoot ?? this.attachShadow({ mode: 'open' }));
173
+ this.#root = root;
174
+ this.#hydrating = this.#serverRendered && hydrationCode != null && root.hasChildNodes();
175
+ sheets ??= light ? [] : sheetsFor(spec.styles);
176
+ // Rendering fresh: a shadow root without a seed (hand-written DSD) or a server-rendered
177
+ // host whose hydration code failed to load starts empty.
178
+ if (!this.#hydrating) {
179
+ if (!light || this.#serverRendered)
180
+ root.replaceChildren();
181
+ if (!light)
182
+ root.adoptedStyleSheets = sheets;
183
+ }
184
+ this.#listen(DEFAULT_EVENTS);
185
+ if (spec.events !== undefined)
186
+ this.#listen(spec.events);
187
+ this.#state(); // init, unless resumed from a seed
188
+ this.#connect();
189
+ }
190
+ #connect() {
191
+ this.#task.depth = hostDepth(this);
192
+ const rebound = this.#model.connect();
193
+ if (DEVTOOLS_ENABLED)
194
+ devConnect(this, tag, true);
195
+ if (!this.#rendered || this.#stale || rebound)
196
+ this.#invalidate();
197
+ }
198
+ #invalidate(onFrame = false) {
199
+ if (this.#root === undefined)
200
+ return;
201
+ if (this.isConnected)
202
+ markDirty(this.#task, onFrame);
203
+ else
204
+ this.#stale = true;
205
+ }
206
+ /** One render (the scheduler calls it): PropsChanged first, then the view and its commit. */
207
+ #render() {
208
+ const root = this.#root;
209
+ if (root === undefined || !this.isConnected) {
210
+ this.#stale = true;
211
+ return;
212
+ }
213
+ this.#stale = false;
214
+ this.#model.syncProps(names);
215
+ const view = spec.view(this.state, intentNames, this.#model.ctx());
216
+ if (this.#hydrating) {
217
+ hydrationCode?.hydrateRoot(this, tag, view, root, light ? undefined : sheets, this.#seen);
218
+ this.#hydrating = false;
219
+ }
220
+ else
221
+ render(view, root, this.#seen);
222
+ if (spec.states !== undefined)
223
+ afterRender(POST_STATES, this.#syncStates);
224
+ if (this.#rendered)
225
+ return;
226
+ this.#rendered = true;
227
+ if (DEVTOOLS_ENABLED)
228
+ devHydrated(this, tag, this.#serverRendered);
229
+ if (this.#model.hasReducer('Hydrated')) {
230
+ const msg = { _tag: 'Hydrated', serverRendered: this.#serverRendered };
231
+ afterRender(POST_HYDRATED, () => {
232
+ this.#model.dispatch(msg);
233
+ });
234
+ }
235
+ const commands = this.#afterInit;
236
+ this.#afterInit = [];
237
+ if (commands.length > 0) {
238
+ afterRender(POST_INIT, () => {
239
+ this.#model.run(commands);
240
+ });
241
+ }
242
+ }
243
+ #syncStates = () => {
244
+ // ElementInternals is attached lazily, once, when the first states sync needs it (05).
245
+ this.#states ??= stateSync(this.attachInternals());
246
+ if (this.#states !== false && spec.states !== undefined)
247
+ this.#states(spec.states(this.state));
248
+ };
249
+ /** Intent event types this host's root listens for (view/05-element.md "Intent events"). */
250
+ #listening = new Set();
251
+ #listen(types) {
252
+ listenForIntents(this.#root, this.#listening, types, this.#onEvent);
253
+ }
254
+ /** Told about each template a render instantiates: listen for what its markup names. */
255
+ #seen = (markup) => {
256
+ this.#listen(eventsOf(markup));
257
+ };
258
+ #onEvent = (event) => {
259
+ if (this.#root === undefined)
260
+ return;
261
+ handleIntent(event, this.#root, parsers, tag, (msg) => {
262
+ if (msg !== undefined && this.isConnected)
263
+ this.#model.dispatch(msg);
264
+ });
265
+ };
266
+ }
267
+ return Element;
268
+ }
269
+ //# sourceMappingURL=element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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","sourcesContent":["// The element define() creates (docs/design-docs/view/05-element.md): a plain HTMLElement\n// subclass. Declared props are prototype accessors over Standard Schema builders; renders go\n// through the global scheduler (view/04-scheduler.md); intents are delegated listeners on the\n// root (ADR 0001). The model side (state, commands, stores) is host-model.ts.\nimport { DEVTOOLS_ENABLED, devConnect, devHydrated } from '#devtools';\nimport type { DriverOverrides } from './command.js';\nimport type { GyralElement } from './element-types.js';\nimport { HostModel, type ModelCommand } from './host-model.js';\nimport { hydrationCode, takeSeed, whenHydrationLoads } from './hydration.js';\nimport {\n DEFAULT_EVENTS,\n eventsOf,\n handleIntent,\n hostDepth,\n intentNames,\n listenForIntents,\n} from './intent.js';\nimport { isLight } from './light-dom.js';\nimport { features } from './features.js';\nimport type { PropFeature, PropTable } from './props.js';\nimport {\n afterRender,\n markDirty,\n POST_HYDRATED,\n POST_INIT,\n POST_STATES,\n type HostTask,\n} from './scheduler.js';\nimport { stateSync, type StateSync } from './states.js';\nimport type { StoreOverrides } from './store.js';\nimport type { ComponentSpec, IntentNames, IntentParser, Tagged } from './types.js';\nimport { render, sheetsFor, type Markup } from './view/index.js';\n\nconst DEFER = 'defer-hydration';\n\ntype Bag = Record<string, unknown>;\n/** The element class for `spec` (not yet registered); define() types it for the spec. */\nexport function elementClass<S, M extends Tagged, P>(\n tag: string,\n spec: ComponentSpec<S, M, P>,\n): CustomElementConstructor {\n const table = (spec.props ?? {}) as PropTable;\n const names = Object.keys(table);\n // Registered by the prop builders (features.ts): set whenever a component declares props.\n const props = features.props as PropFeature;\n const attrs = new Map<string, string>();\n const light = isLight(spec);\n let sheets: CSSStyleSheet[] | undefined;\n const parsers = spec.intent as Readonly<Record<string, IntentParser<M> | undefined>>;\n const model = spec as unknown as ComponentSpec<S, Tagged, P>;\n\n class Element extends HTMLElement implements GyralElement<S, M> {\n static readonly spec = spec;\n static readonly tagName = tag;\n static get observedAttributes(): string[] {\n return [...attrs.keys(), DEFER];\n }\n\n static {\n for (const name of names) {\n const def = table[name];\n const attr = def === undefined ? undefined : props.attributeOf(name, def);\n if (attr !== undefined) attrs.set(attr, name);\n Object.defineProperty(this.prototype, name, {\n configurable: true,\n enumerable: true,\n get(this: Element): unknown {\n return this.#values[name];\n },\n set(this: Element, value: unknown) {\n this.#write(name, props.propertyValue(tag, name, table[name], value));\n },\n });\n }\n }\n\n declare drivers: DriverOverrides;\n declare stores: StoreOverrides;\n declare initialMessages: readonly Tagged[];\n\n #values: Bag = {};\n #root: ShadowRoot | HTMLElement | undefined;\n #rendered = false;\n /** Something changed while disconnected: render on reconnect. */\n #stale = false;\n #serverRendered = false;\n /** The first render adopts the server's DOM (07): resumed from a seed, root not empty. */\n #hydrating = false;\n /** init's commands for a server-rendered host: started after its first render. */\n #afterInit: readonly ModelCommand[] = [];\n #states: StateSync | false | undefined;\n #task: HostTask = {\n tag,\n depth: 0,\n render: () => {\n this.#render();\n },\n };\n #model = new HostModel<S, P>(\n {\n el: this,\n tag,\n props: () => (names.length > 0 ? props.readProps(this.#values, table) : {}),\n root: () => this.#root,\n invalidate: (onFrame) => {\n this.#invalidate(onFrame);\n },\n },\n model,\n );\n\n constructor() {\n super();\n const self = this as unknown as Bag;\n self['drivers'] ??= {};\n self['stores'] ??= {};\n self['initialMessages'] ??= [];\n // Upgrade capture: a property set before the class was defined hides the accessor.\n for (const name of names) {\n if (!Object.hasOwn(this, name)) continue;\n const value = self[name];\n Reflect.deleteProperty(this, name);\n self[name] = value;\n }\n }\n\n get state(): S {\n return this.#state();\n }\n\n #state(): S {\n if (!this.#model.ready && names.length > 0) props.warnMissing(tag, this.#values, table);\n return this.#model.state(this.initialMessages);\n }\n\n send(msg: M): void {\n this.#model.dispatch(msg);\n }\n\n connectedCallback(): void {\n if (this.#root !== undefined) {\n this.#connect();\n return;\n }\n this.#resume();\n this.#begin();\n }\n\n /** `moveBefore()` keeps everything: no disconnect, no re-resolution (view/03-lists.md). */\n connectedMoveCallback(): void {\n // Intentionally empty.\n }\n\n disconnectedCallback(): void {\n this.#model.disconnect();\n if (DEVTOOLS_ENABLED) devConnect(this, tag, false);\n }\n\n attributeChangedCallback(name: string, _old: string | null, raw: string | null): void {\n if (name === DEFER) {\n if (raw === null && this.isConnected && this.#root === undefined) this.#begin();\n return;\n }\n const prop = attrs.get(name);\n if (prop !== undefined)\n this.#write(prop, props.attributeValue(tag, prop, table[prop], name, raw));\n }\n\n #write(name: string, value: unknown): void {\n if (Object.is(this.#values[name], value)) return;\n this.#values[name] = value;\n if (this.#model.ready) this.#invalidate();\n }\n\n /** Hydration seeds (ADR 0012): the server's state and the props no attribute carried. */\n #resume(): void {\n if (this.#model.ready) return;\n const seed = takeSeed(this);\n if (seed === undefined) return;\n for (const [name, value] of Object.entries(seed.props)) {\n const def = table[name];\n if (def === undefined || this.#values[name] !== undefined) continue;\n this.#values[name] = props.propertyValue(tag, name, def, value, 'the seed');\n }\n this.#afterInit = this.#model.resume(seed);\n this.#serverRendered = true;\n }\n\n /**\n * Starts the host, or waits: for the hydration code (a server-rendered host or an island,\n * view/07-hydration.md \"Loading\"), then for an island's trigger (released by\n * attributeChangedCallback).\n */\n #begin(): void {\n const defer = this.hasAttribute(DEFER);\n if (hydrationCode === undefined && (this.#serverRendered || defer)) {\n whenHydrationLoads(() => {\n if (this.isConnected && this.#root === undefined) this.#begin();\n });\n } else if (defer && hydrationCode !== null) hydrationCode?.scheduleIsland(this);\n else this.#start();\n }\n\n #start(): void {\n const root = light ? this : (this.shadowRoot ?? this.attachShadow({ mode: 'open' }));\n this.#root = root;\n this.#hydrating = this.#serverRendered && hydrationCode != null && root.hasChildNodes();\n sheets ??= light ? [] : sheetsFor(spec.styles);\n // Rendering fresh: a shadow root without a seed (hand-written DSD) or a server-rendered\n // host whose hydration code failed to load starts empty.\n if (!this.#hydrating) {\n if (!light || this.#serverRendered) root.replaceChildren();\n if (!light) (root as ShadowRoot).adoptedStyleSheets = sheets;\n }\n this.#listen(DEFAULT_EVENTS);\n if (spec.events !== undefined) this.#listen(spec.events);\n this.#state(); // init, unless resumed from a seed\n this.#connect();\n }\n\n #connect(): void {\n this.#task.depth = hostDepth(this);\n const rebound = this.#model.connect();\n if (DEVTOOLS_ENABLED) devConnect(this, tag, true);\n if (!this.#rendered || this.#stale || rebound) this.#invalidate();\n }\n\n #invalidate(onFrame = false): void {\n if (this.#root === undefined) return;\n if (this.isConnected) markDirty(this.#task, onFrame);\n else this.#stale = true;\n }\n\n /** One render (the scheduler calls it): PropsChanged first, then the view and its commit. */\n #render(): void {\n const root = this.#root;\n if (root === undefined || !this.isConnected) {\n this.#stale = true;\n return;\n }\n this.#stale = false;\n this.#model.syncProps(names);\n const view = spec.view(this.state, intentNames as IntentNames<M>, this.#model.ctx());\n if (this.#hydrating) {\n hydrationCode?.hydrateRoot(this, tag, view, root, light ? undefined : sheets, this.#seen);\n this.#hydrating = false;\n } else render(view, root, this.#seen);\n if (spec.states !== undefined) afterRender(POST_STATES, this.#syncStates);\n if (this.#rendered) return;\n this.#rendered = true;\n if (DEVTOOLS_ENABLED) devHydrated(this, tag, this.#serverRendered);\n if (this.#model.hasReducer('Hydrated')) {\n const msg = { _tag: 'Hydrated', serverRendered: this.#serverRendered } as Tagged;\n afterRender(POST_HYDRATED, () => {\n this.#model.dispatch(msg);\n });\n }\n const commands = this.#afterInit;\n this.#afterInit = [];\n if (commands.length > 0) {\n afterRender(POST_INIT, () => {\n this.#model.run(commands);\n });\n }\n }\n\n #syncStates = (): void => {\n // ElementInternals is attached lazily, once, when the first states sync needs it (05).\n this.#states ??= stateSync(this.attachInternals());\n if (this.#states !== false && spec.states !== undefined)\n this.#states(spec.states(this.state));\n };\n\n /** Intent event types this host's root listens for (view/05-element.md \"Intent events\"). */\n #listening = new Set<string>();\n\n #listen(types: readonly string[]): void {\n listenForIntents(this.#root as Node, this.#listening, types, this.#onEvent);\n }\n\n /** Told about each template a render instantiates: listen for what its markup names. */\n #seen = (markup: Markup): void => {\n this.#listen(eventsOf(markup));\n };\n\n #onEvent = (event: Event): void => {\n if (this.#root === undefined) return;\n handleIntent(event, this.#root, parsers, tag, (msg) => {\n if (msg !== undefined && this.isConnected) this.#model.dispatch(msg);\n });\n };\n }\n return Element;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { Rule, Scope } from 'eslint';
2
+ export type Node = Rule.Node;
3
+ export type NodeOf<T extends Node['type']> = Extract<Node, {
4
+ type: T;
5
+ }>;
6
+ /** The default template sources, as in the Vite preset's `sources` option. */
7
+ export declare const DEFAULT_SOURCES: readonly string[];
8
+ /** The options both rules take. */
9
+ export declare const OPTIONS_SCHEMA: {
10
+ type: string;
11
+ properties: {
12
+ sources: {
13
+ type: string;
14
+ items: {
15
+ type: string;
16
+ };
17
+ };
18
+ };
19
+ additionalProperties: boolean;
20
+ }[];
21
+ export declare function sourcesOf(context: Rule.RuleContext): readonly string[];
22
+ export declare function findVariable(scope: Scope.Scope | null, name: string): Scope.Variable | undefined;
23
+ /** Whether `node` (a tag or callee) is the template sources' `name` export. */
24
+ export declare function isGyral(context: Rule.RuleContext, node: Node, name: string): boolean;
@@ -0,0 +1,78 @@
1
+ // Which identifiers are Gyral's `html` and `each` (@gyral/core/eslint). Like the template
2
+ // compiler (compiler/scan.ts), a binding counts only when it is imported from a template
3
+ // source: `import { html } from '@gyral/core'` (any local name) or `import * as g` used as
4
+ // `g.html`. ESLint's scope analysis resolves the name, so shadowing is handled. Core's own code
5
+ // and tests import from core's modules by relative path; those count too.
6
+ import { dirname, resolve } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ /** The default template sources, as in the Vite preset's `sources` option. */
9
+ export const DEFAULT_SOURCES = ['@gyral/core'];
10
+ /** The options both rules take. */
11
+ export const OPTIONS_SCHEMA = [
12
+ {
13
+ type: 'object',
14
+ properties: { sources: { type: 'array', items: { type: 'string' } } },
15
+ additionalProperties: false,
16
+ },
17
+ ];
18
+ /** Core's modules that export `html` and `each` (this file is src/eslint/ or dist/eslint/). */
19
+ const CORE_MODULES = ['../index', '../view/index', '../view/template', '../view/render/values']
20
+ .flatMap((name) => ['.ts', '.js'].map((ext) => new URL(`${name}${ext}`, import.meta.url)))
21
+ .map((url) => fileURLToPath(url));
22
+ export function sourcesOf(context) {
23
+ const options = context.options[0];
24
+ const sources = typeof options === 'object' && options !== null && 'sources' in options
25
+ ? options.sources
26
+ : undefined;
27
+ return Array.isArray(sources)
28
+ ? sources.filter((s) => typeof s === 'string')
29
+ : DEFAULT_SOURCES;
30
+ }
31
+ function isSource(context, specifier) {
32
+ if (sourcesOf(context).includes(specifier))
33
+ return true;
34
+ if (!specifier.startsWith('.'))
35
+ return false;
36
+ return CORE_MODULES.includes(resolve(dirname(context.filename), specifier));
37
+ }
38
+ /** typescript-eslint's `import type` marker (not in ESTree). */
39
+ const typeOnly = (node) => 'importKind' in node && node.importKind === 'type';
40
+ export function findVariable(scope, name) {
41
+ for (let s = scope; s !== null; s = s.upper) {
42
+ const v = s.set.get(name);
43
+ if (v !== undefined)
44
+ return v;
45
+ }
46
+ return undefined;
47
+ }
48
+ /** How `id` was imported from a template source: the export's name, or `*`. */
49
+ function importedAs(context, id) {
50
+ const variable = findVariable(context.sourceCode.getScope(id), id.name);
51
+ const def = variable?.defs[0];
52
+ if (def?.type !== 'ImportBinding')
53
+ return undefined;
54
+ const declaration = def.parent;
55
+ if (typeOnly(declaration) || !isSource(context, String(declaration.source.value))) {
56
+ return undefined;
57
+ }
58
+ const spec = def.node;
59
+ if (spec.type === 'ImportNamespaceSpecifier')
60
+ return '*';
61
+ if (spec.type !== 'ImportSpecifier' || typeOnly(spec))
62
+ return undefined;
63
+ const { imported } = spec;
64
+ return imported.type === 'Identifier' ? imported.name : String(imported.value);
65
+ }
66
+ /** Whether `node` (a tag or callee) is the template sources' `name` export. */
67
+ export function isGyral(context, node, name) {
68
+ if (node.type === 'Identifier')
69
+ return importedAs(context, node) === name;
70
+ if (node.type !== 'MemberExpression' || node.computed || node.object.type !== 'Identifier') {
71
+ return false;
72
+ }
73
+ const { property } = node;
74
+ return (property.type === 'Identifier' &&
75
+ property.name === name &&
76
+ importedAs(context, node.object) === '*');
77
+ }
78
+ //# sourceMappingURL=imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.js","sourceRoot":"","sources":["../../src/eslint/imports.ts"],"names":[],"mappings":"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","sourcesContent":["// Which identifiers are Gyral's `html` and `each` (@gyral/core/eslint). Like the template\n// compiler (compiler/scan.ts), a binding counts only when it is imported from a template\n// source: `import { html } from '@gyral/core'` (any local name) or `import * as g` used as\n// `g.html`. ESLint's scope analysis resolves the name, so shadowing is handled. Core's own code\n// and tests import from core's modules by relative path; those count too.\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Rule, Scope } from 'eslint';\n\nexport type Node = Rule.Node;\nexport type NodeOf<T extends Node['type']> = Extract<Node, { type: T }>;\n\n/** The default template sources, as in the Vite preset's `sources` option. */\nexport const DEFAULT_SOURCES: readonly string[] = ['@gyral/core'];\n\n/** The options both rules take. */\nexport const OPTIONS_SCHEMA = [\n {\n type: 'object',\n properties: { sources: { type: 'array', items: { type: 'string' } } },\n additionalProperties: false,\n },\n];\n\n/** Core's modules that export `html` and `each` (this file is src/eslint/ or dist/eslint/). */\nconst CORE_MODULES = ['../index', '../view/index', '../view/template', '../view/render/values']\n .flatMap((name) => ['.ts', '.js'].map((ext) => new URL(`${name}${ext}`, import.meta.url)))\n .map((url) => fileURLToPath(url));\n\nexport function sourcesOf(context: Rule.RuleContext): readonly string[] {\n const options: unknown = context.options[0];\n const sources =\n typeof options === 'object' && options !== null && 'sources' in options\n ? options.sources\n : undefined;\n return Array.isArray(sources)\n ? sources.filter((s): s is string => typeof s === 'string')\n : DEFAULT_SOURCES;\n}\n\nfunction isSource(context: Rule.RuleContext, specifier: string): boolean {\n if (sourcesOf(context).includes(specifier)) return true;\n if (!specifier.startsWith('.')) return false;\n return CORE_MODULES.includes(resolve(dirname(context.filename), specifier));\n}\n\n/** typescript-eslint's `import type` marker (not in ESTree). */\nconst typeOnly = (node: object): boolean => 'importKind' in node && node.importKind === 'type';\n\nexport function findVariable(scope: Scope.Scope | null, name: string): Scope.Variable | undefined {\n for (let s = scope; s !== null; s = s.upper) {\n const v = s.set.get(name);\n if (v !== undefined) return v;\n }\n return undefined;\n}\n\n/** How `id` was imported from a template source: the export's name, or `*`. */\nfunction importedAs(context: Rule.RuleContext, id: NodeOf<'Identifier'>): string | undefined {\n const variable = findVariable(context.sourceCode.getScope(id), id.name);\n const def = variable?.defs[0];\n if (def?.type !== 'ImportBinding') return undefined;\n const declaration = def.parent;\n if (typeOnly(declaration) || !isSource(context, String(declaration.source.value))) {\n return undefined;\n }\n const spec = def.node;\n if (spec.type === 'ImportNamespaceSpecifier') return '*';\n if (spec.type !== 'ImportSpecifier' || typeOnly(spec)) return undefined;\n const { imported } = spec;\n return imported.type === 'Identifier' ? imported.name : String(imported.value);\n}\n\n/** Whether `node` (a tag or callee) is the template sources' `name` export. */\nexport function isGyral(context: Rule.RuleContext, node: Node, name: string): boolean {\n if (node.type === 'Identifier') return importedAs(context, node) === name;\n if (node.type !== 'MemberExpression' || node.computed || node.object.type !== 'Identifier') {\n return false;\n }\n const { property } = node;\n return (\n property.type === 'Identifier' &&\n property.name === name &&\n importedAs(context, node.object as NodeOf<'Identifier'>) === '*'\n );\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { ESLint, Linter } from 'eslint';
2
+ import { rowPurityRule } from './row-purity-rule.js';
3
+ import { templateRule } from './template-rule.js';
4
+ export interface GyralPlugin extends ESLint.Plugin {
5
+ readonly rules: {
6
+ readonly template: typeof templateRule;
7
+ readonly 'each-row-purity': typeof rowPurityRule;
8
+ };
9
+ readonly configs: {
10
+ readonly recommended: Linter.Config;
11
+ };
12
+ }
13
+ declare const plugin: GyralPlugin;
14
+ export default plugin;
15
+ export { rowPurityRule, templateRule };
@@ -0,0 +1,15 @@
1
+ import { rowPurityRule } from './row-purity-rule.js';
2
+ import { templateRule } from './template-rule.js';
3
+ const rules = { template: templateRule, 'each-row-purity': rowPurityRule };
4
+ const configs = { recommended: {} };
5
+ const plugin = { meta: { name: '@gyral/core/eslint' }, rules, configs };
6
+ // The config names the plugin object itself, so it can be combined with configs that register
7
+ // `gyral` too (ESLint rejects two different objects under one plugin name).
8
+ configs.recommended = {
9
+ name: 'gyral/recommended',
10
+ plugins: { gyral: plugin },
11
+ rules: { 'gyral/template': 'error', 'gyral/each-row-purity': 'error' },
12
+ };
13
+ export default plugin;
14
+ export { rowPurityRule, templateRule };
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC;AAC3E,MAAM,OAAO,GAAmC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;AAEpE,MAAM,MAAM,GAAgB,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAErF,8FAA8F;AAC9F,4EAA4E;AAC5E,OAAO,CAAC,WAAW,GAAG;IACpB,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;IAC1B,KAAK,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,uBAAuB,EAAE,OAAO,EAAE;CACvE,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC","sourcesContent":["// @gyral/core/eslint: the template rules (view/09-template-rules.md) in the editor, as an\n// ESLint flat-config plugin. ESLint is an optional peer dependency; nothing here reaches a\n// browser or server bundle.\n//\n// import gyral from '@gyral/core/eslint';\n// export default [gyral.configs.recommended];\nimport type { ESLint, Linter } from 'eslint';\nimport { rowPurityRule } from './row-purity-rule.js';\nimport { templateRule } from './template-rule.js';\n\nexport interface GyralPlugin extends ESLint.Plugin {\n readonly rules: {\n readonly template: typeof templateRule;\n readonly 'each-row-purity': typeof rowPurityRule;\n };\n readonly configs: { readonly recommended: Linter.Config };\n}\n\nconst rules = { template: templateRule, 'each-row-purity': rowPurityRule };\nconst configs: { recommended: Linter.Config } = { recommended: {} };\n\nconst plugin: GyralPlugin = { meta: { name: '@gyral/core/eslint' }, rules, configs };\n\n// The config names the plugin object itself, so it can be combined with configs that register\n// `gyral` too (ESLint rejects two different objects under one plugin name).\nconfigs.recommended = {\n name: 'gyral/recommended',\n plugins: { gyral: plugin },\n rules: { 'gyral/template': 'error', 'gyral/each-row-purity': 'error' },\n};\n\nexport default plugin;\nexport { rowPurityRule, templateRule };\n"]}
@@ -0,0 +1,33 @@
1
+ /** One quasi of a template literal: where its text starts in the file, and its offset map. */
2
+ export interface Quasi {
3
+ /** File offset of the quasi's first character (after the backtick or `}`). */
4
+ readonly start: number;
5
+ /** File offset where the quasi's text ends (at the closing backtick or `${`). */
6
+ readonly end: number;
7
+ /** For each cooked offset (and one past the end), the offset in the source text. */
8
+ readonly map: readonly number[];
9
+ }
10
+ /**
11
+ * Maps each offset of the cooked string to the source text it came from. Returns undefined when
12
+ * decoding `text` doesn't give `cooked` (the caller then points at the quasi's start).
13
+ */
14
+ export declare function cookedMap(text: string, cooked: string): number[] | undefined;
15
+ /**
16
+ * Maps each offset of `minified` (and one past its end) to `original`. The minifier only
17
+ * collapses a whitespace run to one space or drops it, and copies everything else.
18
+ */
19
+ export declare function minifiedMap(original: string, minified: string): number[];
20
+ /** A source range, as file offsets. */
21
+ export interface Span {
22
+ readonly start: number;
23
+ readonly end: number;
24
+ }
25
+ /** A position in the normalizer's minified strings: string index, offset (view/normalize/check.ts). */
26
+ type At = readonly [index: number, offset: number];
27
+ /**
28
+ * The range to report for a template's first error: the token the tree builder was handling
29
+ * (a tag, an end tag, text) when it has one; the whole `${…}` when the error is at a hole
30
+ * (including `<${…}>`, where the tag name would be); else the character the tokenizer stopped at.
31
+ */
32
+ export declare function errorSpan(quasis: readonly Quasi[], originals: readonly string[], minified: readonly string[], at: At, from: At | undefined): Span;
33
+ export {};