rooklang 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +177 -2
- package/README.ru.md +178 -0
- package/dist/ast/declarations.d.ts +185 -0
- package/dist/ast/declarations.js +1 -0
- package/dist/ast/expressions.d.ts +201 -0
- package/dist/ast/expressions.js +1 -0
- package/dist/ast/markup.d.ts +141 -0
- package/dist/ast/markup.js +1 -0
- package/dist/ast/statements.d.ts +173 -0
- package/dist/ast/statements.js +1 -0
- package/dist/ast.d.ts +18 -0
- package/dist/ast.js +18 -0
- package/dist/attrs.d.ts +8 -0
- package/dist/attrs.js +23 -0
- package/dist/browser.d.ts +30 -0
- package/dist/browser.js +36 -0
- package/dist/build.d.ts +48 -0
- package/dist/build.js +112 -0
- package/dist/checker/assignments.d.ts +14 -0
- package/dist/checker/assignments.js +197 -0
- package/dist/checker/base.d.ts +124 -0
- package/dist/checker/base.js +180 -0
- package/dist/checker/bigint.d.ts +15 -0
- package/dist/checker/bigint.js +85 -0
- package/dist/checker/builtins.d.ts +9 -0
- package/dist/checker/builtins.js +307 -0
- package/dist/checker/bytes.d.ts +11 -0
- package/dist/checker/bytes.js +72 -0
- package/dist/checker/calls.d.ts +23 -0
- package/dist/checker/calls.js +210 -0
- package/dist/checker/channel.d.ts +14 -0
- package/dist/checker/channel.js +44 -0
- package/dist/checker/channels.d.ts +9 -0
- package/dist/checker/channels.js +95 -0
- package/dist/checker/checker.d.ts +4 -0
- package/dist/checker/checker.js +22 -0
- package/dist/checker/classes.d.ts +24 -0
- package/dist/checker/classes.js +223 -0
- package/dist/checker/completion-names.d.ts +11 -0
- package/dist/checker/completion-names.js +100 -0
- package/dist/checker/completion.d.ts +25 -0
- package/dist/checker/completion.js +222 -0
- package/dist/checker/component-values.d.ts +11 -0
- package/dist/checker/component-values.js +88 -0
- package/dist/checker/components.d.ts +37 -0
- package/dist/checker/components.js +287 -0
- package/dist/checker/context.d.ts +196 -0
- package/dist/checker/context.js +35 -0
- package/dist/checker/control-flow.d.ts +24 -0
- package/dist/checker/control-flow.js +237 -0
- package/dist/checker/declarations.d.ts +26 -0
- package/dist/checker/declarations.js +259 -0
- package/dist/checker/decorator-exports.d.ts +5 -0
- package/dist/checker/decorator-exports.js +40 -0
- package/dist/checker/decorator-members.d.ts +28 -0
- package/dist/checker/decorator-members.js +140 -0
- package/dist/checker/decorator-wrappers.d.ts +20 -0
- package/dist/checker/decorator-wrappers.js +132 -0
- package/dist/checker/decorators.d.ts +28 -0
- package/dist/checker/decorators.js +265 -0
- package/dist/checker/dom-svg.d.ts +7 -0
- package/dist/checker/dom-svg.js +19 -0
- package/dist/checker/dom.d.ts +22 -0
- package/dist/checker/dom.js +361 -0
- package/dist/checker/expressions.d.ts +32 -0
- package/dist/checker/expressions.js +244 -0
- package/dist/checker/flow.d.ts +18 -0
- package/dist/checker/flow.js +134 -0
- package/dist/checker/forwarding.d.ts +15 -0
- package/dist/checker/forwarding.js +78 -0
- package/dist/checker/functions.d.ts +24 -0
- package/dist/checker/functions.js +154 -0
- package/dist/checker/go-for.d.ts +12 -0
- package/dist/checker/go-for.js +93 -0
- package/dist/checker/goroutines.d.ts +19 -0
- package/dist/checker/goroutines.js +110 -0
- package/dist/checker/helpers.d.ts +35 -0
- package/dist/checker/helpers.js +162 -0
- package/dist/checker/imports.d.ts +7 -0
- package/dist/checker/imports.js +83 -0
- package/dist/checker/initializers.d.ts +22 -0
- package/dist/checker/initializers.js +104 -0
- package/dist/checker/iterables.d.ts +5 -0
- package/dist/checker/iterables.js +17 -0
- package/dist/checker/iterators.d.ts +13 -0
- package/dist/checker/iterators.js +95 -0
- package/dist/checker/loops.d.ts +10 -0
- package/dist/checker/loops.js +82 -0
- package/dist/checker/markup.d.ts +28 -0
- package/dist/checker/markup.js +220 -0
- package/dist/checker/members.d.ts +29 -0
- package/dist/checker/members.js +233 -0
- package/dist/checker/operators.d.ts +13 -0
- package/dist/checker/operators.js +177 -0
- package/dist/checker/promise.d.ts +17 -0
- package/dist/checker/promise.js +49 -0
- package/dist/checker/refs.d.ts +26 -0
- package/dist/checker/refs.js +136 -0
- package/dist/checker/resolution.d.ts +23 -0
- package/dist/checker/resolution.js +173 -0
- package/dist/checker/statements.d.ts +28 -0
- package/dist/checker/statements.js +212 -0
- package/dist/checker/styles.d.ts +14 -0
- package/dist/checker/styles.js +49 -0
- package/dist/checker/transfer.d.ts +5 -0
- package/dist/checker/transfer.js +83 -0
- package/dist/checker/types/bytes.d.ts +4 -0
- package/dist/checker/types/bytes.js +51 -0
- package/dist/checker/types/classes.d.ts +22 -0
- package/dist/checker/types/classes.js +93 -0
- package/dist/checker/types/constructors.d.ts +27 -0
- package/dist/checker/types/constructors.js +129 -0
- package/dist/checker/types/generics.d.ts +20 -0
- package/dist/checker/types/generics.js +75 -0
- package/dist/checker/types/lazy.d.ts +30 -0
- package/dist/checker/types/lazy.js +92 -0
- package/dist/checker/types/model.d.ts +149 -0
- package/dist/checker/types/model.js +15 -0
- package/dist/checker/types/params.d.ts +7 -0
- package/dist/checker/types/params.js +220 -0
- package/dist/checker/types/printing.d.ts +6 -0
- package/dist/checker/types/printing.js +109 -0
- package/dist/checker/types/relations.d.ts +16 -0
- package/dist/checker/types/relations.js +294 -0
- package/dist/checker/types.d.ts +9 -0
- package/dist/checker/types.js +10 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +193 -0
- package/dist/codegen/analysis.d.ts +18 -0
- package/dist/codegen/analysis.js +144 -0
- package/dist/codegen/attributes.d.ts +40 -0
- package/dist/codegen/attributes.js +227 -0
- package/dist/codegen/calls.d.ts +22 -0
- package/dist/codegen/calls.js +106 -0
- package/dist/codegen/chan-helpers.d.ts +2 -0
- package/dist/codegen/chan-helpers.js +253 -0
- package/dist/codegen/component-helpers.d.ts +33 -0
- package/dist/codegen/component-helpers.js +18 -0
- package/dist/codegen/component-props.d.ts +42 -0
- package/dist/codegen/component-props.js +173 -0
- package/dist/codegen/component-values.d.ts +27 -0
- package/dist/codegen/component-values.js +146 -0
- package/dist/codegen/components.d.ts +53 -0
- package/dist/codegen/components.js +268 -0
- package/dist/codegen/control-flow.d.ts +25 -0
- package/dist/codegen/control-flow.js +152 -0
- package/dist/codegen/control-statements.d.ts +28 -0
- package/dist/codegen/control-statements.js +226 -0
- package/dist/codegen/creation.d.ts +31 -0
- package/dist/codegen/creation.js +113 -0
- package/dist/codegen/early-returns.d.ts +59 -0
- package/dist/codegen/early-returns.js +244 -0
- package/dist/codegen/element-helpers.d.ts +12 -0
- package/dist/codegen/element-helpers.js +54 -0
- package/dist/codegen/elements.d.ts +71 -0
- package/dist/codegen/elements.js +293 -0
- package/dist/codegen/emitter.d.ts +135 -0
- package/dist/codegen/emitter.js +296 -0
- package/dist/codegen/expressions.d.ts +49 -0
- package/dist/codegen/expressions.js +262 -0
- package/dist/codegen/functions.d.ts +28 -0
- package/dist/codegen/functions.js +170 -0
- package/dist/codegen/go-helpers.d.ts +9 -0
- package/dist/codegen/go-helpers.js +307 -0
- package/dist/codegen/helpers.d.ts +10 -0
- package/dist/codegen/helpers.js +356 -0
- package/dist/codegen/imported.d.ts +38 -0
- package/dist/codegen/imported.js +71 -0
- package/dist/codegen/js.d.ts +20 -0
- package/dist/codegen/js.js +113 -0
- package/dist/codegen/mutations.d.ts +10 -0
- package/dist/codegen/mutations.js +102 -0
- package/dist/codegen/parallel.d.ts +7 -0
- package/dist/codegen/parallel.js +63 -0
- package/dist/codegen/reactive.d.ts +95 -0
- package/dist/codegen/reactive.js +280 -0
- package/dist/codegen/refs.d.ts +62 -0
- package/dist/codegen/refs.js +160 -0
- package/dist/codegen/select-helpers.d.ts +2 -0
- package/dist/codegen/select-helpers.js +135 -0
- package/dist/codegen/simple-statements.d.ts +42 -0
- package/dist/codegen/simple-statements.js +253 -0
- package/dist/codegen/statements.d.ts +22 -0
- package/dist/codegen/statements.js +142 -0
- package/dist/codegen/styles.d.ts +54 -0
- package/dist/codegen/styles.js +157 -0
- package/dist/codegen/syntax.d.ts +18 -0
- package/dist/codegen/syntax.js +70 -0
- package/dist/codegen/thread-helpers.d.ts +2 -0
- package/dist/codegen/thread-helpers.js +198 -0
- package/dist/codegen/updates.d.ts +51 -0
- package/dist/codegen/updates.js +201 -0
- package/dist/codegen/web-components.d.ts +20 -0
- package/dist/codegen/web-components.js +149 -0
- package/dist/compile-file.d.ts +11 -0
- package/dist/compile-file.js +24 -0
- package/dist/compile.d.ts +53 -0
- package/dist/compile.js +187 -0
- package/dist/completion.d.ts +10 -0
- package/dist/completion.js +134 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +44 -0
- package/dist/css/hash.d.ts +1 -0
- package/dist/css/hash.js +10 -0
- package/dist/css/parse.d.ts +42 -0
- package/dist/css/parse.js +138 -0
- package/dist/css/print.d.ts +9 -0
- package/dist/css/print.js +35 -0
- package/dist/css/selectors.d.ts +29 -0
- package/dist/css/selectors.js +168 -0
- package/dist/declarations.d.ts +3 -0
- package/dist/declarations.js +207 -0
- package/dist/decorator-copy.d.ts +18 -0
- package/dist/decorator-copy.js +158 -0
- package/dist/decorators.d.ts +61 -0
- package/dist/decorators.js +241 -0
- package/dist/diagnostics.d.ts +17 -0
- package/dist/diagnostics.js +22 -0
- package/dist/go-for.d.ts +26 -0
- package/dist/go-for.js +155 -0
- package/dist/goroutines.d.ts +37 -0
- package/dist/goroutines.js +253 -0
- package/dist/html-tag.d.ts +21 -0
- package/dist/html-tag.js +103 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/inline-packages.d.ts +32 -0
- package/dist/inline-packages.js +129 -0
- package/dist/iterators.d.ts +10 -0
- package/dist/iterators.js +33 -0
- package/dist/lexer/base.d.ts +24 -0
- package/dist/lexer/base.js +87 -0
- package/dist/lexer/lexer.d.ts +8 -0
- package/dist/lexer/lexer.js +134 -0
- package/dist/lexer/literals.d.ts +18 -0
- package/dist/lexer/literals.js +203 -0
- package/dist/lexer/markup.d.ts +10 -0
- package/dist/lexer/markup.js +93 -0
- package/dist/lexer/style.d.ts +17 -0
- package/dist/lexer/style.js +97 -0
- package/dist/lexer/tables.d.ts +53 -0
- package/dist/lexer/tables.js +101 -0
- package/dist/lexer/token.d.ts +42 -0
- package/dist/lexer/token.js +108 -0
- package/dist/loader.d.ts +1 -0
- package/dist/loader.js +50 -0
- package/dist/methods.d.ts +5 -0
- package/dist/methods.js +81 -0
- package/dist/modules.d.ts +3 -0
- package/dist/modules.js +49 -0
- package/dist/names.d.ts +23 -0
- package/dist/names.js +267 -0
- package/dist/parser/base.d.ts +96 -0
- package/dist/parser/base.js +202 -0
- package/dist/parser/classes.d.ts +8 -0
- package/dist/parser/classes.js +164 -0
- package/dist/parser/control-flow.d.ts +29 -0
- package/dist/parser/control-flow.js +232 -0
- package/dist/parser/declarations.d.ts +27 -0
- package/dist/parser/declarations.js +238 -0
- package/dist/parser/decorators.d.ts +30 -0
- package/dist/parser/decorators.js +176 -0
- package/dist/parser/expressions.d.ts +15 -0
- package/dist/parser/expressions.js +255 -0
- package/dist/parser/literals.d.ts +27 -0
- package/dist/parser/literals.js +206 -0
- package/dist/parser/markup.d.ts +22 -0
- package/dist/parser/markup.js +193 -0
- package/dist/parser/parser.d.ts +14 -0
- package/dist/parser/parser.js +22 -0
- package/dist/parser/simple-statements.d.ts +22 -0
- package/dist/parser/simple-statements.js +156 -0
- package/dist/parser/statements.d.ts +37 -0
- package/dist/parser/statements.js +267 -0
- package/dist/parser/syntax.d.ts +51 -0
- package/dist/parser/syntax.js +173 -0
- package/dist/parser/types.d.ts +23 -0
- package/dist/parser/types.js +218 -0
- package/dist/recursion.d.ts +25 -0
- package/dist/recursion.js +170 -0
- package/dist/refs.d.ts +49 -0
- package/dist/refs.js +210 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +21 -0
- package/dist/source.d.ts +15 -0
- package/dist/source.js +34 -0
- package/dist/style-modules.d.ts +17 -0
- package/dist/style-modules.js +49 -0
- package/dist/styles.d.ts +36 -0
- package/dist/styles.js +160 -0
- package/dist/svg.d.ts +8 -0
- package/dist/svg.js +77 -0
- package/dist/ts-types.d.ts +11 -0
- package/dist/ts-types.js +133 -0
- package/dist/typed-arrays.d.ts +8 -0
- package/dist/typed-arrays.js +43 -0
- package/dist/typescript/convert-base.d.ts +42 -0
- package/dist/typescript/convert-base.js +135 -0
- package/dist/typescript/convert-objects.d.ts +28 -0
- package/dist/typescript/convert-objects.js +245 -0
- package/dist/typescript/convert.d.ts +14 -0
- package/dist/typescript/convert.js +67 -0
- package/dist/typescript/importer.d.ts +37 -0
- package/dist/typescript/importer.js +152 -0
- package/dist/typescript/library.d.ts +23 -0
- package/dist/typescript/library.js +81 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +86 -0
- package/dist/walk-types.d.ts +4 -0
- package/dist/walk-types.js +56 -0
- package/dist/walk.d.ts +5 -0
- package/dist/walk.js +315 -0
- package/package.json +104 -4
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { isIterator } from "../iterators.js";
|
|
2
|
+
import { decoratedName, moduleNamesOf } from "../decorators.js";
|
|
3
|
+
import { inlinePackages } from "../inline-packages.js";
|
|
4
|
+
import { packageExports } from "./imported.js";
|
|
5
|
+
import { ParallelLoopEmitter } from "./parallel.js";
|
|
6
|
+
import { ARROW, POSTFIX } from "./syntax.js";
|
|
7
|
+
/**
|
|
8
|
+
* Imports and classes; with the layers below, all statements and declarations. A decorator of
|
|
9
|
+
* another module is not a value, but its code uses names of its module: the import brings them
|
|
10
|
+
* under hidden names, `import { $$visible$log } from "./visible.js"`, which that module exports.
|
|
11
|
+
* So does an inlined component of another module; a called one brings its function, `$$Card`.
|
|
12
|
+
*/
|
|
13
|
+
export class StatementEmitter extends ParallelLoopEmitter {
|
|
14
|
+
importDeclaration(node) {
|
|
15
|
+
if (this.styleModuleImport(node))
|
|
16
|
+
return;
|
|
17
|
+
// Imports used only as types (e.g. interfaces) do not exist at runtime, and neither do
|
|
18
|
+
// components: a component used as a value is its function, imported below.
|
|
19
|
+
const used = (name) => this.valueNames.has(name.name) && !this.options.components?.has(name.name);
|
|
20
|
+
const clauses = [];
|
|
21
|
+
if (node.defaultImport && used(node.defaultImport)) {
|
|
22
|
+
clauses.push(this.name(node.defaultImport.name));
|
|
23
|
+
}
|
|
24
|
+
if (node.namespaceImport && used(node.namespaceImport)) {
|
|
25
|
+
clauses.push(`* as ${this.name(node.namespaceImport.name)}`);
|
|
26
|
+
}
|
|
27
|
+
const named = node.namedImports
|
|
28
|
+
.filter((specifier) => used(specifier.local))
|
|
29
|
+
.map((specifier) => {
|
|
30
|
+
const local = this.name(specifier.local.name);
|
|
31
|
+
return specifier.imported.name === local ? local : `${specifier.imported.name} as ${local}`;
|
|
32
|
+
});
|
|
33
|
+
const hidden = (exported, local) => named.push(exported === local ? exported : `${exported} as ${local}`);
|
|
34
|
+
for (const specifier of node.namedImports) {
|
|
35
|
+
const component = this.options.components?.get(specifier.local.name);
|
|
36
|
+
const inlined = this.inlinedImports.get(specifier.local.name);
|
|
37
|
+
if (inlined) {
|
|
38
|
+
for (const [exported, local] of inlined.imports)
|
|
39
|
+
hidden(exported, local);
|
|
40
|
+
}
|
|
41
|
+
else if (component) {
|
|
42
|
+
hidden(`$$${component.node.name.name}`, this.functionNames.get(component.node));
|
|
43
|
+
}
|
|
44
|
+
const decorator = this.options.decorators?.get(specifier.local.name);
|
|
45
|
+
if (!decorator || !this.appliedDecorators.has(specifier.local.name))
|
|
46
|
+
continue;
|
|
47
|
+
for (const name of decorator.moduleNames) {
|
|
48
|
+
named.push(decoratedName(decorator.node.name.name, name));
|
|
49
|
+
}
|
|
50
|
+
for (const [exported, local] of this.decoratorImports.get(specifier.local.name)?.imports ??
|
|
51
|
+
[]) {
|
|
52
|
+
hidden(exported, local);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (named.length > 0)
|
|
56
|
+
clauses.push(`{ ${named.join(', ')} }`);
|
|
57
|
+
const importsNames = node.defaultImport !== null || node.namespaceImport !== null || node.namedImports.length > 0;
|
|
58
|
+
if (importsNames && clauses.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
const source = this.importPath(node.source);
|
|
61
|
+
this.line(clauses.length > 0 ? `import ${clauses.join(', ')} from ${source};` : `import ${source};`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `export { log as $$visible$log }`: the names of the module that the code of exported
|
|
65
|
+
* decorators uses, and that of exported components when other modules inline them.
|
|
66
|
+
*/
|
|
67
|
+
hiddenExports() {
|
|
68
|
+
const exports = new Map();
|
|
69
|
+
for (const statement of this.program.body) {
|
|
70
|
+
if (statement.kind !== 'DecoratorDeclaration' || !statement.exported)
|
|
71
|
+
continue;
|
|
72
|
+
for (const name of moduleNamesOf(statement, this.program)) {
|
|
73
|
+
exports.set(decoratedName(statement.name.name, name), this.name(name));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const packages = inlinePackages(this.program);
|
|
77
|
+
const decorators = [...packages.decorators].filter((entry) => entry[1] !== null);
|
|
78
|
+
for (const found of [packages.components, new Map(decorators)]) {
|
|
79
|
+
for (const [exported, local] of packageExports(found, (name) => this.name(name))) {
|
|
80
|
+
exports.set(exported, local);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (exports.size === 0)
|
|
84
|
+
return;
|
|
85
|
+
const names = [...exports].map(([exported, local]) => exported === local ? exported : `${local} as ${exported}`);
|
|
86
|
+
this.blankLine();
|
|
87
|
+
this.line(`export { ${names.join(', ')} };`);
|
|
88
|
+
}
|
|
89
|
+
importPath(source) {
|
|
90
|
+
const path = source.value;
|
|
91
|
+
if (this.options.rewriteImports && /^\.{1,2}\//.test(path) && path.endsWith('.rk')) {
|
|
92
|
+
return JSON.stringify(`${path.slice(0, -'.rk'.length)}.js`);
|
|
93
|
+
}
|
|
94
|
+
return source.raw;
|
|
95
|
+
}
|
|
96
|
+
classDeclaration(node) {
|
|
97
|
+
const exported = node.exported ? 'export ' : '';
|
|
98
|
+
const superClass = node.superClass
|
|
99
|
+
? ` extends ${this.expression(node.superClass, POSTFIX)}`
|
|
100
|
+
: '';
|
|
101
|
+
const body = this.block(() => {
|
|
102
|
+
let previous;
|
|
103
|
+
for (const member of node.members) {
|
|
104
|
+
const bothFields = previous?.kind === 'FieldDeclaration' && member.kind === 'FieldDeclaration';
|
|
105
|
+
if (previous && (!bothFields || this.blankLineBetween(previous, member)))
|
|
106
|
+
this.blankLine();
|
|
107
|
+
this.classMember(member);
|
|
108
|
+
previous = member;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
this.line(`${exported}class ${this.name(node.name.name)}${superClass} ${body}`);
|
|
112
|
+
}
|
|
113
|
+
/** Visibility modifiers are checked by the compiler and do not exist in the output. */
|
|
114
|
+
classMember(member) {
|
|
115
|
+
switch (member.kind) {
|
|
116
|
+
case 'FieldDeclaration': {
|
|
117
|
+
// Field values are part of the class body, where no statements can go.
|
|
118
|
+
const fieldValue = member.value;
|
|
119
|
+
const value = fieldValue
|
|
120
|
+
? this.withHoisting(false, () => this.expression(fieldValue, ARROW))
|
|
121
|
+
: member.type
|
|
122
|
+
? this.zeroValue(member.type)
|
|
123
|
+
: null;
|
|
124
|
+
const prefix = member.isStatic ? 'static ' : '';
|
|
125
|
+
this.line(`${prefix}${member.name.name}${value === null ? '' : ` = ${value}`};`);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'ConstructorDeclaration': {
|
|
129
|
+
const [params, body] = this.func(member.params, member.body);
|
|
130
|
+
this.line(`constructor(${params}) ${body}`);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case 'MethodDeclaration': {
|
|
134
|
+
const [params, body] = this.func(member.params, member.body);
|
|
135
|
+
const modifiers = `${member.isStatic ? 'static ' : ''}${member.async ? 'async ' : ''}`;
|
|
136
|
+
const star = isIterator(member.params) ? '*' : '';
|
|
137
|
+
this.line(`${modifiers}${star}${member.name.name}(${params}) ${body}`);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { type ComponentStyle } from '../styles.ts';
|
|
3
|
+
import { RefEmitter } from './refs.ts';
|
|
4
|
+
/**
|
|
5
|
+
* `style { ... }` of components and decorators. The elements of their own markup get the class of
|
|
6
|
+
* the style; the module adds the CSS of every style whose elements it creates with `$$style`, at
|
|
7
|
+
* its start; values from code are CSS variables, set on the element the component returns once
|
|
8
|
+
* it is created and again when what they read changes.
|
|
9
|
+
*
|
|
10
|
+
* Inlined code is a copy: of a decorator for the components it is applied to, of a component of
|
|
11
|
+
* another module. Copies are found by the nodes as written, `originOf`.
|
|
12
|
+
*/
|
|
13
|
+
/** The style of a component or a decorator as written. */
|
|
14
|
+
interface Owner {
|
|
15
|
+
name: string;
|
|
16
|
+
style: ComponentStyle;
|
|
17
|
+
}
|
|
18
|
+
export declare abstract class StyleEmitter extends RefEmitter {
|
|
19
|
+
/** The styles by the elements and the `style` blocks of their own code; made on first use. */
|
|
20
|
+
private owners;
|
|
21
|
+
/** The styles whose elements the module creates, in the order of first use. */
|
|
22
|
+
protected readonly usedStyles: Set<Owner>;
|
|
23
|
+
/** The CSS modules the module imports: the objects of their classes, and their CSS. */
|
|
24
|
+
private readonly moduleObjects;
|
|
25
|
+
private readonly moduleStyles;
|
|
26
|
+
/** The CSS variables of the component being written, and whether its updates are added. */
|
|
27
|
+
private styleValues;
|
|
28
|
+
/** The class of the style an element of markup belongs to, if its owner has one. */
|
|
29
|
+
protected scopeOf(node: ast.ElementExpression): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* `import styles from "./button.module.css"` becomes the object of its classes, and its CSS is
|
|
32
|
+
* added with the styles of the components.
|
|
33
|
+
*/
|
|
34
|
+
protected styleModuleImport(node: ast.ImportDeclaration): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The objects of the CSS modules, and `$$style(id, css)` for them and for every style of a
|
|
37
|
+
* component the module uses: modules first, so that components can override them.
|
|
38
|
+
*/
|
|
39
|
+
protected styleRegistrations(): string[];
|
|
40
|
+
/** Writes the code of a component, whose result is `result`, with its CSS variables. */
|
|
41
|
+
protected withStyles<T>(component: ast.ComponentDeclaration, result: string, emit: () => T): T;
|
|
42
|
+
/** Sets the CSS variables on the element the component created. */
|
|
43
|
+
protected setStyleValues(result: string): void;
|
|
44
|
+
/** The updates of the CSS variables go with those of what they read. */
|
|
45
|
+
protected registerStyleValues(): void;
|
|
46
|
+
private setValue;
|
|
47
|
+
/**
|
|
48
|
+
* The CSS variables of a component as it is inlined: its own `style` blocks and copies of those
|
|
49
|
+
* of its decorators, whose values are renamed for it.
|
|
50
|
+
*/
|
|
51
|
+
private valuesOf;
|
|
52
|
+
private ownersByNode;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { originOf } from "../decorator-copy.js";
|
|
2
|
+
import { componentStyle, ownElements, scopedCss, styleBlocks, } from "../styles.js";
|
|
3
|
+
import { RefEmitter } from "./refs.js";
|
|
4
|
+
export class StyleEmitter extends RefEmitter {
|
|
5
|
+
/** The styles by the elements and the `style` blocks of their own code; made on first use. */
|
|
6
|
+
owners = null;
|
|
7
|
+
/** The styles whose elements the module creates, in the order of first use. */
|
|
8
|
+
usedStyles = new Set();
|
|
9
|
+
/** The CSS modules the module imports: the objects of their classes, and their CSS. */
|
|
10
|
+
moduleObjects = [];
|
|
11
|
+
moduleStyles = [];
|
|
12
|
+
/** The CSS variables of the component being written, and whether its updates are added. */
|
|
13
|
+
styleValues = null;
|
|
14
|
+
/** The class of the style an element of markup belongs to, if its owner has one. */
|
|
15
|
+
scopeOf(node) {
|
|
16
|
+
const owner = this.ownersByNode().get(originOf(node));
|
|
17
|
+
if (!owner)
|
|
18
|
+
return null;
|
|
19
|
+
this.usedStyles.add(owner);
|
|
20
|
+
return owner.style.scope;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* `import styles from "./button.module.css"` becomes the object of its classes, and its CSS is
|
|
24
|
+
* added with the styles of the components.
|
|
25
|
+
*/
|
|
26
|
+
styleModuleImport(node) {
|
|
27
|
+
const module = this.options.styleModules?.get(node.source.value);
|
|
28
|
+
if (!module)
|
|
29
|
+
return false;
|
|
30
|
+
if (node.defaultImport) {
|
|
31
|
+
const fields = [...module.classes].map(([name, local]) => {
|
|
32
|
+
const key = /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
33
|
+
return `${key}: ${JSON.stringify(local)}`;
|
|
34
|
+
});
|
|
35
|
+
this.moduleObjects.push(`const ${this.name(node.defaultImport.name)} = { ${fields.join(', ')} };`);
|
|
36
|
+
}
|
|
37
|
+
this.moduleStyles.push(module);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The objects of the CSS modules, and `$$style(id, css)` for them and for every style of a
|
|
42
|
+
* component the module uses: modules first, so that components can override them.
|
|
43
|
+
*/
|
|
44
|
+
styleRegistrations() {
|
|
45
|
+
const register = (id, css) => {
|
|
46
|
+
const text = css.replace(/[`\\]|\$\{/g, (match) => `\\${match}`);
|
|
47
|
+
return `$$style(${JSON.stringify(id)}, \`${text}\`);`;
|
|
48
|
+
};
|
|
49
|
+
return [
|
|
50
|
+
...this.moduleObjects,
|
|
51
|
+
...this.moduleStyles.map((module) => register(`rook-module-${module.id}`, module.css)),
|
|
52
|
+
...[...this.usedStyles].map(({ style }) => register(style.scope, scopedCss(style))),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
/** Writes the code of a component, whose result is `result`, with its CSS variables. */
|
|
56
|
+
withStyles(component, result, emit) {
|
|
57
|
+
const saved = this.styleValues;
|
|
58
|
+
const values = this.valuesOf(component);
|
|
59
|
+
this.styleValues = values.length > 0 ? { result, values, registered: false } : null;
|
|
60
|
+
try {
|
|
61
|
+
return emit();
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
this.styleValues = saved;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Sets the CSS variables on the element the component created. */
|
|
68
|
+
setStyleValues(result) {
|
|
69
|
+
for (const value of this.styleValues?.values ?? [])
|
|
70
|
+
this.line(this.setValue(result, value));
|
|
71
|
+
}
|
|
72
|
+
/** The updates of the CSS variables go with those of what they read. */
|
|
73
|
+
registerStyleValues() {
|
|
74
|
+
const { reactive, styleValues } = this;
|
|
75
|
+
if (!reactive || !styleValues || styleValues.registered)
|
|
76
|
+
return;
|
|
77
|
+
styleValues.registered = true;
|
|
78
|
+
for (const value of styleValues.values) {
|
|
79
|
+
const sources = new Set();
|
|
80
|
+
for (const part of value.declaration.value) {
|
|
81
|
+
if (typeof part !== 'number')
|
|
82
|
+
continue;
|
|
83
|
+
for (const source of reactive.dependencies(value.values[part]))
|
|
84
|
+
sources.add(source);
|
|
85
|
+
}
|
|
86
|
+
if (sources.size > 0)
|
|
87
|
+
reactive.depend(sources, this.setValue(styleValues.result, value));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
setValue(result, { variable, declaration, values }) {
|
|
91
|
+
const { value } = declaration;
|
|
92
|
+
const only = value.length === 1 && typeof value[0] === 'number' ? value[0] : null;
|
|
93
|
+
const text = only !== null
|
|
94
|
+
? this.liveExpression(values[only])
|
|
95
|
+
: `\`${value
|
|
96
|
+
.map((part) => typeof part === 'number'
|
|
97
|
+
? `\${${this.liveExpression(values[part])}}`
|
|
98
|
+
: part.replace(/[`\\]|\$\{/g, (match) => `\\${match}`))
|
|
99
|
+
.join('')}\``;
|
|
100
|
+
return `${result}.style.setProperty(${JSON.stringify(variable)}, ${text});`;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The CSS variables of a component as it is inlined: its own `style` blocks and copies of those
|
|
104
|
+
* of its decorators, whose values are renamed for it.
|
|
105
|
+
*/
|
|
106
|
+
valuesOf(component) {
|
|
107
|
+
const groups = new Map();
|
|
108
|
+
for (const block of styleBlocks(component.body)) {
|
|
109
|
+
const owner = this.ownersByNode().get(originOf(block));
|
|
110
|
+
if (owner)
|
|
111
|
+
groups.set(owner, [...(groups.get(owner) ?? []), block]);
|
|
112
|
+
}
|
|
113
|
+
return [...groups].flatMap(([owner, blocks]) => {
|
|
114
|
+
const body = { kind: 'BlockStatement', body: blocks, start: 0, end: 0 };
|
|
115
|
+
// The same text as the original: the same variables, with the values of this copy.
|
|
116
|
+
const copy = componentStyle(owner.name, body);
|
|
117
|
+
return [...copy.variables].map(([declaration, variable]) => ({
|
|
118
|
+
variable,
|
|
119
|
+
declaration,
|
|
120
|
+
values: copy.values,
|
|
121
|
+
}));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
ownersByNode() {
|
|
125
|
+
if (this.owners)
|
|
126
|
+
return this.owners;
|
|
127
|
+
const owners = new Map();
|
|
128
|
+
const add = (node) => {
|
|
129
|
+
const style = componentStyle(node.name.name, node.body);
|
|
130
|
+
if (!style)
|
|
131
|
+
return;
|
|
132
|
+
const owner = { name: node.name.name, style };
|
|
133
|
+
for (const element of ownElements(node.body))
|
|
134
|
+
owners.set(element, owner);
|
|
135
|
+
for (const block of styleBlocks(node.body))
|
|
136
|
+
owners.set(block, owner);
|
|
137
|
+
};
|
|
138
|
+
for (const statement of this.program.body) {
|
|
139
|
+
if (statement.kind === 'ComponentDeclaration' || statement.kind === 'DecoratorDeclaration') {
|
|
140
|
+
add(statement);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// The code of other modules that is inlined here, as written there.
|
|
144
|
+
for (const source of [
|
|
145
|
+
...(this.options.decorators?.values() ?? []),
|
|
146
|
+
...(this.options.components?.values() ?? []),
|
|
147
|
+
]) {
|
|
148
|
+
add(source.node);
|
|
149
|
+
for (const node of source.inline?.components.values() ?? [])
|
|
150
|
+
add(node);
|
|
151
|
+
for (const decorator of source.inline?.decorators.values() ?? [])
|
|
152
|
+
add(decorator.node);
|
|
153
|
+
}
|
|
154
|
+
this.owners = owners;
|
|
155
|
+
return owners;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
export declare const ARROW = 1;
|
|
3
|
+
export declare const CONDITIONAL = 2;
|
|
4
|
+
export declare const UNARY = 14;
|
|
5
|
+
export declare const POSTFIX = 17;
|
|
6
|
+
export declare const PRIMARY = 18;
|
|
7
|
+
export declare const BINARY: Readonly<Record<ast.BinaryOperator, number>>;
|
|
8
|
+
/** Names that Rook allows but strict-mode JS reserves; they get a `$` suffix. */
|
|
9
|
+
export declare const JS_RESERVED: ReadonlySet<string>;
|
|
10
|
+
/** Names that strict-mode JS can read but not declare. */
|
|
11
|
+
export declare const JS_UNDECLARABLE: ReadonlySet<string>;
|
|
12
|
+
/** `[a, , c]`; skipped (empty) elements at the end are dropped: `[q, ]` → `[q]`. */
|
|
13
|
+
export declare function arrayPattern(elements: readonly string[]): string;
|
|
14
|
+
/** A statement written at block level goes one level deeper into a function. */
|
|
15
|
+
export declare function indentMore(text: string): string;
|
|
16
|
+
/** `count` → `Count`, for generated names like `$$updateCount3`. */
|
|
17
|
+
export declare function upperFirst(name: string): string;
|
|
18
|
+
export declare function lowerFirst(name: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Facts about the JS syntax that the generated code must respect.
|
|
2
|
+
// Operator precedence: an operand whose precedence is lower than required gets parentheses.
|
|
3
|
+
export const ARROW = 1;
|
|
4
|
+
export const CONDITIONAL = 2;
|
|
5
|
+
export const UNARY = 14;
|
|
6
|
+
export const POSTFIX = 17;
|
|
7
|
+
export const PRIMARY = 18;
|
|
8
|
+
export const BINARY = {
|
|
9
|
+
'??': 3,
|
|
10
|
+
'||': 3,
|
|
11
|
+
'&&': 4,
|
|
12
|
+
'|': 5,
|
|
13
|
+
'^': 6,
|
|
14
|
+
'&': 7,
|
|
15
|
+
'==': 8,
|
|
16
|
+
'!=': 8,
|
|
17
|
+
'<': 9,
|
|
18
|
+
'>': 9,
|
|
19
|
+
'<=': 9,
|
|
20
|
+
'>=': 9,
|
|
21
|
+
instanceof: 9,
|
|
22
|
+
'<<': 10,
|
|
23
|
+
'>>': 10,
|
|
24
|
+
'>>>': 10,
|
|
25
|
+
'+': 11,
|
|
26
|
+
'-': 11,
|
|
27
|
+
'*': 12,
|
|
28
|
+
'/': 12,
|
|
29
|
+
'%': 12,
|
|
30
|
+
'**': 13,
|
|
31
|
+
};
|
|
32
|
+
/** Names that Rook allows but strict-mode JS reserves; they get a `$` suffix. */
|
|
33
|
+
export const JS_RESERVED = new Set([
|
|
34
|
+
'debugger',
|
|
35
|
+
'delete',
|
|
36
|
+
'do',
|
|
37
|
+
'enum',
|
|
38
|
+
'function',
|
|
39
|
+
'implements',
|
|
40
|
+
'package',
|
|
41
|
+
'private',
|
|
42
|
+
'protected',
|
|
43
|
+
'public',
|
|
44
|
+
'static',
|
|
45
|
+
'var',
|
|
46
|
+
'void',
|
|
47
|
+
'while',
|
|
48
|
+
'with',
|
|
49
|
+
'yield',
|
|
50
|
+
]);
|
|
51
|
+
/** Names that strict-mode JS can read but not declare. */
|
|
52
|
+
export const JS_UNDECLARABLE = new Set(['eval', 'arguments']);
|
|
53
|
+
/** `[a, , c]`; skipped (empty) elements at the end are dropped: `[q, ]` → `[q]`. */
|
|
54
|
+
export function arrayPattern(elements) {
|
|
55
|
+
let end = elements.length;
|
|
56
|
+
while (end > 0 && elements[end - 1] === '')
|
|
57
|
+
end--;
|
|
58
|
+
return `[${elements.slice(0, end).join(', ')}]`;
|
|
59
|
+
}
|
|
60
|
+
/** A statement written at block level goes one level deeper into a function. */
|
|
61
|
+
export function indentMore(text) {
|
|
62
|
+
return text.replace(/\n/g, '\n ');
|
|
63
|
+
}
|
|
64
|
+
/** `count` → `Count`, for generated names like `$$updateCount3`. */
|
|
65
|
+
export function upperFirst(name) {
|
|
66
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
67
|
+
}
|
|
68
|
+
export function lowerFirst(name) {
|
|
69
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
70
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// What threads share: a channel is data, `{ $$chan: id, size, zero }`, copied to other threads like
|
|
2
|
+
// any value, and what it holds lives in the thread that made it, whose key starts its id. Each
|
|
3
|
+
// thread has a registry of its channels and of its ports to the other threads, shared by its
|
|
4
|
+
// modules; the operations of a thread on the channels of another go straight to that thread
|
|
5
|
+
// through a MessagePort, which the main thread sets up on the first need (go-helpers.ts).
|
|
6
|
+
export const THREAD_HELPERS = {
|
|
7
|
+
threads: [
|
|
8
|
+
'// The state of this thread that its modules share: the channels it made, its ports to the other',
|
|
9
|
+
'// threads by their keys, and the requests that wait for an answer.',
|
|
10
|
+
'function $$registry() {',
|
|
11
|
+
' return (globalThis[Symbol.for("rook.threads")] ??= {',
|
|
12
|
+
' key: Math.random().toString(36).slice(2),',
|
|
13
|
+
' channels: 0,',
|
|
14
|
+
' states: new Map(),',
|
|
15
|
+
' held: new FinalizationRegistry((id) => $$hold(id, -1)),',
|
|
16
|
+
' ports: new Map(),',
|
|
17
|
+
' pins: new Map(),',
|
|
18
|
+
' idle: null,',
|
|
19
|
+
' waiting: new Map(),',
|
|
20
|
+
' gone: new Set(),',
|
|
21
|
+
' main: null,',
|
|
22
|
+
' requests: new Map(),',
|
|
23
|
+
' asks: 0,',
|
|
24
|
+
' selects: new Map(),',
|
|
25
|
+
' moved: new WeakSet(),',
|
|
26
|
+
' cancels: new WeakMap(),',
|
|
27
|
+
' handle: null,',
|
|
28
|
+
' });',
|
|
29
|
+
'}',
|
|
30
|
+
'',
|
|
31
|
+
'// The key of the thread that made a channel, which keeps what it holds.',
|
|
32
|
+
'function $$ownerOf(id) {',
|
|
33
|
+
' return id.slice(0, id.lastIndexOf(":"));',
|
|
34
|
+
'}',
|
|
35
|
+
'',
|
|
36
|
+
'// Sends a message to a thread. The channels in it are counted first, as the thread gets copies,',
|
|
37
|
+
'// and the buffers of transfer() move there. A message waits for the port of a thread that has just',
|
|
38
|
+
'// started.',
|
|
39
|
+
'function $$post(key, message) {',
|
|
40
|
+
' const registry = $$registry();',
|
|
41
|
+
' if (registry.gone.has(key)) return;',
|
|
42
|
+
' const { channels, moved } = $$walk(message);',
|
|
43
|
+
' for (const channel of channels) $$hold(channel.$$chan, 1);',
|
|
44
|
+
' const port = registry.ports.get(key);',
|
|
45
|
+
' if (port) return port.postMessage(message, moved);',
|
|
46
|
+
' if (!registry.waiting.has(key)) registry.waiting.set(key, []);',
|
|
47
|
+
' registry.waiting.get(key).push([message, moved]);',
|
|
48
|
+
'}',
|
|
49
|
+
'',
|
|
50
|
+
'// A request to a thread, answered by a message with its number.',
|
|
51
|
+
'function $$ask(key, message) {',
|
|
52
|
+
' const registry = $$registry();',
|
|
53
|
+
' if (registry.gone.has(key)) return Promise.reject($$stopped());',
|
|
54
|
+
' $$pin(key, 1);',
|
|
55
|
+
' return new Promise((resolve, reject) => {',
|
|
56
|
+
' const ask = registry.asks++;',
|
|
57
|
+
' registry.requests.set(ask, { resolve, reject, key });',
|
|
58
|
+
' $$post(key, { ...message, ask });',
|
|
59
|
+
' });',
|
|
60
|
+
'}',
|
|
61
|
+
'',
|
|
62
|
+
'// A thread that this one waits for keeps the program running: in Node, its worker or port is',
|
|
63
|
+
'// referenced while some request or select waits for it.',
|
|
64
|
+
'function $$pin(key, count) {',
|
|
65
|
+
' const registry = $$registry();',
|
|
66
|
+
' const pins = (registry.pins.get(key) ?? 0) + count;',
|
|
67
|
+
' registry.pins.set(key, pins);',
|
|
68
|
+
' const port = registry.ports.get(key);',
|
|
69
|
+
' if (pins > 0) port?.ref?.();',
|
|
70
|
+
' else if (registry.idle) registry.idle(key);',
|
|
71
|
+
' else port?.unref?.();',
|
|
72
|
+
'}',
|
|
73
|
+
'',
|
|
74
|
+
'function $$stopped() {',
|
|
75
|
+
' return new Error("the goroutine that made the channel has stopped");',
|
|
76
|
+
'}',
|
|
77
|
+
'',
|
|
78
|
+
'// A port to a thread: messages that waited for it go now.',
|
|
79
|
+
'function $$connect(key, port, listen) {',
|
|
80
|
+
' const registry = $$registry();',
|
|
81
|
+
' registry.ports.set(key, port);',
|
|
82
|
+
' if (registry.pins.get(key) > 0) port.ref?.();',
|
|
83
|
+
' if (listen) {',
|
|
84
|
+
' const receive = (data) => $$incoming(key, data);',
|
|
85
|
+
' if (port.on) {',
|
|
86
|
+
' port.on("message", receive);',
|
|
87
|
+
' if (!(registry.pins.get(key) > 0)) port.unref?.();',
|
|
88
|
+
' } else {',
|
|
89
|
+
' port.onmessage = (event) => receive(event.data);',
|
|
90
|
+
' }',
|
|
91
|
+
' }',
|
|
92
|
+
' for (const [message, moved] of registry.waiting.get(key) ?? []) port.postMessage(message, moved);',
|
|
93
|
+
' registry.waiting.delete(key);',
|
|
94
|
+
'}',
|
|
95
|
+
'',
|
|
96
|
+
'// A thread has stopped: what waits for it fails, and what it waited for is dropped.',
|
|
97
|
+
'function $$gone(key) {',
|
|
98
|
+
' const registry = $$registry();',
|
|
99
|
+
' registry.gone.add(key);',
|
|
100
|
+
' registry.ports.get(key)?.close?.();',
|
|
101
|
+
' registry.ports.delete(key);',
|
|
102
|
+
' registry.waiting.delete(key);',
|
|
103
|
+
' for (const [ask, request] of registry.requests) {',
|
|
104
|
+
' if (request.key !== key) continue;',
|
|
105
|
+
' registry.requests.delete(ask);',
|
|
106
|
+
' request.reject($$stopped());',
|
|
107
|
+
' }',
|
|
108
|
+
' for (const [id, state] of registry.states) {',
|
|
109
|
+
' const alive = (entry) => (entry.select?.key ?? entry.from) !== key;',
|
|
110
|
+
' state.receivers = state.receivers.filter(alive);',
|
|
111
|
+
' state.senders = state.senders.filter(alive);',
|
|
112
|
+
' registry.handle?.pump(id);',
|
|
113
|
+
' }',
|
|
114
|
+
'}',
|
|
115
|
+
'',
|
|
116
|
+
'// A message from another thread: an answer to a request, or an operation on a channel of this one.',
|
|
117
|
+
'function $$incoming(key, data) {',
|
|
118
|
+
' const registry = $$registry();',
|
|
119
|
+
' $$adopt(data);',
|
|
120
|
+
' if ("answer" in data) {',
|
|
121
|
+
' const request = registry.requests.get(data.answer);',
|
|
122
|
+
' registry.requests.delete(data.answer);',
|
|
123
|
+
' if (request) $$pin(request.key, -1);',
|
|
124
|
+
' if ("error" in data) request?.reject(data.error);',
|
|
125
|
+
' else request?.resolve(data.value);',
|
|
126
|
+
' } else if (data.op === "hold") {',
|
|
127
|
+
' $$hold(data.channel, data.count);',
|
|
128
|
+
' } else if ("op" in data) {',
|
|
129
|
+
' registry.handle.message(key, data);',
|
|
130
|
+
' }',
|
|
131
|
+
'}',
|
|
132
|
+
'',
|
|
133
|
+
'// Each value of a channel, in any thread, holds it: the thread that made it forgets it when none',
|
|
134
|
+
'// is left. A thread counts the channels of a value before a copy of it goes to another thread, and',
|
|
135
|
+
'// a copy lets go of them when it is collected.',
|
|
136
|
+
'function $$hold(id, count) {',
|
|
137
|
+
' const registry = $$registry();',
|
|
138
|
+
' const owner = $$ownerOf(id);',
|
|
139
|
+
' if (owner !== registry.key) return $$post(owner, { op: "hold", channel: id, count });',
|
|
140
|
+
' const state = registry.states.get(id);',
|
|
141
|
+
' if (!state) return;',
|
|
142
|
+
' state.holders += count;',
|
|
143
|
+
' if (state.holders <= 0) registry.states.delete(id);',
|
|
144
|
+
'}',
|
|
145
|
+
'',
|
|
146
|
+
'function $$adopt(value) {',
|
|
147
|
+
' const { held } = $$registry();',
|
|
148
|
+
' for (const channel of $$walk(value).channels) held.register(channel, channel.$$chan);',
|
|
149
|
+
'}',
|
|
150
|
+
'',
|
|
151
|
+
'// A copy in this thread. One that `own` the runtime, such as the arguments of `go` that wait for',
|
|
152
|
+
'// a worker, moves its buffers on.',
|
|
153
|
+
'function $$copy(value, own = false) {',
|
|
154
|
+
' const { channels, moved } = $$walk(value);',
|
|
155
|
+
' for (const channel of channels) $$hold(channel.$$chan, 1);',
|
|
156
|
+
' const copy = structuredClone(value, { transfer: moved });',
|
|
157
|
+
' const { held } = $$registry();',
|
|
158
|
+
' for (const channel of $$walk(copy, own).channels) held.register(channel, channel.$$chan);',
|
|
159
|
+
' return copy;',
|
|
160
|
+
'}',
|
|
161
|
+
'',
|
|
162
|
+
'// `transfer(x)`: the buffer of x moves to the thread that a value with it goes to, instead of a',
|
|
163
|
+
'// copy; here it is left empty.',
|
|
164
|
+
'function $$transfer(value) {',
|
|
165
|
+
' $$registry().moved.add(ArrayBuffer.isView(value) ? value.buffer : value);',
|
|
166
|
+
' return value;',
|
|
167
|
+
'}',
|
|
168
|
+
'',
|
|
169
|
+
'// The channels in a value, each of which becomes a copy in the thread that gets the value, and the',
|
|
170
|
+
'// buffers that move there.',
|
|
171
|
+
'function $$walk(value, own = false, found = { channels: new Set(), moved: [] }, seen = new Set()) {',
|
|
172
|
+
' if (typeof value !== "object" || value === null || seen.has(value)) return found;',
|
|
173
|
+
' seen.add(value);',
|
|
174
|
+
' const buffer =',
|
|
175
|
+
' value instanceof ArrayBuffer ? value : ArrayBuffer.isView(value) ? value.buffer : null;',
|
|
176
|
+
' // A SharedArrayBuffer is shared with the other thread, not moved.',
|
|
177
|
+
' if (buffer && !(buffer instanceof ArrayBuffer)) return found;',
|
|
178
|
+
' if (buffer) {',
|
|
179
|
+
' const { moved } = $$registry();',
|
|
180
|
+
' if (own) moved.add(buffer);',
|
|
181
|
+
' if (moved.has(buffer) && !found.moved.includes(buffer)) found.moved.push(buffer);',
|
|
182
|
+
' return found;',
|
|
183
|
+
' }',
|
|
184
|
+
' if (typeof value.$$chan === "string") {',
|
|
185
|
+
' found.channels.add(value);',
|
|
186
|
+
' return found;',
|
|
187
|
+
' }',
|
|
188
|
+
' const items =',
|
|
189
|
+
' value instanceof Map',
|
|
190
|
+
' ? [...value.keys(), ...value.values()]',
|
|
191
|
+
' : value instanceof Set || Array.isArray(value)',
|
|
192
|
+
' ? value',
|
|
193
|
+
' : Object.values(value);',
|
|
194
|
+
' for (const item of items) $$walk(item, own, found, seen);',
|
|
195
|
+
' return found;',
|
|
196
|
+
'}',
|
|
197
|
+
],
|
|
198
|
+
};
|