Add @cosmwasm/cli
This commit is contained in:
parent
5046963b17
commit
b5d608af10
3
NOTICE
3
NOTICE
@ -5,6 +5,9 @@ and heavily modified from there on.
|
||||
The code in packages/faucet was forked from https://github.com/iov-one/iov-faucet on
|
||||
2020-01-29 at commit 33e2d707e7.
|
||||
|
||||
The code in packages/cli was forked from https://github.com/iov-one/iov-core/tree/v2.0.0-alpha.7/packages/iov-cli
|
||||
on 2020-02-06.
|
||||
|
||||
Copyright 2018-2020 IOV SAS
|
||||
Copyright 2020 Confio UO
|
||||
Copyright 2020 Simon Warta
|
||||
|
||||
1
packages/cli/.eslintignore
Symbolic link
1
packages/cli/.eslintignore
Symbolic link
@ -0,0 +1 @@
|
||||
../../.eslintignore
|
||||
5
packages/cli/.gitignore
vendored
Normal file
5
packages/cli/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
build/
|
||||
dist/
|
||||
docs/
|
||||
|
||||
selftest_userprofile_db/
|
||||
50
packages/cli/README.md
Normal file
50
packages/cli/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# @cosmwasm/cli
|
||||
|
||||
[](https://www.npmjs.com/package/@cosmwasm/cli)
|
||||
|
||||
## Installation and first run
|
||||
|
||||
The `cosmwasm-cli` executable is available via npm. We recommend local
|
||||
installations to your demo project. If you don't have one yet, just
|
||||
`mkdir cosmwasm-cli-installation && cd cosmwasm-cli-installation && yarn init --yes`.
|
||||
|
||||
### locally with yarn
|
||||
|
||||
```
|
||||
$ yarn add @cosmwasm/cli --dev
|
||||
$ ./node_modules/.bin/cosmwasm-cli
|
||||
```
|
||||
|
||||
### locally with npm
|
||||
|
||||
```
|
||||
$ npm install @cosmwasm/cli --save-dev
|
||||
$ ./node_modules/.bin/cosmwasm-cli
|
||||
```
|
||||
|
||||
### globally with yarn
|
||||
|
||||
```
|
||||
$ yarn global add @cosmwasm/cli
|
||||
$ cosmwasm-cli
|
||||
```
|
||||
|
||||
### globally with npm
|
||||
|
||||
```
|
||||
$ npm install -g @cosmwasm/cli
|
||||
$ cosmwasm-cli
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Install `@cosmwasm/cli` and run `cosmwasm-cli` as shown above
|
||||
2. TODO: write README inspired by
|
||||
https://github.com/iov-one/iov-core/blob/master/packages/iov-cli/README.md
|
||||
|
||||
## License
|
||||
|
||||
This package is part of the cosmwasm-js repository, licensed under the Apache
|
||||
License 2.0 (see
|
||||
[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and
|
||||
[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)).
|
||||
6
packages/cli/bin/cosmwasm-cli
Executable file
6
packages/cli/bin/cosmwasm-cli
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require("path");
|
||||
|
||||
// attempt to call in main file....
|
||||
const cli = require(path.join(__dirname, "..", "build", "cli.js"));
|
||||
cli.main(process.argv.slice(2));
|
||||
26
packages/cli/jasmine-testrunner.js
Executable file
26
packages/cli/jasmine-testrunner.js
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require("source-map-support").install();
|
||||
const defaultSpecReporterConfig = require("../../jasmine-spec-reporter.config.json");
|
||||
|
||||
// setup Jasmine
|
||||
const Jasmine = require("jasmine");
|
||||
const jasmine = new Jasmine();
|
||||
jasmine.loadConfig({
|
||||
spec_dir: "build",
|
||||
spec_files: ["**/*.spec.js"],
|
||||
helpers: [],
|
||||
random: false,
|
||||
seed: null,
|
||||
stopSpecOnExpectationFailure: false,
|
||||
});
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000;
|
||||
|
||||
// setup reporter
|
||||
const { SpecReporter } = require("jasmine-spec-reporter");
|
||||
const reporter = new SpecReporter({ ...defaultSpecReporterConfig });
|
||||
|
||||
// initialize and execute
|
||||
jasmine.env.clearReporters();
|
||||
jasmine.addReporter(reporter);
|
||||
jasmine.execute();
|
||||
1
packages/cli/nonces/README.txt
Normal file
1
packages/cli/nonces/README.txt
Normal file
@ -0,0 +1 @@
|
||||
Directory used to trigger lerna package updates for all packages
|
||||
51
packages/cli/package.json
Normal file
51
packages/cli/package.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "@iov/cli",
|
||||
"version": "0.0.3",
|
||||
"description": "Command line interface",
|
||||
"contributors": ["IOV SAS <admin@iov.one>", "Simon Warta"],
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/confio/cosmwasm-js/tree/master/packages/cli"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
|
||||
"format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"",
|
||||
"lint": "eslint --max-warnings 0 \"**/*.{js,ts}\" && tslint -t verbose --project .",
|
||||
"build": "tsc",
|
||||
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
|
||||
"test-node": "node jasmine-testrunner.js",
|
||||
"test-bin": "yarn build-or-skip && ./bin/cosmwasm-cli --selftest",
|
||||
"test": "yarn build-or-skip && yarn test-node"
|
||||
},
|
||||
"bin": {
|
||||
"cosmwasm-cli": "bin/cosmwasm-cli"
|
||||
},
|
||||
"files": [
|
||||
"build/",
|
||||
"types/",
|
||||
"tsconfig_repl.json",
|
||||
"*.md",
|
||||
"!*.spec.*",
|
||||
"!**/testdata/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@cosmwasm/sdk": "^0.0.3",
|
||||
"argparse": "^1.0.10",
|
||||
"babylon": "^6.18.0",
|
||||
"colors": "^1.3.3",
|
||||
"diff": "^3.5.0",
|
||||
"leveldown": "^5.0.0",
|
||||
"recast": "^0.18.0",
|
||||
"ts-node": "^7.0.0",
|
||||
"typescript": "~3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/argparse": "^1.0.34",
|
||||
"@types/babylon": "^6.16.3",
|
||||
"@types/diff": "^3.5.1"
|
||||
}
|
||||
}
|
||||
34
packages/cli/src/async.spec.ts
Normal file
34
packages/cli/src/async.spec.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { wrapInAsyncFunction } from "./async";
|
||||
|
||||
describe("async", () => {
|
||||
it("can convert wrap code in async function", () => {
|
||||
expect(wrapInAsyncFunction("")).toMatch(/\(async \(\) => {\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction(" ")).toMatch(/\(async \(\) => {\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("\n")).toMatch(/\(async \(\) => {\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction(" \n ")).toMatch(/\(async \(\) => {\s+}\)\(\)/);
|
||||
|
||||
// locals become globals
|
||||
expect(wrapInAsyncFunction("var a = 1;")).toMatch(/\(async \(\) => {\s+a = 1;\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("const a = Date.now();")).toMatch(
|
||||
/\(async \(\) => {\s+a = Date.now\(\);\s+}\)\(\)/,
|
||||
);
|
||||
|
||||
// expressions
|
||||
expect(wrapInAsyncFunction("1")).toMatch(/\(async \(\) => {\s+return 1;\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("1;")).toMatch(/\(async \(\) => {\s+return 1;;\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("a+b")).toMatch(/\(async \(\) => {\s+return a\+b;\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("a++")).toMatch(/\(async \(\) => {\s+return a\+\+;\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("Date.now()")).toMatch(/\(async \(\) => {\s+return Date.now\(\);\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("(1)")).toMatch(/\(async \(\) => {\s+return \(1\);\s+}\)\(\)/);
|
||||
|
||||
// multiple statements
|
||||
expect(wrapInAsyncFunction("var a = 1; var b = 2;")).toMatch(
|
||||
/\(async \(\) => {\s+a = 1;\s+b = 2;\s+}\)\(\)/,
|
||||
);
|
||||
expect(wrapInAsyncFunction("var a = 1; a")).toMatch(/\(async \(\) => {\s+a = 1;\s+return a;\s+}\)\(\)/);
|
||||
|
||||
// comments
|
||||
expect(wrapInAsyncFunction("/* abcd */")).toMatch(/\(async \(\) => {\s+\/\* abcd \*\/\s+}\)\(\)/);
|
||||
expect(wrapInAsyncFunction("// abcd")).toMatch(/\(async \(\) => {\s+\/\/ abcd\s+}\)\(\)/);
|
||||
});
|
||||
});
|
||||
47
packages/cli/src/async.ts
Normal file
47
packages/cli/src/async.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import * as recast from "recast";
|
||||
|
||||
import babylon = require("babylon");
|
||||
|
||||
export function wrapInAsyncFunction(code: string): string {
|
||||
const codeInAsyncFunction = `(async () => {
|
||||
${code}
|
||||
})()`;
|
||||
|
||||
const ast = recast.parse(codeInAsyncFunction, { parser: babylon });
|
||||
const body = ast.program.body[0].expression.callee.body.body;
|
||||
|
||||
if (body.length !== 0) {
|
||||
const last = body.pop();
|
||||
if (last.type === "ExpressionStatement") {
|
||||
body.push({
|
||||
type: "ReturnStatement",
|
||||
argument: last,
|
||||
});
|
||||
} else {
|
||||
body.push(last);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove var, let, const from variable declarations to make them available in context
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
ast.program.body[0].expression.callee.body.body = body.map((node: any) => {
|
||||
if (node.type === "VariableDeclaration") {
|
||||
return {
|
||||
type: "ExpressionStatement",
|
||||
expression: {
|
||||
type: "SequenceExpression",
|
||||
expressions: node.declarations.map((declaration: any) => ({
|
||||
type: "AssignmentExpression",
|
||||
operator: "=",
|
||||
left: declaration.id,
|
||||
right: declaration.init,
|
||||
})),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
return recast.print(ast).code;
|
||||
}
|
||||
138
packages/cli/src/cli.ts
Normal file
138
packages/cli/src/cli.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import { ArgumentParser } from "argparse";
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import colors = require("colors/safe");
|
||||
import { join } from "path";
|
||||
|
||||
import { TsRepl } from "./tsrepl";
|
||||
|
||||
export function main(originalArgs: readonly string[]): void {
|
||||
const parser = new ArgumentParser({ description: "The CosmWasm REPL" });
|
||||
parser.addArgument("--version", {
|
||||
action: "storeTrue",
|
||||
help: "Print version and exit",
|
||||
});
|
||||
|
||||
const maintainerGroup = parser.addArgumentGroup({
|
||||
title: "Maintainer options",
|
||||
description: "Don't use those unless a maintainer tells you to.",
|
||||
});
|
||||
maintainerGroup.addArgument("--selftest", {
|
||||
action: "storeTrue",
|
||||
help: "Run a selftext and exit",
|
||||
});
|
||||
maintainerGroup.addArgument("--debug", {
|
||||
action: "storeTrue",
|
||||
help: "Enable debugging",
|
||||
});
|
||||
const args = parser.parseArgs([...originalArgs]);
|
||||
|
||||
if (args.version) {
|
||||
const version = require(join(__dirname, "..", "package.json")).version;
|
||||
console.info(version);
|
||||
return;
|
||||
}
|
||||
|
||||
const imports = new Map<string, readonly string[]>([
|
||||
["@cosmwasm/sdk", ["types", "RestClient"]],
|
||||
[
|
||||
"@iov/crypto",
|
||||
[
|
||||
"Bip39",
|
||||
"Ed25519",
|
||||
"Ed25519Keypair",
|
||||
"EnglishMnemonic",
|
||||
"Random",
|
||||
"Secp256k1",
|
||||
"Sha256",
|
||||
"Sha512",
|
||||
"Slip10",
|
||||
"Slip10Curve",
|
||||
"Slip10RawIndex",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@iov/encoding",
|
||||
[
|
||||
"Bech32",
|
||||
"Encoding",
|
||||
// integers
|
||||
"Int53",
|
||||
"Uint32",
|
||||
"Uint53",
|
||||
"Uint64",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@iov/keycontrol",
|
||||
[
|
||||
"Ed25519HdWallet",
|
||||
"HdPaths",
|
||||
"Keyring",
|
||||
"Secp256k1HdWallet",
|
||||
"UserProfile",
|
||||
"Wallet",
|
||||
"WalletId",
|
||||
"WalletImplementationIdString",
|
||||
"WalletSerializationString",
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
console.info(colors.green("Initializing session for you. Have fun!"));
|
||||
console.info(colors.yellow("Available imports:"));
|
||||
console.info(colors.yellow(" * http"));
|
||||
console.info(colors.yellow(" * https"));
|
||||
console.info(colors.yellow(" * leveldown"));
|
||||
console.info(colors.yellow(" * levelup"));
|
||||
console.info(colors.yellow(" * from long"));
|
||||
console.info(colors.yellow(" - Long"));
|
||||
for (const moduleName of imports.keys()) {
|
||||
console.info(colors.yellow(` * from ${moduleName}`));
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
for (const symbol of imports.get(moduleName)!) {
|
||||
console.info(colors.yellow(` - ${symbol}`));
|
||||
}
|
||||
}
|
||||
console.info(colors.yellow(" * helper functions"));
|
||||
console.info(colors.yellow(" - toAscii"));
|
||||
console.info(colors.yellow(" - fromHex"));
|
||||
console.info(colors.yellow(" - toHex"));
|
||||
|
||||
let init = `
|
||||
import leveldown = require('leveldown');
|
||||
import levelup from "levelup";
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import Long from "long";
|
||||
`;
|
||||
for (const moduleName of imports.keys()) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
init += `import { ${imports.get(moduleName)!.join(", ")} } from "${moduleName}";\n`;
|
||||
}
|
||||
init += `const { toAscii, fromHex, toHex } = Encoding;\n`;
|
||||
|
||||
if (args.selftest) {
|
||||
// execute some trival stuff and exit
|
||||
init += `
|
||||
const hash = new Sha512(new Uint8Array([])).digest();
|
||||
const hexHash = toHex(hash);
|
||||
export class NewDummyClass {};
|
||||
|
||||
const profile = new UserProfile();
|
||||
const wallet = profile.addWallet(Ed25519HdWallet.fromMnemonic("degree tackle suggest window test behind mesh extra cover prepare oak script"));
|
||||
const db = levelup(leveldown('./selftest_userprofile_db'));
|
||||
await profile.storeIn(db, "secret passwd");
|
||||
const profileFromDb = await UserProfile.loadFrom(db, "secret passwd");
|
||||
|
||||
console.info("Done testing, will exit now.");
|
||||
process.exit(0);
|
||||
`;
|
||||
}
|
||||
|
||||
const tsconfigPath = join(__dirname, "..", "tsconfig_repl.json");
|
||||
const installationDir = join(__dirname, "..");
|
||||
new TsRepl(tsconfigPath, init, !!args.debug, installationDir).start().catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
148
packages/cli/src/helpers.spec.ts
Normal file
148
packages/cli/src/helpers.spec.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import { createContext } from "vm";
|
||||
|
||||
import { executeJavaScript, executeJavaScriptAsync } from "./helpers";
|
||||
|
||||
describe("Helpers", () => {
|
||||
describe("executeJavaScript", () => {
|
||||
it("can execute simple JavaScript", () => {
|
||||
{
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("123", "myfile.js", context)).toEqual(123);
|
||||
}
|
||||
|
||||
{
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("1+1", "myfile.js", context)).toEqual(2);
|
||||
}
|
||||
});
|
||||
|
||||
it("can execute multiple commands in one context", () => {
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("let a", "myfile.js", context)).toBeUndefined();
|
||||
expect(executeJavaScript("a = 2", "myfile.js", context)).toEqual(2);
|
||||
expect(executeJavaScript("a", "myfile.js", context)).toEqual(2);
|
||||
expect(executeJavaScript("let b = 3", "myfile.js", context)).toBeUndefined();
|
||||
expect(executeJavaScript("a+b", "myfile.js", context)).toEqual(5);
|
||||
});
|
||||
|
||||
it("has no require() in sandbox context", () => {
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("typeof require", "myfile.js", context)).toEqual("undefined");
|
||||
});
|
||||
|
||||
it("has no exports object in sandbox context", () => {
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("typeof exports", "myfile.js", context)).toEqual("undefined");
|
||||
});
|
||||
|
||||
it("has no module object in sandbox context", () => {
|
||||
const context = createContext({});
|
||||
expect(executeJavaScript("typeof module", "myfile.js", context)).toEqual("undefined");
|
||||
});
|
||||
|
||||
it("can use require when passed into sandbox context", () => {
|
||||
const context = createContext({ require: require });
|
||||
expect(executeJavaScript("const path = require('path')", "myfile.js", context)).toBeUndefined();
|
||||
expect(executeJavaScript("path.join('.')", "myfile.js", context)).toEqual(".");
|
||||
});
|
||||
|
||||
it("can use module when passed into sandbox context", () => {
|
||||
const context = createContext({ module: module });
|
||||
expect(executeJavaScript("module.exports.fooTest", "myfile.js", context)).toBeUndefined();
|
||||
expect(executeJavaScript("module.exports.fooTest = 'bar'", "myfile.js", context)).toEqual("bar");
|
||||
expect(executeJavaScript("module.exports.fooTest", "myfile.js", context)).toEqual("bar");
|
||||
// roll back change to module.exports
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
module.exports.fooTest = undefined;
|
||||
});
|
||||
|
||||
it("can use exports when passed into sandbox context", () => {
|
||||
const context = createContext({ exports: {} });
|
||||
expect(executeJavaScript("exports.fooTest", "myfile.js", context)).toBeUndefined();
|
||||
expect(executeJavaScript("exports.fooTest = 'bar'", "myfile.js", context)).toEqual("bar");
|
||||
expect(executeJavaScript("exports.fooTest", "myfile.js", context)).toEqual("bar");
|
||||
});
|
||||
});
|
||||
|
||||
describe("executeJavaScriptAsync", () => {
|
||||
it("can execute top level await with brackets", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("await (1)", "myfile.js", context)).toEqual(1);
|
||||
});
|
||||
|
||||
it("can execute top level await without brackets", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("await 1", "myfile.js", context)).toEqual(1);
|
||||
});
|
||||
|
||||
it("can handle multiple awaits", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("await 1 + await 2", "myfile.js", context)).toEqual(3);
|
||||
});
|
||||
|
||||
it("errors for local declaration without assignment", async () => {
|
||||
// `var a` cannot be converted into an assignment because it must not override an
|
||||
// existing value. Thus we cannot execute it
|
||||
const context = createContext({});
|
||||
await executeJavaScriptAsync("var a", "myfile.js", context)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch(e => expect(e).toMatch(/SyntaxError:/));
|
||||
await executeJavaScriptAsync("let b", "myfile.js", context)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch(e => expect(e).toMatch(/SyntaxError:/));
|
||||
await executeJavaScriptAsync("const c", "myfile.js", context)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch(e => expect(e).toMatch(/SyntaxError:/));
|
||||
});
|
||||
|
||||
it("can execute multiple commands in one context", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("let a = 2", "myfile.js", context)).toBeUndefined();
|
||||
expect(await executeJavaScriptAsync("a", "myfile.js", context)).toEqual(2);
|
||||
expect(await executeJavaScriptAsync("a += 1", "myfile.js", context)).toEqual(3);
|
||||
expect(await executeJavaScriptAsync("a += 7", "myfile.js", context)).toEqual(10);
|
||||
expect(await executeJavaScriptAsync("a", "myfile.js", context)).toEqual(10);
|
||||
expect((context as any).a).toEqual(10);
|
||||
});
|
||||
|
||||
it("local variables are available across multiple calls in one context", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("let a = 3", "myfile.js", context)).toBeUndefined();
|
||||
expect(await executeJavaScriptAsync("a", "myfile.js", context)).toEqual(3);
|
||||
expect((context as any).a).toEqual(3);
|
||||
});
|
||||
|
||||
it("works with strict mode", async () => {
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync('"use strict"; let a = 3', "myfile.js", context)).toBeUndefined();
|
||||
expect(await executeJavaScriptAsync('"use strict"; a', "myfile.js", context)).toEqual(3);
|
||||
expect((context as any).a).toEqual(3);
|
||||
});
|
||||
|
||||
it("can reassign const", async () => {
|
||||
// a side-effect of local variable assignment manipulation
|
||||
const context = createContext({});
|
||||
expect(await executeJavaScriptAsync("const a = 3", "myfile.js", context)).toBeUndefined();
|
||||
expect((context as any).a).toEqual(3);
|
||||
expect(await executeJavaScriptAsync("const a = 4", "myfile.js", context)).toBeUndefined();
|
||||
expect((context as any).a).toEqual(4);
|
||||
});
|
||||
|
||||
it("can execute timeout promise code", async () => {
|
||||
const context = createContext({ setTimeout: setTimeout });
|
||||
const code = "await (new Promise(resolve => setTimeout(() => resolve('job done'), 5)))";
|
||||
expect(await executeJavaScriptAsync(code, "myfile.js", context)).toEqual("job done");
|
||||
});
|
||||
|
||||
it("can execute timeout code in multiple statements", async () => {
|
||||
const context = createContext({ setTimeout: setTimeout });
|
||||
const code = `
|
||||
const promise = new Promise(resolve => {
|
||||
setTimeout(() => resolve('job done'), 5);
|
||||
});
|
||||
await (promise);
|
||||
`;
|
||||
expect(await executeJavaScriptAsync(code, "myfile.js", context)).toEqual("job done");
|
||||
});
|
||||
});
|
||||
});
|
||||
33
packages/cli/src/helpers.ts
Normal file
33
packages/cli/src/helpers.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { TSError } from "ts-node";
|
||||
import { Context, Script } from "vm";
|
||||
|
||||
import { wrapInAsyncFunction } from "./async";
|
||||
|
||||
export function executeJavaScript(code: string, filename: string, context: Context): any {
|
||||
const script = new Script(code, { filename: filename });
|
||||
return script.runInContext(context);
|
||||
}
|
||||
|
||||
export async function executeJavaScriptAsync(code: string, filename: string, context: Context): Promise<any> {
|
||||
const preparedCode = code.replace(/^\s*"use strict";/, "");
|
||||
|
||||
// wrapped code returns a promise when executed
|
||||
const wrappedCode = wrapInAsyncFunction(preparedCode);
|
||||
const script = new Script(wrappedCode, { filename: filename });
|
||||
const out = await script.runInContext(context);
|
||||
return out;
|
||||
}
|
||||
|
||||
export function isRecoverable(error: TSError): boolean {
|
||||
const recoveryCodes: Set<number> = new Set([
|
||||
1003, // "Identifier expected."
|
||||
1005, // "')' expected."
|
||||
1109, // "Expression expected."
|
||||
1126, // "Unexpected end of text."
|
||||
1160, // "Unterminated template literal."
|
||||
1161, // "Unterminated regular expression literal."
|
||||
2355, // "A function whose declared type is neither 'void' nor 'any' must return a value."
|
||||
]);
|
||||
|
||||
return error.diagnosticCodes.every(code => recoveryCodes.has(code));
|
||||
}
|
||||
56
packages/cli/src/tsrepl.spec.ts
Normal file
56
packages/cli/src/tsrepl.spec.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { join } from "path";
|
||||
|
||||
import { TsRepl } from "./tsrepl";
|
||||
|
||||
const tsConfigPath = join(__dirname, "..", "tsconfig_repl.json");
|
||||
|
||||
describe("TsRepl", () => {
|
||||
it("can be constructed", () => {
|
||||
const noCode = new TsRepl(tsConfigPath, "");
|
||||
expect(noCode).toBeTruthy();
|
||||
|
||||
const jsCode = new TsRepl(tsConfigPath, "const a = 'foo'");
|
||||
expect(jsCode).toBeTruthy();
|
||||
|
||||
const tsCode = new TsRepl(tsConfigPath, "const a: string = 'foo'");
|
||||
expect(tsCode).toBeTruthy();
|
||||
});
|
||||
|
||||
it("can be started", async () => {
|
||||
{
|
||||
const server = await new TsRepl(tsConfigPath, "").start();
|
||||
expect(server).toBeTruthy();
|
||||
}
|
||||
{
|
||||
const server = await new TsRepl(tsConfigPath, "const a = 'foo'").start();
|
||||
expect(server).toBeTruthy();
|
||||
}
|
||||
{
|
||||
const server = await new TsRepl(tsConfigPath, "const a: string = 'foo'").start();
|
||||
expect(server).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("errors when starting with broken TypeScript", async () => {
|
||||
await new TsRepl(tsConfigPath, "const a: string = 123;")
|
||||
.start()
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch(e => expect(e).toMatch(/is not assignable to type 'string'/));
|
||||
|
||||
await new TsRepl(tsConfigPath, "const const const;")
|
||||
.start()
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch(e => expect(e).toMatch(/Variable declaration expected./));
|
||||
});
|
||||
|
||||
it("can be started with top level await", async () => {
|
||||
{
|
||||
const server = await new TsRepl(tsConfigPath, "await 1").start();
|
||||
expect(server).toBeTruthy();
|
||||
}
|
||||
{
|
||||
const server = await new TsRepl(tsConfigPath, "await 1 + await 2").start();
|
||||
expect(server).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
239
packages/cli/src/tsrepl.ts
Normal file
239
packages/cli/src/tsrepl.ts
Normal file
@ -0,0 +1,239 @@
|
||||
import { diffLines } from "diff";
|
||||
import { join } from "path";
|
||||
import { Recoverable, REPLServer, start } from "repl";
|
||||
import { Register, register, TSError } from "ts-node";
|
||||
import { Context, createContext } from "vm";
|
||||
|
||||
import { executeJavaScriptAsync, isRecoverable } from "./helpers";
|
||||
|
||||
interface ReplEvalResult {
|
||||
readonly result: any;
|
||||
readonly error: Error | null;
|
||||
}
|
||||
|
||||
export class TsRepl {
|
||||
private readonly typeScriptService: Register;
|
||||
private readonly debuggingEnabled: boolean;
|
||||
private readonly evalFilename = `[eval].ts`;
|
||||
private readonly evalPath: string;
|
||||
private readonly evalData = { input: "", output: "" };
|
||||
private readonly resetToZero: () => void; // Bookmark to empty TS input
|
||||
private readonly initialTypeScript: string;
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
private context: Context | undefined;
|
||||
|
||||
public constructor(
|
||||
tsconfigPath: string,
|
||||
initialTypeScript: string,
|
||||
debuggingEnabled = false,
|
||||
installationDir?: string, // required when the current working directory is not the installation path
|
||||
) {
|
||||
this.typeScriptService = register({
|
||||
project: tsconfigPath,
|
||||
ignoreDiagnostics: [
|
||||
"1308", // TS1308: 'await' expression is only allowed within an async function.
|
||||
],
|
||||
});
|
||||
this.debuggingEnabled = debuggingEnabled;
|
||||
this.resetToZero = this.appendTypeScriptInput("");
|
||||
this.initialTypeScript = initialTypeScript;
|
||||
this.evalPath = join(installationDir || process.cwd(), this.evalFilename);
|
||||
}
|
||||
|
||||
public async start(): Promise<REPLServer> {
|
||||
/**
|
||||
* A wrapper around replEval used to match the method signature
|
||||
* for "Custom Evaluation Functions"
|
||||
* https://nodejs.org/api/repl.html#repl_custom_evaluation_functions
|
||||
*/
|
||||
const replEvalWrapper = async (
|
||||
code: string,
|
||||
_context: any,
|
||||
_filename: string,
|
||||
callback: (err: Error | null, result?: any) => any,
|
||||
): Promise<void> => {
|
||||
const result = await this.replEval(code);
|
||||
callback(result.error, result.result);
|
||||
};
|
||||
|
||||
const repl = start({
|
||||
prompt: ">> ",
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: process.stdout.isTTY,
|
||||
eval: replEvalWrapper,
|
||||
useGlobal: false,
|
||||
});
|
||||
|
||||
// Prepare context for TypeScript: TypeScript compiler expects the exports shortcut
|
||||
// to exist in `Object.defineProperty(exports, "__esModule", { value: true });`
|
||||
const unsafeReplContext = repl.context as any;
|
||||
if (!unsafeReplContext.exports) {
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
unsafeReplContext.exports = unsafeReplContext.module.exports;
|
||||
}
|
||||
|
||||
// REPL context is created with a default set of module resolution paths,
|
||||
// like for example
|
||||
// [ '/home/me/repl/node_modules',
|
||||
// '/home/me/node_modules',
|
||||
// '/home/node_modules',
|
||||
// '/node_modules',
|
||||
// '/home/me/.node_modules',
|
||||
// '/home/me/.node_libraries',
|
||||
// '/usr/lib/nodejs' ]
|
||||
// However, this does not include the installation path of @iov/cli because
|
||||
// REPL does not inherit module paths from the current process. Thus we override
|
||||
// the repl paths with the current process' paths
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
unsafeReplContext.module.paths = module.paths;
|
||||
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.context = createContext(repl.context);
|
||||
|
||||
const reset = async (): Promise<void> => {
|
||||
this.resetToZero();
|
||||
|
||||
// Ensure code ends with "\n" due to implementation of replEval
|
||||
await this.compileAndExecute(this.initialTypeScript + "\n", false);
|
||||
};
|
||||
|
||||
await reset();
|
||||
repl.on("reset", reset);
|
||||
|
||||
repl.defineCommand("type", {
|
||||
help: "Check the type of a TypeScript identifier",
|
||||
action: (identifier: string) => {
|
||||
if (!identifier) {
|
||||
repl.displayPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
const identifierTypeScriptCode = `${identifier}\n`;
|
||||
const undo = this.appendTypeScriptInput(identifierTypeScriptCode);
|
||||
const identifierFirstPosition = this.evalData.input.length - identifierTypeScriptCode.length;
|
||||
const { name, comment } = this.typeScriptService.getTypeInfo(
|
||||
this.evalData.input,
|
||||
this.evalPath,
|
||||
identifierFirstPosition,
|
||||
);
|
||||
|
||||
undo();
|
||||
|
||||
repl.outputStream.write(`${name}\n${comment ? `${comment}\n` : ""}`);
|
||||
repl.displayPrompt();
|
||||
},
|
||||
});
|
||||
|
||||
return repl;
|
||||
}
|
||||
|
||||
private async compileAndExecute(tsInput: string, isAutocompletionRequest: boolean): Promise<any> {
|
||||
if (!isAutocompletionRequest) {
|
||||
// Expect POSIX lines (https://stackoverflow.com/a/729795)
|
||||
if (tsInput.length > 0 && !tsInput.endsWith("\n")) {
|
||||
throw new Error("final newline missing");
|
||||
}
|
||||
}
|
||||
|
||||
const undo = this.appendTypeScriptInput(tsInput);
|
||||
let output: string;
|
||||
|
||||
try {
|
||||
// lineOffset unused at the moment (https://github.com/TypeStrong/ts-node/issues/661)
|
||||
output = this.typeScriptService.compile(this.evalData.input, this.evalPath);
|
||||
} catch (err) {
|
||||
undo();
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Use `diff` to check for new JavaScript to execute.
|
||||
const changes = diffLines(this.evalData.output, output);
|
||||
|
||||
if (isAutocompletionRequest) {
|
||||
undo();
|
||||
} else {
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.evalData.output = output;
|
||||
}
|
||||
|
||||
// Execute new JavaScript. This may not necessarily be at the end only because e.g. an import
|
||||
// statement in TypeScript is compiled to no JavaScript until the imported symbol is used
|
||||
// somewhere. This btw. leads to a different execution order of imports than in the TS source.
|
||||
let lastResult: any;
|
||||
for (const added of changes.filter(change => change.added)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
lastResult = await executeJavaScriptAsync(added.value, this.evalFilename, this.context!);
|
||||
}
|
||||
return lastResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add user-friendly error handling around compileAndExecute
|
||||
*/
|
||||
private async replEval(code: string): Promise<ReplEvalResult> {
|
||||
// TODO: Figure out how to handle completion here.
|
||||
if (code === ".scope") {
|
||||
return {
|
||||
result: undefined,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
const isAutocompletionRequest = !/\n$/.test(code);
|
||||
|
||||
try {
|
||||
const result = await this.compileAndExecute(code, isAutocompletionRequest);
|
||||
return {
|
||||
result: result,
|
||||
error: null,
|
||||
};
|
||||
} catch (error) {
|
||||
if (this.debuggingEnabled) {
|
||||
console.info("Current REPL TypeScript program:");
|
||||
console.info(this.evalData.input);
|
||||
}
|
||||
|
||||
let outError: Error | null;
|
||||
if (error instanceof TSError) {
|
||||
// Support recoverable compilations using >= node 6.
|
||||
if (Recoverable && isRecoverable(error)) {
|
||||
outError = new Recoverable(error);
|
||||
} else {
|
||||
console.error(error.diagnosticText);
|
||||
outError = null;
|
||||
}
|
||||
} else {
|
||||
outError = error;
|
||||
}
|
||||
|
||||
return {
|
||||
result: undefined,
|
||||
error: outError,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private appendTypeScriptInput(input: string): () => void {
|
||||
const oldInput = this.evalData.input;
|
||||
const oldOutput = this.evalData.output;
|
||||
|
||||
// Handle ASI issues with TypeScript re-evaluation.
|
||||
if (oldInput.charAt(oldInput.length - 1) === "\n" && /^\s*[[(`]/.test(input) && !/;\s*$/.test(oldInput)) {
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.evalData.input = `${this.evalData.input.slice(0, -1)};\n`;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.evalData.input += input;
|
||||
|
||||
const undoFunction = (): void => {
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.evalData.input = oldInput;
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
this.evalData.output = oldOutput;
|
||||
};
|
||||
|
||||
return undoFunction;
|
||||
}
|
||||
}
|
||||
11
packages/cli/tsconfig.json
Normal file
11
packages/cli/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "build",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
10
packages/cli/tsconfig_repl.json
Normal file
10
packages/cli/tsconfig_repl.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2017",
|
||||
"noUnusedLocals": false,
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
||||
3
packages/cli/tslint.json
Normal file
3
packages/cli/tslint.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
||||
105
yarn.lock
105
yarn.lock
@ -944,6 +944,23 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/argparse@^1.0.34":
|
||||
version "1.0.38"
|
||||
resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9"
|
||||
integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==
|
||||
|
||||
"@types/babel-types@*":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.7.tgz#667eb1640e8039436028055737d2b9986ee336e3"
|
||||
integrity sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==
|
||||
|
||||
"@types/babylon@^6.16.3":
|
||||
version "6.16.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/babylon/-/babylon-6.16.5.tgz#1c5641db69eb8cdf378edd25b4be7754beeb48b4"
|
||||
integrity sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==
|
||||
dependencies:
|
||||
"@types/babel-types" "*"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897"
|
||||
@ -969,6 +986,11 @@
|
||||
"@types/keygrip" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/diff@^3.5.1":
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-3.5.3.tgz#7c6c3721ba454d838790100faf7957116ee7deab"
|
||||
integrity sha512-YrLagYnL+tfrgM7bQ5yW34pi5cg9pmh5Gbq2Lmuuh+zh0ZjmK2fU3896PtlpJT3IDG2rdkoG30biHJepgIsMnw==
|
||||
|
||||
"@types/eslint-visitor-keys@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
@ -1501,7 +1523,7 @@ are-we-there-yet@~1.1.2:
|
||||
delegates "^1.0.0"
|
||||
readable-stream "^2.0.6"
|
||||
|
||||
argparse@^1.0.7:
|
||||
argparse@^1.0.10, argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
@ -1577,7 +1599,7 @@ arraybuffer.slice@~0.0.7:
|
||||
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
|
||||
integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
|
||||
|
||||
arrify@^1.0.1:
|
||||
arrify@^1.0.0, arrify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
@ -1621,6 +1643,11 @@ assign-symbols@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
||||
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
|
||||
|
||||
ast-types@0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48"
|
||||
integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==
|
||||
|
||||
astral-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||
@ -1675,6 +1702,11 @@ axios@^0.19.0:
|
||||
dependencies:
|
||||
follow-redirects "1.5.10"
|
||||
|
||||
babylon@^6.18.0:
|
||||
version "6.18.0"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
||||
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
|
||||
|
||||
backo2@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||
@ -1918,7 +1950,7 @@ buffer-fill@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
buffer-from@^1.0.0, buffer-from@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
@ -2252,7 +2284,7 @@ colors@1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
|
||||
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
|
||||
|
||||
colors@^1.1.0:
|
||||
colors@^1.1.0, colors@^1.3.3:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||
@ -2789,6 +2821,11 @@ di@^0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
|
||||
integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=
|
||||
|
||||
diff@^3.1.0, diff@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
@ -3219,7 +3256,7 @@ espree@^6.1.2:
|
||||
acorn-jsx "^5.1.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
esprima@^4.0.0:
|
||||
esprima@^4.0.0, esprima@~4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
@ -4856,6 +4893,15 @@ level-supports@~1.0.0:
|
||||
dependencies:
|
||||
xtend "^4.0.2"
|
||||
|
||||
leveldown@^5.0.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.4.1.tgz#83a8fdd9bb52b1ed69be2ef59822b6cdfcdb51ec"
|
||||
integrity sha512-3lMPc7eU3yj5g+qF1qlALInzIYnkySIosR1AsUKFjL9D8fYbTLuENBAeDRZXIG4qeWOAyqRItOoLu2v2avWiMA==
|
||||
dependencies:
|
||||
abstract-leveldown "~6.2.1"
|
||||
napi-macros "~2.0.0"
|
||||
node-gyp-build "~4.1.0"
|
||||
|
||||
levelup@^4.0.0:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.3.2.tgz#31c5b1b29f146d1d35d692e01a6da4d28fa55ebd"
|
||||
@ -5078,6 +5124,11 @@ make-dir@^2.0.0, make-dir@^2.1.0:
|
||||
pify "^4.0.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
|
||||
integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==
|
||||
|
||||
make-fetch-happen@^5.0.0:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd"
|
||||
@ -5461,6 +5512,11 @@ nanomatch@^1.2.9:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
napi-macros@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b"
|
||||
integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
@ -5495,6 +5551,11 @@ node-fetch@^2.3.0, node-fetch@^2.5.0:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
||||
|
||||
node-gyp-build@~4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb"
|
||||
integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==
|
||||
|
||||
node-gyp@^5.0.2:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.7.tgz#dd4225e735e840cf2870e4037c2ed9c28a31719e"
|
||||
@ -6164,6 +6225,11 @@ prettier@^1.19.1:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
||||
private@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
@ -6507,6 +6573,16 @@ readonly-date@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/readonly-date/-/readonly-date-1.0.0.tgz#5af785464d8c7d7c40b9d738cbde8c646f97dcd9"
|
||||
integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==
|
||||
|
||||
recast@^0.18.0:
|
||||
version "0.18.5"
|
||||
resolved "https://registry.yarnpkg.com/recast/-/recast-0.18.5.tgz#9d5adbc07983a3c8145f3034812374a493e0fe4d"
|
||||
integrity sha512-sD1WJrpLQAkXGyQZyGzTM75WJvyAd98II5CHdK3IYbt/cZlU0UzCRVU11nUFNXX9fBVEt4E9ajkMjBlUlG+Oog==
|
||||
dependencies:
|
||||
ast-types "0.13.2"
|
||||
esprima "~4.0.0"
|
||||
private "^0.1.8"
|
||||
source-map "~0.6.1"
|
||||
|
||||
rechoir@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||
@ -7494,6 +7570,20 @@ trim-off-newlines@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
|
||||
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
||||
|
||||
ts-node@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf"
|
||||
integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==
|
||||
dependencies:
|
||||
arrify "^1.0.0"
|
||||
buffer-from "^1.1.0"
|
||||
diff "^3.1.0"
|
||||
make-error "^1.1.1"
|
||||
minimist "^1.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.5.6"
|
||||
yn "^2.0.0"
|
||||
|
||||
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
||||
@ -8095,3 +8185,8 @@ ylru@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"
|
||||
integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==
|
||||
|
||||
yn@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
|
||||
integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user