rooklang 0.4.1 → 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.
- package/CHANGELOG.md +235 -0
- package/CHANGELOG.ru.md +235 -0
- package/dist/aliases.d.ts +2 -0
- package/dist/aliases.js +83 -0
- package/dist/ast/declarations.d.ts +18 -0
- package/dist/ast/expressions.d.ts +14 -1
- package/dist/ast/markup.d.ts +2 -0
- package/dist/ast/statements.d.ts +2 -2
- package/dist/checker/assignments.js +2 -1
- package/dist/checker/base.d.ts +3 -0
- package/dist/checker/base.js +1 -1
- package/dist/checker/builtins.js +5 -1
- package/dist/checker/calls.d.ts +2 -0
- package/dist/checker/calls.js +18 -3
- package/dist/checker/classes.js +8 -0
- package/dist/checker/component-bindings.d.ts +16 -0
- package/dist/checker/component-bindings.js +65 -2
- package/dist/checker/context.d.ts +12 -0
- package/dist/checker/declarations.d.ts +2 -0
- package/dist/checker/declarations.js +38 -3
- package/dist/checker/expressions.d.ts +1 -0
- package/dist/checker/expressions.js +17 -1
- package/dist/checker/flow.d.ts +1 -1
- package/dist/checker/flow.js +10 -0
- package/dist/checker/functions.d.ts +2 -0
- package/dist/checker/functions.js +33 -7
- package/dist/checker/goroutines.d.ts +2 -2
- package/dist/checker/goroutines.js +2 -2
- package/dist/checker/imports.d.ts +10 -0
- package/dist/checker/imports.js +73 -6
- package/dist/checker/initializers.d.ts +12 -0
- package/dist/checker/initializers.js +80 -0
- package/dist/checker/loops.js +3 -1
- package/dist/checker/members.js +1 -0
- package/dist/checker/resolution.d.ts +2 -0
- package/dist/checker/resolution.js +13 -2
- package/dist/checker/statements.d.ts +5 -0
- package/dist/checker/statements.js +26 -3
- package/dist/checker/tagged-unions.d.ts +6 -0
- package/dist/checker/tagged-unions.js +31 -1
- package/dist/checker/tags.d.ts +6 -0
- package/dist/checker/tags.js +44 -0
- package/dist/checker/types/constructors.js +10 -1
- package/dist/checker/types/params.js +33 -7
- package/dist/checker/types/printing.js +5 -1
- package/dist/checker/types/relations.js +2 -1
- package/dist/codegen/analysis.d.ts +6 -0
- package/dist/codegen/analysis.js +24 -0
- package/dist/codegen/calls.d.ts +2 -0
- package/dist/codegen/calls.js +23 -1
- package/dist/codegen/component-props.js +8 -1
- package/dist/codegen/control-flow.d.ts +3 -0
- package/dist/codegen/control-flow.js +17 -0
- package/dist/codegen/expressions.d.ts +4 -1
- package/dist/codegen/expressions.js +4 -15
- package/dist/codegen/functions.d.ts +2 -0
- package/dist/codegen/functions.js +13 -0
- package/dist/codegen/mutations.d.ts +6 -0
- package/dist/codegen/mutations.js +36 -0
- package/dist/codegen/options.d.ts +2 -0
- package/dist/codegen/reactive.js +9 -5
- package/dist/codegen/simple-statements.js +3 -1
- package/dist/codegen/statements.d.ts +4 -0
- package/dist/codegen/statements.js +37 -1
- package/dist/codegen/updates.d.ts +6 -1
- package/dist/codegen/updates.js +13 -2
- package/dist/compile.js +2 -0
- package/dist/completion.js +2 -1
- package/dist/declarations.js +18 -0
- package/dist/lexer/base.d.ts +3 -2
- package/dist/lexer/base.js +14 -5
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/token.d.ts +6 -1
- package/dist/lexer/token.js +0 -1
- package/dist/modules.js +4 -3
- package/dist/parser/base.d.ts +5 -0
- package/dist/parser/base.js +9 -1
- package/dist/parser/classes.js +4 -3
- package/dist/parser/declarations.d.ts +2 -0
- package/dist/parser/declarations.js +25 -2
- package/dist/parser/decorators.js +1 -1
- package/dist/parser/expressions.js +6 -0
- package/dist/parser/literals.d.ts +2 -4
- package/dist/parser/literals.js +12 -24
- package/dist/parser/objects.d.ts +8 -0
- package/dist/parser/objects.js +51 -0
- package/dist/parser/params.d.ts +37 -4
- package/dist/parser/params.js +143 -9
- package/dist/parser/postfix.d.ts +5 -0
- package/dist/parser/postfix.js +36 -0
- package/dist/parser/simple-statements.d.ts +2 -0
- package/dist/parser/simple-statements.js +50 -0
- package/dist/parser/statements.js +8 -6
- package/dist/parser/syntax.js +0 -1
- package/dist/parser/types.d.ts +0 -4
- package/dist/parser/types.js +17 -32
- package/dist/project-modules.js +21 -0
- package/dist/reexports.d.ts +4 -0
- package/dist/reexports.js +56 -0
- package/dist/typescript/importer.d.ts +4 -0
- package/dist/typescript/importer.js +27 -3
- package/dist/typescript/library.js +5 -2
- package/dist/walk.js +12 -0
- package/package.json +4 -2
|
@@ -96,6 +96,10 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
96
96
|
// `data, err = try JSON.parse(text)`: the value and the error, as from a call.
|
|
97
97
|
if (values.length === 1 && value?.kind === 'TryExpression')
|
|
98
98
|
return;
|
|
99
|
+
if (values.length === 1 && value?.kind === 'ConditionalExpression') {
|
|
100
|
+
this.error('a conditional expression gives one value, not the results of a call: unpack them in the branches of if/else, e.g. if ok { a, err = two() } else { a, err = other() }', start, this.span(start).end);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
99
103
|
this.error(`assignment mismatch: ${plural(targets, 'variable')} but ${plural(values.length, 'value')}`, start, this.span(start).end);
|
|
100
104
|
}
|
|
101
105
|
/** `delete scores[name]`: removes a field, so the target is one. */
|
|
@@ -135,6 +139,49 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
135
139
|
end: body.end,
|
|
136
140
|
};
|
|
137
141
|
}
|
|
142
|
+
/** `const { name, size: bytes, ...rest } = file`: fields of an object, one level deep. */
|
|
143
|
+
parseDestructuring(start, exported, keyword) {
|
|
144
|
+
const names = [];
|
|
145
|
+
const keys = [];
|
|
146
|
+
let rest = false;
|
|
147
|
+
this.expect('{');
|
|
148
|
+
this.parseList('}', () => {
|
|
149
|
+
if (rest)
|
|
150
|
+
this.fail('...rest is the last name: it gets the other fields');
|
|
151
|
+
if (this.accept('...')) {
|
|
152
|
+
rest = true;
|
|
153
|
+
names.push(this.parseIdentifier('variable name'));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const token = this.peek();
|
|
157
|
+
const key = this.check('String') ? this.parseStringLiteral() : this.parsePropertyName();
|
|
158
|
+
if (this.accept(':')) {
|
|
159
|
+
names.push(this.parseIdentifier('variable name'));
|
|
160
|
+
}
|
|
161
|
+
else if (key.kind === 'Identifier' && token.kind === 'Identifier') {
|
|
162
|
+
names.push({ ...key });
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const shown = token.kind === 'String' ? token.text : `"${token.text}"`;
|
|
166
|
+
this.fail(`${shown} needs a name to take its value, e.g. { ${token.text}: name }`);
|
|
167
|
+
}
|
|
168
|
+
keys.push(key);
|
|
169
|
+
});
|
|
170
|
+
this.expect('=', '"=" and the object: a pattern takes its fields from a value');
|
|
171
|
+
const value = this.parseExpression();
|
|
172
|
+
return {
|
|
173
|
+
kind: 'VariableDeclaration',
|
|
174
|
+
exported,
|
|
175
|
+
keyword,
|
|
176
|
+
isPublic: false,
|
|
177
|
+
names,
|
|
178
|
+
type: null,
|
|
179
|
+
values: [value],
|
|
180
|
+
body: null,
|
|
181
|
+
pattern: { keys, rest },
|
|
182
|
+
...this.span(start),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
138
185
|
parseVariableDeclaration(start, exported) {
|
|
139
186
|
const token = this.next();
|
|
140
187
|
const keyword = token.kind === 'const'
|
|
@@ -142,6 +189,9 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
142
189
|
: token.text === 'state' || token.text === 'derived' || token.text === 'ref'
|
|
143
190
|
? token.text
|
|
144
191
|
: 'let';
|
|
192
|
+
if (this.check('{') && (keyword === 'const' || keyword === 'let')) {
|
|
193
|
+
return this.parseDestructuring(start, exported, keyword);
|
|
194
|
+
}
|
|
145
195
|
const names = [this.parseIdentifier('variable name')];
|
|
146
196
|
while (this.accept(','))
|
|
147
197
|
names.push(this.parseIdentifier('variable name'));
|
|
@@ -158,14 +158,15 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
158
158
|
return this.parseDecorated(start, exported);
|
|
159
159
|
case 'interface':
|
|
160
160
|
return this.parseInterface(start, exported);
|
|
161
|
-
case 'type': {
|
|
162
|
-
const declaration = this.parseTypeAlias(start, exported);
|
|
163
|
-
this.endStatement();
|
|
164
|
-
return declaration;
|
|
165
|
-
}
|
|
166
161
|
default: {
|
|
167
162
|
if (this.isDecoratorDeclaration())
|
|
168
163
|
return this.parseDecoratorDeclaration(start, exported);
|
|
164
|
+
// `type` is a keyword only before the name of a type: `const type = blob.type` is a name.
|
|
165
|
+
if (this.checkWord('type') && this.peek(1).kind === 'Identifier') {
|
|
166
|
+
const declaration = this.parseTypeAlias(start, exported);
|
|
167
|
+
this.endStatement();
|
|
168
|
+
return declaration;
|
|
169
|
+
}
|
|
169
170
|
// `data` and `abstract` are keywords only before `class`: elsewhere they are names.
|
|
170
171
|
let words = this.checkWord('abstract') ? 1 : 0;
|
|
171
172
|
if (this.peek(words).text === 'data')
|
|
@@ -178,8 +179,9 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
178
179
|
}
|
|
179
180
|
/** A statement ends with a newline or `;`, or right before the `}` that closes its block. */
|
|
180
181
|
endStatement() {
|
|
181
|
-
if (this.accept(';') || this.check('}') || this.check('EOF'))
|
|
182
|
+
if (this.accept(';') || this.check('}') || this.check('EOF') || this.afterTypeArguments()) {
|
|
182
183
|
return;
|
|
184
|
+
}
|
|
183
185
|
const token = this.peek();
|
|
184
186
|
if (token.kind === '++' || token.kind === '--') {
|
|
185
187
|
this.fail(`"${token.kind}" is a statement and cannot be used inside an expression`);
|
package/dist/parser/syntax.js
CHANGED
package/dist/parser/types.d.ts
CHANGED
|
@@ -13,10 +13,6 @@ export declare abstract class TypeParser extends ClassParser {
|
|
|
13
13
|
* names, which only document them.
|
|
14
14
|
*/
|
|
15
15
|
protected parseParenthesizedType(): ast.TypeNode;
|
|
16
|
-
/** The type of a parameter of a function type, after its name if it has one, kept in `names`. */
|
|
17
|
-
private parseParamType;
|
|
18
|
-
/** `paths []string` in `(paths []string) => Shape`: a name before the type of a parameter. */
|
|
19
|
-
private startsNamedParam;
|
|
20
16
|
/** `{ name string; x, y number; area() number }`, separated by newlines, `;` or `,`. */
|
|
21
17
|
protected parseTypeMembers(): ast.TypeMember[];
|
|
22
18
|
protected parseTypeMember(): ast.TypeMember[];
|
package/dist/parser/types.js
CHANGED
|
@@ -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 {
|
|
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(',') &&
|
|
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.
|
package/dist/project-modules.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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.
|
|
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",
|