rooklang 0.3.3 → 0.4.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 (244) hide show
  1. package/README.md +34 -7
  2. package/README.ru.md +34 -7
  3. package/dist/ast/declarations.d.ts +39 -4
  4. package/dist/ast/expressions.d.ts +34 -8
  5. package/dist/ast/markup.d.ts +6 -1
  6. package/dist/ast/statements.d.ts +28 -2
  7. package/dist/auto-import.d.ts +11 -0
  8. package/dist/auto-import.js +81 -0
  9. package/dist/breaks.d.ts +6 -0
  10. package/dist/breaks.js +36 -0
  11. package/dist/build.d.ts +7 -0
  12. package/dist/build.js +23 -2
  13. package/dist/checker/array-literals.d.ts +6 -0
  14. package/dist/checker/array-literals.js +59 -0
  15. package/dist/checker/assignments.d.ts +4 -0
  16. package/dist/checker/assignments.js +23 -3
  17. package/dist/checker/base.d.ts +8 -2
  18. package/dist/checker/base.js +7 -7
  19. package/dist/checker/builtins.js +22 -8
  20. package/dist/checker/bytes.d.ts +2 -2
  21. package/dist/checker/bytes.js +2 -2
  22. package/dist/checker/calls.js +3 -0
  23. package/dist/checker/channel-objects.d.ts +3 -0
  24. package/dist/checker/channel-objects.js +40 -0
  25. package/dist/checker/channels.d.ts +1 -0
  26. package/dist/checker/channels.js +17 -4
  27. package/dist/checker/checker.d.ts +1 -1
  28. package/dist/checker/checker.js +19 -0
  29. package/dist/checker/class-members.d.ts +22 -0
  30. package/dist/checker/class-members.js +105 -0
  31. package/dist/checker/classes.d.ts +5 -4
  32. package/dist/checker/classes.js +48 -13
  33. package/dist/checker/closing.d.ts +21 -0
  34. package/dist/checker/closing.js +164 -0
  35. package/dist/checker/completion.d.ts +3 -3
  36. package/dist/checker/completion.js +4 -3
  37. package/dist/checker/component-bindings.d.ts +7 -0
  38. package/dist/checker/component-bindings.js +45 -0
  39. package/dist/checker/component-values.js +10 -7
  40. package/dist/checker/components.d.ts +2 -2
  41. package/dist/checker/components.js +8 -3
  42. package/dist/checker/context.d.ts +41 -5
  43. package/dist/checker/context.js +23 -0
  44. package/dist/checker/control-flow.d.ts +11 -5
  45. package/dist/checker/control-flow.js +65 -40
  46. package/dist/checker/declarations.d.ts +2 -2
  47. package/dist/checker/declarations.js +4 -2
  48. package/dist/checker/decode.d.ts +8 -0
  49. package/dist/checker/decode.js +32 -0
  50. package/dist/checker/decorator-exports.js +4 -0
  51. package/dist/checker/decorator-includes.d.ts +9 -0
  52. package/dist/checker/decorator-includes.js +71 -0
  53. package/dist/checker/decorator-members.d.ts +8 -10
  54. package/dist/checker/decorator-members.js +28 -31
  55. package/dist/checker/decorator-wrappers.d.ts +1 -1
  56. package/dist/checker/decorator-wrappers.js +3 -2
  57. package/dist/checker/decorators.d.ts +14 -5
  58. package/dist/checker/decorators.js +52 -25
  59. package/dist/checker/expressions.d.ts +6 -2
  60. package/dist/checker/expressions.js +20 -40
  61. package/dist/checker/flow.d.ts +25 -3
  62. package/dist/checker/flow.js +56 -12
  63. package/dist/checker/functions.d.ts +2 -2
  64. package/dist/checker/functions.js +9 -5
  65. package/dist/checker/helpers.d.ts +5 -2
  66. package/dist/checker/helpers.js +16 -4
  67. package/dist/checker/imports.js +5 -2
  68. package/dist/checker/iterables.d.ts +3 -1
  69. package/dist/checker/iterables.js +13 -0
  70. package/dist/checker/iterators.d.ts +4 -1
  71. package/dist/checker/iterators.js +12 -4
  72. package/dist/checker/json.d.ts +5 -0
  73. package/dist/checker/json.js +92 -0
  74. package/dist/checker/loops.d.ts +2 -0
  75. package/dist/checker/loops.js +50 -5
  76. package/dist/checker/markup.js +14 -2
  77. package/dist/checker/members.js +10 -1
  78. package/dist/checker/operators.d.ts +2 -2
  79. package/dist/checker/operators.js +7 -5
  80. package/dist/checker/queues.d.ts +2 -2
  81. package/dist/checker/queues.js +2 -2
  82. package/dist/checker/resolution.js +5 -3
  83. package/dist/checker/shared-state.d.ts +11 -0
  84. package/dist/checker/shared-state.js +37 -0
  85. package/dist/checker/signals.d.ts +8 -0
  86. package/dist/checker/signals.js +48 -0
  87. package/dist/checker/statements.d.ts +5 -2
  88. package/dist/checker/statements.js +51 -22
  89. package/dist/checker/tagged-unions.d.ts +37 -0
  90. package/dist/checker/tagged-unions.js +132 -0
  91. package/dist/checker/topics.d.ts +6 -0
  92. package/dist/checker/topics.js +17 -0
  93. package/dist/checker/transfer.d.ts +3 -1
  94. package/dist/checker/transfer.js +25 -5
  95. package/dist/checker/try.d.ts +6 -0
  96. package/dist/checker/try.js +19 -0
  97. package/dist/checker/types/constructors.d.ts +1 -1
  98. package/dist/checker/types/constructors.js +2 -2
  99. package/dist/checker/types/generics.js +1 -1
  100. package/dist/checker/types/iterables.d.ts +13 -0
  101. package/dist/checker/types/iterables.js +52 -0
  102. package/dist/checker/types/model.d.ts +25 -0
  103. package/dist/checker/types/params.js +22 -3
  104. package/dist/checker/types/printing.js +4 -3
  105. package/dist/checker/types/relations.js +19 -25
  106. package/dist/checker/types/topic.d.ts +6 -0
  107. package/dist/checker/types/topic.js +23 -0
  108. package/dist/checker/types.d.ts +2 -0
  109. package/dist/checker/types.js +2 -0
  110. package/dist/checker/untyped-globals.d.ts +1 -0
  111. package/dist/checker/untyped-globals.js +3 -1
  112. package/dist/cli.js +18 -36
  113. package/dist/codegen/attributes.d.ts +4 -0
  114. package/dist/codegen/attributes.js +24 -2
  115. package/dist/codegen/calls.d.ts +23 -0
  116. package/dist/codegen/calls.js +88 -7
  117. package/dist/codegen/component-helpers.d.ts +6 -1
  118. package/dist/codegen/component-helpers.js +6 -1
  119. package/dist/codegen/component-props.d.ts +11 -2
  120. package/dist/codegen/component-props.js +50 -5
  121. package/dist/codegen/components.d.ts +6 -5
  122. package/dist/codegen/components.js +44 -21
  123. package/dist/codegen/control-flow.d.ts +2 -0
  124. package/dist/codegen/control-flow.js +23 -9
  125. package/dist/codegen/control-statements.d.ts +10 -2
  126. package/dist/codegen/control-statements.js +56 -19
  127. package/dist/codegen/creation.js +5 -2
  128. package/dist/codegen/decode.d.ts +14 -0
  129. package/dist/codegen/decode.js +93 -0
  130. package/dist/codegen/early-returns.d.ts +4 -3
  131. package/dist/codegen/early-returns.js +17 -5
  132. package/dist/codegen/emitter.d.ts +13 -41
  133. package/dist/codegen/emitter.js +27 -8
  134. package/dist/codegen/expressions.d.ts +9 -0
  135. package/dist/codegen/expressions.js +15 -26
  136. package/dist/codegen/functions.d.ts +11 -1
  137. package/dist/codegen/functions.js +31 -3
  138. package/dist/codegen/js.d.ts +2 -2
  139. package/dist/codegen/js.js +5 -2
  140. package/dist/codegen/live.d.ts +60 -0
  141. package/dist/codegen/live.js +29 -0
  142. package/dist/codegen/mutations.d.ts +7 -0
  143. package/dist/codegen/mutations.js +43 -2
  144. package/dist/codegen/options.d.ts +42 -0
  145. package/dist/codegen/options.js +1 -0
  146. package/dist/codegen/reactive.d.ts +4 -59
  147. package/dist/codegen/reactive.js +34 -72
  148. package/dist/codegen/runtime.d.ts +7 -3
  149. package/dist/codegen/runtime.js +33 -14
  150. package/dist/codegen/shared.d.ts +22 -0
  151. package/dist/codegen/shared.js +93 -0
  152. package/dist/codegen/simple-statements.d.ts +11 -5
  153. package/dist/codegen/simple-statements.js +40 -15
  154. package/dist/codegen/statements.d.ts +5 -0
  155. package/dist/codegen/statements.js +99 -4
  156. package/dist/codegen/try.d.ts +5 -0
  157. package/dist/codegen/try.js +40 -0
  158. package/dist/codegen/updates.d.ts +11 -3
  159. package/dist/codegen/updates.js +52 -4
  160. package/dist/codegen/web-components.d.ts +2 -2
  161. package/dist/codegen/web-components.js +2 -2
  162. package/dist/compile.d.ts +9 -0
  163. package/dist/compile.js +25 -8
  164. package/dist/completion.d.ts +4 -2
  165. package/dist/completion.js +10 -5
  166. package/dist/declarations.js +32 -6
  167. package/dist/decorator-copy.d.ts +1 -1
  168. package/dist/decorator-copy.js +6 -2
  169. package/dist/decorator-names.d.ts +14 -0
  170. package/dist/decorator-names.js +56 -0
  171. package/dist/decorators.d.ts +2 -3
  172. package/dist/decorators.js +36 -14
  173. package/dist/goroutines.d.ts +1 -1
  174. package/dist/goroutines.js +5 -4
  175. package/dist/index.d.ts +6 -2
  176. package/dist/index.js +14 -5
  177. package/dist/inline-packages.d.ts +2 -1
  178. package/dist/inline-packages.js +9 -4
  179. package/dist/lexer/base.d.ts +2 -0
  180. package/dist/lexer/base.js +6 -1
  181. package/dist/lexer/lexer.js +3 -0
  182. package/dist/lexer/literals.d.ts +5 -0
  183. package/dist/lexer/literals.js +51 -1
  184. package/dist/lexer/markup.js +3 -4
  185. package/dist/lexer/tables.js +2 -0
  186. package/dist/lexer/token.d.ts +4 -3
  187. package/dist/lexer/token.js +0 -2
  188. package/dist/loader.js +30 -8
  189. package/dist/names.d.ts +4 -0
  190. package/dist/names.js +29 -0
  191. package/dist/parser/base.d.ts +5 -0
  192. package/dist/parser/base.js +2 -1
  193. package/dist/parser/classes.d.ts +2 -0
  194. package/dist/parser/classes.js +88 -23
  195. package/dist/parser/control-flow.d.ts +8 -1
  196. package/dist/parser/control-flow.js +49 -26
  197. package/dist/parser/declarations.d.ts +5 -12
  198. package/dist/parser/declarations.js +14 -79
  199. package/dist/parser/decorators.d.ts +6 -2
  200. package/dist/parser/decorators.js +43 -19
  201. package/dist/parser/expressions.js +27 -3
  202. package/dist/parser/jumps.d.ts +11 -0
  203. package/dist/parser/jumps.js +77 -0
  204. package/dist/parser/literals.d.ts +9 -0
  205. package/dist/parser/literals.js +45 -6
  206. package/dist/parser/markup.js +1 -1
  207. package/dist/parser/params.d.ts +21 -0
  208. package/dist/parser/params.js +143 -0
  209. package/dist/parser/simple-statements.d.ts +2 -1
  210. package/dist/parser/simple-statements.js +38 -15
  211. package/dist/parser/statements.d.ts +5 -5
  212. package/dist/parser/statements.js +39 -35
  213. package/dist/parser/syntax.d.ts +11 -1
  214. package/dist/parser/syntax.js +16 -2
  215. package/dist/parser/types.js +8 -3
  216. package/dist/project-modules.d.ts +12 -0
  217. package/dist/project-modules.js +106 -0
  218. package/dist/run-file.d.ts +6 -0
  219. package/dist/run-file.js +111 -0
  220. package/dist/runtime/sources.js +13 -6
  221. package/dist/source-map.d.ts +17 -0
  222. package/dist/source-map.js +75 -0
  223. package/dist/ts-types.js +4 -1
  224. package/dist/typescript/convert-objects.d.ts +2 -1
  225. package/dist/typescript/convert-objects.js +18 -9
  226. package/dist/vite.js +12 -4
  227. package/dist/walk-types.js +1 -0
  228. package/dist/walk.d.ts +0 -2
  229. package/dist/walk.js +14 -31
  230. package/package.json +2 -2
  231. package/src/runtime/chan.js +15 -5
  232. package/src/runtime/channel.js +28 -0
  233. package/src/runtime/classes.js +32 -0
  234. package/src/runtime/core.js +10 -1
  235. package/src/runtime/decode.js +221 -0
  236. package/src/runtime/go.js +3 -0
  237. package/src/runtime/index.js +7 -0
  238. package/src/runtime/markup.js +27 -17
  239. package/src/runtime/select.js +14 -9
  240. package/src/runtime/shared.js +17 -0
  241. package/src/runtime/signals.js +59 -0
  242. package/src/runtime/threads.js +32 -5
  243. package/src/runtime/topic.js +118 -0
  244. package/src/runtime/try.js +45 -0
package/README.md CHANGED
@@ -25,14 +25,21 @@ close(jobs)
25
25
  for let i = 0; i < 4; i++ {
26
26
  console.log(<-results)
27
27
  }
28
+ close(results)
28
29
  ```
29
30
 
30
31
  - **Goroutines and channels.** `go f(x)` runs a function as a goroutine, on another processor, and
31
- channels, `select`, `after`, `tick` and `cancel` work as in Go; `go for i, v in pixels { ... }`
32
+ channels, `select`, `after`, `tick` and `cancel` work as in Go, and signals and `AbortSignal`
33
+ are channels too, `signal("SIGTERM")` and `done(request.signal)`; `go for i, v in pixels { ... }`
32
34
  splits an array among the processors. The compiler checks that what goes to another thread can be
33
- copied there.
34
- - **Go syntax.** `func`, the type after the name, several results, errors as a second value,
35
- `defer`, a single `for` loop:
35
+ copied there, and warns of a channel that is never closed; an instance of a `data class` goes
36
+ with its class and methods.
37
+ - **Topics between tabs.** `topic<CartEvent>("cart")` passes values between the tabs of a site
38
+ over a `BroadcastChannel`: `cart <- e` sends and waits for nobody, `for e in cart` receives what
39
+ the other tabs send. `Channel` and `Topic` have what channels and topics compile to, by name, and
40
+ their closed zero values, `Channel.EMPTY` and `Topic.EMPTY`.
41
+ - **Go syntax.** `func`, the type after the name, several results, errors as a second value
42
+ (`try JSON.parse(text)` makes what JS throws one), `defer`, a single `for` loop:
36
43
 
37
44
  ```go
38
45
  func divide(a, b number) (number, error) {
@@ -43,13 +50,16 @@ for let i = 0; i < 4; i++ {
43
50
  }
44
51
  ```
45
52
 
46
- - **Static types with null safety.** A `?User` cannot be used without a check.
53
+ - **Static types with null safety.** A `?User` cannot be used without a check, and
54
+ `decode<User>(body)` checks JSON from outside against the type when the program runs.
47
55
  - **JavaScript inside.** The same values, classes and modules, any JS library; markup creates real
48
56
  DOM elements, and components are compiled away.
49
57
 
50
58
  The guide with examples and a playground: <https://qwern-9876r.github.io/RookLang/>.
51
59
  Specification: [docs/spec.md](docs/spec.md), design of components:
52
60
  [docs/components.md](docs/components.md), an example: [examples/hello.rk](examples/hello.rk).
61
+ Where Rook stands and the road to 1.0: [docs/roadmap.md](docs/roadmap.md); what changed in each
62
+ version: [CHANGELOG.md](CHANGELOG.md).
53
63
 
54
64
  ## Installation
55
65
 
@@ -60,6 +70,7 @@ npm install rooklang
60
70
  npx rook build app.rk # app.js next to the source
61
71
  npx rook build src --out-dir dist # every module of a folder
62
72
  npx rook run app.rk # compile and run
73
+ npx rook run --watch app.rk # and again when a file of it changes
63
74
  ```
64
75
 
65
76
  The compiled code imports what channels, goroutines and markup need from `rooklang/runtime`, so
@@ -97,9 +108,11 @@ staged version is formatted, so edits that are not staged stay out of the commit
97
108
  | `npm run vscode:package` | Pack the VS Code extension into `rooklang.vsix` |
98
109
 
99
110
  Options of `build`: `--out-dir dir` writes the `.js` files to a separate folder, `--stdout` prints
100
- the JS of one file, `--no-check` skips type checking. `--markup` and `--no-markup` turn markup and
111
+ the JS of one file, `--no-check` skips type checking, `--no-source-map` writes no source maps
112
+ (`app.js.map`, by default next to each `.js`). `--markup` and `--no-markup` turn markup and
101
113
  components on or off for all files (with `run` too); by default they are off, and a package turns
102
- them on in its `package.json`: `"rook": { "markup": true }`.
114
+ them on in its `package.json`: `"rook": { "markup": true }`. `run --watch` runs the program again
115
+ when a `.rk` file or a CSS module of it changes, after stopping it with `SIGTERM`.
103
116
 
104
117
  ## Releases
105
118
 
@@ -183,6 +196,20 @@ build, and in development they show up over the page. Markup and components are
183
196
  so `.rk` modules fit a React, Svelte or Vue app as they are; turn them on with
184
197
  `"rook": { "markup": true }` in `package.json`, or `rook({ markup: true })`.
185
198
 
199
+ ### Tests
200
+
201
+ Rook has no test runner of its own: Vitest, with the same plugin in `vitest.config.ts`, runs tests
202
+ of `.rk` modules, written in JS or TS. Goroutines work there too: a worker thread of Vitest loads
203
+ the `.rk` modules with the loader of `rook run`.
204
+
205
+ ```ts
206
+ // math.test.ts
207
+ import { expect, it } from 'vitest';
208
+ import { add } from './math.rk';
209
+
210
+ it('adds', () => expect(add(1, 2)).toBe(3));
211
+ ```
212
+
186
213
  ## Conventions
187
214
 
188
215
  - Imports are written with the `.ts` extension (`import { x } from './lexer.ts'`).
package/README.ru.md CHANGED
@@ -25,14 +25,21 @@ close(jobs)
25
25
  for let i = 0; i < 4; i++ {
26
26
  console.log(<-results)
27
27
  }
28
+ close(results)
28
29
  ```
29
30
 
30
31
  - **Горутины и каналы.** `go f(x)` запускает функцию горутиной, на другом процессоре, а каналы,
31
- `select`, `after`, `tick` и `cancel` работают как в Go; `go for i, v in pixels { ... }` делит
32
+ `select`, `after`, `tick` и `cancel` работают как в Go, а сигналы и `AbortSignal` тоже становятся
33
+ каналами, `signal("SIGTERM")` и `done(request.signal)`; `go for i, v in pixels { ... }` делит
32
34
  массив между процессорами. Компилятор проверяет, что уходящее в другой поток можно туда
33
- скопировать.
34
- - **Синтаксис Go.** `func`, тип после имени, несколько результатов, ошибки вторым значением,
35
- `defer`, один цикл `for`:
35
+ скопировать, и предупреждает о канале, который никогда не закрывают; экземпляр `data class`
36
+ уходит вместе с классом и методами.
37
+ - **Топики между вкладками.** `topic<CartEvent>("cart")` передаёт значения между вкладками сайта
38
+ поверх `BroadcastChannel`: `cart <- e` отправляет и никого не ждёт, `for e in cart` принимает то,
39
+ что шлют другие вкладки. В `Channel` и `Topic` лежит то, во что компилируются каналы и топики, по
40
+ именам, и их закрытые нулевые значения `Channel.EMPTY` и `Topic.EMPTY`.
41
+ - **Синтаксис Go.** `func`, тип после имени, несколько результатов, ошибки вторым значением
42
+ (`try JSON.parse(text)` делает им то, что бросил JS), `defer`, один цикл `for`:
36
43
 
37
44
  ```go
38
45
  func divide(a, b number) (number, error) {
@@ -43,13 +50,16 @@ for let i = 0; i < 4; i++ {
43
50
  }
44
51
  ```
45
52
 
46
- - **Статическая типизация с null-safety.** Значение типа `?User` нельзя использовать без проверки.
53
+ - **Статическая типизация с null-safety.** Значение типа `?User` нельзя использовать без проверки,
54
+ а `decode<User>(body)` во время работы сверяет с типом JSON, пришедший извне.
47
55
  - **Внутри — JavaScript.** Те же значения, классы и модули, любые JS-библиотеки; разметка создаёт
48
56
  настоящие DOM-элементы, а компоненты исчезают при компиляции.
49
57
 
50
58
  Руководство с примерами и песочницей: <https://qwern-9876r.github.io/RookLang/ru/>.
51
59
  Спецификация: [docs/spec.ru.md](docs/spec.ru.md), концепция компонентов:
52
60
  [docs/components.ru.md](docs/components.ru.md), пример: [examples/hello.rk](examples/hello.rk).
61
+ Где Rook сейчас и путь к 1.0: [docs/roadmap.ru.md](docs/roadmap.ru.md); что изменилось в каждой
62
+ версии: [CHANGELOG.ru.md](CHANGELOG.ru.md).
53
63
 
54
64
  ## Установка
55
65
 
@@ -60,6 +70,7 @@ npm install rooklang
60
70
  npx rook build app.rk # app.js рядом с исходником
61
71
  npx rook build src --out-dir dist # все модули папки
62
72
  npx rook run app.rk # скомпилировать и запустить
73
+ npx rook run --watch app.rk # и запускать снова, когда меняется его файл
63
74
  ```
64
75
 
65
76
  Скомпилированный код импортирует то, что нужно каналам, горутинам и разметке, из
@@ -98,9 +109,11 @@ Node.js ≥ 24 (`nvm use`). Компилятор написан на TypeScript,
98
109
  | `npm run vscode:package` | Упаковать расширение для VS Code в `rooklang.vsix` |
99
110
 
100
111
  Опции `build`: `--out-dir dir` — писать `.js` в отдельную папку, `--stdout` — вывести JS одного
101
- файла в консоль, `--no-check` — без проверки типов. `--markup` и `--no-markup` включают или
112
+ файла в консоль, `--no-check` — без проверки типов, `--no-source-map` — без source maps
113
+ (`app.js.map`, по умолчанию рядом с каждым `.js`). `--markup` и `--no-markup` включают или
102
114
  выключают разметку и компоненты для всех файлов (и в `run`); по умолчанию они выключены, а пакет
103
- включает их в своём `package.json`: `"rook": { "markup": true }`.
115
+ включает их в своём `package.json`: `"rook": { "markup": true }`. `run --watch` запускает программу
116
+ заново, когда меняется её `.rk`-файл или CSS-модуль, сначала остановив её через `SIGTERM`.
104
117
 
105
118
  ## Релизы
106
119
 
@@ -183,6 +196,20 @@ export default defineConfig({ plugins: [rook()] });
183
196
  есть; включить их можно через `"rook": { "markup": true }` в `package.json` или
184
197
  `rook({ markup: true })`.
185
198
 
199
+ ### Тесты
200
+
201
+ Своего запускателя тестов у Rook нет: Vitest с тем же плагином в `vitest.config.ts` запускает тесты
202
+ `.rk`-модулей, написанные на JS или TS. Горутины там тоже работают: поток воркера Vitest загружает
203
+ `.rk`-модули загрузчиком `rook run`.
204
+
205
+ ```ts
206
+ // math.test.ts
207
+ import { expect, it } from 'vitest';
208
+ import { add } from './math.rk';
209
+
210
+ it('adds', () => expect(add(1, 2)).toBe(3));
211
+ ```
212
+
186
213
  ## Соглашения
187
214
 
188
215
  - Импорты пишутся с расширением `.ts` (`import { x } from './lexer.ts'`).
@@ -1,4 +1,4 @@
1
- import type { Expression, Identifier, StringLiteral } from './expressions.ts';
1
+ import type { Expression, FuncExpression, Identifier, StringLiteral } from './expressions.ts';
2
2
  import type { TypeMember, TypeNode, TypeReference } from './markup.ts';
3
3
  import type { BlockStatement, Statement } from './statements.ts';
4
4
  export interface NodeBase {
@@ -57,6 +57,19 @@ export interface Parameter extends NodeBase {
57
57
  * properties, which `{...attrs}` puts on one of its elements.
58
58
  */
59
59
  rest?: true;
60
+ /**
61
+ * `bind active string`: a property of a component that it may change. `bind:active={tab}`
62
+ * writes the changes into the parent's `tab`; without it the property is the component's state.
63
+ */
64
+ bind?: true;
65
+ /**
66
+ * `constructor(private name string)`: the parameter is a property too, which the constructor
67
+ * assigns, as in TypeScript.
68
+ */
69
+ property?: {
70
+ visibility: Visibility;
71
+ readonly?: true;
72
+ };
60
73
  }
61
74
  /**
62
75
  * `comp Card(title string, children Content) { ...; return <section>...</section> }`. Components
@@ -89,6 +102,11 @@ export interface DecoratorDeclaration extends NodeBase {
89
102
  params: Parameter[];
90
103
  /** `needs cart, auth`: the decorators whose public members it uses; they are applied above it. */
91
104
  needs: Identifier[];
105
+ /**
106
+ * `includes visible(margin), cart`: decorators applied with it, above its own body, whose
107
+ * arguments see its parameters. Their public members are its own for the component, `@shop.count`.
108
+ */
109
+ includes: DecoratorUse[];
92
110
  body: BlockStatement;
93
111
  }
94
112
  /** `@visible("300px")` or `@visible` before `comp`; the arguments are positional. */
@@ -110,19 +128,29 @@ export interface VariableDeclaration extends NodeBase {
110
128
  exported: boolean;
111
129
  keyword: 'let' | 'const' | 'state' | 'derived' | 'ref';
112
130
  /**
113
- * `public state`, `public derived` or `public const` in a decorator: the component reads it with
114
- * `get(@name.x)`.
131
+ * `public state`, `public derived` or `public const` in a decorator: the component reads it as
132
+ * `@name.x`.
115
133
  */
116
134
  isPublic: boolean;
117
135
  /** `_` is an ordinary identifier here; the checker treats it as "skip this value". */
118
136
  names: Identifier[];
119
137
  type: TypeNode | null;
120
138
  values: Expression[];
139
+ /**
140
+ * `derived summary string { ...; return text }`: the block that computes a derived value, a
141
+ * function without parameters, so that what looks for the `return`s, `defer`s and `break`s of a
142
+ * component stops at it, as at other functions. Its result type is `type`; `values` is empty.
143
+ */
144
+ body: FuncExpression | null;
121
145
  }
122
146
  /** `class Admin extends User implements Shape { ... }` */
123
147
  export interface ClassDeclaration extends NodeBase {
124
148
  kind: 'ClassDeclaration';
125
149
  exported: boolean;
150
+ /** `data class Point`: its fields are data, and its instances go to goroutines with the class. */
151
+ data: boolean;
152
+ /** `abstract class Shape`: it has no instances of its own, only those of its subclasses. */
153
+ abstract?: true;
126
154
  name: Identifier;
127
155
  typeParams: TypeParameter[];
128
156
  /** An expression, as in JS, so that classes from JS modules can be extended. */
@@ -138,6 +166,8 @@ export interface FieldDeclaration extends NodeBase {
138
166
  /** `public` when no modifier is written. */
139
167
  visibility: Visibility;
140
168
  isStatic: boolean;
169
+ /** `readonly id string`: assigned only by its value or in the constructor of its class. */
170
+ readonly?: true;
141
171
  name: Identifier;
142
172
  type: TypeNode | null;
143
173
  value: Expression | null;
@@ -156,10 +186,15 @@ export interface MethodDeclaration extends NodeBase {
156
186
  isStatic: boolean;
157
187
  /** `async load() User`, like `async func`. */
158
188
  async?: true;
189
+ /** `get area() number { ... }` and `set area(v number) { ... }`, as in JS. */
190
+ accessor?: 'get' | 'set';
191
+ /** `abstract area() number`: no body, each subclass that is not abstract implements it. */
192
+ abstract?: true;
159
193
  name: Identifier;
160
194
  params: Parameter[];
161
195
  results: TypeNode[];
162
- body: BlockStatement;
196
+ /** `null` for an abstract method. */
197
+ body: BlockStatement | null;
163
198
  }
164
199
  /** `interface Shape { name string; area() number }` */
165
200
  export interface InterfaceDeclaration extends NodeBase {
@@ -1,7 +1,7 @@
1
1
  import type { NodeBase, Parameter } from './declarations.ts';
2
2
  import type { ElementExpression, TypeNode } from './markup.ts';
3
3
  import type { BlockStatement } from './statements.ts';
4
- export type Expression = Identifier | NumberLiteral | BigIntLiteral | StringLiteral | TemplateLiteral | BooleanLiteral | NullLiteral | ThisExpression | SuperExpression | ArrayLiteral | ObjectLiteral | FuncExpression | ArrowFunction | UnaryExpression | BinaryExpression | ConditionalExpression | CallExpression | NewExpression | MemberExpression | IndexExpression | ElementExpression | DecoratorMember | DecoratorGet | AsExpression | GoExpression | ChanExpression | StreamChanExpression | QueueExpression | ConversionExpression | MakeExpression;
4
+ export type Expression = Identifier | NumberLiteral | BigIntLiteral | StringLiteral | RegExpLiteral | TemplateLiteral | BooleanLiteral | NullLiteral | ThisExpression | SuperExpression | ArrayLiteral | ObjectLiteral | FuncExpression | ArrowFunction | UnaryExpression | BinaryExpression | ConditionalExpression | CallExpression | NewExpression | MemberExpression | IndexExpression | ElementExpression | DecoratorMember | AsExpression | GoExpression | ChanExpression | StreamChanExpression | QueueExpression | TopicExpression | DecodeExpression | TryExpression | ConversionExpression | MakeExpression;
5
5
  /**
6
6
  * `[]byte("rook")`: the UTF-8 bytes of a string. `string(data)`, the way back, is a call, since
7
7
  * `string` may be the name of a value.
@@ -33,6 +33,29 @@ export interface QueueExpression extends NodeBase {
33
33
  element: TypeNode;
34
34
  items: Expression | null;
35
35
  }
36
+ /** `topic<CartEvent>("cart")`: values of the tabs of a site, over a BroadcastChannel of that name. */
37
+ export interface TopicExpression extends NodeBase {
38
+ kind: 'TopicExpression';
39
+ element: TypeNode;
40
+ name: Expression;
41
+ }
42
+ /**
43
+ * `decode<User>(body)`: JSON text, or a value from JS, checked against a type when the program
44
+ * runs; it gives `(User, error)`.
45
+ */
46
+ export interface DecodeExpression extends NodeBase {
47
+ kind: 'DecodeExpression';
48
+ type: TypeNode;
49
+ argument: Expression;
50
+ }
51
+ /**
52
+ * `try JSON.parse(text)`: an exception in the expression, or a rejected Promise that an `await` in
53
+ * it waits for, becomes the error of `(T, error)`.
54
+ */
55
+ export interface TryExpression extends NodeBase {
56
+ kind: 'TryExpression';
57
+ argument: Expression;
58
+ }
36
59
  /**
37
60
  * `chan(res.body, 4)`: a channel that receives the chunks of a ReadableStream, with room for 4,
38
61
  * and a channel that gets the error of the stream, or null at its end.
@@ -82,6 +105,13 @@ export interface StringLiteral extends NodeBase {
82
105
  /** Source text including quotes. */
83
106
  raw: string;
84
107
  }
108
+ /** `/ab+c/gi`, which JS writes the same way. */
109
+ export interface RegExpLiteral extends NodeBase {
110
+ kind: 'RegExpLiteral';
111
+ /** As it is written, between the slashes. */
112
+ pattern: string;
113
+ flags: string;
114
+ }
85
115
  /** `` `Hello, ${name}!` ``: `quasis` always has one element more than `expressions`. */
86
116
  export interface TemplateLiteral extends NodeBase {
87
117
  kind: 'TemplateLiteral';
@@ -201,16 +231,12 @@ export interface IndexExpression extends NodeBase {
201
231
  optional: boolean;
202
232
  }
203
233
  /**
204
- * `@visible.shown`: a public member of a decorator applied to the component. The names belong to
205
- * the decorator, not to any scope, so walkers do not visit them as identifiers.
234
+ * `@visible.shown` or `@visible.show()`: a public member of a decorator applied to the component,
235
+ * read or called. The names belong to the decorator, not to any scope, so walkers do not visit them
236
+ * as identifiers.
206
237
  */
207
238
  export interface DecoratorMember extends NodeBase {
208
239
  kind: 'DecoratorMember';
209
240
  decorator: Identifier;
210
241
  member: Identifier;
211
242
  }
212
- /** `get(@visible.shown)`: reads public state or a constant of a decorator. */
213
- export interface DecoratorGet extends NodeBase {
214
- kind: 'DecoratorGet';
215
- target: DecoratorMember;
216
- }
@@ -60,7 +60,7 @@ export interface EventHandler extends NodeBase {
60
60
  kind: 'EventHandler';
61
61
  body: SimpleStatement[];
62
62
  }
63
- export type TypeNode = TypeReference | ArrayType | NullableType | FuncType | ComponentType | ObjectType | UnionType | LiteralType | ChanType | QueueType;
63
+ export type TypeNode = TypeReference | ArrayType | NullableType | FuncType | ComponentType | ObjectType | UnionType | LiteralType | ChanType | QueueType | TopicType;
64
64
  /**
65
65
  * `chan number`: a channel of numbers, made by `chan<number>(size)`; `chan<- number` only sends
66
66
  * and `<-chan number` only receives.
@@ -75,6 +75,11 @@ export interface QueueType extends NodeBase {
75
75
  kind: 'QueueType';
76
76
  element: TypeNode;
77
77
  }
78
+ /** `topic CartEvent`: values that go to the other tabs of a site, made by `topic<T>(name)`. */
79
+ export interface TopicType extends NodeBase {
80
+ kind: 'TopicType';
81
+ element: TypeNode;
82
+ }
78
83
  /** A named type: `number`, `User`, `Point`, `Box<number>`. */
79
84
  export interface TypeReference extends NodeBase {
80
85
  kind: 'TypeReference';
@@ -4,11 +4,20 @@ import type { JsxElementStatement } from './markup.ts';
4
4
  export type Statement = ImportDeclaration | FuncDeclaration | ComponentDeclaration | DecoratorDeclaration | VariableDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | BlockStatement | ExpressionStatement | AssignmentStatement | IncDecStatement | ReturnStatement | IfStatement | ForStatement | ForInStatement | SwitchStatement | BreakStatement | ContinueStatement | ThrowStatement | TryStatement | DeferStatement | MountStatement | EffectStatement | StyleStatement | DeleteStatement | JsxElementStatement | SendStatement | SelectStatement;
5
5
  /** Statements allowed in the `init` and `update` parts of a `for` header. */
6
6
  export type SimpleStatement = VariableDeclaration | ExpressionStatement | AssignmentStatement | IncDecStatement | DeleteStatement | SendStatement;
7
- /** `ch <- value`: waits until the channel takes the value, so only in async functions. */
7
+ /**
8
+ * `ch <- value`: waits until the channel takes the value, so only in async functions. A send to a
9
+ * topic waits for nobody, so the parser, which does not know the type, leaves the error to the
10
+ * checker: `cannotWait` is the `<-` of a send where waiting is not allowed, and why.
11
+ */
8
12
  export interface SendStatement extends NodeBase {
9
13
  kind: 'SendStatement';
10
14
  channel: Expression;
11
15
  value: Expression;
16
+ cannotWait?: {
17
+ inDefer: boolean;
18
+ start: number;
19
+ end: number;
20
+ };
12
21
  }
13
22
  export interface BlockStatement extends NodeBase {
14
23
  kind: 'BlockStatement';
@@ -44,6 +53,8 @@ export interface ReturnStatement extends NodeBase {
44
53
  }
45
54
  export interface IfStatement extends NodeBase {
46
55
  kind: 'IfStatement';
56
+ /** `if const v, err = f(); err != null { }`: its variables are seen in every branch, as in Go. */
57
+ init: VariableDeclaration | null;
47
58
  condition: Expression;
48
59
  consequent: BlockStatement;
49
60
  /** `else { ... }` or `else if ...`. */
@@ -58,6 +69,8 @@ export interface IfStatement extends NodeBase {
58
69
  */
59
70
  export interface ForStatement extends NodeBase {
60
71
  kind: 'ForStatement';
72
+ /** `outer: for { ... }`, for `break outer` and `continue outer` in a nested loop. */
73
+ label?: Identifier;
61
74
  init: SimpleStatement | null;
62
75
  condition: Expression | null;
63
76
  update: SimpleStatement | null;
@@ -69,15 +82,21 @@ export interface ForStatement extends NodeBase {
69
82
  */
70
83
  export interface ForInStatement extends NodeBase {
71
84
  kind: 'ForInStatement';
85
+ label?: Identifier;
72
86
  key: Identifier | null;
73
87
  value: Identifier;
74
88
  iterable: Expression;
89
+ /** `with key todo.id` of a `{for}` in markup: what its blocks are found again by. */
90
+ withKey?: Expression;
75
91
  body: BlockStatement;
76
92
  go?: boolean;
77
93
  }
78
94
  /** `switch cmd { ... }`, or `switch { ... }` without a discriminant as an if/else chain. */
79
95
  export interface SwitchStatement extends NodeBase {
80
96
  kind: 'SwitchStatement';
97
+ label?: Identifier;
98
+ /** `switch const kind = node.kind; kind { }`, as the initializer of `if`. */
99
+ init: VariableDeclaration | null;
81
100
  discriminant: Expression | null;
82
101
  cases: SwitchCase[];
83
102
  }
@@ -94,6 +113,7 @@ export interface SwitchCase extends NodeBase {
94
113
  */
95
114
  export interface SelectStatement extends NodeBase {
96
115
  kind: 'SelectStatement';
116
+ label?: Identifier;
97
117
  cases: SelectCase[];
98
118
  }
99
119
  /** A case of `select`: a receive, `<-ch`, a send, `ch <- v`, or `default` when it is null. */
@@ -104,12 +124,18 @@ export interface SelectCase extends NodeBase {
104
124
  operation: UnaryExpression | SendStatement | null;
105
125
  body: Statement[];
106
126
  }
127
+ /** `break`, or `break outer`: the label of an enclosing loop, switch or select. */
107
128
  export interface BreakStatement extends NodeBase {
108
129
  kind: 'BreakStatement';
130
+ label?: Identifier;
109
131
  }
132
+ /** `continue`, or `continue outer`: the label of an enclosing loop. */
110
133
  export interface ContinueStatement extends NodeBase {
111
134
  kind: 'ContinueStatement';
135
+ label?: Identifier;
112
136
  }
137
+ /** The statements that can have a label. */
138
+ export type LabeledStatement = ForStatement | ForInStatement | SwitchStatement | SelectStatement;
113
139
  export interface ThrowStatement extends NodeBase {
114
140
  kind: 'ThrowStatement';
115
141
  argument: Expression;
@@ -132,7 +158,7 @@ export interface DeferStatement extends NodeBase {
132
158
  body: CallExpression | BlockStatement;
133
159
  }
134
160
  /**
135
- * `mount() { ... }` at the top level of a component: runs once the component's markup is in the
161
+ * `mount { ... }` at the top level of a component: runs once the component's markup is in the
136
162
  * document. It may return a function, which runs when `{if}`, `{switch}` or `{for}` removes the
137
163
  * markup.
138
164
  */
@@ -0,0 +1,11 @@
1
+ import type { Completion, TextEdit } from './checker/checker.ts';
2
+ import type { CompletionList } from './completion.ts';
3
+ /** A fix of a diagnostic: the import of the name from one module. */
4
+ export interface ImportFix {
5
+ title: string;
6
+ edit: TextEdit;
7
+ }
8
+ /** The names of other modules that fit where the cursor is and that are not in scope there. */
9
+ export declare function importCompletions(source: string, filename: string, list: CompletionList): Completion[];
10
+ /** For an error about a name that is not defined, the imports of the modules that export it. */
11
+ export declare function importFixes(source: string, filename: string, message: string): ImportFix[];
@@ -0,0 +1,81 @@
1
+ import { dirname, relative, resolve, sep } from 'node:path';
2
+ import { markupFor } from "./config.js";
3
+ import { parse } from "./parser/parser.js";
4
+ import { projectExports } from "./project-modules.js";
5
+ const SPACES = {
6
+ value: 'value',
7
+ type: 'type',
8
+ tag: 'tag',
9
+ };
10
+ /** The names of other modules that fit where the cursor is and that are not in scope there. */
11
+ export function importCompletions(source, filename, list) {
12
+ const space = list.context && SPACES[list.context];
13
+ if (!space)
14
+ return [];
15
+ const known = new Set(list.items.map((item) => item.name));
16
+ const candidates = projectExports(filename).filter((each) => each.spaces.includes(space) && !known.has(each.name));
17
+ if (candidates.length === 0)
18
+ return [];
19
+ const imports = importsOf(source, filename);
20
+ return candidates.map((each) => {
21
+ const specifier = specifierOf(filename, each.path);
22
+ return {
23
+ name: each.name,
24
+ kind: each.kind,
25
+ import: { specifier, edit: importEdit(source, imports, each.name, specifier, each.path) },
26
+ };
27
+ });
28
+ }
29
+ const UNDEFINED = [
30
+ [/^"(.+)" is not defined$/, 'value'],
31
+ [/^unknown type "(.+)"$/, 'type'],
32
+ [/^unknown component <(.+)>$/, 'tag'],
33
+ [/^unknown decorator @(.+)$/, 'decorator'],
34
+ ];
35
+ /** For an error about a name that is not defined, the imports of the modules that export it. */
36
+ export function importFixes(source, filename, message) {
37
+ for (const [pattern, space] of UNDEFINED) {
38
+ const name = pattern.exec(message)?.[1];
39
+ if (name === undefined)
40
+ continue;
41
+ const imports = importsOf(source, filename);
42
+ return projectExports(filename)
43
+ .filter((each) => each.name === name && each.spaces.includes(space))
44
+ .map((each) => {
45
+ const specifier = specifierOf(filename, each.path);
46
+ return {
47
+ title: `Add import { ${name} } from "${specifier}"`,
48
+ edit: importEdit(source, imports, name, specifier, each.path),
49
+ };
50
+ });
51
+ }
52
+ return [];
53
+ }
54
+ /** The imports of the module, with the absolute paths of the modules they import. */
55
+ function importsOf(source, filename) {
56
+ const { program } = parse(source, { markup: markupFor(filename) });
57
+ return program.body.flatMap((statement) => statement.kind === 'ImportDeclaration'
58
+ ? [[statement, resolve(dirname(filename), statement.source.value)]]
59
+ : []);
60
+ }
61
+ /** The path of a module as an import of the file `filename` writes it: "./card.rk". */
62
+ function specifierOf(filename, path) {
63
+ const specifier = relative(dirname(resolve(filename)), path)
64
+ .split(sep)
65
+ .join('/');
66
+ return specifier.startsWith('../') ? specifier : `./${specifier}`;
67
+ }
68
+ function importEdit(source, imports, name, specifier, path) {
69
+ const same = imports.find(([node, from]) => from === path && node.namedImports.length > 0);
70
+ const last = same?.[0].namedImports.at(-1);
71
+ if (last)
72
+ return { start: last.end, end: last.end, text: `, ${name}` };
73
+ const line = `import { ${name} } from "${specifier}"`;
74
+ const after = imports.at(-1)?.[0];
75
+ if (!after)
76
+ return { start: 0, end: 0, text: `${line}\n\n` };
77
+ const end = source.indexOf('\n', after.end);
78
+ return end === -1
79
+ ? { start: source.length, end: source.length, text: `\n${line}` }
80
+ : { start: end + 1, end: end + 1, text: `${line}\n` };
81
+ }
@@ -0,0 +1,6 @@
1
+ import type * as ast from './ast.ts';
2
+ /**
3
+ * Whether a `break` in these statements leaves the enclosing loop or switch: an unlabeled one
4
+ * outside of nested loops and switches, or `break label` anywhere when `label` is the statement's.
5
+ */
6
+ export declare function containsBreak(statements: readonly ast.Statement[], label?: ast.Identifier | null): boolean;
package/dist/breaks.js ADDED
@@ -0,0 +1,36 @@
1
+ import { forEachChild } from "./walk.js";
2
+ /**
3
+ * Whether a `break` in these statements leaves the enclosing loop or switch: an unlabeled one
4
+ * outside of nested loops and switches, or `break label` anywhere when `label` is the statement's.
5
+ */
6
+ export function containsBreak(statements, label = null) {
7
+ let found = false;
8
+ const visit = (node, nested) => {
9
+ switch (node.kind) {
10
+ case 'BreakStatement':
11
+ if (node.label ? node.label.name === label?.name : !nested)
12
+ found = true;
13
+ return;
14
+ // An unlabeled `break` inside these belongs to them.
15
+ case 'ForStatement':
16
+ case 'ForInStatement':
17
+ case 'SwitchStatement':
18
+ case 'SelectStatement':
19
+ if (label !== null)
20
+ forEachChild(node, (child) => visit(child, true));
21
+ return;
22
+ case 'FuncDeclaration':
23
+ case 'FuncExpression':
24
+ case 'ArrowFunction':
25
+ case 'ClassDeclaration':
26
+ case 'MountStatement':
27
+ case 'EffectStatement':
28
+ return;
29
+ default:
30
+ forEachChild(node, (child) => visit(child, nested));
31
+ }
32
+ };
33
+ for (const statement of statements)
34
+ visit(statement, false);
35
+ return found;
36
+ }
package/dist/build.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface BuildOptions {
9
9
  declarations?: boolean;
10
10
  /** Markup and components for every file; by default as its package.json says (config.ts). */
11
11
  markup?: boolean;
12
+ /** Write a source map next to each `.js`, `app.js.map`. Defaults to `true`. */
13
+ sourceMap?: boolean;
12
14
  }
13
15
  export interface BuildOutput {
14
16
  /** Absolute path of the `.rk` file. */
@@ -26,6 +28,11 @@ export interface BuildOutput {
26
28
  output: string;
27
29
  code: string;
28
30
  };
31
+ /** The source maps of the module and of its worker module, `.js.map`, unless turned off. */
32
+ maps?: {
33
+ output: string;
34
+ code: string;
35
+ }[];
29
36
  }
30
37
  /** The errors or the warnings of a file. */
31
38
  export interface BuildError {