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,48 @@
1
+ import type { Diagnostic } from './diagnostics.ts';
2
+ import { SourceFile } from './source.ts';
3
+ export interface BuildOptions {
4
+ /** Write the `.js` files here, keeping the folder structure; by default they go next to the sources. */
5
+ outDir?: string;
6
+ /** Check types before generating code. Defaults to `true`. */
7
+ typeCheck?: boolean;
8
+ /** Write a `.d.ts` next to each `.js`, for TypeScript code. Defaults to `typeCheck`. */
9
+ declarations?: boolean;
10
+ /** Markup and components for every file; by default as its package.json says (config.ts). */
11
+ markup?: boolean;
12
+ }
13
+ export interface BuildOutput {
14
+ /** Absolute path of the `.rk` file. */
15
+ source: string;
16
+ /** Absolute path of its `.js` file. */
17
+ output: string;
18
+ code: string;
19
+ /** Its `.d.ts` file, when declarations are written. */
20
+ declarations?: {
21
+ output: string;
22
+ code: string;
23
+ };
24
+ /** Its worker module, `.worker.js`, when it has `go`. */
25
+ worker?: {
26
+ output: string;
27
+ code: string;
28
+ };
29
+ }
30
+ /** The errors or the warnings of a file. */
31
+ export interface BuildError {
32
+ file: SourceFile;
33
+ diagnostics: Diagnostic[];
34
+ }
35
+ export interface BuildResult {
36
+ /** Empty when there are errors: either every file is built or none. */
37
+ outputs: BuildOutput[];
38
+ errors: BuildError[];
39
+ /** Warnings do not stop the build. */
40
+ warnings: BuildError[];
41
+ }
42
+ /**
43
+ * Compiles `.rk` files and directories (recursively), together with every `.rk` file they
44
+ * import, since the generated imports refer to the `.js` files of those modules.
45
+ */
46
+ export declare function build(entries: readonly string[], options?: BuildOptions): BuildResult;
47
+ /** A path for messages: relative to the current directory when it is inside it. */
48
+ export declare function displayPath(path: string): string;
package/dist/build.js ADDED
@@ -0,0 +1,112 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import { compile } from "./index.js";
4
+ import { SourceFile } from "./source.js";
5
+ /**
6
+ * Compiles `.rk` files and directories (recursively), together with every `.rk` file they
7
+ * import, since the generated imports refer to the `.js` files of those modules.
8
+ */
9
+ export function build(entries, options = {}) {
10
+ const directories = entries
11
+ .map((entry) => resolve(entry))
12
+ .filter((path) => statSync(path).isDirectory());
13
+ const queue = entries.flatMap((entry) => {
14
+ const path = resolve(entry);
15
+ return directories.includes(path) ? findRookFiles(path) : [path];
16
+ });
17
+ const seen = new Set(queue);
18
+ const typeCheck = options.typeCheck ?? true;
19
+ const declarations = typeCheck && (options.declarations ?? true);
20
+ const compiled = [];
21
+ const errors = [];
22
+ const warnings = [];
23
+ for (let i = 0; i < queue.length; i++) {
24
+ const source = queue[i];
25
+ const text = readFileSync(source, 'utf8');
26
+ const result = compile(text, {
27
+ filename: source,
28
+ typeCheck,
29
+ declarations,
30
+ markup: options.markup,
31
+ });
32
+ if (result.warnings.length > 0) {
33
+ warnings.push({
34
+ file: new SourceFile(displayPath(source), text),
35
+ diagnostics: result.warnings,
36
+ });
37
+ }
38
+ if (result.diagnostics.length > 0) {
39
+ errors.push({
40
+ file: new SourceFile(displayPath(source), text),
41
+ diagnostics: result.diagnostics,
42
+ });
43
+ }
44
+ else {
45
+ compiled.push({
46
+ source,
47
+ code: result.code,
48
+ declarations: result.declarations,
49
+ worker: result.worker,
50
+ });
51
+ }
52
+ for (const specifier of result.dependencies) {
53
+ // A missing module is reported by the type checker.
54
+ const dependency = resolve(dirname(source), specifier);
55
+ if (dependency.endsWith('.rk') && !seen.has(dependency) && existsSync(dependency)) {
56
+ seen.add(dependency);
57
+ queue.push(dependency);
58
+ }
59
+ }
60
+ }
61
+ if (errors.length > 0)
62
+ return { outputs: [], errors, warnings };
63
+ // The output keeps the folder structure below the given directories and all the files.
64
+ const root = commonDirectory(queue, directories);
65
+ const outputs = compiled.map(({ source, code, declarations: types, worker }) => {
66
+ const target = options.outDir ? join(resolve(options.outDir), relative(root, source)) : source;
67
+ const base = target.replace(/\.rk$/, '');
68
+ return {
69
+ source,
70
+ output: `${base}.js`,
71
+ code,
72
+ ...(types === undefined ? {} : { declarations: { output: `${base}.d.ts`, code: types } }),
73
+ ...(worker === undefined ? {} : { worker: { output: `${base}.worker.js`, code: worker } }),
74
+ };
75
+ });
76
+ return { outputs, errors, warnings };
77
+ }
78
+ /** The `.rk` files in a directory and its subdirectories, except `node_modules` and hidden ones. */
79
+ function findRookFiles(directory) {
80
+ const files = [];
81
+ const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
82
+ for (const entry of entries) {
83
+ const path = join(directory, entry.name);
84
+ if (entry.isDirectory()) {
85
+ if (entry.name !== 'node_modules' && !entry.name.startsWith('.')) {
86
+ files.push(...findRookFiles(path));
87
+ }
88
+ }
89
+ else if (entry.name.endsWith('.rk')) {
90
+ files.push(path);
91
+ }
92
+ }
93
+ return files;
94
+ }
95
+ /** The deepest directory that contains all the files and directories. */
96
+ function commonDirectory(files, directories) {
97
+ const inside = (path, directory) => path.startsWith(directory + sep);
98
+ let directory = directories[0] ?? dirname(files[0] ?? process.cwd());
99
+ while (!files.every((file) => inside(file, directory)) ||
100
+ !directories.every((other) => other === directory || inside(other, directory))) {
101
+ const parent = dirname(directory);
102
+ if (parent === directory)
103
+ break;
104
+ directory = parent;
105
+ }
106
+ return directory;
107
+ }
108
+ /** A path for messages: relative to the current directory when it is inside it. */
109
+ export function displayPath(path) {
110
+ const fromCwd = relative(process.cwd(), path);
111
+ return fromCwd.startsWith('..') ? path : fromCwd;
112
+ }
@@ -0,0 +1,14 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { InitializerChecker } from './initializers.ts';
3
+ import { type Type } from './types.ts';
4
+ export declare abstract class AssignmentChecker extends InitializerChecker {
5
+ /**
6
+ * `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
7
+ * types.
8
+ */
9
+ protected unpack(value: ast.Expression, count: number, declared: Type | null, context?: 'assignment' | 'return'): Type[];
10
+ protected checkAssignment(node: ast.AssignmentStatement): void;
11
+ /** The type a value assigned to `target` must have. */
12
+ protected checkTarget(target: ast.Expression): Type;
13
+ protected checkReturn(node: ast.ReturnStatement): void;
14
+ }
@@ -0,0 +1,197 @@
1
+ import { isUntyped, countValues, callName, isCall } from "./helpers.js";
2
+ import { InitializerChecker } from "./initializers.js";
3
+ import { BOOL, ANY, isAssignable, isNullable, nonNull, typeToString, UNKNOWN, } from "./types.js";
4
+ // Where values go: assignments to variables, fields and elements, and `return`. Both unpack a call
5
+ // that returns several values.
6
+ export class AssignmentChecker extends InitializerChecker {
7
+ /**
8
+ * `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
9
+ * types.
10
+ */
11
+ unpack(value, count, declared, context = 'assignment') {
12
+ const type = this.checkExpression(value, null);
13
+ if (type.kind === 'unknown')
14
+ return Array(count).fill(UNKNOWN);
15
+ // `v, ok = <-ch`: ok is false once the channel is closed and empty.
16
+ if (count === 2 && value.kind === 'UnaryExpression' && value.operator === '<-') {
17
+ if (declared)
18
+ this.expectAssignable(type, declared, value);
19
+ return declared ? [declared, BOOL] : [type, BOOL];
20
+ }
21
+ const returned = type.kind === 'tuple' ? type.types.length : type.kind === 'void' ? 0 : 1;
22
+ if (type.kind !== 'tuple' || returned !== count) {
23
+ const prefix = context === 'return'
24
+ ? `wrong number of return values: expected ${count}, but`
25
+ : `assignment mismatch: ${count} variables but`;
26
+ this.error(`${prefix} ${callName(value)} returns ${countValues(returned)}`, value);
27
+ return Array(count).fill(UNKNOWN);
28
+ }
29
+ if (declared) {
30
+ for (const element of type.types)
31
+ this.expectAssignable(element, declared, value);
32
+ return type.types.map(() => declared);
33
+ }
34
+ return type.types;
35
+ }
36
+ checkAssignment(node) {
37
+ const { targets, values, operator } = node;
38
+ if (operator !== '=') {
39
+ const target = targets[0];
40
+ const targetType = this.checkTarget(target);
41
+ const valueType = this.checkValue(values[0], targetType);
42
+ const result = this.binaryResult(operator.slice(0, -1), targetType, valueType, node);
43
+ this.expectAssignable(result, targetType, values[0]);
44
+ this.forget(target);
45
+ return;
46
+ }
47
+ const targetTypes = targets.map((target) => this.checkTarget(target));
48
+ let valueTypes;
49
+ if (values.length === targets.length) {
50
+ valueTypes = values.map((value, i) => {
51
+ const type = this.checkValue(value, targetTypes[i] ?? null);
52
+ this.expectAssignable(type, targetTypes[i], value);
53
+ return type;
54
+ });
55
+ }
56
+ else {
57
+ valueTypes = this.unpack(values[0], targets.length, null);
58
+ valueTypes.forEach((type, i) => this.expectAssignable(type, targetTypes[i], values[0]));
59
+ }
60
+ targets.forEach((target, i) => {
61
+ this.forget(target);
62
+ const binding = this.narrowableBinding(target);
63
+ const declared = binding?.type;
64
+ if (binding && declared && isNullable(declared) && !isNullable(valueTypes[i] ?? UNKNOWN)) {
65
+ this.flow.set(binding, nonNull(declared));
66
+ }
67
+ });
68
+ }
69
+ /** The type a value assigned to `target` must have. */
70
+ checkTarget(target) {
71
+ if (this.checkDecoratorWrite(target))
72
+ return UNKNOWN;
73
+ switch (target.kind) {
74
+ case 'Identifier': {
75
+ if (target.name === '_')
76
+ return ANY;
77
+ const binding = this.lookupValue(target.name);
78
+ if (!binding) {
79
+ this.error(`"${target.name}" is not defined`, target);
80
+ return UNKNOWN;
81
+ }
82
+ if (binding.kind === 'const') {
83
+ this.error(`cannot assign to "${target.name}": it is a constant`, target);
84
+ }
85
+ else if (binding.kind === 'derived') {
86
+ this.error(`cannot assign to "${target.name}": it is derived, so change what it is computed from`, target);
87
+ }
88
+ else if (binding.kind === 'ref') {
89
+ this.error(`cannot assign to "${target.name}": a ref always points to its element`, target);
90
+ }
91
+ else if (binding.kind === 'loop') {
92
+ this.error(`cannot assign to loop variable "${target.name}"`, target);
93
+ }
94
+ else if (binding.kind === 'prop') {
95
+ this.error(this.decorator
96
+ ? `cannot assign to "${target.name}": parameters of a decorator are read-only`
97
+ : `cannot assign to "${target.name}": component properties are read-only; to change the parent's state, pass a function`, target);
98
+ }
99
+ else if (binding.kind !== 'let' &&
100
+ binding.kind !== 'state' &&
101
+ binding.kind !== 'param' &&
102
+ binding.kind !== 'catch') {
103
+ this.error(`cannot assign to "${target.name}"`, target);
104
+ }
105
+ return binding.type ?? UNKNOWN;
106
+ }
107
+ case 'MemberExpression': {
108
+ const object = this.nonNullValue(target.object);
109
+ const member = this.findMember(object, target.property.name, target.property);
110
+ if (member === 'any')
111
+ return ANY;
112
+ if (!member)
113
+ return UNKNOWN;
114
+ if (member.method) {
115
+ this.error(`cannot assign to method "${target.property.name}"`, target);
116
+ return UNKNOWN;
117
+ }
118
+ return member.type;
119
+ }
120
+ case 'IndexExpression': {
121
+ const object = this.nonNullValue(target.object);
122
+ const keyed = this.keyedIndex(object, target.index);
123
+ if (keyed)
124
+ return keyed;
125
+ this.expectIndex(target.index);
126
+ if (object.kind === 'array')
127
+ return object.element;
128
+ if (object.kind === 'object' && object.numberIndex)
129
+ return object.numberIndex;
130
+ this.error(object.kind === 'string'
131
+ ? 'cannot assign to a character: strings cannot be changed'
132
+ : `cannot index ${typeToString(object)}`, target);
133
+ return UNKNOWN;
134
+ }
135
+ default:
136
+ this.checkValue(target);
137
+ return UNKNOWN;
138
+ }
139
+ }
140
+ checkReturn(node) {
141
+ const fn = this.fn;
142
+ if (!fn)
143
+ return;
144
+ const { values } = node;
145
+ if (fn.isComponent) {
146
+ // A component may return early; whatever it returns is its markup.
147
+ const [value] = values;
148
+ if (value === undefined || values.length > 1) {
149
+ this.error('a component returns its markup: "return <markup>"', node);
150
+ return;
151
+ }
152
+ const type = this.checkValue(value);
153
+ if (!isUntyped(type) && !isAssignable(type, this.dom.node)) {
154
+ this.error(`a component returns markup, not ${typeToString(type)}`, value);
155
+ }
156
+ return;
157
+ }
158
+ if (fn.results === null) {
159
+ // Results are inferred: just record what is returned.
160
+ if (values.length === 1) {
161
+ const type = this.checkExpression(values[0], null);
162
+ fn.returns.push(type.kind === 'tuple' ? type.types : type.kind === 'void' ? [] : [type]);
163
+ }
164
+ else {
165
+ fn.returns.push(values.map((value) => this.checkValue(value)));
166
+ }
167
+ return;
168
+ }
169
+ const expected = fn.results;
170
+ if (values.length === 0) {
171
+ if (expected.length > 0) {
172
+ this.error(`missing return values: expected ${countValues(expected.length)}`, node);
173
+ }
174
+ return;
175
+ }
176
+ if (expected.length === 0) {
177
+ this.error('too many return values: this function returns nothing', values[0]);
178
+ for (const value of values)
179
+ this.checkExpression(value, null);
180
+ return;
181
+ }
182
+ if (values.length === 1 && expected.length > 1 && isCall(values[0])) {
183
+ const types = this.unpack(values[0], expected.length, null, 'return');
184
+ types.forEach((type, i) => this.expectAssignable(type, expected[i], values[0], ' in return'));
185
+ return;
186
+ }
187
+ if (values.length !== expected.length) {
188
+ this.error(`wrong number of return values: expected ${expected.length}, got ${values.length}`, node);
189
+ }
190
+ values.forEach((value, i) => {
191
+ const target = expected[i];
192
+ const type = this.checkValue(value, target ?? null);
193
+ if (target)
194
+ this.expectAssignable(type, target, value, ' in return');
195
+ });
196
+ }
197
+ }
@@ -0,0 +1,124 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { Diagnostic } from '../diagnostics.ts';
3
+ import { type CheckOptions, type BindingKind, type Binding, type ComponentInfo, type DecoratorInfo, type AliasEntry, Scope, type FunctionContext, type ClassContext, type Flow } from './context.ts';
4
+ import { type ClassInfo, type FunctionType, type Member, type Type, type TypeParam } from './types.ts';
5
+ export declare abstract class CheckerBase {
6
+ protected abstract checkClassBodies(node: ast.ClassDeclaration, info: ClassInfo): void;
7
+ protected abstract ensureClassResolved(info: ClassInfo): void;
8
+ protected abstract resolveClass(node: ast.ClassDeclaration, info: ClassInfo): void;
9
+ protected abstract inferredType(type: Type, node: ast.Expression): Type;
10
+ protected abstract resolveAlias(entry: AliasEntry): Type;
11
+ protected abstract resolveType(node: ast.TypeNode): Type;
12
+ protected abstract resolveTypeName(name: ast.Identifier, typeArgs?: readonly ast.TypeNode[]): Type;
13
+ protected abstract checkStatement(node: ast.Statement): void;
14
+ protected abstract checkFor(node: ast.ForStatement): void;
15
+ protected abstract checkForIn(node: ast.ForInStatement): void;
16
+ protected abstract checkIf(node: ast.IfStatement): void;
17
+ protected abstract checkSwitch(node: ast.SwitchStatement): void;
18
+ protected abstract checkTry(node: ast.TryStatement): void;
19
+ protected abstract forget(target: ast.Expression): void;
20
+ protected abstract narrowableBinding(node: ast.Expression): Binding | undefined;
21
+ protected abstract checkFunction(params: readonly ast.Parameter[], type: FunctionType, results: Type[] | null, body: ast.BlockStatement | ast.Expression, options?: {
22
+ isConstructor?: boolean;
23
+ isComponent?: boolean;
24
+ closure?: boolean;
25
+ paramKind?: BindingKind;
26
+ /** An async function: its body returns what the Promise of `results` gives. */
27
+ async?: boolean;
28
+ /** Sees the scope of the body once it is checked. */
29
+ inspect?: (scope: Scope) => void;
30
+ }): Type[];
31
+ protected abstract checkExpression(node: ast.Expression, expected: Type | null): Type;
32
+ protected abstract checkValue(node: ast.Expression, expected?: Type | null): Type;
33
+ protected abstract binaryResult(operator: ast.BinaryOperator, givenLeft: Type, givenRight: Type, node: ast.NodeBase): Type;
34
+ protected abstract checkBinary(node: ast.BinaryExpression, expected: Type | null): Type;
35
+ protected abstract checkConditional(node: ast.ConditionalExpression, expected: Type | null): Type;
36
+ protected abstract checkUnary(node: ast.UnaryExpression): Type;
37
+ protected abstract checkElement(node: ast.ElementExpression): Type;
38
+ protected abstract checkComponentBody(info: ComponentInfo): void;
39
+ protected abstract checkDecoratorBody(info: DecoratorInfo): void;
40
+ protected abstract resolveDecoratorParams(info: DecoratorInfo): void;
41
+ protected abstract checkDecoratorUses(info: ComponentInfo): void;
42
+ protected abstract orderByNeeds(decorators: readonly DecoratorInfo[]): DecoratorInfo[];
43
+ protected abstract wrappedType(info: ComponentInfo, own: Type, seen: Set<ComponentInfo>): Type;
44
+ protected abstract checkDecoratorMember(node: ast.DecoratorMember): Type;
45
+ protected abstract checkDecoratorGet(node: ast.DecoratorGet): Type;
46
+ protected abstract checkDecoratorWrite(target: ast.Expression): boolean;
47
+ protected abstract checkComponentUse(node: ast.ElementExpression, tag: ast.Identifier): Type;
48
+ protected abstract checkRefDeclaration(node: ast.VariableDeclaration): void;
49
+ protected abstract checkStyle(node: ast.StyleStatement): void;
50
+ protected abstract checkRefAttribute(attribute: ast.JsxAttribute, node: ast.ElementExpression): void;
51
+ protected abstract resolveProps(info: ComponentInfo): void;
52
+ protected abstract webComponentProperty(tag: string, attribute: string): Type | null;
53
+ protected abstract checkProp(attribute: ast.JsxAttribute, type: Type, component: string): void;
54
+ protected abstract checkChain(node: ast.MemberExpression | ast.IndexExpression | ast.CallExpression, expected?: Type | null): [Type, boolean];
55
+ protected abstract expectIndex(node: ast.Expression): void;
56
+ protected abstract findMember(object: Type, name: string, node: ast.NodeBase): Member | 'any' | undefined;
57
+ protected abstract keyedIndex(object: Type, index: ast.Expression): Type | null;
58
+ protected abstract nonNullValue(node: ast.Expression): Type;
59
+ protected abstract checkCall(node: ast.CallExpression, callee: Type, expected?: Type | null): Type;
60
+ protected abstract checkNew(node: ast.NewExpression, expected?: Type | null): Type;
61
+ protected abstract checkSuperCall(node: ast.CallExpression): Type;
62
+ protected readonly program: ast.Program;
63
+ protected readonly options: CheckOptions;
64
+ protected readonly diagnostics: Diagnostic[];
65
+ protected readonly moduleScope: Scope;
66
+ protected scope: Scope;
67
+ protected flow: Flow;
68
+ protected fn: FunctionContext | null;
69
+ protected cls: ClassContext | null;
70
+ /**
71
+ * Type parameters of the call whose arguments are being checked: they are not known yet. Other
72
+ * type parameters, like `T` in the body of `func first<T any>`, are types like any other.
73
+ */
74
+ protected inferring: ReadonlySet<TypeParam>;
75
+ /** Names assigned somewhere in the module: their narrowing does not carry into closures. */
76
+ protected readonly assigned: Set<string>;
77
+ /** Classes whose members are not resolved yet, with the code that resolves them. */
78
+ protected readonly unresolvedClasses: Map<ClassInfo, () => void>;
79
+ protected readonly resolvingClasses: Set<ClassInfo>;
80
+ /** The component whose body is being checked, if any. */
81
+ protected component: ComponentInfo | null;
82
+ /** The decorator whose body is being checked, if any. */
83
+ protected decorator: DecoratorInfo | null;
84
+ /** Decorators of the module by name; they are not values, so they have a namespace of their own. */
85
+ protected readonly decorators: Map<string, DecoratorInfo>;
86
+ /** Decorators imported from other modules: their code uses the names of those. */
87
+ protected readonly importedDecorators: Set<DecoratorInfo>;
88
+ protected readonly globals: Scope;
89
+ protected domTypes: {
90
+ node: Type;
91
+ element: Type;
92
+ fragment: Type;
93
+ } | null;
94
+ constructor(program: ast.Program, options: CheckOptions);
95
+ protected error(message: string, node: ast.NodeBase): void;
96
+ protected expectAssignable(source: Type, target: Type, node: ast.NodeBase, context?: string): void;
97
+ protected nullError(node: ast.Expression): void;
98
+ protected withScope<T>(check: () => T): T;
99
+ protected withClass<T>(context: ClassContext | null, check: () => T): T;
100
+ protected lookupValue(name: string): Binding | undefined;
101
+ protected lookupType(name: string): Type | AliasEntry | undefined;
102
+ /**
103
+ * Node, HTMLElement and DocumentFragment: from the library when there is one. Found on first
104
+ * use, after the imports, which may change the library.
105
+ */
106
+ protected get dom(): {
107
+ node: Type;
108
+ element: Type;
109
+ fragment: Type;
110
+ };
111
+ /** The `event` of an `on*` attribute: its `currentTarget` is the element. */
112
+ protected eventOf(attribute: string, element: Type): Type;
113
+ /** A member of a string, number, bigint or bool that the built-in types do not list. */
114
+ protected primitiveMember(kind: 'string' | 'number' | 'bigint' | 'bool', name: string): Member | undefined;
115
+ protected arrayMemberOf(element: Type, name: string): Member | undefined;
116
+ protected declareValue(id: ast.Identifier, kind: BindingKind, type: Type | null): Binding;
117
+ protected declareType(id: ast.Identifier, entry: Type | AliasEntry): void;
118
+ /**
119
+ * Remembers the types of checked expressions that may narrow a variable, e.g.
120
+ * `let u ?User = new User()` makes `u` non-null until it is assigned again.
121
+ */
122
+ protected readonly checkedTypes: WeakMap<ast.Expression, Type>;
123
+ protected typeOfChecked(node: ast.Expression): Type;
124
+ }
@@ -0,0 +1,180 @@
1
+ import { assignedNames } from "../names.js";
2
+ import { arrayMember } from "./builtins.js";
3
+ import { Scope, globalScope, } from "./context.js";
4
+ import { DOCUMENT_FRAGMENT, eventType, HTML_ELEMENT, NODE } from "./dom.js";
5
+ import { explainMismatch, isAssignable, LazyMap, memberNames, nonNull, property, typeToString, UNKNOWN, } from "./types.js";
6
+ // The type checker is a chain of layers, one per area, like the code generator: base → imports →
7
+ // declarations → classes → resolution → initializers → assignments → statements → control flow →
8
+ // functions → expressions → operators → markup → forwarding → components → component values →
9
+ // refs → styles → decorator wrappers → decorator exports → decorators → decorator members →
10
+ // members → calls → goroutines → channels → bytes → loops → go for → iterators → completion →
11
+ // Checker (checker.ts). Statements, expressions and markup check each other recursively, so
12
+ // methods of later layers that earlier ones call are declared here as abstract. This layer has the
13
+ // state of a check, diagnostics, scopes and names, and the DOM types.
14
+ export class CheckerBase {
15
+ program;
16
+ options;
17
+ diagnostics = [];
18
+ moduleScope;
19
+ scope;
20
+ flow = new Map();
21
+ fn = null;
22
+ cls = null;
23
+ /**
24
+ * Type parameters of the call whose arguments are being checked: they are not known yet. Other
25
+ * type parameters, like `T` in the body of `func first<T any>`, are types like any other.
26
+ */
27
+ inferring = new Set();
28
+ /** Names assigned somewhere in the module: their narrowing does not carry into closures. */
29
+ assigned;
30
+ /** Classes whose members are not resolved yet, with the code that resolves them. */
31
+ unresolvedClasses = new Map();
32
+ resolvingClasses = new Set();
33
+ /** The component whose body is being checked, if any. */
34
+ component = null;
35
+ /** The decorator whose body is being checked, if any. */
36
+ decorator = null;
37
+ /** Decorators of the module by name; they are not values, so they have a namespace of their own. */
38
+ decorators = new Map();
39
+ /** Decorators imported from other modules: their code uses the names of those. */
40
+ importedDecorators = new Set();
41
+ globals;
42
+ domTypes = null;
43
+ constructor(program, options) {
44
+ this.program = program;
45
+ this.options = options;
46
+ this.globals = globalScope(options.library);
47
+ this.moduleScope = new Scope(this.globals);
48
+ this.scope = this.moduleScope;
49
+ this.assigned = assignedNames(program);
50
+ }
51
+ error(message, node) {
52
+ if (this.diagnostics.some((d) => d.start === node.start))
53
+ return;
54
+ this.diagnostics.push({ message, start: node.start, end: node.end });
55
+ }
56
+ expectAssignable(source, target, node, context = '') {
57
+ if (isAssignable(source, target))
58
+ return;
59
+ let message = `cannot use ${typeToString(source)} as ${typeToString(target)}${context}`;
60
+ const reason = explainMismatch(nonNull(source), nonNull(target));
61
+ if (source.kind === 'nullable' && isAssignable(nonNull(source), target)) {
62
+ message += ': it may be null, check it first';
63
+ }
64
+ else if (reason !== null) {
65
+ message += `: ${reason}`;
66
+ }
67
+ this.error(message, node);
68
+ }
69
+ nullError(node) {
70
+ const name = node.kind === 'Identifier' ? node.name : null;
71
+ this.error(name !== null
72
+ ? `"${name}" may be null: check it with "if ${name} != null" or use "?."`
73
+ : 'this value may be null: check it for null or use "?."', node);
74
+ }
75
+ withScope(check) {
76
+ const saved = this.scope;
77
+ this.scope = new Scope(saved);
78
+ try {
79
+ return check();
80
+ }
81
+ finally {
82
+ this.scope = saved;
83
+ }
84
+ }
85
+ withClass(context, check) {
86
+ const saved = this.cls;
87
+ this.cls = context;
88
+ try {
89
+ return check();
90
+ }
91
+ finally {
92
+ this.cls = saved;
93
+ }
94
+ }
95
+ lookupValue(name) {
96
+ for (let scope = this.scope; scope; scope = scope.parent) {
97
+ const binding = scope.values.get(name);
98
+ if (binding)
99
+ return binding;
100
+ }
101
+ const type = this.options.library?.value(name);
102
+ if (type === undefined)
103
+ return undefined;
104
+ const binding = { name, kind: 'builtin', type };
105
+ this.globals.values.set(name, binding);
106
+ return binding;
107
+ }
108
+ lookupType(name) {
109
+ for (let scope = this.scope; scope; scope = scope.parent) {
110
+ const entry = scope.types.get(name);
111
+ if (entry)
112
+ return entry;
113
+ }
114
+ const type = this.options.library?.type(name);
115
+ if (type !== undefined)
116
+ this.globals.types.set(name, type);
117
+ return type;
118
+ }
119
+ /**
120
+ * Node, HTMLElement and DocumentFragment: from the library when there is one. Found on first
121
+ * use, after the imports, which may change the library.
122
+ */
123
+ get dom() {
124
+ if (!this.domTypes) {
125
+ const { library } = this.options;
126
+ this.domTypes = {
127
+ node: library?.type('Node') ?? NODE,
128
+ element: library?.type('HTMLElement') ?? HTML_ELEMENT,
129
+ fragment: library?.type('DocumentFragment') ?? DOCUMENT_FRAGMENT,
130
+ };
131
+ }
132
+ return this.domTypes;
133
+ }
134
+ /** The `event` of an `on*` attribute: its `currentTarget` is the element. */
135
+ eventOf(attribute, element) {
136
+ const { library } = this.options;
137
+ if (!library)
138
+ return eventType(element);
139
+ const event = library.event(attribute.slice(2).toLowerCase()) ?? library.type('Event');
140
+ if (event?.kind !== 'object')
141
+ return eventType(element);
142
+ const members = new LazyMap(() => memberNames(event.members), (name) => (name === 'currentTarget' ? property(element) : event.members.get(name)));
143
+ return { ...event, members };
144
+ }
145
+ /** A member of a string, number, bigint or bool that the built-in types do not list. */
146
+ primitiveMember(kind, name) {
147
+ const type = this.options.library?.primitive(kind);
148
+ return type?.kind === 'object' ? type.members.get(name) : undefined;
149
+ }
150
+ arrayMemberOf(element, name) {
151
+ const builtin = arrayMember(element, name);
152
+ if (builtin)
153
+ return builtin;
154
+ const type = this.options.library?.array(element);
155
+ return type?.kind === 'object' ? type.members.get(name) : undefined;
156
+ }
157
+ declareValue(id, kind, type) {
158
+ const binding = { name: id.name, kind, type };
159
+ if (id.name === '_')
160
+ return binding;
161
+ if (this.scope.values.has(id.name)) {
162
+ this.error(`"${id.name}" is already declared in this scope`, id);
163
+ }
164
+ this.scope.values.set(id.name, binding);
165
+ return binding;
166
+ }
167
+ declareType(id, entry) {
168
+ if (this.scope.types.has(id.name))
169
+ this.error(`type "${id.name}" is already declared`, id);
170
+ this.scope.types.set(id.name, entry);
171
+ }
172
+ /**
173
+ * Remembers the types of checked expressions that may narrow a variable, e.g.
174
+ * `let u ?User = new User()` makes `u` non-null until it is assigned again.
175
+ */
176
+ checkedTypes = new WeakMap();
177
+ typeOfChecked(node) {
178
+ return this.checkedTypes.get(node) ?? UNKNOWN;
179
+ }
180
+ }