pyret-embed 0.1.6 → 0.1.8

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.
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/build/web/js/cpo-main.jarr
3
- size: 39402505 bytes
4
- sha256: 95e6384cf0fa7ee3a960292eb03a16a555f8b70ccdc91b84aea4711084d36106
3
+ size: 39419095 bytes
4
+ sha256: 463aeca97884e60038f806d6ace1f16095634fe41c3632d707fde19ef0697108
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/build/web/js/cpo-main.jarr.js
3
- size: 39402505 bytes
4
- sha256: 95e6384cf0fa7ee3a960292eb03a16a555f8b70ccdc91b84aea4711084d36106
3
+ size: 39419095 bytes
4
+ sha256: 463aeca97884e60038f806d6ace1f16095634fe41c3632d707fde19ef0697108
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/build/web/js/cpo-main.jarr.min
3
- size: 36449855 bytes
4
- sha256: 0b794bbca1a5b9dfba357d96738a0e461f29c34b41cce7d6f80eca625fef258b
3
+ size: 36463335 bytes
4
+ sha256: 4fac69dd8fa65ffa6118e45ae2fb72232f6f2336d7c8dd8de84c12d8c126e346
@@ -156,7 +156,22 @@ function makeEvents(config) {
156
156
  await window.RUN_CODE(state.definitionsAtLastRun);
157
157
  }
158
158
  else if (state.definitionsAtLastRun === false) {
159
- await window.RUN_CODE("");
159
+ // "Never run": the empty run below establishes the REPL baseline. Two
160
+ // audiences depend on it -- interaction replay (the loop after this
161
+ // branch assumes a runtime to replay against), and hosts that show the
162
+ // interactions pane, whose prompt only appears at a run's completion
163
+ // (repl-ui's showPrompt) -- pyret-embed's default reset state relies on
164
+ // that for a live prompt at boot.
165
+ //
166
+ // A host that needs neither can send warmStart: false to skip it.
167
+ // While this run is in flight the editor LOOKS ready, but a Run click
168
+ // is silently swallowed and edits race the contents install below --
169
+ // so a host whose pane hides interactions (the vscode cpo editor) pays
170
+ // boot latency plus a race window for an invisible effect. Interaction
171
+ // replay always gets its baseline regardless of the flag.
172
+ if (state.warmStart !== false || (state.interactionsSinceLastRun || []).length > 0) {
173
+ await window.RUN_CODE("");
174
+ }
160
175
  }
161
176
  const interactions = state.interactionsSinceLastRun;
162
177
  for(let i = 0; i < interactions.length; i += 1) {
@@ -164,6 +179,11 @@ function makeEvents(config) {
164
179
  }
165
180
  editorUpdate(state.editorContents);
166
181
  replUpdate(state.replContents);
182
+ // The host-fed settle point (see EXPECTS_HOST_RESET in beforePyret.js):
183
+ // everything above -- the awaited warm-start run included -- is done and
184
+ // the host's contents are installed. RECEIVED_RESET (commSetup) marks
185
+ // receipt, which is the START of that window; this marks its end.
186
+ window.EDITOR_CONTENTS_SETTLED = true;
167
187
  }
168
188
 
169
189
  config.CPO.events.onLoad(async function () {
@@ -328,6 +348,9 @@ function makeEvents(config) {
328
348
  });
329
349
  CPO.loadProgram(toLoad).then((text) => {
330
350
  editorUpdate(text);
351
+ // The share-link flavor of a host reset settles here (it bypasses
352
+ // reset(), so the end of reset() can't cover it).
353
+ window.EDITOR_CONTENTS_SETTLED = true;
331
354
  })
332
355
  .catch((e) => {
333
356
  console.error("Error loading initial state from share link: ", e, link);
@@ -865,6 +865,10 @@
865
865
  running = true;
866
866
  output.empty();
867
867
  promptContainer.hide();
868
+ // This gained stop() after having been just hide() for a while,
869
+ // because we found cases where queued up fadeIns needed to be
870
+ // canceled to avoid showing the prompt while running.
871
+ promptContainer.stop(true, true).hide();
868
872
  lastEditorRun = uiOptions.cm || null;
869
873
  setWhileRunning();
870
874
 
@@ -917,7 +921,7 @@
917
921
  var echoCM = CodeMirror.fromTextArea(echo[0], { readOnly: true });
918
922
  echoCM.setValue(code);
919
923
  CM.setValue("");
920
- promptContainer.hide();
924
+ promptContainer.stop(true, true).hide();
921
925
  setWhileRunning();
922
926
  interactionsCount++;
923
927
  var thisName = 'interactions://' + interactionsCount;
@@ -1005,7 +1009,8 @@
1005
1009
  runCode: runMainCode,
1006
1010
  runner: runner,
1007
1011
  focus: function() { CM.focus(); },
1008
- stop: onBreak
1012
+ stop: onBreak,
1013
+ isRunning: function() { return running; }
1009
1014
  };
1010
1015
  }
1011
1016
 
@@ -0,0 +1,585 @@
1
+ /*
2
+ Support library for running code.pyret.org on the TypeScript compiler
3
+ (lang/src/ts-compiler, bundled onto the page as window.PyretTSCompiler).
4
+
5
+ This module adapts between the TS compiler's plain-JS world and the
6
+ Pyret runtime world that the rest of CPO (repl-ui, error-ui, check-ui)
7
+ expects:
8
+
9
+ - builtin Loadables/locators for the TS compiler, built from the
10
+ same staticModules record the page already carries;
11
+ - an async, context-aware module finder for the dependency
12
+ protocols CPO supports ((url-)file imports resolve relative to the
13
+ importing module via the embedding host's fs/path RPCs, exactly
14
+ like the stock finder);
15
+ - a ReplExecutor that runs compiled programs through the builtin
16
+ load-lib's run-program, so results are genuine load-lib
17
+ ModuleResults and render exactly as they do today;
18
+ - a bridge that converts TS compile errors (with their renderReason/
19
+ renderFancyReason methods returning TS ErrorDisplay trees) into
20
+ Pyret objects whose render-(fancy-)reason methods return genuine
21
+ builtin://error-display values, so error-ui/output-ui render them
22
+ unchanged;
23
+ - a re-thrower that converts TS parse errors into the same Pyret
24
+ parse exceptions the stock parser raises.
25
+ */
26
+ ({
27
+ requires: [
28
+ { "import-type": "builtin", name: "load-lib" },
29
+ { "import-type": "builtin", name: "either" },
30
+ { "import-type": "builtin", name: "error-display" },
31
+ { "import-type": "builtin", name: "ast" }
32
+ ],
33
+ nativeRequires: [],
34
+ provides: {},
35
+ theModule: function(runtime, namespace, uri, loadLib, eitherLib, errorDisplayLib, astLib) {
36
+ var gf = runtime.getField;
37
+ var gmf = function(m, f) { return gf(gf(m, "values"), f); };
38
+ // NOTE: runtime.ffi is not fully populated until the ffi builtin
39
+ // loads, which can happen after this module instantiates; always
40
+ // access it lazily.
41
+
42
+ function tsCompiler() {
43
+ if(!window.PyretTSCompiler) {
44
+ throw new Error("The TypeScript compiler bundle (ts-compiler.gz.js) is not loaded on this page");
45
+ }
46
+ return window.PyretTSCompiler;
47
+ }
48
+
49
+ // ---------------------------------------------------------------
50
+ // Srcloc conversions
51
+ // ---------------------------------------------------------------
52
+
53
+ function tsLocToPyret(loc) {
54
+ if(!loc) { return runtime.makeSrcloc(["unknown location"]); }
55
+ if(loc.$name === "builtin") {
56
+ return runtime.makeSrcloc([loc.moduleName]);
57
+ }
58
+ return runtime.makeSrcloc([
59
+ loc.source,
60
+ loc.startLine, loc.startColumn, loc.startChar,
61
+ loc.endLine, loc.endColumn, loc.endChar
62
+ ]);
63
+ }
64
+
65
+ function pyretLocToTs(pyLoc) {
66
+ var T = tsCompiler();
67
+ if(runtime.hasField(pyLoc, "source")) {
68
+ return new T.srcloc.Srcloc(
69
+ gf(pyLoc, "source"),
70
+ runtime.num_to_fixnum(gf(pyLoc, "start-line")),
71
+ runtime.num_to_fixnum(gf(pyLoc, "start-column")),
72
+ runtime.num_to_fixnum(gf(pyLoc, "start-char")),
73
+ runtime.num_to_fixnum(gf(pyLoc, "end-line")),
74
+ runtime.num_to_fixnum(gf(pyLoc, "end-column")),
75
+ runtime.num_to_fixnum(gf(pyLoc, "end-char")));
76
+ }
77
+ else {
78
+ return new T.srcloc.Builtin(gf(pyLoc, "module-name"));
79
+ }
80
+ }
81
+
82
+ // ---------------------------------------------------------------
83
+ // ErrorDisplay conversion: TS error-display tree -> Pyret
84
+ // builtin://error-display values
85
+ // ---------------------------------------------------------------
86
+
87
+ var ED = gf(errorDisplayLib, "values");
88
+ function edc(name) { return gf(ED, name); }
89
+
90
+ function convertEmbed(val) {
91
+ // Pyret numbers/strings/booleans are plain JS values at runtime, so
92
+ // primitives can be embedded directly; anything else from the TS
93
+ // world is shown via its toString (the TS port's data classes print
94
+ // like the Pyret originals).
95
+ if(typeof val === "number" || typeof val === "string" || typeof val === "boolean") {
96
+ return edc("embed").app(val);
97
+ }
98
+ return edc("text").app(String(val));
99
+ }
100
+
101
+ function convertED(ed) {
102
+ switch(ed.$name) {
103
+ case "paragraph":
104
+ return edc("paragraph").app(runtime.ffi.makeList(ed.contents.map(convertED)));
105
+ case "bulleted-sequence":
106
+ return edc("bulleted-sequence").app(runtime.ffi.makeList(ed.contents.map(convertED)));
107
+ case "v-sequence":
108
+ return edc("v-sequence").app(runtime.ffi.makeList(ed.contents.map(convertED)));
109
+ case "h-sequence":
110
+ return edc("h-sequence").app(runtime.ffi.makeList(ed.contents.map(convertED)), ed.sep);
111
+ case "h-sequence-sep":
112
+ return edc("h-sequence-sep").app(runtime.ffi.makeList(ed.contents.map(convertED)), ed.sep, ed.last);
113
+ case "embed":
114
+ return convertEmbed(ed.val);
115
+ case "text":
116
+ return edc("text").app(ed.str);
117
+ case "loc":
118
+ return edc("loc").app(tsLocToPyret(ed.loc));
119
+ case "maybe-stack-loc":
120
+ return edc("maybe-stack-loc").app(
121
+ ed.n,
122
+ ed.userFramesOnly,
123
+ runtime.makeFunction(function(pyLoc) {
124
+ return convertED(ed.contentsWithLoc(pyretLocToTs(pyLoc)));
125
+ }, "contents-with-loc"),
126
+ convertED(ed.contentsWithoutLoc));
127
+ case "code":
128
+ return edc("code").app(convertED(ed.contents));
129
+ case "cmcode":
130
+ return edc("cmcode").app(tsLocToPyret(ed.loc));
131
+ case "loc-display":
132
+ return edc("loc-display").app(tsLocToPyret(ed.loc), ed.style, convertED(ed.contents));
133
+ case "optional":
134
+ return edc("optional").app(convertED(ed.contents));
135
+ case "highlight":
136
+ return edc("highlight").app(
137
+ convertED(ed.contents),
138
+ runtime.ffi.makeList(ed.locs.map(tsLocToPyret)),
139
+ ed.color);
140
+ default:
141
+ console.error("Unknown TS ErrorDisplay variant: ", ed);
142
+ return edc("text").app(String(ed));
143
+ }
144
+ }
145
+
146
+ // A TS CompileError -> a Pyret object renderable by error-ui. The
147
+ // render methods are called off the data the TS error carries, and
148
+ // produce genuine error-display values.
149
+ function bridgeCompileError(tsError) {
150
+ return runtime.makeObject({
151
+ "render-fancy-reason": runtime.makeMethod0(function(_self) {
152
+ return convertED(tsError.renderFancyReason());
153
+ }),
154
+ "render-reason": runtime.makeMethod0(function(_self) {
155
+ return convertED(tsError.renderReason());
156
+ })
157
+ });
158
+ }
159
+
160
+ // The `left` payload from the TS repl is a list whose elements are
161
+ // either CompileResult errs (with .problems) or raw problem lists
162
+ // (from make-standalone); normalize to the shape repl-ui's
163
+ // displayResult expects: a Pyret list of records with a `problems`
164
+ // list of renderable errors.
165
+ function bridgeCompileErrors(errs) {
166
+ var asRecords = errs.map(function(e) {
167
+ var problems;
168
+ if(e && Array.isArray(e.problems)) { problems = e.problems; }
169
+ else { problems = [e]; }
170
+ return runtime.makeObject({
171
+ "problems": runtime.ffi.makeList(problems.map(bridgeCompileError))
172
+ });
173
+ });
174
+ return runtime.ffi.makeList(asRecords);
175
+ }
176
+
177
+ // ---------------------------------------------------------------
178
+ // Parse errors: re-throw the TS parser's structured errors through
179
+ // the runtime ffi, producing the same Pyret exceptions the stock
180
+ // parse-pyret module raises. MUST be called on the Pyret stack.
181
+ // ---------------------------------------------------------------
182
+
183
+ function isTsParseError(e) {
184
+ return e && e.name === "PyretParseError" && typeof e.kind === "string";
185
+ }
186
+
187
+ function tsCheckOpToPyret(op) {
188
+ var mkOp = gmf(astLib, op.$name);
189
+ var l = tsLocToPyret(op.l);
190
+ if(typeof op.op === "string") { return mkOp.app(l, op.op); }
191
+ return mkOp.app(l);
192
+ }
193
+
194
+ function throwPyretParseError(e) {
195
+ var loc = tsLocToPyret(e.loc);
196
+ switch(e.kind) {
197
+ case "parse-error-next-token":
198
+ return runtime.ffi.throwParseErrorNextToken(loc, e.nextToken);
199
+ case "parse-error-eof":
200
+ return runtime.ffi.throwParseErrorEOF(loc);
201
+ case "parse-error-unterminated-string":
202
+ return runtime.ffi.throwParseErrorUnterminatedString(loc);
203
+ case "parse-error-bad-number":
204
+ return runtime.ffi.throwParseErrorBadNumber(loc);
205
+ case "parse-error-bad-operator":
206
+ return runtime.ffi.throwParseErrorBadOper(loc);
207
+ case "parse-error-bad-check-operator":
208
+ return runtime.ffi.throwParseErrorBadCheckOper(tsCheckOpToPyret(e.op));
209
+ case "parse-error-colon-colon":
210
+ return runtime.ffi.throwParseErrorColonColon(loc, e.nextToken);
211
+ case "parse-error-bad-app":
212
+ return runtime.ffi.throwParseErrorBadApp(tsLocToPyret(e.funLoc), tsLocToPyret(e.argsLoc));
213
+ case "parse-error-bad-fun-header":
214
+ return runtime.ffi.throwParseErrorBadFunHeader(tsLocToPyret(e.funLoc || e.loc), tsLocToPyret(e.argsLoc || e.loc));
215
+ default:
216
+ return runtime.ffi.throwMessageException(String(e.message || e));
217
+ }
218
+ }
219
+
220
+ // ---------------------------------------------------------------
221
+ // Builtin modules: loadables and locators from staticModules
222
+ // ---------------------------------------------------------------
223
+
224
+ function makeBuiltinSupport(staticModules) {
225
+ var T = tsCompiler();
226
+ var CS = T.compileStructs;
227
+ var loadables = new Map();
228
+ var locators = {};
229
+
230
+ Object.keys(staticModules).forEach(function(modUri) {
231
+ if(modUri.indexOf("builtin://") !== 0) { return; }
232
+ var name = modUri.slice("builtin://".length);
233
+ var m = staticModules[modUri];
234
+ var raw = T.builtinModules.builtinRawLocatorFromModule(
235
+ function() { return m; },
236
+ function() { return JSON.stringify(m); });
237
+ var loadable = null;
238
+ function getLoadable() {
239
+ if(loadable === null) {
240
+ var provs = CS.providesFromRawProvides(modUri, {
241
+ uri: modUri,
242
+ values: raw.getRawValueProvides(),
243
+ aliases: raw.getRawAliasProvides(),
244
+ datatypes: raw.getRawDatatypeProvides(),
245
+ modules: raw.getRawModuleProvides()
246
+ });
247
+ loadable = new CS.ModuleAsString(provs, CS.noBuiltins, CS.computedNone,
248
+ CS.ok(new T.jsOfPyret.CCPString("")));
249
+ }
250
+ return loadable;
251
+ }
252
+ loadables.set(modUri, getLoadable());
253
+ locators[modUri] = {
254
+ getUncached: function() { return undefined; },
255
+ needsCompile: function(_provides) { return false; },
256
+ getModifiedTime: function() { return 0; },
257
+ getOptions: function(options) {
258
+ var o = Object.assign({}, options);
259
+ o.checks = "none";
260
+ return o;
261
+ },
262
+ getModule: function() {
263
+ throw new Error("Should never fetch source for builtin module " + modUri);
264
+ },
265
+ getExtraImports: function() { return CS.standardImports; },
266
+ getDependencies: function() {
267
+ return raw.getRawDependencies().map(CS.makeDep);
268
+ },
269
+ getNativeModules: function() {
270
+ return raw.getRawNativeModules().map(function(n) { return new CS.Requirejs(n); });
271
+ },
272
+ getGlobals: function() { return CS.standardGlobals; },
273
+ uri: function() { return modUri; },
274
+ name: function() { return name; },
275
+ setCompiled: function(_loadable, _provides) { return; },
276
+ getCompiled: function() { return getLoadable(); }
277
+ };
278
+ });
279
+
280
+ return { loadables: loadables, locators: locators };
281
+ }
282
+
283
+ // ---------------------------------------------------------------
284
+ // Module finder. The TS compiler's dependency chase is async
285
+ // (CL.compileWorklist awaits each finder result), so modules are
286
+ // located and loaded on demand -- fetch for url imports, the
287
+ // embedding host's fs/path RPCs for (url-)file imports -- with NO
288
+ // prefetch pass. Resolution is context-aware, mirroring the stock
289
+ // finder (cpo-main.js makeFindModule / 913740d28eb): a (url-)file
290
+ // import resolves relative to the directory of the importing
291
+ // module, threaded as a "load-path", and all path arithmetic goes
292
+ // through the same host path RPC the running program uses (the
293
+ // filesystem-internal builtin), so imports and runtime file ops
294
+ // resolve a given path identically.
295
+ // ---------------------------------------------------------------
296
+
297
+ function maybeAppendSlash(s) {
298
+ if(s.endsWith("/")) { return s; }
299
+ return s + "/";
300
+ }
301
+
302
+ function urlResolve(path, base) {
303
+ return new URL(path, base).href;
304
+ }
305
+
306
+ // The same wire calls filesystem-internal.js makes; that module is
307
+ // the single source of truth for path semantics in embedded hosts.
308
+ function hostRpc(module, method, args) {
309
+ if(!window.MESSAGES) {
310
+ return Promise.reject(new Error(
311
+ "This import needs an embedding host (vscode / embed) that provides '" +
312
+ module + "." + method + "' over RPC"));
313
+ }
314
+ return window.MESSAGES.sendRpc(module, method, args);
315
+ }
316
+ function hostReadFileString(p) {
317
+ return hostRpc('fs', 'readFile', [p]).then(function(contents) {
318
+ // The RPC delivers whatever the host's fs produced after a
319
+ // structured clone: a string, a Uint8Array (vscode), or a
320
+ // JSONified Buffer. Decode without assuming a Buffer global --
321
+ // this module's scope inside the jarr doesn't have one.
322
+ if(typeof contents === "string") { return contents; }
323
+ if(contents instanceof Uint8Array) { return new TextDecoder("utf-8").decode(contents); }
324
+ if(contents && contents.type === "Buffer" && Array.isArray(contents.data)) {
325
+ return new TextDecoder("utf-8").decode(new Uint8Array(contents.data));
326
+ }
327
+ if(Array.isArray(contents)) { return new TextDecoder("utf-8").decode(new Uint8Array(contents)); }
328
+ return new TextDecoder("utf-8").decode(new Uint8Array(contents));
329
+ });
330
+ }
331
+ function hostExists(p) {
332
+ return hostRpc('fs', 'stat', [p]).then(function() { return true; }, function(e) {
333
+ if(String(e).includes("EntryNotFound")) { return false; }
334
+ throw e;
335
+ });
336
+ }
337
+
338
+ function getLoadPath(context) {
339
+ if(context && typeof context["load-path"] === "string") { return context["load-path"]; }
340
+ return ".";
341
+ }
342
+ // get-real-path: an absolute REL is honored as-is, otherwise it is
343
+ // joined onto the importer's load-path (both via the host path RPC).
344
+ function getRealPath(context, rel) {
345
+ return hostRpc('path', 'isAbsolute', [rel]).then(function(abs) {
346
+ if(abs) { return rel; }
347
+ return hostRpc('path', 'join', [getLoadPath(context), rel]);
348
+ });
349
+ }
350
+ // For a dependency, compute a Promise of { path, context }: the local
351
+ // path to load (null for non-local protocols) and the context to
352
+ // thread to that module's own imports. Unlike the stock finder we
353
+ // skip the path RPCs entirely when a url-file import will load
354
+ // remotely (all-remote mode, or no embedding host) -- observable
355
+ // behavior is identical, but the plain-web editor never touches RPCs.
356
+ function dependencyResolveInfo(context, dep, urlFileMode) {
357
+ var CS = tsCompiler().compileStructs;
358
+ var rel = null;
359
+ if(CS.isDependency(dep)) {
360
+ if(dep.protocol === "file" || dep.protocol === "js-file") { rel = dep.arguments[0]; }
361
+ else if(dep.protocol === "url-file" && urlFileMode !== "all-remote" && window.MESSAGES) {
362
+ rel = dep.arguments[1];
363
+ }
364
+ }
365
+ if(rel === null) {
366
+ // builtin/gdrive/url and remote url-file: nothing local to track,
367
+ // thread the importer's context through unchanged.
368
+ return Promise.resolve({ path: null, context: context });
369
+ }
370
+ return getRealPath(context, rel).then(function(realPath) {
371
+ return hostRpc('path', 'dirname', [realPath]).then(function(dir) {
372
+ return { path: realPath, context: { "load-path": dir } };
373
+ });
374
+ });
375
+ }
376
+
377
+ // Register a loaded source with CPO's document map so error
378
+ // highlighting (cmcode) can render srclocs into it.
379
+ function registerDocument(uri, text) {
380
+ if(window.CPO && CPO.documents && typeof CodeMirror !== "undefined" && !CPO.documents.has(uri)) {
381
+ CPO.documents.set(uri, new CodeMirror.Doc(text, "pyret"));
382
+ }
383
+ }
384
+
385
+ function fetchText(url) {
386
+ return fetch(url).then(function(response) {
387
+ if(!response.ok) {
388
+ throw new Error("Failed to load " + url + ": " + response.status);
389
+ }
390
+ return response.text();
391
+ });
392
+ }
393
+
394
+ function makeTextLocator(uri, text) {
395
+ var T = tsCompiler();
396
+ var CS = T.compileStructs;
397
+ var CL = T.compileLib;
398
+ var self = {
399
+ getUncached: function() { return undefined; },
400
+ needsCompile: function(_provides) { return true; },
401
+ getModifiedTime: function() { return 0; },
402
+ getOptions: function(options) { return options; },
403
+ getModule: function() { return new CL.PyretString(text); },
404
+ getExtraImports: function() { return CS.standardImports; },
405
+ getDependencies: function() {
406
+ return CL.getStandardDependencies(self.getModule(), uri);
407
+ },
408
+ getNativeModules: function() { return []; },
409
+ getGlobals: function() { return CS.standardGlobals; },
410
+ uri: function() { return uri; },
411
+ name: function() { return uri; },
412
+ setCompiled: function(_loadable, _provides) { return; },
413
+ getCompiled: function() { return undefined; }
414
+ };
415
+ return self;
416
+ }
417
+
418
+ function makeFinderFactory(builtinSupport, urlFileMode) {
419
+ return function makeFinder() {
420
+ var locatorCache = {};
421
+ return function finder(context, dep) {
422
+ var T = tsCompiler();
423
+ var CS = T.compileStructs;
424
+ var CL = T.compileLib;
425
+ function located(uri, locator, ctx) {
426
+ locatorCache[uri] = locator;
427
+ return new CL.Located(locator, ctx);
428
+ }
429
+ if(CS.isBuiltin(dep)) {
430
+ var builtinLocator = builtinSupport.locators["builtin://" + dep.modname];
431
+ if(!builtinLocator) {
432
+ throw new Error("Unknown module: " + dep.modname);
433
+ }
434
+ return located("builtin://" + dep.modname, builtinLocator, context);
435
+ }
436
+ return dependencyResolveInfo(context, dep, urlFileMode).then(function(info) {
437
+ function textLocated(uri, textPromise, ctx) {
438
+ if(locatorCache[uri]) {
439
+ return new CL.Located(locatorCache[uri], ctx);
440
+ }
441
+ return textPromise.then(function(text) {
442
+ registerDocument(uri, text);
443
+ return located(uri, makeTextLocator(uri, text), ctx);
444
+ });
445
+ }
446
+ function localFileLocated(fullUrlForDocs, ctx) {
447
+ return hostRpc('path', 'resolve', [info.path]).then(function(realpath) {
448
+ var uri = "file://" + realpath;
449
+ return textLocated(uri, hostReadFileString(info.path).then(function(text) {
450
+ if(fullUrlForDocs) { registerDocument(fullUrlForDocs, text); }
451
+ return text;
452
+ }), ctx);
453
+ });
454
+ }
455
+ if(dep.protocol === "file") {
456
+ return localFileLocated(null, info.context);
457
+ }
458
+ if(dep.protocol === "url") {
459
+ var url = dep.arguments[0];
460
+ return textLocated(url, fetchText(url), context);
461
+ }
462
+ if(dep.protocol === "url-file") {
463
+ var fullUrl = urlResolve(dep.arguments[1], maybeAppendSlash(dep.arguments[0]));
464
+ if(info.path === null) {
465
+ // all-remote (or no embedding host)
466
+ return textLocated(fullUrl, fetchText(fullUrl), context);
467
+ }
468
+ if(urlFileMode === "all-local") {
469
+ return localFileLocated(fullUrl, info.context);
470
+ }
471
+ // local-if-present
472
+ return hostExists(info.path).then(function(exists) {
473
+ if(exists) { return localFileLocated(fullUrl, info.context); }
474
+ return textLocated(fullUrl, fetchText(fullUrl), context);
475
+ });
476
+ }
477
+ throw new Error("The import protocol '" + dep.protocol +
478
+ "' is not supported when running with the TypeScript compiler: " +
479
+ dep.protocol + "://" + dep.arguments.join(":"));
480
+ });
481
+ };
482
+ };
483
+ }
484
+
485
+ // ---------------------------------------------------------------
486
+ // Executor over load-lib: runs compiled program text in the realm
487
+ // ---------------------------------------------------------------
488
+
489
+ function makeExecutor(pyRuntime) {
490
+ var runProgramPy = gmf(loadLib, "run-program");
491
+ return {
492
+ run: function(realm, programJsSource, _options) {
493
+ return new Promise(function(resolve, reject) {
494
+ runtime.runThunk(function() {
495
+ return runProgramPy.app(
496
+ pyRuntime,
497
+ realm,
498
+ programJsSource,
499
+ runtime.makeObject({ "checks": "main" }),
500
+ runtime.ffi.makeList([]));
501
+ }, function(result) {
502
+ if(runtime.isSuccessResult(result)) {
503
+ resolve(result.result);
504
+ }
505
+ else {
506
+ // An error escaping run-program itself (not the
507
+ // program): surface the whole FailureResult.
508
+ reject(result);
509
+ }
510
+ });
511
+ });
512
+ },
513
+ isSuccessResult: function(moduleResult) {
514
+ return moduleResult.val.runtime.isSuccessResult(moduleResult.val.result);
515
+ },
516
+ getResultRealm: function(moduleResult) {
517
+ return moduleResult.val.realm;
518
+ }
519
+ };
520
+ }
521
+
522
+ // ---------------------------------------------------------------
523
+ // Result plumbing: convert the TS repl's Either (or thrown error)
524
+ // into the same runtime-result-wrapped Pyret Either that repl-ui's
525
+ // displayResult consumes.
526
+ // ---------------------------------------------------------------
527
+
528
+ var pyLeft = gmf(eitherLib, "left");
529
+ var pyRight = gmf(eitherLib, "right");
530
+
531
+ function resolveWithEither(deferred, either) {
532
+ runtime.runThunk(function() {
533
+ if(either.$name === "left") {
534
+ return pyLeft.app(bridgeCompileErrors(either.v));
535
+ }
536
+ else {
537
+ return pyRight.app(either.v);
538
+ }
539
+ }, function(result) {
540
+ deferred.resolve(result);
541
+ });
542
+ }
543
+
544
+ function resolveWithError(deferred, err) {
545
+ // A FailureResult rejected out of the executor passes through
546
+ // unchanged (it is already what displayResult expects).
547
+ if(err && runtime.isFailureResult && runtime.isFailureResult(err)) {
548
+ deferred.resolve(err);
549
+ return;
550
+ }
551
+ runtime.runThunk(function() {
552
+ if(isTsParseError(err)) {
553
+ return throwPyretParseError(err);
554
+ }
555
+ console.error("Error while compiling with the TS compiler: ", err);
556
+ return runtime.ffi.throwMessageException(String((err && err.message) || err));
557
+ }, function(result) {
558
+ deferred.resolve(result);
559
+ });
560
+ }
561
+
562
+ /*
563
+ Report a run that was stopped before it ever became a Pyret computation.
564
+ (This happens for stops during the TS compiler's compile)
565
+ */
566
+ function resolveWithUserBreak(deferred) {
567
+ runtime.runThunk(function() {
568
+ return runtime.raise(runtime.ffi.userBreak);
569
+ }, function(result) {
570
+ deferred.resolve(result);
571
+ });
572
+ }
573
+
574
+ return runtime.makeJSModuleReturn({
575
+ makeBuiltinSupport: makeBuiltinSupport,
576
+ makeFinderFactory: makeFinderFactory,
577
+ makeExecutor: makeExecutor,
578
+ resolveWithEither: resolveWithEither,
579
+ resolveWithError: resolveWithError,
580
+ resolveWithUserBreak: resolveWithUserBreak,
581
+ bridgeCompileErrors: bridgeCompileErrors,
582
+ tsCompiler: tsCompiler
583
+ });
584
+ }
585
+ })