rooklang 0.3.2 → 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.
- package/README.md +32 -7
- package/README.ru.md +32 -7
- package/dist/ast/declarations.d.ts +20 -1
- package/dist/ast/expressions.d.ts +31 -1
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +25 -1
- package/dist/auto-import.d.ts +11 -0
- package/dist/auto-import.js +81 -0
- package/dist/breaks.d.ts +6 -0
- package/dist/breaks.js +36 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +23 -2
- package/dist/checker/array-literals.d.ts +6 -0
- package/dist/checker/array-literals.js +59 -0
- package/dist/checker/assignments.d.ts +7 -0
- package/dist/checker/assignments.js +48 -8
- package/dist/checker/base.d.ts +2 -1
- package/dist/checker/base.js +6 -12
- package/dist/checker/builtins.js +15 -5
- package/dist/checker/bytes.d.ts +2 -2
- package/dist/checker/bytes.js +2 -2
- package/dist/checker/calls.js +3 -0
- package/dist/checker/channel-objects.d.ts +3 -0
- package/dist/checker/channel-objects.js +40 -0
- package/dist/checker/channels.d.ts +1 -0
- package/dist/checker/channels.js +16 -2
- package/dist/checker/checker.d.ts +1 -1
- package/dist/checker/checker.js +19 -0
- package/dist/checker/class-members.d.ts +22 -0
- package/dist/checker/class-members.js +105 -0
- package/dist/checker/classes.d.ts +5 -4
- package/dist/checker/classes.js +41 -12
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +2 -2
- package/dist/checker/completion.js +2 -2
- package/dist/checker/context.d.ts +19 -0
- package/dist/checker/control-flow.d.ts +6 -0
- package/dist/checker/control-flow.js +34 -0
- package/dist/checker/declarations.js +1 -0
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/error-results.d.ts +6 -0
- package/dist/checker/error-results.js +31 -0
- package/dist/checker/expressions.d.ts +5 -1
- package/dist/checker/expressions.js +13 -34
- package/dist/checker/flow.js +8 -4
- package/dist/checker/helpers.d.ts +1 -1
- package/dist/checker/helpers.js +9 -2
- package/dist/checker/iterables.d.ts +3 -1
- package/dist/checker/iterables.js +13 -0
- package/dist/checker/iterators.d.ts +4 -1
- package/dist/checker/iterators.js +12 -4
- package/dist/checker/json.d.ts +5 -0
- package/dist/checker/json.js +92 -0
- package/dist/checker/loops.js +23 -3
- package/dist/checker/members.js +7 -0
- package/dist/checker/operators.d.ts +2 -2
- package/dist/checker/operators.js +7 -5
- package/dist/checker/queues.d.ts +2 -2
- package/dist/checker/queues.js +2 -2
- package/dist/checker/resolution.js +5 -3
- package/dist/checker/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -0
- package/dist/checker/statements.js +19 -0
- package/dist/checker/topics.d.ts +6 -0
- package/dist/checker/topics.js +17 -0
- package/dist/checker/transfer.d.ts +3 -1
- package/dist/checker/transfer.js +25 -5
- package/dist/checker/try.d.ts +6 -0
- package/dist/checker/try.js +19 -0
- package/dist/checker/types/constructors.d.ts +1 -1
- package/dist/checker/types/constructors.js +2 -2
- package/dist/checker/types/generics.js +1 -1
- package/dist/checker/types/iterables.d.ts +13 -0
- package/dist/checker/types/iterables.js +52 -0
- package/dist/checker/types/model.d.ts +25 -0
- package/dist/checker/types/params.js +22 -3
- package/dist/checker/types/printing.js +4 -3
- package/dist/checker/types/relations.js +19 -25
- package/dist/checker/types/topic.d.ts +6 -0
- package/dist/checker/types/topic.js +23 -0
- package/dist/checker/types.d.ts +2 -0
- package/dist/checker/types.js +2 -0
- package/dist/cli.js +18 -36
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/control-statements.d.ts +19 -3
- package/dist/codegen/control-statements.js +105 -20
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/emitter.d.ts +10 -41
- package/dist/codegen/emitter.js +9 -4
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -24
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/options.d.ts +40 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/runtime.d.ts +7 -5
- package/dist/codegen/runtime.js +28 -20
- package/dist/codegen/simple-statements.d.ts +10 -4
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.js +64 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +19 -6
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.js +5 -2
- package/dist/goroutines.d.ts +1 -1
- package/dist/goroutines.js +5 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -5
- package/dist/lexer/base.d.ts +2 -0
- package/dist/lexer/base.js +6 -1
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/literals.d.ts +5 -0
- package/dist/lexer/literals.js +51 -1
- package/dist/lexer/markup.js +3 -4
- package/dist/lexer/tables.js +2 -0
- package/dist/lexer/token.d.ts +4 -3
- package/dist/lexer/token.js +0 -2
- package/dist/loader.js +40 -13
- package/dist/parser/base.d.ts +4 -0
- package/dist/parser/base.js +2 -1
- package/dist/parser/classes.d.ts +2 -0
- package/dist/parser/classes.js +88 -23
- package/dist/parser/control-flow.d.ts +3 -1
- package/dist/parser/control-flow.js +21 -24
- package/dist/parser/declarations.d.ts +7 -4
- package/dist/parser/declarations.js +45 -6
- package/dist/parser/expressions.js +27 -3
- package/dist/parser/jumps.d.ts +11 -0
- package/dist/parser/jumps.js +77 -0
- package/dist/parser/literals.d.ts +9 -0
- package/dist/parser/literals.js +45 -6
- package/dist/parser/markup.js +1 -1
- package/dist/parser/simple-statements.d.ts +1 -1
- package/dist/parser/simple-statements.js +16 -9
- package/dist/parser/statements.d.ts +0 -3
- package/dist/parser/statements.js +22 -25
- package/dist/parser/syntax.d.ts +11 -1
- package/dist/parser/syntax.js +16 -2
- package/dist/parser/types.js +8 -3
- package/dist/project-modules.d.ts +12 -0
- package/dist/project-modules.js +106 -0
- package/dist/run-file.d.ts +6 -0
- package/dist/run-file.js +111 -0
- package/dist/runtime/sources.js +11 -5
- package/dist/runtime-module.d.ts +1 -0
- package/dist/runtime-module.js +3 -0
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.d.ts +4 -0
- package/dist/ts-types.js +20 -3
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +13 -5
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +11 -28
- package/package.json +1 -1
- package/src/runtime/chan.js +15 -5
- package/src/runtime/channel.js +28 -0
- package/src/runtime/classes.js +32 -0
- package/src/runtime/core.js +10 -1
- package/src/runtime/decode.js +190 -0
- package/src/runtime/go.js +14 -5
- package/src/runtime/index.js +6 -0
- package/src/runtime/select.js +14 -9
- package/src/runtime/signals.js +59 -0
- package/src/runtime/threads.js +32 -5
- package/src/runtime/topic.js +118 -0
- package/src/runtime/try.js +45 -0
package/dist/completion.d.ts
CHANGED
|
@@ -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;
|
package/dist/completion.js
CHANGED
|
@@ -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
|
|
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 {
|
|
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)
|
package/dist/declarations.js
CHANGED
|
@@ -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
|
-
|
|
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')
|
package/dist/decorator-copy.js
CHANGED
|
@@ -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
|
-
|
|
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':
|
package/dist/goroutines.d.ts
CHANGED
|
@@ -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;
|
package/dist/goroutines.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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) {
|
package/dist/lexer/base.d.ts
CHANGED
|
@@ -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
|
package/dist/lexer/base.js
CHANGED
|
@@ -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);
|
package/dist/lexer/lexer.js
CHANGED
|
@@ -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 });
|
package/dist/lexer/literals.d.ts
CHANGED
|
@@ -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;
|
package/dist/lexer/literals.js
CHANGED
|
@@ -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;
|
package/dist/lexer/markup.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { LiteralLexer } from "./literals.js";
|
|
2
|
-
import {
|
|
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
|
-
|
|
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 (
|
|
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));
|
package/dist/lexer/tables.js
CHANGED
|
@@ -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',
|
package/dist/lexer/token.d.ts
CHANGED
|
@@ -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"
|
|
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 `/>`.
|
package/dist/lexer/token.js
CHANGED
package/dist/loader.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { registerHooks } from 'node:module';
|
|
4
|
-
import { join } from 'node:path';
|
|
2
|
+
import { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire, registerHooks } from 'node:module';
|
|
4
|
+
import { dirname, extname, join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { isMainThread } from 'node:worker_threads';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { compileFile } from "./compile-file.js";
|
|
7
|
+
import { RUNTIME_MODULE } from "./runtime-module.js";
|
|
8
|
+
import { sourceMapComment } from "./source-map.js";
|
|
10
9
|
// `rook run` imports this module first (`node --import`): Node then imports `.rk` files,
|
|
11
10
|
// compiled when they load, and `app.rk?goroutines` is the worker module of `go` in app.rk.
|
|
12
11
|
// The worker threads of `go` import it too, since they get the `--import` of the process. The main
|
|
13
12
|
// thread compiles a module before any worker needs it, so it leaves the code of each module and
|
|
14
13
|
// of its worker module in ROOK_RUN_CACHE, a directory of the run, and the workers take it from
|
|
15
|
-
// there: compiling again, with the types, would take each of them as long.
|
|
16
|
-
//
|
|
14
|
+
// there: compiling again, with the types, would take each of them as long. So a worker does not
|
|
15
|
+
// even load the compiler, which takes ~80 ms, unless a module is missing from the cache; the hooks
|
|
16
|
+
// are synchronous, so it is loaded with require(). ROOK_NO_CHECK turns type checking off, and
|
|
17
|
+
// ROOK_MARKUP, 1 or 0, markup and components on or off for every file. The code has its source map
|
|
18
|
+
// in it, and the process runs with --enable-source-maps, so stack traces show the lines of .rk.
|
|
19
|
+
// The main thread lists the files it compiles, and those they read, in `files` of the directory of
|
|
20
|
+
// the run, with the time each was changed, for `rook run --watch` (run-file.ts).
|
|
17
21
|
const typeCheck = process.env['ROOK_NO_CHECK'] === undefined;
|
|
18
22
|
const markupSetting = process.env['ROOK_MARKUP'];
|
|
19
23
|
const markup = markupSetting === undefined ? undefined : markupSetting === '1';
|
|
@@ -24,6 +28,9 @@ function cached(url) {
|
|
|
24
28
|
}
|
|
25
29
|
// The compiled code imports the runtime of this compiler, wherever the program is.
|
|
26
30
|
const runtime = import.meta.resolve(RUNTIME_MODULE);
|
|
31
|
+
const require = createRequire(import.meta.url);
|
|
32
|
+
// The files next to this one: .ts in src, .js in the dist of the package.
|
|
33
|
+
const extension = extname(fileURLToPath(import.meta.url));
|
|
27
34
|
registerHooks({
|
|
28
35
|
resolve(specifier, context, nextResolve) {
|
|
29
36
|
if (specifier === RUNTIME_MODULE)
|
|
@@ -39,20 +46,40 @@ registerHooks({
|
|
|
39
46
|
return { format: 'module', source: readFileSync(file, 'utf8'), shortCircuit: true };
|
|
40
47
|
}
|
|
41
48
|
const moduleUrl = new URL(pathname, url).href;
|
|
49
|
+
const { displayPath } = require(`./build${extension}`);
|
|
50
|
+
const { compileFile } = require(`./compile-file${extension}`);
|
|
42
51
|
const path = displayPath(fileURLToPath(moduleUrl));
|
|
52
|
+
if (isMainThread && cache)
|
|
53
|
+
list([resolve(path)]);
|
|
43
54
|
const result = compileFile(path, {
|
|
44
55
|
rewriteImports: false,
|
|
45
56
|
typeCheck,
|
|
46
57
|
markup,
|
|
47
58
|
quiet: !isMainThread,
|
|
48
59
|
});
|
|
60
|
+
const code = withMap(result.code, result.map);
|
|
61
|
+
const worker = result.worker === undefined ? undefined : withMap(result.worker, result.workerMap);
|
|
49
62
|
if (isMainThread && cache) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
// The CSS modules it imports; the .rk modules are listed when they are loaded.
|
|
64
|
+
const read = result.dependencies.map((each) => resolve(dirname(path), each));
|
|
65
|
+
list(read.filter((each) => existsSync(each)));
|
|
66
|
+
writeFileSync(cached(moduleUrl), code);
|
|
67
|
+
if (worker !== undefined)
|
|
68
|
+
writeFileSync(cached(`${moduleUrl}?goroutines`), worker);
|
|
54
69
|
}
|
|
55
|
-
const source = search === '?goroutines' ? (
|
|
70
|
+
const source = search === '?goroutines' ? (worker ?? '') : code;
|
|
56
71
|
return { format: 'module', source, shortCircuit: true };
|
|
57
72
|
},
|
|
58
73
|
});
|
|
74
|
+
/** The code with its source map in it. */
|
|
75
|
+
function withMap(code, map) {
|
|
76
|
+
if (map === undefined || code === '')
|
|
77
|
+
return code;
|
|
78
|
+
const data = Buffer.from(JSON.stringify(map)).toString('base64');
|
|
79
|
+
return `${code}${sourceMapComment(`data:application/json;base64,${data}`)}`;
|
|
80
|
+
}
|
|
81
|
+
/** Adds files to the list of the run: a line of the path and the time it was changed. */
|
|
82
|
+
function list(paths) {
|
|
83
|
+
const lines = paths.map((path) => `${path}\t${statSync(path).mtimeMs}\n`);
|
|
84
|
+
appendFileSync(join(cache, 'files'), lines.join(''));
|
|
85
|
+
}
|
package/dist/parser/base.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { StringToken, Token, TokenKind } from '../lexer/token.ts';
|
|
|
4
4
|
import { type Context } from './syntax.ts';
|
|
5
5
|
export declare abstract class ParserBase {
|
|
6
6
|
protected abstract parseBreakOrContinue(): ast.BreakStatement | ast.ContinueStatement;
|
|
7
|
+
protected abstract parseLabeled(): ast.Statement;
|
|
8
|
+
protected abstract parseReturn(): ast.ReturnStatement;
|
|
7
9
|
protected abstract parseDefer(): ast.DeferStatement;
|
|
8
10
|
protected abstract parseFor(): ast.ForStatement | ast.ForInStatement;
|
|
9
11
|
protected abstract parseGoFor(): ast.ForStatement | ast.ForInStatement;
|
|
@@ -37,6 +39,8 @@ export declare abstract class ParserBase {
|
|
|
37
39
|
protected abstract parseMake(): ast.MakeExpression;
|
|
38
40
|
protected abstract parseStreamChan(start: number): ast.StreamChanExpression;
|
|
39
41
|
protected abstract parseQueue(): ast.QueueExpression | null;
|
|
42
|
+
protected abstract parseTopic(): ast.TopicExpression | null;
|
|
43
|
+
protected abstract parseDecode(): ast.DecodeExpression | null;
|
|
40
44
|
protected abstract parseArrowFunction(async?: boolean): ast.ArrowFunction;
|
|
41
45
|
protected abstract parseFuncExpression(async?: boolean): ast.FuncExpression;
|
|
42
46
|
protected abstract parseIdentifier(what?: string): ast.Identifier;
|
package/dist/parser/base.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STATEMENT_KEYWORDS, BRACKETS, ParseError, describe } from "./syntax.js";
|
|
2
2
|
// The parser is a chain of layers, one per area: base → simple statements → statements → control
|
|
3
|
-
// flow → declarations → decorators → classes → types → expressions → literals → markup → Parser
|
|
3
|
+
// flow → jumps → declarations → decorators → classes → types → expressions → literals → markup → Parser
|
|
4
4
|
// (parser.ts). Statements, expressions and markup contain each other, so methods of later layers
|
|
5
5
|
// that earlier ones call are declared here as abstract.
|
|
6
6
|
//
|
|
@@ -21,6 +21,7 @@ export class ParserBase {
|
|
|
21
21
|
noObjectLiteral: false,
|
|
22
22
|
inComponent: false,
|
|
23
23
|
inMarkup: false,
|
|
24
|
+
labels: [],
|
|
24
25
|
};
|
|
25
26
|
/** Expressions written in parentheses, which the tree itself does not record. */
|
|
26
27
|
parenthesized = new WeakSet();
|
package/dist/parser/classes.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { DecoratorParser } from './decorators.ts';
|
|
|
3
3
|
export declare abstract class ClassParser extends DecoratorParser {
|
|
4
4
|
protected parseClass(start: number, exported: boolean): ast.ClassDeclaration;
|
|
5
5
|
protected parseClassMember(): ast.ClassMember[];
|
|
6
|
+
/** A method, a getter or a setter, after its name; an abstract one has no body. */
|
|
7
|
+
private parseMethod;
|
|
6
8
|
protected parseInterface(start: number, exported: boolean): ast.InterfaceDeclaration;
|
|
7
9
|
protected parseTypeAlias(start: number, exported: boolean): ast.TypeAliasDeclaration;
|
|
8
10
|
}
|