@fluixi/compiler 1.0.0-alpha.82 → 1.0.0-alpha.84

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 (188) hide show
  1. package/dist/analyze/expr-shape.cjs +62 -1
  2. package/dist/analyze/expr-shape.d.cts +14 -0
  3. package/dist/analyze/expr-shape.mjs +41 -1
  4. package/dist/analyze/intrinsics.cjs +80 -1
  5. package/dist/analyze/intrinsics.d.cts +66 -0
  6. package/dist/analyze/intrinsics.mjs +59 -1
  7. package/dist/analyze/paren.cjs +134 -0
  8. package/dist/analyze/paren.d.cts +23 -0
  9. package/dist/analyze/paren.d.ts +24 -0
  10. package/dist/analyze/paren.d.ts.map +1 -0
  11. package/dist/analyze/paren.js +168 -0
  12. package/dist/analyze/paren.mjs +113 -0
  13. package/dist/analyze/props-destructure.cjs +226 -1
  14. package/dist/analyze/props-destructure.d.cts +83 -0
  15. package/dist/analyze/props-destructure.mjs +205 -1
  16. package/dist/analyze/reactive-bindings.cjs +190 -1
  17. package/dist/analyze/reactive-bindings.d.cts +42 -0
  18. package/dist/analyze/reactive-bindings.mjs +167 -1
  19. package/dist/analyze/reactive-expr.cjs +38 -1
  20. package/dist/analyze/reactive-expr.d.cts +38 -0
  21. package/dist/analyze/reactive-expr.mjs +17 -1
  22. package/dist/analyze/static-graph.cjs +811 -0
  23. package/dist/analyze/static-graph.d.cts +102 -0
  24. package/dist/analyze/static-graph.d.ts +103 -0
  25. package/dist/analyze/static-graph.d.ts.map +1 -0
  26. package/dist/analyze/static-graph.js +869 -0
  27. package/dist/analyze/static-graph.mjs +793 -0
  28. package/dist/babel-NRW4EFTG.mjs +636 -0
  29. package/dist/chunk-3YKZ4GNH.mjs +11 -0
  30. package/dist/chunk-EZR7NZOY.mjs +1718 -0
  31. package/dist/chunk-FSSWUXUR.mjs +16 -0
  32. package/dist/chunk-MJV3UXIL.mjs +158 -0
  33. package/dist/chunk-R2VHAZAX.mjs +1078 -0
  34. package/dist/codegen/backend.cjs +18 -1
  35. package/dist/codegen/backend.d.cts +35 -0
  36. package/dist/codegen/backends/imperative.cjs +542 -1
  37. package/dist/codegen/backends/imperative.d.cts +11 -0
  38. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  39. package/dist/codegen/backends/imperative.js +68 -13
  40. package/dist/codegen/backends/imperative.mjs +519 -1
  41. package/dist/codegen/contract.cjs +52 -1
  42. package/dist/codegen/contract.d.cts +24 -0
  43. package/dist/codegen/contract.mjs +31 -1
  44. package/dist/codegen/partial-template.cjs +176 -1
  45. package/dist/codegen/partial-template.d.cts +61 -0
  46. package/dist/codegen/partial-template.mjs +153 -1
  47. package/dist/codegen/serialize-static.cjs +125 -1
  48. package/dist/codegen/serialize-static.d.cts +38 -0
  49. package/dist/codegen/serialize-static.d.ts +16 -0
  50. package/dist/codegen/serialize-static.d.ts.map +1 -1
  51. package/dist/codegen/serialize-static.js +25 -0
  52. package/dist/codegen/serialize-static.mjs +104 -1
  53. package/dist/frontend/babel/build-ir-lit.cjs +1509 -1
  54. package/dist/frontend/babel/build-ir-lit.d.cts +26 -0
  55. package/dist/frontend/babel/build-ir-lit.mjs +1478 -1
  56. package/dist/frontend/babel/build-ir.cjs +1524 -1
  57. package/dist/frontend/babel/build-ir.d.cts +35 -0
  58. package/dist/frontend/babel/build-ir.mjs +1495 -1
  59. package/dist/frontend/babel/index.cjs +2186 -1
  60. package/dist/frontend/babel/index.d.cts +33 -0
  61. package/dist/frontend/babel/index.mjs +2156 -1
  62. package/dist/frontend/babel/lower-template.cjs +1507 -1
  63. package/dist/frontend/babel/lower-template.d.cts +23 -0
  64. package/dist/frontend/babel/lower-template.mjs +1478 -1
  65. package/dist/frontend/babel/plugin.cjs +2132 -1
  66. package/dist/frontend/babel/plugin.d.cts +95 -0
  67. package/dist/frontend/babel/plugin.mjs +2103 -1
  68. package/dist/frontend/babel/server-functions.cjs +110 -1
  69. package/dist/frontend/babel/server-functions.d.cts +10 -0
  70. package/dist/frontend/babel/server-functions.mjs +89 -1
  71. package/dist/frontend/babel/types.cjs +18 -1
  72. package/dist/frontend/babel/types.d.cts +33 -0
  73. package/dist/frontend/types.cjs +18 -1
  74. package/dist/frontend/types.d.cts +25 -0
  75. package/dist/frontend/vocabulary.cjs +98 -0
  76. package/dist/frontend/vocabulary.d.cts +79 -0
  77. package/dist/frontend/vocabulary.d.ts +80 -0
  78. package/dist/frontend/vocabulary.d.ts.map +1 -0
  79. package/dist/frontend/vocabulary.js +124 -0
  80. package/dist/frontend/vocabulary.mjs +77 -0
  81. package/dist/index.cjs +1594 -13
  82. package/dist/index.d.cts +27 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +3 -0
  85. package/dist/index.mjs +1574 -13
  86. package/dist/integrations.cjs +4307 -25
  87. package/dist/integrations.d.cts +215 -0
  88. package/dist/integrations.d.ts +16 -0
  89. package/dist/integrations.d.ts.map +1 -1
  90. package/dist/integrations.js +21 -2
  91. package/dist/integrations.mjs +531 -17
  92. package/dist/ir/nodes.cjs +18 -1
  93. package/dist/ir/nodes.d.cts +168 -0
  94. package/dist/ir/nodes.d.ts +46 -0
  95. package/dist/ir/nodes.d.ts.map +1 -1
  96. package/dist/lower/compile-template.cjs +1356 -1
  97. package/dist/lower/compile-template.d.cts +69 -0
  98. package/dist/lower/compile-template.d.ts +12 -0
  99. package/dist/lower/compile-template.d.ts.map +1 -1
  100. package/dist/lower/compile-template.js +5 -1
  101. package/dist/lower/compile-template.mjs +1336 -1
  102. package/dist/lower/jsx.cjs +529 -1
  103. package/dist/lower/jsx.d.cts +25 -0
  104. package/dist/lower/jsx.d.ts +2 -0
  105. package/dist/lower/jsx.d.ts.map +1 -1
  106. package/dist/lower/jsx.js +27 -4
  107. package/dist/lower/jsx.mjs +506 -1
  108. package/dist/lower/template.cjs +1061 -1
  109. package/dist/lower/template.d.cts +53 -0
  110. package/dist/lower/template.d.ts +14 -1
  111. package/dist/lower/template.d.ts.map +1 -1
  112. package/dist/lower/template.js +106 -19
  113. package/dist/lower/template.mjs +1042 -1
  114. package/dist/lower/text.cjs +42 -1
  115. package/dist/lower/text.d.cts +10 -0
  116. package/dist/lower/text.mjs +21 -1
  117. package/dist/optimize/analyze-static.cjs +112 -1
  118. package/dist/optimize/analyze-static.d.cts +39 -0
  119. package/dist/optimize/analyze-static.mjs +91 -1
  120. package/dist/optimize/collapse-strategies.cjs +90 -1
  121. package/dist/optimize/collapse-strategies.d.cts +30 -0
  122. package/dist/optimize/collapse-strategies.mjs +67 -1
  123. package/dist/options.cjs +33 -1
  124. package/dist/options.d.cts +39 -0
  125. package/dist/options.mjs +12 -1
  126. package/dist/resolve/builtins.cjs +73 -1
  127. package/dist/resolve/builtins.d.cts +68 -0
  128. package/dist/resolve/builtins.mjs +53 -1
  129. package/dist/resolve/component-globals.cjs +233 -13
  130. package/dist/resolve/component-globals.d.cts +80 -0
  131. package/dist/resolve/component-globals.mjs +210 -13
  132. package/dist/resolve/component-resolver.cjs +80 -1
  133. package/dist/resolve/component-resolver.d.cts +68 -0
  134. package/dist/resolve/component-resolver.mjs +59 -1
  135. package/dist/resolve/load-directive.cjs +93 -1
  136. package/dist/resolve/load-directive.d.cts +53 -0
  137. package/dist/resolve/load-directive.mjs +72 -1
  138. package/dist/resolve/resolve-ir.cjs +108 -1
  139. package/dist/resolve/resolve-ir.d.cts +53 -0
  140. package/dist/resolve/resolve-ir.mjs +85 -1
  141. package/dist/resolve/write-globals.cjs +318 -13
  142. package/dist/resolve/write-globals.d.cts +30 -0
  143. package/dist/resolve/write-globals.mjs +297 -13
  144. package/dist/server-fns-4ZXN6DYU.mjs +78 -0
  145. package/dist/server-functions-V6D6DRIA.mjs +82 -0
  146. package/dist/transform/bindings.cjs +71 -1
  147. package/dist/transform/bindings.d.cts +16 -0
  148. package/dist/transform/bindings.mjs +50 -1
  149. package/dist/transform/index.cjs +2732 -8
  150. package/dist/transform/index.d.cts +15 -0
  151. package/dist/transform/index.mjs +3773 -17
  152. package/dist/transform/intrinsics.cjs +216 -3
  153. package/dist/transform/intrinsics.d.cts +21 -0
  154. package/dist/transform/intrinsics.mjs +193 -3
  155. package/dist/transform/props.cjs +441 -3
  156. package/dist/transform/props.d.cts +45 -0
  157. package/dist/transform/props.mjs +1482 -12
  158. package/dist/transform/server-fn-id.cjs +35 -1
  159. package/dist/transform/server-fn-id.d.cts +9 -0
  160. package/dist/transform/server-fn-id.mjs +14 -1
  161. package/dist/transform/server-fns.cjs +114 -2
  162. package/dist/transform/server-fns.d.cts +12 -0
  163. package/dist/transform/server-fns.mjs +1155 -11
  164. package/dist/transform/source-locations.cjs +330 -0
  165. package/dist/transform/source-locations.d.cts +43 -0
  166. package/dist/transform/source-locations.d.ts +44 -0
  167. package/dist/transform/source-locations.d.ts.map +1 -0
  168. package/dist/transform/source-locations.js +238 -0
  169. package/dist/transform/source-locations.mjs +307 -0
  170. package/dist/transform/templates.cjs +2701 -7
  171. package/dist/transform/templates.d.cts +69 -0
  172. package/dist/transform/templates.d.ts +12 -4
  173. package/dist/transform/templates.d.ts.map +1 -1
  174. package/dist/transform/templates.js +72 -7
  175. package/dist/transform/templates.mjs +3743 -16
  176. package/dist/transform-BT4MMASR.mjs +893 -0
  177. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  178. package/package.json +6 -5
  179. package/dist/babel-4JUDAR2G.mjs +0 -1
  180. package/dist/chunk-3SAEGOMQ.mjs +0 -1
  181. package/dist/chunk-IBRM4W7I.mjs +0 -10
  182. package/dist/chunk-OHHZCYIQ.mjs +0 -1
  183. package/dist/chunk-PVR2SN3B.mjs +0 -1
  184. package/dist/chunk-QUIYULS2.mjs +0 -1
  185. package/dist/chunk-YR3SAEO7.mjs +0 -1
  186. package/dist/server-fns-6QM243HC.mjs +0 -2
  187. package/dist/server-functions-E3LKCZ4R.mjs +0 -1
  188. package/dist/transform-5WC4FWJE.mjs +0 -8
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Lower a parsed Template AST (`@fluixi/template-parser`) into the host-agnostic IR.
3
+ *
4
+ * No parser in here: the `${…}` holes arrive already described as `TemplateHole`,
5
+ * since reading them means reading JavaScript and that belongs to a front-end.
6
+ *
7
+ * Pipeline: pieces → parseTemplate → Template AST → lowerTemplate → IRNode
8
+ */
9
+ import { type Piece } from '@fluixi/template-parser';
10
+ import type { HolePosition, IRNode } from '../ir/nodes.cjs';
11
+ /**
12
+ * One `${…}` slot, as the front-end describes it. Conclusions about the
13
+ * expression only — never a syntax tree, or the parser dependency creeps back.
14
+ */
15
+ export interface TemplateHole {
16
+ /** The expression source, verbatim. Anything nested inside it is already compiled. */
17
+ code: string;
18
+ /** Whether it must be wired reactively (see analyze/reactive-expr.ts). */
19
+ reactive: boolean;
20
+ /**
21
+ * Set when the hole is a single-expression arrow — `${item => …}`. `each` turns
22
+ * that into `For`'s render function, so it needs the parameters and the body
23
+ * separately rather than the arrow as written.
24
+ */
25
+ arrow?: {
26
+ params: string[];
27
+ body: string;
28
+ bodyReactive: boolean;
29
+ };
30
+ /** Whether the hole is an object literal — `class=${{…}}` lowers to classList. */
31
+ object?: boolean;
32
+ /**
33
+ * Where the `${…}` was written. Only kept when the host asked for source locations.
34
+ *
35
+ * A template is emitted as one overwrite, so every hole in it traces back through the
36
+ * source map to the line the template opens at. Without this, every node a template makes
37
+ * reports that one position.
38
+ */
39
+ at?: HolePosition;
40
+ }
41
+ /**
42
+ * Lower a parsed template to IR. `pieces` are the static runs and hole slots as
43
+ * the parser wants them; `holes` describes each `${…}` in the same order. `used`
44
+ * collects the runtime symbols the emitted code imports.
45
+ */
46
+ export declare function lowerTemplate(pieces: Piece[], holes: TemplateHole[], used: Set<string>, opts?: {
47
+ svg?: boolean;
48
+ sourceFile?: string;
49
+ positionAt?: (offset: number) => {
50
+ line: number;
51
+ column: number;
52
+ };
53
+ }): IRNode;
@@ -7,7 +7,7 @@
7
7
  * Pipeline: pieces → parseTemplate → Template AST → lowerTemplate → IRNode
8
8
  */
9
9
  import { type Piece } from '@fluixi/template-parser';
10
- import type { IRNode } from '../ir/nodes.js';
10
+ import type { HolePosition, IRNode } from '../ir/nodes.js';
11
11
  /**
12
12
  * One `${…}` slot, as the front-end describes it. Conclusions about the
13
13
  * expression only — never a syntax tree, or the parser dependency creeps back.
@@ -29,6 +29,14 @@ export interface TemplateHole {
29
29
  };
30
30
  /** Whether the hole is an object literal — `class=${{…}}` lowers to classList. */
31
31
  object?: boolean;
32
+ /**
33
+ * Where the `${…}` was written. Only kept when the host asked for source locations.
34
+ *
35
+ * A template is emitted as one overwrite, so every hole in it traces back through the
36
+ * source map to the line the template opens at. Without this, every node a template makes
37
+ * reports that one position.
38
+ */
39
+ at?: HolePosition;
32
40
  }
33
41
  /**
34
42
  * Lower a parsed template to IR. `pieces` are the static runs and hole slots as
@@ -37,5 +45,10 @@ export interface TemplateHole {
37
45
  */
38
46
  export declare function lowerTemplate(pieces: Piece[], holes: TemplateHole[], used: Set<string>, opts?: {
39
47
  svg?: boolean;
48
+ sourceFile?: string;
49
+ positionAt?: (offset: number) => {
50
+ line: number;
51
+ column: number;
52
+ };
40
53
  }): IRNode;
41
54
  //# sourceMappingURL=template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lower/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAML,KAAK,KAAK,EAGX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,gBAAgB,CAAC;AAKjE;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA4bD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3B,MAAM,CAGR"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lower/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAML,KAAK,KAAK,EAGX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAsB,MAAM,gBAAgB,CAAC;AAM/E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB;AAmhBD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO,GACnH,MAAM,CAGR"}
@@ -10,6 +10,7 @@ import { parseTemplate, } from '@fluixi/template-parser';
10
10
  import { parseLoadDirective } from '../resolve/load-directive.js';
11
11
  import { imperativeBackend, inlineTemplates } from '../codegen/backends/imperative.js';
12
12
  import { cleanTemplateText } from './text.js';
13
+ import { paren } from '../analyze/paren.js';
13
14
  const DELEGATED = new Set([
14
15
  'click', 'dblclick', 'input', 'change', 'submit', 'focus', 'blur',
15
16
  'keydown', 'keyup', 'keypress', 'mousedown', 'mouseup',
@@ -24,9 +25,27 @@ function capitalize(s) {
24
25
  class Lowerer {
25
26
  holes;
26
27
  used;
27
- constructor(holes, used) {
28
+ sourceFile;
29
+ positionAt;
30
+ constructor(holes, used, sourceFile, positionAt) {
28
31
  this.holes = holes;
29
32
  this.used = used;
33
+ this.sourceFile = sourceFile;
34
+ this.positionAt = positionAt;
35
+ }
36
+ /**
37
+ * Where a tag is written, for the mark a component node carries.
38
+ *
39
+ * The parser reports offsets, and they are offsets into the file: the pieces it was given
40
+ * carry the positions the front-end recorded. Only built when the host asked for source
41
+ * locations, which is the only time either of these is set.
42
+ */
43
+ tagAt(node) {
44
+ const start = node.loc?.start;
45
+ if (!this.sourceFile || !this.positionAt || start == null)
46
+ return undefined;
47
+ const { line, column } = this.positionAt(start);
48
+ return { file: this.sourceFile, line, column };
30
49
  }
31
50
  hole(i) {
32
51
  // -1 marks a recovered-from missing binding; emit a harmless undefined.
@@ -95,7 +114,7 @@ class Lowerer {
95
114
  return null; // dropped, like JSX comments
96
115
  case 'Expression': {
97
116
  const h = this.hole(node.hole);
98
- return { kind: 'expr', code: h.code, reactive: h.reactive };
117
+ return { kind: 'expr', code: h.code, reactive: h.reactive, ...(h.at ? { at: h.at } : {}) };
99
118
  }
100
119
  case 'Fragment':
101
120
  return { kind: 'fragment', children: this.lowerChildren(node.children) };
@@ -115,9 +134,10 @@ class Lowerer {
115
134
  if (el.tag === 'component' && isAttr && isAttr.value && isAttr.value.kind === 'hole') {
116
135
  const rest = el.attributes.filter((a) => a !== isAttr);
117
136
  const node = this.lowerComponent({ ...el, kind: 'Component', tag: 'Dynamic', tagHole: null, attributes: rest });
118
- node.props.unshift({ name: 'component', kind: 'attr', expr: `() => (${this.hole(isAttr.value.hole).code})` });
137
+ node.props.unshift({ name: 'component', kind: 'attr', expr: `() => ${paren(this.hole(isAttr.value.hole).code, 'arrowBody')}` });
119
138
  return node;
120
139
  }
140
+ const at = this.tagAt(el);
121
141
  return {
122
142
  kind: 'element',
123
143
  tag: el.tag,
@@ -125,6 +145,7 @@ class Lowerer {
125
145
  props: this.lowerAttributes(el.attributes),
126
146
  children: this.lowerChildren(el.children),
127
147
  static: false,
148
+ ...(at ? { at } : {}),
128
149
  };
129
150
  }
130
151
  lowerComponent(el) {
@@ -160,11 +181,20 @@ class Lowerer {
160
181
  const load = loadAttr
161
182
  ? parseLoadDirective(`load:${loadAttr.strategy}`, loadAttr.modifier)
162
183
  : undefined;
184
+ // The parser's positions are relative to the template string, not the file, so the
185
+ // tag itself has none to give. The first hole is the closest thing that does, and for
186
+ // the control-flow components — `<Show when=${…}>`, `<For each=${…}>` — it is the
187
+ // expression driving the subtree anyway.
188
+ // A reactive one: a literal carries a position too now, and it drives nothing.
189
+ const bindAt = props.find((p) => p.at && p.reactive)?.at;
190
+ const at = this.tagAt(el);
163
191
  return {
164
192
  kind: 'component',
165
193
  name,
166
194
  props,
167
195
  children: this.lowerChildren(rest),
196
+ ...(at ? { at } : {}),
197
+ ...(bindAt ? { bindAt } : {}),
168
198
  ...(load ? { load } : {}),
169
199
  };
170
200
  }
@@ -193,14 +223,15 @@ class Lowerer {
193
223
  lowerIf(el, condHole, elseEl) {
194
224
  const cond = this.hole(condHole);
195
225
  const props = [
196
- { name: 'when', kind: 'attr', expr: cond.code, reactive: cond.reactive },
226
+ { name: 'when', kind: 'attr', expr: cond.code, reactive: cond.reactive, ...(cond.at ? { at: cond.at } : {}) },
197
227
  ];
198
228
  if (elseEl) {
199
229
  const elseIR = this.stripAndLower(elseEl, (a) => a.kind === 'ElseDirective');
200
230
  props.push({ name: 'fallback', kind: 'attr', expr: this.emit(elseIR), jsxElement: true });
201
231
  }
202
232
  const child = this.stripAndLower(el, (a) => a.kind === 'IfDirective');
203
- return { kind: 'component', name: 'Show', props, children: [child] };
233
+ const at = this.tagAt(el);
234
+ return { kind: 'component', name: 'Show', props, children: [child], ...(at ? { at } : {}), ...(cond.at ? { bindAt: cond.at } : {}) };
204
235
  }
205
236
  /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
206
237
  lowerEach(el) {
@@ -208,7 +239,7 @@ class Lowerer {
208
239
  if (!eachDir || eachDir.kind !== 'EachDirective')
209
240
  return this.lowerNode(el);
210
241
  const each = this.hole(eachDir.hole);
211
- const props = [{ name: 'each', kind: 'attr', expr: each.code, reactive: each.reactive }];
242
+ const props = [{ name: 'each', kind: 'attr', expr: each.code, reactive: each.reactive, ...(each.at ? { at: each.at } : {}) }];
212
243
  if (eachDir.key) {
213
244
  // `<For by>` wants a KEY FUNCTION, not a string. `key="id"` selects the
214
245
  // `id` field: `(item) => item["id"]`; `key=${fn}` passes the function.
@@ -223,14 +254,15 @@ class Lowerer {
223
254
  if (arrow) {
224
255
  const bodyIR = { kind: 'expr', code: arrow.body, reactive: arrow.bodyReactive };
225
256
  const repeated = this.rebuildWithChildren(el, [bodyIR]);
226
- childrenCode = `(${arrow.params.join(', ')}) => (${this.emit(repeated)})`;
257
+ childrenCode = `(${arrow.params.join(', ')}) => ${paren(this.emit(repeated), 'arrowBody')}`;
227
258
  }
228
259
  else {
229
260
  const repeated = this.stripAndLower(el, (a) => a.kind === 'EachDirective');
230
- childrenCode = `() => (${this.emit(repeated)})`;
261
+ childrenCode = `() => ${paren(this.emit(repeated), 'arrowBody')}`;
231
262
  }
232
263
  const children = [{ kind: 'expr', code: childrenCode, reactive: false }];
233
- return { kind: 'component', name: 'For', props, children };
264
+ const at = this.tagAt(el);
265
+ return { kind: 'component', name: 'For', props, children, ...(at ? { at } : {}), ...(each.at ? { bindAt: each.at } : {}) };
234
266
  }
235
267
  /** The single `${item => expr}` child arrow, if that's the element's content. */
236
268
  itemArrow(el) {
@@ -248,7 +280,16 @@ class Lowerer {
248
280
  rebuildWithChildren(el, children) {
249
281
  const props = this.lowerAttributes(el.attributes.filter((a) => a.kind !== 'EachDirective'));
250
282
  if (el.kind === 'Element') {
251
- return { kind: 'element', tag: el.tag, svg: el.namespace === 'svg', props, children, static: false };
283
+ const at = this.tagAt(el);
284
+ return {
285
+ kind: 'element',
286
+ tag: el.tag,
287
+ svg: el.namespace === 'svg',
288
+ props,
289
+ children,
290
+ static: false,
291
+ ...(at ? { at } : {}),
292
+ };
252
293
  }
253
294
  return { kind: 'component', name: el.tag, props, children };
254
295
  }
@@ -257,8 +298,10 @@ class Lowerer {
257
298
  const props = [];
258
299
  const classEntries = [];
259
300
  let classReactive = false;
301
+ let classAt;
260
302
  const styleEntries = [];
261
303
  let styleReactive = false;
304
+ let styleAt;
262
305
  const usePairs = [];
263
306
  for (const a of attrs) {
264
307
  switch (a.kind) {
@@ -270,13 +313,26 @@ class Lowerer {
270
313
  props.push(this.plainAttr(a));
271
314
  break;
272
315
  case 'PropertyBinding':
273
- props.push({ name: a.name, kind: 'prop', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
316
+ props.push({
317
+ name: a.name,
318
+ kind: 'prop',
319
+ expr: this.hole(a.hole).code,
320
+ reactive: this.hole(a.hole).reactive,
321
+ ...(this.hole(a.hole).at ? { at: this.hole(a.hole).at } : {}),
322
+ });
274
323
  break;
275
324
  case 'EventBinding':
276
325
  props.push(this.eventProp(a));
277
326
  break;
278
327
  case 'RefBinding':
279
- props.push({ name: 'ref', kind: 'ref', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
328
+ props.push({
329
+ name: 'ref',
330
+ kind: 'ref',
331
+ expr: this.hole(a.hole).code,
332
+ reactive: this.hole(a.hole).reactive,
333
+ // Which variable ends up holding the element, and where it was written.
334
+ ...(this.hole(a.hole).at ? { at: this.hole(a.hole).at } : {}),
335
+ });
280
336
  break;
281
337
  case 'Spread':
282
338
  props.push({ name: '', kind: 'spread', expr: this.hole(a.hole).code });
@@ -285,16 +341,18 @@ class Lowerer {
285
341
  const h = this.hole(a.hole);
286
342
  classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
287
343
  classReactive = classReactive || h.reactive;
344
+ classAt ??= h.at;
288
345
  break;
289
346
  }
290
347
  case 'StyleDirective': {
291
348
  const h = this.hole(a.hole);
292
349
  styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
293
350
  styleReactive = styleReactive || h.reactive;
351
+ styleAt ??= h.at;
294
352
  break;
295
353
  }
296
354
  case 'BindDirective':
297
- props.push(...this.bindProps(a.name, this.hole(a.hole).code));
355
+ props.push(...this.bindProps(a.name, this.hole(a.hole)));
298
356
  break;
299
357
  case 'UseDirective': {
300
358
  // `use=${dir}` → [dir]; `use:name` → [name]; `use:name=${opts}` →
@@ -303,18 +361,33 @@ class Lowerer {
303
361
  if (!dir)
304
362
  break;
305
363
  // parens so an object literal opts isn't read as a block body
306
- usePairs.push(a.name != null && a.hole != null ? `[${dir}, () => (${this.hole(a.hole).code})]` : `[${dir}]`);
364
+ usePairs.push(a.name != null && a.hole != null ? `[${dir}, () => ${paren(this.hole(a.hole).code, 'arrowBody')}]` : `[${dir}]`);
307
365
  break;
308
366
  }
309
367
  }
310
368
  }
311
369
  // Merge all class:/style: directives into one classList/style prop so they
312
370
  // don't collide as duplicate keys in the emitted props object.
371
+ // One prop covers every directive of its kind, so it carries the first hole's
372
+ // position — a real source line beats the stack fallback, which lands in the
373
+ // compiled file.
313
374
  if (classEntries.length > 0) {
314
- props.push({ name: 'classList', kind: 'attr', expr: `{ ${classEntries.join(', ')} }`, reactive: classReactive });
375
+ props.push({
376
+ name: 'classList',
377
+ kind: 'attr',
378
+ expr: `{ ${classEntries.join(', ')} }`,
379
+ reactive: classReactive,
380
+ ...(classAt ? { at: classAt } : {}),
381
+ });
315
382
  }
316
383
  if (styleEntries.length > 0) {
317
- props.push({ name: 'style', kind: 'attr', expr: `{ ${styleEntries.join(', ')} }`, reactive: styleReactive });
384
+ props.push({
385
+ name: 'style',
386
+ kind: 'attr',
387
+ expr: `{ ${styleEntries.join(', ')} }`,
388
+ reactive: styleReactive,
389
+ ...(styleAt ? { at: styleAt } : {}),
390
+ });
318
391
  }
319
392
  // All use: directives normalize to one `use` prop = an array of [dir, accessor?] pairs.
320
393
  if (usePairs.length > 0) {
@@ -369,7 +442,8 @@ class Lowerer {
369
442
  return opts.length ? `{ ${opts.join(', ')} }` : null;
370
443
  }
371
444
  /** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e…value)}`. */
372
- bindProps(prop, sigCode) {
445
+ bindProps(prop, h) {
446
+ const sigCode = h.code;
373
447
  const checked = prop === 'checked';
374
448
  const evt = checked ? 'change' : 'input';
375
449
  const accessor = checked ? 'checked' : 'value';
@@ -380,7 +454,14 @@ class Lowerer {
380
454
  this.used.add('bindPair');
381
455
  const sig = `bindPair(${sigCode})`;
382
456
  return [
383
- { name: prop, kind: 'attr', expr: `${sig}[0]()`, reactive: true },
457
+ {
458
+ name: prop,
459
+ kind: 'attr',
460
+ expr: `${sig}[0]()`,
461
+ reactive: true,
462
+ directive: `bind:${prop}`,
463
+ ...(h.at ? { at: h.at } : {}),
464
+ },
384
465
  {
385
466
  name: 'on' + capitalize(evt),
386
467
  kind: 'event',
@@ -418,21 +499,27 @@ class Lowerer {
418
499
  const h = this.hole(v.hole);
419
500
  base.expr = h.code;
420
501
  base.reactive = h.reactive;
502
+ if (h.at)
503
+ base.at = h.at;
421
504
  return base;
422
505
  }
423
506
  // mixed: template literal, reactive if any hole is
424
507
  let reactive = false;
508
+ let at;
425
509
  const body = v.parts
426
510
  .map((p) => {
427
511
  if ('text' in p)
428
512
  return escapeTemplate(p.text);
429
513
  const h = this.hole(p.hole);
430
514
  reactive = reactive || h.reactive;
515
+ at ??= h.at;
431
516
  return '${' + h.code + '}';
432
517
  })
433
518
  .join('');
434
519
  base.expr = '`' + body + '`';
435
520
  base.reactive = reactive;
521
+ if (at)
522
+ base.at = at;
436
523
  return base;
437
524
  }
438
525
  }
@@ -443,5 +530,5 @@ class Lowerer {
443
530
  */
444
531
  export function lowerTemplate(pieces, holes, used, opts = {}) {
445
532
  const { root } = parseTemplate(pieces, { svg: opts.svg });
446
- return new Lowerer(holes, used).lowerRoot(root.children);
533
+ return new Lowerer(holes, used, opts.sourceFile, opts.positionAt).lowerRoot(root.children);
447
534
  }