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
@@ -1,4 +1,5 @@
1
1
  import { decoratedName } from "../decorators.js";
2
+ import { mayChange } from "../methods.js";
2
3
  import { forEachChild } from "../walk.js";
3
4
  // What code reads: the names of values, and the variable a change goes into.
4
5
  /** The variable at the start of `a.b[i].c()`, or `null`. */
@@ -11,8 +12,6 @@ export function rootName(node) {
11
12
  return rootName(node.object);
12
13
  case 'CallExpression':
13
14
  return rootName(node.callee);
14
- case 'DecoratorGet':
15
- return rootName(node.target);
16
15
  case 'DecoratorMember':
17
16
  return decoratedName(node.decorator.name, node.member.name);
18
17
  default:
@@ -100,3 +99,45 @@ export function effectReads(node, names) {
100
99
  forEachChild(node, (child) => effectReads(child, names));
101
100
  }
102
101
  }
102
+ /**
103
+ * What a statement (or an expression body) changes: the targets of assignments, `++` and
104
+ * `delete`, and the values that a method that may change them is called on, such as `push`.
105
+ * Nested blocks and functions are not included: their statements are handled on their own.
106
+ */
107
+ export function writeTargets(node, typeOf) {
108
+ const found = [];
109
+ const visit = (node) => {
110
+ switch (node.kind) {
111
+ case 'BlockStatement':
112
+ case 'EventHandler':
113
+ case 'FuncDeclaration':
114
+ case 'FuncExpression':
115
+ case 'ArrowFunction':
116
+ case 'ClassDeclaration':
117
+ return;
118
+ case 'SwitchCase':
119
+ node.tests.forEach(visit);
120
+ return;
121
+ case 'AssignmentStatement':
122
+ found.push(...node.targets);
123
+ break;
124
+ case 'IncDecStatement':
125
+ case 'DeleteStatement':
126
+ found.push(node.target);
127
+ break;
128
+ case 'CallExpression': {
129
+ const { callee } = node;
130
+ if (callee.kind === 'MemberExpression' &&
131
+ mayChange(typeOf(callee.object), callee.property.name)) {
132
+ found.push(callee.object);
133
+ }
134
+ break;
135
+ }
136
+ default:
137
+ break;
138
+ }
139
+ forEachChild(node, visit);
140
+ };
141
+ visit(node);
142
+ return found;
143
+ }
@@ -0,0 +1,42 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { Type } from '../checker/types.ts';
3
+ import type { DecoratorSource } from '../decorators.ts';
4
+ import type { InlinePackage } from '../inline-packages.ts';
5
+ import type { StyleModule } from '../style-modules.ts';
6
+ import type { ImportedComponent } from './imported.ts';
7
+ import type { RuntimeMode } from './runtime.ts';
8
+ export interface JsOptions {
9
+ /** The Rook source, used to keep the blank lines between statements. */
10
+ source: string;
11
+ /** Replace `.rk` with `.js` in relative import paths, for output written next to the sources. */
12
+ rewriteImports: boolean;
13
+ /** Types from the checker, when it ran; without them the generated code is more general. */
14
+ types?: WeakMap<ast.Expression, Type> | undefined;
15
+ /** Decorators imported from other modules, by name: their code is inlined here. */
16
+ decorators?: ReadonlyMap<string, ImportedDecorator> | undefined;
17
+ /** Components imported from other modules, by name: called, or inlined when they are small. */
18
+ components?: ReadonlyMap<string, ImportedComponent> | undefined;
19
+ /** Components of the module that become functions, since inlining them adds too many lines. */
20
+ functions?: ReadonlySet<string> | undefined;
21
+ /** The CSS modules the module imports, by their paths as written. */
22
+ styleModules?: ReadonlyMap<string, StyleModule> | undefined;
23
+ /** For `go`: where the worker module is (`./app.worker.js`), or in it, the functions it runs. */
24
+ goroutines?: {
25
+ workerUrl: string;
26
+ nodeLoader?: string | undefined;
27
+ } | {
28
+ targets: readonly string[];
29
+ } | undefined;
30
+ /** How the module gets the runtime it uses: imported, by default, or in it (runtime.ts). */
31
+ runtime?: RuntimeMode | undefined;
32
+ /** The data classes of the module, whose imports stay even if only types use them. */
33
+ dataClasses?: ReadonlySet<string> | undefined;
34
+ /** State of other modules that this one imports, `local → name there`: it only reads it. */
35
+ shared?: ReadonlyMap<string, string> | undefined;
36
+ }
37
+ /** A decorator of another module, with the types of the expressions of that module. */
38
+ export interface ImportedDecorator extends DecoratorSource {
39
+ types: WeakMap<ast.Expression, Type> | null;
40
+ /** The components of that module its code uses, which go with it into the components here. */
41
+ inline: InlinePackage | null;
42
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,59 +1,8 @@
1
1
  import type * as ast from '../ast.ts';
2
2
  import type { Type } from '../checker/types.ts';
3
+ import type { Live, Source } from './live.ts';
3
4
  import { readNames } from './mutations.ts';
4
- /** What markup can depend on: a `state` variable, or a property that gets the parent's state. */
5
- export interface Source {
6
- /** Unique in the module; marks the places that change the source in the generated code. */
7
- readonly id: number;
8
- readonly name: string;
9
- readonly kind: 'state' | 'prop';
10
- /** The function that brings dependent nodes up to date, e.g. `$$updateCount3`. */
11
- readonly update: string;
12
- /**
13
- * Statements that compute again the `derived` values that read it: `double = count * 2`. They
14
- * run first, also before the markup exists, so that the values are right whenever they are read.
15
- */
16
- readonly computations: string[];
17
- /** Statements that bring the nodes depending on it up to date. */
18
- readonly dependents: string[];
19
- /** Places that change it. */
20
- readonly writes: Write[];
21
- }
22
- export interface Write {
23
- /** Whether it may run before the markup exists: in a function of the body, not in a handler. */
24
- early: boolean;
25
- /** A dependent it does not need: `bind:` does not update its own element. */
26
- skip: number | null;
27
- }
28
- /** Everything a change of the source runs: the computations, then the updates of the nodes. */
29
- export declare function updatesOf(source: Source): string[];
30
- /**
31
- * Where the updates of live markup go: the update functions of a component's state, or the update
32
- * of a block of `{if ...}` / `{for ...}`.
33
- */
34
- export interface Live {
35
- /** Sources whose values an expression reads. */
36
- dependencies(node: ast.Node): Set<Source>;
37
- /** Adds a statement that brings a part of the markup up to date when the sources change. */
38
- depend(sources: ReadonlySet<Source>, statement: string): void;
39
- /** The index that the next statement for a source gets, for `bind:` to skip its own update. */
40
- ownIndex(source: Source): number | null;
41
- }
42
- /**
43
- * A block of markup control flow: a branch of `{if ...}` or the content for one item of
44
- * `{for ...}`. It is created again when needed, so its updates go into a function of its own,
45
- * which runs when any source that the block reads changes.
46
- */
47
- export declare class Block implements Live {
48
- readonly statements: string[];
49
- /** Sources that any statement depends on. */
50
- readonly sources: Set<Source>;
51
- private readonly parent;
52
- constructor(parent: Live);
53
- dependencies(node: ast.Node): Set<Source>;
54
- depend(sources: ReadonlySet<Source>, statement: string): void;
55
- ownIndex(): null;
56
- }
5
+ export { Block, type Live, type Source, type Write, updatesOf } from './live.ts';
57
6
  /**
58
7
  * The reactivity of one inlined component. The analysis goes by names: a local variable that
59
8
  * has the name of a state variable can only cause extra updates, never a missed one.
@@ -70,7 +19,7 @@ export declare class Reactive implements Live {
70
19
  private readonly functions;
71
20
  /** Local functions that the live markup calls: they run while it is created and updated. */
72
21
  private readonly renderFunctions;
73
- constructor(component: ast.ComponentDeclaration, setup: unknown, props: Iterable<string>, createSource: (name: string, kind: Source['kind']) => Source);
22
+ constructor(component: ast.ComponentDeclaration, setup: unknown, props: Iterable<string>, createSource: (name: string, kind: Source['kind']) => Source, shared?: ReadonlySet<string>);
74
23
  /** Whether a statement of the body declares a function that the live markup calls. */
75
24
  declaresRenderFunction(node: ast.Statement): boolean;
76
25
  /** Adds a statement that computes a `derived` value again when the sources change. */
@@ -82,11 +31,7 @@ export declare class Reactive implements Live {
82
31
  * the names that the node itself reads.
83
32
  */
84
33
  dependencies(node: ast.Node, read?: typeof readNames): Set<Source>;
85
- /**
86
- * `state` variables that a statement (or an expression body) changes: by assignment, `++`,
87
- * or a method that may change the value, such as `push`. Nested blocks and functions are not
88
- * included: their statements are handled on their own.
89
- */
34
+ /** `state` variables that a statement changes, by assignment, `++` or a method like `push`. */
90
35
  writtenBy(node: ast.Node, typeOf: (node: ast.Expression) => Type | undefined): Set<Source>;
91
36
  /** Sources whose value `node` refers into: `todos`, `todos[i].tags`, `todos.find(...)`. */
92
37
  rootSources(node: ast.Expression): ReadonlySet<Source>;
@@ -1,38 +1,11 @@
1
+ import { ownStatements } from "../checker/helpers.js";
1
2
  import { wrappedMarkup } from "../decorators.js";
2
3
  import { containsReturn, returnedMarkup } from "../recursion.js";
3
4
  import { forEachChild } from "../walk.js";
4
- import { mayChange } from "../methods.js";
5
- import { readNames, rootName } from "./mutations.js";
5
+ import { readNames, rootName, writeTargets } from "./mutations.js";
6
+ // Reactivity of components: which markup depends on which state, and which code changes it.
7
+ export { Block, updatesOf } from "./live.js";
6
8
  const EMPTY = new Set();
7
- /** Everything a change of the source runs: the computations, then the updates of the nodes. */
8
- export function updatesOf(source) {
9
- return [...source.computations, ...source.dependents];
10
- }
11
- /**
12
- * A block of markup control flow: a branch of `{if ...}` or the content for one item of
13
- * `{for ...}`. It is created again when needed, so its updates go into a function of its own,
14
- * which runs when any source that the block reads changes.
15
- */
16
- export class Block {
17
- statements = [];
18
- /** Sources that any statement depends on. */
19
- sources = new Set();
20
- parent;
21
- constructor(parent) {
22
- this.parent = parent;
23
- }
24
- dependencies(node) {
25
- return this.parent.dependencies(node);
26
- }
27
- depend(sources, statement) {
28
- this.statements.push(statement);
29
- for (const source of sources)
30
- this.sources.add(source);
31
- }
32
- ownIndex() {
33
- return null;
34
- }
35
- }
36
9
  /**
37
10
  * The reactivity of one inlined component. The analysis goes by names: a local variable that
38
11
  * has the name of a state variable can only cause extra updates, never a missed one.
@@ -49,10 +22,22 @@ export class Reactive {
49
22
  functions = new Map();
50
23
  /** Local functions that the live markup calls: they run while it is created and updated. */
51
24
  renderFunctions = new Set();
52
- constructor(component, setup, props, createSource) {
25
+ constructor(component, setup, props, createSource, shared = new Set()) {
53
26
  this.setup = setup;
27
+ // State of the module that the component reads; its own names, set after, hide it.
28
+ const read = new Set();
29
+ readNames(component, read);
30
+ for (const name of read) {
31
+ if (shared.has(name))
32
+ this.sources.set(name, createSource(name, 'shared'));
33
+ }
54
34
  for (const name of props)
55
35
  this.sources.set(name, createSource(name, 'prop'));
36
+ // A bound property changes as state does, whether the parent sets it or not.
37
+ for (const param of component.params) {
38
+ if (param.bind)
39
+ this.sources.set(param.name.name, createSource(param.name.name, 'state'));
40
+ }
56
41
  // Markup declared after an early return may never be created, so it is not updated.
57
42
  let returned = false;
58
43
  for (const statement of component.body.body) {
@@ -146,52 +131,15 @@ export class Reactive {
146
131
  visit(node, read);
147
132
  return found;
148
133
  }
149
- /**
150
- * `state` variables that a statement (or an expression body) changes: by assignment, `++`,
151
- * or a method that may change the value, such as `push`. Nested blocks and functions are not
152
- * included: their statements are handled on their own.
153
- */
134
+ /** `state` variables that a statement changes, by assignment, `++` or a method like `push`. */
154
135
  writtenBy(node, typeOf) {
155
136
  const found = new Set();
156
- const write = (target) => {
137
+ for (const target of writeTargets(node, typeOf)) {
157
138
  for (const source of this.rootSources(target)) {
158
139
  if (source.kind === 'state')
159
140
  found.add(source);
160
141
  }
161
- };
162
- const visit = (node) => {
163
- switch (node.kind) {
164
- case 'BlockStatement':
165
- case 'EventHandler':
166
- case 'FuncDeclaration':
167
- case 'FuncExpression':
168
- case 'ArrowFunction':
169
- case 'ClassDeclaration':
170
- return;
171
- case 'SwitchCase':
172
- node.tests.forEach(visit);
173
- return;
174
- case 'AssignmentStatement':
175
- node.targets.forEach(write);
176
- break;
177
- case 'IncDecStatement':
178
- case 'DeleteStatement':
179
- write(node.target);
180
- break;
181
- case 'CallExpression': {
182
- const { callee } = node;
183
- if (callee.kind === 'MemberExpression' &&
184
- mayChange(typeOf(callee.object), callee.property.name)) {
185
- write(callee.object);
186
- }
187
- break;
188
- }
189
- default:
190
- break;
191
- }
192
- forEachChild(node, visit);
193
- };
194
- visit(node);
142
+ }
195
143
  return found;
196
144
  }
197
145
  /** Sources whose value `node` refers into: `todos`, `todos[i].tags`, `todos.find(...)`. */
@@ -241,8 +189,22 @@ export class Reactive {
241
189
  add(node.value.name, this.rootSources(node.iterable));
242
190
  if (node.key && inMarkup)
243
191
  add(node.key.name, this.dependencies(node.iterable));
192
+ // With a key, the block of an item may get another object with the same key.
193
+ if (node.withKey && inMarkup)
194
+ add(node.value.name, this.dependencies(node.iterable));
244
195
  break;
245
196
  case 'VariableDeclaration':
197
+ // `derived first { return todos[0] }` points into what it returns.
198
+ if (node.body) {
199
+ const name = node.names[0].name;
200
+ add(name, this.dependencies(node.body));
201
+ for (const statement of ownStatements(node.body.body)) {
202
+ if (statement.kind !== 'ReturnStatement')
203
+ continue;
204
+ for (const value of statement.values)
205
+ add(name, this.rootSources(value));
206
+ }
207
+ }
246
208
  node.names.forEach((name, i) => {
247
209
  const value = node.values.length === node.names.length ? node.values[i] : node.values[0];
248
210
  if (!value)
@@ -1,7 +1,11 @@
1
1
  /** How a module gets the runtime: by an import, or with the code of the runtime in it. */
2
2
  export type RuntimeMode = 'import' | 'inline';
3
- /** Predeclared functions of goroutines and channels: `close(ch)` is `$$close(ch)`. */
4
- export declare const PREDECLARED: ReadonlySet<string>;
3
+ /**
4
+ * Predeclared names of goroutines and channels, and what they are in JS: `close(ch)` is
5
+ * `$$Channel.close(ch)`, and `Channel` itself is `$$Channel`, so that the names of a program stay
6
+ * its own.
7
+ */
8
+ export declare const PREDECLARED: ReadonlyMap<string, string>;
5
9
  /** The names of the runtime that code uses, in order. */
6
10
  export declare function runtimeNames(code: string): string[];
7
11
  /** What gives a module these names of the runtime: an import, or the files that have them. */
@@ -10,4 +14,4 @@ export declare function runtimeCode(names: readonly string[], mode: RuntimeMode)
10
14
  * The `go` of a module: in a module, a call in a worker that starts with its worker module; in the
11
15
  * worker module, a call in the same worker, of a function of `$$goroutines`.
12
16
  */
13
- export declare function moduleGo(workerUrl: string | null): string[];
17
+ export declare function moduleGo(workerUrl: string | null, nodeLoader?: string): string[];
@@ -1,13 +1,22 @@
1
1
  import { RUNTIME_MODULE } from "../runtime-module.js";
2
2
  import { RUNTIME_FILES } from "../runtime/sources.js";
3
- /** Predeclared functions of goroutines and channels: `close(ch)` is `$$close(ch)`. */
4
- export const PREDECLARED = new Set([
5
- 'after',
6
- 'cancel',
7
- 'close',
8
- 'stream',
9
- 'tick',
10
- 'transfer',
3
+ /**
4
+ * Predeclared names of goroutines and channels, and what they are in JS: `close(ch)` is
5
+ * `$$Channel.close(ch)`, and `Channel` itself is `$$Channel`, so that the names of a program stay
6
+ * its own.
7
+ */
8
+ export const PREDECLARED = new Map([
9
+ ['abortSignal', '$$Channel.abortSignal'],
10
+ ['after', '$$Channel.after'],
11
+ ['cancel', '$$cancel'],
12
+ ['Channel', '$$Channel'],
13
+ ['close', '$$Channel.close'],
14
+ ['done', '$$Channel.done'],
15
+ ['signal', '$$Channel.signal'],
16
+ ['stream', '$$Channel.stream'],
17
+ ['tick', '$$Channel.tick'],
18
+ ['Topic', '$$Topic'],
19
+ ['transfer', '$$transfer'],
11
20
  ]);
12
21
  /** A name of generated code, but not a member: `$$chan(...)`, and not `channel.$$chan`. */
13
22
  const NAME = /(?<![.\w$])\$\$[A-Za-z_][\w$]*/g;
@@ -50,20 +59,25 @@ export function runtimeCode(names, mode) {
50
59
  add(owners.get(name));
51
60
  // The files only declare, but for a few that register what a thread shares, so their order does
52
61
  // not matter: the one of RUNTIME_FILES.
53
- return [...files]
62
+ const code = [...files]
54
63
  .filter(([file]) => needed.has(file))
55
64
  .flatMap(([, { code }], i) => [...(i > 0 ? [''] : []), ...code.split('\n')]);
65
+ const list = names.join(', ');
66
+ // A function of its own keeps the names of the runtime that are not `$$`, such as check() of
67
+ // decode, apart from those of the program, which may declare a check() too: only the names that
68
+ // the code uses come out of it.
69
+ return [`const { ${list} } = (() => {`, ...code, '', `return { ${list} };`, '})();'];
56
70
  }
57
71
  /**
58
72
  * The `go` of a module: in a module, a call in a worker that starts with its worker module; in the
59
73
  * worker module, a call in the same worker, of a function of `$$goroutines`.
60
74
  */
61
- export function moduleGo(workerUrl) {
75
+ export function moduleGo(workerUrl, nodeLoader) {
62
76
  if (workerUrl === null) {
63
77
  return ['const $$go = (fn, args) => $$goHere($$goroutines, fn, args);'];
64
78
  }
65
79
  return [
66
- ...startWorker(workerUrl),
80
+ ...startWorker(workerUrl, nodeLoader),
67
81
  '',
68
82
  'const $$go = (name, args) => $$goWorker($$startWorker, name, args);',
69
83
  ];
@@ -72,9 +86,14 @@ export function moduleGo(workerUrl) {
72
86
  * The start of a worker of a module: a Web Worker, or a worker thread of Node. Node takes only the
73
87
  * path of a `file:` URL, without the query of `app.rk?goroutines`, so its worker starts with a
74
88
  * module that imports the whole URL. An absolute URL, such as the Blob of the playground, is used
75
- * as it is.
89
+ * as it is. With `nodeLoader`, as under Vitest, the worker of Node imports the loader of `.rk`
90
+ * files first, since its thread has neither Vite nor the loader of `rook run`.
76
91
  */
77
- function startWorker(url) {
92
+ function startWorker(url, nodeLoader) {
93
+ // The loader registers its hooks when it runs, so the module comes after it, by a dynamic import.
94
+ const code = nodeLoader === undefined
95
+ ? '`import ${JSON.stringify(url.href)};`'
96
+ : `\`import ${JSON.stringify(nodeLoader)}; await import(\${JSON.stringify(url.href)});\``;
78
97
  const base = /^[a-z][a-z\d+.-]*:/i.test(url) ? '' : ', import.meta.url';
79
98
  const workerUrl = `new URL(${JSON.stringify(url)}${base})`;
80
99
  return [
@@ -86,7 +105,7 @@ function startWorker(url) {
86
105
  ' const { Worker: NodeWorker } = await import(/* @vite-ignore */ threads);',
87
106
  // Vite bundles the Web Worker; this URL is only for Node.
88
107
  ` const url = new URL(/* @vite-ignore */ ${JSON.stringify(url)}${base});`,
89
- ' const code = `import ${JSON.stringify(url.href)};`;',
108
+ ` const code = ${code};`,
90
109
  ' return new NodeWorker(new URL(`data:text/javascript,${encodeURIComponent(code)}`));',
91
110
  '}',
92
111
  ];
@@ -0,0 +1,22 @@
1
+ import type * as ast from '../ast.ts';
2
+ import type { Type } from '../checker/types.ts';
3
+ export interface SharedState {
4
+ /** The sets of updates by the name of the state here: its own, and those it imports. */
5
+ readonly listeners: ReadonlyMap<string, string>;
6
+ /** The states of this module, which only it changes. */
7
+ readonly own: ReadonlySet<string>;
8
+ /** Names that refer into the value of a state of this module: `for item in items`. */
9
+ readonly aliases: ReadonlyMap<string, ReadonlySet<string>>;
10
+ }
11
+ /** The set of updates of a state: `$$listeners$items`, or that of its local name for an import. */
12
+ export declare function listenersName(name: string): string;
13
+ /** The states of the program and those it imports, `local → name in its module`. */
14
+ export declare function sharedState(program: ast.Program, imported: ReadonlyMap<string, string> | undefined): SharedState;
15
+ /**
16
+ * The states of this module that a statement changes, through them or through names that refer
17
+ * into them. The analysis goes by names, as that of components: a local variable with the name of
18
+ * a state only causes an extra run of the updates.
19
+ */
20
+ export declare function sharedWrites(node: ast.Node, shared: SharedState, typeOf: (node: ast.Expression) => Type | undefined): string[];
21
+ /** The states of this module whose value `node` refers into: `items`, `items[0]`, `item.tags`. */
22
+ export declare function rootsOf(node: ast.Expression, shared: SharedState): ReadonlySet<string>;
@@ -0,0 +1,93 @@
1
+ import { forEachChild } from "../walk.js";
2
+ import { rootName, writeTargets } from "./mutations.js";
3
+ /** The set of updates of a state: `$$listeners$items`, or that of its local name for an import. */
4
+ export function listenersName(name) {
5
+ return `$$listeners$${name}`;
6
+ }
7
+ /** The states of the program and those it imports, `local → name in its module`. */
8
+ export function sharedState(program, imported) {
9
+ const own = new Set();
10
+ for (const statement of program.body) {
11
+ if (statement.kind !== 'VariableDeclaration' || statement.keyword !== 'state')
12
+ continue;
13
+ for (const name of statement.names)
14
+ if (name.name !== '_')
15
+ own.add(name.name);
16
+ }
17
+ const listeners = new Map();
18
+ for (const name of [...own, ...(imported?.keys() ?? [])])
19
+ listeners.set(name, listenersName(name));
20
+ return { listeners, own, aliases: own.size > 0 ? aliasesOf(program, own) : new Map() };
21
+ }
22
+ /**
23
+ * The states of this module that a statement changes, through them or through names that refer
24
+ * into them. The analysis goes by names, as that of components: a local variable with the name of
25
+ * a state only causes an extra run of the updates.
26
+ */
27
+ export function sharedWrites(node, shared, typeOf) {
28
+ if (shared.own.size === 0)
29
+ return [];
30
+ const found = new Set();
31
+ for (const target of writeTargets(node, typeOf)) {
32
+ for (const name of rootsOf(target, shared))
33
+ found.add(name);
34
+ }
35
+ return [...found];
36
+ }
37
+ /** The states of this module whose value `node` refers into: `items`, `items[0]`, `item.tags`. */
38
+ export function rootsOf(node, shared) {
39
+ const name = rootName(node);
40
+ if (name === null)
41
+ return new Set();
42
+ if (shared.own.has(name))
43
+ return new Set([name]);
44
+ return shared.aliases.get(name) ?? new Set();
45
+ }
46
+ /**
47
+ * Names that refer into the states anywhere in the module: the items of `for item in items`, a
48
+ * variable given `items[0]`, the parameters of a callback of `items.find(item => ...)`. Repeats
49
+ * until nothing changes, since an alias of an alias is one too.
50
+ */
51
+ function aliasesOf(program, own) {
52
+ const aliases = new Map();
53
+ const roots = (node) => rootsOf(node, { listeners: new Map(), own, aliases });
54
+ let changed = true;
55
+ const add = (name, states) => {
56
+ if (name === '_' || own.has(name))
57
+ return;
58
+ for (const state of states) {
59
+ const set = aliases.get(name) ?? new Set();
60
+ if (set.has(state))
61
+ continue;
62
+ set.add(state);
63
+ aliases.set(name, set);
64
+ changed = true;
65
+ }
66
+ };
67
+ const visit = (node) => {
68
+ if (node.kind === 'ForInStatement')
69
+ add(node.value.name, roots(node.iterable));
70
+ if (node.kind === 'VariableDeclaration' && node.keyword !== 'state') {
71
+ node.names.forEach((name, i) => {
72
+ const value = node.values.length === node.names.length ? node.values[i] : node.values[0];
73
+ if (value)
74
+ add(name.name, roots(value));
75
+ });
76
+ }
77
+ if (node.kind === 'CallExpression' && node.callee.kind === 'MemberExpression') {
78
+ const states = roots(node.callee.object);
79
+ for (const arg of node.arguments) {
80
+ if (arg.kind !== 'ArrowFunction' && arg.kind !== 'FuncExpression')
81
+ continue;
82
+ for (const param of arg.params)
83
+ add(param.name.name, states);
84
+ }
85
+ }
86
+ forEachChild(node, visit);
87
+ };
88
+ while (changed) {
89
+ changed = false;
90
+ visit(program);
91
+ }
92
+ return aliases;
93
+ }
@@ -1,13 +1,12 @@
1
1
  import type * as ast from '../ast.ts';
2
- import { CallEmitter } from './calls.ts';
2
+ import { TryEmitter } from './try.ts';
3
3
  /** Statements: lists and blocks of them, variables, assignments, `return`; the others are in later layers. */
4
- export declare abstract class SimpleStatementEmitter extends CallEmitter {
4
+ export declare abstract class SimpleStatementEmitter extends TryEmitter {
5
5
  protected abstract forInStatement(node: ast.ForInStatement): void;
6
6
  protected abstract selectStatement(node: ast.SelectStatement): void;
7
7
  protected abstract forStatement(node: ast.ForStatement): void;
8
8
  protected abstract ifStatement(node: ast.IfStatement): string;
9
- protected abstract switchStatement(node: ast.SwitchStatement, discriminant: ast.Expression): void;
10
- protected abstract taglessSwitch(node: ast.SwitchStatement): void;
9
+ protected abstract switchStatement(node: ast.SwitchStatement): void;
11
10
  protected abstract tryStatement(node: ast.TryStatement): string;
12
11
  protected abstract classDeclaration(node: ast.ClassDeclaration): void;
13
12
  protected abstract importDeclaration(node: ast.ImportDeclaration): void;
@@ -15,7 +14,7 @@ export declare abstract class SimpleStatementEmitter extends CallEmitter {
15
14
  protected labels: number;
16
15
  /** A `defer` inside an if/loop/switch; implemented by FunctionEmitter. */
17
16
  protected abstract deferStatement(node: ast.DeferStatement): void;
18
- /** `mount() { ... }` of a component (see ComponentEmitter). */
17
+ /** `mount { ... }` of a component (see FunctionEmitter). */
19
18
  protected abstract mountStatement(node: ast.MountStatement): void;
20
19
  /** `effect { ... }` of a component (see FunctionEmitter). */
21
20
  protected abstract effectStatement(node: ast.EffectStatement): void;
@@ -29,7 +28,14 @@ export declare abstract class SimpleStatementEmitter extends CallEmitter {
29
28
  /** A block's statements in a new scope, without the braces; `then` writes more code in it. */
30
29
  protected blockStatements(statements: readonly ast.Statement[], then?: () => void): void;
31
30
  protected blockStatement(node: ast.BlockStatement): string;
31
+ /**
32
+ * The start of the statement being written, which its first line gets as a mark for the source
33
+ * map (source-map.ts). Imports have none, since the import of the runtime goes among them, and
34
+ * neither does the code of other modules, whose places are in their own sources.
35
+ */
36
+ private sourceMark;
32
37
  protected statement(node: ast.Statement): void;
38
+ protected line(text: string): void;
33
39
  protected emitStatement(node: ast.Statement): void;
34
40
  /** `let a, b = 1, 2` → `let a = 1, b = 2`; `const q, err = f()` → `const [q, err] = f()`. */
35
41
  protected variableDeclaration(node: ast.VariableDeclaration): string;