rooklang 0.3.3 → 0.3.4

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 (170) hide show
  1. package/README.md +32 -7
  2. package/README.ru.md +32 -7
  3. package/dist/ast/declarations.d.ts +20 -1
  4. package/dist/ast/expressions.d.ts +31 -1
  5. package/dist/ast/markup.d.ts +6 -1
  6. package/dist/ast/statements.d.ts +25 -1
  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 +2 -0
  16. package/dist/checker/assignments.js +14 -0
  17. package/dist/checker/base.js +6 -6
  18. package/dist/checker/builtins.js +15 -5
  19. package/dist/checker/bytes.d.ts +2 -2
  20. package/dist/checker/bytes.js +2 -2
  21. package/dist/checker/calls.js +3 -0
  22. package/dist/checker/channel-objects.d.ts +3 -0
  23. package/dist/checker/channel-objects.js +40 -0
  24. package/dist/checker/channels.d.ts +1 -0
  25. package/dist/checker/channels.js +16 -2
  26. package/dist/checker/checker.d.ts +1 -1
  27. package/dist/checker/checker.js +19 -0
  28. package/dist/checker/class-members.d.ts +22 -0
  29. package/dist/checker/class-members.js +105 -0
  30. package/dist/checker/classes.d.ts +5 -4
  31. package/dist/checker/classes.js +41 -12
  32. package/dist/checker/closing.d.ts +21 -0
  33. package/dist/checker/closing.js +164 -0
  34. package/dist/checker/completion.d.ts +2 -2
  35. package/dist/checker/completion.js +2 -2
  36. package/dist/checker/context.d.ts +13 -0
  37. package/dist/checker/control-flow.d.ts +5 -0
  38. package/dist/checker/control-flow.js +17 -0
  39. package/dist/checker/declarations.js +1 -0
  40. package/dist/checker/decode.d.ts +8 -0
  41. package/dist/checker/decode.js +32 -0
  42. package/dist/checker/expressions.d.ts +5 -1
  43. package/dist/checker/expressions.js +13 -34
  44. package/dist/checker/flow.js +8 -4
  45. package/dist/checker/helpers.d.ts +1 -1
  46. package/dist/checker/helpers.js +9 -2
  47. package/dist/checker/iterables.d.ts +3 -1
  48. package/dist/checker/iterables.js +13 -0
  49. package/dist/checker/iterators.d.ts +4 -1
  50. package/dist/checker/iterators.js +12 -4
  51. package/dist/checker/json.d.ts +5 -0
  52. package/dist/checker/json.js +92 -0
  53. package/dist/checker/loops.js +23 -3
  54. package/dist/checker/members.js +7 -0
  55. package/dist/checker/operators.d.ts +2 -2
  56. package/dist/checker/operators.js +7 -5
  57. package/dist/checker/queues.d.ts +2 -2
  58. package/dist/checker/queues.js +2 -2
  59. package/dist/checker/resolution.js +5 -3
  60. package/dist/checker/signals.d.ts +8 -0
  61. package/dist/checker/signals.js +48 -0
  62. package/dist/checker/topics.d.ts +6 -0
  63. package/dist/checker/topics.js +17 -0
  64. package/dist/checker/transfer.d.ts +3 -1
  65. package/dist/checker/transfer.js +25 -5
  66. package/dist/checker/try.d.ts +6 -0
  67. package/dist/checker/try.js +19 -0
  68. package/dist/checker/types/constructors.d.ts +1 -1
  69. package/dist/checker/types/constructors.js +2 -2
  70. package/dist/checker/types/generics.js +1 -1
  71. package/dist/checker/types/iterables.d.ts +13 -0
  72. package/dist/checker/types/iterables.js +52 -0
  73. package/dist/checker/types/model.d.ts +25 -0
  74. package/dist/checker/types/params.js +22 -3
  75. package/dist/checker/types/printing.js +4 -3
  76. package/dist/checker/types/relations.js +19 -25
  77. package/dist/checker/types/topic.d.ts +6 -0
  78. package/dist/checker/types/topic.js +23 -0
  79. package/dist/checker/types.d.ts +2 -0
  80. package/dist/checker/types.js +2 -0
  81. package/dist/cli.js +18 -36
  82. package/dist/codegen/calls.d.ts +23 -0
  83. package/dist/codegen/calls.js +88 -7
  84. package/dist/codegen/control-statements.d.ts +10 -2
  85. package/dist/codegen/control-statements.js +56 -19
  86. package/dist/codegen/decode.d.ts +14 -0
  87. package/dist/codegen/decode.js +93 -0
  88. package/dist/codegen/emitter.d.ts +10 -41
  89. package/dist/codegen/emitter.js +9 -4
  90. package/dist/codegen/expressions.d.ts +9 -0
  91. package/dist/codegen/expressions.js +15 -24
  92. package/dist/codegen/js.d.ts +2 -2
  93. package/dist/codegen/js.js +5 -2
  94. package/dist/codegen/options.d.ts +40 -0
  95. package/dist/codegen/options.js +1 -0
  96. package/dist/codegen/runtime.d.ts +7 -3
  97. package/dist/codegen/runtime.js +27 -13
  98. package/dist/codegen/simple-statements.d.ts +10 -4
  99. package/dist/codegen/simple-statements.js +40 -15
  100. package/dist/codegen/statements.js +64 -4
  101. package/dist/codegen/try.d.ts +5 -0
  102. package/dist/codegen/try.js +40 -0
  103. package/dist/compile.d.ts +9 -0
  104. package/dist/compile.js +19 -6
  105. package/dist/completion.d.ts +4 -2
  106. package/dist/completion.js +10 -5
  107. package/dist/declarations.js +32 -6
  108. package/dist/decorator-copy.js +5 -2
  109. package/dist/goroutines.d.ts +1 -1
  110. package/dist/goroutines.js +5 -4
  111. package/dist/index.d.ts +6 -2
  112. package/dist/index.js +14 -5
  113. package/dist/lexer/base.d.ts +2 -0
  114. package/dist/lexer/base.js +6 -1
  115. package/dist/lexer/lexer.js +3 -0
  116. package/dist/lexer/literals.d.ts +5 -0
  117. package/dist/lexer/literals.js +51 -1
  118. package/dist/lexer/markup.js +3 -4
  119. package/dist/lexer/tables.js +2 -0
  120. package/dist/lexer/token.d.ts +4 -3
  121. package/dist/lexer/token.js +0 -2
  122. package/dist/loader.js +30 -8
  123. package/dist/parser/base.d.ts +4 -0
  124. package/dist/parser/base.js +2 -1
  125. package/dist/parser/classes.d.ts +2 -0
  126. package/dist/parser/classes.js +88 -23
  127. package/dist/parser/control-flow.d.ts +3 -1
  128. package/dist/parser/control-flow.js +21 -24
  129. package/dist/parser/declarations.d.ts +7 -4
  130. package/dist/parser/declarations.js +45 -6
  131. package/dist/parser/expressions.js +27 -3
  132. package/dist/parser/jumps.d.ts +11 -0
  133. package/dist/parser/jumps.js +77 -0
  134. package/dist/parser/literals.d.ts +9 -0
  135. package/dist/parser/literals.js +45 -6
  136. package/dist/parser/markup.js +1 -1
  137. package/dist/parser/simple-statements.d.ts +1 -1
  138. package/dist/parser/simple-statements.js +16 -9
  139. package/dist/parser/statements.d.ts +0 -3
  140. package/dist/parser/statements.js +22 -25
  141. package/dist/parser/syntax.d.ts +11 -1
  142. package/dist/parser/syntax.js +16 -2
  143. package/dist/parser/types.js +8 -3
  144. package/dist/project-modules.d.ts +12 -0
  145. package/dist/project-modules.js +106 -0
  146. package/dist/run-file.d.ts +6 -0
  147. package/dist/run-file.js +111 -0
  148. package/dist/runtime/sources.js +11 -5
  149. package/dist/source-map.d.ts +17 -0
  150. package/dist/source-map.js +75 -0
  151. package/dist/ts-types.js +4 -1
  152. package/dist/typescript/convert-objects.d.ts +2 -1
  153. package/dist/typescript/convert-objects.js +18 -9
  154. package/dist/vite.js +12 -4
  155. package/dist/walk-types.js +1 -0
  156. package/dist/walk.d.ts +0 -2
  157. package/dist/walk.js +11 -28
  158. package/package.json +1 -1
  159. package/src/runtime/chan.js +15 -5
  160. package/src/runtime/channel.js +28 -0
  161. package/src/runtime/classes.js +32 -0
  162. package/src/runtime/core.js +10 -1
  163. package/src/runtime/decode.js +190 -0
  164. package/src/runtime/go.js +3 -0
  165. package/src/runtime/index.js +6 -0
  166. package/src/runtime/select.js +14 -9
  167. package/src/runtime/signals.js +59 -0
  168. package/src/runtime/threads.js +32 -5
  169. package/src/runtime/topic.js +118 -0
  170. package/src/runtime/try.js +45 -0
@@ -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;
@@ -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;
@@ -1,10 +1,11 @@
1
1
  import { isIterator } from "../iterators.js";
2
+ import { sourceMark } from "../source-map.js";
2
3
  import { holdsBigInts, TYPED_ARRAYS, TYPED_ELEMENTS } from "../typed-arrays.js";
3
4
  import { declarationsOf } from "./analysis.js";
4
- import { CallEmitter } from "./calls.js";
5
+ import { TryEmitter } from "./try.js";
5
6
  import { ARROW, arrayPattern, POSTFIX } from "./syntax.js";
6
7
  /** Statements: lists and blocks of them, variables, assignments, `return`; the others are in later layers. */
7
- export class SimpleStatementEmitter extends CallEmitter {
8
+ export class SimpleStatementEmitter extends TryEmitter {
8
9
  /** Counter for the labels of switches compiled to if/else. */
9
10
  labels = 0;
10
11
  statements(list) {
@@ -34,8 +35,34 @@ export class SimpleStatementEmitter extends CallEmitter {
34
35
  blockStatement(node) {
35
36
  return this.block(() => this.blockStatements(node.body));
36
37
  }
38
+ /**
39
+ * The start of the statement being written, which its first line gets as a mark for the source
40
+ * map (source-map.ts). Imports have none, since the import of the runtime goes among them, and
41
+ * neither does the code of other modules, whose places are in their own sources.
42
+ */
43
+ sourceMark = null;
37
44
  statement(node) {
38
- this.withHoisting(true, () => this.emitStatement(node));
45
+ // A statement may write the statements inside it first, as a function writes its body before
46
+ // its first line: then the mark waits for that line.
47
+ const outer = this.sourceMark;
48
+ this.sourceMark =
49
+ node.kind === 'ImportDeclaration' || this.foreign.has(node) ? null : node.start;
50
+ try {
51
+ this.withHoisting(true, () => this.emitStatement(node));
52
+ }
53
+ finally {
54
+ this.sourceMark = outer;
55
+ }
56
+ }
57
+ // The marks of the updates of components stand alone in their lines (updates.ts).
58
+ line(text) {
59
+ if (this.sourceMark === null || text.startsWith('\uE000')) {
60
+ super.line(text);
61
+ return;
62
+ }
63
+ const mark = sourceMark(this.sourceMark);
64
+ this.sourceMark = null;
65
+ super.line(mark + text);
39
66
  }
40
67
  emitStatement(node) {
41
68
  switch (node.kind) {
@@ -115,18 +142,15 @@ export class SimpleStatementEmitter extends CallEmitter {
115
142
  this.selectStatement(node);
116
143
  break;
117
144
  case 'SwitchStatement':
118
- if (node.discriminant === null)
119
- this.taglessSwitch(node);
120
- else
121
- this.switchStatement(node, node.discriminant);
145
+ this.switchStatement(node);
122
146
  break;
123
147
  case 'BreakStatement': {
124
- const label = this.fn.breakTargets.at(-1);
148
+ const label = node.label ? this.label(node.label) : this.fn.breakTargets.at(-1);
125
149
  this.line(label ? `break ${label};` : 'break;');
126
150
  break;
127
151
  }
128
152
  case 'ContinueStatement':
129
- this.line('continue;');
153
+ this.line(node.label ? `continue ${this.label(node.label)};` : 'continue;');
130
154
  break;
131
155
  case 'ThrowStatement':
132
156
  this.line(`throw ${this.expression(node.argument, 0)};`);
@@ -137,12 +161,9 @@ export class SimpleStatementEmitter extends CallEmitter {
137
161
  case 'DeferStatement':
138
162
  this.deferStatement(node);
139
163
  break;
140
- // Waits until the channel takes the value.
141
- case 'SendStatement': {
142
- const channel = this.expression(node.channel, ARROW);
143
- this.line(`await $$send(${channel}, ${this.expression(node.value, ARROW)});`);
164
+ case 'SendStatement':
165
+ this.line(`${this.send(node)};`);
144
166
  break;
145
- }
146
167
  case 'MountStatement':
147
168
  this.mountStatement(node);
148
169
  break;
@@ -192,9 +213,13 @@ export class SimpleStatementEmitter extends CallEmitter {
192
213
  // An empty queue, as `[]` is an empty array.
193
214
  case 'QueueType':
194
215
  return 'new $$Queue()';
216
+ // Closed, the same for all types: what is sent to them goes nowhere.
217
+ case 'ChanType':
218
+ return '$$Channel.EMPTY';
219
+ case 'TopicType':
220
+ return '$$Topic.EMPTY';
195
221
  case 'FuncType':
196
222
  case 'ComponentType':
197
- case 'ChanType':
198
223
  case 'ObjectType':
199
224
  case 'UnionType':
200
225
  case 'LiteralType':
@@ -1,3 +1,4 @@
1
+ import { isSuperCall } from "../checker/helpers.js";
1
2
  import { isIterator } from "../iterators.js";
2
3
  import { decoratedName, moduleNamesOf } from "../decorators.js";
3
4
  import { inlinePackages } from "../inline-packages.js";
@@ -16,7 +17,9 @@ export class StatementEmitter extends ParallelLoopEmitter {
16
17
  return;
17
18
  // Imports used only as types (e.g. interfaces) do not exist at runtime, and neither do
18
19
  // components: a component used as a value is its function, imported below.
19
- const used = (name) => this.valueNames.has(name.name) && !this.options.components?.has(name.name);
20
+ // A data class, even used only as a type, registers itself where it is imported.
21
+ const used = (name) => (this.valueNames.has(name.name) || this.options.dataClasses?.has(name.name) === true) &&
22
+ !this.options.components?.has(name.name);
20
23
  const clauses = [];
21
24
  if (node.defaultImport && used(node.defaultImport)) {
22
25
  clauses.push(this.name(node.defaultImport.name));
@@ -100,7 +103,10 @@ export class StatementEmitter extends ParallelLoopEmitter {
100
103
  : '';
101
104
  const body = this.block(() => {
102
105
  let previous;
106
+ // An abstract method has no code: only the subclasses have it.
103
107
  for (const member of node.members) {
108
+ if (member.kind === 'MethodDeclaration' && member.abstract)
109
+ continue;
104
110
  const bothFields = previous?.kind === 'FieldDeclaration' && member.kind === 'FieldDeclaration';
105
111
  if (previous && (!bothFields || this.blankLineBetween(previous, member)))
106
112
  this.blankLine();
@@ -109,6 +115,12 @@ export class StatementEmitter extends ParallelLoopEmitter {
109
115
  }
110
116
  });
111
117
  this.line(`${exported}class ${this.name(node.name.name)}${superClass} ${body}`);
118
+ // A data class is registered, so that its instances get it back in another thread
119
+ // (runtime/classes.js); the id is the same in the worker module, which has a copy of it.
120
+ if (node.data) {
121
+ const id = `${node.name.name}:${hash(this.options.source.slice(node.start, node.end))}`;
122
+ this.line(`$$dataClass(${this.name(node.name.name)}, ${JSON.stringify(id)});`);
123
+ }
112
124
  }
113
125
  /** Visibility modifiers are checked by the compiler and do not exist in the output. */
114
126
  classMember(member) {
@@ -126,17 +138,65 @@ export class StatementEmitter extends ParallelLoopEmitter {
126
138
  break;
127
139
  }
128
140
  case 'ConstructorDeclaration': {
129
- const [params, body] = this.func(member.params, member.body);
141
+ const [params, body] = this.func(member.params, assignProperties(member));
130
142
  this.line(`constructor(${params}) ${body}`);
131
143
  break;
132
144
  }
133
145
  case 'MethodDeclaration': {
146
+ if (!member.body)
147
+ break;
134
148
  const [params, body] = this.func(member.params, member.body);
135
- const modifiers = `${member.isStatic ? 'static ' : ''}${member.async ? 'async ' : ''}`;
136
- const star = isIterator(member.params) ? '*' : '';
149
+ const modifiers = `${member.isStatic ? 'static ' : ''}${member.async ? 'async ' : ''}` +
150
+ (member.accessor ? `${member.accessor} ` : '');
151
+ const star = isIterator(member.params) && !member.accessor ? '*' : '';
137
152
  this.line(`${modifiers}${star}${member.name.name}(${params}) ${body}`);
138
153
  break;
139
154
  }
140
155
  }
141
156
  }
142
157
  }
158
+ /**
159
+ * The body of a constructor whose parameters are properties, `constructor(private name string)`:
160
+ * it assigns them first, `this.name = name`, or right after `super(...)` in a derived class.
161
+ */
162
+ function assignProperties(node) {
163
+ const assignments = node.params.flatMap((param) => {
164
+ if (!param.property)
165
+ return [];
166
+ const at = { start: param.start, end: param.end };
167
+ const self = { kind: 'ThisExpression', ...at };
168
+ const target = {
169
+ kind: 'MemberExpression',
170
+ object: self,
171
+ property: param.name,
172
+ optional: false,
173
+ ...at,
174
+ };
175
+ return [
176
+ {
177
+ kind: 'AssignmentStatement',
178
+ operator: '=',
179
+ targets: [target],
180
+ values: [param.name],
181
+ ...at,
182
+ },
183
+ ];
184
+ });
185
+ if (assignments.length === 0)
186
+ return node.body;
187
+ const statements = node.body.body;
188
+ const after = statements.findIndex(isSuperCall) + 1;
189
+ return {
190
+ ...node.body,
191
+ body: [...statements.slice(0, after), ...assignments, ...statements.slice(after)],
192
+ };
193
+ }
194
+ /** FNV-1a of a text, as 8 hex digits: an id that changes only with the text. */
195
+ function hash(text) {
196
+ let value = 0x811c9dc5;
197
+ for (let i = 0; i < text.length; i++) {
198
+ value ^= text.charCodeAt(i);
199
+ value = Math.imul(value, 0x01000193) >>> 0;
200
+ }
201
+ return value.toString(16).padStart(8, '0');
202
+ }
@@ -0,0 +1,5 @@
1
+ import type * as ast from '../ast.ts';
2
+ import { DecodeEmitter } from './decode.ts';
3
+ export declare abstract class TryEmitter extends DecodeEmitter {
4
+ protected tryValue(node: ast.TryExpression): [string, number];
5
+ }
@@ -0,0 +1,40 @@
1
+ import { isErrorType } from "../checker/error-results.js";
2
+ import { forEachChild } from "../walk.js";
3
+ import { startsWithObjectLiteral } from "./analysis.js";
4
+ import { DecodeEmitter } from "./decode.js";
5
+ import { ARROW, POSTFIX, UNARY } from "./syntax.js";
6
+ // `try expr`: the expression runs in a function that the runtime calls inside try/catch
7
+ // (runtime/try.js), `$$catch(() => JSON.parse(text))`. One that waits is an async function, whose
8
+ // rejected Promise is caught as well: `await $$catchAsync(async () => await fetch(url))`. The runtime
9
+ // is told how many values the expression gives, and whether the last of them is an error already.
10
+ export class TryEmitter extends DecodeEmitter {
11
+ tryValue(node) {
12
+ // The expression may not run to the end, so it creates no elements in advance.
13
+ const code = this.withHoisting(false, () => this.expression(node.argument, ARROW));
14
+ const body = startsWithObjectLiteral(node.argument) ? `(${code})` : code;
15
+ const type = this.options.types?.get(node.argument);
16
+ let results = '';
17
+ if (type?.kind === 'void' || (type !== undefined && isErrorType(type))) {
18
+ results = type.kind === 'void' ? ', 0' : ', 1, true';
19
+ }
20
+ else if (type?.kind === 'tuple') {
21
+ results = `, ${type.types.length}${isErrorType(type.types.at(-1)) ? ', true' : ''}`;
22
+ }
23
+ return waits(node.argument)
24
+ ? [`await $$catchAsync(async () => ${body}${results})`, UNARY]
25
+ : [`$$catch(() => ${body}${results})`, POSTFIX];
26
+ }
27
+ }
28
+ /** Whether the expression waits, with `await` or `<-`, outside the functions in it. */
29
+ function waits(node) {
30
+ if (node.kind === 'UnaryExpression' && (node.operator === 'await' || node.operator === '<-')) {
31
+ return true;
32
+ }
33
+ if (node.kind === 'FuncExpression' || node.kind === 'ArrowFunction')
34
+ return false;
35
+ let found = false;
36
+ forEachChild(node, (child) => {
37
+ found ||= waits(child);
38
+ });
39
+ return found;
40
+ }
package/dist/compile.d.ts CHANGED
@@ -2,6 +2,7 @@ import type * as ast from './ast.ts';
2
2
  import { type CheckOptions, type ImportResult, type Library } from './checker/checker.ts';
3
3
  import type { RuntimeMode } from './codegen/runtime.ts';
4
4
  import type { Diagnostic } from './diagnostics.ts';
5
+ import { type SourceMap } from './source-map.ts';
5
6
  import { type StyleModule } from './style-modules.ts';
6
7
  export interface CoreCompileOptions {
7
8
  /** Replace `.rk` with `.js` in relative import paths. Defaults to `true`. */
@@ -22,12 +23,16 @@ export interface CoreCompileOptions {
22
23
  readStyleModule?: ((specifier: string) => string | null) | undefined;
23
24
  /** Where the worker module of `go` is, relative to the module; see workerUrl(). */
24
25
  workerUrl?: string | undefined;
26
+ /** A module that a worker thread of Node imports first, to load `.rk` files (vite.ts). */
27
+ nodeLoader?: string | undefined;
25
28
  /**
26
29
  * How the code gets the runtime of Rook: `import`, the default, imports it from
27
30
  * "rooklang/runtime"; `inline` puts the parts it uses into the module, for code that runs
28
31
  * without imports (src/codegen/runtime.ts).
29
32
  */
30
33
  runtime?: RuntimeMode | undefined;
34
+ /** The name of the source in the source maps, `app.rk`. */
35
+ sourceName?: string | undefined;
31
36
  }
32
37
  /**
33
38
  * Where the worker module of a module is: a `.worker.js` next to its `.js`, or, where `.rk`
@@ -45,6 +50,10 @@ export interface CompileResult {
45
50
  dependencies: string[];
46
51
  /** The `.d.ts` for the module, with the `declarations` option. */
47
52
  declarations?: string;
53
+ /** Where the lines of `code` come from in the source, when there is code. */
54
+ map?: SourceMap;
55
+ /** That of the worker module. */
56
+ workerMap?: SourceMap;
48
57
  /** The worker module that runs the functions `go` calls, when the module has `go`. */
49
58
  worker?: string;
50
59
  }
package/dist/compile.js CHANGED
@@ -3,6 +3,7 @@ import { property, STRING } from "./checker/types.js";
3
3
  import { generateJs } from "./codegen/js.js";
4
4
  import { printDeclarations } from "./declarations.js";
5
5
  import { goroutines, goTarget, workerProgram } from "./goroutines.js";
6
+ import { withoutMarks } from "./source-map.js";
6
7
  import { parse } from "./parser/parser.js";
7
8
  import { isStyleModule, styleModule, unusedClasses } from "./style-modules.js";
8
9
  /**
@@ -24,6 +25,8 @@ export function compileModule(source, options = {}) {
24
25
  return { code: '', diagnostics: problems, warnings: [], dependencies };
25
26
  let types;
26
27
  let declarations;
28
+ // Without the checker, only the data classes that the module declares are known.
29
+ let dataClasses = new Set(program.body.flatMap((each) => each.kind === 'ClassDeclaration' && each.data ? [each.name.name] : []));
27
30
  let warnings = [];
28
31
  if (options.typeCheck ?? true) {
29
32
  const checked = check(program, checkOptions);
@@ -32,6 +35,7 @@ export function compileModule(source, options = {}) {
32
35
  return { code: '', diagnostics: checked.diagnostics, warnings, dependencies };
33
36
  }
34
37
  types = checked.types;
38
+ dataClasses = checked.dataClasses;
35
39
  if (options.declarations)
36
40
  declarations = printDeclarations(program, checked);
37
41
  }
@@ -42,25 +46,34 @@ export function compileModule(source, options = {}) {
42
46
  types,
43
47
  styleModules,
44
48
  runtime: options.runtime,
49
+ dataClasses,
45
50
  ...importedCode(program, options.importModule),
46
51
  };
47
- const code = generateJs(program, {
52
+ const sourceName = options.sourceName ?? 'main.rk';
53
+ const main = withoutMarks(generateJs(program, {
48
54
  ...jsOptions,
49
- goroutines: { workerUrl: options.workerUrl ?? workerUrl('main.rk', rewriteImports) },
50
- });
55
+ goroutines: {
56
+ workerUrl: options.workerUrl ?? workerUrl('main.rk', rewriteImports),
57
+ nodeLoader: options.nodeLoader,
58
+ },
59
+ }), source, sourceName);
51
60
  const targets = [
52
61
  ...new Set(goroutines(program).flatMap((node) => goTarget(program, node) ?? [])),
53
62
  ];
54
63
  const worker = targets.length > 0
55
- ? generateJs(workerProgram(program, targets), { ...jsOptions, goroutines: { targets } })
64
+ ? withoutMarks(generateJs(workerProgram(program, targets, dataClasses), {
65
+ ...jsOptions,
66
+ goroutines: { targets },
67
+ }), source, sourceName)
56
68
  : undefined;
57
69
  return {
58
- code,
70
+ code: main.code,
59
71
  diagnostics: [],
60
72
  warnings,
61
73
  dependencies,
62
74
  ...(declarations ? { declarations } : {}),
63
- ...(worker === undefined ? {} : { worker }),
75
+ map: main.map,
76
+ ...(worker === undefined ? {} : { worker: worker.code, workerMap: worker.map }),
64
77
  };
65
78
  }
66
79
  /**
@@ -1,10 +1,12 @@
1
- import { type Completion } from './checker/checker.ts';
1
+ import { type Completion, type CompletionContext } from './checker/checker.ts';
2
2
  import { type CoreCompileOptions } from './compile.ts';
3
- export type { Completion, CompletionContext } from './checker/checker.ts';
3
+ export type { Completion, CompletionContext, TextEdit } from './checker/checker.ts';
4
4
  export interface CompletionList {
5
5
  /** Offsets of what the chosen item replaces: the part of the name before the cursor. */
6
6
  start: number;
7
7
  end: number;
8
+ /** What the cursor is in; none where nothing is suggested or the check did not reach it. */
9
+ context?: CompletionContext;
8
10
  items: Completion[];
9
11
  }
10
12
  export declare function completeModule(source: string, offset: number, options?: CoreCompileOptions): CompletionList;
@@ -1,9 +1,8 @@
1
- import { check } from "./checker/checker.js";
1
+ import { check, } from "./checker/checker.js";
2
2
  import { prepareCheck } from "./compile.js";
3
3
  import { tokenize } from "./lexer/lexer.js";
4
4
  import { KEYWORDS } from "./lexer/token.js";
5
5
  import { parse } from "./parser/parser.js";
6
- import { RESERVED } from "./parser/syntax.js";
7
6
  import { forEachChild } from "./walk.js";
8
7
  const CURSOR = '$$cursor';
9
8
  const STRINGS = new Set([
@@ -15,10 +14,11 @@ const STRINGS = new Set([
15
14
  'JsxText',
16
15
  'JsxString',
17
16
  'CssText',
17
+ 'RegExp',
18
18
  ]);
19
- const VALUE_KEYWORDS = KEYWORDS.filter((keyword) => !RESERVED.has(keyword));
19
+ const VALUE_KEYWORDS = KEYWORDS;
20
20
  // `queue` is a type of its own only in a type: elsewhere it is a name.
21
- const TYPE_KEYWORDS = ['func', 'comp', 'chan', 'queue'];
21
+ const TYPE_KEYWORDS = ['func', 'comp', 'chan', 'queue', 'topic'];
22
22
  /** Keywords only at the start of a statement at the top level of a component or a decorator. */
23
23
  const COMPONENT_KEYWORDS = ['state', 'derived', 'ref', 'style', 'effect', 'mount'];
24
24
  export function completeModule(source, offset, options = {}) {
@@ -55,7 +55,12 @@ export function completeModule(source, offset, options = {}) {
55
55
  const { program } = parse(text, { markup: options.markup ?? true });
56
56
  const result = completeIn(program, options);
57
57
  if (result) {
58
- return { start, end: offset, items: withKeywords(result, statementKeywords(program)) };
58
+ return {
59
+ start,
60
+ end: offset,
61
+ context: result.context,
62
+ items: withKeywords(result, statementKeywords(program)),
63
+ };
59
64
  }
60
65
  // The cursor is where a name is declared, `let ti|`: nothing to suggest.
61
66
  if (pathTo(program, CURSOR).length > 0)
@@ -137,7 +137,7 @@ class DeclarationPrinter {
137
137
  switch (member.kind) {
138
138
  case 'FieldDeclaration': {
139
139
  const found = (member.isStatic ? info.statics : info.members).get(member.name.name);
140
- const modifiers = this.modifiers(member.visibility, member.isStatic);
140
+ const modifiers = this.modifiers(member.visibility, member.isStatic, member.readonly);
141
141
  // As in the declaration files TypeScript writes, private members have no type.
142
142
  if (member.visibility === 'private')
143
143
  lines.push(`${modifiers}${member.name.name};`);
@@ -147,6 +147,17 @@ class DeclarationPrinter {
147
147
  }
148
148
  case 'ConstructorDeclaration': {
149
149
  const modifiers = this.modifiers(member.visibility, false);
150
+ // The parameters that are properties are declared as fields, as TypeScript writes them.
151
+ member.params.forEach((param, i) => {
152
+ const { property } = param;
153
+ const type = info.ctor?.params[i];
154
+ if (!property)
155
+ return;
156
+ const field = `${this.modifiers(property.visibility, false, property.readonly)}${param.name.name}`;
157
+ lines.push(property.visibility === 'private' || !type
158
+ ? `${field};`
159
+ : `${field}: ${tsType(type)};`);
160
+ });
150
161
  if (info.ctor) {
151
162
  lines.push(`${modifiers}constructor${parameters(member.params, info.ctor)};`);
152
163
  }
@@ -154,8 +165,11 @@ class DeclarationPrinter {
154
165
  }
155
166
  case 'MethodDeclaration': {
156
167
  const found = (member.isStatic ? info.statics : info.members).get(member.name.name);
157
- const modifiers = this.modifiers(member.visibility, member.isStatic);
158
- if (member.visibility === 'private')
168
+ const modifiers = this.modifiers(member.visibility, member.isStatic) +
169
+ (member.abstract ? 'abstract ' : '');
170
+ if (member.accessor)
171
+ lines.push(accessor(member, modifiers, found?.type));
172
+ else if (member.visibility === 'private')
159
173
  lines.push(`${modifiers}${member.name.name}();`);
160
174
  else if (found?.type.kind === 'function') {
161
175
  lines.push(`${modifiers}${member.name.name}${this.signature(member.params, found.type)};`);
@@ -164,10 +178,11 @@ class DeclarationPrinter {
164
178
  }
165
179
  }
166
180
  }
167
- this.lines.push(`${exported}declare class ${jsName(node.name.name)}${this.typeParams(info.typeParams)}${heritage} {`, ...lines.map((line) => ` ${line}`), '}');
181
+ this.lines.push(`${exported}declare ${node.abstract ? 'abstract ' : ''}class ${jsName(node.name.name)}${this.typeParams(info.typeParams)}${heritage} {`, ...lines.map((line) => ` ${line}`), '}');
168
182
  }
169
- modifiers(visibility, isStatic) {
170
- return `${visibility === 'public' ? '' : `${visibility} `}${isStatic ? 'static ' : ''}`;
183
+ modifiers(visibility, isStatic, readonly) {
184
+ return (`${visibility === 'public' ? '' : `${visibility} `}${isStatic ? 'static ' : ''}` +
185
+ (readonly ? 'readonly ' : ''));
171
186
  }
172
187
  /** `<T, U extends Shape>` */
173
188
  typeParams(params) {
@@ -191,6 +206,17 @@ function parameters(params, type) {
191
206
  });
192
207
  return `(${list.join(', ')})`;
193
208
  }
209
+ /** `get area(): number;` or `set area(v: number);`; a private one, as TypeScript writes it, untyped. */
210
+ function accessor(node, modifiers, type) {
211
+ const head = `${modifiers}${node.accessor} ${node.name.name}`;
212
+ const param = jsName(node.params[0]?.name.name ?? 'value');
213
+ if (node.visibility === 'private' || !type) {
214
+ return node.accessor === 'get' ? `${head}();` : `${head}(${param});`;
215
+ }
216
+ return node.accessor === 'get'
217
+ ? `${head}(): ${tsType(type)};`
218
+ : `${head}(${param}: ${tsType(type)});`;
219
+ }
194
220
  /** `Base` or `events.EventEmitter` in `extends`; other expressions cannot be written in types. */
195
221
  function expressionName(node) {
196
222
  if (node.kind === 'Identifier')
@@ -47,7 +47,10 @@ export function renamer(renames, copied, other = {}) {
47
47
  const generic = (node, hidden) => {
48
48
  const result = {};
49
49
  for (const [key, value] of Object.entries(node)) {
50
- if (Array.isArray(value)) {
50
+ // Labels are not values: `outer: for` and `break outer` keep their names.
51
+ if (key === 'label')
52
+ result[key] = value;
53
+ else if (Array.isArray(value)) {
51
54
  result[key] = value.map((item) => (isNode(item) ? copy(item, hidden) : item));
52
55
  }
53
56
  else {
@@ -106,7 +109,7 @@ export function renamer(renames, copied, other = {}) {
106
109
  return done({
107
110
  ...node,
108
111
  params: node.params.map((param) => copy(param, inner)),
109
- body: copy(node.body, inner),
112
+ body: node.body && copy(node.body, inner),
110
113
  }, node);
111
114
  }
112
115
  case 'FieldDeclaration':
@@ -34,4 +34,4 @@ export declare function reach(program: ast.Program, names: Iterable<string>): Re
34
34
  * The program of the worker module: the imports and the declarations that the functions `targets`
35
35
  * reach, unexported, since the worker module is only loaded by the worker.
36
36
  */
37
- export declare function workerProgram(program: ast.Program, targets: readonly string[]): ast.Program;
37
+ export declare function workerProgram(program: ast.Program, targets: readonly string[], dataClasses?: Iterable<string>): ast.Program;
@@ -165,9 +165,10 @@ export function reach(program, names) {
165
165
  * The program of the worker module: the imports and the declarations that the functions `targets`
166
166
  * reach, unexported, since the worker module is only loaded by the worker.
167
167
  */
168
- export function workerProgram(program, targets) {
168
+ export function workerProgram(program, targets, dataClasses = []) {
169
169
  const lifted = { ...program, body: [...program.body, ...literalFunctions(program)] };
170
- const { statements, imports } = reach(lifted, targets);
170
+ // The data classes come too, so that their instances get their class here (runtime/classes.js).
171
+ const { statements, imports } = reach(lifted, [...targets, ...dataClasses]);
171
172
  const body = [];
172
173
  for (const [statement, names] of imports) {
173
174
  body.push({
@@ -229,8 +230,8 @@ function usedNames(statement) {
229
230
  if (member.value)
230
231
  add(freeNames({ params: [], body: block([expression(member.value)]) }));
231
232
  }
232
- else {
233
- add(freeNames(member));
233
+ else if (member.body) {
234
+ add(freeNames({ params: member.params, body: member.body }));
234
235
  }
235
236
  }
236
237
  return names;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { type CompileResult } from './compile.ts';
2
2
  import { type CompletionList } from './completion.ts';
3
+ export { importFixes, type ImportFix } from './auto-import.ts';
3
4
  export type { CompileResult } from './compile.ts';
4
- export type { Completion, CompletionList } from './completion.ts';
5
+ export type { Completion, CompletionList, TextEdit } from './completion.ts';
5
6
  export type { Diagnostic } from './diagnostics.ts';
6
7
  export { formatDiagnostic } from './diagnostics.ts';
7
8
  export { SourceFile } from './source.ts';
@@ -19,6 +20,8 @@ export interface CompileOptions {
19
20
  declarations?: boolean;
20
21
  /** Where the worker module of `go` is, relative to the module; see workerUrl() in compile.ts. */
21
22
  workerUrl?: string;
23
+ /** A module that a worker thread of Node imports first, to load `.rk` files (vite.ts). */
24
+ nodeLoader?: string;
22
25
  /** Markup and components; by default as the package.json of the file says (src/config.ts). */
23
26
  markup?: boolean;
24
27
  /** Import the runtime from "rooklang/runtime", the default, or put it into the module. */
@@ -27,7 +30,8 @@ export interface CompileOptions {
27
30
  export declare function compile(source: string, options?: CompileOptions): CompileResult;
28
31
  /**
29
32
  * What can be written at `offset` in a module, for editors: names, members, types, tags and
30
- * attributes. The imports are read from disk, like those of `compile`.
33
+ * attributes, and after them the names that other modules of the package export, with the imports
34
+ * they need. The imports are read from disk, like those of `compile`.
31
35
  */
32
36
  export declare function complete(source: string, offset: number, options?: {
33
37
  filename?: string;
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
+ import { importCompletions } from "./auto-import.js";
1
2
  import { compileModule, workerUrl, } from "./compile.js";
2
3
  import { completeModule } from "./completion.js";
3
4
  import { markupFor } from "./config.js";
4
5
  import { loadModuleExports } from "./modules.js";
5
6
  import { readFileSync } from 'node:fs';
6
- import { dirname, join, resolve } from 'node:path';
7
+ import { basename, dirname, join, resolve } from 'node:path';
7
8
  import { importDeclarations, libraryFor } from "./typescript/importer.js";
9
+ export { importFixes } from "./auto-import.js";
8
10
  export { formatDiagnostic } from "./diagnostics.js";
9
11
  export { SourceFile } from "./source.js";
10
12
  export function compile(source, options = {}) {
@@ -16,20 +18,27 @@ export function compile(source, options = {}) {
16
18
  markup,
17
19
  rewriteImports,
18
20
  workerUrl: options.workerUrl ?? workerUrl(filename ?? 'main.rk', rewriteImports),
21
+ nodeLoader: options.nodeLoader,
19
22
  typeCheck: options.typeCheck ?? true,
20
23
  declarations: options.declarations ?? false,
21
24
  runtime: options.runtime,
25
+ sourceName: filename === undefined ? undefined : basename(filename),
22
26
  });
23
27
  }
24
28
  /**
25
29
  * What can be written at `offset` in a module, for editors: names, members, types, tags and
26
- * attributes. The imports are read from disk, like those of `compile`.
30
+ * attributes, and after them the names that other modules of the package export, with the imports
31
+ * they need. The imports are read from disk, like those of `compile`.
27
32
  */
28
33
  export function complete(source, offset, options = {}) {
29
- return completeModule(source, offset, {
30
- ...filesOf(options.filename, true, options.markup),
31
- markup: options.markup ?? markupFor(options.filename),
34
+ const { filename } = options;
35
+ const list = completeModule(source, offset, {
36
+ ...filesOf(filename, true, options.markup),
37
+ markup: options.markup ?? markupFor(filename),
32
38
  });
39
+ if (filename === undefined)
40
+ return list;
41
+ return { ...list, items: [...list.items, ...importCompletions(source, filename, list)] };
33
42
  }
34
43
  /** Reading what a module imports: other modules, declarations, CSS modules and the library. */
35
44
  function filesOf(filename, typeCheck, markup) {