mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
Delete go implementation (#276)
The go prototype will remain at https://github.com/ry/deno/tree/golang
This commit is contained in:
parent
ef9dc2464e
commit
86354a29a4
73 changed files with 114 additions and 7988 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,8 +0,0 @@
|
|||
node_modules/
|
||||
.cache/
|
||||
dist/
|
||||
deno
|
||||
assets.go
|
||||
msg.pb.go
|
||||
/msg.pb.js
|
||||
/msg.pb.d.ts
|
|
@ -44,7 +44,8 @@ install:
|
|||
- gn args $BUILD_PATH --list
|
||||
- ccache -s
|
||||
# Travis hangs without -j2 argument to ninja.
|
||||
- ninja -j2 -C $BUILD_PATH mock_runtime_test deno deno_rs
|
||||
- ninja -j2 -C $BUILD_PATH mock_runtime_test deno
|
||||
- ninja -j2 -C $BUILD_PATH deno_rs
|
||||
script:
|
||||
- $BUILD_PATH/mock_runtime_test
|
||||
- $BUILD_PATH/deno foo bar
|
||||
|
|
83
Makefile
83
Makefile
|
@ -1,83 +0,0 @@
|
|||
TS_FILES = \
|
||||
console.ts \
|
||||
deno.d.ts \
|
||||
deno.ts \
|
||||
dispatch.ts \
|
||||
fetch.ts \
|
||||
globals.ts \
|
||||
main.ts \
|
||||
msg.pb.d.ts \
|
||||
msg.pb.js \
|
||||
os.ts \
|
||||
runtime.ts \
|
||||
text-encoding.d.ts \
|
||||
timers.ts \
|
||||
tsconfig.json \
|
||||
types.ts \
|
||||
url.js \
|
||||
util.ts \
|
||||
v8_source_maps.ts \
|
||||
v8worker2.d.ts
|
||||
|
||||
GO_FILES = \
|
||||
cmd/main.go \
|
||||
assets.go \
|
||||
deno_dir.go \
|
||||
deno_dir_test.go \
|
||||
dispatch.go \
|
||||
echo.go \
|
||||
fetch.go \
|
||||
main.go \
|
||||
msg.pb.go \
|
||||
os.go \
|
||||
os_test.go \
|
||||
timers.go \
|
||||
util.go \
|
||||
util_test.go \
|
||||
integration_test.go
|
||||
|
||||
|
||||
deno: msg.pb.go $(GO_FILES)
|
||||
go build -o deno ./cmd
|
||||
|
||||
assets.go: dist/main.js
|
||||
cp node_modules/typescript/lib/*d.ts dist/
|
||||
cp deno.d.ts dist/
|
||||
go-bindata -pkg deno -o assets.go dist/
|
||||
|
||||
msg.pb.go: msg.proto
|
||||
protoc --go_out=. msg.proto
|
||||
|
||||
msg.pb.js: msg.proto node_modules
|
||||
./node_modules/.bin/pbjs -t static-module -w commonjs -o msg.pb.js msg.proto
|
||||
|
||||
msg.pb.d.ts: msg.pb.js node_modules
|
||||
./node_modules/.bin/pbts -o msg.pb.d.ts msg.pb.js
|
||||
|
||||
dist/main.js: $(TS_FILES) node_modules
|
||||
./node_modules/.bin/tsc --noEmit # Only for type checking.
|
||||
./node_modules/.bin/parcel build --out-dir=dist/ --log-level=1 --no-minify main.ts
|
||||
|
||||
node_modules:
|
||||
yarn
|
||||
|
||||
clean:
|
||||
-rm -f deno assets.go msg.pb.go msg.pb.js msg.pb.d.ts
|
||||
-rm -rf dist/
|
||||
|
||||
distclean: clean
|
||||
-rm -rf node_modules/
|
||||
|
||||
lint: node_modules
|
||||
yarn lint
|
||||
go vet
|
||||
|
||||
fmt: node_modules
|
||||
yarn fmt
|
||||
go fmt
|
||||
clang-format msg.proto -i
|
||||
|
||||
test: deno
|
||||
go test -v
|
||||
|
||||
.PHONY: test lint clean distclean
|
79
README.md
79
README.md
|
@ -72,79 +72,20 @@ includes submitting trivial PRs (like improving README build instructions).
|
|||
|
||||
## Compile instructions
|
||||
|
||||
I will release binaries at some point, but for now you have to build it
|
||||
yourself.
|
||||
First install the javascript deps.
|
||||
|
||||
You will need [Go](https://golang.org) with `$GOPATH` defined and
|
||||
`$GOPATH/bin` in your `$PATH`.
|
||||
cd deno2
|
||||
|
||||
You will also need [yarn](https://yarnpkg.com/lang/en/docs/install/) installed.
|
||||
cd js; yarn install
|
||||
|
||||
You need Protobuf 3. On Linux this might work:
|
||||
gn gen out/Debug --args='cc_wrapper="ccache" is_debug=true '
|
||||
|
||||
``` bash
|
||||
cd ~
|
||||
wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip
|
||||
unzip protoc-3.1.0-linux-x86_64.zip
|
||||
export PATH=$HOME/bin:$PATH
|
||||
```
|
||||
Then build with ninja:
|
||||
|
||||
On macOS, using [HomeBrew](https://brew.sh/):
|
||||
ninja -C out/Debug/ deno
|
||||
|
||||
``` bash
|
||||
brew install protobuf
|
||||
```
|
||||
|
||||
Then you need [protoc-gen-go](https://github.com/golang/protobuf/tree/master/protoc-gen-go) and [go-bindata](https://github.com/jteeuwen/go-bindata):
|
||||
|
||||
``` bash
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go get -u github.com/jteeuwen/go-bindata/...
|
||||
```
|
||||
|
||||
You need to get and build [v8worker2](https://github.com/ry/v8worker2). __The package will not build with `go
|
||||
get` and will log out an error ⚠__
|
||||
```bash
|
||||
# pkg-config --cflags v8.pc
|
||||
Failed to open 'v8.pc': No such file or directory
|
||||
No package 'v8.pc' found
|
||||
pkg-config: exit status 1
|
||||
```
|
||||
|
||||
__which can be ignored__. It takes about 30 minutes to build:
|
||||
|
||||
``` bash
|
||||
go get -u github.com/ry/v8worker2
|
||||
cd $GOPATH/src/github.com/ry/v8worker2
|
||||
./build.py --use_ccache
|
||||
```
|
||||
Maybe also run `git submodule update --init` in the `v8worker2/` dir.
|
||||
|
||||
Finally, you can get `deno` and its other Go deps.
|
||||
|
||||
``` bash
|
||||
go get -u github.com/ry/deno/...
|
||||
```
|
||||
|
||||
Now you can build deno and run it:
|
||||
|
||||
``` bash
|
||||
cd $GOPATH/src/github.com/ry/deno
|
||||
|
||||
make # Wait for redacted
|
||||
|
||||
./deno testdata/001_hello.js # Output: Hello World
|
||||
```
|
||||
|
||||
## `make` commands
|
||||
|
||||
``` bash
|
||||
make deno # Builds the deno executable.
|
||||
|
||||
make test # Runs the tests.
|
||||
|
||||
make fmt # Formats the code.
|
||||
|
||||
make clean # Cleans the build.
|
||||
```
|
||||
Other useful commands:
|
||||
|
||||
gn args out/Debug/ --list # List build args
|
||||
gn args out/Debug/ # Modify args in $EDITOR
|
||||
gn desc out/Debug/ :deno
|
||||
|
|
13
cmd/main.go
13
cmd/main.go
|
@ -1,13 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ry/deno"
|
||||
)
|
||||
|
||||
func main() {
|
||||
deno.Init()
|
||||
deno.Eval("deno_main.js", "denoMain()")
|
||||
deno.Loop()
|
||||
}
|
124
console.ts
124
console.ts
|
@ -1,124 +0,0 @@
|
|||
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)}`);
|
||||
}
|
||||
}
|
||||
}
|
14
deno.d.ts
vendored
14
deno.d.ts
vendored
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
declare module "deno" {
|
||||
type MessageCallback = (msg: Uint8Array) => void;
|
||||
function sub(channel: string, cb: MessageCallback): void;
|
||||
function pub(channel: string, payload: Uint8Array): null | ArrayBuffer;
|
||||
|
||||
function readFileSync(filename: string): Uint8Array;
|
||||
function writeFileSync(
|
||||
filename: string,
|
||||
data: Uint8Array,
|
||||
perm: number
|
||||
): void;
|
||||
}
|
6
deno.ts
6
deno.ts
|
@ -1,6 +0,0 @@
|
|||
// 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";
|
|
@ -1,68 +0,0 @@
|
|||
# Deno Prototype 2
|
||||
|
||||
## Status
|
||||
|
||||
This code is a rewrite of the privileged parts of Deno. It will soon become
|
||||
the root of the project.
|
||||
|
||||
There are several goals:
|
||||
|
||||
* Use the gn build system for fast builds, sane configuration, and easy
|
||||
linking into Chrome.
|
||||
|
||||
* Use V8 snapshots to improve startup time.
|
||||
|
||||
* Remove Golang. Although it has been working nicely, I am concerned the
|
||||
double GC will become a problem sometime down the road.
|
||||
|
||||
* Distribute a C++ library called libdeno, containing the snapshotted
|
||||
typescript runtime.
|
||||
|
||||
* Test the message passing and other functionality at that layer before
|
||||
involving higher level languages.
|
||||
|
||||
The contenders for building the privileged part of Deno are Rust and C++.
|
||||
Thanks to Chrome and gn, using C++ to link into high level libraries is not
|
||||
untenable. However, there's a lot of interest in Rust in the JS community and
|
||||
it seems like a reasonable choice. TBD.
|
||||
|
||||
There are many people exploring the project, so care will be taken to keep the
|
||||
original code functional while this is developed. However, once it's ready
|
||||
the code in this deno2/ directory will be moved to the root.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Get Depot Tools and make sure it's in your path.
|
||||
http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
|
||||
|
||||
For linux you need these prereqs:
|
||||
|
||||
sudo apt-get install libgtk-3-dev pkg-config ccache
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
First install the javascript deps.
|
||||
|
||||
cd js; yarn install
|
||||
|
||||
TODO(ry) Remove the above step by a deps submodule.
|
||||
|
||||
Wrapper around the gclient/gn/ninja for end users. Try this first:
|
||||
|
||||
./tools/build.py --use_ccache --debug
|
||||
|
||||
If that doesn't work, or you need more control, try calling gn manually:
|
||||
|
||||
gn gen out/Debug --args='cc_wrapper="ccache" is_debug=true '
|
||||
|
||||
Then build with ninja:
|
||||
|
||||
ninja -C out/Debug/ deno
|
||||
|
||||
|
||||
Other useful commands:
|
||||
|
||||
gn args out/Debug/ --list # List build args
|
||||
gn args out/Debug/ # Modify args in $EDITOR
|
|
@ -1 +0,0 @@
|
|||
../msg.proto
|
102
deno2/msg.proto
Normal file
102
deno2/msg.proto
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
syntax = "proto3";
|
||||
package deno;
|
||||
option go_package = "deno";
|
||||
|
||||
message BaseMsg {
|
||||
string channel = 1;
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
||||
message Msg {
|
||||
enum Command {
|
||||
ERROR = 0;
|
||||
START = 1;
|
||||
CODE_FETCH = 2;
|
||||
CODE_FETCH_RES = 3;
|
||||
CODE_CACHE = 4;
|
||||
EXIT = 5;
|
||||
TIMER_START = 6;
|
||||
TIMER_READY = 7;
|
||||
TIMER_CLEAR = 8;
|
||||
FETCH_REQ = 9;
|
||||
FETCH_RES = 10;
|
||||
READ_FILE_SYNC = 11;
|
||||
READ_FILE_SYNC_RES = 12;
|
||||
WRITE_FILE_SYNC = 13;
|
||||
}
|
||||
Command command = 1;
|
||||
|
||||
// We avoid creating a message for each command (and use oneof or any types)
|
||||
// In order to reduce code in the size of the generated javascript
|
||||
// "msg.pb.js". It seems that each new message adds 20k and we want to
|
||||
// potentially add many hundreds of commands. Therefore we just prefix command
|
||||
// arguments by their name.
|
||||
|
||||
// ERROR
|
||||
string error = 2;
|
||||
|
||||
// START
|
||||
string start_cwd = 10;
|
||||
repeated string start_argv = 11;
|
||||
bool start_debug_flag = 12;
|
||||
string start_main_js = 13; // The contents of dist/main.js
|
||||
string start_main_map = 14; // The contents of dist/main.map
|
||||
|
||||
// CODE_FETCH
|
||||
string code_fetch_module_specifier = 20;
|
||||
string code_fetch_containing_file = 21;
|
||||
|
||||
// CODE_FETCH_RES
|
||||
// If it's a non-http module, moduleName and filename will be the same.
|
||||
// For http modules, moduleName is its resolved http URL, and filename
|
||||
// is the location of the locally downloaded source code.
|
||||
string code_fetch_res_module_name = 30;
|
||||
string code_fetch_res_filename = 31;
|
||||
string code_fetch_res_source_code = 32;
|
||||
string code_fetch_res_output_code = 33; // Non-empty only if cached.
|
||||
|
||||
// CODE_CACHE
|
||||
string code_cache_filename = 41;
|
||||
string code_cache_source_code = 42;
|
||||
string code_cache_output_code = 43;
|
||||
|
||||
// EXIT
|
||||
int32 exit_code = 50;
|
||||
|
||||
// TIMER_START
|
||||
int32 timer_start_id = 60;
|
||||
bool timer_start_interval = 61;
|
||||
int32 timer_start_delay = 62; // In milliseconds.
|
||||
|
||||
// TIMER_READY
|
||||
int32 timer_ready_id = 70;
|
||||
bool timer_ready_done = 71;
|
||||
|
||||
// TIMER_CLEAR
|
||||
int32 timer_clear_id = 80;
|
||||
|
||||
// FETCH_REQ
|
||||
int32 fetch_req_id = 90;
|
||||
string fetch_req_url = 91;
|
||||
// repeated string fetch_req_header_line = 91
|
||||
|
||||
// FETCH_RES
|
||||
int32 fetch_res_id = 100;
|
||||
int32 fetch_res_status = 101;
|
||||
repeated string fetch_res_header_line = 102;
|
||||
bytes fetch_res_body = 103;
|
||||
|
||||
// READ_FILE_SYNC
|
||||
string read_file_sync_filename = 110;
|
||||
|
||||
// READ_FILE_SYNC_RES
|
||||
bytes read_file_sync_data = 120;
|
||||
|
||||
// WRITE_FILE_SYNC
|
||||
string write_file_sync_filename = 130;
|
||||
bytes write_file_sync_data = 131;
|
||||
uint32 write_file_sync_perm = 132;
|
||||
// write_file_sync_perm specified by https://godoc.org/os#FileMode
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Get Depot Tools and make sure it's in your path.
|
||||
# http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
|
||||
# Use .gclient to modify the deps.
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
TARGET = "deno"
|
||||
|
||||
parser = argparse.ArgumentParser(description="build.py")
|
||||
parser.add_argument('--debug', dest='debug', action='store_true')
|
||||
parser.add_argument('--use_ccache', dest='use_ccache', action='store_true')
|
||||
parser.add_argument('--sync', dest='sync', action='store_true')
|
||||
parser.set_defaults(debug=False, use_ccache=False, sync=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(root_path)
|
||||
buildName = "Debug" if args.debug else "Default"
|
||||
buildDir = os.path.join(root_path, "out", buildName)
|
||||
# Run sync if any of the dep dirs don't exist.
|
||||
# Or the user supplied the --sync flag.
|
||||
if args.sync or dirsMissing():
|
||||
run(["gclient", "sync", "--no-history"])
|
||||
|
||||
# Run gn gen out/Default if out doesn't exist.
|
||||
if not os.path.exists(buildDir):
|
||||
gn_gen = ["gn", "gen", buildDir]
|
||||
gn_args = []
|
||||
if args.debug:
|
||||
gn_args.append("is_debug=true")
|
||||
if args.use_ccache:
|
||||
gn_args.append("cc_wrapper=\"ccache\"")
|
||||
if len(gn_args) > 0:
|
||||
gn_gen += ["--args=%s" % " ".join(gn_args)]
|
||||
run(gn_gen)
|
||||
|
||||
# Always run ninja.
|
||||
run(["ninja", "-C", buildDir, TARGET])
|
||||
|
||||
|
||||
def run(args):
|
||||
print " ".join(args)
|
||||
env = os.environ.copy()
|
||||
subprocess.check_call(args, env=env)
|
||||
|
||||
|
||||
def dirsMissing():
|
||||
dirsToLoad = [
|
||||
"v8",
|
||||
"third_party/protobuf",
|
||||
"tools/protoc_wrapper",
|
||||
"third_party/zlib",
|
||||
]
|
||||
for d in dirsToLoad:
|
||||
if not os.path.exists(d):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if '__main__' == __name__:
|
||||
main()
|
111
deno_dir.go
111
deno_dir.go
|
@ -1,111 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var flagCacheDir = flag.String("cachedir", "",
|
||||
"Where to cache compilation artifacts. Default: ~/.deno")
|
||||
|
||||
var DenoDir string
|
||||
var CacheDir string
|
||||
var SrcDir string
|
||||
|
||||
func SourceCodeHash(filename string, sourceCodeBuf []byte) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(filename))
|
||||
h.Write(sourceCodeBuf)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func CacheFileName(filename string, sourceCodeBuf []byte) string {
|
||||
cacheKey := SourceCodeHash(filename, sourceCodeBuf)
|
||||
return path.Join(CacheDir, cacheKey+".js")
|
||||
}
|
||||
|
||||
// Fetches a remoteUrl but also caches it to the localFilename.
|
||||
func FetchRemoteSource(remoteUrl string, localFilename string) ([]byte, error) {
|
||||
logDebug("FetchRemoteSource %s %s", remoteUrl, localFilename)
|
||||
assert(strings.HasPrefix(localFilename, SrcDir),
|
||||
"Expected filename to start with SrcDir: "+localFilename)
|
||||
var sourceReader io.Reader
|
||||
|
||||
file, err := os.Open(localFilename)
|
||||
if *flagReload || os.IsNotExist(err) {
|
||||
// Fetch from HTTP.
|
||||
println("Downloading", remoteUrl)
|
||||
res, err := http.Get(remoteUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
err = os.MkdirAll(path.Dir(localFilename), 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write to local file. Need to reopen it for writing.
|
||||
file, err = os.OpenFile(localFilename, os.O_RDWR|os.O_CREATE, 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sourceReader = io.TeeReader(res.Body, file) // Fancy!
|
||||
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
sourceReader = file
|
||||
}
|
||||
defer file.Close()
|
||||
return ioutil.ReadAll(sourceReader)
|
||||
}
|
||||
|
||||
func LoadOutputCodeCache(filename string, sourceCodeBuf []byte) (
|
||||
outputCode string, err error) {
|
||||
cacheFn := CacheFileName(filename, sourceCodeBuf)
|
||||
outputCodeBuf, err := ioutil.ReadFile(cacheFn)
|
||||
if os.IsNotExist(err) {
|
||||
// Ignore error if we can't find the cache file.
|
||||
err = nil
|
||||
} else if err == nil {
|
||||
outputCode = string(outputCodeBuf)
|
||||
}
|
||||
return outputCode, err
|
||||
}
|
||||
|
||||
func UserHomeDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
home := path.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"))
|
||||
if home == "" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
}
|
||||
return home
|
||||
}
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
func createDirs() {
|
||||
if *flagCacheDir == "" {
|
||||
DenoDir = path.Join(UserHomeDir(), ".deno")
|
||||
} else {
|
||||
DenoDir = *flagCacheDir
|
||||
}
|
||||
CacheDir = path.Join(DenoDir, "cache")
|
||||
err := os.MkdirAll(CacheDir, 0700)
|
||||
check(err)
|
||||
SrcDir = path.Join(DenoDir, "src")
|
||||
err = os.MkdirAll(SrcDir, 0700)
|
||||
check(err)
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func SetCacheDirForTest(prefix string) {
|
||||
dir, err := ioutil.TempDir("", prefix)
|
||||
check(err)
|
||||
CacheDir = dir
|
||||
}
|
||||
|
||||
func TestLoadOutputCodeCache(t *testing.T) {
|
||||
SetCacheDirForTest("TestLoadOutputCodeCache")
|
||||
|
||||
filename := "Hello.ts"
|
||||
sourceCodeBuf := []byte("1+2")
|
||||
|
||||
cacheFn := CacheFileName(filename, sourceCodeBuf)
|
||||
|
||||
outputCode, err := LoadOutputCodeCache(filename, sourceCodeBuf)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
if outputCode != "" {
|
||||
t.Fatalf("Expected empty outputCode but got <<%s>>", outputCode)
|
||||
}
|
||||
|
||||
// Now let's write to the cache file
|
||||
err = ioutil.WriteFile(cacheFn, []byte("blah"), 0700)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// Try it again.
|
||||
outputCode, err = LoadOutputCodeCache(filename, sourceCodeBuf)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
if outputCode != "blah" {
|
||||
t.Fatalf("Bad outputCode but got <<%s>>", outputCode)
|
||||
}
|
||||
}
|
126
dispatch.go
126
dispatch.go
|
@ -1,126 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var resChan = make(chan *BaseMsg, 10)
|
||||
var doneChan = make(chan bool)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
var stats struct {
|
||||
v8workerSend int
|
||||
v8workerRespond int
|
||||
v8workerRecv int
|
||||
v8workerBytesSent int
|
||||
v8workerBytesRecv int
|
||||
}
|
||||
|
||||
var channels = make(map[string][]Subscriber)
|
||||
|
||||
type Subscriber func(payload []byte) []byte
|
||||
|
||||
func recv(buf []byte) (response []byte) {
|
||||
stats.v8workerRecv++
|
||||
stats.v8workerBytesRecv += len(buf)
|
||||
|
||||
msg := &BaseMsg{}
|
||||
check(proto.Unmarshal(buf, msg))
|
||||
assert(len(msg.Payload) > 0, "BaseMsg has empty payload.")
|
||||
subscribers, ok := channels[msg.Channel]
|
||||
if !ok {
|
||||
panic("No subscribers for channel " + msg.Channel)
|
||||
}
|
||||
for i := 0; i < len(subscribers); i++ {
|
||||
s := subscribers[i]
|
||||
r := s(msg.Payload)
|
||||
if r != nil {
|
||||
response = r
|
||||
}
|
||||
}
|
||||
if response != nil {
|
||||
stats.v8workerRespond++
|
||||
stats.v8workerBytesSent += len(response)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func Sub(channel string, cb Subscriber) {
|
||||
subscribers, ok := channels[channel]
|
||||
if !ok {
|
||||
subscribers = make([]Subscriber, 0)
|
||||
}
|
||||
subscribers = append(subscribers, cb)
|
||||
channels[channel] = subscribers
|
||||
}
|
||||
|
||||
func Pub(channel string, payload []byte) {
|
||||
wg.Add(1)
|
||||
resChan <- &BaseMsg{
|
||||
Channel: channel,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
func PubMsg(channel string, msg *Msg) {
|
||||
payload, err := proto.Marshal(msg)
|
||||
check(err)
|
||||
Pub(channel, payload)
|
||||
}
|
||||
|
||||
func DispatchLoop() {
|
||||
wg.Add(1)
|
||||
first := true
|
||||
|
||||
// In a goroutine, we wait on for all goroutines to complete (for example
|
||||
// timers). We use this to signal to the main thread to exit.
|
||||
// wg.Add(1) basically translates to uv_ref, if this was Node.
|
||||
// wg.Done() basically translates to uv_unref
|
||||
go func() {
|
||||
wg.Wait()
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-resChan:
|
||||
out, err := proto.Marshal(msg)
|
||||
check(err)
|
||||
err = worker.SendBytes(out)
|
||||
stats.v8workerSend++
|
||||
stats.v8workerBytesSent += len(out)
|
||||
exitOnError(err)
|
||||
wg.Done() // Corresponds to the wg.Add(1) in Pub().
|
||||
case <-doneChan:
|
||||
// All goroutines have completed. Now we can exit main().
|
||||
checkChanEmpty()
|
||||
return
|
||||
}
|
||||
|
||||
// We don't want to exit until we've received at least one message.
|
||||
// This is so the program doesn't exit after sending the "start"
|
||||
// message.
|
||||
if first {
|
||||
wg.Done()
|
||||
}
|
||||
first = false
|
||||
}
|
||||
}
|
||||
|
||||
func checkChanEmpty() {
|
||||
// We've received a done event. As a sanity check, make sure that resChan is
|
||||
// empty.
|
||||
select {
|
||||
case _, ok := <-resChan:
|
||||
if ok {
|
||||
panic("Read a message from resChan after doneChan closed.")
|
||||
} else {
|
||||
panic("resChan closed. Unexpected.")
|
||||
}
|
||||
default:
|
||||
// No value ready, moving on.
|
||||
}
|
||||
}
|
73
dispatch.ts
73
dispatch.ts
|
@ -1,73 +0,0 @@
|
|||
// 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;
|
11
echo.go
11
echo.go
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
// For testing
|
||||
func InitEcho() {
|
||||
Sub("echo", func(buf []byte) []byte {
|
||||
Pub("echo", buf)
|
||||
return nil
|
||||
})
|
||||
}
|
72
fetch.go
72
fetch.go
|
@ -1,72 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func InitFetch() {
|
||||
Sub("fetch", func(buf []byte) []byte {
|
||||
msg := &Msg{}
|
||||
check(proto.Unmarshal(buf, msg))
|
||||
switch msg.Command {
|
||||
case Msg_FETCH_REQ:
|
||||
return Fetch(
|
||||
msg.FetchReqId,
|
||||
msg.FetchReqUrl)
|
||||
default:
|
||||
panic("[fetch] Unexpected message " + string(buf))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fetch(id int32, targetUrl string) []byte {
|
||||
logDebug("Fetch %d %s", id, targetUrl)
|
||||
async(func() {
|
||||
resMsg := &Msg{
|
||||
Command: Msg_FETCH_RES,
|
||||
FetchResId: id,
|
||||
}
|
||||
|
||||
if !Perms.Net {
|
||||
resMsg.Error = "Network access denied."
|
||||
PubMsg("fetch", resMsg)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.Get(targetUrl)
|
||||
if err != nil {
|
||||
resMsg.Error = err.Error()
|
||||
PubMsg("fetch", resMsg)
|
||||
return
|
||||
}
|
||||
if resp == nil {
|
||||
resMsg.Error = "resp is nil "
|
||||
PubMsg("fetch", resMsg)
|
||||
return
|
||||
}
|
||||
|
||||
resMsg.FetchResStatus = int32(resp.StatusCode)
|
||||
logDebug("fetch success %d %s", resMsg.FetchResStatus, targetUrl)
|
||||
PubMsg("fetch", resMsg)
|
||||
|
||||
// Now we read the body and send another message0
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if resp == nil {
|
||||
resMsg.Error = "resp is nil "
|
||||
PubMsg("fetch", resMsg)
|
||||
return
|
||||
}
|
||||
|
||||
resMsg.FetchResBody = body
|
||||
PubMsg("fetch", resMsg)
|
||||
|
||||
// TODO streaming.
|
||||
})
|
||||
return nil
|
||||
}
|
146
fetch.ts
146
fetch.ts
|
@ -1,146 +0,0 @@
|
|||
// 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
globals.ts
32
globals.ts
|
@ -1,32 +0,0 @@
|
|||
// 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;
|
|
@ -1,139 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var denoFn string
|
||||
|
||||
// Some tests require an HTTP server. We start one here.
|
||||
// Note that "localhost:4545" is hardcoded into the tests at the moment,
|
||||
// so if the server runs on a different port, it will fail.
|
||||
func startServer() {
|
||||
l, err := net.Listen("tcp", ":4545")
|
||||
check(err)
|
||||
rootHandler := http.FileServer(http.Dir("."))
|
||||
go func() {
|
||||
err := http.Serve(l, rootHandler)
|
||||
check(err)
|
||||
}()
|
||||
}
|
||||
|
||||
func listTestFiles() []string {
|
||||
files, err := ioutil.ReadDir("testdata")
|
||||
check(err)
|
||||
out := make([]string, 0)
|
||||
for _, file := range files {
|
||||
fn := file.Name()
|
||||
if strings.HasSuffix(fn, ".out") {
|
||||
out = append(out, fn)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func checkOutput(t *testing.T, outFile string, shouldSucceed bool) {
|
||||
outFile = path.Join("testdata", outFile)
|
||||
jsFile := strings.TrimSuffix(outFile, ".out")
|
||||
|
||||
expected, err := ioutil.ReadFile(outFile)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
actual, _, err := deno(jsFile)
|
||||
if shouldSucceed && err != nil {
|
||||
t.Fatalf("Expected success %s", err.Error())
|
||||
} else if !shouldSucceed && err == nil {
|
||||
t.Fatalf("Expected failure but got success")
|
||||
}
|
||||
if !patternMatch(string(expected), string(actual)) {
|
||||
t.Fatalf(`Actual output does not match expected.
|
||||
-----Actual-------------------
|
||||
%s-----Expected-----------------
|
||||
%s------------------------------`, string(actual), string(expected))
|
||||
}
|
||||
}
|
||||
|
||||
func deno(inputFn string) (actual []byte, cachedir string, err error) {
|
||||
cachedir, err = ioutil.TempDir("", "TestIntegration")
|
||||
check(err)
|
||||
|
||||
cmd := exec.Command(denoFn, "--cachedir="+cachedir, inputFn)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &out
|
||||
err = cmd.Run()
|
||||
actual = out.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func integrationTestSetup() {
|
||||
if denoFn == "" {
|
||||
startServer()
|
||||
cwd, err := os.Getwd()
|
||||
check(err)
|
||||
denoFn = path.Join(cwd, "deno")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationFiles(t *testing.T) {
|
||||
integrationTestSetup()
|
||||
outFiles := listTestFiles()
|
||||
for _, outFile := range outFiles {
|
||||
t.Run(outFile, func(t *testing.T) {
|
||||
shouldSucceed := strings.Index(outFile, "error") < 0
|
||||
checkOutput(t, outFile, shouldSucceed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationUrlArgs(t *testing.T) {
|
||||
integrationTestSetup()
|
||||
|
||||
// Using good port 4545
|
||||
_, cachedir, err := deno("http://localhost:4545/testdata/001_hello.js")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected success. %s", err.Error())
|
||||
}
|
||||
cacheFn := path.Join(cachedir, "src/localhost:4545/testdata/001_hello.js")
|
||||
println("good cacheFn", cacheFn)
|
||||
if !exists(cacheFn) {
|
||||
t.Fatalf("Expected 200 at '%s'", cacheFn)
|
||||
}
|
||||
// TODO check output
|
||||
|
||||
// Using bad port 4546 instead of 4545.
|
||||
_, cachedir, err = deno("http://localhost:4546/testdata/001_hello.js")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected 404. %s", err.Error())
|
||||
}
|
||||
// Check that cache dir is empty.
|
||||
cacheFn = path.Join(cachedir, "src/localhost:4546/testdata/001_hello.js")
|
||||
println("bad cacheFn", cacheFn)
|
||||
if exists(cacheFn) {
|
||||
t.Fatalf("Expected 404 at '%s'", cacheFn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestsTs(t *testing.T) {
|
||||
integrationTestSetup()
|
||||
// TODO Need unit test for each of the permissions.
|
||||
cmd := exec.Command(denoFn, "--allow-net", "--allow-write", "tests.ts")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
141
main.go
141
main.go
|
@ -1,141 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/ry/v8worker2"
|
||||
)
|
||||
|
||||
var flagReload = flag.Bool("reload", false, "Reload cached remote source code.")
|
||||
var flagV8Options = flag.Bool("v8-options", false, "Print V8 command line options.")
|
||||
var flagDebug = flag.Bool("debug", false, "Enable debug output.")
|
||||
var flagCPUProf = flag.String("cpuprof", "", "Write golang cpu profile to file.")
|
||||
var flagMemProf = flag.String("memprof", "", "Write golang memory profile to file.")
|
||||
|
||||
var flagAllowRead = flag.Bool("allow-read", true,
|
||||
"Allow program to read file system.")
|
||||
var flagAllowWrite = flag.Bool("allow-write", false,
|
||||
"Allow program to write to the fs.")
|
||||
var flagAllowNet = flag.Bool("allow-net", false,
|
||||
"Allow program to make network connection.")
|
||||
|
||||
var memProfile *os.File
|
||||
|
||||
var Perms struct {
|
||||
FsRead bool
|
||||
FsWrite bool
|
||||
Net bool
|
||||
}
|
||||
|
||||
func setPerms() {
|
||||
Perms.FsRead = *flagAllowRead
|
||||
Perms.FsWrite = *flagAllowWrite
|
||||
Perms.Net = *flagAllowNet
|
||||
}
|
||||
|
||||
func stringAsset(filename string) string {
|
||||
data, err := Asset(path.Join("dist", filename))
|
||||
check(err)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func FlagsParse() []string {
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
setPerms()
|
||||
if *flagV8Options {
|
||||
args = append(args, "--help")
|
||||
}
|
||||
// Adding this causes testdata/007_stack_trace.ts to fail without a
|
||||
// stacktrace.
|
||||
// args = append(args, "--abort-on-uncaught-exception")
|
||||
args = v8worker2.SetFlags(args)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
// There is a single global worker for this process.
|
||||
// This file should be the only part of deno that directly access it, so that
|
||||
// all interaction with V8 can go through a single point.
|
||||
var worker *v8worker2.Worker
|
||||
var workerArgs []string
|
||||
var main_js string
|
||||
var main_map string
|
||||
|
||||
func Init() {
|
||||
workerArgs = FlagsParse()
|
||||
|
||||
if len(workerArgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s file.ts\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Maybe start Golang profilers.
|
||||
// Use --prof for profiling JS.
|
||||
StartProfiling()
|
||||
|
||||
createDirs()
|
||||
InitOS()
|
||||
InitEcho()
|
||||
InitTimers()
|
||||
InitFetch()
|
||||
|
||||
worker = v8worker2.New(recv)
|
||||
|
||||
main_js = stringAsset("main.js")
|
||||
err := worker.Load("/main.js", main_js)
|
||||
exitOnError(err)
|
||||
main_map = stringAsset("main.map")
|
||||
}
|
||||
|
||||
func StartProfiling() {
|
||||
if *flagCPUProf != "" {
|
||||
cpuProfile, err := os.Create(*flagCPUProf)
|
||||
check(err)
|
||||
check(pprof.StartCPUProfile(cpuProfile))
|
||||
}
|
||||
if *flagMemProf != "" {
|
||||
var err error
|
||||
memProfile, err = os.Create(*flagMemProf)
|
||||
check(err)
|
||||
check(pprof.WriteHeapProfile(memProfile))
|
||||
}
|
||||
}
|
||||
|
||||
func stopProfiling() {
|
||||
if *flagCPUProf != "" {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
if *flagMemProf != "" {
|
||||
check(memProfile.Close())
|
||||
}
|
||||
}
|
||||
|
||||
// It's up to library users to call
|
||||
// deno.Eval("deno_main.js", "denoMain()")
|
||||
func Eval(filename string, code string) {
|
||||
err := worker.Load(filename, code)
|
||||
exitOnError(err)
|
||||
}
|
||||
|
||||
func Loop() {
|
||||
cwd, err := os.Getwd()
|
||||
check(err)
|
||||
PubMsg("start", &Msg{
|
||||
Command: Msg_START,
|
||||
StartCwd: cwd,
|
||||
StartArgv: workerArgs,
|
||||
StartDebugFlag: *flagDebug,
|
||||
StartMainJs: main_js,
|
||||
StartMainMap: main_map,
|
||||
})
|
||||
DispatchLoop()
|
||||
stopProfiling()
|
||||
}
|
54
main.ts
54
main.ts
|
@ -1,54 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
// This allows us to have async/await in our code. It must be loaded first.
|
||||
import "babel-polyfill";
|
||||
|
||||
import * as dispatch from "./dispatch";
|
||||
import { deno as pb } from "./msg.pb";
|
||||
|
||||
import * as runtime from "./runtime";
|
||||
import * as util from "./util";
|
||||
|
||||
import { initTimers } from "./timers";
|
||||
import { initFetch } from "./fetch";
|
||||
|
||||
// To control internal logging output
|
||||
// Set with the -debug command-line flag.
|
||||
export let debug = false;
|
||||
let startCalled = false;
|
||||
|
||||
// denoMain is needed to allow hooks into the system.
|
||||
// Also eventual snapshot support needs it.
|
||||
// tslint:disable-next-line:no-any
|
||||
(window as any)["denoMain"] = () => {
|
||||
// tslint:disable-next-line:no-any
|
||||
delete (window as any)["denoMain"];
|
||||
|
||||
initTimers();
|
||||
initFetch();
|
||||
|
||||
dispatch.sub("start", (payload: Uint8Array) => {
|
||||
if (startCalled) {
|
||||
throw Error("start message received more than once!");
|
||||
}
|
||||
startCalled = true;
|
||||
|
||||
const msg = pb.Msg.decode(payload);
|
||||
const {
|
||||
startCwd: cwd,
|
||||
startArgv: argv,
|
||||
startDebugFlag: debugFlag,
|
||||
startMainJs: mainJs,
|
||||
startMainMap: mainMap
|
||||
} = msg;
|
||||
|
||||
debug = debugFlag;
|
||||
util.log("start", { cwd, argv, debugFlag });
|
||||
|
||||
runtime.setup(mainJs, mainMap);
|
||||
|
||||
const inputFn = argv[0];
|
||||
const mod = runtime.resolveModule(inputFn, `${cwd}/`);
|
||||
mod.compileAndRun();
|
||||
});
|
||||
};
|
102
msg.proto
102
msg.proto
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
syntax = "proto3";
|
||||
package deno;
|
||||
option go_package = "deno";
|
||||
|
||||
message BaseMsg {
|
||||
string channel = 1;
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
||||
message Msg {
|
||||
enum Command {
|
||||
ERROR = 0;
|
||||
START = 1;
|
||||
CODE_FETCH = 2;
|
||||
CODE_FETCH_RES = 3;
|
||||
CODE_CACHE = 4;
|
||||
EXIT = 5;
|
||||
TIMER_START = 6;
|
||||
TIMER_READY = 7;
|
||||
TIMER_CLEAR = 8;
|
||||
FETCH_REQ = 9;
|
||||
FETCH_RES = 10;
|
||||
READ_FILE_SYNC = 11;
|
||||
READ_FILE_SYNC_RES = 12;
|
||||
WRITE_FILE_SYNC = 13;
|
||||
}
|
||||
Command command = 1;
|
||||
|
||||
// We avoid creating a message for each command (and use oneof or any types)
|
||||
// In order to reduce code in the size of the generated javascript
|
||||
// "msg.pb.js". It seems that each new message adds 20k and we want to
|
||||
// potentially add many hundreds of commands. Therefore we just prefix command
|
||||
// arguments by their name.
|
||||
|
||||
// ERROR
|
||||
string error = 2;
|
||||
|
||||
// START
|
||||
string start_cwd = 10;
|
||||
repeated string start_argv = 11;
|
||||
bool start_debug_flag = 12;
|
||||
string start_main_js = 13; // The contents of dist/main.js
|
||||
string start_main_map = 14; // The contents of dist/main.map
|
||||
|
||||
// CODE_FETCH
|
||||
string code_fetch_module_specifier = 20;
|
||||
string code_fetch_containing_file = 21;
|
||||
|
||||
// CODE_FETCH_RES
|
||||
// If it's a non-http module, moduleName and filename will be the same.
|
||||
// For http modules, moduleName is its resolved http URL, and filename
|
||||
// is the location of the locally downloaded source code.
|
||||
string code_fetch_res_module_name = 30;
|
||||
string code_fetch_res_filename = 31;
|
||||
string code_fetch_res_source_code = 32;
|
||||
string code_fetch_res_output_code = 33; // Non-empty only if cached.
|
||||
|
||||
// CODE_CACHE
|
||||
string code_cache_filename = 41;
|
||||
string code_cache_source_code = 42;
|
||||
string code_cache_output_code = 43;
|
||||
|
||||
// EXIT
|
||||
int32 exit_code = 50;
|
||||
|
||||
// TIMER_START
|
||||
int32 timer_start_id = 60;
|
||||
bool timer_start_interval = 61;
|
||||
int32 timer_start_delay = 62; // In milliseconds.
|
||||
|
||||
// TIMER_READY
|
||||
int32 timer_ready_id = 70;
|
||||
bool timer_ready_done = 71;
|
||||
|
||||
// TIMER_CLEAR
|
||||
int32 timer_clear_id = 80;
|
||||
|
||||
// FETCH_REQ
|
||||
int32 fetch_req_id = 90;
|
||||
string fetch_req_url = 91;
|
||||
// repeated string fetch_req_header_line = 91
|
||||
|
||||
// FETCH_RES
|
||||
int32 fetch_res_id = 100;
|
||||
int32 fetch_res_status = 101;
|
||||
repeated string fetch_res_header_line = 102;
|
||||
bytes fetch_res_body = 103;
|
||||
|
||||
// READ_FILE_SYNC
|
||||
string read_file_sync_filename = 110;
|
||||
|
||||
// READ_FILE_SYNC_RES
|
||||
bytes read_file_sync_data = 120;
|
||||
|
||||
// WRITE_FILE_SYNC
|
||||
string write_file_sync_filename = 130;
|
||||
bytes write_file_sync_data = 131;
|
||||
uint32 write_file_sync_perm = 132;
|
||||
// write_file_sync_perm specified by https://godoc.org/os#FileMode
|
||||
}
|
195
os.go
195
os.go
|
@ -1,195 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/spf13/afero"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const assetPrefix string = "/$asset$/"
|
||||
|
||||
var fs afero.Fs
|
||||
|
||||
func InitOS() {
|
||||
if Perms.FsWrite {
|
||||
assert(Perms.FsRead, "Write access requires read access.")
|
||||
fs = afero.NewOsFs()
|
||||
} else if Perms.FsRead {
|
||||
fs = afero.NewReadOnlyFs(afero.NewOsFs())
|
||||
} else {
|
||||
panic("Not implemented.")
|
||||
}
|
||||
|
||||
Sub("os", func(buf []byte) []byte {
|
||||
msg := &Msg{}
|
||||
check(proto.Unmarshal(buf, msg))
|
||||
switch msg.Command {
|
||||
case Msg_CODE_FETCH:
|
||||
return HandleCodeFetch(
|
||||
msg.CodeFetchModuleSpecifier,
|
||||
msg.CodeFetchContainingFile)
|
||||
case Msg_CODE_CACHE:
|
||||
return HandleCodeCache(
|
||||
msg.CodeCacheFilename,
|
||||
msg.CodeCacheSourceCode,
|
||||
msg.CodeCacheOutputCode)
|
||||
case Msg_EXIT:
|
||||
os.Exit(int(msg.ExitCode))
|
||||
case Msg_READ_FILE_SYNC:
|
||||
return ReadFileSync(msg.ReadFileSyncFilename)
|
||||
case Msg_WRITE_FILE_SYNC:
|
||||
return WriteFileSync(msg.WriteFileSyncFilename, msg.WriteFileSyncData,
|
||||
msg.WriteFileSyncPerm)
|
||||
default:
|
||||
panic("[os] Unexpected message " + string(buf))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func SrcFileToUrl(filename string) string {
|
||||
assert(len(SrcDir) > 0, "SrcDir shouldn't be empty")
|
||||
if strings.HasPrefix(filename, SrcDir) {
|
||||
rest := strings.TrimPrefix(filename, SrcDir)
|
||||
if rest[0] == '/' {
|
||||
rest = rest[1:]
|
||||
}
|
||||
|
||||
return "http://" + rest
|
||||
} else {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveModule(moduleSpecifier string, containingFile string) (
|
||||
moduleName string, filename string, err error) {
|
||||
|
||||
logDebug("os.go ResolveModule moduleSpecifier %s containingFile %s",
|
||||
moduleSpecifier, containingFile)
|
||||
|
||||
containingFile = SrcFileToUrl(containingFile)
|
||||
moduleSpecifier = SrcFileToUrl(moduleSpecifier)
|
||||
|
||||
logDebug("os.go ResolveModule after moduleSpecifier %s containingFile %s",
|
||||
moduleSpecifier, containingFile)
|
||||
|
||||
moduleUrl, err := url.Parse(moduleSpecifier)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseUrl, err := url.Parse(containingFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resolved := baseUrl.ResolveReference(moduleUrl)
|
||||
moduleName = resolved.String()
|
||||
if resolved.IsAbs() {
|
||||
filename = path.Join(SrcDir, resolved.Host, resolved.Path)
|
||||
} else {
|
||||
filename = resolved.Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HandleCodeFetch(moduleSpecifier string, containingFile string) (out []byte) {
|
||||
assert(moduleSpecifier != "", "moduleSpecifier shouldn't be empty")
|
||||
res := &Msg{}
|
||||
var sourceCodeBuf []byte
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
}
|
||||
out, err = proto.Marshal(res)
|
||||
check(err)
|
||||
}()
|
||||
|
||||
moduleName, filename, err := ResolveModule(moduleSpecifier, containingFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logDebug("CodeFetch moduleName %s moduleSpecifier %s containingFile %s filename %s",
|
||||
moduleName, moduleSpecifier, containingFile, filename)
|
||||
|
||||
if isRemote(moduleName) {
|
||||
sourceCodeBuf, err = FetchRemoteSource(moduleName, filename)
|
||||
} else if strings.HasPrefix(moduleName, assetPrefix) {
|
||||
f := strings.TrimPrefix(moduleName, assetPrefix)
|
||||
sourceCodeBuf, err = Asset(path.Join("dist", f))
|
||||
if err != nil {
|
||||
logDebug("%s Asset doesn't exist. Return without error", moduleName)
|
||||
err = nil
|
||||
}
|
||||
} else {
|
||||
assert(moduleName == filename,
|
||||
"if a module isn't remote, it should have the same filename")
|
||||
sourceCodeBuf, err = ioutil.ReadFile(moduleName)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
outputCode, err := LoadOutputCodeCache(filename, sourceCodeBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var sourceCode = string(sourceCodeBuf)
|
||||
res = &Msg{
|
||||
Command: Msg_CODE_FETCH_RES,
|
||||
CodeFetchResModuleName: moduleName,
|
||||
CodeFetchResFilename: filename,
|
||||
CodeFetchResSourceCode: sourceCode,
|
||||
CodeFetchResOutputCode: outputCode,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HandleCodeCache(filename string, sourceCode string,
|
||||
outputCode string) []byte {
|
||||
|
||||
fn := CacheFileName(filename, []byte(sourceCode))
|
||||
outputCodeBuf := []byte(outputCode)
|
||||
err := ioutil.WriteFile(fn, outputCodeBuf, 0600)
|
||||
res := &Msg{}
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
}
|
||||
out, err := proto.Marshal(res)
|
||||
check(err)
|
||||
return out
|
||||
}
|
||||
|
||||
func ReadFileSync(filename string) []byte {
|
||||
data, err := afero.ReadFile(fs, filename)
|
||||
res := &Msg{
|
||||
Command: Msg_READ_FILE_SYNC_RES,
|
||||
}
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
} else {
|
||||
res.ReadFileSyncData = data
|
||||
}
|
||||
out, err := proto.Marshal(res)
|
||||
check(err)
|
||||
return out
|
||||
}
|
||||
|
||||
func WriteFileSync(filename string, data []byte, perm uint32) []byte {
|
||||
err := afero.WriteFile(fs, filename, data, os.FileMode(perm))
|
||||
res := &Msg{}
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
}
|
||||
out, err := proto.Marshal(res)
|
||||
check(err)
|
||||
return out
|
||||
}
|
65
os.ts
65
os.ts
|
@ -1,65 +0,0 @@
|
|||
// 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
|
||||
});
|
||||
}
|
87
os_test.go
87
os_test.go
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func AssertEqual(t *testing.T, actual string, expected string) {
|
||||
if actual != expected {
|
||||
t.Fatalf("not equal <<%s>> <<%s>>", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveModule1(t *testing.T) {
|
||||
createDirs()
|
||||
moduleName, filename, err := ResolveModule(
|
||||
"http://localhost:4545/testdata/subdir/print_hello.ts",
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/006_url_imports.ts")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
AssertEqual(t, moduleName,
|
||||
"http://localhost:4545/testdata/subdir/print_hello.ts")
|
||||
AssertEqual(t, filename,
|
||||
path.Join(SrcDir, "localhost:4545/testdata/subdir/print_hello.ts"))
|
||||
}
|
||||
|
||||
func TestResolveModule2(t *testing.T) {
|
||||
createDirs()
|
||||
moduleName, filename, err := ResolveModule(
|
||||
"./subdir/print_hello.ts",
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/006_url_imports.ts")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
AssertEqual(t, moduleName,
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/subdir/print_hello.ts")
|
||||
AssertEqual(t, filename,
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/subdir/print_hello.ts")
|
||||
}
|
||||
|
||||
func TestResolveModule3(t *testing.T) {
|
||||
createDirs()
|
||||
// In the case where the containingFile is a directory (indicated with a
|
||||
// trailing slash)
|
||||
moduleName, filename, err := ResolveModule(
|
||||
"testdata/001_hello.js",
|
||||
"/Users/rld/go/src/github.com/ry/deno/")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
AssertEqual(t, moduleName,
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/001_hello.js")
|
||||
AssertEqual(t, filename,
|
||||
"/Users/rld/go/src/github.com/ry/deno/testdata/001_hello.js")
|
||||
}
|
||||
|
||||
func TestResolveModule4(t *testing.T) {
|
||||
createDirs()
|
||||
// Files in SrcDir should resolve to URLs.
|
||||
moduleName, filename, err := ResolveModule(
|
||||
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"),
|
||||
".")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
AssertEqual(t, moduleName,
|
||||
"http://unpkg.com/liltest@0.0.5/index.ts")
|
||||
AssertEqual(t, filename,
|
||||
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"))
|
||||
}
|
||||
|
||||
func TestResolveModuleExtensionsAintSpecial(t *testing.T) {
|
||||
createDirs()
|
||||
moduleName, filename, err := ResolveModule(
|
||||
"./util",
|
||||
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"))
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
AssertEqual(t, moduleName,
|
||||
"http://unpkg.com/liltest@0.0.5/util")
|
||||
AssertEqual(t, filename,
|
||||
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/util"))
|
||||
}
|
25
package.json
25
package.json
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"name": "deno",
|
||||
"scripts": {
|
||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||
"fmt": "prettier --write *.ts* *.js *.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/base64-js": "^1.2.5",
|
||||
"@types/source-map-support": "^0.4.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"espree": "^3.5.3",
|
||||
"jsdoc": "^3.5.5",
|
||||
"parcel-bundler": "^1.8.1",
|
||||
"prettier": "^1.12.1",
|
||||
"protobufjs": "^6.8.6",
|
||||
"source-map": "0.6.0",
|
||||
"text-encoding": "^0.6.4",
|
||||
"tmp": "0.0.33",
|
||||
"tslint": "5.10.0",
|
||||
"typescript": "^2.8.3",
|
||||
"uglify-js": "^2.8.29"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
339
runtime.ts
339
runtime.ts
|
@ -1,339 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
};
|
1
testdata/001_hello.js
vendored
1
testdata/001_hello.js
vendored
|
@ -1 +0,0 @@
|
|||
console.log("Hello World");
|
1
testdata/001_hello.js.out
vendored
1
testdata/001_hello.js.out
vendored
|
@ -1 +0,0 @@
|
|||
Hello World
|
1
testdata/002_hello.ts
vendored
1
testdata/002_hello.ts
vendored
|
@ -1 +0,0 @@
|
|||
console.log("Hello World");
|
1
testdata/002_hello.ts.out
vendored
1
testdata/002_hello.ts.out
vendored
|
@ -1 +0,0 @@
|
|||
Hello World
|
3
testdata/003_relative_import.ts
vendored
3
testdata/003_relative_import.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
import { printHello } from "./subdir/print_hello.ts";
|
||||
|
||||
printHello();
|
1
testdata/003_relative_import.ts.out
vendored
1
testdata/003_relative_import.ts.out
vendored
|
@ -1 +0,0 @@
|
|||
Hello
|
11
testdata/004_set_timeout.ts
vendored
11
testdata/004_set_timeout.ts
vendored
|
@ -1,11 +0,0 @@
|
|||
setTimeout(() => {
|
||||
console.log("World");
|
||||
}, 10);
|
||||
|
||||
console.log("Hello");
|
||||
|
||||
const id = setTimeout(() => {
|
||||
console.log("Not printed");
|
||||
}, 10000);
|
||||
|
||||
clearTimeout(id);
|
2
testdata/004_set_timeout.ts.out
vendored
2
testdata/004_set_timeout.ts.out
vendored
|
@ -1,2 +0,0 @@
|
|||
Hello
|
||||
World
|
11
testdata/005_more_imports.ts
vendored
11
testdata/005_more_imports.ts
vendored
|
@ -1,11 +0,0 @@
|
|||
import { returnsHi, returnsFoo2, printHello3 } from "./subdir/mod1.ts";
|
||||
|
||||
printHello3();
|
||||
|
||||
if (returnsHi() !== "Hi") {
|
||||
throw Error("Unexpected");
|
||||
}
|
||||
|
||||
if (returnsFoo2() !== "Foo") {
|
||||
throw Error("Unexpected");
|
||||
}
|
1
testdata/005_more_imports.ts.out
vendored
1
testdata/005_more_imports.ts.out
vendored
|
@ -1 +0,0 @@
|
|||
Hello
|
3
testdata/006_url_imports.ts
vendored
3
testdata/006_url_imports.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
import { printHello } from "http://localhost:4545/testdata/subdir/print_hello.ts";
|
||||
printHello();
|
||||
console.log("success");
|
3
testdata/006_url_imports.ts.out
vendored
3
testdata/006_url_imports.ts.out
vendored
|
@ -1,3 +0,0 @@
|
|||
Downloading http://localhost:4545/testdata/subdir/print_hello.ts
|
||||
Hello
|
||||
success
|
7
testdata/008_stack_trace.ts
vendored
7
testdata/008_stack_trace.ts
vendored
|
@ -1,7 +0,0 @@
|
|||
import { throwsError } from "./subdir/mod1.ts";
|
||||
|
||||
function foo() {
|
||||
throwsError();
|
||||
}
|
||||
|
||||
foo();
|
18
testdata/009_pub_sub.ts
vendored
18
testdata/009_pub_sub.ts
vendored
|
@ -1,18 +0,0 @@
|
|||
import * as deno from "deno";
|
||||
|
||||
deno.sub("echo", (ui8: Uint8Array) => {
|
||||
const str = String.fromCharCode.apply(null, ui8);
|
||||
console.log("Got message", str);
|
||||
});
|
||||
|
||||
function str2ui8(str: string): Uint8Array {
|
||||
const ui8 = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
ui8[i] = str.charCodeAt(i);
|
||||
}
|
||||
return ui8;
|
||||
}
|
||||
|
||||
console.log("Before deno.pub()");
|
||||
deno.pub("echo", str2ui8("hello"));
|
||||
console.log("After deno.pub()");
|
3
testdata/009_pub_sub.ts.out
vendored
3
testdata/009_pub_sub.ts.out
vendored
|
@ -1,3 +0,0 @@
|
|||
Before deno.pub()
|
||||
After deno.pub()
|
||||
Got message hello
|
7
testdata/010_set_interval.ts
vendored
7
testdata/010_set_interval.ts
vendored
|
@ -1,7 +0,0 @@
|
|||
const id = setInterval(function() {
|
||||
console.log("test")
|
||||
}, 200);
|
||||
|
||||
setTimeout(function() {
|
||||
clearInterval(id)
|
||||
}, 500)
|
2
testdata/010_set_interval.ts.out
vendored
2
testdata/010_set_interval.ts.out
vendored
|
@ -1,2 +0,0 @@
|
|||
test
|
||||
test
|
11
testdata/012_async.ts
vendored
11
testdata/012_async.ts
vendored
|
@ -1,11 +0,0 @@
|
|||
// Check that we can use the async keyword.
|
||||
async function main() {
|
||||
await new Promise((resolve, reject) => {
|
||||
console.log("2");
|
||||
setTimeout(resolve, 100);
|
||||
});
|
||||
console.log("3");
|
||||
}
|
||||
|
||||
console.log("1");
|
||||
main();
|
3
testdata/012_async.ts.out
vendored
3
testdata/012_async.ts.out
vendored
|
@ -1,3 +0,0 @@
|
|||
1
|
||||
2
|
||||
3
|
9
testdata/async_error.ts
vendored
9
testdata/async_error.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
|
||||
console.log("hello");
|
||||
const foo = async () => {
|
||||
console.log("before error");
|
||||
throw Error("error");
|
||||
}
|
||||
|
||||
foo();
|
||||
console.log("world");
|
10
testdata/async_error.ts.out
vendored
10
testdata/async_error.ts.out
vendored
|
@ -1,10 +0,0 @@
|
|||
hello
|
||||
before error
|
||||
error Error: error
|
||||
at foo ([WILDCARD]testdata/async_error.ts:4:11)
|
||||
at eval ([WILDCARD]testdata/async_error.ts:6:1)
|
||||
at Object.eval [as globalEval] (<anonymous>)
|
||||
at execute (/main.js:[WILDCARD])
|
||||
at FileModule.compileAndRun (/main.js:[WILDCARD])
|
||||
at /main.js:[WILDCARD]
|
||||
at /main.js:[WILDCARD]
|
9
testdata/error.ts
vendored
9
testdata/error.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
function foo() {
|
||||
throw Error("bad");
|
||||
}
|
||||
|
||||
function bar() {
|
||||
foo()
|
||||
}
|
||||
|
||||
bar()
|
10
testdata/error.ts.out
vendored
10
testdata/error.ts.out
vendored
|
@ -1,10 +0,0 @@
|
|||
/main.js:[WILDCARD]
|
||||
throw _iteratorError;
|
||||
^
|
||||
Error: bad
|
||||
at foo ([WILDCARD]testdata/error.ts:2:9)
|
||||
at bar ([WILDCARD]testdata/error.ts:6:3)
|
||||
at eval ([WILDCARD]testdata/error.ts:9:1)
|
||||
at Object.eval [as globalEval] (<anonymous>)
|
||||
at execute (../runtime.ts:[WILDCARD])
|
||||
at FileModule.compileAndRun (../runtime.ts:[WILDCARD]
|
3
testdata/import_typescript.ts
vendored
3
testdata/import_typescript.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
import * as ts from "typescript";
|
||||
|
||||
console.log("typescript version", ts.version);
|
1
testdata/import_typescript.ts.out
vendored
1
testdata/import_typescript.ts.out
vendored
|
@ -1 +0,0 @@
|
|||
typescript version 2.8.3
|
17
testdata/subdir/mod1.ts
vendored
17
testdata/subdir/mod1.ts
vendored
|
@ -1,17 +0,0 @@
|
|||
import { returnsFoo, printHello2 } from "./subdir2/mod2.ts";
|
||||
|
||||
export function returnsHi(): string {
|
||||
return "Hi";
|
||||
}
|
||||
|
||||
export function returnsFoo2(): string {
|
||||
return returnsFoo();
|
||||
}
|
||||
|
||||
export function printHello3(): void {
|
||||
printHello2();
|
||||
}
|
||||
|
||||
export function throwsError(): void {
|
||||
throw Error("exception from mod1");
|
||||
}
|
3
testdata/subdir/print_hello.ts
vendored
3
testdata/subdir/print_hello.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
export function printHello(): void {
|
||||
console.log("Hello");
|
||||
}
|
9
testdata/subdir/subdir2/mod2.ts
vendored
9
testdata/subdir/subdir2/mod2.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
import { printHello } from "../print_hello.ts";
|
||||
|
||||
export function returnsFoo(): string {
|
||||
return "Foo";
|
||||
}
|
||||
|
||||
export function printHello2(): void {
|
||||
printHello();
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*!
|
||||
Copyright 2018 Propel http://propel.site/. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
export { assert, assertEqual, equal } from "./util.ts";
|
||||
|
||||
export type TestFunction = () => void | Promise<void>;
|
||||
|
||||
export interface TestDefinition {
|
||||
fn: TestFunction;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const exitOnFail = true;
|
||||
|
||||
/* A subset of the tests can be ran by providing a filter expression.
|
||||
* In Node.js the filter is specified on the command line:
|
||||
*
|
||||
* ts-node test_node log # all tests with 'log' in the name
|
||||
* ts-node test_node ^util # tests starting with 'util'
|
||||
*
|
||||
* In the browser, the filter is specified as part of the url:
|
||||
*
|
||||
* http://localhost:9876/test.html#script=some/script.js&filter=log
|
||||
* http://localhost:9876/test.html#script=some/script.js&filter=^util
|
||||
*/
|
||||
let filterExpr: string = null;
|
||||
|
||||
const filterRegExp = filterExpr ? new RegExp(filterExpr, "i") : null;
|
||||
const tests: TestDefinition[] = [];
|
||||
|
||||
export function test(t: TestDefinition | TestFunction): void {
|
||||
const fn: TestFunction = typeof t === "function" ? t : t.fn;
|
||||
const name: string = t.name;
|
||||
|
||||
if (!name) {
|
||||
throw new Error("Test function may not be anonymous");
|
||||
}
|
||||
if (filter(name)) {
|
||||
tests.push({ fn, name });
|
||||
}
|
||||
}
|
||||
|
||||
function filter(name: string): boolean {
|
||||
if (filterRegExp) {
|
||||
return filterRegExp.test(name);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const { fn, name } = tests[i];
|
||||
console.log(`${i+1}/${tests.length} +${passed} -${failed}: ${name}`);
|
||||
try {
|
||||
await fn();
|
||||
passed++;
|
||||
} catch (e) {
|
||||
console.error("\nTest FAIL", name);
|
||||
console.error((e && e.stack) || e);
|
||||
failed++;
|
||||
if (exitOnFail) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nDONE. Test passed: ${passed}, failed: ${failed}`);
|
||||
|
||||
if (failed === 0) {
|
||||
// All good.
|
||||
} else {
|
||||
// Use setTimeout to avoid the error being ignored due to unhandled
|
||||
// promise rejections being swallowed.
|
||||
setTimeout(() => {
|
||||
throw new Error(`There were ${failed} test failures.`);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(runTests, 0);
|
|
@ -1,60 +0,0 @@
|
|||
/*!
|
||||
Copyright 2018 Propel http://propel.site/. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
export function assertEqual(actual: any, expected: any, msg?: string) {
|
||||
if (!msg) { msg = `actual: ${actual} expected: ${expected}`; }
|
||||
if (!equal(actual, expected)) {
|
||||
console.error(
|
||||
"assertEqual failed. actual = ", actual, "expected =", expected);
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assert(expr: boolean, msg = "") {
|
||||
if (!expr) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
export function equal(c: any, d: any): boolean {
|
||||
const seen = new Map();
|
||||
return (function compare(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (typeof a === "number" && typeof b === "number" &&
|
||||
isNaN(a) && isNaN(b)) {
|
||||
return true;
|
||||
}
|
||||
if (a && typeof a === "object" && b && typeof b === "object") {
|
||||
if (seen.get(a) === b) {
|
||||
return true;
|
||||
}
|
||||
if (Object.keys(a).length !== Object.keys(b).length) {
|
||||
return false;
|
||||
}
|
||||
for (const key in { ...a, ...b }) {
|
||||
if (!compare(a[key], b[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
seen.set(a, b);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})(c, d);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*!
|
||||
Copyright 2018 Propel http://propel.site/. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { test } from "./testing.ts";
|
||||
import { assert } from "./util.ts";
|
||||
import * as util from "./util.ts";
|
||||
|
||||
test(async function util_equal() {
|
||||
assert(util.equal("world", "world"));
|
||||
assert(!util.equal("hello", "world"));
|
||||
assert(util.equal(5, 5));
|
||||
assert(!util.equal(5, 6));
|
||||
assert(util.equal(NaN, NaN));
|
||||
assert(util.equal({ hello: "world" }, { hello: "world" }));
|
||||
assert(!util.equal({ world: "hello" }, { hello: "world" }));
|
||||
assert(util.equal({ hello: "world", hi: { there: "everyone" } },
|
||||
{ hello: "world", hi: { there: "everyone" } }));
|
||||
assert(!util.equal({ hello: "world", hi: { there: "everyone" } },
|
||||
{ hello: "world", hi: { there: "everyone else" } }));
|
||||
});
|
126
tests.ts
126
tests.ts
|
@ -1,126 +0,0 @@
|
|||
// 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
text-encoding.d.ts
vendored
6
text-encoding.d.ts
vendored
|
@ -1,6 +0,0 @@
|
|||
// 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;
|
||||
}
|
79
timers.go
79
timers.go
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Timer struct {
|
||||
Id int32
|
||||
Done bool
|
||||
Cleared bool
|
||||
Interval bool
|
||||
Delay int32 // In milliseconds
|
||||
}
|
||||
|
||||
var timers = make(map[int32]*Timer)
|
||||
|
||||
func InitTimers() {
|
||||
Sub("timers", func(buf []byte) []byte {
|
||||
msg := &Msg{}
|
||||
check(proto.Unmarshal(buf, msg))
|
||||
switch msg.Command {
|
||||
case Msg_TIMER_START:
|
||||
id := msg.TimerStartId
|
||||
t := &Timer{
|
||||
Id: id,
|
||||
Done: false,
|
||||
Interval: msg.TimerStartInterval,
|
||||
Delay: msg.TimerStartDelay,
|
||||
Cleared: false,
|
||||
}
|
||||
if t.Delay < 10 {
|
||||
t.Delay = 10
|
||||
}
|
||||
t.StartTimer()
|
||||
timers[id] = t
|
||||
return nil
|
||||
case Msg_TIMER_CLEAR:
|
||||
// TODO maybe need mutex here.
|
||||
timer := timers[msg.TimerClearId]
|
||||
timer.Clear()
|
||||
default:
|
||||
panic("[timers] Unexpected message " + string(buf))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Timer) Clear() {
|
||||
if !t.Cleared {
|
||||
wg.Done()
|
||||
t.Cleared = true
|
||||
delete(timers, t.Id)
|
||||
}
|
||||
t.Done = true
|
||||
}
|
||||
|
||||
func (t *Timer) StartTimer() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer t.Clear()
|
||||
for {
|
||||
time.Sleep(time.Duration(t.Delay) * time.Millisecond)
|
||||
if !t.Interval {
|
||||
t.Done = true
|
||||
}
|
||||
PubMsg("timers", &Msg{
|
||||
Command: Msg_TIMER_READY,
|
||||
TimerReadyId: t.Id,
|
||||
TimerReadyDone: t.Done,
|
||||
})
|
||||
if t.Done {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
89
timers.ts
89
timers.ts
|
@ -1,89 +0,0 @@
|
|||
// 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
|
||||
});
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"module": "commonjs",
|
||||
"noImplicitAny": true,
|
||||
"baseUrl": ".",
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"declaration": true,
|
||||
"target": "es2017",
|
||||
"lib": ["es2017", "dom"],
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"noUnusedParameters": false,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowUnreachableCode": false,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": ["*.ts", "*.js"],
|
||||
"exclude": ["tests.ts"]
|
||||
}
|
67
tslint.json
67
tslint.json
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"rules": {
|
||||
"array-type": [true, "array-simple"],
|
||||
"arrow-return-shorthand": true,
|
||||
"ban": [
|
||||
true,
|
||||
["fit"],
|
||||
["fdescribe"],
|
||||
["xit"],
|
||||
["xdescribe"],
|
||||
["fitAsync"],
|
||||
["xitAsync"],
|
||||
["fitFakeAsync"],
|
||||
["xitFakeAsync"]
|
||||
],
|
||||
"ban-types": [
|
||||
true,
|
||||
["Object", "Use {} instead."],
|
||||
["String", "Use 'string' instead."],
|
||||
["Number", "Use 'number' instead."],
|
||||
["Boolean", "Use 'boolean' instead."]
|
||||
],
|
||||
"class-name": true,
|
||||
"curly": true,
|
||||
"interface-name": [true, "never-prefix"],
|
||||
"jsdoc-format": true,
|
||||
"forin": false,
|
||||
"label-position": true,
|
||||
"max-line-length": [true, 80],
|
||||
"new-parens": true,
|
||||
"no-angle-bracket-type-assertion": true,
|
||||
"no-any": true,
|
||||
"no-construct": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-debugger": true,
|
||||
"no-default-export": true,
|
||||
"no-inferrable-types": true,
|
||||
//"no-namespace": [true, "allow-declarations"],
|
||||
"no-reference": true,
|
||||
"no-require-imports": true,
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-unused-variable": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-shorthand": true,
|
||||
"only-arrow-functions": [
|
||||
true,
|
||||
"allow-declarations",
|
||||
"allow-named-functions"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [true, "double"],
|
||||
"radix": true,
|
||||
"restrict-plus-operands": true,
|
||||
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||
"switch-default": true,
|
||||
"triple-equals": [true, "allow-null-check"],
|
||||
"use-isnan": true,
|
||||
"variable-name": [
|
||||
true,
|
||||
"check-format",
|
||||
"ban-keywords",
|
||||
"allow-leading-underscore",
|
||||
"allow-trailing-underscore"
|
||||
]
|
||||
}
|
||||
}
|
10
types.ts
10
types.ts
|
@ -1,10 +0,0 @@
|
|||
// 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;
|
||||
}
|
671
url.js
671
url.js
|
@ -1,671 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* Forked from https://github.com/github/url-polyfill
|
||||
* Version 16c1aa Feb 9 2018.
|
||||
*/
|
||||
|
||||
(function(scope) {
|
||||
"use strict";
|
||||
|
||||
// feature detect for URL constructor
|
||||
var hasWorkingUrl = false;
|
||||
if (!scope.forceJURL) {
|
||||
try {
|
||||
var u = new URL("b", "http://a");
|
||||
u.pathname = "c%20d";
|
||||
hasWorkingUrl = u.href === "http://a/c%20d";
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (hasWorkingUrl) return;
|
||||
|
||||
var relative = Object.create(null);
|
||||
relative["ftp"] = 21;
|
||||
relative["file"] = 0;
|
||||
relative["gopher"] = 70;
|
||||
relative["http"] = 80;
|
||||
relative["https"] = 443;
|
||||
relative["ws"] = 80;
|
||||
relative["wss"] = 443;
|
||||
|
||||
var relativePathDotMapping = Object.create(null);
|
||||
relativePathDotMapping["%2e"] = ".";
|
||||
relativePathDotMapping[".%2e"] = "..";
|
||||
relativePathDotMapping["%2e."] = "..";
|
||||
relativePathDotMapping["%2e%2e"] = "..";
|
||||
|
||||
function isRelativeScheme(scheme) {
|
||||
return relative[scheme] !== undefined;
|
||||
}
|
||||
|
||||
function invalid() {
|
||||
clear.call(this);
|
||||
this._isInvalid = true;
|
||||
}
|
||||
|
||||
function IDNAToASCII(h) {
|
||||
if ("" == h) {
|
||||
invalid.call(this);
|
||||
}
|
||||
// XXX
|
||||
return h.toLowerCase();
|
||||
}
|
||||
|
||||
function percentEscape(c) {
|
||||
var unicode = c.charCodeAt(0);
|
||||
if (
|
||||
unicode > 0x20 &&
|
||||
unicode < 0x7f &&
|
||||
// " # < > ? `
|
||||
[0x22, 0x23, 0x3c, 0x3e, 0x3f, 0x60].indexOf(unicode) == -1
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
return encodeURIComponent(c);
|
||||
}
|
||||
|
||||
function percentEscapeQuery(c) {
|
||||
// XXX This actually needs to encode c using encoding and then
|
||||
// convert the bytes one-by-one.
|
||||
|
||||
var unicode = c.charCodeAt(0);
|
||||
if (
|
||||
unicode > 0x20 &&
|
||||
unicode < 0x7f &&
|
||||
// " # < > ` (do not escape '?')
|
||||
[0x22, 0x23, 0x3c, 0x3e, 0x60].indexOf(unicode) == -1
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
return encodeURIComponent(c);
|
||||
}
|
||||
|
||||
var EOF = undefined,
|
||||
ALPHA = /[a-zA-Z]/,
|
||||
ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
|
||||
|
||||
function parse(input, stateOverride, base) {
|
||||
function err(message) {
|
||||
errors.push(message);
|
||||
}
|
||||
|
||||
var state = stateOverride || "scheme start",
|
||||
cursor = 0,
|
||||
buffer = "",
|
||||
seenAt = false,
|
||||
seenBracket = false,
|
||||
errors = [];
|
||||
|
||||
loop: while (
|
||||
(input[cursor - 1] != EOF || cursor == 0) &&
|
||||
!this._isInvalid
|
||||
) {
|
||||
var c = input[cursor];
|
||||
switch (state) {
|
||||
case "scheme start":
|
||||
if (c && ALPHA.test(c)) {
|
||||
buffer += c.toLowerCase(); // ASCII-safe
|
||||
state = "scheme";
|
||||
} else if (!stateOverride) {
|
||||
buffer = "";
|
||||
state = "no scheme";
|
||||
continue;
|
||||
} else {
|
||||
err("Invalid scheme.");
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
|
||||
case "scheme":
|
||||
if (c && ALPHANUMERIC.test(c)) {
|
||||
buffer += c.toLowerCase(); // ASCII-safe
|
||||
} else if (":" == c) {
|
||||
this._scheme = buffer;
|
||||
buffer = "";
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
if (isRelativeScheme(this._scheme)) {
|
||||
this._isRelative = true;
|
||||
}
|
||||
if ("file" == this._scheme) {
|
||||
state = "relative";
|
||||
} else if (
|
||||
this._isRelative &&
|
||||
base &&
|
||||
base._scheme == this._scheme
|
||||
) {
|
||||
state = "relative or authority";
|
||||
} else if (this._isRelative) {
|
||||
state = "authority first slash";
|
||||
} else {
|
||||
state = "scheme data";
|
||||
}
|
||||
} else if (!stateOverride) {
|
||||
buffer = "";
|
||||
cursor = 0;
|
||||
state = "no scheme";
|
||||
continue;
|
||||
} else if (EOF == c) {
|
||||
break loop;
|
||||
} else {
|
||||
err("Code point not allowed in scheme: " + c);
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
|
||||
case "scheme data":
|
||||
if ("?" == c) {
|
||||
query = "?";
|
||||
state = "query";
|
||||
} else if ("#" == c) {
|
||||
this._fragment = "#";
|
||||
state = "fragment";
|
||||
} else {
|
||||
// XXX error handling
|
||||
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
|
||||
this._schemeData += percentEscape(c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "no scheme":
|
||||
if (!base || !isRelativeScheme(base._scheme)) {
|
||||
err("Missing scheme.");
|
||||
invalid.call(this);
|
||||
} else {
|
||||
state = "relative";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "relative or authority":
|
||||
if ("/" == c && "/" == input[cursor + 1]) {
|
||||
state = "authority ignore slashes";
|
||||
} else {
|
||||
err("Expected /, got: " + c);
|
||||
state = "relative";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "relative":
|
||||
this._isRelative = true;
|
||||
if ("file" != this._scheme) this._scheme = base._scheme;
|
||||
if (EOF == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = base._query;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
break loop;
|
||||
} else if ("/" == c || "\\" == c) {
|
||||
if ("\\" == c) err("\\ is an invalid code point.");
|
||||
state = "relative slash";
|
||||
} else if ("?" == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = "?";
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
state = "query";
|
||||
} else if ("#" == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = base._query;
|
||||
this._fragment = "#";
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
state = "fragment";
|
||||
} else {
|
||||
var nextC = input[cursor + 1];
|
||||
var nextNextC = input[cursor + 2];
|
||||
if (
|
||||
"file" != this._scheme ||
|
||||
!ALPHA.test(c) ||
|
||||
(nextC != ":" && nextC != "|") ||
|
||||
(EOF != nextNextC &&
|
||||
"/" != nextNextC &&
|
||||
"\\" != nextNextC &&
|
||||
"?" != nextNextC &&
|
||||
"#" != nextNextC)
|
||||
) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
this._path = base._path.slice();
|
||||
this._path.pop();
|
||||
}
|
||||
state = "relative path";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "relative slash":
|
||||
if ("/" == c || "\\" == c) {
|
||||
if ("\\" == c) {
|
||||
err("\\ is an invalid code point.");
|
||||
}
|
||||
if ("file" == this._scheme) {
|
||||
state = "file host";
|
||||
} else {
|
||||
state = "authority ignore slashes";
|
||||
}
|
||||
} else {
|
||||
if ("file" != this._scheme) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
}
|
||||
state = "relative path";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "authority first slash":
|
||||
if ("/" == c) {
|
||||
state = "authority second slash";
|
||||
} else {
|
||||
err("Expected '/', got: " + c);
|
||||
state = "authority ignore slashes";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "authority second slash":
|
||||
state = "authority ignore slashes";
|
||||
if ("/" != c) {
|
||||
err("Expected '/', got: " + c);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "authority ignore slashes":
|
||||
if ("/" != c && "\\" != c) {
|
||||
state = "authority";
|
||||
continue;
|
||||
} else {
|
||||
err("Expected authority, got: " + c);
|
||||
}
|
||||
break;
|
||||
|
||||
case "authority":
|
||||
if ("@" == c) {
|
||||
if (seenAt) {
|
||||
err("@ already seen.");
|
||||
buffer += "%40";
|
||||
}
|
||||
seenAt = true;
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
var cp = buffer[i];
|
||||
if ("\t" == cp || "\n" == cp || "\r" == cp) {
|
||||
err("Invalid whitespace in authority.");
|
||||
continue;
|
||||
}
|
||||
// XXX check URL code points
|
||||
if (":" == cp && null === this._password) {
|
||||
this._password = "";
|
||||
continue;
|
||||
}
|
||||
var tempC = percentEscape(cp);
|
||||
null !== this._password
|
||||
? (this._password += tempC)
|
||||
: (this._username += tempC);
|
||||
}
|
||||
buffer = "";
|
||||
} else if (
|
||||
EOF == c ||
|
||||
"/" == c ||
|
||||
"\\" == c ||
|
||||
"?" == c ||
|
||||
"#" == c
|
||||
) {
|
||||
cursor -= buffer.length;
|
||||
buffer = "";
|
||||
state = "host";
|
||||
continue;
|
||||
} else {
|
||||
buffer += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case "file host":
|
||||
if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
|
||||
if (
|
||||
buffer.length == 2 &&
|
||||
ALPHA.test(buffer[0]) &&
|
||||
(buffer[1] == ":" || buffer[1] == "|")
|
||||
) {
|
||||
state = "relative path";
|
||||
} else if (buffer.length == 0) {
|
||||
state = "relative path start";
|
||||
} else {
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = "";
|
||||
state = "relative path start";
|
||||
}
|
||||
continue;
|
||||
} else if ("\t" == c || "\n" == c || "\r" == c) {
|
||||
err("Invalid whitespace in file host.");
|
||||
} else {
|
||||
buffer += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case "host":
|
||||
case "hostname":
|
||||
if (":" == c && !seenBracket) {
|
||||
// XXX host parsing
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = "";
|
||||
state = "port";
|
||||
if ("hostname" == stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
} else if (
|
||||
EOF == c ||
|
||||
"/" == c ||
|
||||
"\\" == c ||
|
||||
"?" == c ||
|
||||
"#" == c
|
||||
) {
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = "";
|
||||
state = "relative path start";
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
continue;
|
||||
} else if ("\t" != c && "\n" != c && "\r" != c) {
|
||||
if ("[" == c) {
|
||||
seenBracket = true;
|
||||
} else if ("]" == c) {
|
||||
seenBracket = false;
|
||||
}
|
||||
buffer += c;
|
||||
} else {
|
||||
err("Invalid code point in host/hostname: " + c);
|
||||
}
|
||||
break;
|
||||
|
||||
case "port":
|
||||
if (/[0-9]/.test(c)) {
|
||||
buffer += c;
|
||||
} else if (
|
||||
EOF == c ||
|
||||
"/" == c ||
|
||||
"\\" == c ||
|
||||
"?" == c ||
|
||||
"#" == c ||
|
||||
stateOverride
|
||||
) {
|
||||
if ("" != buffer) {
|
||||
var temp = parseInt(buffer, 10);
|
||||
if (temp != relative[this._scheme]) {
|
||||
this._port = temp + "";
|
||||
}
|
||||
buffer = "";
|
||||
}
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
state = "relative path start";
|
||||
continue;
|
||||
} else if ("\t" == c || "\n" == c || "\r" == c) {
|
||||
err("Invalid code point in port: " + c);
|
||||
} else {
|
||||
invalid.call(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case "relative path start":
|
||||
if ("\\" == c) err("'\\' not allowed in path.");
|
||||
state = "relative path";
|
||||
if ("/" != c && "\\" != c) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case "relative path":
|
||||
if (
|
||||
EOF == c ||
|
||||
"/" == c ||
|
||||
"\\" == c ||
|
||||
(!stateOverride && ("?" == c || "#" == c))
|
||||
) {
|
||||
if ("\\" == c) {
|
||||
err("\\ not allowed in relative path.");
|
||||
}
|
||||
var tmp;
|
||||
if ((tmp = relativePathDotMapping[buffer.toLowerCase()])) {
|
||||
buffer = tmp;
|
||||
}
|
||||
if (".." == buffer) {
|
||||
this._path.pop();
|
||||
if ("/" != c && "\\" != c) {
|
||||
this._path.push("");
|
||||
}
|
||||
} else if ("." == buffer && "/" != c && "\\" != c) {
|
||||
this._path.push("");
|
||||
} else if ("." != buffer) {
|
||||
if (
|
||||
"file" == this._scheme &&
|
||||
this._path.length == 0 &&
|
||||
buffer.length == 2 &&
|
||||
ALPHA.test(buffer[0]) &&
|
||||
buffer[1] == "|"
|
||||
) {
|
||||
buffer = buffer[0] + ":";
|
||||
}
|
||||
this._path.push(buffer);
|
||||
}
|
||||
buffer = "";
|
||||
if ("?" == c) {
|
||||
this._query = "?";
|
||||
state = "query";
|
||||
} else if ("#" == c) {
|
||||
this._fragment = "#";
|
||||
state = "fragment";
|
||||
}
|
||||
} else if ("\t" != c && "\n" != c && "\r" != c) {
|
||||
buffer += percentEscape(c);
|
||||
}
|
||||
break;
|
||||
|
||||
case "query":
|
||||
if (!stateOverride && "#" == c) {
|
||||
this._fragment = "#";
|
||||
state = "fragment";
|
||||
} else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
|
||||
this._query += percentEscapeQuery(c);
|
||||
}
|
||||
break;
|
||||
|
||||
case "fragment":
|
||||
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
|
||||
this._fragment += c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
this._scheme = "";
|
||||
this._schemeData = "";
|
||||
this._username = "";
|
||||
this._password = null;
|
||||
this._host = "";
|
||||
this._port = "";
|
||||
this._path = [];
|
||||
this._query = "";
|
||||
this._fragment = "";
|
||||
this._isInvalid = false;
|
||||
this._isRelative = false;
|
||||
}
|
||||
|
||||
// Does not process domain names or IP addresses.
|
||||
// Does not handle encoding for the query parameter.
|
||||
function jURL(url, base /* , encoding */) {
|
||||
if (base !== undefined && !(base instanceof jURL))
|
||||
base = new jURL(String(base));
|
||||
|
||||
url = String(url);
|
||||
|
||||
this._url = url;
|
||||
clear.call(this);
|
||||
|
||||
var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
|
||||
// encoding = encoding || 'utf-8'
|
||||
|
||||
parse.call(this, input, null, base);
|
||||
}
|
||||
|
||||
jURL.prototype = {
|
||||
toString: function() {
|
||||
return this.href;
|
||||
},
|
||||
get href() {
|
||||
if (this._isInvalid) return this._url;
|
||||
|
||||
var authority = "";
|
||||
if ("" != this._username || null != this._password) {
|
||||
authority =
|
||||
this._username +
|
||||
(null != this._password ? ":" + this._password : "") +
|
||||
"@";
|
||||
}
|
||||
|
||||
return (
|
||||
this.protocol +
|
||||
(this._isRelative ? "//" + authority + this.host : "") +
|
||||
this.pathname +
|
||||
this._query +
|
||||
this._fragment
|
||||
);
|
||||
},
|
||||
set href(href) {
|
||||
clear.call(this);
|
||||
parse.call(this, href);
|
||||
},
|
||||
|
||||
get protocol() {
|
||||
return this._scheme + ":";
|
||||
},
|
||||
set protocol(protocol) {
|
||||
if (this._isInvalid) return;
|
||||
parse.call(this, protocol + ":", "scheme start");
|
||||
},
|
||||
|
||||
get host() {
|
||||
return this._isInvalid
|
||||
? ""
|
||||
: this._port
|
||||
? this._host + ":" + this._port
|
||||
: this._host;
|
||||
},
|
||||
set host(host) {
|
||||
if (this._isInvalid || !this._isRelative) return;
|
||||
parse.call(this, host, "host");
|
||||
},
|
||||
|
||||
get hostname() {
|
||||
return this._host;
|
||||
},
|
||||
set hostname(hostname) {
|
||||
if (this._isInvalid || !this._isRelative) return;
|
||||
parse.call(this, hostname, "hostname");
|
||||
},
|
||||
|
||||
get port() {
|
||||
return this._port;
|
||||
},
|
||||
set port(port) {
|
||||
if (this._isInvalid || !this._isRelative) return;
|
||||
parse.call(this, port, "port");
|
||||
},
|
||||
|
||||
get pathname() {
|
||||
return this._isInvalid
|
||||
? ""
|
||||
: this._isRelative
|
||||
? "/" + this._path.join("/")
|
||||
: this._schemeData;
|
||||
},
|
||||
set pathname(pathname) {
|
||||
if (this._isInvalid || !this._isRelative) return;
|
||||
this._path = [];
|
||||
parse.call(this, pathname, "relative path start");
|
||||
},
|
||||
|
||||
get search() {
|
||||
return this._isInvalid || !this._query || "?" == this._query
|
||||
? ""
|
||||
: this._query;
|
||||
},
|
||||
set search(search) {
|
||||
if (this._isInvalid || !this._isRelative) return;
|
||||
this._query = "?";
|
||||
if ("?" == search[0]) search = search.slice(1);
|
||||
parse.call(this, search, "query");
|
||||
},
|
||||
|
||||
get hash() {
|
||||
return this._isInvalid || !this._fragment || "#" == this._fragment
|
||||
? ""
|
||||
: this._fragment;
|
||||
},
|
||||
set hash(hash) {
|
||||
if (this._isInvalid) return;
|
||||
this._fragment = "#";
|
||||
if ("#" == hash[0]) hash = hash.slice(1);
|
||||
parse.call(this, hash, "fragment");
|
||||
},
|
||||
|
||||
get origin() {
|
||||
var host;
|
||||
if (this._isInvalid || !this._scheme) {
|
||||
return "";
|
||||
}
|
||||
// javascript: Gecko returns String(""), WebKit/Blink String("null")
|
||||
// Gecko throws error for "data://"
|
||||
// data: Gecko returns "", Blink returns "data://", WebKit returns "null"
|
||||
// Gecko returns String("") for file: mailto:
|
||||
// WebKit/Blink returns String("SCHEME://") for file: mailto:
|
||||
switch (this._scheme) {
|
||||
case "data":
|
||||
case "file":
|
||||
case "javascript":
|
||||
case "mailto":
|
||||
return "null";
|
||||
}
|
||||
host = this.host;
|
||||
if (!host) {
|
||||
return "";
|
||||
}
|
||||
return this._scheme + "://" + host;
|
||||
}
|
||||
};
|
||||
|
||||
// Copy over the static methods
|
||||
var OriginalURL = scope.URL;
|
||||
if (OriginalURL) {
|
||||
jURL.createObjectURL = function(blob) {
|
||||
// IE extension allows a second optional options argument.
|
||||
// http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
|
||||
return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
|
||||
};
|
||||
jURL.revokeObjectURL = function(url) {
|
||||
OriginalURL.revokeObjectURL(url);
|
||||
};
|
||||
}
|
||||
|
||||
scope.URL = jURL;
|
||||
})(window);
|
106
util.go
106
util.go
|
@ -1,106 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
package deno
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func logDebug(format string, v ...interface{}) {
|
||||
// Unless the debug flag is specified, discard logs.
|
||||
if *flagDebug {
|
||||
fmt.Printf(format+"\n", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// exists returns whether the given file or directory exists or not
|
||||
func exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func assert(cond bool, msg string) {
|
||||
if !cond {
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func isRemote(filename string) bool {
|
||||
u, err := url.Parse(filename)
|
||||
check(err)
|
||||
return u.IsAbs()
|
||||
}
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func exitOnError(err error) {
|
||||
if err != nil {
|
||||
os.Stderr.WriteString(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func async(cb func()) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
cb()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
const wildcard = "[WILDCARD]"
|
||||
|
||||
// Matches the pattern string against the text string. The pattern can
|
||||
// contain "[WILDCARD]" substrings which will match one or more characters.
|
||||
// Returns true if matched.
|
||||
func patternMatch(pattern string, text string) bool {
|
||||
// Empty pattern only match empty text.
|
||||
if len(pattern) == 0 {
|
||||
return len(text) == 0
|
||||
}
|
||||
|
||||
if pattern == wildcard {
|
||||
return true
|
||||
}
|
||||
|
||||
parts := strings.Split(pattern, wildcard)
|
||||
|
||||
if len(parts) == 1 {
|
||||
return pattern == text
|
||||
}
|
||||
|
||||
if strings.HasPrefix(text, parts[0]) {
|
||||
text = text[len(parts[0]):]
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 1; i < len(parts); i++ {
|
||||
// If the last part is empty, we match.
|
||||
if i == len(parts)-1 {
|
||||
if parts[i] == "" || parts[i] == "\n" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
index := strings.Index(text, parts[i])
|
||||
if index < 0 {
|
||||
return false
|
||||
}
|
||||
text = text[index+len(parts[i]):]
|
||||
}
|
||||
|
||||
return len(text) == 0
|
||||
}
|
53
util.ts
53
util.ts
|
@ -1,53 +0,0 @@
|
|||
// 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>;
|
||||
}
|
62
util_test.go
62
util_test.go
|
@ -1,62 +0,0 @@
|
|||
package deno
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const exStackTrace = `hello
|
||||
before error
|
||||
error Error: error
|
||||
at foo (/Users/rld/go/src/github.com/ry/deno/testdata/013_async_throw.ts:4:11)
|
||||
at eval (/Users/rld/go/src/github.com/ry/deno/testdata/013_async_throw.ts:6:1)
|
||||
at Object.eval [as globalEval] (<anonymous>)
|
||||
at execute (/main.js:144781:15)
|
||||
at FileModule.compileAndRun (/main.js:144678:13)
|
||||
at /main.js:145161:13
|
||||
at /main.js:15733:13`
|
||||
const exStackTracePattern = `hello
|
||||
before error
|
||||
error Error: error
|
||||
at foo ([WILDCARD]testdata/013_async_throw.ts:4:11)
|
||||
at eval ([WILDCARD]testdata/013_async_throw.ts:6:1)
|
||||
at Object.eval [as globalEval] (<anonymous>)
|
||||
at execute (/main.js:[WILDCARD]`
|
||||
|
||||
func TestPatternMatch(t *testing.T) {
|
||||
if patternMatch("aa", "a") != false {
|
||||
t.Fatalf("Wrong resullt (1).")
|
||||
}
|
||||
if patternMatch("aaa[WILDCARD]b", "aaaxsdfdb") != true {
|
||||
t.Fatalf("Wrong resullt (2).")
|
||||
}
|
||||
if patternMatch("aab[WILDCARD]", "xsd") != false {
|
||||
t.Fatalf("Wrong resullt (3).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abc") != true {
|
||||
t.Fatalf("Wrong resullt (4).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD]b[WILDCARD]c", "axbc") != true {
|
||||
t.Fatalf("Wrong resullt (5).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abxc") != true {
|
||||
t.Fatalf("Wrong resullt (6).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD]b[WILDCARD]c", "axbxc") != true {
|
||||
t.Fatalf("Wrong resullt (7).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abcx") != false {
|
||||
t.Fatalf("Wrong resullt (8).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD][WILDCARD]c", "abc") != true {
|
||||
t.Fatalf("Wrong resullt (9).")
|
||||
}
|
||||
if patternMatch("a[WILDCARD][WILDCARD]c", "ac") != true {
|
||||
t.Fatalf("Wrong resullt (10).")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternMatchStackTrace(t *testing.T) {
|
||||
if patternMatch(exStackTracePattern, exStackTrace) != true {
|
||||
t.Fatalf("Wrong resullt (11).")
|
||||
}
|
||||
}
|
|
@ -1,273 +0,0 @@
|
|||
// 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;
|
||||
}
|
8
v8worker2.d.ts
vendored
8
v8worker2.d.ts
vendored
|
@ -1,8 +0,0 @@
|
|||
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
|
||||
// All rights reserved. MIT License.
|
||||
declare namespace V8Worker2 {
|
||||
function print(...args: any[]): void;
|
||||
type RecvCallback = (ab: ArrayBuffer) => void;
|
||||
function recv(cb: RecvCallback): void;
|
||||
function send(ab: ArrayBuffer): null | ArrayBuffer;
|
||||
}
|
Loading…
Reference in a new issue