mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
Import ts file from prototype without change
From commit 559453cf6c
Excluding v8worker.d.ts, main.ts, and deno.d.ts.
Updates tslint.json to be original settings.
This commit is contained in:
parent
21e1425656
commit
fe404dfce9
16 changed files with 1374 additions and 73 deletions
124
js/console.ts
Normal file
124
js/console.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
const print = V8Worker2.print;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
type ConsoleContext = Set<any>;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
function getClassInstanceName(instance: any): string {
|
||||||
|
if (typeof instance !== "object") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (instance && instance.__proto__ && instance.__proto__.constructor) {
|
||||||
|
return instance.__proto__.constructor.name; // could be "Object" or "Array"
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
function stringify(ctx: ConsoleContext, value: any): string {
|
||||||
|
switch (typeof value) {
|
||||||
|
case "string":
|
||||||
|
return value;
|
||||||
|
case "number":
|
||||||
|
case "boolean":
|
||||||
|
case "undefined":
|
||||||
|
case "symbol":
|
||||||
|
return String(value);
|
||||||
|
case "function":
|
||||||
|
if (value.name && value.name !== "anonymous") {
|
||||||
|
// from MDN spec
|
||||||
|
return `[Function: ${value.name}]`;
|
||||||
|
}
|
||||||
|
return "[Function]";
|
||||||
|
case "object":
|
||||||
|
if (value === null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.has(value)) {
|
||||||
|
return "[Circular]";
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.add(value);
|
||||||
|
const entries: string[] = [];
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const el of value) {
|
||||||
|
entries.push(stringify(ctx, el));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.delete(value);
|
||||||
|
|
||||||
|
if (entries.length === 0) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
return `[ ${entries.join(", ")} ]`;
|
||||||
|
} else {
|
||||||
|
let baseString = "";
|
||||||
|
|
||||||
|
const className = getClassInstanceName(value);
|
||||||
|
let shouldShowClassName = false;
|
||||||
|
if (className && className !== "Object" && className !== "anonymous") {
|
||||||
|
shouldShowClassName = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
entries.push(`${key}: ${stringify(ctx, value[key])}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.delete(value);
|
||||||
|
|
||||||
|
if (entries.length === 0) {
|
||||||
|
baseString = "{}";
|
||||||
|
} else {
|
||||||
|
baseString = `{ ${entries.join(", ")} }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldShowClassName) {
|
||||||
|
baseString = `${className} ${baseString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseString;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "[Not Implemented]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
function stringifyArgs(args: any[]): string {
|
||||||
|
const out: string[] = [];
|
||||||
|
for (const a of args) {
|
||||||
|
if (typeof a === "string") {
|
||||||
|
out.push(a);
|
||||||
|
} else {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
out.push(stringify(new Set<any>(), a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Console {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
log(...args: any[]): void {
|
||||||
|
print(stringifyArgs(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug = this.log;
|
||||||
|
info = this.log;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
warn(...args: any[]): void {
|
||||||
|
print(`ERROR: ${stringifyArgs(args)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = this.warn;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
assert(condition: boolean, ...args: any[]): void {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(`Assertion failed: ${stringifyArgs(args)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
js/deno.ts
Normal file
6
js/deno.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
// Public deno module.
|
||||||
|
// TODO get rid of deno.d.ts
|
||||||
|
export { pub, sub } from "./dispatch";
|
||||||
|
export { readFileSync, writeFileSync } from "./os";
|
73
js/dispatch.ts
Normal file
73
js/dispatch.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import { typedArrayToArrayBuffer } from "./util";
|
||||||
|
import { _global } from "./globals";
|
||||||
|
import { deno as pb } from "./msg.pb";
|
||||||
|
|
||||||
|
export type MessageCallback = (msg: Uint8Array) => void;
|
||||||
|
//type MessageStructCallback = (msg: pb.IMsg) => void;
|
||||||
|
|
||||||
|
const send = V8Worker2.send;
|
||||||
|
const channels = new Map<string, MessageCallback[]>();
|
||||||
|
|
||||||
|
export function sub(channel: string, cb: MessageCallback): void {
|
||||||
|
let subscribers = channels.get(channel);
|
||||||
|
if (!subscribers) {
|
||||||
|
subscribers = [];
|
||||||
|
channels.set(channel, subscribers);
|
||||||
|
}
|
||||||
|
subscribers.push(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
export function subMsg(channel: string, cb: MessageStructCallback): void {
|
||||||
|
sub(channel, (payload: Uint8Array) => {
|
||||||
|
const msg = pb.Msg.decode(payload);
|
||||||
|
if (msg.error != null) {
|
||||||
|
f.onError(new Error(msg.error));
|
||||||
|
} else {
|
||||||
|
cb(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function pub(channel: string, payload: Uint8Array): null | ArrayBuffer {
|
||||||
|
const msg = pb.BaseMsg.fromObject({ channel, payload });
|
||||||
|
const ui8 = pb.BaseMsg.encode(msg).finish();
|
||||||
|
const ab = typedArrayToArrayBuffer(ui8);
|
||||||
|
return send(ab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal version of "pub".
|
||||||
|
// TODO add internal version of "sub"
|
||||||
|
export function pubInternal(channel: string, obj: pb.IMsg): null | pb.Msg {
|
||||||
|
const msg = pb.Msg.fromObject(obj);
|
||||||
|
const ui8 = pb.Msg.encode(msg).finish();
|
||||||
|
const resBuf = pub(channel, ui8);
|
||||||
|
if (resBuf != null && resBuf.byteLength > 0) {
|
||||||
|
const res = pb.Msg.decode(new Uint8Array(resBuf));
|
||||||
|
if (res != null && res.error != null && res.error.length > 0) {
|
||||||
|
throw Error(res.error);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V8Worker2.recv((ab: ArrayBuffer) => {
|
||||||
|
const msg = pb.BaseMsg.decode(new Uint8Array(ab));
|
||||||
|
const subscribers = channels.get(msg.channel);
|
||||||
|
if (subscribers == null) {
|
||||||
|
throw Error(`No subscribers for channel "${msg.channel}".`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const subscriber of subscribers) {
|
||||||
|
subscriber(msg.payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete the V8Worker2 from the global object, so that no one else can receive
|
||||||
|
// messages.
|
||||||
|
_global["V8Worker2"] = null;
|
146
js/fetch.ts
Normal file
146
js/fetch.ts
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import {
|
||||||
|
assert,
|
||||||
|
log,
|
||||||
|
createResolvable,
|
||||||
|
Resolvable,
|
||||||
|
typedArrayToArrayBuffer
|
||||||
|
} from "./util";
|
||||||
|
import { pubInternal, sub } from "./dispatch";
|
||||||
|
import { deno as pb } from "./msg.pb";
|
||||||
|
|
||||||
|
export function initFetch() {
|
||||||
|
sub("fetch", (payload: Uint8Array) => {
|
||||||
|
const msg = pb.Msg.decode(payload);
|
||||||
|
assert(msg.command === pb.Msg.Command.FETCH_RES);
|
||||||
|
const id = msg.fetchResId;
|
||||||
|
const f = fetchRequests.get(id);
|
||||||
|
assert(f != null, `Couldn't find FetchRequest id ${id}`);
|
||||||
|
|
||||||
|
f.onMsg(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchRequests = new Map<number, FetchRequest>();
|
||||||
|
|
||||||
|
class FetchResponse implements Response {
|
||||||
|
readonly url: string;
|
||||||
|
body: null;
|
||||||
|
bodyUsed = false; // TODO
|
||||||
|
status: number;
|
||||||
|
statusText = "FIXME"; // TODO
|
||||||
|
readonly type = "basic"; // TODO
|
||||||
|
redirected = false; // TODO
|
||||||
|
headers: null; // TODO
|
||||||
|
//private bodyChunks: Uint8Array[] = [];
|
||||||
|
private first = true;
|
||||||
|
|
||||||
|
constructor(readonly req: FetchRequest) {
|
||||||
|
this.url = req.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyWaiter: Resolvable<ArrayBuffer>;
|
||||||
|
arrayBuffer(): Promise<ArrayBuffer> {
|
||||||
|
this.bodyWaiter = createResolvable();
|
||||||
|
return this.bodyWaiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
blob(): Promise<Blob> {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
formData(): Promise<FormData> {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async json(): Promise<object> {
|
||||||
|
const text = await this.text();
|
||||||
|
return JSON.parse(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
async text(): Promise<string> {
|
||||||
|
const ab = await this.arrayBuffer();
|
||||||
|
const decoder = new TextDecoder("utf-8");
|
||||||
|
return decoder.decode(ab);
|
||||||
|
}
|
||||||
|
|
||||||
|
get ok(): boolean {
|
||||||
|
return 200 <= this.status && this.status < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): Response {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeader: (res: Response) => void;
|
||||||
|
onError: (error: Error) => void;
|
||||||
|
|
||||||
|
onMsg(msg: pb.Msg) {
|
||||||
|
if (msg.error !== null && msg.error !== "") {
|
||||||
|
//throw new Error(msg.error)
|
||||||
|
this.onError(new Error(msg.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.first) {
|
||||||
|
this.first = false;
|
||||||
|
this.status = msg.fetchResStatus;
|
||||||
|
this.onHeader(this);
|
||||||
|
} else {
|
||||||
|
// Body message. Assuming it all comes in one message now.
|
||||||
|
const ab = typedArrayToArrayBuffer(msg.fetchResBody);
|
||||||
|
this.bodyWaiter.resolve(ab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextFetchId = 0;
|
||||||
|
//TODO implements Request
|
||||||
|
class FetchRequest {
|
||||||
|
private readonly id: number;
|
||||||
|
response: FetchResponse;
|
||||||
|
constructor(readonly url: string) {
|
||||||
|
this.id = nextFetchId++;
|
||||||
|
fetchRequests.set(this.id, this);
|
||||||
|
this.response = new FetchResponse(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsg(msg: pb.Msg) {
|
||||||
|
this.response.onMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
fetchRequests.delete(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
log("dispatch FETCH_REQ", this.id, this.url);
|
||||||
|
const res = pubInternal("fetch", {
|
||||||
|
command: pb.Msg.Command.FETCH_REQ,
|
||||||
|
fetchReqId: this.id,
|
||||||
|
fetchReqUrl: this.url
|
||||||
|
});
|
||||||
|
assert(res == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetch(
|
||||||
|
input?: Request | string,
|
||||||
|
init?: RequestInit
|
||||||
|
): Promise<Response> {
|
||||||
|
const fetchReq = new FetchRequest(input as string);
|
||||||
|
const response = fetchReq.response;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
response.onHeader = (response: any) => {
|
||||||
|
log("onHeader");
|
||||||
|
resolve(response);
|
||||||
|
};
|
||||||
|
response.onError = (error: Error) => {
|
||||||
|
log("onError", error);
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
fetchReq.start();
|
||||||
|
});
|
||||||
|
}
|
32
js/globals.ts
Normal file
32
js/globals.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import * as timer from "./timers";
|
||||||
|
|
||||||
|
// If you use the eval function indirectly, by invoking it via a reference
|
||||||
|
// other than eval, as of ECMAScript 5 it works in the global scope rather than
|
||||||
|
// the local scope. This means, for instance, that function declarations create
|
||||||
|
// global functions, and that the code being evaluated doesn't have access to
|
||||||
|
// local variables within the scope where it's being called.
|
||||||
|
export const globalEval = eval;
|
||||||
|
|
||||||
|
// A reference to the global object.
|
||||||
|
// TODO The underscore is because it's conflicting with @types/node.
|
||||||
|
export const _global = globalEval("this");
|
||||||
|
|
||||||
|
_global["window"] = _global; // Create a window object.
|
||||||
|
import "./url";
|
||||||
|
|
||||||
|
_global["setTimeout"] = timer.setTimeout;
|
||||||
|
_global["setInterval"] = timer.setInterval;
|
||||||
|
_global["clearTimeout"] = timer.clearTimer;
|
||||||
|
_global["clearInterval"] = timer.clearTimer;
|
||||||
|
|
||||||
|
import { Console } from "./console";
|
||||||
|
_global["console"] = new Console();
|
||||||
|
|
||||||
|
import { fetch } from "./fetch";
|
||||||
|
_global["fetch"] = fetch;
|
||||||
|
|
||||||
|
import { TextEncoder, TextDecoder } from "text-encoding";
|
||||||
|
_global["TextEncoder"] = TextEncoder;
|
||||||
|
_global["TextDecoder"] = TextDecoder;
|
|
@ -25,9 +25,9 @@ window["denoMain"] = () => {
|
||||||
|
|
||||||
const argv: string[] = [];
|
const argv: string[] = [];
|
||||||
for (let i = 0; i < msg.startArgvLength(); i++) {
|
for (let i = 0; i < msg.startArgvLength(); i++) {
|
||||||
const arg = msg.startArgv(i);
|
argv.push(msg.startArgv(i));
|
||||||
deno.print(`argv[${i}] ${arg}`);
|
|
||||||
}
|
}
|
||||||
|
deno.print(`argv ${argv}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
function typedArrayToArrayBuffer(ta: Uint8Array): ArrayBuffer {
|
function typedArrayToArrayBuffer(ta: Uint8Array): ArrayBuffer {
|
||||||
|
|
65
js/os.ts
Normal file
65
js/os.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import { ModuleInfo } from "./types";
|
||||||
|
import { pubInternal } from "./dispatch";
|
||||||
|
import { deno as pb } from "./msg.pb";
|
||||||
|
import { assert } from "./util";
|
||||||
|
|
||||||
|
export function exit(exitCode = 0): void {
|
||||||
|
pubInternal("os", {
|
||||||
|
command: pb.Msg.Command.EXIT,
|
||||||
|
exitCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeFetch(
|
||||||
|
moduleSpecifier: string,
|
||||||
|
containingFile: string
|
||||||
|
): ModuleInfo {
|
||||||
|
const res = pubInternal("os", {
|
||||||
|
command: pb.Msg.Command.CODE_FETCH,
|
||||||
|
codeFetchModuleSpecifier: moduleSpecifier,
|
||||||
|
codeFetchContainingFile: containingFile
|
||||||
|
});
|
||||||
|
assert(res.command === pb.Msg.Command.CODE_FETCH_RES);
|
||||||
|
return {
|
||||||
|
moduleName: res.codeFetchResModuleName,
|
||||||
|
filename: res.codeFetchResFilename,
|
||||||
|
sourceCode: res.codeFetchResSourceCode,
|
||||||
|
outputCode: res.codeFetchResOutputCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeCache(
|
||||||
|
filename: string,
|
||||||
|
sourceCode: string,
|
||||||
|
outputCode: string
|
||||||
|
): void {
|
||||||
|
pubInternal("os", {
|
||||||
|
command: pb.Msg.Command.CODE_CACHE,
|
||||||
|
codeCacheFilename: filename,
|
||||||
|
codeCacheSourceCode: sourceCode,
|
||||||
|
codeCacheOutputCode: outputCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readFileSync(filename: string): Uint8Array {
|
||||||
|
const res = pubInternal("os", {
|
||||||
|
command: pb.Msg.Command.READ_FILE_SYNC,
|
||||||
|
readFileSyncFilename: filename
|
||||||
|
});
|
||||||
|
return res.readFileSyncData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeFileSync(
|
||||||
|
filename: string,
|
||||||
|
data: Uint8Array,
|
||||||
|
perm: number
|
||||||
|
): void {
|
||||||
|
pubInternal("os", {
|
||||||
|
command: pb.Msg.Command.WRITE_FILE_SYNC,
|
||||||
|
writeFileSyncFilename: filename,
|
||||||
|
writeFileSyncData: data,
|
||||||
|
writeFileSyncPerm: perm
|
||||||
|
});
|
||||||
|
}
|
339
js/runtime.ts
Normal file
339
js/runtime.ts
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
// Glossary
|
||||||
|
// outputCode = generated javascript code
|
||||||
|
// sourceCode = typescript code (or input javascript code)
|
||||||
|
// moduleName = a resolved module name
|
||||||
|
// fileName = an unresolved raw fileName.
|
||||||
|
// for http modules , its the path to the locally downloaded
|
||||||
|
// version.
|
||||||
|
|
||||||
|
import * as ts from "typescript";
|
||||||
|
import * as util from "./util";
|
||||||
|
import { log } from "./util";
|
||||||
|
import * as os from "./os";
|
||||||
|
import * as sourceMaps from "./v8_source_maps";
|
||||||
|
import { _global, globalEval } from "./globals";
|
||||||
|
import * as deno from "./deno";
|
||||||
|
|
||||||
|
const EOL = "\n";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
export type AmdFactory = (...args: any[]) => undefined | object;
|
||||||
|
export type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
||||||
|
|
||||||
|
// Uncaught exceptions are sent to window.onerror by v8worker2.
|
||||||
|
// https://git.io/vhOsf
|
||||||
|
window.onerror = (message, source, lineno, colno, error) => {
|
||||||
|
// TODO Currently there is a bug in v8_source_maps.ts that causes a segfault
|
||||||
|
// if it is used within window.onerror. To workaround we uninstall the
|
||||||
|
// Error.prepareStackTrace handler. Users will get unmapped stack traces on
|
||||||
|
// uncaught exceptions until this issue is fixed.
|
||||||
|
Error.prepareStackTrace = null;
|
||||||
|
console.log(error.message, error.stack);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setup(mainJs: string, mainMap: string): void {
|
||||||
|
sourceMaps.install({
|
||||||
|
installPrepareStackTrace: true,
|
||||||
|
getGeneratedContents: (filename: string): string => {
|
||||||
|
if (filename === "/main.js") {
|
||||||
|
return mainJs;
|
||||||
|
} else if (filename === "/main.map") {
|
||||||
|
return mainMap;
|
||||||
|
} else {
|
||||||
|
const mod = FileModule.load(filename);
|
||||||
|
if (!mod) {
|
||||||
|
console.error("getGeneratedContents cannot find", filename);
|
||||||
|
}
|
||||||
|
return mod.outputCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This class represents a module. We call it FileModule to make it explicit
|
||||||
|
// that each module represents a single file.
|
||||||
|
// Access to FileModule instances should only be done thru the static method
|
||||||
|
// FileModule.load(). FileModules are NOT executed upon first load, only when
|
||||||
|
// compileAndRun is called.
|
||||||
|
export class FileModule {
|
||||||
|
scriptVersion: string;
|
||||||
|
readonly exports = {};
|
||||||
|
|
||||||
|
private static readonly map = new Map<string, FileModule>();
|
||||||
|
constructor(
|
||||||
|
readonly fileName: string,
|
||||||
|
readonly sourceCode = "",
|
||||||
|
public outputCode = ""
|
||||||
|
) {
|
||||||
|
util.assert(
|
||||||
|
!FileModule.map.has(fileName),
|
||||||
|
`FileModule.map already has ${fileName}`
|
||||||
|
);
|
||||||
|
FileModule.map.set(fileName, this);
|
||||||
|
if (outputCode !== "") {
|
||||||
|
this.scriptVersion = "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAndRun(): void {
|
||||||
|
if (!this.outputCode) {
|
||||||
|
// If there is no cached outputCode, then compile the code.
|
||||||
|
util.assert(
|
||||||
|
this.sourceCode != null && this.sourceCode.length > 0,
|
||||||
|
`Have no source code from ${this.fileName}`
|
||||||
|
);
|
||||||
|
const compiler = Compiler.instance();
|
||||||
|
this.outputCode = compiler.compile(this.fileName);
|
||||||
|
os.codeCache(this.fileName, this.sourceCode, this.outputCode);
|
||||||
|
}
|
||||||
|
util.log("compileAndRun", this.sourceCode);
|
||||||
|
execute(this.fileName, this.outputCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static load(fileName: string): FileModule {
|
||||||
|
return this.map.get(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getScriptsWithSourceCode(): string[] {
|
||||||
|
const out = [];
|
||||||
|
for (const fn of this.map.keys()) {
|
||||||
|
const m = this.map.get(fn);
|
||||||
|
if (m.sourceCode) {
|
||||||
|
out.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeDefine(fileName: string): AmdDefine {
|
||||||
|
const localDefine = (deps: string[], factory: AmdFactory): void => {
|
||||||
|
const localRequire = (x: string) => {
|
||||||
|
log("localRequire", x);
|
||||||
|
};
|
||||||
|
const currentModule = FileModule.load(fileName);
|
||||||
|
const localExports = currentModule.exports;
|
||||||
|
log("localDefine", fileName, deps, localExports);
|
||||||
|
const args = deps.map(dep => {
|
||||||
|
if (dep === "require") {
|
||||||
|
return localRequire;
|
||||||
|
} else if (dep === "exports") {
|
||||||
|
return localExports;
|
||||||
|
} else if (dep === "typescript") {
|
||||||
|
return ts;
|
||||||
|
} else if (dep === "deno") {
|
||||||
|
return deno;
|
||||||
|
} else {
|
||||||
|
const resolved = resolveModuleName(dep, fileName);
|
||||||
|
const depModule = FileModule.load(resolved);
|
||||||
|
depModule.compileAndRun();
|
||||||
|
return depModule.exports;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
factory(...args);
|
||||||
|
};
|
||||||
|
return localDefine;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveModule(
|
||||||
|
moduleSpecifier: string,
|
||||||
|
containingFile: string
|
||||||
|
): null | FileModule {
|
||||||
|
//util.log("resolveModule", { moduleSpecifier, containingFile });
|
||||||
|
util.assert(moduleSpecifier != null && moduleSpecifier.length > 0);
|
||||||
|
// We ask golang to sourceCodeFetch. It will load the sourceCode and if
|
||||||
|
// there is any outputCode cached, it will return that as well.
|
||||||
|
let fetchResponse;
|
||||||
|
try {
|
||||||
|
fetchResponse = os.codeFetch(moduleSpecifier, containingFile);
|
||||||
|
} catch (e) {
|
||||||
|
// TODO Only catch "no such file or directory" errors. Need error codes.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { filename, sourceCode, outputCode } = fetchResponse;
|
||||||
|
if (sourceCode.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
util.log("resolveModule sourceCode length ", sourceCode.length);
|
||||||
|
const m = FileModule.load(filename);
|
||||||
|
if (m != null) {
|
||||||
|
return m;
|
||||||
|
} else {
|
||||||
|
return new FileModule(filename, sourceCode, outputCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveModuleName(
|
||||||
|
moduleSpecifier: string,
|
||||||
|
containingFile: string
|
||||||
|
): string | undefined {
|
||||||
|
const mod = resolveModule(moduleSpecifier, containingFile);
|
||||||
|
if (mod) {
|
||||||
|
return mod.fileName;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execute(fileName: string, outputCode: string): void {
|
||||||
|
util.assert(outputCode && outputCode.length > 0);
|
||||||
|
_global["define"] = makeDefine(fileName);
|
||||||
|
outputCode += `\n//# sourceURL=${fileName}`;
|
||||||
|
globalEval(outputCode);
|
||||||
|
_global["define"] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a singleton class. Use Compiler.instance() to access.
|
||||||
|
class Compiler {
|
||||||
|
options: ts.CompilerOptions = {
|
||||||
|
allowJs: true,
|
||||||
|
module: ts.ModuleKind.AMD,
|
||||||
|
outDir: "$deno$",
|
||||||
|
inlineSourceMap: true,
|
||||||
|
lib: ["es2017"],
|
||||||
|
inlineSources: true,
|
||||||
|
target: ts.ScriptTarget.ES2017
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
allowJs: true,
|
||||||
|
module: ts.ModuleKind.AMD,
|
||||||
|
noEmit: false,
|
||||||
|
outDir: '$deno$',
|
||||||
|
*/
|
||||||
|
private service: ts.LanguageService;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
const host = new TypeScriptHost(this.options);
|
||||||
|
this.service = ts.createLanguageService(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _instance: Compiler;
|
||||||
|
static instance(): Compiler {
|
||||||
|
return this._instance || (this._instance = new this());
|
||||||
|
}
|
||||||
|
|
||||||
|
compile(fileName: string): string {
|
||||||
|
const output = this.service.getEmitOutput(fileName);
|
||||||
|
|
||||||
|
// Get the relevant diagnostics - this is 3x faster than
|
||||||
|
// `getPreEmitDiagnostics`.
|
||||||
|
const diagnostics = this.service
|
||||||
|
.getCompilerOptionsDiagnostics()
|
||||||
|
.concat(this.service.getSyntacticDiagnostics(fileName))
|
||||||
|
.concat(this.service.getSemanticDiagnostics(fileName));
|
||||||
|
if (diagnostics.length > 0) {
|
||||||
|
const errMsg = ts.formatDiagnosticsWithColorAndContext(
|
||||||
|
diagnostics,
|
||||||
|
formatDiagnosticsHost
|
||||||
|
);
|
||||||
|
console.log(errMsg);
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
util.assert(!output.emitSkipped);
|
||||||
|
|
||||||
|
const outputCode = output.outputFiles[0].text;
|
||||||
|
// let sourceMapCode = output.outputFiles[0].text;
|
||||||
|
return outputCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the compiler host for type checking.
|
||||||
|
class TypeScriptHost implements ts.LanguageServiceHost {
|
||||||
|
constructor(readonly options: ts.CompilerOptions) {}
|
||||||
|
|
||||||
|
getScriptFileNames(): string[] {
|
||||||
|
const keys = FileModule.getScriptsWithSourceCode();
|
||||||
|
util.log("getScriptFileNames", keys);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
getScriptVersion(fileName: string): string {
|
||||||
|
util.log("getScriptVersion", fileName);
|
||||||
|
const m = FileModule.load(fileName);
|
||||||
|
return m.scriptVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
|
||||||
|
util.log("getScriptSnapshot", fileName);
|
||||||
|
const m = resolveModule(fileName, ".");
|
||||||
|
if (m == null) {
|
||||||
|
util.log("getScriptSnapshot", fileName, "NOT FOUND");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
//const m = resolveModule(fileName, ".");
|
||||||
|
util.assert(m.sourceCode.length > 0);
|
||||||
|
return ts.ScriptSnapshot.fromString(m.sourceCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileExists(fileName: string): boolean {
|
||||||
|
const m = resolveModule(fileName, ".");
|
||||||
|
const exists = m != null;
|
||||||
|
util.log("fileExist", fileName, exists);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(path: string, encoding?: string): string | undefined {
|
||||||
|
util.log("readFile", path);
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewLine() {
|
||||||
|
return EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentDirectory() {
|
||||||
|
util.log("getCurrentDirectory");
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompilationSettings() {
|
||||||
|
util.log("getCompilationSettings");
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||||
|
const fn = ts.getDefaultLibFileName(options);
|
||||||
|
util.log("getDefaultLibFileName", fn);
|
||||||
|
const m = resolveModule(fn, "/$asset$/");
|
||||||
|
return m.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveModuleNames(
|
||||||
|
moduleNames: string[],
|
||||||
|
containingFile: string,
|
||||||
|
reusedNames?: string[]
|
||||||
|
): Array<ts.ResolvedModule | undefined> {
|
||||||
|
//util.log("resolveModuleNames", { moduleNames, reusedNames });
|
||||||
|
return moduleNames.map((name: string) => {
|
||||||
|
let resolvedFileName;
|
||||||
|
if (name === "deno") {
|
||||||
|
resolvedFileName = resolveModuleName("deno.d.ts", "/$asset$/");
|
||||||
|
} else if (name === "typescript") {
|
||||||
|
resolvedFileName = resolveModuleName("typescript.d.ts", "/$asset$/");
|
||||||
|
} else {
|
||||||
|
resolvedFileName = resolveModuleName(name, containingFile);
|
||||||
|
if (resolvedFileName == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isExternalLibraryImport = false;
|
||||||
|
return { resolvedFileName, isExternalLibraryImport };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
|
||||||
|
getCurrentDirectory(): string {
|
||||||
|
return ".";
|
||||||
|
},
|
||||||
|
getCanonicalFileName(fileName: string): string {
|
||||||
|
return fileName;
|
||||||
|
},
|
||||||
|
getNewLine(): string {
|
||||||
|
return EOL;
|
||||||
|
}
|
||||||
|
};
|
126
js/tests.ts
Normal file
126
js/tests.ts
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
// This test is executed as part of integration_test.go
|
||||||
|
// But it can also be run manually:
|
||||||
|
// ./deno tests.ts
|
||||||
|
// There must also be a static file http server running on localhost:4545
|
||||||
|
// serving the deno project directory. Try this:
|
||||||
|
// http-server -p 4545 --cors .
|
||||||
|
import { test, assert, assertEqual } from "./testing/testing.ts";
|
||||||
|
import { readFileSync, writeFileSync } from "deno";
|
||||||
|
|
||||||
|
test(async function tests_test() {
|
||||||
|
assert(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(async function tests_fetch() {
|
||||||
|
const response = await fetch("http://localhost:4545/package.json");
|
||||||
|
const json = await response.json();
|
||||||
|
assertEqual(json.name, "deno");
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function tests_console_assert() {
|
||||||
|
console.assert(true);
|
||||||
|
|
||||||
|
let hasThrown = false;
|
||||||
|
try {
|
||||||
|
console.assert(false);
|
||||||
|
} catch {
|
||||||
|
hasThrown = true;
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(async function tests_readFileSync() {
|
||||||
|
const data = readFileSync("package.json");
|
||||||
|
if (!data.byteLength) {
|
||||||
|
throw Error(
|
||||||
|
`Expected positive value for data.byteLength ${data.byteLength}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const decoder = new TextDecoder("utf-8");
|
||||||
|
const json = decoder.decode(data);
|
||||||
|
const pkg = JSON.parse(json);
|
||||||
|
assertEqual(pkg.name, "deno");
|
||||||
|
});
|
||||||
|
|
||||||
|
test(async function tests_writeFileSync() {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const data = enc.encode("Hello");
|
||||||
|
// TODO need ability to get tmp dir.
|
||||||
|
// const fn = "/tmp/test.txt";
|
||||||
|
writeFileSync("/tmp/test.txt", data, 0o666);
|
||||||
|
const dataRead = readFileSync("/tmp/test.txt");
|
||||||
|
const dec = new TextDecoder("utf-8");
|
||||||
|
const actual = dec.decode(dataRead);
|
||||||
|
assertEqual("Hello", actual);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function tests_console_assert() {
|
||||||
|
console.assert(true);
|
||||||
|
|
||||||
|
let hasThrown = false;
|
||||||
|
try {
|
||||||
|
console.assert(false);
|
||||||
|
} catch {
|
||||||
|
hasThrown = true;
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function tests_console_stringify_circular() {
|
||||||
|
class Base {
|
||||||
|
a = 1;
|
||||||
|
m1() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Extended extends Base {
|
||||||
|
b = 2;
|
||||||
|
m2() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const nestedObj: any = {
|
||||||
|
num: 1,
|
||||||
|
bool: true,
|
||||||
|
str: "a",
|
||||||
|
method() {},
|
||||||
|
un: undefined,
|
||||||
|
nu: null,
|
||||||
|
arrowFunc: () => {},
|
||||||
|
extendedClass: new Extended(),
|
||||||
|
nFunc: new Function(),
|
||||||
|
extendedCstr: Extended
|
||||||
|
};
|
||||||
|
|
||||||
|
const circularObj = {
|
||||||
|
num: 2,
|
||||||
|
bool: false,
|
||||||
|
str: "b",
|
||||||
|
method() {},
|
||||||
|
un: undefined,
|
||||||
|
nu: null,
|
||||||
|
nested: nestedObj,
|
||||||
|
emptyObj: {},
|
||||||
|
arr: [1, "s", false, null, nestedObj],
|
||||||
|
baseClass: new Base()
|
||||||
|
};
|
||||||
|
|
||||||
|
nestedObj.o = circularObj;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(1);
|
||||||
|
console.log("s");
|
||||||
|
console.log(false);
|
||||||
|
console.log(Symbol(1));
|
||||||
|
console.log(null);
|
||||||
|
console.log(undefined);
|
||||||
|
console.log(new Extended());
|
||||||
|
console.log(function f() {});
|
||||||
|
console.log(nestedObj);
|
||||||
|
console.log(JSON);
|
||||||
|
console.log(console);
|
||||||
|
} catch {
|
||||||
|
throw new Error("Expected no crash on circular object");
|
||||||
|
}
|
||||||
|
});
|
6
js/text-encoding.d.ts
vendored
Normal file
6
js/text-encoding.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Remove and depend on @types/text-encoding once this PR is merged
|
||||||
|
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26141
|
||||||
|
declare module "text-encoding" {
|
||||||
|
export const TextEncoder: TextEncoder;
|
||||||
|
export const TextDecoder: TextDecoder;
|
||||||
|
}
|
89
js/timers.ts
Normal file
89
js/timers.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import { deno as pb } from "./msg.pb";
|
||||||
|
import { pubInternal, sub } from "./dispatch";
|
||||||
|
import { assert } from "./util";
|
||||||
|
|
||||||
|
let nextTimerId = 1;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
export type TimerCallback = (...args: any[]) => void;
|
||||||
|
|
||||||
|
interface Timer {
|
||||||
|
id: number;
|
||||||
|
cb: TimerCallback;
|
||||||
|
interval: boolean;
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
args: any[];
|
||||||
|
delay: number; // milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
const timers = new Map<number, Timer>();
|
||||||
|
|
||||||
|
export function initTimers() {
|
||||||
|
sub("timers", onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMessage(payload: Uint8Array) {
|
||||||
|
const msg = pb.Msg.decode(payload);
|
||||||
|
assert(msg.command === pb.Msg.Command.TIMER_READY);
|
||||||
|
const { timerReadyId, timerReadyDone } = msg;
|
||||||
|
const timer = timers.get(timerReadyId);
|
||||||
|
if (!timer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timer.cb(...timer.args);
|
||||||
|
if (timerReadyDone) {
|
||||||
|
timers.delete(timerReadyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTimer(
|
||||||
|
cb: TimerCallback,
|
||||||
|
delay: number,
|
||||||
|
interval: boolean,
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
args: any[]
|
||||||
|
): number {
|
||||||
|
const timer = {
|
||||||
|
id: nextTimerId++,
|
||||||
|
interval,
|
||||||
|
delay,
|
||||||
|
args,
|
||||||
|
cb
|
||||||
|
};
|
||||||
|
timers.set(timer.id, timer);
|
||||||
|
pubInternal("timers", {
|
||||||
|
command: pb.Msg.Command.TIMER_START,
|
||||||
|
timerStartId: timer.id,
|
||||||
|
timerStartInterval: timer.interval,
|
||||||
|
timerStartDelay: timer.delay
|
||||||
|
});
|
||||||
|
return timer.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setTimeout(
|
||||||
|
cb: TimerCallback,
|
||||||
|
delay: number,
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
...args: any[]
|
||||||
|
): number {
|
||||||
|
return setTimer(cb, delay, false, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setInterval(
|
||||||
|
cb: TimerCallback,
|
||||||
|
delay: number,
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
...args: any[]
|
||||||
|
): number {
|
||||||
|
return setTimer(cb, delay, true, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearTimer(id: number) {
|
||||||
|
timers.delete(id);
|
||||||
|
pubInternal("timers", {
|
||||||
|
command: pb.Msg.Command.TIMER_CLEAR,
|
||||||
|
timerClearId: id
|
||||||
|
});
|
||||||
|
}
|
10
js/types.ts
Normal file
10
js/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
export type TypedArray = Uint8Array | Float32Array | Int32Array;
|
||||||
|
|
||||||
|
export interface ModuleInfo {
|
||||||
|
moduleName?: string;
|
||||||
|
filename?: string;
|
||||||
|
sourceCode?: string;
|
||||||
|
outputCode?: string;
|
||||||
|
}
|
53
js/util.ts
Normal file
53
js/util.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
import { debug } from "./main";
|
||||||
|
import { TypedArray } from "./types";
|
||||||
|
|
||||||
|
// Internal logging for deno. Use the "debug" variable above to control
|
||||||
|
// output.
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
export function log(...args: any[]): void {
|
||||||
|
if (debug) {
|
||||||
|
console.log(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assert(cond: boolean, msg = "") {
|
||||||
|
if (!cond) {
|
||||||
|
throw Error(`Assert fail. ${msg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typedArrayToArrayBuffer(ta: TypedArray): ArrayBuffer {
|
||||||
|
const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength);
|
||||||
|
return ab as ArrayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayToStr(ui8: Uint8Array): string {
|
||||||
|
return String.fromCharCode(...ui8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A `Resolvable` is a Promise with the `reject` and `resolve` functions
|
||||||
|
// placed as methods on the promise object itself. It allows you to do:
|
||||||
|
//
|
||||||
|
// const p = createResolvable<number>();
|
||||||
|
// ...
|
||||||
|
// p.resolve(42);
|
||||||
|
//
|
||||||
|
// It'd be prettier to make Resolvable a class that inherits from Promise,
|
||||||
|
// rather than an interface. This is possible in ES2016, however typescript
|
||||||
|
// produces broken code when targeting ES5 code.
|
||||||
|
// See https://github.com/Microsoft/TypeScript/issues/15202
|
||||||
|
// At the time of writing, the github issue is closed but the problem remains.
|
||||||
|
export interface Resolvable<T> extends Promise<T> {
|
||||||
|
resolve: (value?: T | PromiseLike<T>) => void;
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
reject: (reason?: any) => void;
|
||||||
|
}
|
||||||
|
export function createResolvable<T>(): Resolvable<T> {
|
||||||
|
let methods;
|
||||||
|
const promise = new Promise<T>((resolve, reject) => {
|
||||||
|
methods = { resolve, reject };
|
||||||
|
});
|
||||||
|
return Object.assign(promise, methods) as Resolvable<T>;
|
||||||
|
}
|
273
js/v8_source_maps.ts
Normal file
273
js/v8_source_maps.ts
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
// Copyright 2014 Evan Wallace
|
||||||
|
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
// All rights reserved. MIT License.
|
||||||
|
// Originated from source-map-support but has been heavily modified for deno.
|
||||||
|
import { SourceMapConsumer, MappedPosition } from "source-map";
|
||||||
|
import * as base64 from "base64-js";
|
||||||
|
import { arrayToStr } from "./util";
|
||||||
|
|
||||||
|
const consumers = new Map<string, SourceMapConsumer>();
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
// A callback the returns generated file contents.
|
||||||
|
getGeneratedContents: GetGeneratedContentsCallback;
|
||||||
|
// Usually set the following to true. Set to false for testing.
|
||||||
|
installPrepareStackTrace: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CallSite extends NodeJS.CallSite {
|
||||||
|
getScriptNameOrSourceURL(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
source: string; // Filename
|
||||||
|
column: number;
|
||||||
|
line: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetGeneratedContentsCallback = (fileName: string) => string;
|
||||||
|
|
||||||
|
let getGeneratedContents: GetGeneratedContentsCallback;
|
||||||
|
|
||||||
|
export function install(options: Options) {
|
||||||
|
getGeneratedContents = options.getGeneratedContents;
|
||||||
|
if (options.installPrepareStackTrace) {
|
||||||
|
Error.prepareStackTrace = prepareStackTraceWrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareStackTraceWrapper(
|
||||||
|
error: Error,
|
||||||
|
stack: CallSite[]
|
||||||
|
): string {
|
||||||
|
try {
|
||||||
|
return prepareStackTrace(error, stack);
|
||||||
|
} catch (prepareStackError) {
|
||||||
|
Error.prepareStackTrace = null;
|
||||||
|
console.log("=====Error inside of prepareStackTrace====");
|
||||||
|
console.log(prepareStackError.stack.toString());
|
||||||
|
console.log("=====Original error=======================");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareStackTrace(error: Error, stack: CallSite[]): string {
|
||||||
|
const frames = stack.map(
|
||||||
|
(frame: CallSite) => `\n at ${wrapCallSite(frame).toString()}`
|
||||||
|
);
|
||||||
|
return error.toString() + frames.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapCallSite(frame: CallSite): CallSite {
|
||||||
|
if (frame.isNative()) {
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most call sites will return the source file from getFileName(), but code
|
||||||
|
// passed to eval() ending in "//# sourceURL=..." will return the source file
|
||||||
|
// from getScriptNameOrSourceURL() instead
|
||||||
|
const source = frame.getFileName() || frame.getScriptNameOrSourceURL();
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
const line = frame.getLineNumber();
|
||||||
|
const column = frame.getColumnNumber() - 1;
|
||||||
|
const position = mapSourcePosition({ source, line, column });
|
||||||
|
frame = cloneCallSite(frame);
|
||||||
|
frame.getFileName = () => position.source;
|
||||||
|
frame.getLineNumber = () => position.line;
|
||||||
|
frame.getColumnNumber = () => Number(position.column) + 1;
|
||||||
|
frame.getScriptNameOrSourceURL = () => position.source;
|
||||||
|
frame.toString = () => CallSiteToString(frame);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code called using eval() needs special handling
|
||||||
|
let origin = frame.isEval() && frame.getEvalOrigin();
|
||||||
|
if (origin) {
|
||||||
|
origin = mapEvalOrigin(origin);
|
||||||
|
frame = cloneCallSite(frame);
|
||||||
|
frame.getEvalOrigin = () => origin;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here then we were unable to change the source position
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneCallSite(frame: CallSite): CallSite {
|
||||||
|
// tslint:disable:no-any
|
||||||
|
const obj: any = {};
|
||||||
|
const frame_ = frame as any;
|
||||||
|
const props = Object.getOwnPropertyNames(Object.getPrototypeOf(frame));
|
||||||
|
props.forEach(name => {
|
||||||
|
obj[name] = /^(?:is|get)/.test(name)
|
||||||
|
? () => frame_[name].call(frame)
|
||||||
|
: frame_[name];
|
||||||
|
});
|
||||||
|
return (obj as any) as CallSite;
|
||||||
|
// tslint:enable:no-any
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from source-map-support, original copied from V8's messages.js
|
||||||
|
// MIT License. Copyright (c) 2014 Evan Wallace
|
||||||
|
function CallSiteToString(frame: CallSite): string {
|
||||||
|
let fileName;
|
||||||
|
let fileLocation = "";
|
||||||
|
if (frame.isNative()) {
|
||||||
|
fileLocation = "native";
|
||||||
|
} else {
|
||||||
|
fileName = frame.getScriptNameOrSourceURL();
|
||||||
|
if (!fileName && frame.isEval()) {
|
||||||
|
fileLocation = frame.getEvalOrigin();
|
||||||
|
fileLocation += ", "; // Expecting source position to follow.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName) {
|
||||||
|
fileLocation += fileName;
|
||||||
|
} else {
|
||||||
|
// Source code does not originate from a file and is not native, but we
|
||||||
|
// can still get the source position inside the source string, e.g. in
|
||||||
|
// an eval string.
|
||||||
|
fileLocation += "<anonymous>";
|
||||||
|
}
|
||||||
|
const lineNumber = frame.getLineNumber();
|
||||||
|
if (lineNumber != null) {
|
||||||
|
fileLocation += ":" + String(lineNumber);
|
||||||
|
const columnNumber = frame.getColumnNumber();
|
||||||
|
if (columnNumber) {
|
||||||
|
fileLocation += ":" + String(columnNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = "";
|
||||||
|
const functionName = frame.getFunctionName();
|
||||||
|
let addSuffix = true;
|
||||||
|
const isConstructor = frame.isConstructor();
|
||||||
|
const isMethodCall = !(frame.isToplevel() || isConstructor);
|
||||||
|
if (isMethodCall) {
|
||||||
|
let typeName = frame.getTypeName();
|
||||||
|
// Fixes shim to be backward compatable with Node v0 to v4
|
||||||
|
if (typeName === "[object Object]") {
|
||||||
|
typeName = "null";
|
||||||
|
}
|
||||||
|
const methodName = frame.getMethodName();
|
||||||
|
if (functionName) {
|
||||||
|
if (typeName && functionName.indexOf(typeName) !== 0) {
|
||||||
|
line += typeName + ".";
|
||||||
|
}
|
||||||
|
line += functionName;
|
||||||
|
if (
|
||||||
|
methodName &&
|
||||||
|
functionName.indexOf("." + methodName) !==
|
||||||
|
functionName.length - methodName.length - 1
|
||||||
|
) {
|
||||||
|
line += ` [as ${methodName} ]`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line += typeName + "." + (methodName || "<anonymous>");
|
||||||
|
}
|
||||||
|
} else if (isConstructor) {
|
||||||
|
line += "new " + (functionName || "<anonymous>");
|
||||||
|
} else if (functionName) {
|
||||||
|
line += functionName;
|
||||||
|
} else {
|
||||||
|
line += fileLocation;
|
||||||
|
addSuffix = false;
|
||||||
|
}
|
||||||
|
if (addSuffix) {
|
||||||
|
line += ` (${fileLocation})`;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex for detecting source maps
|
||||||
|
const reSourceMap = /^data:application\/json[^,]+base64,/;
|
||||||
|
|
||||||
|
function loadConsumer(source: string): SourceMapConsumer {
|
||||||
|
let consumer = consumers.get(source);
|
||||||
|
if (consumer == null) {
|
||||||
|
const code = getGeneratedContents(source);
|
||||||
|
if (!code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sourceMappingURL = retrieveSourceMapURL(code);
|
||||||
|
if (!sourceMappingURL) {
|
||||||
|
throw Error("No source map?");
|
||||||
|
}
|
||||||
|
|
||||||
|
let sourceMapData: string;
|
||||||
|
if (reSourceMap.test(sourceMappingURL)) {
|
||||||
|
// Support source map URL as a data url
|
||||||
|
const rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(",") + 1);
|
||||||
|
const ui8 = base64.toByteArray(rawData);
|
||||||
|
sourceMapData = arrayToStr(ui8);
|
||||||
|
sourceMappingURL = source;
|
||||||
|
} else {
|
||||||
|
// Support source map URLs relative to the source URL
|
||||||
|
//sourceMappingURL = supportRelativeURL(source, sourceMappingURL);
|
||||||
|
sourceMapData = getGeneratedContents(sourceMappingURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("sourceMapData", sourceMapData);
|
||||||
|
const rawSourceMap = JSON.parse(sourceMapData);
|
||||||
|
consumer = new SourceMapConsumer(rawSourceMap);
|
||||||
|
consumers.set(source, consumer);
|
||||||
|
}
|
||||||
|
return consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function retrieveSourceMapURL(fileData: string): string {
|
||||||
|
// Get the URL of the source map
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
const re = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/gm;
|
||||||
|
// Keep executing the search to find the *last* sourceMappingURL to avoid
|
||||||
|
// picking up sourceMappingURLs from comments, strings, etc.
|
||||||
|
let lastMatch, match;
|
||||||
|
while ((match = re.exec(fileData))) {
|
||||||
|
lastMatch = match;
|
||||||
|
}
|
||||||
|
if (!lastMatch) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return lastMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapSourcePosition(position: Position): MappedPosition {
|
||||||
|
const consumer = loadConsumer(position.source);
|
||||||
|
if (consumer == null) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
const mapped = consumer.originalPositionFor(position);
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses code generated by FormatEvalOrigin(), a function inside V8:
|
||||||
|
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js
|
||||||
|
function mapEvalOrigin(origin: string): string {
|
||||||
|
// Most eval() calls are in this format
|
||||||
|
let match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin);
|
||||||
|
if (match) {
|
||||||
|
const position = mapSourcePosition({
|
||||||
|
source: match[2],
|
||||||
|
line: Number(match[3]),
|
||||||
|
column: Number(match[4]) - 1
|
||||||
|
});
|
||||||
|
const pos = [
|
||||||
|
position.source,
|
||||||
|
position.line,
|
||||||
|
Number(position.column) + 1
|
||||||
|
].join(":");
|
||||||
|
return `eval at ${match[1]} (${pos})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse nested eval() calls using recursion
|
||||||
|
match = /^eval at ([^(]+) \((.+)\)$/.exec(origin);
|
||||||
|
if (match) {
|
||||||
|
return `eval at ${match[1]} (${mapEvalOrigin(match[2])})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we still return useful information if we didn't find anything
|
||||||
|
return origin;
|
||||||
|
}
|
|
@ -13,6 +13,6 @@
|
||||||
"allowUnreachableCode": false,
|
"allowUnreachableCode": false,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true
|
||||||
},
|
},
|
||||||
"include": ["js/**/*.ts", "js/**/*.js"],
|
"include": [ "js/main.ts" ],
|
||||||
"exclude": ["js/mock_runtime.js", "js/node_modules"]
|
"exclude": [ "node_modules" ]
|
||||||
}
|
}
|
||||||
|
|
97
tslint.json
97
tslint.json
|
@ -1,77 +1,59 @@
|
||||||
{
|
{
|
||||||
"extends": [
|
|
||||||
"tslint-eslint-rules", "tslint-no-circular-imports"
|
|
||||||
],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"align": [
|
|
||||||
true,
|
|
||||||
// TODO "arguments",
|
|
||||||
// TODO "elements",
|
|
||||||
"members",
|
|
||||||
"parameters",
|
|
||||||
"statements"
|
|
||||||
],
|
|
||||||
"array-bracket-spacing": [true, "never"],
|
|
||||||
"array-type": [true, "array-simple"],
|
"array-type": [true, "array-simple"],
|
||||||
"arrow-return-shorthand": true,
|
"arrow-return-shorthand": true,
|
||||||
"ban": [true,
|
"ban": [
|
||||||
{"name": "parseInt", "message": "tsstyle#type-coercion"},
|
true,
|
||||||
{"name": "parseFloat", "message": "tsstyle#type-coercion"},
|
["fit"],
|
||||||
{"name": "Array", "message": "tsstyle#array-constructor"}
|
["fdescribe"],
|
||||||
|
["xit"],
|
||||||
|
["xdescribe"],
|
||||||
|
["fitAsync"],
|
||||||
|
["xitAsync"],
|
||||||
|
["fitFakeAsync"],
|
||||||
|
["xitFakeAsync"]
|
||||||
],
|
],
|
||||||
"ban-types": [true,
|
"ban-types": [
|
||||||
|
true,
|
||||||
["Object", "Use {} instead."],
|
["Object", "Use {} instead."],
|
||||||
["String", "Use 'string' instead."],
|
["String", "Use 'string' instead."],
|
||||||
["Number", "Use 'number' instead."],
|
["Number", "Use 'number' instead."],
|
||||||
["Boolean", "Use 'boolean' instead."]
|
["Boolean", "Use 'boolean' instead."]
|
||||||
],
|
],
|
||||||
"brace-style": [true, "1tbs", { "allowSingleLine": true }],
|
|
||||||
"block-spacing": [true, "always"],
|
|
||||||
"class-name": true,
|
"class-name": true,
|
||||||
"comment-format": [true, "check-space"],
|
"curly": true,
|
||||||
"curly": [true, "ignore-same-line"],
|
|
||||||
"eofline": true,
|
|
||||||
"forin": true,
|
|
||||||
"ter-func-call-spacing": true,
|
|
||||||
"indent": [true, "spaces", 2],
|
|
||||||
"interface-name": [true, "never-prefix"],
|
"interface-name": [true, "never-prefix"],
|
||||||
"jsdoc-format": true,
|
"jsdoc-format": true,
|
||||||
|
"forin": false,
|
||||||
"label-position": true,
|
"label-position": true,
|
||||||
"linebreak-style": [true, "LF"],
|
"max-line-length": [true, 80],
|
||||||
"max-line-length": { "options": [80] },
|
|
||||||
"member-access": [true, "no-public"],
|
|
||||||
"new-parens": true,
|
"new-parens": true,
|
||||||
"no-angle-bracket-type-assertion": true,
|
"no-angle-bracket-type-assertion": true,
|
||||||
//"no-any": true,
|
"no-any": true,
|
||||||
"no-arg": true,
|
|
||||||
"no-conditional-assignment": true,
|
|
||||||
"no-consecutive-blank-lines": true,
|
|
||||||
"no-construct": true,
|
"no-construct": true,
|
||||||
|
"no-consecutive-blank-lines": true,
|
||||||
"no-debugger": true,
|
"no-debugger": true,
|
||||||
"no-default-export": true,
|
"no-default-export": true,
|
||||||
"no-duplicate-variable": true,
|
|
||||||
"no-inferrable-types": true,
|
"no-inferrable-types": true,
|
||||||
"no-irregular-whitespace": true,
|
//"no-namespace": [true, "allow-declarations"],
|
||||||
"no-namespace": [true, "allow-declarations"],
|
|
||||||
"no-multi-spaces": true,
|
|
||||||
"no-reference": true,
|
"no-reference": true,
|
||||||
|
"no-require-imports": true,
|
||||||
"no-string-throw": true,
|
"no-string-throw": true,
|
||||||
"no-trailing-whitespace": true,
|
|
||||||
"no-unused-expression": true,
|
"no-unused-expression": true,
|
||||||
|
"no-unused-variable": true,
|
||||||
"no-var-keyword": true,
|
"no-var-keyword": true,
|
||||||
"object-literal-shorthand": true,
|
"object-literal-shorthand": true,
|
||||||
"object-literal-sort-keys": [true, "match-declaration-order"],
|
"only-arrow-functions": [
|
||||||
"one-line": [true, "check-catch", "check-finally", "check-else"],
|
true,
|
||||||
//"only-arrow-functions": [true, "allow-declarations", "allow-named-functions"],
|
"allow-declarations",
|
||||||
"ordered-imports": true,
|
"allow-named-functions"
|
||||||
"quotemark": [true, "double"],
|
],
|
||||||
"prefer-const": true,
|
"prefer-const": true,
|
||||||
|
"quotemark": [true, "double"],
|
||||||
"radix": true,
|
"radix": true,
|
||||||
// https://github.com/buzinas/tslint-eslint-rules/issues/318
|
"restrict-plus-operands": true,
|
||||||
// "space-before-blocks": true,
|
|
||||||
"space-before-function-paren": false,
|
|
||||||
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||||
//"switch-default": true,
|
"switch-default": true,
|
||||||
"triple-equals": [true, "allow-null-check"],
|
"triple-equals": [true, "allow-null-check"],
|
||||||
"use-isnan": true,
|
"use-isnan": true,
|
||||||
"variable-name": [
|
"variable-name": [
|
||||||
|
@ -80,29 +62,6 @@
|
||||||
"ban-keywords",
|
"ban-keywords",
|
||||||
"allow-leading-underscore",
|
"allow-leading-underscore",
|
||||||
"allow-trailing-underscore"
|
"allow-trailing-underscore"
|
||||||
],
|
|
||||||
"whitespace": [
|
|
||||||
true,
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-module",
|
|
||||||
"check-separator",
|
|
||||||
"check-rest-spread",
|
|
||||||
"check-type",
|
|
||||||
"check-typecast",
|
|
||||||
"check-type-operator",
|
|
||||||
"check-preblock"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jsRules": {
|
|
||||||
"max-line-length": { "options": [80] },
|
|
||||||
"linebreak-style": [true, "LF"],
|
|
||||||
"indent": [true, "spaces", 2]
|
|
||||||
},
|
|
||||||
"linterOptions": {
|
|
||||||
"exclude": [
|
|
||||||
"js/msg.pb.js"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue