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,296 @@
1
+ import { decorate } from "../decorators.js";
2
+ import { htmlTagName } from "../html-tag.js";
3
+ import { localize } from "./imported.js";
4
+ import { assignedNames, collectValueNames } from "../names.js";
5
+ import { recursiveComponents } from "../recursion.js";
6
+ import { forEachChild } from "../walk.js";
7
+ import { JS_RESERVED, JS_UNDECLARABLE } from "./syntax.js";
8
+ /**
9
+ * The base of the JS generator: the output buffer, scopes of declared names and the state of the
10
+ * current function. The generator is split into layers, each adding to the previous one:
11
+ *
12
+ * Emitter → ExpressionEmitter → CallEmitter → SimpleStatementEmitter → ControlStatementEmitter
13
+ * → ParallelLoopEmitter → StatementEmitter → UpdateEmitter → AttributeEmitter
14
+ * → CreationEmitter → ElementEmitter → ControlFlowEmitter → ComponentPropsEmitter
15
+ * → ComponentValueEmitter → EarlyReturnEmitter → RefEmitter
16
+ * → StyleEmitter → ComponentEmitter → WebComponentEmitter → FunctionEmitter → JsGenerator
17
+ */
18
+ export class Emitter {
19
+ program;
20
+ options;
21
+ lines = [];
22
+ level = 0;
23
+ scopes = [];
24
+ fn = {
25
+ deferMode: 'none',
26
+ deferList: '$$defer',
27
+ temps: 0,
28
+ breakTargets: [],
29
+ };
30
+ /** Helpers that the module must define, such as $$runDeferred. */
31
+ helpers = new Set();
32
+ /**
33
+ * Whether code for the current expression may be written before the statement that contains it.
34
+ * Elements use this to be created in statements of their own; it is off where an expression is
35
+ * not always evaluated exactly once (branches of `?:`, loop conditions and so on).
36
+ */
37
+ hoist = false;
38
+ /** Top-level `type` aliases, to find the zero value of `let id ID`. */
39
+ typeAliases = new Map();
40
+ /** Names that are assigned somewhere in the module; others keep their value. */
41
+ assigned;
42
+ /** Names used as values; imports used only as types are dropped. */
43
+ valueNames = new Set();
44
+ /** Components of the module with their decorators applied, inlined where they are used. */
45
+ components = new Map();
46
+ /**
47
+ * Components that become functions: recursive ones cannot be inlined, and web components
48
+ * (`@html-tag`) create their markup in the element's class.
49
+ */
50
+ functionComponents;
51
+ /** Web components of the module by their tags: `<app-card count={3} />` sets their properties. */
52
+ webComponents = new Map();
53
+ /** The JS functions of components of other modules: `$$Card`, imported from their modules. */
54
+ functionNames = new Map();
55
+ /** For those, the element at the top of what they create, found in their modules. */
56
+ importedRoots = new Map();
57
+ /** The inlined copies of each component and the lines they take, to choose functions (js.ts). */
58
+ inlined = new Map();
59
+ /** The decorators that components of the module apply, by name. */
60
+ appliedDecorators = new Set();
61
+ /** Copies of the code of other modules: their positions are in the sources of those. */
62
+ foreign = new WeakSet();
63
+ /** Components of other modules inlined here, by name, with what their packages bring. */
64
+ inlinedImports = new Map();
65
+ /** The components that decorators of other modules bring, by the names of the decorators. */
66
+ decoratorImports = new Map();
67
+ /** Components a package brings: they have no function here, so they are always inlined. */
68
+ alwaysInlined = new Set();
69
+ constructor(program, options) {
70
+ this.program = program;
71
+ this.options = options;
72
+ const decorators = new Map(options.decorators);
73
+ const imported = [...(options.decorators?.values() ?? [])];
74
+ for (const statement of program.body) {
75
+ if (statement.kind === 'DecoratorDeclaration') {
76
+ decorators.set(statement.name.name, { node: statement, moduleNames: [] });
77
+ }
78
+ else if (statement.kind === 'ComponentDeclaration') {
79
+ for (const use of statement.decorators)
80
+ this.appliedDecorators.add(use.name.name);
81
+ }
82
+ }
83
+ const originals = new WeakSet();
84
+ const collect = (node) => {
85
+ originals.add(node);
86
+ forEachChild(node, collect);
87
+ };
88
+ const sources = [...imported, ...(options.components?.values() ?? [])];
89
+ for (const each of imported)
90
+ collect(each.node);
91
+ for (const each of sources) {
92
+ for (const node of each.inline?.components.values() ?? [])
93
+ collect(node);
94
+ for (const source of each.inline?.decorators.values() ?? [])
95
+ collect(source.node);
96
+ }
97
+ // Copies of other modules' code keep the types of the originals, from their own modules.
98
+ const { types } = options;
99
+ const keepType = (copy, original) => {
100
+ const expression = original;
101
+ const type = types?.get(expression) ??
102
+ sources.map((each) => each.types?.get(expression)).find((each) => each !== undefined);
103
+ if (type)
104
+ types?.set(copy, type);
105
+ if (originals.has(original) || this.foreign.has(original))
106
+ this.foreign.add(copy);
107
+ };
108
+ // The components that a decorator of another module uses come with it, under tags of their own.
109
+ for (const [name, decorator] of options.decorators ?? []) {
110
+ if (!decorator.inline || !this.appliedDecorators.has(name))
111
+ continue;
112
+ // Its members cannot hide the names its components use: the checker of its module sees to it.
113
+ const localized = localize(name, name, decorator.inline, keepType);
114
+ this.decoratorImports.set(name, localized);
115
+ decorators.set(name, { ...decorator, tags: localized.tags });
116
+ for (const [tag, node] of localized.inline) {
117
+ this.components.set(tag, node);
118
+ this.alwaysInlined.add(tag);
119
+ }
120
+ }
121
+ for (const statement of program.body) {
122
+ if (statement.kind === 'TypeAliasDeclaration') {
123
+ this.typeAliases.set(statement.name.name, statement.type);
124
+ }
125
+ else if (statement.kind === 'ComponentDeclaration') {
126
+ this.components.set(statement.name.name, decorate(statement, decorators, keepType));
127
+ }
128
+ }
129
+ // A component used as a value is a function, which tags that take it from there call.
130
+ collectValueNames(program, this.valueNames, (name) => this.components.has(name) || options.components?.has(name) === true);
131
+ // A small component of another module is inlined, unless generateJs chose to call it.
132
+ for (const [name, component] of options.components ?? []) {
133
+ if (!component.inline || options.functions?.has(name) || this.valueNames.has(name))
134
+ continue;
135
+ const localized = localize(name, component.node.name.name, component.inline, keepType);
136
+ this.inlinedImports.set(name, localized);
137
+ for (const [tag, node] of localized.inline) {
138
+ this.components.set(tag, node);
139
+ if (tag !== name)
140
+ this.alwaysInlined.add(tag);
141
+ }
142
+ }
143
+ // Nodes of other modules that were not copied, as names that are not renamed, keep their types.
144
+ const fill = (node) => {
145
+ const expression = node;
146
+ if (!types.has(expression)) {
147
+ const type = sources.map((each) => each.types?.get(expression)).find(Boolean);
148
+ if (type)
149
+ types.set(expression, type);
150
+ }
151
+ forEachChild(node, fill);
152
+ };
153
+ if (types && sources.length > 0) {
154
+ for (const component of this.components.values())
155
+ fill(component);
156
+ }
157
+ this.assigned = assignedNames(program);
158
+ for (const component of this.components.values())
159
+ assignedNames(component, this.assigned);
160
+ const functionComponents = recursiveComponents(this.components);
161
+ for (const [name, component] of this.components) {
162
+ // Other modules call an exported component, so it is a function here too.
163
+ if (component.exported || this.valueNames.has(name))
164
+ functionComponents.add(component);
165
+ if (!component.htmlTag)
166
+ continue;
167
+ functionComponents.add(component);
168
+ this.webComponents.set(htmlTagName(component), component);
169
+ }
170
+ for (const name of options.functions ?? []) {
171
+ const component = this.components.get(name);
172
+ if (component)
173
+ functionComponents.add(component);
174
+ }
175
+ // Other components of other modules are called: their functions are imported.
176
+ const called = [
177
+ ...(options.components ?? []),
178
+ ]
179
+ .filter(([name]) => !this.inlinedImports.has(name))
180
+ .map(([name, { node, root }]) => [name, { node, name: `$$${name}`, root }]);
181
+ for (const localized of [...this.inlinedImports.values(), ...this.decoratorImports.values()]) {
182
+ called.push(...localized.functions);
183
+ }
184
+ for (const [tag, { node, name, root }] of called) {
185
+ this.components.set(tag, node);
186
+ this.functionNames.set(node, name);
187
+ this.importedRoots.set(node, root);
188
+ functionComponents.add(node);
189
+ }
190
+ this.functionComponents = functionComponents;
191
+ }
192
+ // ─── Output ────────────────────────────────────────────────────────────────────────────────────
193
+ indent() {
194
+ return ' '.repeat(this.level);
195
+ }
196
+ /** Writes a line at the current indentation. Later lines of `text` are already indented. */
197
+ line(text) {
198
+ this.lines.push(this.indent() + text);
199
+ }
200
+ blankLine() {
201
+ if (this.lines.length > 0 && this.lines.at(-1) !== '')
202
+ this.lines.push('');
203
+ }
204
+ /** Returns the lines written by `emit`, one level deeper than the current one. */
205
+ capture(emit) {
206
+ const saved = this.lines;
207
+ this.lines = [];
208
+ this.level++;
209
+ try {
210
+ emit();
211
+ return this.lines;
212
+ }
213
+ finally {
214
+ this.level--;
215
+ this.lines = saved;
216
+ }
217
+ }
218
+ /** Renders the lines written by `emit` as a `{ ... }` block, for use inside a line. */
219
+ block(emit) {
220
+ const inner = this.capture(emit);
221
+ return inner.length === 0 ? '{}' : `{\n${inner.join('\n')}\n${this.indent()}}`;
222
+ }
223
+ blankLineBetween(previous, next) {
224
+ if (this.foreign.has(previous) || this.foreign.has(next))
225
+ return false;
226
+ return /\n[^\S\n]*\n/.test(this.options.source.slice(previous.end, next.start));
227
+ }
228
+ /** A component of the module as it is inlined: with its decorators applied. */
229
+ applied(component) {
230
+ return this.components.get(component.name.name) ?? component;
231
+ }
232
+ // ─── Scopes and names ──────────────────────────────────────────────────────────────────────────
233
+ withScope(bindings, emit) {
234
+ this.scopes.push(new Map(bindings));
235
+ try {
236
+ return emit();
237
+ }
238
+ finally {
239
+ this.scopes.pop();
240
+ }
241
+ }
242
+ lookup(name) {
243
+ for (let i = this.scopes.length - 1; i >= 0; i--) {
244
+ const kind = this.scopes[i].get(name);
245
+ if (kind !== undefined)
246
+ return kind;
247
+ }
248
+ return undefined;
249
+ }
250
+ /** The JS name for a Rook name. */
251
+ name(name) {
252
+ const rename = JS_RESERVED.has(name) || (JS_UNDECLARABLE.has(name) && this.lookup(name) !== undefined);
253
+ return rename ? `${name}$` : name;
254
+ }
255
+ params(params) {
256
+ // Several `_` parameters would be duplicate names in JS.
257
+ const blanks = params.filter((param) => param.name.name === '_').length;
258
+ return params
259
+ .map((param, i) => param.name.name === '_' && blanks > 1 ? `_${i}` : this.name(param.name.name))
260
+ .join(', ');
261
+ }
262
+ // ─── Functions ─────────────────────────────────────────────────────────────────────────────────
263
+ withFunction(mode, params, emit) {
264
+ const saved = this.fn;
265
+ this.fn = { deferMode: mode, deferList: '$$defer', temps: 0, breakTargets: [] };
266
+ try {
267
+ return this.withScope(params.map((param) => [param.name.name, 'param']), emit);
268
+ }
269
+ finally {
270
+ this.fn = saved;
271
+ }
272
+ }
273
+ withHoisting(hoist, emit) {
274
+ const saved = this.hoist;
275
+ this.hoist = hoist;
276
+ try {
277
+ return emit();
278
+ }
279
+ finally {
280
+ this.hoist = saved;
281
+ }
282
+ }
283
+ /** A fresh temporary name: `$$0`, `$$1`, ... */
284
+ temp() {
285
+ return `$$${this.fn.temps++}`;
286
+ }
287
+ withBreakTarget(label, emit) {
288
+ this.fn.breakTargets.push(label);
289
+ try {
290
+ return emit();
291
+ }
292
+ finally {
293
+ this.fn.breakTargets.pop();
294
+ }
295
+ }
296
+ }
@@ -0,0 +1,49 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { Emitter } from './emitter.ts';
3
+ /** Expressions, with the parentheses that JS operator precedence requires. */
4
+ export declare abstract class ExpressionEmitter extends Emitter {
5
+ /** Parameters and body of a function; implemented by FunctionEmitter. */
6
+ protected abstract func(params: ast.Parameter[], body: ast.BlockStatement): [string, string];
7
+ /** Code that creates an element, as an expression; implemented by ElementEmitter. */
8
+ protected abstract element(node: ast.ElementExpression): string;
9
+ /** Writes a statement; implemented by StatementEmitter. */
10
+ protected abstract statement(node: ast.Statement): void;
11
+ /** Whether an arrow function's expression body changes component state; see ElementEmitter. */
12
+ protected abstract changesState(body: ast.Expression): boolean;
13
+ /** Go-style zero value of a type, or `null`; implemented by SimpleStatementEmitter. */
14
+ protected abstract zeroValue(type: ast.TypeNode): string | null;
15
+ /** `f(x)`, `new C(x)`, `[a, b]` and `[]byte(s)`; implemented by CallEmitter. */
16
+ protected abstract call(node: ast.CallExpression): string;
17
+ protected abstract newExpression(node: ast.NewExpression): string;
18
+ protected abstract arrayLiteral(node: ast.ArrayLiteral): string;
19
+ protected abstract conversion(node: ast.ConversionExpression): string;
20
+ protected abstract make(node: ast.MakeExpression): string;
21
+ /** `<-ch` that gives `[value, ok]`, in `v, ok = <-ch`. */
22
+ protected readonly commaOk: WeakSet<ast.Expression>;
23
+ /** `items[i]` in a part of `go for`, which is `items[$$i]` there (parallel.ts). */
24
+ protected readonly partElements: Set<ast.Expression>;
25
+ /** In the worker module, `go` runs in the same worker (go-helpers.ts). */
26
+ protected get inWorker(): boolean;
27
+ /** `v, ok = <-ch`: two variables from a single receive. */
28
+ protected markCommaOk(count: number, values: readonly ast.Expression[]): void;
29
+ /** The expression, in parentheses if it binds weaker than `minPrecedence`. */
30
+ protected expression(node: ast.Expression, minPrecedence: number, forceParens?: boolean): string;
31
+ /** An expression used as a statement: JS would read a leading `{` as a block. */
32
+ protected expressionStatement(node: ast.Expression): string;
33
+ /** `1.toFixed()` is not valid JS, so number literals before `.` get parentheses. */
34
+ protected memberObject(node: ast.Expression, render: (node: ast.Expression) => string): string;
35
+ /** Whether `error` refers to the predeclared function rather than to a declared name. */
36
+ protected isBuiltinError(node: ast.Expression): boolean;
37
+ /**
38
+ * The component a capitalized name refers to here: not a variable that hides it, as the
39
+ * property `Header` of an inlined component does.
40
+ */
41
+ protected componentNamed(name: string): ast.ComponentDeclaration | undefined;
42
+ private expressionWithPrecedence;
43
+ protected elements(elements: readonly (ast.Expression | ast.SpreadElement)[]): string;
44
+ private template;
45
+ private objectLiteral;
46
+ private arrowFunction;
47
+ private unary;
48
+ private binary;
49
+ }
@@ -0,0 +1,262 @@
1
+ import { decoratedName } from "../decorators.js";
2
+ import { goCaptures, goParams, goTarget } from "../goroutines.js";
3
+ import { isIterator, usesThis } from "../iterators.js";
4
+ import { containsElement, startsWithObjectLiteral } from "./analysis.js";
5
+ import { Emitter } from "./emitter.js";
6
+ import { PREDECLARED } from "./helpers.js";
7
+ import { ARROW, BINARY, CONDITIONAL, POSTFIX, PRIMARY, UNARY } from "./syntax.js";
8
+ /** Expressions, with the parentheses that JS operator precedence requires. */
9
+ export class ExpressionEmitter extends Emitter {
10
+ /** `<-ch` that gives `[value, ok]`, in `v, ok = <-ch`. */
11
+ commaOk = new WeakSet();
12
+ /** `items[i]` in a part of `go for`, which is `items[$$i]` there (parallel.ts). */
13
+ partElements = new Set();
14
+ /** In the worker module, `go` runs in the same worker (go-helpers.ts). */
15
+ get inWorker() {
16
+ return this.options.goroutines !== undefined && 'targets' in this.options.goroutines;
17
+ }
18
+ /** `v, ok = <-ch`: two variables from a single receive. */
19
+ markCommaOk(count, values) {
20
+ const [value] = values;
21
+ if (count === 2 && values.length === 1 && value?.kind === 'UnaryExpression') {
22
+ if (value.operator === '<-')
23
+ this.commaOk.add(value);
24
+ }
25
+ }
26
+ /** The expression, in parentheses if it binds weaker than `minPrecedence`. */
27
+ expression(node, minPrecedence, forceParens = false) {
28
+ const [text, precedence] = this.expressionWithPrecedence(node);
29
+ return precedence < minPrecedence || forceParens ? `(${text})` : text;
30
+ }
31
+ /** An expression used as a statement: JS would read a leading `{` as a block. */
32
+ expressionStatement(node) {
33
+ const text = this.expression(node, 0);
34
+ return startsWithObjectLiteral(node) ? `(${text})` : text;
35
+ }
36
+ /** `1.toFixed()` is not valid JS, so number literals before `.` get parentheses. */
37
+ memberObject(node, render) {
38
+ const text = render(node);
39
+ return node.kind === 'NumberLiteral' ? `(${text})` : text;
40
+ }
41
+ /** Whether `error` refers to the predeclared function rather than to a declared name. */
42
+ isBuiltinError(node) {
43
+ return (node.kind === 'Identifier' && node.name === 'error' && this.lookup('error') === undefined);
44
+ }
45
+ /**
46
+ * The component a capitalized name refers to here: not a variable that hides it, as the
47
+ * property `Header` of an inlined component does.
48
+ */
49
+ componentNamed(name) {
50
+ const binding = this.lookup(name);
51
+ return binding === undefined || binding === 'import' ? this.components.get(name) : undefined;
52
+ }
53
+ expressionWithPrecedence(node) {
54
+ switch (node.kind) {
55
+ case 'Identifier': {
56
+ const component = this.componentNamed(node.name);
57
+ // A component as a value is its function.
58
+ if (component) {
59
+ return [this.functionNames.get(component) ?? `$$${component.name.name}`, PRIMARY];
60
+ }
61
+ // `close(ch)` and the like are predeclared; JS has a `close` of its own, the window's.
62
+ const helper = Object.hasOwn(PREDECLARED, node.name) ? PREDECLARED[node.name] : undefined;
63
+ if (helper && this.lookup(node.name) === undefined) {
64
+ this.helpers.add(helper);
65
+ return [`$$${node.name}`, PRIMARY];
66
+ }
67
+ // The predeclared `error` used as a value is JS's `Error`, which also works without `new`.
68
+ return [this.isBuiltinError(node) ? 'Error' : this.name(node.name), PRIMARY];
69
+ }
70
+ case 'NumberLiteral':
71
+ case 'BigIntLiteral':
72
+ case 'StringLiteral':
73
+ return [node.raw, PRIMARY];
74
+ case 'TemplateLiteral':
75
+ return [this.template(node), PRIMARY];
76
+ case 'BooleanLiteral':
77
+ return [String(node.value), PRIMARY];
78
+ case 'NullLiteral':
79
+ return ['null', PRIMARY];
80
+ // The member is a variable of the component's code: see decorate().
81
+ case 'DecoratorMember':
82
+ return [decoratedName(node.decorator.name, node.member.name), PRIMARY];
83
+ case 'DecoratorGet':
84
+ return [decoratedName(node.target.decorator.name, node.target.member.name), PRIMARY];
85
+ case 'ThisExpression':
86
+ return ['this', PRIMARY];
87
+ // `as` is only for the checker.
88
+ case 'AsExpression':
89
+ return this.expressionWithPrecedence(node.expression);
90
+ case 'SuperExpression':
91
+ return ['super', PRIMARY];
92
+ case 'ArrayLiteral': {
93
+ const text = this.arrayLiteral(node);
94
+ return [text, text.startsWith('[') ? PRIMARY : POSTFIX];
95
+ }
96
+ case 'ObjectLiteral':
97
+ return [this.objectLiteral(node), PRIMARY];
98
+ case 'FuncExpression': {
99
+ // Func literals become arrow functions, so `this` inside them is the enclosing `this`. An
100
+ // iterator is a generator function, which is never an arrow: it gets that `this` bound.
101
+ const [params, body] = this.func(node.params, node.body);
102
+ if (isIterator(node.params)) {
103
+ return usesThis(node.body)
104
+ ? [`function* () ${body}.bind(this)`, POSTFIX]
105
+ : [`function* () ${body}`, PRIMARY];
106
+ }
107
+ return [`${node.async ? 'async ' : ''}(${params}) => ${body}`, ARROW];
108
+ }
109
+ case 'ArrowFunction':
110
+ return [this.arrowFunction(node), ARROW];
111
+ // `chan<number>(10)`: the channel keeps the zero value that a closed one gives.
112
+ case 'ChanExpression': {
113
+ const size = node.size ? this.expression(node.size, ARROW) : '0';
114
+ this.helpers.add('chan');
115
+ return [`$$chan(${size}, ${this.zeroValue(node.element) ?? 'null'})`, POSTFIX];
116
+ }
117
+ // The worker module has the function under the same name, a literal under the name given to
118
+ // it, after the local names it uses; the arguments are copied there. In the worker module a
119
+ // literal runs in the same worker, with copies of those names as its first parameters.
120
+ case 'GoExpression': {
121
+ const { callee } = node.call;
122
+ const literal = callee.kind === 'FuncExpression' ? callee : null;
123
+ const captured = literal ? goCaptures(this.program, node) : [];
124
+ const args = [
125
+ ...captured.map((name) => this.name(name)),
126
+ this.elements(node.call.arguments),
127
+ ];
128
+ // Without type checking, a callee that is neither fails when the call runs.
129
+ const fn = literal && this.inWorker
130
+ ? this.expression({ ...literal, params: [...goParams(literal, captured), ...literal.params] }, ARROW)
131
+ : JSON.stringify(goTarget(this.program, node));
132
+ this.helpers.add(this.inWorker ? 'goLocal' : 'go');
133
+ return [`$$go(${fn}, [${args.filter((arg) => arg !== '').join(', ')}])`, POSTFIX];
134
+ }
135
+ case 'UnaryExpression':
136
+ return [this.unary(node), UNARY];
137
+ case 'BinaryExpression':
138
+ return [this.binary(node), BINARY[node.operator]];
139
+ case 'ConditionalExpression': {
140
+ const test = this.expression(node.test, CONDITIONAL + 1);
141
+ // Only one branch runs, so neither may create elements in advance.
142
+ const [consequent, alternate] = this.withHoisting(false, () => [
143
+ this.expression(node.consequent, ARROW),
144
+ this.expression(node.alternate, ARROW),
145
+ ]);
146
+ return [`${test} ? ${consequent} : ${alternate}`, CONDITIONAL];
147
+ }
148
+ case 'CallExpression':
149
+ return [this.call(node), POSTFIX];
150
+ case 'NewExpression':
151
+ return [this.newExpression(node), POSTFIX];
152
+ case 'ConversionExpression':
153
+ return [this.conversion(node), POSTFIX];
154
+ case 'MakeExpression':
155
+ return [this.make(node), POSTFIX];
156
+ case 'MemberExpression': {
157
+ const object = this.memberObject(node.object, (o) => this.expression(o, POSTFIX));
158
+ return [`${object}${node.optional ? '?.' : '.'}${node.property.name}`, POSTFIX];
159
+ }
160
+ case 'IndexExpression': {
161
+ const object = this.memberObject(node.object, (o) => this.expression(o, POSTFIX));
162
+ const index = this.partElements.has(node) ? '$$i' : this.expression(node.index, 0);
163
+ return [`${object}${node.optional ? '?.[' : '['}${index}]`, POSTFIX];
164
+ }
165
+ case 'ElementExpression':
166
+ return [this.element(node), POSTFIX];
167
+ }
168
+ }
169
+ elements(elements) {
170
+ return elements
171
+ .map((element) => element.kind === 'SpreadElement'
172
+ ? `...${this.expression(element.argument, ARROW)}`
173
+ : this.expression(element, ARROW))
174
+ .join(', ');
175
+ }
176
+ template(node) {
177
+ let text = '`';
178
+ node.quasis.forEach((quasi, i) => {
179
+ text += quasi.raw;
180
+ const expression = node.expressions[i];
181
+ if (expression)
182
+ text += `\${${this.expression(expression, 0)}}`;
183
+ });
184
+ return `${text}\``;
185
+ }
186
+ objectLiteral(node) {
187
+ if (node.properties.length === 0)
188
+ return '{}';
189
+ const properties = node.properties.map((property) => {
190
+ if (property.kind === 'SpreadElement') {
191
+ return `...${this.expression(property.argument, ARROW)}`;
192
+ }
193
+ const key = property.key.kind === 'StringLiteral' ? property.key.raw : property.key.name;
194
+ const value = this.expression(property.value, ARROW);
195
+ // A renamed shorthand (`{ delete }` → `delete$`) must keep its key.
196
+ return property.shorthand && value === key ? key : `${key}: ${value}`;
197
+ });
198
+ return `{ ${properties.join(', ')} }`;
199
+ }
200
+ arrowFunction(node) {
201
+ const { body } = node;
202
+ const async = node.async ? 'async ' : '';
203
+ if (body.kind === 'BlockStatement') {
204
+ const [params, block] = this.func(node.params, body);
205
+ return `${async}(${params}) => ${block}`;
206
+ }
207
+ return this.withFunction('none', node.params, () => {
208
+ const params = this.params(node.params);
209
+ if (containsElement(body) || this.changesState(body)) {
210
+ // Elements are created by statements, and so are updates after a change of state: the
211
+ // body becomes a block.
212
+ const statement = {
213
+ kind: 'ReturnStatement',
214
+ values: [body],
215
+ start: body.start,
216
+ end: body.end,
217
+ };
218
+ const block = this.block(() => this.statement(statement));
219
+ return `${async}(${params}) => ${block}`;
220
+ }
221
+ // `() => ({ ... })`: a body starting with `{` would be read as a block.
222
+ const text = this.expression(body, ARROW, startsWithObjectLiteral(body));
223
+ return `${async}(${params}) => ${text}`;
224
+ });
225
+ }
226
+ unary(node) {
227
+ if (node.operator === '<-') {
228
+ this.helpers.add('chan');
229
+ const channel = this.expression(node.argument, ARROW);
230
+ return `await $$receive(${channel}${this.commaOk.has(node) ? ', true' : ''})`;
231
+ }
232
+ const argument = this.expression(node.argument, UNARY);
233
+ if (node.operator === 'typeof' || node.operator === 'await') {
234
+ return `${node.operator} ${argument}`;
235
+ }
236
+ // `- -x` must not turn into `--x`.
237
+ const space = (node.operator === '-' || node.operator === '+') && argument.startsWith(node.operator);
238
+ return `${node.operator}${space ? ' ' : ''}${argument}`;
239
+ }
240
+ binary(node) {
241
+ const { operator } = node;
242
+ const precedence = BINARY[operator];
243
+ const rightAssociative = operator === '**';
244
+ const nullishMix = (operand) => operand.kind === 'BinaryExpression' &&
245
+ (operator === '??'
246
+ ? operand.operator === '||' || operand.operator === '&&'
247
+ : (operator === '||' || operator === '&&') && operand.operator === '??');
248
+ // JS rejects `-a ** b` and mixing `??` with `||` / `&&` unless there are parentheses.
249
+ const left = this.expression(node.left, rightAssociative ? precedence + 1 : precedence, (operator === '**' && node.left.kind === 'UnaryExpression') || nullishMix(node.left));
250
+ // The right side of `&&`, `||` and `??` may not be evaluated.
251
+ const shortCircuits = operator === '&&' || operator === '||' || operator === '??';
252
+ const right = this.withHoisting(this.hoist && !shortCircuits, () => this.expression(node.right, rightAssociative ? precedence : precedence + 1, nullishMix(node.right)));
253
+ let jsOperator = operator;
254
+ if (operator === '==' || operator === '!=') {
255
+ // `x == null` stays loose so that it also matches `undefined` coming from JS code.
256
+ const withNull = node.left.kind === 'NullLiteral' || node.right.kind === 'NullLiteral';
257
+ if (!withNull)
258
+ jsOperator = `${operator}=`;
259
+ }
260
+ return `${left} ${jsOperator} ${right}`;
261
+ }
262
+ }
@@ -0,0 +1,28 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { WebComponentEmitter } from './web-components.ts';
3
+ /** Function bodies and `defer`. */
4
+ export declare abstract class FunctionEmitter extends WebComponentEmitter {
5
+ /** Parameters and body of a function, method or func literal. */
6
+ protected func(params: ast.Parameter[], body: ast.BlockStatement): [string, string];
7
+ /**
8
+ * `effect { ... }` becomes a function, `$$effect5`, which runs once the markup is created and
9
+ * again after the updates of what it reads (see EarlyReturnEmitter). What it defers is run before
10
+ * it runs again, and when `{if}`, `{switch}` or `{for}` removes the component.
11
+ */
12
+ protected effectStatement(node: ast.EffectStatement): void;
13
+ private effectBody;
14
+ /** A `defer` in 'stack' and 'effect' modes; in 'try' mode statementsWithDefers handles them. */
15
+ protected deferStatement(node: ast.DeferStatement): void;
16
+ /** Statements of a body whose `defer`s are all at its top level: one try/finally per `defer`. */
17
+ private statementsWithDefers;
18
+ /**
19
+ * Go evaluates the function and arguments of a deferred call at the `defer` statement. Values
20
+ * that cannot change in between are used as they are; others are saved to temporaries here.
21
+ * Returns a function that writes the deferred code.
22
+ */
23
+ private prepareDeferred;
24
+ private deferredCall;
25
+ /** The expression itself if its value cannot change before the deferred call runs. */
26
+ private stable;
27
+ private isStable;
28
+ }