rooklang 0.4.2 → 0.5.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 (100) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/CHANGELOG.ru.md +235 -0
  3. package/dist/aliases.d.ts +2 -0
  4. package/dist/aliases.js +83 -0
  5. package/dist/ast/declarations.d.ts +18 -0
  6. package/dist/ast/expressions.d.ts +14 -1
  7. package/dist/ast/markup.d.ts +2 -0
  8. package/dist/ast/statements.d.ts +2 -2
  9. package/dist/checker/assignments.js +2 -1
  10. package/dist/checker/base.d.ts +3 -0
  11. package/dist/checker/base.js +1 -1
  12. package/dist/checker/builtins.js +5 -1
  13. package/dist/checker/calls.d.ts +2 -0
  14. package/dist/checker/calls.js +18 -3
  15. package/dist/checker/classes.js +8 -0
  16. package/dist/checker/component-bindings.d.ts +16 -0
  17. package/dist/checker/component-bindings.js +65 -2
  18. package/dist/checker/context.d.ts +12 -0
  19. package/dist/checker/declarations.d.ts +2 -0
  20. package/dist/checker/declarations.js +38 -3
  21. package/dist/checker/expressions.d.ts +1 -0
  22. package/dist/checker/expressions.js +17 -1
  23. package/dist/checker/flow.d.ts +1 -1
  24. package/dist/checker/flow.js +10 -0
  25. package/dist/checker/functions.d.ts +2 -0
  26. package/dist/checker/functions.js +33 -7
  27. package/dist/checker/goroutines.d.ts +2 -2
  28. package/dist/checker/goroutines.js +2 -2
  29. package/dist/checker/imports.d.ts +10 -0
  30. package/dist/checker/imports.js +73 -6
  31. package/dist/checker/initializers.d.ts +12 -0
  32. package/dist/checker/initializers.js +80 -0
  33. package/dist/checker/loops.js +3 -1
  34. package/dist/checker/members.js +1 -0
  35. package/dist/checker/resolution.d.ts +2 -0
  36. package/dist/checker/resolution.js +13 -2
  37. package/dist/checker/statements.d.ts +5 -0
  38. package/dist/checker/statements.js +26 -3
  39. package/dist/checker/tagged-unions.d.ts +6 -0
  40. package/dist/checker/tagged-unions.js +31 -1
  41. package/dist/checker/tags.d.ts +6 -0
  42. package/dist/checker/tags.js +44 -0
  43. package/dist/checker/types/constructors.js +10 -1
  44. package/dist/checker/types/params.js +33 -7
  45. package/dist/checker/types/printing.js +5 -1
  46. package/dist/checker/types/relations.js +2 -1
  47. package/dist/codegen/calls.d.ts +2 -0
  48. package/dist/codegen/calls.js +23 -1
  49. package/dist/codegen/component-props.js +8 -1
  50. package/dist/codegen/control-flow.d.ts +3 -0
  51. package/dist/codegen/control-flow.js +17 -0
  52. package/dist/codegen/expressions.d.ts +4 -1
  53. package/dist/codegen/expressions.js +4 -15
  54. package/dist/codegen/functions.d.ts +2 -0
  55. package/dist/codegen/functions.js +13 -0
  56. package/dist/codegen/mutations.d.ts +6 -0
  57. package/dist/codegen/mutations.js +36 -0
  58. package/dist/codegen/options.d.ts +2 -0
  59. package/dist/codegen/reactive.js +9 -5
  60. package/dist/codegen/simple-statements.js +3 -1
  61. package/dist/codegen/statements.d.ts +4 -0
  62. package/dist/codegen/statements.js +37 -1
  63. package/dist/compile.js +2 -0
  64. package/dist/completion.js +2 -1
  65. package/dist/declarations.js +18 -0
  66. package/dist/lexer/base.d.ts +3 -2
  67. package/dist/lexer/base.js +14 -5
  68. package/dist/lexer/lexer.js +3 -0
  69. package/dist/lexer/token.d.ts +6 -1
  70. package/dist/lexer/token.js +0 -1
  71. package/dist/modules.js +4 -3
  72. package/dist/parser/base.d.ts +5 -0
  73. package/dist/parser/base.js +9 -1
  74. package/dist/parser/classes.js +4 -3
  75. package/dist/parser/declarations.d.ts +2 -0
  76. package/dist/parser/declarations.js +25 -2
  77. package/dist/parser/decorators.js +1 -1
  78. package/dist/parser/expressions.js +6 -0
  79. package/dist/parser/literals.d.ts +2 -4
  80. package/dist/parser/literals.js +12 -24
  81. package/dist/parser/objects.d.ts +8 -0
  82. package/dist/parser/objects.js +51 -0
  83. package/dist/parser/params.d.ts +37 -4
  84. package/dist/parser/params.js +143 -9
  85. package/dist/parser/postfix.d.ts +5 -0
  86. package/dist/parser/postfix.js +36 -0
  87. package/dist/parser/simple-statements.d.ts +2 -0
  88. package/dist/parser/simple-statements.js +50 -0
  89. package/dist/parser/statements.js +8 -6
  90. package/dist/parser/syntax.js +0 -1
  91. package/dist/parser/types.d.ts +0 -4
  92. package/dist/parser/types.js +17 -32
  93. package/dist/project-modules.js +21 -0
  94. package/dist/reexports.d.ts +4 -0
  95. package/dist/reexports.js +56 -0
  96. package/dist/typescript/importer.d.ts +4 -0
  97. package/dist/typescript/importer.js +27 -3
  98. package/dist/typescript/library.js +5 -2
  99. package/dist/walk.js +12 -0
  100. package/package.json +4 -2
@@ -78,9 +78,17 @@ export class TypeParser extends ClassParser {
78
78
  this.next();
79
79
  this.expect('(');
80
80
  const names = [];
81
- const params = this.parseList(')', () => this.parseParamType(names));
81
+ const params = this.groupParams(this.parseList(')', () => this.parseParamType(names)), names);
82
+ const rest = this.restOf(params, names);
82
83
  const results = this.parseResults();
83
- return { kind: 'FuncType', params, results, ...iterator(names), ...this.span(start) };
84
+ return {
85
+ kind: 'FuncType',
86
+ params,
87
+ ...(rest ? { rest } : {}),
88
+ results,
89
+ ...iterator(names),
90
+ ...this.span(start),
91
+ };
84
92
  }
85
93
  // `chan T`, `chan<- T` that only sends, `<-chan T` that only receives.
86
94
  case 'chan': {
@@ -133,7 +141,7 @@ export class TypeParser extends ClassParser {
133
141
  parseParenthesizedType() {
134
142
  const start = this.next().start;
135
143
  const names = [];
136
- const params = this.parseList(')', () => this.parseParamType(names));
144
+ const params = this.groupParams(this.parseList(')', () => this.parseParamType(names)), names);
137
145
  if (this.accept('=>')) {
138
146
  const result = this.parseType();
139
147
  const voids = result.kind === 'TypeReference' && result.name.name === 'void';
@@ -156,39 +164,16 @@ export class TypeParser extends ClassParser {
156
164
  end: start,
157
165
  });
158
166
  }
159
- /** The type of a parameter of a function type, after its name if it has one, kept in `names`. */
160
- parseParamType(names) {
161
- names.push(this.check('Identifier') && this.startsNamedParam() ? this.next().text : '');
162
- return this.parseType();
163
- }
164
- /** `paths []string` in `(paths []string) => Shape`: a name before the type of a parameter. */
165
- startsNamedParam() {
166
- const next = this.peek(1);
167
- switch (next.kind) {
168
- case 'Identifier':
169
- case '?':
170
- case '{':
171
- case '(':
172
- case 'func':
173
- case 'chan':
174
- case '<-':
175
- case 'String':
176
- case 'Number':
177
- return true;
178
- // `x []number` is named.
179
- case '[':
180
- return this.peek(2).kind === ']';
181
- default:
182
- return false;
183
- }
184
- }
185
167
  /** `{ name string; x, y number; area() number }`, separated by newlines, `;` or `,`. */
186
168
  parseTypeMembers() {
187
169
  this.expect('{');
188
170
  const members = [];
189
171
  this.parseSeparated(() => this.check('}'), () => {
190
172
  members.push(...this.parseTypeMember());
191
- if (!this.accept(',') && !this.accept(';') && !this.check('}')) {
173
+ if (!this.accept(',') &&
174
+ !this.accept(';') &&
175
+ !this.check('}') &&
176
+ !this.afterTypeArguments()) {
192
177
  this.fail(`expected newline, ";" or "}" after member, found ${describe(this.peek())}`);
193
178
  }
194
179
  });
@@ -210,8 +195,8 @@ export class TypeParser extends ClassParser {
210
195
  }
211
196
  const name = this.parsePropertyName('member name');
212
197
  if (this.check('(')) {
213
- const params = this.parseParams(false);
214
- const results = this.parseResults();
198
+ const params = this.parseParams(false, { rest: 'typed' });
199
+ const results = this.parseResults(true);
215
200
  return [{ kind: 'MethodSignature', name, params, results, ...this.span(start) }];
216
201
  }
217
202
  // `x, y number` declares several properties of the same type.
@@ -7,6 +7,8 @@ const SKIPPED = new Set(['node_modules', 'dist', 'build', 'out', 'coverage']);
7
7
  /** A bound on the files read, for a file opened in a directory as big as the home directory. */
8
8
  const MAX_FILES = 5000;
9
9
  const parsed = new Map();
10
+ /** The modules being read, for `export ... from` that go round in a cycle. */
11
+ const reading = new Set();
10
12
  /** The names exported by the other `.rk` modules of the package of `filename`. */
11
13
  export function projectExports(filename) {
12
14
  const self = resolve(filename);
@@ -69,7 +71,10 @@ function exportsOf(path) {
69
71
  const cached = parsed.get(path);
70
72
  if (cached?.version === version)
71
73
  return cached.names;
74
+ if (reading.has(path))
75
+ return [];
72
76
  let names;
77
+ reading.add(path);
73
78
  try {
74
79
  const { program } = parse(readFileSync(path, 'utf8'), { markup: markupFor(path) });
75
80
  names = program.body.flatMap((statement) => exported(statement, path));
@@ -77,11 +82,16 @@ function exportsOf(path) {
77
82
  catch {
78
83
  names = [];
79
84
  }
85
+ finally {
86
+ reading.delete(path);
87
+ }
80
88
  parsed.set(path, { version, names });
81
89
  return names;
82
90
  }
83
91
  function exported(statement, path) {
84
92
  const name = (node, kind, spaces) => ({ name: node.name, kind, spaces, path });
93
+ if (statement.kind === 'ExportFromDeclaration')
94
+ return reexported(statement, path);
85
95
  if (!('exported' in statement) || !statement.exported)
86
96
  return [];
87
97
  switch (statement.kind) {
@@ -104,3 +114,14 @@ function exported(statement, path) {
104
114
  return [];
105
115
  }
106
116
  }
117
+ /** `export { Rows } from "./rows.rk"`: the names of that module, as this one exports them. */
118
+ function reexported(statement, path) {
119
+ if (!statement.source.value.endsWith('.rk'))
120
+ return [];
121
+ const names = exportsOf(resolve(dirname(path), statement.source.value));
122
+ if (!statement.specifiers)
123
+ return names.map((each) => ({ ...each, path }));
124
+ return statement.specifiers.flatMap(({ imported, local }) => names
125
+ .filter((each) => each.name === imported.name)
126
+ .map((each) => ({ ...each, name: local.name, path })));
127
+ }
@@ -0,0 +1,4 @@
1
+ import type * as ast from './ast.ts';
2
+ import type { ImportResult } from './checker/checker.ts';
3
+ /** The JS names that each `export { ... } from` passes on, `name` or `name as other`. */
4
+ export declare function reexportedNames(program: ast.Program, importModule: ((specifier: string) => ImportResult) | undefined): Map<ast.ExportFromDeclaration, string[]>;
@@ -0,0 +1,56 @@
1
+ import { packageExports } from "./codegen/imported.js";
2
+ import { listenersName } from "./codegen/shared.js";
3
+ import { decoratedName } from "./decorators.js";
4
+ // `export { Rows } from "./rows.rk"` in JS: a component or a decorator goes with the hidden names
5
+ // that the modules using it import from where they import it (codegen/imported.ts), and state with
6
+ // its listeners. `export *` passes them all on by itself.
7
+ /** The JS names that each `export { ... } from` passes on, `name` or `name as other`. */
8
+ export function reexportedNames(program, importModule) {
9
+ const found = new Map();
10
+ for (const statement of program.body) {
11
+ if (statement.kind !== 'ExportFromDeclaration' || !statement.specifiers)
12
+ continue;
13
+ const result = statement.source.value.endsWith('.rk')
14
+ ? importModule?.(statement.source.value)
15
+ : undefined;
16
+ const exports = result && 'exports' in result ? result.exports : undefined;
17
+ const names = [];
18
+ const add = (name, as = name) => names.push(name === as ? name : `${name} as ${as}`);
19
+ for (const { imported, local } of statement.specifiers) {
20
+ const name = imported.name;
21
+ // Without the types of the module, the name is passed on as it is written.
22
+ if (!exports) {
23
+ add(name, local.name);
24
+ continue;
25
+ }
26
+ const component = exports.components?.get(name);
27
+ const decorator = exports.decorators?.get(name);
28
+ if (exports.values.has(name))
29
+ add(name, local.name);
30
+ if (exports.shared?.has(name))
31
+ add(listenersName(name), listenersName(local.name));
32
+ if (component) {
33
+ add(`$$${name}`);
34
+ for (const hidden of packageNames(name, component.inline))
35
+ add(hidden);
36
+ }
37
+ if (decorator) {
38
+ for (const used of decorator.moduleNames)
39
+ add(decoratedName(name, used));
40
+ for (const hidden of packageNames(name, decorator.inline))
41
+ add(hidden);
42
+ }
43
+ }
44
+ found.set(statement, [...new Set(names)]);
45
+ }
46
+ return found;
47
+ }
48
+ /** The hidden names that inlining the component or decorator `root` imports from its module. */
49
+ function packageNames(root, inline) {
50
+ if (!inline)
51
+ return [];
52
+ return [
53
+ ...packageExports(new Map([[root, inline]]), (name) => name).keys(),
54
+ ...[...inline.functions.keys()].map((other) => `$$${other}`),
55
+ ];
56
+ }
@@ -14,6 +14,8 @@ export declare class DeclarationImporter {
14
14
  private readonly shared;
15
15
  private libraryTypes;
16
16
  private readonly results;
17
+ /** The paths of each tsconfig.json, by its path. */
18
+ private readonly configPaths;
17
19
  /** The exports of the module `specifier` imported from the file `fromFile`. */
18
20
  import(fromFile: string, specifier: string): ImportResult;
19
21
  /**
@@ -23,6 +25,8 @@ export declare class DeclarationImporter {
23
25
  library(fromFile: string): Library | undefined;
24
26
  private loadTypeScript;
25
27
  private resolve;
28
+ /** `paths` of the nearest tsconfig.json, `@/lib/*`, which imports of the file may use. */
29
+ private pathsFor;
26
30
  private addTypeRoots;
27
31
  private options;
28
32
  /**
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
- import { dirname, join } from 'node:path';
3
+ import { dirname, join, resolve } from 'node:path';
4
4
  import { TypeConverter } from "./convert.js";
5
5
  import { LibraryTypes } from "./library.js";
6
6
  // Types of imports that are not `.rk` modules: npm packages, `node:` modules and `.ts` files.
@@ -22,6 +22,8 @@ export class DeclarationImporter {
22
22
  shared = new Map();
23
23
  libraryTypes;
24
24
  results = new Map();
25
+ /** The paths of each tsconfig.json, by its path. */
26
+ configPaths = new Map();
25
27
  /** The exports of the module `specifier` imported from the file `fromFile`. */
26
28
  import(fromFile, specifier) {
27
29
  const key = `${dirname(fromFile)}\0${specifier}`;
@@ -65,7 +67,8 @@ export class DeclarationImporter {
65
67
  }
66
68
  resolve(ts, fromFile, specifier) {
67
69
  this.addTypeRoots(fromFile);
68
- const { resolvedModule } = ts.resolveModuleName(specifier, fromFile, this.options(ts), ts.sys);
70
+ const options = { ...this.options(ts), ...this.pathsFor(ts, fromFile) };
71
+ const { resolvedModule } = ts.resolveModuleName(specifier, fromFile, options, ts.sys);
69
72
  if (resolvedModule) {
70
73
  const file = resolvedModule.resolvedFileName;
71
74
  // A JS file without declarations next to it.
@@ -78,7 +81,9 @@ export class DeclarationImporter {
78
81
  const { program, converter } = this.current(ts);
79
82
  const source = program.getSourceFile(file);
80
83
  const module = source && program.getTypeChecker().getSymbolAtLocation(source);
81
- return module ? { exports: converter.moduleExports(module) } : undefined;
84
+ if (module)
85
+ return { exports: converter.moduleExports(module) };
86
+ // `@types/bun` only refers to `bun-types`, whose `declare module "bun"` declares the module.
82
87
  }
83
88
  // `node:fs`, `fs`: modules declared with `declare module "..."`, as in @types/node.
84
89
  const { program, converter } = this.current(ts);
@@ -89,6 +94,25 @@ export class DeclarationImporter {
89
94
  .find((module) => module.name === name);
90
95
  return ambient ? { exports: converter.moduleExports(ambient) } : undefined;
91
96
  }
97
+ /** `paths` of the nearest tsconfig.json, `@/lib/*`, which imports of the file may use. */
98
+ pathsFor(ts, fromFile) {
99
+ const config = ts.findConfigFile(dirname(fromFile), (path) => ts.sys.fileExists(path));
100
+ if (!config)
101
+ return {};
102
+ let options = this.configPaths.get(config);
103
+ if (!options) {
104
+ const { config: json } = ts.readConfigFile(config, (path) => ts.sys.readFile(path));
105
+ const compilerOptions = json?.compilerOptions;
106
+ options = compilerOptions?.paths
107
+ ? {
108
+ paths: compilerOptions.paths,
109
+ baseUrl: resolve(dirname(config), compilerOptions.baseUrl ?? '.'),
110
+ }
111
+ : {};
112
+ this.configPaths.set(config, options);
113
+ }
114
+ return options;
115
+ }
92
116
  addTypeRoots(fromFile) {
93
117
  for (let directory = dirname(fromFile);; directory = dirname(directory)) {
94
118
  const types = join(directory, 'node_modules', '@types');
@@ -16,8 +16,11 @@ export class LibraryTypes {
16
16
  const lib = program.getSourceFile(ts.getDefaultLibFilePath(program.getCompilerOptions()));
17
17
  if (!lib)
18
18
  return;
19
- for (const symbol of this.checker.getSymbolsInScope(lib, ts.SymbolFlags.Value)) {
20
- this.values.set(symbol.name, symbol);
19
+ // `declare global { export import Bun = BunModule }` of bun-types is an alias of a module.
20
+ for (const symbol of this.checker.getSymbolsInScope(lib, ts.SymbolFlags.Value | ts.SymbolFlags.Alias)) {
21
+ const value = symbol.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(symbol) : symbol;
22
+ if (value.flags & ts.SymbolFlags.Value)
23
+ this.values.set(symbol.name, value);
21
24
  }
22
25
  for (const symbol of this.checker.getSymbolsInScope(lib, ts.SymbolFlags.Type)) {
23
26
  this.types.set(symbol.name, symbol);
package/dist/walk.js CHANGED
@@ -14,6 +14,11 @@ export function forEachChild(node, visit) {
14
14
  case 'BlockStatement':
15
15
  each(node.body);
16
16
  break;
17
+ case 'ExportFromDeclaration':
18
+ if (node.specifiers)
19
+ each(node.specifiers);
20
+ visit(node.source);
21
+ break;
17
22
  case 'ImportDeclaration':
18
23
  optional(node.defaultImport);
19
24
  optional(node.namespaceImport);
@@ -182,6 +187,13 @@ export function forEachChild(node, visit) {
182
187
  case 'StyleStatement':
183
188
  each(node.parts);
184
189
  break;
190
+ case 'NonNullExpression':
191
+ visit(node.expression);
192
+ break;
193
+ case 'TaggedTemplateExpression':
194
+ visit(node.tag);
195
+ visit(node.quasi);
196
+ break;
185
197
  case 'TemplateLiteral':
186
198
  node.quasis.forEach((quasi, i) => {
187
199
  visit(quasi);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rooklang",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "A language with JavaScript semantics, Go-style syntax, goroutines on real threads and static types that compiles to readable JavaScript",
5
5
  "keywords": [
6
6
  "rooklang",
@@ -60,7 +60,9 @@
60
60
  },
61
61
  "files": [
62
62
  "dist",
63
- "src/runtime/*.js"
63
+ "src/runtime/*.js",
64
+ "CHANGELOG.md",
65
+ "CHANGELOG.ru.md"
64
66
  ],
65
67
  "scripts": {
66
68
  "rook": "node src/cli.ts",