rooklang 0.0.0-stage → 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 (314) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -2
  3. package/README.ru.md +178 -0
  4. package/dist/ast/declarations.d.ts +185 -0
  5. package/dist/ast/declarations.js +1 -0
  6. package/dist/ast/expressions.d.ts +201 -0
  7. package/dist/ast/expressions.js +1 -0
  8. package/dist/ast/markup.d.ts +141 -0
  9. package/dist/ast/markup.js +1 -0
  10. package/dist/ast/statements.d.ts +173 -0
  11. package/dist/ast/statements.js +1 -0
  12. package/dist/ast.d.ts +18 -0
  13. package/dist/ast.js +18 -0
  14. package/dist/attrs.d.ts +8 -0
  15. package/dist/attrs.js +23 -0
  16. package/dist/browser.d.ts +30 -0
  17. package/dist/browser.js +36 -0
  18. package/dist/build.d.ts +48 -0
  19. package/dist/build.js +112 -0
  20. package/dist/checker/assignments.d.ts +14 -0
  21. package/dist/checker/assignments.js +197 -0
  22. package/dist/checker/base.d.ts +124 -0
  23. package/dist/checker/base.js +180 -0
  24. package/dist/checker/bigint.d.ts +15 -0
  25. package/dist/checker/bigint.js +85 -0
  26. package/dist/checker/builtins.d.ts +9 -0
  27. package/dist/checker/builtins.js +307 -0
  28. package/dist/checker/bytes.d.ts +11 -0
  29. package/dist/checker/bytes.js +72 -0
  30. package/dist/checker/calls.d.ts +23 -0
  31. package/dist/checker/calls.js +210 -0
  32. package/dist/checker/channel.d.ts +14 -0
  33. package/dist/checker/channel.js +44 -0
  34. package/dist/checker/channels.d.ts +9 -0
  35. package/dist/checker/channels.js +95 -0
  36. package/dist/checker/checker.d.ts +4 -0
  37. package/dist/checker/checker.js +22 -0
  38. package/dist/checker/classes.d.ts +24 -0
  39. package/dist/checker/classes.js +223 -0
  40. package/dist/checker/completion-names.d.ts +11 -0
  41. package/dist/checker/completion-names.js +100 -0
  42. package/dist/checker/completion.d.ts +25 -0
  43. package/dist/checker/completion.js +222 -0
  44. package/dist/checker/component-values.d.ts +11 -0
  45. package/dist/checker/component-values.js +88 -0
  46. package/dist/checker/components.d.ts +37 -0
  47. package/dist/checker/components.js +287 -0
  48. package/dist/checker/context.d.ts +196 -0
  49. package/dist/checker/context.js +35 -0
  50. package/dist/checker/control-flow.d.ts +24 -0
  51. package/dist/checker/control-flow.js +237 -0
  52. package/dist/checker/declarations.d.ts +26 -0
  53. package/dist/checker/declarations.js +259 -0
  54. package/dist/checker/decorator-exports.d.ts +5 -0
  55. package/dist/checker/decorator-exports.js +40 -0
  56. package/dist/checker/decorator-members.d.ts +28 -0
  57. package/dist/checker/decorator-members.js +140 -0
  58. package/dist/checker/decorator-wrappers.d.ts +20 -0
  59. package/dist/checker/decorator-wrappers.js +132 -0
  60. package/dist/checker/decorators.d.ts +28 -0
  61. package/dist/checker/decorators.js +265 -0
  62. package/dist/checker/dom-svg.d.ts +7 -0
  63. package/dist/checker/dom-svg.js +19 -0
  64. package/dist/checker/dom.d.ts +22 -0
  65. package/dist/checker/dom.js +361 -0
  66. package/dist/checker/expressions.d.ts +32 -0
  67. package/dist/checker/expressions.js +244 -0
  68. package/dist/checker/flow.d.ts +18 -0
  69. package/dist/checker/flow.js +134 -0
  70. package/dist/checker/forwarding.d.ts +15 -0
  71. package/dist/checker/forwarding.js +78 -0
  72. package/dist/checker/functions.d.ts +24 -0
  73. package/dist/checker/functions.js +154 -0
  74. package/dist/checker/go-for.d.ts +12 -0
  75. package/dist/checker/go-for.js +93 -0
  76. package/dist/checker/goroutines.d.ts +19 -0
  77. package/dist/checker/goroutines.js +110 -0
  78. package/dist/checker/helpers.d.ts +35 -0
  79. package/dist/checker/helpers.js +162 -0
  80. package/dist/checker/imports.d.ts +7 -0
  81. package/dist/checker/imports.js +83 -0
  82. package/dist/checker/initializers.d.ts +22 -0
  83. package/dist/checker/initializers.js +104 -0
  84. package/dist/checker/iterables.d.ts +5 -0
  85. package/dist/checker/iterables.js +17 -0
  86. package/dist/checker/iterators.d.ts +13 -0
  87. package/dist/checker/iterators.js +95 -0
  88. package/dist/checker/loops.d.ts +10 -0
  89. package/dist/checker/loops.js +82 -0
  90. package/dist/checker/markup.d.ts +28 -0
  91. package/dist/checker/markup.js +220 -0
  92. package/dist/checker/members.d.ts +29 -0
  93. package/dist/checker/members.js +233 -0
  94. package/dist/checker/operators.d.ts +13 -0
  95. package/dist/checker/operators.js +177 -0
  96. package/dist/checker/promise.d.ts +17 -0
  97. package/dist/checker/promise.js +49 -0
  98. package/dist/checker/refs.d.ts +26 -0
  99. package/dist/checker/refs.js +136 -0
  100. package/dist/checker/resolution.d.ts +23 -0
  101. package/dist/checker/resolution.js +173 -0
  102. package/dist/checker/statements.d.ts +28 -0
  103. package/dist/checker/statements.js +212 -0
  104. package/dist/checker/styles.d.ts +14 -0
  105. package/dist/checker/styles.js +49 -0
  106. package/dist/checker/transfer.d.ts +5 -0
  107. package/dist/checker/transfer.js +83 -0
  108. package/dist/checker/types/bytes.d.ts +4 -0
  109. package/dist/checker/types/bytes.js +51 -0
  110. package/dist/checker/types/classes.d.ts +22 -0
  111. package/dist/checker/types/classes.js +93 -0
  112. package/dist/checker/types/constructors.d.ts +27 -0
  113. package/dist/checker/types/constructors.js +129 -0
  114. package/dist/checker/types/generics.d.ts +20 -0
  115. package/dist/checker/types/generics.js +75 -0
  116. package/dist/checker/types/lazy.d.ts +30 -0
  117. package/dist/checker/types/lazy.js +92 -0
  118. package/dist/checker/types/model.d.ts +149 -0
  119. package/dist/checker/types/model.js +15 -0
  120. package/dist/checker/types/params.d.ts +7 -0
  121. package/dist/checker/types/params.js +220 -0
  122. package/dist/checker/types/printing.d.ts +6 -0
  123. package/dist/checker/types/printing.js +109 -0
  124. package/dist/checker/types/relations.d.ts +16 -0
  125. package/dist/checker/types/relations.js +294 -0
  126. package/dist/checker/types.d.ts +9 -0
  127. package/dist/checker/types.js +10 -0
  128. package/dist/cli.d.ts +2 -0
  129. package/dist/cli.js +193 -0
  130. package/dist/codegen/analysis.d.ts +18 -0
  131. package/dist/codegen/analysis.js +144 -0
  132. package/dist/codegen/attributes.d.ts +40 -0
  133. package/dist/codegen/attributes.js +227 -0
  134. package/dist/codegen/calls.d.ts +22 -0
  135. package/dist/codegen/calls.js +106 -0
  136. package/dist/codegen/chan-helpers.d.ts +2 -0
  137. package/dist/codegen/chan-helpers.js +253 -0
  138. package/dist/codegen/component-helpers.d.ts +33 -0
  139. package/dist/codegen/component-helpers.js +18 -0
  140. package/dist/codegen/component-props.d.ts +42 -0
  141. package/dist/codegen/component-props.js +173 -0
  142. package/dist/codegen/component-values.d.ts +27 -0
  143. package/dist/codegen/component-values.js +146 -0
  144. package/dist/codegen/components.d.ts +53 -0
  145. package/dist/codegen/components.js +268 -0
  146. package/dist/codegen/control-flow.d.ts +25 -0
  147. package/dist/codegen/control-flow.js +152 -0
  148. package/dist/codegen/control-statements.d.ts +28 -0
  149. package/dist/codegen/control-statements.js +226 -0
  150. package/dist/codegen/creation.d.ts +31 -0
  151. package/dist/codegen/creation.js +113 -0
  152. package/dist/codegen/early-returns.d.ts +59 -0
  153. package/dist/codegen/early-returns.js +244 -0
  154. package/dist/codegen/element-helpers.d.ts +12 -0
  155. package/dist/codegen/element-helpers.js +54 -0
  156. package/dist/codegen/elements.d.ts +71 -0
  157. package/dist/codegen/elements.js +293 -0
  158. package/dist/codegen/emitter.d.ts +135 -0
  159. package/dist/codegen/emitter.js +296 -0
  160. package/dist/codegen/expressions.d.ts +49 -0
  161. package/dist/codegen/expressions.js +262 -0
  162. package/dist/codegen/functions.d.ts +28 -0
  163. package/dist/codegen/functions.js +170 -0
  164. package/dist/codegen/go-helpers.d.ts +9 -0
  165. package/dist/codegen/go-helpers.js +307 -0
  166. package/dist/codegen/helpers.d.ts +10 -0
  167. package/dist/codegen/helpers.js +356 -0
  168. package/dist/codegen/imported.d.ts +38 -0
  169. package/dist/codegen/imported.js +71 -0
  170. package/dist/codegen/js.d.ts +20 -0
  171. package/dist/codegen/js.js +113 -0
  172. package/dist/codegen/mutations.d.ts +10 -0
  173. package/dist/codegen/mutations.js +102 -0
  174. package/dist/codegen/parallel.d.ts +7 -0
  175. package/dist/codegen/parallel.js +63 -0
  176. package/dist/codegen/reactive.d.ts +95 -0
  177. package/dist/codegen/reactive.js +280 -0
  178. package/dist/codegen/refs.d.ts +62 -0
  179. package/dist/codegen/refs.js +160 -0
  180. package/dist/codegen/select-helpers.d.ts +2 -0
  181. package/dist/codegen/select-helpers.js +135 -0
  182. package/dist/codegen/simple-statements.d.ts +42 -0
  183. package/dist/codegen/simple-statements.js +253 -0
  184. package/dist/codegen/statements.d.ts +22 -0
  185. package/dist/codegen/statements.js +142 -0
  186. package/dist/codegen/styles.d.ts +54 -0
  187. package/dist/codegen/styles.js +157 -0
  188. package/dist/codegen/syntax.d.ts +18 -0
  189. package/dist/codegen/syntax.js +70 -0
  190. package/dist/codegen/thread-helpers.d.ts +2 -0
  191. package/dist/codegen/thread-helpers.js +198 -0
  192. package/dist/codegen/updates.d.ts +51 -0
  193. package/dist/codegen/updates.js +201 -0
  194. package/dist/codegen/web-components.d.ts +20 -0
  195. package/dist/codegen/web-components.js +149 -0
  196. package/dist/compile-file.d.ts +11 -0
  197. package/dist/compile-file.js +24 -0
  198. package/dist/compile.d.ts +53 -0
  199. package/dist/compile.js +187 -0
  200. package/dist/completion.d.ts +10 -0
  201. package/dist/completion.js +134 -0
  202. package/dist/config.d.ts +2 -0
  203. package/dist/config.js +44 -0
  204. package/dist/css/hash.d.ts +1 -0
  205. package/dist/css/hash.js +10 -0
  206. package/dist/css/parse.d.ts +42 -0
  207. package/dist/css/parse.js +138 -0
  208. package/dist/css/print.d.ts +9 -0
  209. package/dist/css/print.js +35 -0
  210. package/dist/css/selectors.d.ts +29 -0
  211. package/dist/css/selectors.js +168 -0
  212. package/dist/declarations.d.ts +3 -0
  213. package/dist/declarations.js +207 -0
  214. package/dist/decorator-copy.d.ts +18 -0
  215. package/dist/decorator-copy.js +158 -0
  216. package/dist/decorators.d.ts +61 -0
  217. package/dist/decorators.js +241 -0
  218. package/dist/diagnostics.d.ts +17 -0
  219. package/dist/diagnostics.js +22 -0
  220. package/dist/go-for.d.ts +26 -0
  221. package/dist/go-for.js +155 -0
  222. package/dist/goroutines.d.ts +37 -0
  223. package/dist/goroutines.js +253 -0
  224. package/dist/html-tag.d.ts +21 -0
  225. package/dist/html-tag.js +103 -0
  226. package/dist/index.d.ts +33 -0
  227. package/dist/index.js +55 -0
  228. package/dist/inline-packages.d.ts +32 -0
  229. package/dist/inline-packages.js +129 -0
  230. package/dist/iterators.d.ts +10 -0
  231. package/dist/iterators.js +33 -0
  232. package/dist/lexer/base.d.ts +24 -0
  233. package/dist/lexer/base.js +87 -0
  234. package/dist/lexer/lexer.d.ts +8 -0
  235. package/dist/lexer/lexer.js +134 -0
  236. package/dist/lexer/literals.d.ts +18 -0
  237. package/dist/lexer/literals.js +203 -0
  238. package/dist/lexer/markup.d.ts +10 -0
  239. package/dist/lexer/markup.js +93 -0
  240. package/dist/lexer/style.d.ts +17 -0
  241. package/dist/lexer/style.js +97 -0
  242. package/dist/lexer/tables.d.ts +53 -0
  243. package/dist/lexer/tables.js +101 -0
  244. package/dist/lexer/token.d.ts +42 -0
  245. package/dist/lexer/token.js +108 -0
  246. package/dist/loader.d.ts +1 -0
  247. package/dist/loader.js +50 -0
  248. package/dist/methods.d.ts +5 -0
  249. package/dist/methods.js +81 -0
  250. package/dist/modules.d.ts +3 -0
  251. package/dist/modules.js +49 -0
  252. package/dist/names.d.ts +23 -0
  253. package/dist/names.js +267 -0
  254. package/dist/parser/base.d.ts +96 -0
  255. package/dist/parser/base.js +202 -0
  256. package/dist/parser/classes.d.ts +8 -0
  257. package/dist/parser/classes.js +164 -0
  258. package/dist/parser/control-flow.d.ts +29 -0
  259. package/dist/parser/control-flow.js +232 -0
  260. package/dist/parser/declarations.d.ts +27 -0
  261. package/dist/parser/declarations.js +238 -0
  262. package/dist/parser/decorators.d.ts +30 -0
  263. package/dist/parser/decorators.js +176 -0
  264. package/dist/parser/expressions.d.ts +15 -0
  265. package/dist/parser/expressions.js +255 -0
  266. package/dist/parser/literals.d.ts +27 -0
  267. package/dist/parser/literals.js +206 -0
  268. package/dist/parser/markup.d.ts +22 -0
  269. package/dist/parser/markup.js +193 -0
  270. package/dist/parser/parser.d.ts +14 -0
  271. package/dist/parser/parser.js +22 -0
  272. package/dist/parser/simple-statements.d.ts +22 -0
  273. package/dist/parser/simple-statements.js +156 -0
  274. package/dist/parser/statements.d.ts +37 -0
  275. package/dist/parser/statements.js +267 -0
  276. package/dist/parser/syntax.d.ts +51 -0
  277. package/dist/parser/syntax.js +173 -0
  278. package/dist/parser/types.d.ts +23 -0
  279. package/dist/parser/types.js +218 -0
  280. package/dist/recursion.d.ts +25 -0
  281. package/dist/recursion.js +170 -0
  282. package/dist/refs.d.ts +49 -0
  283. package/dist/refs.js +210 -0
  284. package/dist/run.d.ts +1 -0
  285. package/dist/run.js +21 -0
  286. package/dist/source.d.ts +15 -0
  287. package/dist/source.js +34 -0
  288. package/dist/style-modules.d.ts +17 -0
  289. package/dist/style-modules.js +49 -0
  290. package/dist/styles.d.ts +36 -0
  291. package/dist/styles.js +160 -0
  292. package/dist/svg.d.ts +8 -0
  293. package/dist/svg.js +77 -0
  294. package/dist/ts-types.d.ts +11 -0
  295. package/dist/ts-types.js +133 -0
  296. package/dist/typed-arrays.d.ts +8 -0
  297. package/dist/typed-arrays.js +43 -0
  298. package/dist/typescript/convert-base.d.ts +42 -0
  299. package/dist/typescript/convert-base.js +135 -0
  300. package/dist/typescript/convert-objects.d.ts +28 -0
  301. package/dist/typescript/convert-objects.js +245 -0
  302. package/dist/typescript/convert.d.ts +14 -0
  303. package/dist/typescript/convert.js +67 -0
  304. package/dist/typescript/importer.d.ts +37 -0
  305. package/dist/typescript/importer.js +152 -0
  306. package/dist/typescript/library.d.ts +23 -0
  307. package/dist/typescript/library.js +81 -0
  308. package/dist/vite.d.ts +11 -0
  309. package/dist/vite.js +86 -0
  310. package/dist/walk-types.d.ts +4 -0
  311. package/dist/walk-types.js +56 -0
  312. package/dist/walk.d.ts +5 -0
  313. package/dist/walk.js +315 -0
  314. package/package.json +104 -4
@@ -0,0 +1,146 @@
1
+ import { isNullable } from "../checker/types.js";
2
+ import { isConstant, isSimple } from "./analysis.js";
3
+ import { withoutTrailingUndefined } from "./component-helpers.js";
4
+ import { ComponentPropsEmitter } from "./component-props.js";
5
+ import { ARROW, BINARY, lowerFirst, POSTFIX, upperFirst } from "./syntax.js";
6
+ /**
7
+ * Tags that take the component from a value: `<section.content title={title} />` calls the
8
+ * function of the component, `section.content(title)`, with the properties in the order of the
9
+ * type of the value, `comp(title string)`. Like the call of a function component, it returns the
10
+ * node and, when the parent's state changes a property, the function that sets them again. When
11
+ * the value itself depends on state, the component is created again each time it changes:
12
+ * `$$dynamic` keeps it between markers, in a block of its own.
13
+ */
14
+ export class ComponentValueEmitter extends ComponentPropsEmitter {
15
+ /** Tags inside the blocks of `$$dynamic`: they call the component the block gets. */
16
+ fixedComponents = new Map();
17
+ valueTag(node, value, live) {
18
+ const type = this.typeOf(value);
19
+ if (!type) {
20
+ throw new Error(`<${node.tag.name}> takes its component from a value, whose type gives the order of the properties: compile it with type checking`);
21
+ }
22
+ const params = type.kind === 'component' ? type.params : [];
23
+ const fixed = this.fixedComponents.get(node);
24
+ if (fixed === undefined && live && live.dependencies(value).size > 0) {
25
+ return this.dynamicTag(node, value, live);
26
+ }
27
+ return this.callValue(node, fixed ?? this.expression(value, POSTFIX), params, live);
28
+ }
29
+ /** `$$dynamic(() => page, ($$component3) => { ...; return [block, update]; })` */
30
+ dynamicTag(node, value, live) {
31
+ const id = this.nextId();
32
+ const base = resultName(node);
33
+ const name = `$$${base}${id}`;
34
+ const update = `$$update${upperFirst(base)}${id}`;
35
+ const component = {
36
+ kind: 'Identifier',
37
+ name: `$$component${id}`,
38
+ start: node.start,
39
+ end: node.start,
40
+ };
41
+ const statement = {
42
+ kind: 'JsxElementStatement',
43
+ element: node,
44
+ start: node.start,
45
+ end: node.end,
46
+ };
47
+ this.fixedComponents.set(node, component.name);
48
+ let create;
49
+ try {
50
+ create = this.blockCreator([statement], live, undefined, component);
51
+ }
52
+ finally {
53
+ this.fixedComponents.delete(node);
54
+ }
55
+ this.helpers.add('dynamic');
56
+ this.line(`const [${name}, ${update}] = $$dynamic(${this.liveFunction(value)}, ${create});`);
57
+ live.depend(live.dependencies(node), `${update}();`);
58
+ return name;
59
+ }
60
+ /** The call of the component; properties that depend on state are set again by its setter. */
61
+ callValue(node, callee, params, live) {
62
+ const id = this.nextId();
63
+ const base = resultName(node);
64
+ const result = `$$${base}${id}`;
65
+ const attributes = new Map();
66
+ const args = [];
67
+ // The setter takes the properties without `children`, in the same order.
68
+ const settable = [];
69
+ const sources = new Set();
70
+ for (const attribute of node.attributes) {
71
+ if (attribute.kind === 'JsxAttribute')
72
+ attributes.set(attribute.name.name, attribute);
73
+ }
74
+ for (const param of params) {
75
+ if (param.name === 'children') {
76
+ const fragment = `$$children${this.nextId()}`;
77
+ this.line(`const ${fragment} = document.createDocumentFragment();`);
78
+ this.children(fragment, node.children, live);
79
+ args.push(fragment);
80
+ continue;
81
+ }
82
+ const attribute = attributes.get(param.name);
83
+ const text = attribute ? this.valueArgument(attribute, param) : 'undefined';
84
+ const value = attribute?.value;
85
+ const propSources = live && value && value.kind !== 'EventHandler' ? live.dependencies(value) : null;
86
+ args.push(text);
87
+ if (value && value.kind !== 'EventHandler' && propSources && propSources.size > 0) {
88
+ for (const source of propSources)
89
+ sources.add(source);
90
+ settable.push(this.nullAsDefault(this.liveExpression(value, BINARY['??'] + 1), value, param));
91
+ }
92
+ else {
93
+ settable.push(text);
94
+ }
95
+ }
96
+ const call = `${callee}(${withoutTrailingUndefined(args).join(', ')})`;
97
+ if (!live || sources.size === 0) {
98
+ this.line(`const [${result}] = ${call};`);
99
+ return result;
100
+ }
101
+ const setter = `$$set${upperFirst(base)}${id}`;
102
+ this.line(`const [${result}, ${setter}] = ${call};`);
103
+ live.depend(sources, `${setter}(${withoutTrailingUndefined(settable).join(', ')});`);
104
+ return result;
105
+ }
106
+ /** The value of a property: literals and names as they are, other values in a temporary. */
107
+ valueArgument(attribute, param) {
108
+ const { value } = attribute;
109
+ if (value === null)
110
+ return 'true';
111
+ if (value.kind === 'EventHandler') {
112
+ const temp = this.temp();
113
+ this.line(`const ${temp} = ${this.handler(value)};`);
114
+ return temp;
115
+ }
116
+ if (isConstant(value) || isSimple(value)) {
117
+ return this.nullAsDefault(this.expression(value, BINARY['??'] + 1), value, param);
118
+ }
119
+ // A function given to the component runs later, not while the markup is being created.
120
+ const text = value.kind === 'ArrowFunction' || value.kind === 'FuncExpression'
121
+ ? this.withRendering(0, () => this.expression(value, ARROW))
122
+ : this.expression(value, ARROW);
123
+ const temp = this.temp();
124
+ this.line(`const ${temp} = ${text};`);
125
+ return this.nullAsDefault(temp, value, param);
126
+ }
127
+ /**
128
+ * A property of type `?T` may belong to one with a default value, which null must give: so null
129
+ * is passed as undefined, the value that takes the default.
130
+ */
131
+ nullAsDefault(text, value, param) {
132
+ if (!isNullable(param.type))
133
+ return text;
134
+ if (value.kind === 'NullLiteral')
135
+ return 'undefined';
136
+ const type = this.typeOf(value);
137
+ return !type || type.kind === 'any' || type.kind === 'unknown' || isNullable(type)
138
+ ? `${text} ?? undefined`
139
+ : text;
140
+ }
141
+ }
142
+ /** `$$sectionContent3` for `<section.content />`, `$$page3` for `<Page />`. */
143
+ function resultName(node) {
144
+ const parts = (node.tag?.name ?? 'component').split('.');
145
+ return parts.map((part, i) => (i === 0 ? lowerFirst(part) : upperFirst(part))).join('');
146
+ }
@@ -0,0 +1,53 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { Reactive } from './reactive.ts';
3
+ import { type Live } from './reactive.ts';
4
+ import { type Ending } from './component-helpers.ts';
5
+ import { StyleEmitter } from './styles.ts';
6
+ /**
7
+ * Components, inlined where they are used: `<Card title="Hi" />` becomes a block with the code of
8
+ * `Card`. A component with state also gets the functions that update its markup.
9
+ *
10
+ * A recursive component cannot be inlined into itself, so it becomes a function `$$Tree(...)`. It
11
+ * takes the properties as arguments and returns the node and a function that sets the properties
12
+ * again, which the parent calls when their values depend on its state.
13
+ */
14
+ export declare abstract class ComponentEmitter extends StyleEmitter {
15
+ /** An early `return` in a component: its markup, then the exit from the body. */
16
+ protected statement(node: ast.Statement): void;
17
+ /**
18
+ * `derived double = count * 2` is a variable computed again, first, in the updates of what it
19
+ * reads; one that reads no state is a constant.
20
+ */
21
+ private derivedValue;
22
+ /**
23
+ * An argument of a decorator that reads properties of the component: when they change, it is
24
+ * computed again, and what reads it is updated.
25
+ */
26
+ private decoratorArgument;
27
+ /**
28
+ * `mount() { ... }` becomes `$$mount(() => { ... })`. Its body runs later, like a handler: what
29
+ * it changes updates the markup.
30
+ */
31
+ protected mountStatement(node: ast.MountStatement): void;
32
+ /**
33
+ * Inlines a component: its code goes into a block where its properties are constants. Attribute
34
+ * values are computed before the block, so that the component's own names cannot hide the
35
+ * names they refer to. In live markup, a property whose value depends on the parent's state is
36
+ * a variable, and the parent's updates call a function that sets it.
37
+ */
38
+ protected expand(node: ast.ElementExpression, component: ast.ComponentDeclaration, live: Live | null): string;
39
+ /**
40
+ * A recursive component: `function $$Tree(item) { ...; return [node, setItem]; }`. Every property
41
+ * except `children` may get the caller's state, so all of them are sources here.
42
+ */
43
+ protected componentFunction(component: ast.ComponentDeclaration): void;
44
+ /**
45
+ * The attributes of a use that are not properties of the component, as a function that sets them
46
+ * on the element `{...attrs}` is on: `($$target) => { $$target.disabled = true; }`. It is
47
+ * written here, so their values see the names of the caller; when they depend on its state, the
48
+ * element is kept for its updates. A class or a style is added to that of the element.
49
+ */
50
+ private attrsApplier;
51
+ /** The statements of the body, then its markup. `label` is for early returns of a block. */
52
+ protected body(component: ast.ComponentDeclaration, result: string, reactive: Reactive, ending: Ending, label: string | null): void;
53
+ }
@@ -0,0 +1,268 @@
1
+ import { isDecoratorArgument } from "../decorators.js";
2
+ import { attrsParam, attrsTarget } from "../attrs.js";
3
+ import { ownRoot } from "../refs.js";
4
+ import { isSvgElement } from "../svg.js";
5
+ import {} from "./reactive.js";
6
+ import { ARROW, lowerFirst, upperFirst } from "./syntax.js";
7
+ import { resolveMarkers } from "./updates.js";
8
+ import { hasEarlyReturn, hasMount } from "./component-helpers.js";
9
+ import { StyleEmitter } from "./styles.js";
10
+ /**
11
+ * Components, inlined where they are used: `<Card title="Hi" />` becomes a block with the code of
12
+ * `Card`. A component with state also gets the functions that update its markup.
13
+ *
14
+ * A recursive component cannot be inlined into itself, so it becomes a function `$$Tree(...)`. It
15
+ * takes the properties as arguments and returns the node and a function that sets the properties
16
+ * again, which the parent calls when their values depend on its state.
17
+ */
18
+ export class ComponentEmitter extends StyleEmitter {
19
+ /** An early `return` in a component: its markup, then the exit from the body. */
20
+ statement(node) {
21
+ const early = this.earlyReturns;
22
+ if (early && node.kind === 'ReturnStatement' && this.fn === early.fn) {
23
+ const [value] = node.values;
24
+ if (value)
25
+ this.withHoisting(true, () => this.returnedMarkup(value, early));
26
+ this.line(early.exit);
27
+ this.returnedEarly = true;
28
+ return;
29
+ }
30
+ if (node.kind === 'VariableDeclaration' && node.keyword === 'derived') {
31
+ this.derivedValue(node);
32
+ return;
33
+ }
34
+ if (node.kind === 'VariableDeclaration' && node.keyword === 'ref') {
35
+ this.refDeclaration(node);
36
+ return;
37
+ }
38
+ // The CSS goes to the start of the module, its values to the element the component creates.
39
+ if (node.kind === 'StyleStatement')
40
+ return;
41
+ super.statement(node);
42
+ if (isDecoratorArgument(node))
43
+ this.decoratorArgument(node);
44
+ }
45
+ /**
46
+ * `derived double = count * 2` is a variable computed again, first, in the updates of what it
47
+ * reads; one that reads no state is a constant.
48
+ */
49
+ derivedValue(node) {
50
+ const { reactive } = this;
51
+ const [name] = node.names;
52
+ const [value] = node.values;
53
+ const sources = reactive && value ? reactive.dependencies(value) : new Set();
54
+ super.statement(sources.size > 0 ? node : { ...node, keyword: 'const' });
55
+ if (!reactive || !name || !value || sources.size === 0)
56
+ return;
57
+ const expression = this.withHoisting(false, () => this.expression(value, ARROW));
58
+ reactive.compute(sources, `${this.name(name.name)} = ${expression};`);
59
+ }
60
+ /**
61
+ * An argument of a decorator that reads properties of the component: when they change, it is
62
+ * computed again, and what reads it is updated.
63
+ */
64
+ decoratorArgument(node) {
65
+ const { reactive } = this;
66
+ const [value] = node.values;
67
+ const source = reactive?.sources.get(node.names[0].name);
68
+ if (!reactive || !value || !source)
69
+ return;
70
+ const props = new Set([...reactive.dependencies(value)].filter((s) => s.kind === 'prop'));
71
+ if (props.size === 0)
72
+ return;
73
+ const assignment = `${this.name(source.name)} = ${this.expression(value, ARROW)};`;
74
+ reactive.depend(props, `${assignment}\n${this.indent()}${this.write(source)};`);
75
+ }
76
+ /**
77
+ * `mount() { ... }` becomes `$$mount(() => { ... })`. Its body runs later, like a handler: what
78
+ * it changes updates the markup.
79
+ */
80
+ mountStatement(node) {
81
+ this.helpers.add('mount');
82
+ this.inHandler++;
83
+ try {
84
+ const [, body] = this.withRendering(0, () => this.func([], node.body));
85
+ this.line(`$$mount(() => ${this.mountNode ?? 'null'}, () => ${body});`);
86
+ }
87
+ finally {
88
+ this.inHandler--;
89
+ }
90
+ }
91
+ /**
92
+ * Inlines a component: its code goes into a block where its properties are constants. Attribute
93
+ * values are computed before the block, so that the component's own names cannot hide the
94
+ * names they refer to. In live markup, a property whose value depends on the parent's state is
95
+ * a variable, and the parent's updates call a function that sets it.
96
+ */
97
+ expand(node, component, live) {
98
+ const name = component.name.name;
99
+ const isFunction = this.functionComponents.has(component);
100
+ const start = this.lines.length;
101
+ if (!isFunction)
102
+ this.line(`// <${name}>`);
103
+ const values = new Map();
104
+ const reactiveProps = new Map();
105
+ const rest = attrsParam(component);
106
+ const forwarded = [];
107
+ for (const attribute of node.attributes) {
108
+ if (attribute.kind === 'JsxSpreadAttribute') {
109
+ this.spreadProps(attribute.argument, component, live, values, reactiveProps);
110
+ continue;
111
+ }
112
+ const prop = attribute.name.name;
113
+ if (prop === 'ref')
114
+ continue;
115
+ if (rest && !component.params.some((param) => param.name.name === prop)) {
116
+ forwarded.push(attribute);
117
+ continue;
118
+ }
119
+ values.set(prop, this.prop(attribute, component));
120
+ reactiveProps.delete(prop);
121
+ const { value } = attribute;
122
+ if (live && value && value.kind !== 'EventHandler') {
123
+ const sources = live.dependencies(value);
124
+ if (sources.size > 0)
125
+ reactiveProps.set(prop, [this.liveExpression(value), sources]);
126
+ }
127
+ }
128
+ if (component.params.some((param) => param.name.name === 'children')) {
129
+ const fragment = `$$children${this.nextId()}`;
130
+ this.line(`const ${fragment} = document.createDocumentFragment();`);
131
+ this.children(fragment, node.children, live);
132
+ values.set('children', fragment);
133
+ }
134
+ if (rest)
135
+ values.set(rest.name.name, this.attrsApplier(component, forwarded, live));
136
+ const root = this.rootFor(node);
137
+ if (isFunction)
138
+ return this.callComponent(component, values, reactiveProps, live, root?.name);
139
+ const result = `$$${lowerFirst(name)}${this.nextId()}`;
140
+ const label = hasEarlyReturn(component) ? `$$body${this.nextId()}` : null;
141
+ const mountNode = hasMount(component) ? `$$mountNode${this.nextId()}` : null;
142
+ this.line(mountNode ? `let ${result}, ${mountNode};` : `let ${result};`);
143
+ const setters = new Map();
144
+ for (const [prop, [text, sources]] of reactiveProps) {
145
+ const setter = `$$set${upperFirst(prop)}${this.nextId()}`;
146
+ this.line(`let ${setter};`);
147
+ live?.depend(sources, `${setter}(${text});`);
148
+ setters.set(prop, setter);
149
+ }
150
+ const reactive = this.reactiveOf(component, setters.keys());
151
+ const props = component.params.map((param) => [param.name.name, 'const']);
152
+ const preset = this.inlineRoot(component, root);
153
+ const block = this.withRefs(component, preset, () => this.withStyles(component, result, () => this.inComponent(reactive, mountNode, () => this.block(() => this.withScope(props, () => {
154
+ for (const param of component.params) {
155
+ const value = values.get(param.name.name) ??
156
+ (param.defaultValue ? this.expression(param.defaultValue, ARROW) : 'null');
157
+ const keyword = setters.has(param.name.name) ? 'let' : 'const';
158
+ this.line(`${keyword} ${this.name(param.name.name)} = ${value};`);
159
+ }
160
+ this.body(component, result, reactive, { kind: 'inline', setters }, label);
161
+ })))));
162
+ // The setters may change arguments of decorators, so their markers are resolved too.
163
+ const code = resolveMarkers(this.placeSetters(block, result), reactive);
164
+ this.line(label ? `${label}: ${code}` : code);
165
+ const copy = this.lines.slice(start).join('\n').split('\n').length;
166
+ // By the tag: a component of another module has one of its own here.
167
+ const tag = node.tag?.name ?? name;
168
+ const inlined = this.inlined.get(tag) ?? { copies: 0, lines: 0 };
169
+ this.inlined.set(tag, { copies: inlined.copies + 1, lines: inlined.lines + copy });
170
+ return result;
171
+ }
172
+ /**
173
+ * A recursive component: `function $$Tree(item) { ...; return [node, setItem]; }`. Every property
174
+ * except `children` may get the caller's state, so all of them are sources here.
175
+ */
176
+ componentFunction(component) {
177
+ // `children` and `...attrs` are given once: the setter does not take them.
178
+ const props = component.params.filter((param) => param.name.name !== 'children' && !param.rest);
179
+ this.withFunction('none', component.params, () => {
180
+ const reactive = this.reactiveOf(component, props.map((param) => param.name.name));
181
+ const result = `$$${lowerFirst(component.name.name)}${this.nextId()}`;
182
+ const mountNode = hasMount(component) ? `$$mountNode${this.nextId()}` : null;
183
+ // A ref to the component created the element at its top: `$$root`, undefined without one.
184
+ const own = this.takesRoot(component) ? ownRoot(component) : null;
185
+ const preset = own && typeof own !== 'string' ? { node: own, name: '$$root', created: false } : null;
186
+ const params = [this.parameters(component.params, ''), ...(preset ? ['$$root'] : [])]
187
+ .filter((each) => each !== '')
188
+ .join(', ');
189
+ const body = this.withRefs(component, preset, () => this.withStyles(component, result, () => this.inComponent(reactive, mountNode, () => this.block(() => {
190
+ this.line(mountNode ? `let ${result}, ${mountNode};` : `let ${result};`);
191
+ this.body(component, result, reactive, { kind: 'function', props }, null);
192
+ }))));
193
+ // Other modules import an exported component under the name of its function.
194
+ const exported = component.exported ? 'export ' : '';
195
+ this.line(`${exported}function $$${component.name.name}(${params}) ${resolveMarkers(body, reactive)}`);
196
+ });
197
+ }
198
+ /**
199
+ * The attributes of a use that are not properties of the component, as a function that sets them
200
+ * on the element `{...attrs}` is on: `($$target) => { $$target.disabled = true; }`. It is
201
+ * written here, so their values see the names of the caller; when they depend on its state, the
202
+ * element is kept for its updates. A class or a style is added to that of the element.
203
+ */
204
+ attrsApplier(component, attributes, live) {
205
+ const tag = attrsTarget(component)?.tag?.name;
206
+ if (!tag || attributes.length === 0)
207
+ return 'null';
208
+ const svg = isSvgElement(tag, false);
209
+ const id = this.nextId();
210
+ const reactive = attributes.some(({ value }) => live !== null &&
211
+ value !== null &&
212
+ value.kind !== 'EventHandler' &&
213
+ live.dependencies(value).size > 0);
214
+ const element = reactive ? `$$attrsElement${id}` : '$$target';
215
+ if (reactive)
216
+ this.line(`let ${element};`);
217
+ const body = this.block(() => {
218
+ if (reactive)
219
+ this.line(`${element} = $$target;`);
220
+ for (const attribute of attributes) {
221
+ const { name, value } = attribute;
222
+ if (name.name === 'class' && value?.kind === 'StringLiteral') {
223
+ const classes = value.value.split(/\s+/).filter(Boolean);
224
+ this.line(`${element}.classList.add(${classes.map((each) => JSON.stringify(each)).join(', ')});`);
225
+ }
226
+ else if (name.name === 'style' && value?.kind === 'StringLiteral') {
227
+ this.line(`${element}.style.cssText += ${JSON.stringify(`; ${value.value}`)};`);
228
+ }
229
+ else {
230
+ this.attribute(element, tag, attribute, live, svg);
231
+ }
232
+ }
233
+ });
234
+ const applier = `$$attrs${id}`;
235
+ this.line(`const ${applier} = ($$target) => ${body};`);
236
+ return applier;
237
+ }
238
+ /** The statements of the body, then its markup. `label` is for early returns of a block. */
239
+ body(component, result, reactive, ending, label) {
240
+ const body = component.body.body;
241
+ const last = body.at(-1);
242
+ const setup = last?.kind === 'ReturnStatement' ? body.slice(0, -1) : body;
243
+ const early = hasEarlyReturn(component)
244
+ ? this.prepareEarlyReturns(result, reactive, ending, label, component)
245
+ : null;
246
+ const saved = this.earlyReturns;
247
+ this.earlyReturns = early;
248
+ try {
249
+ this.blockStatements(setup, () => {
250
+ if (last?.kind !== 'ReturnStatement' || !last.values[0])
251
+ return;
252
+ const previous = setup.at(-1);
253
+ if (previous && this.blankLineBetween(previous, last))
254
+ this.blankLine();
255
+ const value = last.values[0];
256
+ this.withHoisting(true, () => {
257
+ if (early)
258
+ this.lastReturn(value, ending, early);
259
+ else
260
+ this.markup(value, result, reactive, ending);
261
+ });
262
+ });
263
+ }
264
+ finally {
265
+ this.earlyReturns = saved;
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,25 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { ElementEmitter, type ControlStatement } from './elements.ts';
3
+ import { type Live, type Source } from './reactive.ts';
4
+ /**
5
+ * `{if ...}`, `{switch ...}` and `{for ...}` in live markup that depend on state. Markup that does
6
+ * not change compiles to plain control flow (see ElementEmitter.children); here helpers keep the
7
+ * content between markers instead, and each block is a function that creates its content and
8
+ * returns its update.
9
+ */
10
+ export declare abstract class ControlFlowEmitter extends ElementEmitter {
11
+ /** Writes the code that creates the content and returns what to append; see the class. */
12
+ protected liveControl(statement: ControlStatement, live: Live, sources: ReadonlySet<Source>): string;
13
+ /** The choice of an if/else chain, `() => a ? 0 : b ? 1 : -1`, and its blocks. */
14
+ private ifBranches;
15
+ /** The choice of a switch: the same switch, returning the number of the case. */
16
+ private switchBranches;
17
+ /**
18
+ * The function that creates a block of markup control flow:
19
+ * `(item, index) => { ...; return [fragment, update]; }`. Parts of the block that depend on
20
+ * state go into its update. The block of `<Page />` gets the component, `($$component3) => ...`.
21
+ */
22
+ protected blockCreator(statements: readonly ast.Statement[], parent: Live, loop?: ast.ForInStatement, component?: ast.Identifier): string;
23
+ /** A classic `for` loop in live markup: a function that runs it and returns the content. */
24
+ private loopContent;
25
+ }
@@ -0,0 +1,152 @@
1
+ import { declarationsOf } from "./analysis.js";
2
+ import { ElementEmitter } from "./elements.js";
3
+ import { Block } from "./reactive.js";
4
+ import { CONDITIONAL, indentMore, upperFirst } from "./syntax.js";
5
+ /**
6
+ * `{if ...}`, `{switch ...}` and `{for ...}` in live markup that depend on state. Markup that does
7
+ * not change compiles to plain control flow (see ElementEmitter.children); here helpers keep the
8
+ * content between markers instead, and each block is a function that creates its content and
9
+ * returns its update.
10
+ */
11
+ export class ControlFlowEmitter extends ElementEmitter {
12
+ /** Writes the code that creates the content and returns what to append; see the class. */
13
+ liveControl(statement, live, sources) {
14
+ const id = this.nextId();
15
+ const kind = statement.kind === 'IfStatement'
16
+ ? 'if'
17
+ : statement.kind === 'SwitchStatement'
18
+ ? 'switch'
19
+ : 'for';
20
+ const name = `$$${kind}${id}`;
21
+ const update = `$$update${upperFirst(kind)}${id}`;
22
+ let call;
23
+ switch (statement.kind) {
24
+ case 'IfStatement':
25
+ case 'SwitchStatement': {
26
+ const [choose, blocks] = statement.kind === 'IfStatement'
27
+ ? this.ifBranches(statement, live)
28
+ : this.switchBranches(statement, live);
29
+ this.helpers.add('branches');
30
+ call = `$$branches(${choose}, [${blocks.join(', ')}])`;
31
+ break;
32
+ }
33
+ case 'ForInStatement': {
34
+ this.helpers.add('list');
35
+ const items = this.liveFunction(statement.iterable);
36
+ call = `$$list(${items}, ${this.blockCreator(statement.body.body, live, statement)})`;
37
+ break;
38
+ }
39
+ case 'ForStatement':
40
+ // Without items to find the blocks by, the loop runs again and creates new content.
41
+ this.helpers.add('content');
42
+ call = `$$content(${this.loopContent(statement)})`;
43
+ break;
44
+ }
45
+ this.line(`const [${name}, ${update}] = ${call};`);
46
+ live.depend(sources, `${update}();`);
47
+ return name;
48
+ }
49
+ /** The choice of an if/else chain, `() => a ? 0 : b ? 1 : -1`, and its blocks. */
50
+ ifBranches(node, live) {
51
+ const conditions = [];
52
+ const blocks = [];
53
+ let current = node;
54
+ while (current?.kind === 'IfStatement') {
55
+ conditions.push(this.liveExpression(current.condition, CONDITIONAL + 1));
56
+ blocks.push(this.blockCreator(current.consequent.body, live));
57
+ current = current.alternate;
58
+ }
59
+ if (current)
60
+ blocks.push(this.blockCreator(current.body, live));
61
+ let choose = current ? String(conditions.length) : '-1';
62
+ for (let i = conditions.length - 1; i >= 0; i--)
63
+ choose = `${conditions[i]} ? ${i} : ${choose}`;
64
+ return [`() => ${choose}`, blocks];
65
+ }
66
+ /** The choice of a switch: the same switch, returning the number of the case. */
67
+ switchBranches(node, live) {
68
+ const blocks = node.cases.map((switchCase) => this.blockCreator(switchCase.body, live));
69
+ const choice = {
70
+ ...node,
71
+ cases: node.cases.map((switchCase, i) => ({
72
+ ...switchCase,
73
+ body: [returnNumber(i, switchCase)],
74
+ })),
75
+ };
76
+ const hasDefault = node.cases.some((switchCase) => switchCase.tests.length === 0);
77
+ const body = this.withFunction('none', [], () => this.withRendering(this.rendering + 1, () => this.block(() => {
78
+ this.statement(choice);
79
+ if (!hasDefault)
80
+ this.line('return -1;');
81
+ })));
82
+ return [`() => ${body}`, blocks];
83
+ }
84
+ /**
85
+ * The function that creates a block of markup control flow:
86
+ * `(item, index) => { ...; return [fragment, update]; }`. Parts of the block that depend on
87
+ * state go into its update. The block of `<Page />` gets the component, `($$component3) => ...`.
88
+ */
89
+ blockCreator(statements, parent, loop, component) {
90
+ const block = new Block(parent);
91
+ const params = loop
92
+ ? [loop.value, ...(loop.key ? [loop.key] : [])].map(parameter)
93
+ : component
94
+ ? [parameter(component)]
95
+ : [];
96
+ return this.withFunction('none', params, () => this.withRendering(this.rendering + 1, () => {
97
+ const paramList = this.params(params);
98
+ const fragment = `$$block${this.nextId()}`;
99
+ const body = this.block(() => this.withScope(declarationsOf(statements), () => {
100
+ this.line(`const ${fragment} = document.createDocumentFragment();`);
101
+ this.children(fragment, statements, block);
102
+ // The block of an item that stays gets the item's new position.
103
+ const key = loop?.key && loop.key.name !== '_' ? this.name(loop.key.name) : null;
104
+ const updates = [...(key ? [`${key} = $$index;`] : []), ...block.statements];
105
+ if (updates.length === 0) {
106
+ this.line(`return [${fragment}];`);
107
+ return;
108
+ }
109
+ const update = this.block(() => {
110
+ for (const statement of updates)
111
+ this.line(indentMore(statement));
112
+ });
113
+ this.line(`return [${fragment}, (${key ? '$$index' : ''}) => ${update}];`);
114
+ }));
115
+ return `(${paramList}) => ${body}`;
116
+ }));
117
+ }
118
+ /** A classic `for` loop in live markup: a function that runs it and returns the content. */
119
+ loopContent(statement) {
120
+ return this.withFunction('none', [], () => this.withRendering(this.rendering + 1, () => {
121
+ const fragment = `$$block${this.nextId()}`;
122
+ const body = this.block(() => {
123
+ this.line(`const ${fragment} = document.createDocumentFragment();`);
124
+ this.withContentTarget(fragment, () => this.statement(statement));
125
+ this.line(`return ${fragment};`);
126
+ });
127
+ return `() => ${body}`;
128
+ }));
129
+ }
130
+ }
131
+ /** `return 2` for the choice of a switch case. */
132
+ function returnNumber(value, at) {
133
+ const number = {
134
+ kind: 'NumberLiteral',
135
+ value,
136
+ raw: String(value),
137
+ start: at.start,
138
+ end: at.start,
139
+ };
140
+ return { kind: 'ReturnStatement', values: [number], start: at.start, end: at.start };
141
+ }
142
+ /** A loop variable as a parameter of the function that creates the loop's blocks. */
143
+ function parameter(name) {
144
+ return {
145
+ kind: 'Parameter',
146
+ name,
147
+ type: null,
148
+ defaultValue: null,
149
+ start: name.start,
150
+ end: name.end,
151
+ };
152
+ }
@@ -0,0 +1,28 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { SimpleStatementEmitter } from './simple-statements.ts';
3
+ /** if, for, switch and try. */
4
+ export declare abstract class ControlStatementEmitter extends SimpleStatementEmitter {
5
+ protected ifStatement(node: ast.IfStatement): string;
6
+ protected loopBody(body: ast.BlockStatement): string;
7
+ protected forStatement(node: ast.ForStatement): void;
8
+ /** A statement inside a `for` header, without the `;`. */
9
+ protected simpleStatement(node: ast.SimpleStatement): string;
10
+ /**
11
+ * `for x in xs` → `for (const x of xs)`, `m.values()` for a Map, `seq()` for an iterator;
12
+ * `for i, x in xs` iterates `xs.entries()`, which gives the keys and values of a Map too.
13
+ */
14
+ protected forInStatement(node: ast.ForInStatement): void;
15
+ /** Each `case` ends with an implicit `break`; a case with declarations gets its own block. */
16
+ protected switchStatement(node: ast.SwitchStatement, discriminant: ast.Expression): void;
17
+ /**
18
+ * `select`: $$select gives the index of the case that went on and what it received, and a
19
+ * switch runs the case; a `break` inside it leaves the select, as in Go. `default` is -1.
20
+ */
21
+ protected selectStatement(node: ast.SelectStatement): void;
22
+ /**
23
+ * `switch { case cond: ... }` becomes an if/else chain. A `break` inside it must leave the
24
+ * switch, not an enclosing loop, so then the chain gets a label to break to.
25
+ */
26
+ protected taglessSwitch(node: ast.SwitchStatement): void;
27
+ protected tryStatement(node: ast.TryStatement): string;
28
+ }