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,140 @@
1
+ import { ARRAY_MUTATORS } from "../methods.js";
2
+ import { DecoratorChecker } from "./decorators.js";
3
+ import { nonNull, UNKNOWN } from "./types.js";
4
+ // The public members of decorators, in the components they are applied to: `@visible.show()` calls
5
+ // a function, `get(@visible.shown)` reads state or a constant, and none of them can be changed
6
+ // outside the decorator.
7
+ export class DecoratorMemberChecker extends DecoratorChecker {
8
+ /** `@visible.show`: a public function; state and constants are read with `get`. */
9
+ checkDecoratorMember(node) {
10
+ const member = this.findDecoratorMember(node);
11
+ if (member && member.kind !== 'func') {
12
+ const { decorator, member: name } = node;
13
+ this.error(`read "${name.name}" of @${decorator.name} with get(@${decorator.name}.${name.name})`, node);
14
+ }
15
+ return member?.type ?? UNKNOWN;
16
+ }
17
+ /** `get(@visible.shown)`: public state or a constant. */
18
+ checkDecoratorGet(node) {
19
+ const member = this.findDecoratorMember(node.target);
20
+ if (member?.kind === 'func') {
21
+ const { decorator, member: name } = node.target;
22
+ this.error(`"${name.name}" of @${decorator.name} is a function: call it, @${decorator.name}.${name.name}()`, node);
23
+ }
24
+ return member?.type ?? UNKNOWN;
25
+ }
26
+ /**
27
+ * A public member of a decorator applied to the component being checked, or of one that the
28
+ * decorator being checked needs.
29
+ */
30
+ findDecoratorMember(node) {
31
+ const name = node.decorator.name;
32
+ const member = node.member.name;
33
+ if (this.decorator)
34
+ return this.neededMember(node, this.decorator.node);
35
+ const component = this.component;
36
+ if (!component) {
37
+ this.error(`@${name}.${member} can only be used in a component that @${name} is applied to`, node);
38
+ return null;
39
+ }
40
+ const info = component.decorators.find((decorator) => decorator.node.name.name === name);
41
+ if (!info) {
42
+ this.error(this.decorators.has(name)
43
+ ? `@${name} is not applied to ${component.node.name.name}: write @${name} before comp`
44
+ : `unknown decorator @${name}`, node.decorator);
45
+ return null;
46
+ }
47
+ return this.publicMember(node, info);
48
+ }
49
+ /** `get(@cart.count)` in `dec cartBadge() needs cart`. */
50
+ neededMember(node, decorator) {
51
+ const name = node.decorator.name;
52
+ const current = decorator.name.name;
53
+ if (name === current) {
54
+ this.error(`inside @${name} its members are used by their names: ${node.member.name}`, node);
55
+ return null;
56
+ }
57
+ if (!decorator.needs.some((need) => need.name === name)) {
58
+ this.error(`@${current} can use @${name} only if it needs it: write "needs ${name}" after its parameters`, node.decorator);
59
+ return null;
60
+ }
61
+ const info = this.decorators.get(name);
62
+ // An unknown decorator after `needs` is reported there.
63
+ return info ? this.publicMember(node, info) : null;
64
+ }
65
+ publicMember(node, info) {
66
+ const name = node.decorator.name;
67
+ const member = node.member.name;
68
+ const found = info.members.get(member);
69
+ if (!found) {
70
+ this.error(info.names.has(member)
71
+ ? `"${member}" of @${name} is not public: write "public" before its declaration`
72
+ : `@${name} has no member "${member}"`, node.member);
73
+ }
74
+ return found ?? null;
75
+ }
76
+ /**
77
+ * Members of a decorator are read-only outside it: `get(@cart.count) = 0`, `get(@cart.items)[0]
78
+ * = x`. Returns whether `target` is one of them.
79
+ */
80
+ checkDecoratorWrite(target) {
81
+ const root = decoratorRoot(target);
82
+ if (!root)
83
+ return false;
84
+ if (this.findDecoratorMember(root))
85
+ this.readOnlyError(root, target);
86
+ return true;
87
+ }
88
+ /** `get(@cart.items).push(x)`: a method that changes the array. */
89
+ checkDecoratorMutation(node) {
90
+ const { callee } = node;
91
+ if (callee.kind !== 'MemberExpression')
92
+ return;
93
+ const root = decoratorRoot(callee.object);
94
+ if (!root || !ARRAY_MUTATORS.has(callee.property.name))
95
+ return;
96
+ if (nonNull(this.typeOfChecked(callee.object)).kind !== 'array')
97
+ return;
98
+ this.readOnlyError(root, node);
99
+ }
100
+ readOnlyError(member, node) {
101
+ const name = member.decorator.name;
102
+ const info = this.decorators.get(name);
103
+ const change = [...(info?.members ?? [])].find(([, found]) => found.kind === 'func')?.[0];
104
+ const hint = change
105
+ ? `change it with one of its public functions, e.g. @${name}.${change}()`
106
+ : `give @${name} a public function that changes it`;
107
+ this.error(`"${member.member.name}" of @${name} is read-only outside it; ${hint}`, node);
108
+ }
109
+ /** `card.count` without `@`: `card` is not a value, but a decorator may have that name. */
110
+ decoratorWithoutAt(node) {
111
+ const { object } = node;
112
+ if (object.kind !== 'Identifier' || this.lookupValue(object.name))
113
+ return false;
114
+ const info = this.decorators.get(object.name);
115
+ if (!info)
116
+ return false;
117
+ const name = object.name;
118
+ const member = node.property.name;
119
+ this.error(info.members.get(member)?.kind === 'func'
120
+ ? `"${name}" is not defined; to call a function of @${name} write @${name}.${member}()`
121
+ : `"${name}" is not defined; to read public state of @${name} write get(@${name}.${member})`, object);
122
+ return true;
123
+ }
124
+ }
125
+ /** The member of a decorator that `get(@cart.items)[0].done` starts with, if any. */
126
+ function decoratorRoot(node) {
127
+ switch (node.kind) {
128
+ case 'DecoratorMember':
129
+ return node;
130
+ case 'DecoratorGet':
131
+ return node.target;
132
+ case 'MemberExpression':
133
+ case 'IndexExpression':
134
+ return decoratorRoot(node.object);
135
+ case 'CallExpression':
136
+ return decoratorRoot(node.callee);
137
+ default:
138
+ return null;
139
+ }
140
+ }
@@ -0,0 +1,20 @@
1
+ import type { ComponentInfo, DecoratorInfo } from './context.ts';
2
+ import { StyleChecker } from './styles.ts';
3
+ import { type Type } from './types.ts';
4
+ type WrapperInfo = NonNullable<DecoratorInfo['wrapper']>;
5
+ export declare abstract class DecoratorWrapperChecker extends StyleChecker {
6
+ /** The type of what the wrapper gets, resolved with the other declarations of the module. */
7
+ protected resolveWrapper(info: DecoratorInfo): void;
8
+ /** The `return` of a decorator's body, once the body is checked. */
9
+ protected checkWrapper(info: DecoratorInfo): void;
10
+ private checkWrapperParam;
11
+ private takesMarkup;
12
+ /** A wrapper is inlined where the component returns, so it returns once, at its end. */
13
+ private checkWrapperBody;
14
+ protected wrappedType(info: ComponentInfo, own: Type, seen: Set<ComponentInfo>): Type;
15
+ /** What a wrapper gives, found without checking it again, like the markup of a component. */
16
+ protected wrapperResult(wrapper: WrapperInfo, seen: Set<ComponentInfo>): Type;
17
+ /** What the component returns fits its wrappers, from the bottom decorator up. */
18
+ protected checkWrappedTypes(info: ComponentInfo): void;
19
+ }
20
+ export {};
@@ -0,0 +1,132 @@
1
+ import { decoratorNames, wrapperBody, wrapperOf } from "../decorators.js";
2
+ import { isUntyped, ownStatements } from "./helpers.js";
3
+ import { StyleChecker } from "./styles.js";
4
+ import { isAssignable, typeToString, UNKNOWN } from "./types.js";
5
+ // The wrappers of decorators, `return (content Element) => <div>{content}</div>`: what they get from
6
+ // the component and what they give instead. `<Page />` has the type of what the outermost gives.
7
+ const EXAMPLE = 'return (content Element) => <div>{content}</div>';
8
+ export class DecoratorWrapperChecker extends StyleChecker {
9
+ /** The type of what the wrapper gets, resolved with the other declarations of the module. */
10
+ resolveWrapper(info) {
11
+ const node = wrapperOf(info.node);
12
+ const type = node?.param.type;
13
+ info.wrapper = node && { node, param: type ? this.resolveType(type) : UNKNOWN, result: null };
14
+ }
15
+ /** The `return` of a decorator's body, once the body is checked. */
16
+ checkWrapper(info) {
17
+ const { body } = info.node;
18
+ const last = body.body.at(-1);
19
+ for (const statement of ownStatements(body)) {
20
+ if (statement.kind === 'ReturnStatement' && statement !== last) {
21
+ this.error(`a decorator returns its wrapper at the end of its body: ${EXAMPLE}`, statement);
22
+ }
23
+ }
24
+ if (last?.kind !== 'ReturnStatement')
25
+ return;
26
+ if (!info.wrapper) {
27
+ const [value] = last.values;
28
+ const isFunction = value?.kind === 'ArrowFunction' || value?.kind === 'FuncExpression';
29
+ this.error(isFunction && last.values.length === 1
30
+ ? 'the wrapper takes one parameter, what the component returns: (content Element) => ...'
31
+ : `a decorator returns its wrapper, a function literal: ${EXAMPLE}`, value ?? last);
32
+ return;
33
+ }
34
+ this.checkWrapperParam(info, info.wrapper);
35
+ this.checkWrapperBody(info.wrapper);
36
+ }
37
+ checkWrapperParam(info, wrapper) {
38
+ const { fn, param } = wrapper.node;
39
+ const name = param.name.name;
40
+ // Without a type, the parameter is reported as one of any function literal.
41
+ if (param.type && !this.takesMarkup(wrapper)) {
42
+ this.error(`the wrapper gets markup, so "${name}" is a Node, an Element or a type of element, not ${typeToString(wrapper.param)}`, param.type);
43
+ }
44
+ // The code of the wrapper goes into the component next to the decorator's.
45
+ const members = decoratorNames(info.node);
46
+ const statements = fn.body.kind === 'BlockStatement' ? fn.body.body : [];
47
+ for (const identifier of [param.name, ...declaredIdentifiers(statements)]) {
48
+ if (members.has(identifier.name)) {
49
+ this.error(`"${identifier.name}" of the wrapper hides "${identifier.name}" of @${info.node.name.name}: rename one of them`, identifier);
50
+ }
51
+ }
52
+ }
53
+ takesMarkup(wrapper) {
54
+ return isUntyped(wrapper.param) || isAssignable(wrapper.param, this.dom.node);
55
+ }
56
+ /** A wrapper is inlined where the component returns, so it returns once, at its end. */
57
+ checkWrapperBody(wrapper) {
58
+ const { fn } = wrapper.node;
59
+ const end = wrapperBody(fn).at(-1);
60
+ if (fn.body.kind === 'BlockStatement') {
61
+ for (const statement of ownStatements(fn.body)) {
62
+ if (statement.kind === 'DeferStatement') {
63
+ this.error('defer is not supported in decorators yet', statement);
64
+ }
65
+ else if (statement !== end) {
66
+ this.error('a wrapper returns once, at the end of its body; other returns are not supported yet', statement);
67
+ }
68
+ }
69
+ }
70
+ const [value] = end?.kind === 'ReturnStatement' ? end.values : [];
71
+ if (!value) {
72
+ this.error('the wrapper ends with "return <markup>", e.g. return content', end ?? fn);
73
+ return;
74
+ }
75
+ const type = this.typeOfChecked(value);
76
+ if (!isUntyped(type) && !isAssignable(type, this.dom.node)) {
77
+ this.error(`the wrapper returns markup, not ${typeToString(type)}`, value);
78
+ }
79
+ }
80
+ wrappedType(info, own, seen) {
81
+ return info.decorators.reduceRight((type, decorator) => (decorator.wrapper ? this.wrapperResult(decorator.wrapper, seen) : type), own);
82
+ }
83
+ /** What a wrapper gives, found without checking it again, like the markup of a component. */
84
+ wrapperResult(wrapper, seen) {
85
+ // That of an exported decorator was found in its module, where its tags are names.
86
+ if (wrapper.result)
87
+ return wrapper.result;
88
+ const end = wrapperBody(wrapper.node.fn).at(-1);
89
+ const [value] = end?.kind === 'ReturnStatement' ? end.values : [];
90
+ if (value?.kind === 'Identifier' && value.name === wrapper.node.param.name.name) {
91
+ return wrapper.param;
92
+ }
93
+ return this.markupType(value, seen);
94
+ }
95
+ /** What the component returns fits its wrappers, from the bottom decorator up. */
96
+ checkWrappedTypes(info) {
97
+ let type = this.ownResult(info, new Set());
98
+ let source = null;
99
+ for (const decorator of [...info.decorators].reverse()) {
100
+ const { wrapper } = decorator;
101
+ if (!wrapper)
102
+ continue;
103
+ const name = decorator.node.name.name;
104
+ const expected = typeToString(wrapper.param);
105
+ // A parameter that takes no markup is reported with the wrapper.
106
+ const fits = isUntyped(type) || isUntyped(wrapper.param) || isAssignable(type, wrapper.param);
107
+ if (!fits && this.takesMarkup(wrapper)) {
108
+ const use = info.node.decorators.find((each) => each.name.name === name);
109
+ const given = typeToString(type);
110
+ this.error(source === null
111
+ ? `@${name} wraps ${expected}, but ${info.node.name.name} returns ${given}: apply @${name} to a component that returns ${expected}`
112
+ : `@${name} wraps ${expected}, but ${source} gives ${given}: change the order of the decorators or the type of the wrapper`, use ?? info.node.name);
113
+ }
114
+ type = this.wrapperResult(wrapper, new Set());
115
+ source = `@${name}`;
116
+ }
117
+ }
118
+ }
119
+ /** The names that statements declare in their own block. */
120
+ function declaredIdentifiers(statements) {
121
+ return statements.flatMap((statement) => {
122
+ switch (statement.kind) {
123
+ case 'VariableDeclaration':
124
+ return statement.names.filter((name) => name.name !== '_');
125
+ case 'FuncDeclaration':
126
+ case 'ClassDeclaration':
127
+ return [statement.name];
128
+ default:
129
+ return [];
130
+ }
131
+ });
132
+ }
@@ -0,0 +1,28 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { ComponentInfo, DecoratorInfo, Scope } from './context.ts';
3
+ import { DecoratorExportChecker } from './decorator-exports.ts';
4
+ export declare abstract class DecoratorChecker extends DecoratorExportChecker {
5
+ protected resolveDecoratorParams(info: DecoratorInfo): void;
6
+ /** `needs cart, auth`: other decorators, each once. */
7
+ protected checkNeedsDeclaration(info: DecoratorInfo): void;
8
+ /** Decorators after the ones they need. A cycle of needs is an error: none can be first. */
9
+ protected orderByNeeds(decorators: readonly DecoratorInfo[]): DecoratorInfo[];
10
+ /**
11
+ * The body is checked once, by itself: it does not see the components it is applied to. Its
12
+ * public members get their types here, before the bodies of components are checked.
13
+ */
14
+ protected checkDecoratorBody(info: DecoratorInfo): void;
15
+ protected collectMembers(info: DecoratorInfo, scope: Scope): void;
16
+ /**
17
+ * The decorators before `comp`: they exist, are applied once and below the ones they need, get
18
+ * valid arguments, and their wrappers get what they can take.
19
+ */
20
+ protected checkDecoratorUses(info: ComponentInfo): void;
21
+ /** Arguments are positional and see the properties of the component, as its body does. */
22
+ protected checkDecoratorArguments(use: ast.DecoratorUse, decorator: DecoratorInfo, info: ComponentInfo): void;
23
+ /**
24
+ * The code of the decorator and its arguments go into the component's body, so the names of the
25
+ * module they use must not be hidden there by the component's declarations.
26
+ */
27
+ protected checkInlinedNames(use: ast.DecoratorUse, decorator: DecoratorInfo, info: ComponentInfo): void;
28
+ }
@@ -0,0 +1,265 @@
1
+ import { freeNames } from "../names.js";
2
+ import { forEachChild } from "../walk.js";
3
+ import { DecoratorExportChecker } from "./decorator-exports.js";
4
+ import { lookupIn, ownStatements } from "./helpers.js";
5
+ import { func, UNKNOWN } from "./types.js";
6
+ // Decorators: their parameters, needs and bodies, and the decorators applied to a component with
7
+ // their arguments. Wrappers are in decorator-wrappers.ts, exported decorators in
8
+ // decorator-exports.ts, the public members a component uses in decorator-members.ts.
9
+ export class DecoratorChecker extends DecoratorExportChecker {
10
+ resolveDecoratorParams(info) {
11
+ const names = new Set();
12
+ let optional = null;
13
+ info.params = info.node.params.map((param) => {
14
+ const { name } = param.name;
15
+ if (names.has(name))
16
+ this.error(`duplicate parameter "${name}"`, param.name);
17
+ names.add(name);
18
+ if (param.defaultValue)
19
+ optional = param;
20
+ else if (optional) {
21
+ this.error(`"${name}" needs a default value: the arguments are positional, so parameters with default values go last`, param.name);
22
+ }
23
+ return param.type ? this.resolveType(param.type) : UNKNOWN;
24
+ });
25
+ this.resolveWrapper(info);
26
+ this.checkNeedsDeclaration(info);
27
+ }
28
+ /** `needs cart, auth`: other decorators, each once. */
29
+ checkNeedsDeclaration(info) {
30
+ const needed = new Set();
31
+ for (const need of info.node.needs) {
32
+ const { name } = need;
33
+ const other = this.decorators.get(name);
34
+ if (name === info.node.name.name)
35
+ this.error(`@${name} cannot need itself`, need);
36
+ else if (needed.has(name))
37
+ this.error(`@${name} is needed twice: name it once`, need);
38
+ else if (!other)
39
+ this.error(`unknown decorator @${name}`, need);
40
+ else
41
+ info.needed.push(other);
42
+ needed.add(name);
43
+ }
44
+ }
45
+ /** Decorators after the ones they need. A cycle of needs is an error: none can be first. */
46
+ orderByNeeds(decorators) {
47
+ const order = [];
48
+ const path = [];
49
+ const visit = (info) => {
50
+ if (order.includes(info))
51
+ return;
52
+ const at = path.indexOf(info);
53
+ if (at >= 0) {
54
+ const cycle = path.slice(at).map((each) => `@${each.node.name.name}`);
55
+ const steps = cycle.map((name, i) => `${name} needs ${cycle[(i + 1) % cycle.length]}`);
56
+ this.error(`decorators cannot need each other: ${steps.join(', ')}; remove one of the needs`, info.node.name);
57
+ return;
58
+ }
59
+ path.push(info);
60
+ for (const needed of info.needed)
61
+ visit(needed);
62
+ path.pop();
63
+ order.push(info);
64
+ };
65
+ decorators.forEach(visit);
66
+ return order;
67
+ }
68
+ /**
69
+ * The body is checked once, by itself: it does not see the components it is applied to. Its
70
+ * public members get their types here, before the bodies of components are checked.
71
+ */
72
+ checkDecoratorBody(info) {
73
+ const { node } = info;
74
+ node.params.forEach((param, i) => {
75
+ if (!param.defaultValue)
76
+ return;
77
+ const type = info.params[i] ?? UNKNOWN;
78
+ this.expectAssignable(this.checkValue(param.defaultValue, type), type, param.defaultValue);
79
+ });
80
+ for (const statement of ownStatements(node.body)) {
81
+ if (statement.kind === 'DeferStatement') {
82
+ this.error('defer is not supported in decorators yet', statement);
83
+ }
84
+ }
85
+ const saved = { component: this.component, decorator: this.decorator };
86
+ this.component = null;
87
+ this.decorator = info;
88
+ try {
89
+ this.withRefs(node.body, () => this.withClass(null, () => this.checkFunction(node.params, func(info.params, []), null, node.body, {
90
+ paramKind: 'prop',
91
+ inspect: (scope) => this.collectMembers(info, scope),
92
+ })));
93
+ }
94
+ finally {
95
+ this.component = saved.component;
96
+ this.decorator = saved.decorator;
97
+ }
98
+ this.checkWrapper(info);
99
+ this.checkStyles(node.name.name, node.body, `@${node.name.name}`);
100
+ if (node.exported)
101
+ this.checkExportedDecorator(info);
102
+ }
103
+ collectMembers(info, scope) {
104
+ const add = (name, kind) => {
105
+ const type = scope.values.get(name.name)?.type ?? UNKNOWN;
106
+ if (name.name !== '_')
107
+ info.members.set(name.name, { kind, type });
108
+ };
109
+ for (const statement of info.node.body.body) {
110
+ if (statement.kind === 'FuncDeclaration' && statement.isPublic) {
111
+ add(statement.name, 'func');
112
+ }
113
+ else if (statement.kind === 'VariableDeclaration' &&
114
+ statement.isPublic &&
115
+ statement.keyword !== 'let' &&
116
+ statement.keyword !== 'ref') {
117
+ for (const name of statement.names)
118
+ add(name, statement.keyword);
119
+ }
120
+ }
121
+ }
122
+ /**
123
+ * The decorators before `comp`: they exist, are applied once and below the ones they need, get
124
+ * valid arguments, and their wrappers get what they can take.
125
+ */
126
+ checkDecoratorUses(info) {
127
+ const applied = new Set();
128
+ for (const use of info.node.decorators) {
129
+ const name = use.name.name;
130
+ const decorator = this.decorators.get(name);
131
+ if (!decorator) {
132
+ this.error(`unknown decorator @${name}`, use.name);
133
+ continue;
134
+ }
135
+ if (applied.has(decorator))
136
+ this.error(`@${name} is applied twice: apply it once`, use);
137
+ // The body of a decorator may read the state of those it needs right away.
138
+ for (const needed of decorator.needed) {
139
+ if (applied.has(needed))
140
+ continue;
141
+ const other = needed.node.name.name;
142
+ this.error(`@${name} needs @${other} above it: @${other} @${name} comp ...`, use);
143
+ }
144
+ applied.add(decorator);
145
+ this.checkDecoratorArguments(use, decorator, info);
146
+ this.checkInlinedNames(use, decorator, info);
147
+ }
148
+ this.checkWrappedTypes(info);
149
+ }
150
+ /** Arguments are positional and see the properties of the component, as its body does. */
151
+ checkDecoratorArguments(use, decorator, info) {
152
+ const name = use.name.name;
153
+ const { params } = decorator.node;
154
+ const extra = use.arguments[params.length];
155
+ if (extra) {
156
+ const count = params.length === 1 ? '1 argument' : `${params.length} arguments`;
157
+ this.error(params.length === 0 ? `@${name} takes no arguments` : `@${name} takes ${count}`, extra);
158
+ }
159
+ this.withScope(() => {
160
+ for (const param of info.node.params) {
161
+ this.declareValue(param.name, 'prop', info.props.get(param.name.name)?.type ?? UNKNOWN);
162
+ }
163
+ params.forEach((param, i) => {
164
+ const type = decorator.params[i] ?? UNKNOWN;
165
+ const arg = use.arguments[i];
166
+ if (!arg) {
167
+ if (!param.defaultValue) {
168
+ this.error(`@${name} needs the argument "${param.name.name}"`, use);
169
+ }
170
+ return;
171
+ }
172
+ const context = ` for "${param.name.name}" of @${name}`;
173
+ this.expectAssignable(this.checkValue(arg, type), type, arg, context);
174
+ });
175
+ });
176
+ }
177
+ /**
178
+ * The code of the decorator and its arguments go into the component's body, so the names of the
179
+ * module they use must not be hidden there by the component's declarations.
180
+ */
181
+ checkInlinedNames(use, decorator, info) {
182
+ const name = use.name.name;
183
+ const component = info.node.name.name;
184
+ const declared = bodyNames(info.node.body);
185
+ const params = new Set(info.node.params.map((param) => param.name.name));
186
+ // Those of another module come through hidden exports, under names of their own.
187
+ const imported = this.importedDecorators.has(decorator);
188
+ const hidden = imported ? decorator.moduleNames : [];
189
+ decorator.freeNames ??= freeNames(decorator.node);
190
+ // The global names that the code of another module uses, its components' too, stay global.
191
+ const globals = imported
192
+ ? [...decorator.freeNames].filter((used) => !hidden.includes(used))
193
+ : [];
194
+ for (const used of [...globals, ...(decorator.inline?.globals ?? [])]) {
195
+ const binding = lookupIn(this.moduleScope, used);
196
+ if (!declared.has(used) && !params.has(used) && (!binding || binding.kind === 'builtin')) {
197
+ continue;
198
+ }
199
+ this.error(`@${name} uses the global "${used}", but here it is another declaration: rename it`, use);
200
+ return;
201
+ }
202
+ for (const used of decorator.freeNames) {
203
+ if (hidden.includes(used))
204
+ continue;
205
+ if (declared.has(used) || params.has(used)) {
206
+ this.error(`@${name} uses "${used}" of the module, but ${component} declares its own "${used}": rename one of them`, use);
207
+ return;
208
+ }
209
+ }
210
+ const statements = use.arguments.map((expression) => {
211
+ return { kind: 'ExpressionStatement', expression, start: use.start, end: use.end };
212
+ });
213
+ const body = {
214
+ kind: 'BlockStatement',
215
+ body: statements,
216
+ start: use.start,
217
+ end: use.end,
218
+ };
219
+ for (const used of freeNames({ params: info.node.params, body })) {
220
+ if (declared.has(used)) {
221
+ this.error(`the arguments of @${name} use "${used}" of the module, but ${component} declares its own "${used}": rename one of them`, use);
222
+ return;
223
+ }
224
+ }
225
+ }
226
+ }
227
+ /**
228
+ * Names declared in a component's body outside nested functions: those that the code of its
229
+ * decorators, which goes into the body, could see instead of the module's.
230
+ */
231
+ function bodyNames(body) {
232
+ const names = new Set();
233
+ const visit = (node) => {
234
+ switch (node.kind) {
235
+ case 'VariableDeclaration':
236
+ for (const name of node.names)
237
+ names.add(name.name);
238
+ break;
239
+ case 'FuncDeclaration':
240
+ case 'ClassDeclaration':
241
+ names.add(node.name.name);
242
+ return;
243
+ case 'FuncExpression':
244
+ case 'ArrowFunction':
245
+ case 'EventHandler':
246
+ case 'MountStatement':
247
+ case 'EffectStatement':
248
+ return;
249
+ case 'ForInStatement':
250
+ names.add(node.value.name);
251
+ if (node.key)
252
+ names.add(node.key.name);
253
+ break;
254
+ case 'CatchClause':
255
+ if (node.param)
256
+ names.add(node.param.name);
257
+ break;
258
+ default:
259
+ break;
260
+ }
261
+ forEachChild(node, visit);
262
+ };
263
+ forEachChild(body, visit);
264
+ return names;
265
+ }
@@ -0,0 +1,7 @@
1
+ import { type ObjectType, type Type } from './types.ts';
2
+ export declare const SVG_ELEMENT: ObjectType;
3
+ export declare const SVG_SVG_ELEMENT: ObjectType;
4
+ /** The type of the SVG element `<tag>` creates. */
5
+ export declare function svgElementType(tag: string): ObjectType;
6
+ /** SVG types that programs can name, e.g. `ref icon SVGSVGElement`. */
7
+ export declare const SVG_TYPES: ReadonlyMap<string, Type>;
@@ -0,0 +1,19 @@
1
+ import { ELEMENT_MEMBERS } from "./dom.js";
2
+ import { ANY, property } from "./types.js";
3
+ // SVG elements among the built-in DOM types, for the compiler without TypeScript's lib.dom: the
4
+ // members of any element, and `className`, which in SVG is an object, not a string.
5
+ function object(name, members) {
6
+ return { kind: 'object', name, members: new Map(Object.entries(members)), call: null };
7
+ }
8
+ const SVG_MEMBERS = { ...ELEMENT_MEMBERS, className: property(ANY) };
9
+ export const SVG_ELEMENT = object('SVGElement', SVG_MEMBERS);
10
+ export const SVG_SVG_ELEMENT = object('SVGSVGElement', SVG_MEMBERS);
11
+ /** The type of the SVG element `<tag>` creates. */
12
+ export function svgElementType(tag) {
13
+ return tag === 'svg' ? SVG_SVG_ELEMENT : SVG_ELEMENT;
14
+ }
15
+ /** SVG types that programs can name, e.g. `ref icon SVGSVGElement`. */
16
+ export const SVG_TYPES = new Map([
17
+ ['SVGElement', SVG_ELEMENT],
18
+ ['SVGSVGElement', SVG_SVG_ELEMENT],
19
+ ]);
@@ -0,0 +1,22 @@
1
+ import { type Member, type ObjectType, type Type } from './types.ts';
2
+ export declare const ELEMENT_MEMBERS: Record<string, Member>;
3
+ export declare const NODE: ObjectType;
4
+ export declare const HTML_ELEMENT: ObjectType;
5
+ export declare const DOCUMENT_FRAGMENT: ObjectType;
6
+ /** The type of the element `<tag>` creates. */
7
+ export declare function elementType(tag: string): ObjectType;
8
+ /**
9
+ * The type of a component's `children`: the markup between its tags. At runtime it is a
10
+ * DocumentFragment, but only placing it as `{children}` is allowed.
11
+ */
12
+ export declare const CONTENT: ObjectType;
13
+ /** DOM types that programs can name, e.g. `func render() HTMLLIElement`. */
14
+ export declare const DOM_TYPES: ReadonlyMap<string, Type>;
15
+ /** The DOM property that an attribute sets, e.g. `class` → `className`; `null` for setAttribute. */
16
+ export declare function domProperty(tag: string, attribute: string): {
17
+ name: string;
18
+ type: Type;
19
+ } | null;
20
+ export declare const EVENT: ObjectType;
21
+ /** The `event` of a handler on an element: `currentTarget` is that element. */
22
+ export declare function eventType(element: Type): ObjectType;