conjure-js 0.0.8 → 0.0.11
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/dist-cli/conjure-js.mjs +393 -101
- package/package.json +8 -11
- package/scripts/gen-core-source.mjs +126 -0
- package/src/bin/__fixtures__/smoke/repl-smoke.ts +6 -1
- package/src/bin/cli.ts +3 -51
- package/src/bin/nrepl.ts +244 -58
- package/src/bin/version.ts +1 -1
- package/src/clojure/core.clj.d.ts +0 -123
- package/src/clojure/demo/math.clj +23 -6
- package/src/clojure/demo/math.clj.d.ts +2 -2
- package/src/clojure/demo.clj +22 -1
- package/src/core/assertions.ts +3 -0
- package/src/core/core-env.ts +2 -0
- package/src/core/env.ts +32 -12
- package/src/core/evaluator/evaluate.ts +14 -4
- package/src/core/evaluator/special-forms.ts +57 -2
- package/src/core/factories.ts +4 -0
- package/src/core/index.ts +8 -1
- package/src/core/printer.ts +2 -0
- package/src/core/reader.ts +16 -0
- package/src/core/session.ts +71 -37
- package/src/core/stdlib/meta.ts +1 -0
- package/src/core/stdlib/utils.ts +1 -0
- package/src/core/stdlib/vars.ts +53 -0
- package/src/core/tokenizer.ts +4 -0
- package/src/core/types.ts +25 -5
- package/src/host/browser.ts +12 -0
- package/src/host/node.ts +55 -0
- package/src/vite-plugin-clj/clj.d.ts +8 -0
- package/src/vite-plugin-clj/codegen.ts +201 -0
- package/src/vite-plugin-clj/index.ts +190 -0
- package/src/vite-plugin-clj/namespace-utils.ts +79 -0
- package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/assets/editor.worker-CdQrwHl8.js +0 -26
- package/dist/assets/main-A7ZMId9A.css +0 -1
- package/dist/assets/main-CmI-7epE.js +0 -3137
- package/dist/index.html +0 -195
- package/dist/vite.svg +0 -1
- package/src/main.ts +0 -1
- package/src/monaco-esm.d.ts +0 -7
- package/src/playground/clojure-tokens.ts +0 -67
- package/src/playground/editor.worker.ts +0 -5
- package/src/playground/find-form.ts +0 -138
- package/src/playground/playground.ts +0 -342
- package/src/playground/samples/00-welcome.clj +0 -385
- package/src/playground/samples/01-collections.clj +0 -191
- package/src/playground/samples/02-higher-order-functions.clj +0 -215
- package/src/playground/samples/03-destructuring.clj +0 -194
- package/src/playground/samples/04-strings-and-regex.clj +0 -202
- package/src/playground/samples/05-error-handling.clj +0 -212
- package/src/repl/repl.ts +0 -116
package/dist-cli/conjure-js.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// src/bin/cli.ts
|
|
3
|
-
import { existsSync as
|
|
3
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, realpathSync } from "node:fs";
|
|
4
4
|
import { dirname as dirname2, resolve as resolve3 } from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { stdin as input, stdout as output } from "node:process";
|
|
@@ -24,7 +24,8 @@ var valueKeywords = {
|
|
|
24
24
|
atom: "atom",
|
|
25
25
|
reduced: "reduced",
|
|
26
26
|
volatile: "volatile",
|
|
27
|
-
regex: "regex"
|
|
27
|
+
regex: "regex",
|
|
28
|
+
var: "var"
|
|
28
29
|
};
|
|
29
30
|
var tokenKeywords = {
|
|
30
31
|
LParen: "LParen",
|
|
@@ -45,7 +46,8 @@ var tokenKeywords = {
|
|
|
45
46
|
Symbol: "Symbol",
|
|
46
47
|
AnonFnStart: "AnonFnStart",
|
|
47
48
|
Deref: "Deref",
|
|
48
|
-
Regex: "Regex"
|
|
49
|
+
Regex: "Regex",
|
|
50
|
+
VarQuote: "VarQuote"
|
|
49
51
|
};
|
|
50
52
|
var tokenSymbols = {
|
|
51
53
|
Quote: "quote",
|
|
@@ -108,6 +110,12 @@ class EnvError extends Error {
|
|
|
108
110
|
this.name = "EnvError";
|
|
109
111
|
}
|
|
110
112
|
}
|
|
113
|
+
function derefValue(val) {
|
|
114
|
+
return val.kind === "var" ? val.value : val;
|
|
115
|
+
}
|
|
116
|
+
function makeNamespace(name) {
|
|
117
|
+
return { name, vars: new Map, aliases: new Map, readerAliases: new Map };
|
|
118
|
+
}
|
|
111
119
|
function makeEnv(outer) {
|
|
112
120
|
return {
|
|
113
121
|
bindings: new Map,
|
|
@@ -117,9 +125,12 @@ function makeEnv(outer) {
|
|
|
117
125
|
function lookup(name, env) {
|
|
118
126
|
let current = env;
|
|
119
127
|
while (current) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
const raw = current.bindings.get(name);
|
|
129
|
+
if (raw !== undefined)
|
|
130
|
+
return derefValue(raw);
|
|
131
|
+
const v = current.ns?.vars.get(name);
|
|
132
|
+
if (v !== undefined)
|
|
133
|
+
return v.value;
|
|
123
134
|
current = current.outer;
|
|
124
135
|
}
|
|
125
136
|
throw new EvaluationError(`Symbol ${name} not found`, { name });
|
|
@@ -127,9 +138,25 @@ function lookup(name, env) {
|
|
|
127
138
|
function tryLookup(name, env) {
|
|
128
139
|
let current = env;
|
|
129
140
|
while (current) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
const raw = current.bindings.get(name);
|
|
142
|
+
if (raw !== undefined)
|
|
143
|
+
return derefValue(raw);
|
|
144
|
+
const v = current.ns?.vars.get(name);
|
|
145
|
+
if (v !== undefined)
|
|
146
|
+
return v.value;
|
|
147
|
+
current = current.outer;
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
function lookupVar(name, env) {
|
|
152
|
+
let current = env;
|
|
153
|
+
while (current) {
|
|
154
|
+
const raw = current.bindings.get(name);
|
|
155
|
+
if (raw !== undefined && raw.kind === "var")
|
|
156
|
+
return raw;
|
|
157
|
+
const v = current.ns?.vars.get(name);
|
|
158
|
+
if (v !== undefined)
|
|
159
|
+
return v;
|
|
133
160
|
current = current.outer;
|
|
134
161
|
}
|
|
135
162
|
return;
|
|
@@ -161,7 +188,7 @@ function getRootEnv(env) {
|
|
|
161
188
|
function getNamespaceEnv(env) {
|
|
162
189
|
let current = env;
|
|
163
190
|
while (current) {
|
|
164
|
-
if (current.
|
|
191
|
+
if (current.ns)
|
|
165
192
|
return current;
|
|
166
193
|
current = current.outer;
|
|
167
194
|
}
|
|
@@ -204,6 +231,7 @@ var cljRegex = (pattern, flags = "") => ({
|
|
|
204
231
|
pattern,
|
|
205
232
|
flags
|
|
206
233
|
});
|
|
234
|
+
var cljVar = (ns, name, value, meta) => ({ kind: "var", ns, name, value, meta });
|
|
207
235
|
var cljAtom = (value) => ({ kind: "atom", value });
|
|
208
236
|
var cljReduced = (value) => ({
|
|
209
237
|
kind: "reduced",
|
|
@@ -651,7 +679,8 @@ var specialFormKeywords = {
|
|
|
651
679
|
recur: "recur",
|
|
652
680
|
defmulti: "defmulti",
|
|
653
681
|
defmethod: "defmethod",
|
|
654
|
-
try: "try"
|
|
682
|
+
try: "try",
|
|
683
|
+
var: "var"
|
|
655
684
|
};
|
|
656
685
|
function keywordToDispatchFn(kw) {
|
|
657
686
|
return cljNativeFunction(`kw:${kw.name}`, (...args) => {
|
|
@@ -777,7 +806,15 @@ function evaluateDef(list, env, ctx) {
|
|
|
777
806
|
}
|
|
778
807
|
if (list.value[2] === undefined)
|
|
779
808
|
return cljNil();
|
|
780
|
-
|
|
809
|
+
const nsEnv = getNamespaceEnv(env);
|
|
810
|
+
const cljNs = nsEnv.ns;
|
|
811
|
+
const newValue = ctx.evaluate(list.value[2], env);
|
|
812
|
+
const existing = cljNs.vars.get(name.name);
|
|
813
|
+
if (existing) {
|
|
814
|
+
existing.value = newValue;
|
|
815
|
+
} else {
|
|
816
|
+
cljNs.vars.set(name.name, cljVar(cljNs.name, name.name, newValue));
|
|
817
|
+
}
|
|
781
818
|
return cljNil();
|
|
782
819
|
}
|
|
783
820
|
var evaluateNs = (_list, _env, _ctx) => {
|
|
@@ -939,6 +976,38 @@ function evaluateDefmethod(list, env, ctx) {
|
|
|
939
976
|
define(mmName.name, updated, getNamespaceEnv(env));
|
|
940
977
|
return cljNil();
|
|
941
978
|
}
|
|
979
|
+
function evaluateVar(list, env, _ctx) {
|
|
980
|
+
const sym = list.value[1];
|
|
981
|
+
if (!isSymbol(sym)) {
|
|
982
|
+
throw new EvaluationError("var expects a symbol", { list });
|
|
983
|
+
}
|
|
984
|
+
const slashIdx = sym.name.indexOf("/");
|
|
985
|
+
if (slashIdx > 0 && slashIdx < sym.name.length - 1) {
|
|
986
|
+
const alias = sym.name.slice(0, slashIdx);
|
|
987
|
+
const localName = sym.name.slice(slashIdx + 1);
|
|
988
|
+
const nsEnv = getNamespaceEnv(env);
|
|
989
|
+
const aliasCljNs = nsEnv.ns?.aliases.get(alias);
|
|
990
|
+
if (aliasCljNs) {
|
|
991
|
+
const v3 = aliasCljNs.vars.get(localName);
|
|
992
|
+
if (!v3)
|
|
993
|
+
throw new EvaluationError(`Var ${sym.name} not found`, { sym });
|
|
994
|
+
return v3;
|
|
995
|
+
}
|
|
996
|
+
const targetEnv = getRootEnv(env).resolveNs?.(alias) ?? null;
|
|
997
|
+
if (!targetEnv) {
|
|
998
|
+
throw new EvaluationError(`No such namespace: ${alias}`, { sym });
|
|
999
|
+
}
|
|
1000
|
+
const v2 = lookupVar(localName, targetEnv);
|
|
1001
|
+
if (!v2)
|
|
1002
|
+
throw new EvaluationError(`Var ${sym.name} not found`, { sym });
|
|
1003
|
+
return v2;
|
|
1004
|
+
}
|
|
1005
|
+
const v = lookupVar(sym.name, env);
|
|
1006
|
+
if (!v) {
|
|
1007
|
+
throw new EvaluationError(`Unable to resolve var: ${sym.name} in this context`, { sym });
|
|
1008
|
+
}
|
|
1009
|
+
return v;
|
|
1010
|
+
}
|
|
942
1011
|
var specialFormEvaluatorEntries = {
|
|
943
1012
|
try: evaluateTry,
|
|
944
1013
|
quote: evaluateQuote,
|
|
@@ -953,7 +1022,8 @@ var specialFormEvaluatorEntries = {
|
|
|
953
1022
|
loop: evaluateLoop,
|
|
954
1023
|
recur: evaluateRecur,
|
|
955
1024
|
defmulti: evaluateDefmulti,
|
|
956
|
-
defmethod: evaluateDefmethod
|
|
1025
|
+
defmethod: evaluateDefmethod,
|
|
1026
|
+
var: evaluateVar
|
|
957
1027
|
};
|
|
958
1028
|
function evaluateSpecialForm(symbol, list, env, ctx) {
|
|
959
1029
|
const evalFn = specialFormEvaluatorEntries[symbol];
|
|
@@ -995,6 +1065,7 @@ var isAtom = (value) => value.kind === "atom";
|
|
|
995
1065
|
var isReduced = (value) => value.kind === "reduced";
|
|
996
1066
|
var isVolatile = (value) => value.kind === "volatile";
|
|
997
1067
|
var isRegex = (value) => value.kind === "regex";
|
|
1068
|
+
var isVar = (value) => value.kind === "var";
|
|
998
1069
|
var isCollection = (value) => isVector(value) || isMap(value) || isList(value);
|
|
999
1070
|
var isSeqable = (value) => isCollection(value) || value.kind === "string";
|
|
1000
1071
|
var equalityHandlers = {
|
|
@@ -1036,7 +1107,8 @@ var equalityHandlers = {
|
|
|
1036
1107
|
[valueKeywords.atom]: (a, b) => a === b,
|
|
1037
1108
|
[valueKeywords.reduced]: (a, b) => isEqual(a.value, b.value),
|
|
1038
1109
|
[valueKeywords.volatile]: (a, b) => a === b,
|
|
1039
|
-
[valueKeywords.regex]: (a, b) => a === b
|
|
1110
|
+
[valueKeywords.regex]: (a, b) => a === b,
|
|
1111
|
+
[valueKeywords.var]: (a, b) => a === b
|
|
1040
1112
|
};
|
|
1041
1113
|
var isEqual = (a, b) => {
|
|
1042
1114
|
if (a.kind !== b.kind)
|
|
@@ -1118,6 +1190,8 @@ function printString(value) {
|
|
|
1118
1190
|
const prefix = value.flags ? `(?${value.flags})` : "";
|
|
1119
1191
|
return `#"${prefix}${escaped}"`;
|
|
1120
1192
|
}
|
|
1193
|
+
case valueKeywords.var:
|
|
1194
|
+
return `#'${value.ns}/${value.name}`;
|
|
1121
1195
|
default:
|
|
1122
1196
|
throw new EvaluationError(`unhandled value type: ${value.kind}`, {
|
|
1123
1197
|
value
|
|
@@ -2041,6 +2115,8 @@ var metaFunctions = {
|
|
|
2041
2115
|
if (val.kind === "function" || val.kind === "native-function") {
|
|
2042
2116
|
return val.meta ?? cljNil();
|
|
2043
2117
|
}
|
|
2118
|
+
if (val.kind === "var")
|
|
2119
|
+
return val.meta ?? cljNil();
|
|
2044
2120
|
return cljNil();
|
|
2045
2121
|
}), "Returns the metadata map of a value, or nil if the value has no metadata.", [["val"]]),
|
|
2046
2122
|
"with-meta": withDoc(cljNativeFunction("with-meta", (val, m) => {
|
|
@@ -2257,7 +2333,18 @@ function evaluateWithContext(expr, env, ctx) {
|
|
|
2257
2333
|
const alias = expr.name.slice(0, slashIdx);
|
|
2258
2334
|
const sym = expr.name.slice(slashIdx + 1);
|
|
2259
2335
|
const nsEnv = getNamespaceEnv(env);
|
|
2260
|
-
const
|
|
2336
|
+
const aliasCljNs = nsEnv.ns?.aliases.get(alias);
|
|
2337
|
+
if (aliasCljNs) {
|
|
2338
|
+
const v = aliasCljNs.vars.get(sym);
|
|
2339
|
+
if (v === undefined) {
|
|
2340
|
+
throw new EvaluationError(`Symbol ${expr.name} not found`, {
|
|
2341
|
+
symbol: expr.name,
|
|
2342
|
+
env
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
return v.value;
|
|
2346
|
+
}
|
|
2347
|
+
const targetEnv = getRootEnv(env).resolveNs?.(alias) ?? null;
|
|
2261
2348
|
if (!targetEnv) {
|
|
2262
2349
|
throw new EvaluationError(`No such namespace or alias: ${alias}`, {
|
|
2263
2350
|
symbol: expr.name,
|
|
@@ -2819,6 +2906,7 @@ var utilFunctions = {
|
|
|
2819
2906
|
map: ":map",
|
|
2820
2907
|
function: ":function",
|
|
2821
2908
|
regex: ":regex",
|
|
2909
|
+
var: ":var",
|
|
2822
2910
|
"native-function": ":function"
|
|
2823
2911
|
};
|
|
2824
2912
|
const name = kindToKeyword[x.kind];
|
|
@@ -2951,6 +3039,28 @@ var utilFunctions = {
|
|
|
2951
3039
|
}), "Coerces to boolean. Everything is true except false and nil.", [["x"]])
|
|
2952
3040
|
};
|
|
2953
3041
|
|
|
3042
|
+
// src/core/stdlib/vars.ts
|
|
3043
|
+
var varFunctions = {
|
|
3044
|
+
"var?": withDoc(cljNativeFunction("var?", (x) => cljBoolean(isVar(x))), "Returns true if x is a Var.", [["x"]]),
|
|
3045
|
+
"var-get": withDoc(cljNativeFunction("var-get", (x) => {
|
|
3046
|
+
if (!isVar(x)) {
|
|
3047
|
+
throw new EvaluationError(`var-get expects a Var, got ${x.kind}`, { x });
|
|
3048
|
+
}
|
|
3049
|
+
return x.value;
|
|
3050
|
+
}), "Returns the value in the Var object.", [["x"]]),
|
|
3051
|
+
"alter-var-root": withDoc(cljNativeFunctionWithContext("alter-var-root", (ctx, callEnv, varVal, f, ...args) => {
|
|
3052
|
+
if (!isVar(varVal)) {
|
|
3053
|
+
throw new EvaluationError(`alter-var-root expects a Var as its first argument, got ${varVal.kind}`, { varVal });
|
|
3054
|
+
}
|
|
3055
|
+
if (!isAFunction(f)) {
|
|
3056
|
+
throw new EvaluationError(`alter-var-root expects a function as its second argument, got ${f.kind}`, { f });
|
|
3057
|
+
}
|
|
3058
|
+
const newVal = ctx.applyFunction(f, [varVal.value, ...args], callEnv);
|
|
3059
|
+
varVal.value = newVal;
|
|
3060
|
+
return newVal;
|
|
3061
|
+
}), "Atomically alters the root binding of var v by applying f to its current value plus any additional args.", [["v", "f", "&", "args"]])
|
|
3062
|
+
};
|
|
3063
|
+
|
|
2954
3064
|
// src/core/core-env.ts
|
|
2955
3065
|
var nativeFunctions = {
|
|
2956
3066
|
...arithmeticFunctions,
|
|
@@ -2963,7 +3073,8 @@ var nativeFunctions = {
|
|
|
2963
3073
|
...transducerFunctions,
|
|
2964
3074
|
...regexFunctions,
|
|
2965
3075
|
...stringFunctions,
|
|
2966
|
-
...utilFunctions
|
|
3076
|
+
...utilFunctions,
|
|
3077
|
+
...varFunctions
|
|
2967
3078
|
};
|
|
2968
3079
|
function loadCoreFunctions(env, output) {
|
|
2969
3080
|
for (const [key, value] of Object.entries(nativeFunctions)) {
|
|
@@ -3287,6 +3398,10 @@ function parseDispatch(ctx) {
|
|
|
3287
3398
|
if (next === '"') {
|
|
3288
3399
|
return parseRegexLiteral(ctx, start);
|
|
3289
3400
|
}
|
|
3401
|
+
if (next === "'") {
|
|
3402
|
+
scanner.advance();
|
|
3403
|
+
return { kind: tokenKeywords.VarQuote, start, end: scanner.position() };
|
|
3404
|
+
}
|
|
3290
3405
|
if (next === "{") {
|
|
3291
3406
|
throw new TokenizerError("Set literals are not yet supported", start);
|
|
3292
3407
|
}
|
|
@@ -3497,6 +3612,16 @@ var readUnquote = (ctx) => {
|
|
|
3497
3612
|
}
|
|
3498
3613
|
return { kind: valueKeywords.list, value: [cljSymbol("unquote"), value] };
|
|
3499
3614
|
};
|
|
3615
|
+
var readVarQuote = (ctx) => {
|
|
3616
|
+
const scanner = ctx.scanner;
|
|
3617
|
+
const token = scanner.peek();
|
|
3618
|
+
if (!token) {
|
|
3619
|
+
throw new ReaderError("Unexpected end of input while parsing var quote", scanner.position());
|
|
3620
|
+
}
|
|
3621
|
+
scanner.advance();
|
|
3622
|
+
const value = readForm(ctx);
|
|
3623
|
+
return cljList([cljSymbol("var"), value]);
|
|
3624
|
+
};
|
|
3500
3625
|
var readDeref = (ctx) => {
|
|
3501
3626
|
const scanner = ctx.scanner;
|
|
3502
3627
|
const token = scanner.peek();
|
|
@@ -3818,6 +3943,8 @@ function readForm(ctx) {
|
|
|
3818
3943
|
return readAnonFn(ctx);
|
|
3819
3944
|
case tokenKeywords.Deref:
|
|
3820
3945
|
return readDeref(ctx);
|
|
3946
|
+
case tokenKeywords.VarQuote:
|
|
3947
|
+
return readVarQuote(ctx);
|
|
3821
3948
|
case tokenKeywords.Regex:
|
|
3822
3949
|
return readRegex(ctx);
|
|
3823
3950
|
default:
|
|
@@ -4764,10 +4891,7 @@ function processRequireSpec(spec, currentEnv, registry, resolveNamespace) {
|
|
|
4764
4891
|
position: i2
|
|
4765
4892
|
});
|
|
4766
4893
|
}
|
|
4767
|
-
|
|
4768
|
-
currentEnv.readerAliases = new Map;
|
|
4769
|
-
}
|
|
4770
|
-
currentEnv.readerAliases.set(alias.name, nsName);
|
|
4894
|
+
currentEnv.ns.readerAliases.set(alias.name, nsName);
|
|
4771
4895
|
i2++;
|
|
4772
4896
|
} else {
|
|
4773
4897
|
throw new EvaluationError(`:as-alias specs only support :as-alias, got ${kw.name}`, { spec });
|
|
@@ -4798,10 +4922,7 @@ function processRequireSpec(spec, currentEnv, registry, resolveNamespace) {
|
|
|
4798
4922
|
position: i
|
|
4799
4923
|
});
|
|
4800
4924
|
}
|
|
4801
|
-
|
|
4802
|
-
currentEnv.aliases = new Map;
|
|
4803
|
-
}
|
|
4804
|
-
currentEnv.aliases.set(alias.name, targetEnv);
|
|
4925
|
+
currentEnv.ns.aliases.set(alias.name, targetEnv.ns);
|
|
4805
4926
|
i++;
|
|
4806
4927
|
} else if (kw.name === ":refer") {
|
|
4807
4928
|
i++;
|
|
@@ -4819,13 +4940,18 @@ function processRequireSpec(spec, currentEnv, registry, resolveNamespace) {
|
|
|
4819
4940
|
sym
|
|
4820
4941
|
});
|
|
4821
4942
|
}
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
}
|
|
4826
|
-
|
|
4943
|
+
const v = lookupVar(sym.name, targetEnv);
|
|
4944
|
+
if (v !== undefined) {
|
|
4945
|
+
currentEnv.ns.vars.set(sym.name, v);
|
|
4946
|
+
} else {
|
|
4947
|
+
let value;
|
|
4948
|
+
try {
|
|
4949
|
+
value = lookup(sym.name, targetEnv);
|
|
4950
|
+
} catch {
|
|
4951
|
+
throw new EvaluationError(`Symbol ${sym.name} not found in namespace ${nsName}`, { nsName, symbol: sym.name });
|
|
4952
|
+
}
|
|
4953
|
+
define(sym.name, value, currentEnv);
|
|
4827
4954
|
}
|
|
4828
|
-
define(sym.name, value, currentEnv);
|
|
4829
4955
|
}
|
|
4830
4956
|
i++;
|
|
4831
4957
|
} else {
|
|
@@ -4833,21 +4959,31 @@ function processRequireSpec(spec, currentEnv, registry, resolveNamespace) {
|
|
|
4833
4959
|
}
|
|
4834
4960
|
}
|
|
4835
4961
|
}
|
|
4962
|
+
function cloneBindings(bindings) {
|
|
4963
|
+
const out = new Map;
|
|
4964
|
+
for (const [k, v] of bindings) {
|
|
4965
|
+
out.set(k, v.kind === "var" ? { ...v } : v);
|
|
4966
|
+
}
|
|
4967
|
+
return out;
|
|
4968
|
+
}
|
|
4836
4969
|
function cloneEnv(env, memo) {
|
|
4837
4970
|
if (memo.has(env))
|
|
4838
4971
|
return memo.get(env);
|
|
4839
4972
|
const cloned = {
|
|
4840
|
-
bindings:
|
|
4841
|
-
outer: null
|
|
4842
|
-
namespace: env.namespace
|
|
4973
|
+
bindings: cloneBindings(env.bindings),
|
|
4974
|
+
outer: null
|
|
4843
4975
|
};
|
|
4976
|
+
if (env.ns) {
|
|
4977
|
+
cloned.ns = {
|
|
4978
|
+
name: env.ns.name,
|
|
4979
|
+
vars: new Map([...env.ns.vars].map(([k, v]) => [k, { ...v }])),
|
|
4980
|
+
aliases: new Map,
|
|
4981
|
+
readerAliases: new Map(env.ns.readerAliases)
|
|
4982
|
+
};
|
|
4983
|
+
}
|
|
4844
4984
|
memo.set(env, cloned);
|
|
4845
4985
|
if (env.outer)
|
|
4846
4986
|
cloned.outer = cloneEnv(env.outer, memo);
|
|
4847
|
-
if (env.aliases)
|
|
4848
|
-
cloned.aliases = new Map([...env.aliases].map(([k, v]) => [k, cloneEnv(v, memo)]));
|
|
4849
|
-
if (env.readerAliases)
|
|
4850
|
-
cloned.readerAliases = new Map(env.readerAliases);
|
|
4851
4987
|
return cloned;
|
|
4852
4988
|
}
|
|
4853
4989
|
function cloneRegistry(registry) {
|
|
@@ -4855,6 +4991,16 @@ function cloneRegistry(registry) {
|
|
|
4855
4991
|
const next = new Map;
|
|
4856
4992
|
for (const [name, env] of registry)
|
|
4857
4993
|
next.set(name, cloneEnv(env, memo));
|
|
4994
|
+
for (const [name, env] of registry) {
|
|
4995
|
+
const clonedEnv = next.get(name);
|
|
4996
|
+
if (env.ns && clonedEnv.ns) {
|
|
4997
|
+
for (const [alias, origNs] of env.ns.aliases) {
|
|
4998
|
+
const targetCloned = next.get(origNs.name);
|
|
4999
|
+
if (targetCloned?.ns)
|
|
5000
|
+
clonedEnv.ns.aliases.set(alias, targetCloned.ns);
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
4858
5004
|
return next;
|
|
4859
5005
|
}
|
|
4860
5006
|
function buildSessionApi(state, options) {
|
|
@@ -4902,7 +5048,7 @@ function buildSessionApi(state, options) {
|
|
|
4902
5048
|
function ensureNs(name) {
|
|
4903
5049
|
if (!registry.has(name)) {
|
|
4904
5050
|
const nsEnv = makeEnv(coreEnv);
|
|
4905
|
-
nsEnv.
|
|
5051
|
+
nsEnv.ns = makeNamespace(name);
|
|
4906
5052
|
registry.set(name, nsEnv);
|
|
4907
5053
|
}
|
|
4908
5054
|
return registry.get(name);
|
|
@@ -4912,6 +5058,9 @@ function buildSessionApi(state, options) {
|
|
|
4912
5058
|
currentNs = name;
|
|
4913
5059
|
}
|
|
4914
5060
|
function getNs(name) {
|
|
5061
|
+
return registry.get(name)?.ns ?? null;
|
|
5062
|
+
}
|
|
5063
|
+
function getNsEnv(name) {
|
|
4915
5064
|
return registry.get(name) ?? null;
|
|
4916
5065
|
}
|
|
4917
5066
|
define("require", cljNativeFunction("require", (...args) => {
|
|
@@ -4969,13 +5118,17 @@ function buildSessionApi(state, options) {
|
|
|
4969
5118
|
evaluate(source) {
|
|
4970
5119
|
try {
|
|
4971
5120
|
const tokens = tokenize(source);
|
|
4972
|
-
const
|
|
5121
|
+
const declaredNs = extractNsNameFromTokens(tokens);
|
|
5122
|
+
if (declaredNs) {
|
|
5123
|
+
ensureNs(declaredNs);
|
|
5124
|
+
currentNs = declaredNs;
|
|
5125
|
+
}
|
|
5126
|
+
const env = getNsEnv(currentNs);
|
|
4973
5127
|
const aliasMap = extractAliasMapFromTokens(tokens);
|
|
4974
|
-
env.aliases
|
|
4975
|
-
|
|
4976
|
-
aliasMap.set(alias, nsEnv.namespace);
|
|
5128
|
+
env.ns?.aliases.forEach((ns, alias) => {
|
|
5129
|
+
aliasMap.set(alias, ns.name);
|
|
4977
5130
|
});
|
|
4978
|
-
env.readerAliases
|
|
5131
|
+
env.ns?.readerAliases.forEach((nsName, alias) => {
|
|
4979
5132
|
aliasMap.set(alias, nsName);
|
|
4980
5133
|
});
|
|
4981
5134
|
const forms = readForms(tokens, currentNs, aliasMap);
|
|
@@ -5003,7 +5156,7 @@ function buildSessionApi(state, options) {
|
|
|
5003
5156
|
},
|
|
5004
5157
|
evaluateForms(forms) {
|
|
5005
5158
|
try {
|
|
5006
|
-
const env =
|
|
5159
|
+
const env = getNsEnv(currentNs);
|
|
5007
5160
|
let result = cljNil();
|
|
5008
5161
|
for (const form of forms) {
|
|
5009
5162
|
const expanded = ctx.expandAll(form, env);
|
|
@@ -5028,6 +5181,9 @@ function buildSessionApi(state, options) {
|
|
|
5028
5181
|
while (env) {
|
|
5029
5182
|
for (const key of env.bindings.keys())
|
|
5030
5183
|
seen.add(key);
|
|
5184
|
+
if (env.ns)
|
|
5185
|
+
for (const key of env.ns.vars.keys())
|
|
5186
|
+
seen.add(key);
|
|
5031
5187
|
env = env.outer;
|
|
5032
5188
|
}
|
|
5033
5189
|
const candidates = [...seen];
|
|
@@ -5041,11 +5197,11 @@ function buildSessionApi(state, options) {
|
|
|
5041
5197
|
function createSession(options) {
|
|
5042
5198
|
const registry = new Map;
|
|
5043
5199
|
const coreEnv = makeEnv();
|
|
5044
|
-
coreEnv.
|
|
5200
|
+
coreEnv.ns = makeNamespace("clojure.core");
|
|
5045
5201
|
loadCoreFunctions(coreEnv, options?.output);
|
|
5046
5202
|
registry.set("clojure.core", coreEnv);
|
|
5047
5203
|
const userEnv = makeEnv(coreEnv);
|
|
5048
|
-
userEnv.
|
|
5204
|
+
userEnv.ns = makeNamespace("user");
|
|
5049
5205
|
registry.set("user", userEnv);
|
|
5050
5206
|
const session = buildSessionApi({ registry, currentNs: "user" }, options);
|
|
5051
5207
|
const coreLoader = builtInNamespaceSources["clojure.core"];
|
|
@@ -5121,8 +5277,8 @@ function discoverSourceRoots(startDir) {
|
|
|
5121
5277
|
|
|
5122
5278
|
// src/bin/nrepl.ts
|
|
5123
5279
|
import * as net from "net";
|
|
5124
|
-
import { readFileSync as
|
|
5125
|
-
import { join as join2
|
|
5280
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync, existsSync as existsSync3 } from "node:fs";
|
|
5281
|
+
import { join as join2 } from "node:path";
|
|
5126
5282
|
|
|
5127
5283
|
// src/bin/bencode.ts
|
|
5128
5284
|
import * as stream from "stream";
|
|
@@ -5275,15 +5431,13 @@ class BDecoderStream extends stream.Transform {
|
|
|
5275
5431
|
}
|
|
5276
5432
|
|
|
5277
5433
|
// src/bin/version.ts
|
|
5278
|
-
var VERSION = "0.0.
|
|
5434
|
+
var VERSION = "0.0.11";
|
|
5279
5435
|
|
|
5280
|
-
// src/
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
function injectHostFunctions(session) {
|
|
5286
|
-
const coreEnv = session.getNs("clojure.core");
|
|
5436
|
+
// src/host/node.ts
|
|
5437
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
5438
|
+
import { resolve as resolve2 } from "node:path";
|
|
5439
|
+
function injectNodeHostFunctions(session) {
|
|
5440
|
+
const coreEnv = session.registry.get("clojure.core");
|
|
5287
5441
|
define("slurp", cljNativeFunction("slurp", (pathVal) => {
|
|
5288
5442
|
const filePath = resolve2(valueToString(pathVal));
|
|
5289
5443
|
if (!existsSync2(filePath)) {
|
|
@@ -5310,15 +5464,22 @@ function injectHostFunctions(session) {
|
|
|
5310
5464
|
return cljNil();
|
|
5311
5465
|
}), coreEnv);
|
|
5312
5466
|
}
|
|
5313
|
-
|
|
5467
|
+
|
|
5468
|
+
// src/bin/nrepl.ts
|
|
5469
|
+
var CONJURE_VERSION = VERSION;
|
|
5470
|
+
function makeSessionId() {
|
|
5471
|
+
return crypto.randomUUID();
|
|
5472
|
+
}
|
|
5473
|
+
function createManagedSession(id, snapshot, encoder, sourceRoots) {
|
|
5314
5474
|
let currentMsgId = "";
|
|
5315
5475
|
const session = createSessionFromSnapshot(snapshot, {
|
|
5316
5476
|
output: (text) => {
|
|
5317
5477
|
send(encoder, { id: currentMsgId, session: id, out: text });
|
|
5318
5478
|
},
|
|
5319
|
-
readFile: (filePath) =>
|
|
5479
|
+
readFile: (filePath) => readFileSync3(filePath, "utf8"),
|
|
5480
|
+
sourceRoots
|
|
5320
5481
|
});
|
|
5321
|
-
|
|
5482
|
+
injectNodeHostFunctions(session);
|
|
5322
5483
|
return {
|
|
5323
5484
|
id,
|
|
5324
5485
|
session,
|
|
@@ -5327,7 +5488,8 @@ function createManagedSession(id, snapshot, encoder) {
|
|
|
5327
5488
|
},
|
|
5328
5489
|
set currentMsgId(v) {
|
|
5329
5490
|
currentMsgId = v;
|
|
5330
|
-
}
|
|
5491
|
+
},
|
|
5492
|
+
nsToFile: new Map
|
|
5331
5493
|
};
|
|
5332
5494
|
}
|
|
5333
5495
|
function send(encoder, msg) {
|
|
@@ -5341,10 +5503,10 @@ function done(encoder, id, sessionId, extra = {}) {
|
|
|
5341
5503
|
...extra
|
|
5342
5504
|
});
|
|
5343
5505
|
}
|
|
5344
|
-
function handleClone(msg, sessions, snapshot, encoder) {
|
|
5506
|
+
function handleClone(msg, sessions, snapshot, encoder, sourceRoots) {
|
|
5345
5507
|
const id = msg["id"] ?? "";
|
|
5346
5508
|
const newId = makeSessionId();
|
|
5347
|
-
const managed = createManagedSession(newId, snapshot, encoder);
|
|
5509
|
+
const managed = createManagedSession(newId, snapshot, encoder, sourceRoots);
|
|
5348
5510
|
sessions.set(newId, managed);
|
|
5349
5511
|
done(encoder, id, undefined, { "new-session": newId });
|
|
5350
5512
|
}
|
|
@@ -5358,6 +5520,9 @@ function handleDescribe(msg, encoder) {
|
|
|
5358
5520
|
close: {},
|
|
5359
5521
|
complete: {},
|
|
5360
5522
|
describe: {},
|
|
5523
|
+
eldoc: {},
|
|
5524
|
+
info: {},
|
|
5525
|
+
lookup: {},
|
|
5361
5526
|
"load-file": {}
|
|
5362
5527
|
},
|
|
5363
5528
|
versions: {
|
|
@@ -5401,6 +5566,9 @@ function handleLoadFile(msg, managed, encoder) {
|
|
|
5401
5566
|
}
|
|
5402
5567
|
const nsHint = fileName.replace(/\.clj$/, "").replace(/\//g, ".") || undefined;
|
|
5403
5568
|
const loadedNs = managed.session.loadFile(source, nsHint);
|
|
5569
|
+
if (filePath && loadedNs) {
|
|
5570
|
+
managed.nsToFile.set(loadedNs, filePath);
|
|
5571
|
+
}
|
|
5404
5572
|
managed.session.setNs(loadedNs);
|
|
5405
5573
|
done(encoder, id, managed.id, {
|
|
5406
5574
|
value: "nil",
|
|
@@ -5432,17 +5600,159 @@ function handleClose(msg, sessions, encoder) {
|
|
|
5432
5600
|
sessions.delete(sessionId);
|
|
5433
5601
|
send(encoder, { id, session: sessionId, status: ["done"] });
|
|
5434
5602
|
}
|
|
5603
|
+
function resolveSymbol(sym, managed, contextNs) {
|
|
5604
|
+
const ns = contextNs ?? managed.session.currentNs;
|
|
5605
|
+
const slashIdx = sym.indexOf("/");
|
|
5606
|
+
if (slashIdx > 0) {
|
|
5607
|
+
const qualifier = sym.slice(0, slashIdx);
|
|
5608
|
+
const localName2 = sym.slice(slashIdx + 1);
|
|
5609
|
+
const nsEnvFull2 = managed.session.registry.get(qualifier);
|
|
5610
|
+
if (nsEnvFull2) {
|
|
5611
|
+
const value2 = tryLookup(localName2, nsEnvFull2);
|
|
5612
|
+
if (value2 !== undefined)
|
|
5613
|
+
return { value: value2, resolvedNs: qualifier, localName: localName2 };
|
|
5614
|
+
}
|
|
5615
|
+
const currentNsData = managed.session.getNs(ns);
|
|
5616
|
+
const aliasedNs = currentNsData?.aliases.get(qualifier);
|
|
5617
|
+
if (aliasedNs) {
|
|
5618
|
+
const v = aliasedNs.vars.get(localName2);
|
|
5619
|
+
if (v !== undefined)
|
|
5620
|
+
return { value: v.value, resolvedNs: aliasedNs.name, localName: localName2 };
|
|
5621
|
+
}
|
|
5622
|
+
return null;
|
|
5623
|
+
}
|
|
5624
|
+
const localName = sym;
|
|
5625
|
+
const nsEnvFull = managed.session.registry.get(ns);
|
|
5626
|
+
if (!nsEnvFull)
|
|
5627
|
+
return null;
|
|
5628
|
+
const value = tryLookup(sym, nsEnvFull);
|
|
5629
|
+
if (value === undefined)
|
|
5630
|
+
return null;
|
|
5631
|
+
const varObj = lookupVar(sym, nsEnvFull);
|
|
5632
|
+
let resolvedNs;
|
|
5633
|
+
if (varObj) {
|
|
5634
|
+
resolvedNs = varObj.ns;
|
|
5635
|
+
} else if (value.kind === "function" || value.kind === "macro") {
|
|
5636
|
+
resolvedNs = getNamespaceEnv(value.env).ns?.name ?? ns;
|
|
5637
|
+
} else if (value.kind === "native-function") {
|
|
5638
|
+
const i = value.name.indexOf("/");
|
|
5639
|
+
resolvedNs = i > 0 ? value.name.slice(0, i) : ns;
|
|
5640
|
+
} else {
|
|
5641
|
+
resolvedNs = ns;
|
|
5642
|
+
}
|
|
5643
|
+
return { value, resolvedNs, localName };
|
|
5644
|
+
}
|
|
5645
|
+
function extractMeta(value) {
|
|
5646
|
+
const type = value.kind === "macro" ? "macro" : value.kind === "function" || value.kind === "native-function" ? "function" : "var";
|
|
5647
|
+
const meta = value.kind === "function" ? value.meta : value.kind === "native-function" ? value.meta : undefined;
|
|
5648
|
+
let doc = "";
|
|
5649
|
+
let arglistsStr = "";
|
|
5650
|
+
let eldocArgs = null;
|
|
5651
|
+
if (meta) {
|
|
5652
|
+
const docEntry = meta.entries.find(([k]) => k.kind === "keyword" && k.name === ":doc");
|
|
5653
|
+
if (docEntry && docEntry[1].kind === "string")
|
|
5654
|
+
doc = docEntry[1].value;
|
|
5655
|
+
const argsEntry = meta.entries.find(([k]) => k.kind === "keyword" && k.name === ":arglists");
|
|
5656
|
+
if (argsEntry && argsEntry[1].kind === "vector") {
|
|
5657
|
+
const arglists = argsEntry[1];
|
|
5658
|
+
arglistsStr = "(" + arglists.value.map((al) => printString(al)).join(" ") + ")";
|
|
5659
|
+
eldocArgs = arglists.value.map((al) => {
|
|
5660
|
+
if (al.kind !== "vector")
|
|
5661
|
+
return [printString(al)];
|
|
5662
|
+
return al.value.map((p) => p.kind === "symbol" ? p.name : printString(p));
|
|
5663
|
+
});
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
if (arglistsStr === "" && (value.kind === "function" || value.kind === "macro")) {
|
|
5667
|
+
const arityStrs = value.arities.map((arity) => {
|
|
5668
|
+
const params = arity.params.map((p) => printString(p));
|
|
5669
|
+
if (arity.restParam)
|
|
5670
|
+
params.push("&", printString(arity.restParam));
|
|
5671
|
+
return "[" + params.join(" ") + "]";
|
|
5672
|
+
});
|
|
5673
|
+
arglistsStr = "(" + arityStrs.join(" ") + ")";
|
|
5674
|
+
eldocArgs = value.arities.map((arity) => {
|
|
5675
|
+
const params = arity.params.map((p) => printString(p));
|
|
5676
|
+
if (arity.restParam)
|
|
5677
|
+
params.push("&", printString(arity.restParam));
|
|
5678
|
+
return params;
|
|
5679
|
+
});
|
|
5680
|
+
}
|
|
5681
|
+
return { doc, arglistsStr, eldocArgs, type };
|
|
5682
|
+
}
|
|
5683
|
+
function handleInfo(msg, managed, encoder) {
|
|
5684
|
+
const id = msg["id"] ?? "";
|
|
5685
|
+
const sym = msg["sym"];
|
|
5686
|
+
const nsOverride = msg["ns"];
|
|
5687
|
+
if (!sym) {
|
|
5688
|
+
done(encoder, id, managed.id, { status: ["no-info", "done"] });
|
|
5689
|
+
return;
|
|
5690
|
+
}
|
|
5691
|
+
const resolved = resolveSymbol(sym, managed, nsOverride);
|
|
5692
|
+
if (!resolved) {
|
|
5693
|
+
const nsFile = managed.nsToFile.get(sym);
|
|
5694
|
+
if (nsFile) {
|
|
5695
|
+
done(encoder, id, managed.id, {
|
|
5696
|
+
ns: sym,
|
|
5697
|
+
name: sym,
|
|
5698
|
+
type: "namespace",
|
|
5699
|
+
file: nsFile
|
|
5700
|
+
});
|
|
5701
|
+
return;
|
|
5702
|
+
}
|
|
5703
|
+
done(encoder, id, managed.id, { status: ["no-info", "done"] });
|
|
5704
|
+
return;
|
|
5705
|
+
}
|
|
5706
|
+
const meta = extractMeta(resolved.value);
|
|
5707
|
+
const file = managed.nsToFile.get(resolved.resolvedNs);
|
|
5708
|
+
done(encoder, id, managed.id, {
|
|
5709
|
+
ns: resolved.resolvedNs,
|
|
5710
|
+
name: resolved.localName,
|
|
5711
|
+
doc: meta.doc,
|
|
5712
|
+
"arglists-str": meta.arglistsStr,
|
|
5713
|
+
type: meta.type,
|
|
5714
|
+
...file ? { file } : {}
|
|
5715
|
+
});
|
|
5716
|
+
}
|
|
5717
|
+
function handleLookup(msg, managed, encoder) {
|
|
5718
|
+
handleInfo(msg, managed, encoder);
|
|
5719
|
+
}
|
|
5720
|
+
function handleEldoc(msg, managed, encoder) {
|
|
5721
|
+
const id = msg["id"] ?? "";
|
|
5722
|
+
const sym = msg["sym"];
|
|
5723
|
+
const nsOverride = msg["ns"];
|
|
5724
|
+
if (!sym) {
|
|
5725
|
+
done(encoder, id, managed.id, { status: ["no-eldoc", "done"] });
|
|
5726
|
+
return;
|
|
5727
|
+
}
|
|
5728
|
+
const resolved = resolveSymbol(sym, managed, nsOverride);
|
|
5729
|
+
if (!resolved) {
|
|
5730
|
+
done(encoder, id, managed.id, { status: ["no-eldoc", "done"] });
|
|
5731
|
+
return;
|
|
5732
|
+
}
|
|
5733
|
+
const meta = extractMeta(resolved.value);
|
|
5734
|
+
if (!meta.eldocArgs) {
|
|
5735
|
+
done(encoder, id, managed.id, { status: ["no-eldoc", "done"] });
|
|
5736
|
+
return;
|
|
5737
|
+
}
|
|
5738
|
+
done(encoder, id, managed.id, {
|
|
5739
|
+
name: resolved.localName,
|
|
5740
|
+
ns: resolved.resolvedNs,
|
|
5741
|
+
type: meta.type,
|
|
5742
|
+
eldoc: meta.eldocArgs
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5435
5745
|
function handleUnknown(msg, encoder) {
|
|
5436
5746
|
const id = msg["id"] ?? "";
|
|
5437
5747
|
send(encoder, { id, status: ["unknown-op", "done"] });
|
|
5438
5748
|
}
|
|
5439
|
-
function handleMessage(msg, sessions, snapshot, encoder, defaultSession) {
|
|
5749
|
+
function handleMessage(msg, sessions, snapshot, encoder, defaultSession, sourceRoots) {
|
|
5440
5750
|
const op = msg["op"];
|
|
5441
5751
|
const sessionId = msg["session"];
|
|
5442
5752
|
const managed = sessionId ? sessions.get(sessionId) ?? defaultSession : defaultSession;
|
|
5443
5753
|
switch (op) {
|
|
5444
5754
|
case "clone":
|
|
5445
|
-
handleClone(msg, sessions, snapshot, encoder);
|
|
5755
|
+
handleClone(msg, sessions, snapshot, encoder, sourceRoots);
|
|
5446
5756
|
break;
|
|
5447
5757
|
case "describe":
|
|
5448
5758
|
handleDescribe(msg, encoder);
|
|
@@ -5459,6 +5769,15 @@ function handleMessage(msg, sessions, snapshot, encoder, defaultSession) {
|
|
|
5459
5769
|
case "close":
|
|
5460
5770
|
handleClose(msg, sessions, encoder);
|
|
5461
5771
|
break;
|
|
5772
|
+
case "info":
|
|
5773
|
+
handleInfo(msg, managed, encoder);
|
|
5774
|
+
break;
|
|
5775
|
+
case "lookup":
|
|
5776
|
+
handleLookup(msg, managed, encoder);
|
|
5777
|
+
break;
|
|
5778
|
+
case "eldoc":
|
|
5779
|
+
handleEldoc(msg, managed, encoder);
|
|
5780
|
+
break;
|
|
5462
5781
|
default:
|
|
5463
5782
|
handleUnknown(msg, encoder);
|
|
5464
5783
|
}
|
|
@@ -5468,7 +5787,7 @@ function startNreplServer(options = {}) {
|
|
|
5468
5787
|
const host = options.host ?? "127.0.0.1";
|
|
5469
5788
|
const warmSession = createSession({
|
|
5470
5789
|
sourceRoots: options.sourceRoots,
|
|
5471
|
-
readFile: (filePath) =>
|
|
5790
|
+
readFile: (filePath) => readFileSync3(filePath, "utf8")
|
|
5472
5791
|
});
|
|
5473
5792
|
const snapshot = snapshotSession(warmSession);
|
|
5474
5793
|
const server = net.createServer((socket) => {
|
|
@@ -5478,10 +5797,10 @@ function startNreplServer(options = {}) {
|
|
|
5478
5797
|
socket.pipe(decoder);
|
|
5479
5798
|
const sessions = new Map;
|
|
5480
5799
|
const defaultId = makeSessionId();
|
|
5481
|
-
const defaultSession = createManagedSession(defaultId, snapshot, encoder);
|
|
5800
|
+
const defaultSession = createManagedSession(defaultId, snapshot, encoder, options.sourceRoots);
|
|
5482
5801
|
sessions.set(defaultId, defaultSession);
|
|
5483
5802
|
decoder.on("data", (msg) => {
|
|
5484
|
-
handleMessage(msg, sessions, snapshot, encoder, defaultSession);
|
|
5803
|
+
handleMessage(msg, sessions, snapshot, encoder, defaultSession, options.sourceRoots);
|
|
5485
5804
|
});
|
|
5486
5805
|
socket.on("error", () => {});
|
|
5487
5806
|
socket.on("close", () => {
|
|
@@ -5490,14 +5809,15 @@ function startNreplServer(options = {}) {
|
|
|
5490
5809
|
});
|
|
5491
5810
|
const portFile = join2(process.cwd(), ".nrepl-port");
|
|
5492
5811
|
server.listen(port, host, () => {
|
|
5493
|
-
|
|
5812
|
+
writeFileSync2(portFile, String(port), "utf8");
|
|
5494
5813
|
process.stdout.write(`Conjure nREPL server v${VERSION} started on port ${port}
|
|
5495
5814
|
`);
|
|
5496
5815
|
});
|
|
5497
5816
|
const cleanup = () => {
|
|
5498
|
-
if (
|
|
5817
|
+
if (existsSync3(portFile))
|
|
5499
5818
|
unlinkSync(portFile);
|
|
5500
5819
|
};
|
|
5820
|
+
server.on("close", cleanup);
|
|
5501
5821
|
process.on("exit", cleanup);
|
|
5502
5822
|
process.on("SIGINT", () => {
|
|
5503
5823
|
cleanup();
|
|
@@ -5519,41 +5839,13 @@ function makeCliIo() {
|
|
|
5519
5839
|
`)
|
|
5520
5840
|
};
|
|
5521
5841
|
}
|
|
5522
|
-
function injectHostFunctions2(session) {
|
|
5523
|
-
const coreEnv = session.getNs("clojure.core");
|
|
5524
|
-
define("slurp", cljNativeFunction("slurp", (pathVal) => {
|
|
5525
|
-
const filePath = resolve3(valueToString(pathVal));
|
|
5526
|
-
if (!existsSync3(filePath)) {
|
|
5527
|
-
throw new Error(`slurp: file not found: ${filePath}`);
|
|
5528
|
-
}
|
|
5529
|
-
return cljString(readFileSync3(filePath, "utf8"));
|
|
5530
|
-
}), coreEnv);
|
|
5531
|
-
define("spit", cljNativeFunction("spit", (pathVal, content) => {
|
|
5532
|
-
const filePath = resolve3(valueToString(pathVal));
|
|
5533
|
-
writeFileSync2(filePath, valueToString(content), "utf8");
|
|
5534
|
-
return cljNil();
|
|
5535
|
-
}), coreEnv);
|
|
5536
|
-
define("load", cljNativeFunction("load", (pathVal) => {
|
|
5537
|
-
const filePath = resolve3(valueToString(pathVal));
|
|
5538
|
-
if (!existsSync3(filePath)) {
|
|
5539
|
-
throw new Error(`load: file not found: ${filePath}`);
|
|
5540
|
-
}
|
|
5541
|
-
const source = readFileSync3(filePath, "utf8");
|
|
5542
|
-
const inferred = inferSourceRoot(filePath, source);
|
|
5543
|
-
if (inferred)
|
|
5544
|
-
session.addSourceRoot(inferred);
|
|
5545
|
-
const loadedNs = session.loadFile(source);
|
|
5546
|
-
session.setNs(loadedNs);
|
|
5547
|
-
return cljNil();
|
|
5548
|
-
}), coreEnv);
|
|
5549
|
-
}
|
|
5550
5842
|
function createCliSession(sourceRoots, io) {
|
|
5551
5843
|
const session = createSession({
|
|
5552
5844
|
output: (text) => io.writeLine(text),
|
|
5553
5845
|
sourceRoots,
|
|
5554
|
-
readFile: (filePath) =>
|
|
5846
|
+
readFile: (filePath) => readFileSync4(filePath, "utf8")
|
|
5555
5847
|
});
|
|
5556
|
-
|
|
5848
|
+
injectNodeHostFunctions(session);
|
|
5557
5849
|
return session;
|
|
5558
5850
|
}
|
|
5559
5851
|
function getSourceRoots(filePath) {
|
|
@@ -5572,12 +5864,12 @@ function printUsage(io) {
|
|
|
5572
5864
|
}
|
|
5573
5865
|
function runFile(fileArg, io = makeCliIo()) {
|
|
5574
5866
|
const filePath = resolve3(fileArg);
|
|
5575
|
-
if (!
|
|
5867
|
+
if (!existsSync4(filePath)) {
|
|
5576
5868
|
io.writeError(`File not found: ${fileArg}`);
|
|
5577
5869
|
return 1;
|
|
5578
5870
|
}
|
|
5579
5871
|
try {
|
|
5580
|
-
const source =
|
|
5872
|
+
const source = readFileSync4(filePath, "utf8");
|
|
5581
5873
|
const inferredRoot = inferSourceRoot(filePath, source);
|
|
5582
5874
|
const sourceRoots = inferredRoot ? [...new Set([inferredRoot, ...getSourceRoots(filePath)])] : getSourceRoots(filePath);
|
|
5583
5875
|
const session = createCliSession(sourceRoots, io);
|