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,287 @@
1
+ import { attributeName, htmlTagName, RESERVED_PROPERTIES, tagNameProblem } from "../html-tag.js";
2
+ import { freeNames } from "../names.js";
3
+ import { endlessRecursion } from "../recursion.js";
4
+ import { CONTENT } from "./dom.js";
5
+ import { lookupIn, ownStatements, isUntyped, callSignature } from "./helpers.js";
6
+ import { ATTRIBUTES, ForwardingChecker } from "./forwarding.js";
7
+ import { BOOL, func, hasZeroValue, isAssignable, isNullable, nonNull, spreadFields, typesEqual, typeToString, UNKNOWN, } from "./types.js";
8
+ // Components: their bodies and uses, properties and spread, names they use from the module, recursion.
9
+ export class ComponentChecker extends ForwardingChecker {
10
+ resolveProps(info) {
11
+ for (const param of info.node.params) {
12
+ // `...attrs` takes the attributes that are not properties.
13
+ if (param.rest)
14
+ continue;
15
+ const name = param.name.name;
16
+ const type = param.type ? this.resolveType(param.type) : UNKNOWN;
17
+ if (name === 'children' && type !== CONTENT && type.kind !== 'unknown') {
18
+ this.error('the "children" property has the type Content', param.type ?? param);
19
+ }
20
+ if (info.props.has(name))
21
+ this.error(`duplicate property "${name}"`, param.name);
22
+ const optional = param.defaultValue !== null || isNullable(type) || name === 'children';
23
+ info.props.set(name, { type, optional });
24
+ }
25
+ }
26
+ /** The body runs once where the component is used and must end with `return <markup>`. */
27
+ checkComponentBody(info) {
28
+ const { node } = info;
29
+ this.checkDecoratorUses(info);
30
+ const last = node.body.body.at(-1);
31
+ if (last?.kind !== 'ReturnStatement' || last.values.length !== 1) {
32
+ this.error('a component ends with "return <markup>"', {
33
+ start: node.body.end - 1,
34
+ end: node.body.end,
35
+ });
36
+ }
37
+ for (const statement of ownStatements(node.body)) {
38
+ if (statement.kind === 'DeferStatement') {
39
+ this.error('defer is not supported in components yet', statement);
40
+ }
41
+ }
42
+ const types = [];
43
+ this.checkAttrsSpreads(info);
44
+ for (const param of node.params) {
45
+ const prop = info.props.get(param.name.name);
46
+ const type = param.rest ? ATTRIBUTES : (prop?.type ?? UNKNOWN);
47
+ types.push(type);
48
+ if (param.defaultValue) {
49
+ const value = this.checkValue(param.defaultValue, type);
50
+ this.expectAssignable(value, type, param.defaultValue);
51
+ }
52
+ }
53
+ // Other modules get the type of <Card /> from here.
54
+ if (node.exported)
55
+ info.result = this.componentResult(info, new Set());
56
+ const saved = this.component;
57
+ this.component = info;
58
+ try {
59
+ this.withClass(null, () => this.checkFunction(node.params, func(types, []), null, node.body, {
60
+ paramKind: 'prop',
61
+ isComponent: true,
62
+ }));
63
+ }
64
+ finally {
65
+ this.component = saved;
66
+ }
67
+ }
68
+ /** `<Card title="Profile">...</Card>`: properties are checked like the arguments of a call. */
69
+ checkComponentUse(node, tag) {
70
+ const binding = this.lookupValue(tag.name);
71
+ const info = binding?.component;
72
+ if (!info) {
73
+ this.error(binding ? `"${tag.name}" is not a component` : `unknown component <${tag.name}>`, tag);
74
+ return UNKNOWN;
75
+ }
76
+ // The code of a function component is not inlined here, but that of another module may be.
77
+ if (!info.isFunction)
78
+ this.checkHygiene(info, tag);
79
+ for (const name of info.inline?.globals ?? []) {
80
+ const binding = this.lookupValue(name);
81
+ if (!binding || binding.kind === 'builtin')
82
+ continue;
83
+ this.error(`<${tag.name}> uses the global "${name}", but here it is another declaration: rename it`, tag);
84
+ break;
85
+ }
86
+ const given = new Set();
87
+ const spread = new Set();
88
+ for (const attribute of node.attributes) {
89
+ if (attribute.kind === 'JsxSpreadAttribute') {
90
+ this.checkSpreadProps(attribute.argument, info, tag.name, spread);
91
+ continue;
92
+ }
93
+ const name = attribute.name.name;
94
+ const prop = info.props.get(name);
95
+ if (name === 'ref') {
96
+ this.checkRefAttribute(attribute, node);
97
+ continue;
98
+ }
99
+ if (given.has(name))
100
+ this.error(`duplicate property "${name}"`, attribute.name);
101
+ given.add(name);
102
+ if (name === 'children' && prop) {
103
+ this.error(`pass children between the tags: <${tag.name}>...</${tag.name}>`, attribute.name);
104
+ }
105
+ else if (!prop) {
106
+ if (!this.checkForwarded(attribute, info, tag.name)) {
107
+ this.error(`<${tag.name}> has no property "${name}"`, attribute.name);
108
+ }
109
+ }
110
+ else {
111
+ this.checkProp(attribute, prop.type, tag.name);
112
+ }
113
+ }
114
+ for (const [name, prop] of info.props) {
115
+ if (!prop.optional && !given.has(name) && !spread.has(name)) {
116
+ this.error(`<${tag.name}> needs the property "${name}"`, tag);
117
+ }
118
+ }
119
+ if (node.children.length > 0 && !info.props.has('children')) {
120
+ this.error(`<${tag.name}> takes no children`, node.children[0]);
121
+ }
122
+ this.checkChildren(node.children);
123
+ return this.componentResult(info, new Set());
124
+ }
125
+ /**
126
+ * `<Product {...product} />`: fields of the object with the names of properties are passed as
127
+ * those properties; other fields are ignored. Adds the names of the given properties to `given`.
128
+ */
129
+ checkSpreadProps(argument, info, component, given) {
130
+ const type = this.checkValue(argument);
131
+ if (isUntyped(type)) {
132
+ for (const name of info.props.keys())
133
+ given.add(name);
134
+ return;
135
+ }
136
+ const fields = isNullable(type) ? null : spreadFields(type);
137
+ if (!fields) {
138
+ if (isNullable(type))
139
+ this.nullError(argument);
140
+ else {
141
+ this.error(`cannot spread ${typeToString(type)} into the properties of <${component}>`, argument);
142
+ }
143
+ // After the error, missing properties would only repeat it.
144
+ for (const name of info.props.keys())
145
+ given.add(name);
146
+ return;
147
+ }
148
+ for (const [name, prop] of info.props) {
149
+ const field = fields.get(name);
150
+ if (field === undefined || name === 'children')
151
+ continue;
152
+ given.add(name);
153
+ this.expectAssignable(field, prop.type, argument, ` for "${name}" of <${component}>`);
154
+ }
155
+ }
156
+ checkProp(attribute, type, component) {
157
+ const name = attribute.name.name;
158
+ const { value } = attribute;
159
+ if (value?.kind === 'EventHandler') {
160
+ // Code for a callback property; `event` is the callback's first argument, if it has one.
161
+ const signature = callSignature(nonNull(type));
162
+ if (!signature && !isUntyped(type)) {
163
+ this.error(`"${name}" of <${component}> is not a function, so it needs a value`, value);
164
+ return;
165
+ }
166
+ this.checkEventHandler(value, signature?.params[0] ?? null);
167
+ return;
168
+ }
169
+ // A string gets the literal type where one is expected: <Button kind="ghost" />.
170
+ const valueType = value === null ? BOOL : this.checkValue(value, type);
171
+ this.expectAssignable(valueType, type, value ?? attribute, ` for "${name}" of <${component}>`);
172
+ }
173
+ /**
174
+ * The component's code is inlined where it is used, so the names of the module that it uses
175
+ * must not be hidden there by local declarations.
176
+ */
177
+ checkHygiene(info, tag) {
178
+ // The code of its decorators is inlined with it.
179
+ info.freeNames ??= freeNames(info.expanded);
180
+ for (const name of info.freeNames) {
181
+ if (this.lookupValue(name) !== lookupIn(this.moduleScope, name)) {
182
+ this.error(`<${tag.name}> uses "${name}" of the module, but here "${name}" is another declaration: rename one of them`, tag);
183
+ return;
184
+ }
185
+ }
186
+ }
187
+ /** The type of `<Card />`: what its markup creates, through the wrappers of its decorators. */
188
+ componentResult(info, seen) {
189
+ return info.result ?? this.wrappedType(info, this.ownResult(info, seen), seen);
190
+ }
191
+ /** What the markup the component itself returns creates. */
192
+ ownResult(info, seen) {
193
+ seen.add(info);
194
+ // With early returns, the result is what all the returned elements have in common.
195
+ const types = ownStatements(info.node.body)
196
+ .filter((statement) => statement.kind === 'ReturnStatement')
197
+ .map((statement) => this.markupType(statement.values[0], seen));
198
+ const [first] = types;
199
+ if (first === undefined)
200
+ return this.dom.node;
201
+ if (types.every((type) => typesEqual(type, first)))
202
+ return first;
203
+ const { element, node } = this.dom;
204
+ return types.every((type) => isAssignable(type, element)) ? element : node;
205
+ }
206
+ /** The type of the markup a component returns, found without checking it again. */
207
+ markupType(value, seen) {
208
+ if (value?.kind !== 'ElementExpression')
209
+ return this.dom.node;
210
+ if (value.tag === null)
211
+ return this.dom.fragment;
212
+ if (!/^[A-Z]/.test(value.tag.name))
213
+ return this.elementOf(value.tag.name);
214
+ const other = lookupIn(this.moduleScope, value.tag.name)?.component;
215
+ if (!other || seen.has(other))
216
+ return this.dom.node;
217
+ return this.componentResult(other, new Set(seen));
218
+ }
219
+ /**
220
+ * A component may use itself, but some use on the cycle must be under a condition (if, for, a
221
+ * branch of `?:`...), or creating the component never ends.
222
+ */
223
+ checkEndlessRecursion() {
224
+ const components = new Map();
225
+ for (const statement of this.program.body) {
226
+ if (statement.kind !== 'ComponentDeclaration')
227
+ continue;
228
+ const info = this.moduleScope.values.get(statement.name.name)?.component;
229
+ components.set(statement.name.name, info?.expanded ?? statement);
230
+ }
231
+ for (const { cycle, tag } of endlessRecursion(components)) {
232
+ this.error(`endless recursion: ${cycle.join(' → ')} always creates itself again; put the use inside if or for`, tag);
233
+ }
234
+ }
235
+ /** `@html-tag` components: valid and different tags, and properties HTMLElement does not have. */
236
+ checkHtmlTags() {
237
+ const tags = new Map();
238
+ for (const statement of this.program.body) {
239
+ if (statement.kind !== 'ComponentDeclaration' || !statement.htmlTag)
240
+ continue;
241
+ const tag = htmlTagName(statement);
242
+ const given = statement.htmlTag.name;
243
+ const problem = tagNameProblem(tag);
244
+ const other = tags.get(tag);
245
+ if (problem) {
246
+ this.error(given
247
+ ? `"${tag}" is not a valid name of a web component: ${problem}`
248
+ : `the tag of "${statement.name.name}" would be "${tag}", not a valid name of a web component: ${problem}; give one, e.g. @html-tag("app-${tag}")`, given ?? statement.htmlTag);
249
+ }
250
+ else if (other) {
251
+ this.error(`the tag "${tag}" is already used by ${other.name.name}`, given ?? statement.htmlTag);
252
+ }
253
+ tags.set(tag, statement);
254
+ const info = this.moduleScope.values.get(statement.name.name)?.component;
255
+ for (const param of statement.params) {
256
+ const name = param.name.name;
257
+ const type = info?.props.get(name)?.type;
258
+ if (RESERVED_PROPERTIES.has(name)) {
259
+ this.error(`a web component cannot have the property "${name}": HTMLElement has it already`, param.name);
260
+ }
261
+ else if (name !== 'children' && !param.defaultValue && type && !hasZeroValue(type)) {
262
+ // `<app-card>` in HTML has no properties until they are set.
263
+ this.error(`"${name}" needs a default value: the element can be created without it, e.g. in HTML, and ${typeToString(type)} has no zero value`, param.name);
264
+ }
265
+ }
266
+ }
267
+ }
268
+ /** The type of a property of a web component of this module, by its tag and an attribute. */
269
+ webComponentProperty(tag, attribute) {
270
+ for (const statement of this.program.body) {
271
+ if (statement.kind !== 'ComponentDeclaration' || !statement.htmlTag)
272
+ continue;
273
+ if (htmlTagName(statement) !== tag)
274
+ continue;
275
+ const info = this.moduleScope.values.get(statement.name.name)?.component;
276
+ if (!info)
277
+ return null;
278
+ for (const [name, prop] of info.props) {
279
+ if (name !== 'children' && (name === attribute || attributeName(name) === attribute)) {
280
+ return prop.type;
281
+ }
282
+ }
283
+ return null;
284
+ }
285
+ return null;
286
+ }
287
+ }
@@ -0,0 +1,196 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { Wrapper } from '../decorators.ts';
3
+ import type { Diagnostic } from '../diagnostics.ts';
4
+ import type { InlinePackage } from '../inline-packages.ts';
5
+ import type { Root } from '../refs.ts';
6
+ import { type ClassInfo, type Type, type TypeParam } from './types.ts';
7
+ export interface ModuleExports {
8
+ values: Map<string, Type>;
9
+ types: Map<string, Type>;
10
+ /** `export dec`: decorators have a namespace of their own. */
11
+ decorators?: Map<string, DecoratorInfo>;
12
+ /** `export comp`: components are used as tags, not as values. */
13
+ components?: Map<string, ComponentInfo>;
14
+ }
15
+ /** What `importModule` found: the module's exports, an error for the import, or nothing (untyped). */
16
+ export type ImportResult = {
17
+ exports: ModuleExports;
18
+ } | {
19
+ error: string;
20
+ } | undefined;
21
+ /**
22
+ * Types of the JS standard library and the DOM from TypeScript's lib files and global `@types`.
23
+ * They extend the built-in types of builtins.ts and dom.ts, which are used without them (in the
24
+ * browser).
25
+ */
26
+ export interface Library {
27
+ /** A global value, e.g. `document`. */
28
+ value(name: string): Type | undefined;
29
+ /** A global type, e.g. `HTMLElement`. */
30
+ type(name: string): Type | undefined;
31
+ /** `String`, `Number`, `BigInt` or `Boolean`: members that the built-in types do not list. */
32
+ primitive(kind: 'string' | 'number' | 'bigint' | 'bool'): Type | undefined;
33
+ /** `Array<element>`: members that the built-in array type does not list. */
34
+ array(element: Type): Type | undefined;
35
+ /** What `<tag>` creates: `HTMLElementTagNameMap[tag]`, or `SVGElementTagNameMap[tag]` in SVG. */
36
+ element(tag: string, svg?: boolean): Type | undefined;
37
+ /** The event of `on<name>`: `HTMLElementEventMap[name]`, e.g. `MouseEvent` for `click`. */
38
+ event(name: string): Type | undefined;
39
+ /** For completion in editors: the names of the global values, the global types or the tags. */
40
+ names?(of: 'value' | 'type' | 'tag'): Iterable<string>;
41
+ }
42
+ export interface CheckOptions {
43
+ /** Called for imports of `.rk` modules; without it, every import is untyped (`any`). */
44
+ importModule?: (specifier: string) => ImportResult;
45
+ /**
46
+ * Called for the other imports (packages, `node:` modules, `.ts` files): their types come from
47
+ * TypeScript declarations. Without it, or for a module without types, the import is untyped.
48
+ */
49
+ importDeclarations?: (specifier: string) => ImportResult;
50
+ /** Types of the standard library and the DOM beyond the built-in ones. */
51
+ library?: Library;
52
+ /**
53
+ * For completion in editors: the name written where the cursor is. The result then lists what
54
+ * can be written there (see completion.ts).
55
+ */
56
+ completion?: string;
57
+ }
58
+ export interface CheckResult {
59
+ diagnostics: Diagnostic[];
60
+ /** Problems that do not stop compilation, e.g. a CSS selector that matches nothing. */
61
+ warnings: Diagnostic[];
62
+ exports: ModuleExports;
63
+ /** Types of every declaration at the top level of the module, exported or not. */
64
+ declarations: ModuleExports;
65
+ /** Types of checked expressions, for code generation (e.g. how to insert element children). */
66
+ types: WeakMap<ast.Expression, Type>;
67
+ /** With the `completion` option: what can be written at the cursor, if the check reached it. */
68
+ completion?: CompletionResult;
69
+ }
70
+ /** Where the cursor is: a value, a member after a dot, a type, a tag or an attribute of markup. */
71
+ export type CompletionContext = 'value' | 'member' | 'type' | 'tag' | 'attribute';
72
+ export interface CompletionResult {
73
+ context: CompletionContext;
74
+ items: Completion[];
75
+ }
76
+ export interface Completion {
77
+ name: string;
78
+ kind: 'variable' | 'constant' | 'function' | 'class' | 'component' | 'type' | 'property' | 'method' | 'keyword' | 'tag' | 'attribute';
79
+ /** The type, as Rook writes it. */
80
+ detail?: string;
81
+ }
82
+ export type BindingKind = 'let' | 'const' | 'state' | 'derived' | 'ref' | 'param' | 'prop' | 'function' | 'class' | 'import' | 'loop' | 'catch' | 'builtin' | 'component';
83
+ export interface Binding {
84
+ name: string;
85
+ kind: BindingKind;
86
+ /** `null` while a variable is declared but its declaration has not been checked yet. */
87
+ type: Type | null;
88
+ /** For components, which exist only at compile time and are used as tags. */
89
+ component?: ComponentInfo;
90
+ }
91
+ export interface ComponentInfo {
92
+ node: ast.ComponentDeclaration;
93
+ /** Properties by name; optional ones have a default value or a nullable type. */
94
+ props: Map<string, {
95
+ type: Type;
96
+ optional: boolean;
97
+ }>;
98
+ /** Names from the module that the component's code uses, computed when first needed. */
99
+ freeNames: Set<string> | null;
100
+ /**
101
+ * Compiled to a function, not inlined: it uses itself, directly or through other components, it
102
+ * is a web component (`@html-tag`), or other modules use it (`export comp`).
103
+ */
104
+ isFunction: boolean;
105
+ /** The decorators applied to it that exist, in the order they are written. */
106
+ decorators: DecoratorInfo[];
107
+ /** The component with its decorators applied: the code that is inlined where it is used. */
108
+ expanded: ast.ComponentDeclaration;
109
+ /**
110
+ * The type of `<Card />` for an exported component, found in its module: the tags of its markup
111
+ * are names there.
112
+ */
113
+ result: Type | null;
114
+ /**
115
+ * The element at the top of what `<Card />` creates, which `<Card ref={card} />` creates in
116
+ * advance, or why it is not known; found in its module once its body is checked.
117
+ */
118
+ root: Root | null;
119
+ /**
120
+ * For an exported component: what another module needs to inline it instead of calling it, and
121
+ * the types of the expressions of this module, for the code generator there.
122
+ */
123
+ inline: InlinePackage | null;
124
+ expressionTypes: WeakMap<ast.Expression, Type> | null;
125
+ }
126
+ export interface DecoratorInfo {
127
+ node: ast.DecoratorDeclaration;
128
+ /** The types of the parameters, resolved with the other declarations of the module. */
129
+ params: Type[];
130
+ /** Public state, constants and functions, known once the body is checked. */
131
+ members: Map<string, PublicMember>;
132
+ /** The declarations of the body and the parameters: a member that is not public is one of them. */
133
+ names: Set<string>;
134
+ /** Names from the module that the decorator's code uses, computed when first needed. */
135
+ freeNames: Set<string> | null;
136
+ /** `return (content Element) => ...`, with the type of what it gets from the component. */
137
+ wrapper: {
138
+ node: Wrapper;
139
+ param: Type;
140
+ result: Type | null;
141
+ } | null;
142
+ /** The decorators after `needs` that exist; they are applied above it. */
143
+ needed: DecoratorInfo[];
144
+ /**
145
+ * For an exported decorator: the names of its module that its code uses, which other modules get
146
+ * through hidden exports, and the types of its expressions, for the code generator there.
147
+ */
148
+ moduleNames: string[];
149
+ expressionTypes: WeakMap<ast.Expression, Type> | null;
150
+ /** For an exported decorator: the components of its module that its code uses. */
151
+ inline: InlinePackage | null;
152
+ }
153
+ export interface PublicMember {
154
+ kind: 'state' | 'derived' | 'const' | 'func';
155
+ type: Type;
156
+ }
157
+ /** A `type` alias, resolved when first used. */
158
+ export interface AliasEntry {
159
+ kind: 'alias';
160
+ node: ast.TypeAliasDeclaration;
161
+ scope: Scope;
162
+ /** `type Pair<A, B any> ...`: references give arguments for these. */
163
+ params: TypeParam[];
164
+ resolved: Type | null;
165
+ resolving: boolean;
166
+ }
167
+ export declare class Scope {
168
+ readonly parent: Scope | null;
169
+ readonly values: Map<string, Binding>;
170
+ readonly types: Map<string, Type | AliasEntry>;
171
+ constructor(parent: Scope | null);
172
+ }
173
+ export interface FunctionContext {
174
+ /** Declared result types, or `null` when they are inferred from the `return` statements. */
175
+ results: Type[] | null;
176
+ /** Types of the `return` statements, for inferring the results. */
177
+ returns: Type[][];
178
+ isConstructor: boolean;
179
+ /** The body of a component: every `return` gives markup. */
180
+ isComponent?: boolean;
181
+ /** An async function, which may wait: `await`, `<-ch`, `for v in ch`. */
182
+ isAsync?: boolean;
183
+ }
184
+ export interface ClassContext {
185
+ info: ClassInfo;
186
+ isStatic: boolean;
187
+ }
188
+ /**
189
+ * Types of variables narrowed by the code before the current point, e.g. `?User` → `User`
190
+ * after `if user == null { return }`.
191
+ */
192
+ export type Flow = Map<Binding, Type>;
193
+ export type Narrowing = [Binding, Type][];
194
+ export declare const NARROWABLE: ReadonlySet<BindingKind>;
195
+ /** Built-in globals. With a library, the untyped ones and the DOM types come from it instead. */
196
+ export declare function globalScope(library: Library | undefined): Scope;
@@ -0,0 +1,35 @@
1
+ import { GLOBAL_TYPES, GLOBAL_VALUES, LIBRARY_TYPES } from "./builtins.js";
2
+ import {} from "./types.js";
3
+ export class Scope {
4
+ parent;
5
+ values = new Map();
6
+ types = new Map();
7
+ constructor(parent) {
8
+ this.parent = parent;
9
+ }
10
+ }
11
+ export const NARROWABLE = new Set([
12
+ 'let',
13
+ 'const',
14
+ 'state',
15
+ 'derived',
16
+ 'param',
17
+ 'prop',
18
+ 'catch',
19
+ 'loop',
20
+ ]);
21
+ /** Built-in globals. With a library, the untyped ones and the DOM types come from it instead. */
22
+ export function globalScope(library) {
23
+ const scope = new Scope(null);
24
+ for (const [name, type] of GLOBAL_VALUES) {
25
+ if (library && type.kind === 'any')
26
+ continue;
27
+ scope.values.set(name, { name, kind: 'builtin', type });
28
+ }
29
+ for (const [name, type] of GLOBAL_TYPES) {
30
+ if (library && LIBRARY_TYPES.has(name))
31
+ continue;
32
+ scope.types.set(name, type);
33
+ }
34
+ return scope;
35
+ }
@@ -0,0 +1,24 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { type Binding, type Flow, type Narrowing } from './context.ts';
3
+ import { StatementChecker } from './statements.ts';
4
+ export declare abstract class ControlFlowChecker extends StatementChecker {
5
+ protected checkIf(node: ast.IfStatement): void;
6
+ protected checkFor(node: ast.ForStatement): void;
7
+ protected checkSimpleStatement(node: ast.SimpleStatement): void;
8
+ protected checkSwitch(node: ast.SwitchStatement): void;
9
+ /**
10
+ * `switch filter { case "all": ... default: ... }`: in a case the discriminant has the literals
11
+ * of its tests, in `default` the literals of no case.
12
+ */
13
+ protected narrowCase(node: ast.SwitchStatement, switchCase: ast.SwitchCase, entry: Flow): Narrowing;
14
+ protected checkTry(node: ast.TryStatement): void;
15
+ protected checkCondition(node: ast.Expression): void;
16
+ /** Variables whose type is narrowed when `node` evaluates to `assumeTrue`. */
17
+ protected narrow(node: ast.Expression, assumeTrue: boolean): Narrowing;
18
+ /** `x instanceof Date`: an instance of the class when true, the rest of a union when false. */
19
+ protected narrowInstanceof(left: ast.Expression, right: ast.Expression, assumeTrue: boolean): Narrowing;
20
+ protected narrowableBinding(node: ast.Expression): Binding | undefined;
21
+ /** An assignment to a variable ends its narrowing. */
22
+ protected forget(target: ast.Expression): void;
23
+ protected dropNarrowing(names: ReadonlySet<string>): void;
24
+ }