@reactivescript/language-server 0.0.0 → 0.1.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/README.md +101 -45
- package/bin/server.js +24 -0
- package/package.json +35 -7
- package/src/index.js +98 -0
- package/src/rxc.js +94 -0
- package/src/rxc.wasm +0 -0
package/README.md
CHANGED
|
@@ -1,45 +1,101 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
# rx-language-server
|
|
2
|
+
|
|
3
|
+
The `.rx` language server (spec/11 CP-8e), built on [Volar](https://volarjs.dev).
|
|
4
|
+
A `.rx` file's virtual code is its TSX projection (spec/13 TY-1c), which the
|
|
5
|
+
crate makes with mappings back onto the `.rx` (CP-8c). TypeScript answers hover,
|
|
6
|
+
go to definition, references, rename, completion and type errors on the
|
|
7
|
+
projection, and Volar maps the answers back. The crate's own diagnostics (parse
|
|
8
|
+
errors, else the checker's, each with its rule) are a second service.
|
|
9
|
+
|
|
10
|
+
- `src/rxc.js` asks the crate for a file's analysis, with the editor's unsaved
|
|
11
|
+
text as overlays: its WebAssembly build in process (`src/wasm.rs`; one build
|
|
12
|
+
for every platform, about a millisecond a file once loaded), or, when `RXC`
|
|
13
|
+
names a binary, `rxc serve` as a process per request. It writes the
|
|
14
|
+
projection's prelude to `node_modules/.reactivescript/prelude.d.ts`.
|
|
15
|
+
- `src/index.js` is the language plugin (exact mappings carry every feature;
|
|
16
|
+
ranges only place type errors) and the diagnostics service.
|
|
17
|
+
- `bin/server.js` is the server, over stdio or IPC.
|
|
18
|
+
|
|
19
|
+
The project needs a `tsconfig.json` with Solid's JSX (`"jsx": "preserve"`,
|
|
20
|
+
`"jsxImportSource": "@solidjs/web"`) and Solid in its `node_modules`.
|
|
21
|
+
|
|
22
|
+
## Running it
|
|
23
|
+
|
|
24
|
+
From npm (published from this folder, `../RELEASING.md`):
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx @reactivescript/language-server --stdio
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
From the repository:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
cargo build --lib --release --target wasm32-unknown-unknown --manifest-path ../../compiler/Cargo.toml
|
|
34
|
+
npm install
|
|
35
|
+
node bin/server.js --stdio
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`initializationOptions: { typescript: { tsdk } }` picks TypeScript's `lib`
|
|
39
|
+
folder; without it, the server uses its own `typescript`.
|
|
40
|
+
|
|
41
|
+
## Editors
|
|
42
|
+
|
|
43
|
+
**VS Code** (and Cursor, VSCodium): the extension in
|
|
44
|
+
[`../vscode-rx`](../vscode-rx), which bundles this server and the WebAssembly
|
|
45
|
+
build.
|
|
46
|
+
|
|
47
|
+
**Neovim** (0.11+), in `init.lua`:
|
|
48
|
+
|
|
49
|
+
```lua
|
|
50
|
+
vim.filetype.add({ extension = { rx = "reactivescript" } })
|
|
51
|
+
vim.lsp.config("rx", {
|
|
52
|
+
cmd = { "node", "/path/to/reactivescript/tooling/rx-language-server/bin/server.js", "--stdio" },
|
|
53
|
+
filetypes = { "reactivescript" },
|
|
54
|
+
root_markers = { "tsconfig.json", "package.json" },
|
|
55
|
+
})
|
|
56
|
+
vim.lsp.enable("rx")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Neovim has no `.rx` grammar yet; the server's semantic tokens color names.
|
|
60
|
+
|
|
61
|
+
**Helix**, in `languages.toml`:
|
|
62
|
+
|
|
63
|
+
```toml
|
|
64
|
+
[language-server.rx]
|
|
65
|
+
command = "node"
|
|
66
|
+
args = ["/path/to/reactivescript/tooling/rx-language-server/bin/server.js", "--stdio"]
|
|
67
|
+
|
|
68
|
+
[[language]]
|
|
69
|
+
name = "reactivescript"
|
|
70
|
+
scope = "source.rx"
|
|
71
|
+
file-types = ["rx"]
|
|
72
|
+
roots = ["tsconfig.json", "package.json"]
|
|
73
|
+
language-servers = ["rx"]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**JetBrains IDEs** (WebStorm, IntelliJ): install the
|
|
77
|
+
[LSP4IJ](https://plugins.jetbrains.com/plugin/23257-lsp4ij) plugin, add a
|
|
78
|
+
language server with the command
|
|
79
|
+
`node /path/to/reactivescript/tooling/rx-language-server/bin/server.js --stdio`,
|
|
80
|
+
map the file name pattern `*.rx` to it, and add the `tooling/vscode-rx`
|
|
81
|
+
folder under Settings › Editor › TextMate Bundles for highlighting.
|
|
82
|
+
|
|
83
|
+
**Zed** needs an extension of its own (a Zed extension declares the language
|
|
84
|
+
and starts the server); there isn't one yet.
|
|
85
|
+
|
|
86
|
+
The `/path/to/reactivescript` paths are a checkout of this repository; with
|
|
87
|
+
the npm package, the command is `npx @reactivescript/language-server --stdio`
|
|
88
|
+
instead (or `rx-language-server --stdio` once installed).
|
|
89
|
+
|
|
90
|
+
## Tests
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
cargo build --release --manifest-path ../../compiler/Cargo.toml
|
|
94
|
+
npm install && node test/run.js # VERBOSE=1 prints each answer
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`test/run.js` drives the server over LSP on a copy of `examples/shop` (Solid's
|
|
98
|
+
types from `ref/node_modules`, via `ref/scripts/solid-next.sh`): hover, go to
|
|
99
|
+
definition across modules, references, completion while typing (in code and in
|
|
100
|
+
markup), a parse error, a TypeScript type error and a crate diagnostic, each
|
|
101
|
+
placed on the `.rx`.
|
package/bin/server.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The .rx language server over stdio (or whatever transport the client asks for): Volar's TypeScript project with the
|
|
3
|
+
// .rx language plugin, TypeScript's services, and the crate's diagnostics.
|
|
4
|
+
const { createConnection, createServer, createTypeScriptProject, loadTsdkByPath } = require("@volar/language-server/node");
|
|
5
|
+
const { create: createTypeScriptServices } = require("volar-service-typescript");
|
|
6
|
+
const { rxLanguagePlugin, rxDiagnostics } = require("../src/index.js");
|
|
7
|
+
|
|
8
|
+
const connection = createConnection();
|
|
9
|
+
const server = createServer(connection);
|
|
10
|
+
|
|
11
|
+
connection.listen();
|
|
12
|
+
|
|
13
|
+
connection.onInitialize(params => {
|
|
14
|
+
const tsdkPath = params.initializationOptions?.typescript?.tsdk ?? require("node:path").dirname(require.resolve("typescript"));
|
|
15
|
+
const tsdk = loadTsdkByPath(tsdkPath, params.locale);
|
|
16
|
+
return server.initialize(
|
|
17
|
+
params,
|
|
18
|
+
createTypeScriptProject(tsdk.typescript, tsdk.diagnosticMessages, () => ({ languagePlugins: [rxLanguagePlugin(tsdk.typescript)] })),
|
|
19
|
+
[...createTypeScriptServices(tsdk.typescript), rxDiagnostics()],
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
connection.onInitialized(server.initialized);
|
|
24
|
+
connection.onShutdown(server.shutdown);
|
package/package.json
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactivescript/language-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The .rx language server (spec/11 CP-8): Volar over the crate's TSX projection, TypeScript's answers mapped back onto .rx, the crate's own diagnostics.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/solidjs/reactivescript.git",
|
|
9
|
+
"directory": "tooling/rx-language-server"
|
|
10
|
+
},
|
|
11
|
+
"type": "commonjs",
|
|
12
|
+
"bin": {
|
|
13
|
+
"rx-language-server": "./bin/server.js"
|
|
14
|
+
},
|
|
15
|
+
"main": "./src/index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"bin",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node test/run.js",
|
|
25
|
+
"prepack": "node scripts/wasm.js"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@volar/language-core": "2.4.30",
|
|
29
|
+
"@volar/language-server": "2.4.30",
|
|
30
|
+
"@volar/typescript": "2.4.30",
|
|
31
|
+
"volar-service-typescript": "0.0.71",
|
|
32
|
+
"vscode-uri": "^3.0.8",
|
|
33
|
+
"typescript": "^6.0.3"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@volar/test-utils": "2.4.30"
|
|
37
|
+
}
|
|
10
38
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// The .rx language plugin for Volar (spec/11 CP-8). A `.rx` file's virtual code is its TSX projection (spec/13 TY-1c),
|
|
2
|
+
// made by the crate with mappings onto the `.rx`: TypeScript's hover, definition, references, rename, completion and
|
|
3
|
+
// type errors come back through them. The crate's own diagnostics (rule IDs, spec/99) are a service of their own.
|
|
4
|
+
const { analyze, preludeFor } = require("./rxc");
|
|
5
|
+
|
|
6
|
+
/** Every feature, where the projection copies the `.rx`'s text (a name, a member, a literal, a type, a tag). */
|
|
7
|
+
const EXACT = { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: false };
|
|
8
|
+
/** Where it wrote text for a piece of the `.rx` (an expression, a statement): only for placing type errors. */
|
|
9
|
+
const COARSE = { verification: true };
|
|
10
|
+
|
|
11
|
+
/** A module: `.rx`, not a declaration file (`.d.rx`, MO-8/MO-9), which the crate reads beside the modules that import it. */
|
|
12
|
+
const isModule = f => f.endsWith(".rx") && !f.endsWith(".d.rx");
|
|
13
|
+
|
|
14
|
+
const snapshotOf = text => ({ getText: (s, e) => text.slice(s, e), getLength: () => text.length, getChangeRange: () => undefined });
|
|
15
|
+
|
|
16
|
+
/** The last analysis of each file, by its text: the virtual code and the diagnostics share one `rxc` run. */
|
|
17
|
+
const cache = new Map();
|
|
18
|
+
function analysis(file, text) {
|
|
19
|
+
const hit = cache.get(file);
|
|
20
|
+
if (hit && hit.text === text) return hit.result;
|
|
21
|
+
let result;
|
|
22
|
+
try { result = analyze(file, text, preludeFor(file)); } catch (e) {
|
|
23
|
+
console.error(`rx: analyzing ${file} failed: ${e?.stack ?? e}`); // the server's log, else the failure is silent
|
|
24
|
+
result = { code: null, mappings: [], ranges: [], diagnostics: [], error: String(e) };
|
|
25
|
+
}
|
|
26
|
+
cache.set(file, { text, result });
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function virtualCode(file, snapshot) {
|
|
31
|
+
const text = snapshot.getText(0, snapshot.getLength());
|
|
32
|
+
const a = analysis(file, text);
|
|
33
|
+
const code = a.code ?? "";
|
|
34
|
+
const mappings = [
|
|
35
|
+
...(a.mappings ?? []).map(([g, s, n]) => ({ sourceOffsets: [s], generatedOffsets: [g], lengths: [n], data: EXACT })),
|
|
36
|
+
...(a.ranges ?? []).map(([g, gn, s, n]) => ({ sourceOffsets: [s], generatedOffsets: [g], lengths: [n], generatedLengths: [gn], data: COARSE })),
|
|
37
|
+
];
|
|
38
|
+
return {
|
|
39
|
+
id: "root",
|
|
40
|
+
languageId: "reactivescript",
|
|
41
|
+
snapshot,
|
|
42
|
+
mappings: [{ sourceOffsets: [0], generatedOffsets: [0], lengths: [text.length], data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true } }],
|
|
43
|
+
embeddedCodes: [{ id: "tsx", languageId: "typescriptreact", snapshot: snapshotOf(code), mappings }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @param {typeof import("typescript")} ts */
|
|
48
|
+
function rxLanguagePlugin(ts, asFileName = uri => uri.fsPath ?? String(uri)) {
|
|
49
|
+
return {
|
|
50
|
+
getLanguageId(id) { return isModule(String(id.path ?? id)) ? "reactivescript" : undefined; },
|
|
51
|
+
createVirtualCode(id, languageId, snapshot) {
|
|
52
|
+
if (languageId !== "reactivescript" || !isModule(asFileName(id))) return undefined;
|
|
53
|
+
return virtualCode(asFileName(id), snapshot);
|
|
54
|
+
},
|
|
55
|
+
typescript: {
|
|
56
|
+
extraFileExtensions: [{ extension: "rx", isMixedContent: true, scriptKind: ts.ScriptKind.Deferred }],
|
|
57
|
+
getServiceScript(root) {
|
|
58
|
+
const tsx = root.embeddedCodes?.find(c => c.id === "tsx");
|
|
59
|
+
return tsx ? { code: tsx, extension: ".tsx", scriptKind: ts.ScriptKind.TSX } : undefined;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The crate's diagnostics on a `.rx` document: the editor's parse's errors, else the checker's (CP-8d). Volar hands
|
|
66
|
+
* the service the root virtual code's document, whose URI encodes the `.rx` file's. */
|
|
67
|
+
function rxDiagnostics() {
|
|
68
|
+
return {
|
|
69
|
+
name: "reactivescript-diagnostics",
|
|
70
|
+
capabilities: { diagnosticProvider: { interFileDependencies: true, workspaceDiagnostics: false } },
|
|
71
|
+
create(context) {
|
|
72
|
+
return {
|
|
73
|
+
provideDiagnostics(document) {
|
|
74
|
+
if (document.languageId !== "reactivescript") return;
|
|
75
|
+
const { URI } = require("vscode-uri");
|
|
76
|
+
const uri = URI.parse(document.uri);
|
|
77
|
+
const source = context.decodeEmbeddedDocumentUri(uri)?.[0] ?? uri;
|
|
78
|
+
if (source.scheme !== "file") return;
|
|
79
|
+
const file = source.fsPath;
|
|
80
|
+
if (!isModule(file)) return;
|
|
81
|
+
const a = analysis(file, document.getText());
|
|
82
|
+
return (a.diagnostics ?? []).filter(d => d.range).map(d => ({
|
|
83
|
+
range: {
|
|
84
|
+
start: { line: d.range.start.line - 1, character: d.range.start.column },
|
|
85
|
+
end: { line: d.range.end.line - 1, character: d.range.end.column },
|
|
86
|
+
},
|
|
87
|
+
severity: 1,
|
|
88
|
+
source: "rx",
|
|
89
|
+
code: d.code,
|
|
90
|
+
message: `${d.message} (${d.rule})`,
|
|
91
|
+
}));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { rxLanguagePlugin, rxDiagnostics, analysis };
|
package/src/rxc.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// The crate's side of the server: `rxc serve`'s requests (spec/11 CP-8e). Volar asks for a file's virtual code
|
|
2
|
+
// synchronously, so the requests are synchronous calls: into the crate's WebAssembly build, loaded in process (one build
|
|
3
|
+
// for every platform), or, when $RXC names a binary, a short `rxc serve` run per request.
|
|
4
|
+
const { spawnSync } = require("node:child_process");
|
|
5
|
+
const { existsSync, mkdirSync, writeFileSync, readFileSync, statSync } = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
const repo = path.resolve(__dirname, "../../..");
|
|
9
|
+
|
|
10
|
+
/** The `rxc` binary, when $RXC names one. */
|
|
11
|
+
function rxcPath() {
|
|
12
|
+
return process.env.RXC || path.join(repo, "compiler/target/release/rxc");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The WebAssembly build: $RX_WASM, else the one bundled beside this file (the packaged extension), else the
|
|
16
|
+
* repository's (`cargo build --lib --release --target wasm32-unknown-unknown`). */
|
|
17
|
+
function wasmPath() {
|
|
18
|
+
if (process.env.RX_WASM) return process.env.RX_WASM;
|
|
19
|
+
for (const f of [path.join(__dirname, "rxc.wasm"), path.join(repo, "compiler/target/wasm32-unknown-unknown/release/reactivescript.wasm")]) if (existsSync(f)) return f;
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The crate in process, over `src/wasm.rs`'s ABI. */
|
|
24
|
+
function loadWasm(file) {
|
|
25
|
+
let x;
|
|
26
|
+
const enc = new TextEncoder(), dec = new TextDecoder();
|
|
27
|
+
const put = bytes => {
|
|
28
|
+
const p = x.rx_alloc(bytes.length + 4);
|
|
29
|
+
const m = new Uint8Array(x.memory.buffer);
|
|
30
|
+
new DataView(x.memory.buffer).setUint32(p, bytes.length, true);
|
|
31
|
+
m.set(bytes, p + 4);
|
|
32
|
+
return p;
|
|
33
|
+
};
|
|
34
|
+
const env = {
|
|
35
|
+
rx_read(ptr, len) {
|
|
36
|
+
const f = dec.decode(new Uint8Array(x.memory.buffer, ptr, len));
|
|
37
|
+
try { if (!statSync(f).isFile()) return 0; return put(readFileSync(f)); } catch { return 0; }
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
x = new WebAssembly.Instance(new WebAssembly.Module(readFileSync(file)), { env }).exports;
|
|
41
|
+
return (method, params) => {
|
|
42
|
+
const req = enc.encode(JSON.stringify({ method, params }));
|
|
43
|
+
const p = x.rx_alloc(req.length);
|
|
44
|
+
new Uint8Array(x.memory.buffer).set(req, p);
|
|
45
|
+
const out = x.rx_call(p, req.length);
|
|
46
|
+
const n = new DataView(x.memory.buffer).getUint32(out, true);
|
|
47
|
+
const text = dec.decode(new Uint8Array(x.memory.buffer, out + 4, n));
|
|
48
|
+
x.rx_free(out, n + 4);
|
|
49
|
+
return JSON.parse(text);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function viaProcess(method, params) {
|
|
54
|
+
const r = spawnSync(rxcPath(), ["serve"], { input: JSON.stringify({ id: 1, method, params }) + "\n", maxBuffer: 1 << 28 });
|
|
55
|
+
if (r.error) throw r.error;
|
|
56
|
+
const line = r.stdout.toString().split("\n").find(Boolean);
|
|
57
|
+
if (!line) throw new Error(`rxc serve: no answer (${r.stderr.toString().slice(0, 300)})`);
|
|
58
|
+
return JSON.parse(line).result;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let wasm;
|
|
62
|
+
function ask(method, params) {
|
|
63
|
+
if (process.env.RXC || !wasmPath()) return viaProcess(method, params);
|
|
64
|
+
try {
|
|
65
|
+
return (wasm ??= loadWasm(wasmPath()))(method, params);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
wasm = undefined; // a trap (a panic in the crate) leaves the instance unusable: the next request makes a new one
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A file's projection (`code`, `mappings`, `ranges`, UTF-16 offsets) and its diagnostics, as the editor has it. */
|
|
73
|
+
function analyze(file, text, prelude) {
|
|
74
|
+
return ask("analyze", { file, overlays: { [file]: text }, prelude });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Where a file's projection imports the prelude from: written once beside the nearest `node_modules`, so its own
|
|
78
|
+
* `@solidjs/web` resolves; a `.d.ts`, so the import needs no extension. */
|
|
79
|
+
const preludes = new Map();
|
|
80
|
+
function preludeFor(file) {
|
|
81
|
+
let dir = path.dirname(file);
|
|
82
|
+
while (!existsSync(path.join(dir, "node_modules")) && path.dirname(dir) !== dir) dir = path.dirname(dir);
|
|
83
|
+
const at = path.join(dir, "node_modules", ".reactivescript", "prelude");
|
|
84
|
+
if (!preludes.has(at)) {
|
|
85
|
+
const text = ask("prelude", {});
|
|
86
|
+
mkdirSync(path.dirname(at), { recursive: true });
|
|
87
|
+
const f = at + ".d.ts";
|
|
88
|
+
if (!existsSync(f) || readFileSync(f, "utf8") !== text) writeFileSync(f, text);
|
|
89
|
+
preludes.set(at, true);
|
|
90
|
+
}
|
|
91
|
+
return at;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { analyze, preludeFor, rxcPath, wasmPath };
|
package/src/rxc.wasm
ADDED
|
Binary file
|