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
@@ -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')
@@ -5,7 +5,7 @@ export type CopyListener = (copy: ast.Node, original: ast.Node) => void;
5
5
  export declare function originOf(node: ast.Node): ast.Node;
6
6
  /**
7
7
  * Renames of the names that are not values: tags of components, `<Icon />`, and decorators,
8
- * `get(@visible.shown)`. Code of another module refers to those of its own module.
8
+ * `@visible.shown`. Code of another module refers to those of its own module.
9
9
  */
10
10
  export interface OtherNames {
11
11
  tags?: ReadonlyMap<string, string>;
@@ -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':
@@ -140,6 +143,7 @@ export function renamer(renames, copied, other = {}) {
140
143
  return done({
141
144
  ...node,
142
145
  iterable: copy(node.iterable, hidden),
146
+ ...(node.withKey ? { withKey: copy(node.withKey, scoped(names, hidden)) } : {}),
143
147
  body: copy(node.body, scoped(names, hidden)),
144
148
  }, node);
145
149
  }
@@ -0,0 +1,14 @@
1
+ import type * as ast from './ast.ts';
2
+ import type { DecoratorSource } from './decorators.ts';
3
+ /** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
4
+ export declare function decoratedName(decorator: string, member: string): string;
5
+ /**
6
+ * The public members that a decorator gives the components it is applied to: its own and those of
7
+ * the decorators it includes, each with whether it changes (state and derived values do).
8
+ */
9
+ export declare function publicMembers(decorator: ast.DecoratorDeclaration, decorators: ReadonlyMap<string, DecoratorSource>, seen?: ReadonlySet<ast.DecoratorDeclaration>): Map<string, boolean>;
10
+ /**
11
+ * `@shop.count` of a member that `shop` gets from the `cart` it includes: a variable of `shop`
12
+ * that is that of `cart`, `derived` when it changes, so that what reads it is updated.
13
+ */
14
+ export declare function aliases(included: DecoratorSource, use: ast.DecoratorUse, prefix: string, decorators: ReadonlyMap<string, DecoratorSource>, prefixes: ReadonlyMap<string, string> | undefined): ast.VariableDeclaration[];
@@ -0,0 +1,56 @@
1
+ // The names of the members of decorators in the components they are applied to, and the members
2
+ // that a decorator gives with those of the decorators it includes.
3
+ /** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
4
+ export function decoratedName(decorator, member) {
5
+ return `$$${decorator}$${member}`;
6
+ }
7
+ /**
8
+ * The public members that a decorator gives the components it is applied to: its own and those of
9
+ * the decorators it includes, each with whether it changes (state and derived values do).
10
+ */
11
+ export function publicMembers(decorator, decorators, seen = new Set()) {
12
+ const members = new Map();
13
+ const inner = new Set([...seen, decorator]);
14
+ for (const use of decorator.includes) {
15
+ const included = decorators.get(use.name.name);
16
+ if (!included || inner.has(included.node))
17
+ continue;
18
+ for (const [name, changes] of publicMembers(included.node, decorators, inner)) {
19
+ members.set(name, changes);
20
+ }
21
+ }
22
+ for (const statement of decorator.body.body) {
23
+ if (statement.kind === 'FuncDeclaration' && statement.isPublic) {
24
+ members.set(statement.name.name, false);
25
+ }
26
+ else if (statement.kind === 'VariableDeclaration' && statement.isPublic) {
27
+ const changes = statement.keyword === 'state' || statement.keyword === 'derived';
28
+ for (const name of statement.names)
29
+ members.set(name.name, changes);
30
+ }
31
+ }
32
+ return members;
33
+ }
34
+ /**
35
+ * `@shop.count` of a member that `shop` gets from the `cart` it includes: a variable of `shop`
36
+ * that is that of `cart`, `derived` when it changes, so that what reads it is updated.
37
+ */
38
+ export function aliases(included, use, prefix, decorators, prefixes) {
39
+ const from = prefixes?.get(included.node.name.name) ?? included.node.name.name;
40
+ return [...publicMembers(included.node, decorators)].map(([member, changes]) => ({
41
+ kind: 'VariableDeclaration',
42
+ exported: false,
43
+ isPublic: false,
44
+ keyword: changes ? 'derived' : 'const',
45
+ names: [
46
+ { kind: 'Identifier', name: decoratedName(prefix, member), start: use.start, end: use.end },
47
+ ],
48
+ type: null,
49
+ values: [
50
+ { kind: 'Identifier', name: decoratedName(from, member), start: use.start, end: use.end },
51
+ ],
52
+ body: null,
53
+ start: use.start,
54
+ end: use.end,
55
+ }));
56
+ }
@@ -1,7 +1,6 @@
1
1
  import type * as ast from './ast.ts';
2
2
  import { type CopyListener } from './decorator-copy.ts';
3
- /** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
4
- export declare function decoratedName(decorator: string, member: string): string;
3
+ export { decoratedName, publicMembers } from './decorator-names.ts';
5
4
  /**
6
5
  * Whether a declaration of an applied component gets an argument of a decorator. When the
7
6
  * argument reads the component's properties, the declaration is a `state` that the properties
@@ -45,7 +44,7 @@ export declare function moduleNamesOf(decorator: ast.DecoratorDeclaration, progr
45
44
  * The component with its decorators applied. Its body first declares the arguments of each
46
45
  * decorator and runs its body, from the top decorator down, then runs the component's own body.
47
46
  * The names of a decorator get its prefix, `$$visible$shown`, so they clash neither with each other
48
- * nor with the component's names, and `get(@visible.shown)` reads that variable. The `mount()` of
47
+ * nor with the component's names, and `@visible.shown` reads that variable. The `mount` of
49
48
  * the decorators runs after the component's own, from the inside out, so it goes before every
50
49
  * `return` of the component's body.
51
50
  *
@@ -1,14 +1,12 @@
1
1
  import { renamer } from "./decorator-copy.js";
2
+ import { aliases, decoratedName } from "./decorator-names.js";
2
3
  import { declaredIn, freeNames, mentionsName, topLevelNames } from "./names.js";
3
4
  import { containsReturn } from "./recursion.js";
4
5
  import { forEachChild } from "./walk.js";
5
6
  // Applying decorators: a component with decorators becomes one component whose body runs the
6
7
  // bodies of its decorators first. The checker uses it to see what the inlined code uses, the
7
8
  // generator to write it.
8
- /** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
9
- export function decoratedName(decorator, member) {
10
- return `$$${decorator}$${member}`;
11
- }
9
+ export { decoratedName, publicMembers } from "./decorator-names.js";
12
10
  const argumentDeclarations = new WeakSet();
13
11
  const wrappedContents = new WeakSet();
14
12
  /**
@@ -69,7 +67,7 @@ export function moduleNamesOf(decorator, program) {
69
67
  * The component with its decorators applied. Its body first declares the arguments of each
70
68
  * decorator and runs its body, from the top decorator down, then runs the component's own body.
71
69
  * The names of a decorator get its prefix, `$$visible$shown`, so they clash neither with each other
72
- * nor with the component's names, and `get(@visible.shown)` reads that variable. The `mount()` of
70
+ * nor with the component's names, and `@visible.shown` reads that variable. The `mount` of
73
71
  * the decorators runs after the component's own, from the inside out, so it goes before every
74
72
  * `return` of the component's body.
75
73
  *
@@ -83,37 +81,55 @@ export function moduleNamesOf(decorator, program) {
83
81
  export function decorate(component, decorators, copied, prefixes) {
84
82
  if (component.decorators.length === 0)
85
83
  return component;
86
- const props = new Set(component.params.map((param) => param.name.name));
84
+ // Arguments that read the properties, or such arguments of a decorator that includes another,
85
+ // are state: they change with them.
86
+ const reactive = new Set(component.params.map((param) => param.name.name));
87
87
  const setup = [];
88
88
  const mounts = [];
89
89
  const wrappers = [];
90
- for (const use of component.decorators) {
91
- const source = decorators.get(use.name.name);
92
- if (!source)
93
- continue;
90
+ // `argument` copies an argument into the component: one of the component stays as it is, one of
91
+ // a decorator it includes is that decorator's code.
92
+ const apply = (source, args, argument, applying) => {
94
93
  const decorator = source.node;
94
+ // A decorator that includes itself, through others too, is reported by the checker.
95
+ const inner = new Set([...applying, decorator]);
95
96
  const prefix = prefixes?.get(decorator.name.name) ?? decorator.name.name;
96
97
  const names = [...decoratorNames(decorator), ...source.moduleNames];
97
98
  const renames = membersOf(prefix, names);
98
99
  const copy = renamer(renames, copied, { decorators: prefixes, tags: source.tags });
99
100
  decorator.params.forEach((param, i) => {
100
- // A default value is the decorator's code, an argument the component's.
101
- const value = use.arguments[i] ?? (param.defaultValue && copy(param.defaultValue, new Set()));
101
+ const given = args[i];
102
+ // A default value is the decorator's code.
103
+ const value = given
104
+ ? argument(given)
105
+ : param.defaultValue && copy(param.defaultValue, new Set());
106
+ const name = renames.get(param.name.name);
107
+ const changes = value !== null && value !== undefined && mentionsName(value, reactive);
102
108
  const declaration = {
103
109
  kind: 'VariableDeclaration',
104
110
  exported: false,
105
111
  isPublic: false,
106
- keyword: value && mentionsName(value, props) ? 'state' : 'const',
107
- names: [{ ...param.name, name: renames.get(param.name.name) }],
112
+ keyword: changes ? 'state' : 'const',
113
+ names: [{ ...param.name, name }],
108
114
  type: param.type,
109
115
  values: value ? [value] : [],
116
+ body: null,
110
117
  start: param.start,
111
118
  end: param.end,
112
119
  };
120
+ if (changes)
121
+ reactive.add(name);
113
122
  argumentDeclarations.add(declaration);
114
123
  copied?.(declaration, param);
115
124
  setup.push(declaration);
116
125
  });
126
+ for (const use of decorator.includes) {
127
+ const included = decorators.get(use.name.name);
128
+ if (!included || inner.has(included.node))
129
+ continue;
130
+ apply(included, use.arguments, (value) => copy(value, new Set()), inner);
131
+ setup.push(...aliases(included, use, prefix, decorators, prefixes));
132
+ }
117
133
  const wrapper = wrapperOf(decorator);
118
134
  const own = [];
119
135
  for (const statement of decorator.body.body) {
@@ -128,6 +144,11 @@ export function decorate(component, decorators, copied, prefixes) {
128
144
  mounts.push(own);
129
145
  if (wrapper)
130
146
  wrappers.push(applyWrapper(source, wrapper, copied, prefix, prefixes));
147
+ };
148
+ for (const use of component.decorators) {
149
+ const source = decorators.get(use.name.name);
150
+ if (source)
151
+ apply(source, use.arguments, (value) => value, new Set());
131
152
  }
132
153
  const inside = mounts.reverse().flat();
133
154
  const chain = wrappers.reverse();
@@ -186,6 +207,7 @@ function wrapped(node, chain) {
186
207
  names: [{ ...wrapper.content }],
187
208
  type: wrapper.type,
188
209
  values: [value],
210
+ body: null,
189
211
  start: node.start,
190
212
  end: node.end,
191
213
  };
@@ -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) {
@@ -27,6 +27,7 @@ export interface ModulePackages {
27
27
  * recursive or a web component cannot be inlined, and neither can one that reaches components or
28
28
  * decorators the module imports: those would bring the names of a third module. Nor can code that
29
29
  * uses a component of the module as a value, which is the function in the module, or that assigns
30
- * a variable of the module: another module could only read it.
30
+ * a variable of the module: another module could only read it. Nor can code that reads state of
31
+ * the module, `state items` at its top level: it listens to the changes there.
31
32
  */
32
33
  export declare function inlinePackages(program: ast.Program): ModulePackages;
@@ -7,7 +7,8 @@ import { forEachChild } from "./walk.js";
7
7
  * recursive or a web component cannot be inlined, and neither can one that reaches components or
8
8
  * decorators the module imports: those would bring the names of a third module. Nor can code that
9
9
  * uses a component of the module as a value, which is the function in the module, or that assigns
10
- * a variable of the module: another module could only read it.
10
+ * a variable of the module: another module could only read it. Nor can code that reads state of
11
+ * the module, `state items` at its top level: it listens to the changes there.
11
12
  */
12
13
  export function inlinePackages(program) {
13
14
  const components = new Map();
@@ -30,9 +31,13 @@ export function inlinePackages(program) {
30
31
  };
31
32
  const topLevel = topLevelNames(program);
32
33
  const usesComponentValues = (node) => componentValues(node, (name) => components.has(name)).length > 0;
33
- const assignsModuleNames = (node) => {
34
+ const states = new Set(program.body.flatMap((statement) => statement.kind === 'VariableDeclaration' && statement.keyword === 'state'
35
+ ? statement.names.map((name) => name.name)
36
+ : []));
37
+ // It assigns a variable of the module or reads its state, which only the module can do.
38
+ const needsItsModule = (node) => {
34
39
  const assigned = assignedNames(node);
35
- return [...freeNames(node)].some((name) => topLevel.has(name) && assigned.has(name));
40
+ return [...freeNames(node)].some((name) => topLevel.has(name) && (assigned.has(name) || states.has(name)));
36
41
  };
37
42
  /** The package of a component, or of the tags in the code of a decorator. */
38
43
  const build = (root, tags) => {
@@ -69,7 +74,7 @@ export function inlinePackages(program) {
69
74
  found.components.set(current, component);
70
75
  if (usesComponentValues(expanded.get(current)))
71
76
  return false;
72
- if (assignsModuleNames(expanded.get(current)))
77
+ if (needsItsModule(expanded.get(current)))
73
78
  return false;
74
79
  for (const use of component.decorators) {
75
80
  const source = sources.get(use.name.name);
@@ -10,6 +10,8 @@ export declare abstract class LexerBase {
10
10
  constructor(text: string);
11
11
  /** Skips whitespace and comments; returns whether a line break was skipped. */
12
12
  protected skipTrivia(): boolean;
13
+ /** After a value `/` divides; elsewhere it starts a regular expression, as in JS. */
14
+ protected lastEndsValue(): boolean;
13
15
  protected lastEndsStatement(): boolean;
14
16
  /**
15
17
  * Unlike Go, a line starting with `.` / `?.` continues a method chain, and a line starting with
@@ -1,4 +1,4 @@
1
- import { ENDS_STATEMENT, isDigit } from "./tables.js";
1
+ import { ENDS_STATEMENT, ENDS_VALUE, isDigit } from "./tables.js";
2
2
  // The lexer is a chain of layers: base → literals → markup → style → Lexer (lexer.ts). This layer
3
3
  // keeps the position and the modes (code, markup tags, markup content, CSS), skips spaces and
4
4
  // comments, and inserts semicolons at the ends of lines.
@@ -48,6 +48,11 @@ export class LexerBase {
48
48
  }
49
49
  return sawNewline;
50
50
  }
51
+ /** After a value `/` divides; elsewhere it starts a regular expression, as in JS. */
52
+ lastEndsValue() {
53
+ const last = this.tokens.at(-1);
54
+ return last !== undefined && ENDS_VALUE.has(last.kind);
55
+ }
51
56
  lastEndsStatement() {
52
57
  const last = this.tokens.at(-1);
53
58
  return last !== undefined && ENDS_STATEMENT.has(last.kind);
@@ -71,6 +71,9 @@ class Lexer extends StyleLexer {
71
71
  this.push('}', start, start + 1);
72
72
  this.modes.pop();
73
73
  }
74
+ else if (c === '/' && !this.lastEndsValue()) {
75
+ this.scanRegExp(start);
76
+ }
74
77
  else if (c === '<' && this.startsElement()) {
75
78
  this.push('JsxTagOpen', start, start + 1);
76
79
  this.modes.push({ kind: 'tag', start });
@@ -1,6 +1,11 @@
1
1
  import { LexerBase } from './base.ts';
2
2
  import { type CharTest } from './tables.ts';
3
3
  export declare abstract class LiteralLexer extends LexerBase {
4
+ /**
5
+ * `/ab+c/gi`: the pattern goes up to a `/` that is not escaped or inside `[...]`, on one line,
6
+ * as in JS. JS checks the pattern when it loads the module, so an invalid one is an error here.
7
+ */
8
+ protected scanRegExp(start: number): void;
4
9
  protected scanNumber(start: number): void;
5
10
  /** Consumes digits and `_` separators starting at `pos`; returns the end offset. */
6
11
  protected scanDigits(pos: number, isValid: CharTest): number;
@@ -1,7 +1,57 @@
1
1
  import { LexerBase } from "./base.js";
2
2
  import { IDENTIFIER, SIMPLE_ESCAPES, isDigit, isHexDigit, isOctalDigit, isBinaryDigit, } from "./tables.js";
3
- // Numbers, strings, template strings and escapes.
3
+ // Numbers, strings, template strings, regular expressions and escapes.
4
+ /** The flags of a regular expression, each at most once. */
5
+ const FLAGS = /^(?!.*(.).*\1)[dgimsuvy]*$/;
4
6
  export class LiteralLexer extends LexerBase {
7
+ /**
8
+ * `/ab+c/gi`: the pattern goes up to a `/` that is not escaped or inside `[...]`, on one line,
9
+ * as in JS. JS checks the pattern when it loads the module, so an invalid one is an error here.
10
+ */
11
+ scanRegExp(start) {
12
+ const t = this.text;
13
+ let pos = start + 1;
14
+ let inClass = false;
15
+ while (pos < t.length && t[pos] !== '\n' && (inClass || t[pos] !== '/')) {
16
+ if (t[pos] === '\\' && t[pos + 1] !== '\n')
17
+ pos++;
18
+ else if (t[pos] === '[')
19
+ inClass = true;
20
+ else if (t[pos] === ']')
21
+ inClass = false;
22
+ pos++;
23
+ }
24
+ const pattern = t.slice(start + 1, pos);
25
+ if (t[pos] !== '/') {
26
+ this.error('unterminated regular expression: end it with "/"', start, pos);
27
+ this.tokens.push({
28
+ kind: 'RegExp',
29
+ text: t.slice(start, pos),
30
+ value: pattern,
31
+ start,
32
+ end: pos,
33
+ });
34
+ this.pos = pos;
35
+ return;
36
+ }
37
+ IDENTIFIER.lastIndex = pos + 1;
38
+ const flags = IDENTIFIER.exec(t)?.[0] ?? '';
39
+ const end = pos + 1 + flags.length;
40
+ this.tokens.push({ kind: 'RegExp', text: t.slice(start, end), value: pattern, start, end });
41
+ this.pos = end;
42
+ if (!FLAGS.test(flags) || (flags.includes('u') && flags.includes('v'))) {
43
+ this.error(`invalid flags "${flags}" of a regular expression: each of d, g, i, m, s, u, v and y goes once, and not u with v`, pos + 1, end);
44
+ return;
45
+ }
46
+ try {
47
+ new RegExp(pattern, flags);
48
+ }
49
+ catch (error) {
50
+ // "Invalid regular expression: /(/: Unterminated group" → "unterminated group".
51
+ const reason = error.message.replace(/^.*: /, '');
52
+ this.error(`invalid regular expression: ${reason.charAt(0).toLowerCase()}${reason.slice(1)}`, start, end);
53
+ }
54
+ }
5
55
  scanNumber(start) {
6
56
  const t = this.text;
7
57
  let pos = start;
@@ -1,14 +1,13 @@
1
1
  import { LiteralLexer } from "./literals.js";
2
- import { ENDS_VALUE, JSX_NAME, decodeEntities } from "./tables.js";
2
+ import { JSX_NAME, decodeEntities } from "./tables.js";
3
3
  // Markup: where an element starts, tags with their attributes, and text content.
4
4
  export class MarkupLexer extends LiteralLexer {
5
5
  /** `<` starts markup where a value is expected and is "less than" after a value. */
6
6
  startsElement() {
7
- const last = this.tokens.at(-1);
8
- if (last && ENDS_VALUE.has(last.kind))
7
+ if (this.lastEndsValue())
9
8
  return false;
10
9
  // `chan<number>(10)` makes a channel: its `<` opens the type of the values.
11
- if (last?.kind === 'chan')
10
+ if (this.tokens.at(-1)?.kind === 'chan')
12
11
  return false;
13
12
  const next = this.text[this.pos + 1];
14
13
  return next === '>' || (next !== undefined && /^[\p{ID_Start}$_]$/u.test(next));
@@ -8,6 +8,7 @@ export const ENDS_STATEMENT = new Set([
8
8
  'Number',
9
9
  'BigInt',
10
10
  'String',
11
+ 'RegExp',
11
12
  'Template',
12
13
  'TemplateTail',
13
14
  'true',
@@ -34,6 +35,7 @@ export const ENDS_VALUE = new Set([
34
35
  'Number',
35
36
  'BigInt',
36
37
  'String',
38
+ 'RegExp',
37
39
  'Template',
38
40
  'TemplateTail',
39
41
  'true',
@@ -1,4 +1,4 @@
1
- export declare const KEYWORDS: readonly ["func", "comp", "go", "chan", "return", "let", "const", "type", "interface", "class", "extends", "new", "this", "super", "if", "else", "for", "in", "break", "continue", "switch", "case", "default", "defer", "try", "catch", "finally", "throw", "import", "export", "null", "true", "false", "typeof", "instanceof", "async", "await", "map"];
1
+ export declare const KEYWORDS: readonly ["func", "comp", "go", "chan", "return", "let", "const", "type", "interface", "class", "extends", "new", "this", "super", "if", "else", "for", "in", "break", "continue", "switch", "case", "default", "defer", "try", "catch", "finally", "throw", "import", "export", "null", "true", "false", "typeof", "instanceof", "async", "await"];
2
2
  export declare const PUNCTUATORS: readonly ["{", "}", "(", ")", "[", "]", ";", ",", ":", ".", "...", "?", "?.", "??", "=>", "=", "+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>=", "&&=", "||=", "??=", "==", "!=", "<", "<-", ">", "<=", ">=", "+", "-", "*", "/", "%", "**", "++", "--", "&", "|", "^", "~", "<<", ">>", ">>>", "&&", "||", "!", "@"];
3
3
  export type Keyword = (typeof KEYWORDS)[number];
4
4
  export type Punctuator = (typeof PUNCTUATORS)[number];
@@ -7,9 +7,10 @@ export type Punctuator = (typeof PUNCTUATORS)[number];
7
7
  * substitutions: `a${x}b${y}c` becomes TemplateHead("a") x TemplateMiddle("b") y TemplateTail("c"),
8
8
  * and a template without substitutions is a single Template token. In markup, JsxText is the text
9
9
  * between tags and JsxString an attribute value in quotes; both have HTML entities decoded. CssText is
10
- * the raw text of a `style { ... }` block between the values from code.
10
+ * the raw text of a `style { ... }` block between the values from code. RegExp is `/ab+c/g`, whose
11
+ * value is the pattern as it is written, without the slashes and the flags.
11
12
  */
12
- export type StringKind = 'String' | 'Template' | 'TemplateHead' | 'TemplateMiddle' | 'TemplateTail' | 'JsxText' | 'JsxString' | 'CssText';
13
+ export type StringKind = 'String' | 'Template' | 'TemplateHead' | 'TemplateMiddle' | 'TemplateTail' | 'JsxText' | 'JsxString' | 'CssText' | 'RegExp';
13
14
  /**
14
15
  * Tokens of markup such as `<a href="/">text</a>`: JsxTagOpen `<`, JsxCloseTagOpen `</`, JsxName
15
16
  * (tag and attribute names, which may contain `-` and `:`), JsxTagEnd `>` and JsxSelfClose `/>`.