mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 09:10:40 -05:00
Add eslint for linting (#235)
This commit is contained in:
parent
385f866a54
commit
c0390ade3d
41 changed files with 734 additions and 649 deletions
4
.eslintignore
Normal file
4
.eslintignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
/flags/
|
||||
/fs/
|
||||
/http/
|
||||
/ws/
|
23
.eslintrc.json
Normal file
23
.eslintrc.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/array-type": ["error", "array-simple"],
|
||||
"@typescript-eslint/explicit-member-accessibility": ["off"],
|
||||
"@typescript-eslint/no-non-null-assertion": ["off"],
|
||||
"@typescript-eslint/no-parameter-properties": ["off"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ "argsIgnorePattern": "^_" }
|
||||
]
|
||||
}
|
||||
}
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
|||
.DS_Store
|
||||
.idea
|
||||
tsconfig.json
|
||||
deno.d.ts
|
||||
deno.d.ts
|
||||
node_modules
|
||||
package.json
|
||||
package-lock.json
|
||||
|
|
|
@ -1,31 +1,39 @@
|
|||
variables:
|
||||
DENO_VERSION: 'v0.3.1'
|
||||
DENO_VERSION: "v0.3.1"
|
||||
TS_VERSION: "3.2.1"
|
||||
|
||||
# TODO DRY up the jobs
|
||||
# TODO Try to get eslint to run under Deno, like prettier
|
||||
jobs:
|
||||
- job: "Linux"
|
||||
pool:
|
||||
vmImage: "Ubuntu-16.04"
|
||||
steps:
|
||||
- script: npm install eslint typescript@$(TS_VERSION) @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier
|
||||
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION)
|
||||
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/'
|
||||
- script: npx eslint **/*.ts
|
||||
- script: deno format.ts --allow-run --allow-write --allow-read --check
|
||||
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
|
||||
- job: 'Linux'
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
steps:
|
||||
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION)
|
||||
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/'
|
||||
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
- script: deno format.ts --allow-run --allow-write --allow-read --check
|
||||
- job: "Mac"
|
||||
pool:
|
||||
vmImage: "macOS-10.13"
|
||||
steps:
|
||||
- script: npm -g install eslint typescript@$(TS_VERSION) @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier
|
||||
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION)
|
||||
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/'
|
||||
- script: eslint **/*.ts
|
||||
- script: deno format.ts --allow-run --allow-write --allow-read --check
|
||||
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
|
||||
- job: 'Mac'
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
steps:
|
||||
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION)
|
||||
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/'
|
||||
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
- script: deno format.ts --allow-run --allow-write --allow-read --check
|
||||
|
||||
- job: 'Windows'
|
||||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
steps:
|
||||
- powershell: iwr https://deno.land/x/install/install.ps1 -out install.ps1; .\install.ps1 $(DENO_VERSION)
|
||||
- bash: echo "##vso[task.prependpath]C:\Users\VssAdministrator\.deno\\bin"
|
||||
- bash: deno.exe test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
- bash: deno.exe format.ts --allow-run --allow-write --allow-read --check
|
||||
- job: "Windows"
|
||||
pool:
|
||||
vmImage: "vs2017-win2016"
|
||||
steps:
|
||||
- bash: npm install eslint typescript@$(TS_VERSION) @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier
|
||||
- powershell: iwr https://deno.land/x/install/install.ps1 -out install.ps1; .\install.ps1 $(DENO_VERSION)
|
||||
- bash: echo "##vso[task.prependpath]C:\Users\VssAdministrator\.deno\\bin"
|
||||
- bash: npx eslint **/*.ts
|
||||
- bash: deno.exe format.ts --allow-run --allow-write --allow-read --check
|
||||
- bash: deno.exe test.ts --allow-run --allow-net --allow-write --allow-read
|
||||
|
|
|
@ -14,7 +14,7 @@ bench({
|
|||
runs: 100,
|
||||
func(b: BenchmarkTimer) {
|
||||
b.start();
|
||||
for (let i: number = 0; i < 1e6; i++);
|
||||
for (let i = 0; i < 1e6; i++);
|
||||
b.stop();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,10 +14,10 @@ export interface BenchmarkTimer {
|
|||
}
|
||||
|
||||
/** Defines a benchmark through a named function. */
|
||||
export type BenchmarkFunction = {
|
||||
export interface BenchmarkFunction {
|
||||
(b: BenchmarkTimer): void | Promise<void>;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Defines a benchmark definition with configurable runs. */
|
||||
export interface BenchmarkDefinition {
|
||||
|
@ -69,7 +69,7 @@ function createBenchmarkTimer(clock: BenchmarkClock): BenchmarkTimer {
|
|||
};
|
||||
}
|
||||
|
||||
const candidates: Array<BenchmarkDefinition> = [];
|
||||
const candidates: BenchmarkDefinition[] = [];
|
||||
|
||||
/** Registers a benchmark as a candidate for the runBenchmarks executor. */
|
||||
export function bench(
|
||||
|
@ -95,27 +95,27 @@ export async function runBenchmarks({
|
|||
skip = /^\s*$/
|
||||
}: BenchmarkRunOptions = {}): Promise<void> {
|
||||
// Filtering candidates by the "only" and "skip" constraint
|
||||
const benchmarks: Array<BenchmarkDefinition> = candidates.filter(
|
||||
const benchmarks: BenchmarkDefinition[] = candidates.filter(
|
||||
({ name }) => only.test(name) && !skip.test(name)
|
||||
);
|
||||
// Init main counters and error flag
|
||||
const filtered: number = candidates.length - benchmarks.length;
|
||||
let measured: number = 0;
|
||||
let failed: boolean = false;
|
||||
const filtered = candidates.length - benchmarks.length;
|
||||
let measured = 0;
|
||||
let failed = false;
|
||||
// Setting up a shared benchmark clock and timer
|
||||
const clock: BenchmarkClock = { start: NaN, stop: NaN };
|
||||
const b: BenchmarkTimer = createBenchmarkTimer(clock);
|
||||
const b = createBenchmarkTimer(clock);
|
||||
// Iterating given benchmark definitions (await-in-loop)
|
||||
console.log(
|
||||
"running",
|
||||
benchmarks.length,
|
||||
`benchmark${benchmarks.length === 1 ? " ..." : "s ..."}`
|
||||
);
|
||||
for (const { name, runs, func } of benchmarks) {
|
||||
for (const { name, runs = 0, func } of benchmarks) {
|
||||
// See https://github.com/denoland/deno/pull/1452 about groupCollapsed
|
||||
console.groupCollapsed(`benchmark ${name} ... `);
|
||||
// Trying benchmark.func
|
||||
let result: string;
|
||||
let result = "";
|
||||
try {
|
||||
if (runs === 1) {
|
||||
// b is a benchmark timer interfacing an unset (NaN) benchmark clock
|
||||
|
@ -126,7 +126,7 @@ export async function runBenchmarks({
|
|||
} else if (runs > 1) {
|
||||
// Averaging runs
|
||||
let pendingRuns = runs;
|
||||
let totalMs: number = 0;
|
||||
let totalMs = 0;
|
||||
// Would be better 2 not run these serially
|
||||
while (true) {
|
||||
// b is a benchmark timer interfacing an unset (NaN) benchmark clock
|
||||
|
|
|
@ -6,19 +6,19 @@ import "example.ts";
|
|||
test(async function benching() {
|
||||
bench(function forIncrementX1e9(b: BenchmarkTimer) {
|
||||
b.start();
|
||||
for (let i: number = 0; i < 1e9; i++);
|
||||
for (let i = 0; i < 1e9; i++);
|
||||
b.stop();
|
||||
});
|
||||
|
||||
bench(function forDecrementX1e9(b: BenchmarkTimer) {
|
||||
b.start();
|
||||
for (let i: number = 1e9; i > 0; i--);
|
||||
for (let i = 1e9; i > 0; i--);
|
||||
b.stop();
|
||||
});
|
||||
|
||||
bench(async function forAwaitFetchDenolandX10(b: BenchmarkTimer) {
|
||||
b.start();
|
||||
for (let i: number = 0; i < 10; i++) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await fetch("https://deno.land/");
|
||||
}
|
||||
b.stop();
|
||||
|
@ -36,7 +36,7 @@ test(async function benching() {
|
|||
runs: 100,
|
||||
func(b: BenchmarkTimer) {
|
||||
b.start();
|
||||
for (let i: number = 0; i < 1e6; i++);
|
||||
for (let i = 0; i < 1e6; i++);
|
||||
b.stop();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ export function bytesFindIndex(a: Uint8Array, pat: Uint8Array): number {
|
|||
}
|
||||
|
||||
/** Find last index of binary pattern from a. If not found, then return -1 **/
|
||||
export function bytesFindLastIndex(a: Uint8Array, pat: Uint8Array) {
|
||||
export function bytesFindLastIndex(a: Uint8Array, pat: Uint8Array): number {
|
||||
const e = pat[pat.length - 1];
|
||||
for (let i = a.length - 1; i >= 0; i--) {
|
||||
if (a[i] !== e) continue;
|
||||
|
|
|
@ -9,7 +9,7 @@ interface Code {
|
|||
|
||||
let enabled = !noColor;
|
||||
|
||||
export function setEnabled(value: boolean) {
|
||||
export function setEnabled(value: boolean): void {
|
||||
if (noColor) {
|
||||
return;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ function code(open: number, close: number): Code {
|
|||
};
|
||||
}
|
||||
|
||||
function run(str: string, code: Code) {
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled
|
||||
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
|
||||
: str;
|
||||
|
|
|
@ -13,13 +13,13 @@ export function parseDate(dateStr: string, format: DateFormat): Date {
|
|||
|
||||
if (format === "mm-dd-yyyy") {
|
||||
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
|
||||
[, m, d, y] = datePattern.exec(dateStr);
|
||||
[, m, d, y] = datePattern.exec(dateStr)!;
|
||||
} else if (format === "dd-mm-yyyy") {
|
||||
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
|
||||
[, d, m, y] = datePattern.exec(dateStr);
|
||||
[, d, m, y] = datePattern.exec(dateStr)!;
|
||||
} else if (format === "yyyy-mm-dd") {
|
||||
const datePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
[, y, m, d] = datePattern.exec(dateStr);
|
||||
[, y, m, d] = datePattern.exec(dateStr)!;
|
||||
} else {
|
||||
throw new Error("Invalid date format!");
|
||||
}
|
||||
|
@ -50,22 +50,22 @@ export function parseDateTime(
|
|||
|
||||
if (format === "mm-dd-yyyy hh:mm") {
|
||||
const datePattern = /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2})$/;
|
||||
[, m, d, y, ho, mi] = datePattern.exec(datetimeStr);
|
||||
[, m, d, y, ho, mi] = datePattern.exec(datetimeStr)!;
|
||||
} else if (format === "dd-mm-yyyy hh:mm") {
|
||||
const datePattern = /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2})$/;
|
||||
[, d, m, y, ho, mi] = datePattern.exec(datetimeStr);
|
||||
[, d, m, y, ho, mi] = datePattern.exec(datetimeStr)!;
|
||||
} else if (format === "yyyy-mm-dd hh:mm") {
|
||||
const datePattern = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/;
|
||||
[, y, m, d, ho, mi] = datePattern.exec(datetimeStr);
|
||||
[, y, m, d, ho, mi] = datePattern.exec(datetimeStr)!;
|
||||
} else if (format === "hh:mm mm-dd-yyyy") {
|
||||
const datePattern = /^(\d{2}):(\d{2}) (\d{2})-(\d{2})-(\d{4})$/;
|
||||
[, ho, mi, m, d, y] = datePattern.exec(datetimeStr);
|
||||
[, ho, mi, m, d, y] = datePattern.exec(datetimeStr)!;
|
||||
} else if (format === "hh:mm dd-mm-yyyy") {
|
||||
const datePattern = /^(\d{2}):(\d{2}) (\d{2})-(\d{2})-(\d{4})$/;
|
||||
[, ho, mi, d, m, y] = datePattern.exec(datetimeStr);
|
||||
[, ho, mi, d, m, y] = datePattern.exec(datetimeStr)!;
|
||||
} else if (format === "hh:mm yyyy-mm-dd") {
|
||||
const datePattern = /^(\d{2}):(\d{2}) (\d{4})-(\d{2})-(\d{2})$/;
|
||||
[, ho, mi, y, m, d] = datePattern.exec(datetimeStr);
|
||||
[, ho, mi, y, m, d] = datePattern.exec(datetimeStr)!;
|
||||
} else {
|
||||
throw new Error("Invalid datetime format!");
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ test(function parseDateTime() {
|
|||
|
||||
test(function invalidParseDateTimeFormatThrows() {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(datetime as any).parseDateTime("2019-01-01 00:00", "x-y-z");
|
||||
assert(false, "no exception was thrown");
|
||||
} catch (e) {
|
||||
|
@ -55,6 +56,7 @@ test(function parseDate() {
|
|||
|
||||
test(function invalidParseDateFormatThrows() {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(datetime as any).parseDate("2019-01-01", "x-y-z");
|
||||
assert(false, "no exception was thrown");
|
||||
} catch (e) {
|
||||
|
|
|
@ -9,7 +9,7 @@ function pathBase(p: string): string {
|
|||
return parts[parts.length - 1];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
async function main(): Promise<void> {
|
||||
const token = env()["GIST_TOKEN"];
|
||||
if (!token) {
|
||||
console.error("GIST_TOKEN environmental variable not set.");
|
||||
|
@ -46,7 +46,7 @@ async function main() {
|
|||
headers: [
|
||||
["Content-Type", "application/json"],
|
||||
["User-Agent", "Deno-Gist"],
|
||||
["Authorization", "token " + token]
|
||||
["Authorization", `token ${token}`]
|
||||
],
|
||||
body
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
isWebSocketPingEvent
|
||||
} from "https://deno.land/x/ws/mod.ts";
|
||||
|
||||
async function main() {
|
||||
async function main(): Promise<void> {
|
||||
console.log("websocket server is running on 0.0.0.0:8080");
|
||||
for await (const req of serve("0.0.0.0:8080")) {
|
||||
if (req.url === "/ws") {
|
||||
|
@ -22,7 +22,7 @@ async function main() {
|
|||
// binary message
|
||||
console.log("ws:Binary", ev);
|
||||
} else if (isWebSocketPingEvent(ev)) {
|
||||
const [_, body] = ev;
|
||||
const [, body] = ev;
|
||||
// ping
|
||||
console.log("ws:Ping", body);
|
||||
} else if (isWebSocketCloseEvent(ev)) {
|
||||
|
|
|
@ -211,7 +211,7 @@ export class BufReader implements Reader {
|
|||
* delim.
|
||||
* For simple uses, a Scanner may be more convenient.
|
||||
*/
|
||||
async readString(delim: string): Promise<string> {
|
||||
async readString(_delim: string): Promise<string> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
const { Buffer } = Deno;
|
||||
import { Reader, ReadResult } from "deno";
|
||||
import { test, assert, assertEqual } from "../testing/mod.ts";
|
||||
import { BufReader, BufState, BufWriter } from "./bufio.ts";
|
||||
import { BufReader, BufWriter } from "./bufio.ts";
|
||||
import * as iotest from "./iotest.ts";
|
||||
import { charCode, copyBytes, stringsReader } from "./util.ts";
|
||||
|
||||
|
@ -34,7 +34,10 @@ test(async function bufioReaderSimple() {
|
|||
assert.equal(s, data);
|
||||
});
|
||||
|
||||
type ReadMaker = { name: string; fn: (r: Reader) => Reader };
|
||||
interface ReadMaker {
|
||||
name: string;
|
||||
fn: (r: Reader) => Reader;
|
||||
}
|
||||
|
||||
const readMakers: ReadMaker[] = [
|
||||
{ name: "full", fn: r => r },
|
||||
|
@ -44,18 +47,6 @@ const readMakers: ReadMaker[] = [
|
|||
// { name: "timeout", fn: r => new iotest.TimeoutReader(r) },
|
||||
];
|
||||
|
||||
function readLines(b: BufReader): string {
|
||||
let s = "";
|
||||
while (true) {
|
||||
let s1 = b.readString("\n");
|
||||
if (s1 == null) {
|
||||
break; // EOF
|
||||
}
|
||||
s += s1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Call read to accumulate the text of a file
|
||||
async function reads(buf: BufReader, m: number): Promise<string> {
|
||||
const b = new Uint8Array(1000);
|
||||
|
@ -71,7 +62,10 @@ async function reads(buf: BufReader, m: number): Promise<string> {
|
|||
return decoder.decode(b.subarray(0, nb));
|
||||
}
|
||||
|
||||
type NamedBufReader = { name: string; fn: (r: BufReader) => Promise<string> };
|
||||
interface NamedBufReader {
|
||||
name: string;
|
||||
fn: (r: BufReader) => Promise<string>;
|
||||
}
|
||||
|
||||
const bufreaders: NamedBufReader[] = [
|
||||
{ name: "1", fn: (b: BufReader) => reads(b, 1) },
|
||||
|
@ -187,6 +181,7 @@ async function testReadLine(input: Uint8Array): Promise<void> {
|
|||
if (err == "EOF") {
|
||||
break;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
let want = testOutput.subarray(done, done + line.byteLength);
|
||||
assertEqual(
|
||||
line,
|
||||
|
@ -290,6 +285,7 @@ test(async function bufioWriter() {
|
|||
const data = new Uint8Array(8192);
|
||||
|
||||
for (let i = 0; i < data.byteLength; i++) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
data[i] = charCode(" ") + (i % (charCode("~") - charCode(" ")));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
const { Buffer } = Deno;
|
||||
import { Reader, ReadResult } from "deno";
|
||||
import { assert, assertEqual, runTests, test } from "../testing/mod.ts";
|
||||
import { assert, assertEqual, test } from "../testing/mod.ts";
|
||||
import {
|
||||
copyN,
|
||||
readInt,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { copyN } from "./ioutil.ts";
|
|||
test(async function ioStringWriter() {
|
||||
const w = new StringWriter("base");
|
||||
const r = new StringReader("0123456789");
|
||||
const n = await copyN(w, r, 4);
|
||||
await copyN(w, r, 4);
|
||||
assert.equal(w.toString(), "base0123");
|
||||
await copy(w, r);
|
||||
assert.equal(w.toString(), "base0123456789");
|
||||
|
|
|
@ -24,7 +24,7 @@ export class BaseHandler {
|
|||
this.formatter = options.formatter || DEFAULT_FORMATTER;
|
||||
}
|
||||
|
||||
handle(logRecord: LogRecord) {
|
||||
handle(logRecord: LogRecord): void {
|
||||
if (this.level > logRecord.level) return;
|
||||
|
||||
const msg = this.format(logRecord);
|
||||
|
@ -48,9 +48,9 @@ export class BaseHandler {
|
|||
});
|
||||
}
|
||||
|
||||
log(msg: string) {}
|
||||
async setup() {}
|
||||
async destroy() {}
|
||||
log(_msg: string): void {}
|
||||
async setup(): Promise<void> {}
|
||||
async destroy(): Promise<void> {}
|
||||
}
|
||||
|
||||
export class ConsoleHandler extends BaseHandler {
|
||||
|
@ -77,7 +77,7 @@ export class ConsoleHandler extends BaseHandler {
|
|||
return msg;
|
||||
}
|
||||
|
||||
log(msg: string) {
|
||||
log(msg: string): void {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ export abstract class WriterHandler extends BaseHandler {
|
|||
protected _writer: Writer;
|
||||
private _encoder = new TextEncoder();
|
||||
|
||||
log(msg: string) {
|
||||
log(msg: string): void {
|
||||
this._writer.write(this._encoder.encode(msg + "\n"));
|
||||
}
|
||||
}
|
||||
|
@ -104,13 +104,13 @@ export class FileHandler extends WriterHandler {
|
|||
this._filename = options.filename;
|
||||
}
|
||||
|
||||
async setup() {
|
||||
async setup(): Promise<void> {
|
||||
// open file in append mode - write only
|
||||
this._file = await open(this._filename, "a");
|
||||
this._writer = this._file;
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
async destroy(): Promise<void> {
|
||||
await this._file.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import { assertEqual, test } from "../testing/mod.ts";
|
||||
import { LogRecord, Logger } from "./logger.ts";
|
||||
import { LogLevel, getLevelName, getLevelByName } from "./levels.ts";
|
||||
import { BaseHandler } from "./handlers.ts";
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { BaseHandler } from "./handlers.ts";
|
|||
|
||||
export interface LogRecord {
|
||||
msg: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
args: any[];
|
||||
datetime: Date;
|
||||
level: number;
|
||||
|
@ -13,6 +14,7 @@ export interface LogRecord {
|
|||
export class Logger {
|
||||
level: number;
|
||||
levelName: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
handlers: any[];
|
||||
|
||||
constructor(levelName: string, handlers?: BaseHandler[]) {
|
||||
|
@ -22,7 +24,8 @@ export class Logger {
|
|||
this.handlers = handlers || [];
|
||||
}
|
||||
|
||||
_log(level: number, msg: string, ...args: any[]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
_log(level: number, msg: string, ...args: any[]): void {
|
||||
if (this.level > level) return;
|
||||
|
||||
// TODO: it'd be a good idea to make it immutable, so
|
||||
|
@ -41,23 +44,28 @@ export class Logger {
|
|||
});
|
||||
}
|
||||
|
||||
debug(msg: string, ...args: any[]) {
|
||||
return this._log(LogLevel.DEBUG, msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
debug(msg: string, ...args: any[]): void {
|
||||
this._log(LogLevel.DEBUG, msg, ...args);
|
||||
}
|
||||
|
||||
info(msg: string, ...args: any[]) {
|
||||
return this._log(LogLevel.INFO, msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
info(msg: string, ...args: any[]): void {
|
||||
this._log(LogLevel.INFO, msg, ...args);
|
||||
}
|
||||
|
||||
warning(msg: string, ...args: any[]) {
|
||||
return this._log(LogLevel.WARNING, msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
warning(msg: string, ...args: any[]): void {
|
||||
this._log(LogLevel.WARNING, msg, ...args);
|
||||
}
|
||||
|
||||
error(msg: string, ...args: any[]) {
|
||||
return this._log(LogLevel.ERROR, msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
error(msg: string, ...args: any[]): void {
|
||||
this._log(LogLevel.ERROR, msg, ...args);
|
||||
}
|
||||
|
||||
critical(msg: string, ...args: any[]) {
|
||||
return this._log(LogLevel.CRITICAL, msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
critical(msg: string, ...args: any[]): void {
|
||||
this._log(LogLevel.CRITICAL, msg, ...args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ test(function customHandler() {
|
|||
test(function logFunctions() {
|
||||
let handler: TestHandler;
|
||||
|
||||
const doLog = (level: string) => {
|
||||
const doLog = (level: string): void => {
|
||||
handler = new TestHandler(level);
|
||||
let logger = new Logger(level, [handler]);
|
||||
logger.debug("foo");
|
||||
|
|
39
log/mod.ts
39
log/mod.ts
|
@ -36,8 +36,8 @@ const DEFAULT_CONFIG: LogConfig = {
|
|||
};
|
||||
|
||||
const state = {
|
||||
handlers: new Map(),
|
||||
loggers: new Map(),
|
||||
handlers: new Map<string, BaseHandler>(),
|
||||
loggers: new Map<string, Logger>(),
|
||||
config: DEFAULT_CONFIG
|
||||
};
|
||||
|
||||
|
@ -48,18 +48,7 @@ export const handlers = {
|
|||
FileHandler
|
||||
};
|
||||
|
||||
export const debug = (msg: string, ...args: any[]) =>
|
||||
getLogger("default").debug(msg, ...args);
|
||||
export const info = (msg: string, ...args: any[]) =>
|
||||
getLogger("default").info(msg, ...args);
|
||||
export const warning = (msg: string, ...args: any[]) =>
|
||||
getLogger("default").warning(msg, ...args);
|
||||
export const error = (msg: string, ...args: any[]) =>
|
||||
getLogger("default").error(msg, ...args);
|
||||
export const critical = (msg: string, ...args: any[]) =>
|
||||
getLogger("default").critical(msg, ...args);
|
||||
|
||||
export function getLogger(name?: string) {
|
||||
export function getLogger(name?: string): Logger {
|
||||
if (!name) {
|
||||
return state.loggers.get("default");
|
||||
}
|
||||
|
@ -73,11 +62,27 @@ export function getLogger(name?: string) {
|
|||
return state.loggers.get(name);
|
||||
}
|
||||
|
||||
export function getHandler(name: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const debug = (msg: string, ...args: any[]): void =>
|
||||
getLogger("default").debug(msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const info = (msg: string, ...args: any[]): void =>
|
||||
getLogger("default").info(msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const warning = (msg: string, ...args: any[]): void =>
|
||||
getLogger("default").warning(msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const error = (msg: string, ...args: any[]): void =>
|
||||
getLogger("default").error(msg, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const critical = (msg: string, ...args: any[]): void =>
|
||||
getLogger("default").critical(msg, ...args);
|
||||
|
||||
export function getHandler(name: string): BaseHandler {
|
||||
return state.handlers.get(name);
|
||||
}
|
||||
|
||||
export async function setup(config: LogConfig) {
|
||||
export async function setup(config: LogConfig): Promise<void> {
|
||||
state.config = {
|
||||
handlers: { ...DEFAULT_CONFIG.handlers, ...config.handlers },
|
||||
loggers: { ...DEFAULT_CONFIG.loggers, ...config.loggers }
|
||||
|
@ -106,7 +111,7 @@ export async function setup(config: LogConfig) {
|
|||
for (const loggerName in loggers) {
|
||||
const loggerConfig = loggers[loggerName];
|
||||
const handlerNames = loggerConfig.handlers || [];
|
||||
const handlers = [];
|
||||
const handlers: BaseHandler[] = [];
|
||||
|
||||
handlerNames.forEach(handlerName => {
|
||||
if (state.handlers.has(handlerName)) {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import { assertEqual, test } from "../testing/mod.ts";
|
||||
import * as log from "./mod.ts";
|
||||
import { BaseHandler } from "./handlers.ts";
|
||||
import { LogRecord } from "./logger.ts";
|
||||
import { LogLevel } from "./levels.ts";
|
||||
|
||||
import "./handlers_test.ts";
|
||||
|
@ -18,7 +16,7 @@ import "./logger_test.ts";
|
|||
class TestHandler extends log.handlers.BaseHandler {
|
||||
public messages: string[] = [];
|
||||
|
||||
log(msg: string) {
|
||||
log(msg: string): void {
|
||||
this.messages.push(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export const types = new Map<string, string>();
|
|||
function populateMaps(
|
||||
extensions: Map<string, string[]>,
|
||||
types: Map<string, string>
|
||||
) {
|
||||
): void {
|
||||
const preference = ["nginx", "apache", undefined, "iana"];
|
||||
|
||||
for (const type of Object.keys(db)) {
|
||||
|
@ -98,6 +98,17 @@ export function charset(type: string): string | undefined {
|
|||
}
|
||||
}
|
||||
|
||||
/** Given an extension, lookup the appropriate media type for that extension.
|
||||
* Likely you should be using `contentType()` though instead.
|
||||
*/
|
||||
export function lookup(path: string): string | undefined {
|
||||
const extension = extname("x." + path)
|
||||
.toLowerCase()
|
||||
.substr(1);
|
||||
|
||||
return types.get(extension);
|
||||
}
|
||||
|
||||
/** Given an extension or media type, return the full `Content-Type` header
|
||||
* string. Returns `undefined` if not resolvable.
|
||||
*/
|
||||
|
@ -136,14 +147,3 @@ export function extension(type: string): string | undefined {
|
|||
|
||||
return exts[0];
|
||||
}
|
||||
|
||||
/** Given an extension, lookup the appropriate media type for that extension.
|
||||
* Likely you should be using `contentType()` though instead.
|
||||
*/
|
||||
export function lookup(path: string): string | undefined {
|
||||
const extension = extname("x." + path)
|
||||
.toLowerCase()
|
||||
.substr(1);
|
||||
|
||||
return types.get(extension);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/** FormFile object */
|
||||
export type FormFile = {
|
||||
export interface FormFile {
|
||||
/** filename */
|
||||
filename: string;
|
||||
/** content-type header value of file */
|
||||
|
@ -12,10 +12,11 @@ export type FormFile = {
|
|||
content?: Uint8Array;
|
||||
/** temporal file path. Set if file size is bigger than specified max-memory size at reading form */
|
||||
tempfile?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Type guard for FormFile */
|
||||
export function isFormFile(x): x is FormFile {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function isFormFile(x: any): x is FormFile {
|
||||
return (
|
||||
typeof x === "object" &&
|
||||
x.hasOwnProperty("filename") &&
|
||||
|
|
|
@ -17,7 +17,7 @@ import { TextProtoReader } from "../textproto/mod.ts";
|
|||
import { encoder } from "../strings/strings.ts";
|
||||
import * as path from "../fs/path.ts";
|
||||
|
||||
function randomBoundary() {
|
||||
function randomBoundary(): string {
|
||||
let boundary = "--------------------------";
|
||||
for (let i = 0; i < 24; i++) {
|
||||
boundary += Math.floor(Math.random() * 10).toString(16);
|
||||
|
@ -25,6 +25,185 @@ function randomBoundary() {
|
|||
return boundary;
|
||||
}
|
||||
|
||||
export function matchAfterPrefix(
|
||||
a: Uint8Array,
|
||||
prefix: Uint8Array,
|
||||
bufState: BufState
|
||||
): number {
|
||||
if (a.length === prefix.length) {
|
||||
if (bufState) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const c = a[prefix.length];
|
||||
if (
|
||||
c === " ".charCodeAt(0) ||
|
||||
c === "\t".charCodeAt(0) ||
|
||||
c === "\r".charCodeAt(0) ||
|
||||
c === "\n".charCodeAt(0) ||
|
||||
c === "-".charCodeAt(0)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function scanUntilBoundary(
|
||||
buf: Uint8Array,
|
||||
dashBoundary: Uint8Array,
|
||||
newLineDashBoundary: Uint8Array,
|
||||
total: number,
|
||||
state: BufState
|
||||
): [number, BufState] {
|
||||
if (total === 0) {
|
||||
if (bytesHasPrefix(buf, dashBoundary)) {
|
||||
switch (matchAfterPrefix(buf, dashBoundary, state)) {
|
||||
case -1:
|
||||
return [dashBoundary.length, null];
|
||||
case 0:
|
||||
return [0, null];
|
||||
case 1:
|
||||
return [0, "EOF"];
|
||||
}
|
||||
if (bytesHasPrefix(dashBoundary, buf)) {
|
||||
return [0, state];
|
||||
}
|
||||
}
|
||||
}
|
||||
const i = bytesFindIndex(buf, newLineDashBoundary);
|
||||
if (i >= 0) {
|
||||
switch (matchAfterPrefix(buf.slice(i), newLineDashBoundary, state)) {
|
||||
case -1:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
return [i + newLineDashBoundary.length, null];
|
||||
case 0:
|
||||
return [i, null];
|
||||
case 1:
|
||||
return [i, "EOF"];
|
||||
}
|
||||
}
|
||||
if (bytesHasPrefix(newLineDashBoundary, buf)) {
|
||||
return [0, state];
|
||||
}
|
||||
const j = bytesFindLastIndex(buf, newLineDashBoundary.slice(0, 1));
|
||||
if (j >= 0 && bytesHasPrefix(newLineDashBoundary, buf.slice(j))) {
|
||||
return [j, null];
|
||||
}
|
||||
return [buf.length, state];
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
class PartReader implements Reader, Closer {
|
||||
n: number = 0;
|
||||
total: number = 0;
|
||||
bufState: BufState = null;
|
||||
index = i++;
|
||||
|
||||
constructor(private mr: MultipartReader, public readonly headers: Headers) {}
|
||||
|
||||
async read(p: Uint8Array): Promise<ReadResult> {
|
||||
const br = this.mr.bufReader;
|
||||
const returnResult = (nread: number, bufState: BufState): ReadResult => {
|
||||
if (bufState && bufState !== "EOF") {
|
||||
throw bufState;
|
||||
}
|
||||
return { nread, eof: bufState === "EOF" };
|
||||
};
|
||||
if (this.n === 0 && !this.bufState) {
|
||||
const [peek] = await br.peek(br.buffered());
|
||||
const [n, state] = scanUntilBoundary(
|
||||
peek,
|
||||
this.mr.dashBoundary,
|
||||
this.mr.newLineDashBoundary,
|
||||
this.total,
|
||||
this.bufState
|
||||
);
|
||||
this.n = n;
|
||||
this.bufState = state;
|
||||
if (this.n === 0 && !this.bufState) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
const [, state] = await br.peek(peek.length + 1);
|
||||
this.bufState = state;
|
||||
if (this.bufState === "EOF") {
|
||||
this.bufState = new RangeError("unexpected eof");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.n === 0) {
|
||||
return returnResult(0, this.bufState);
|
||||
}
|
||||
|
||||
let n = 0;
|
||||
if (p.byteLength > this.n) {
|
||||
n = this.n;
|
||||
}
|
||||
const buf = p.slice(0, n);
|
||||
const [nread] = await this.mr.bufReader.readFull(buf);
|
||||
p.set(buf);
|
||||
this.total += nread;
|
||||
this.n -= nread;
|
||||
if (this.n === 0) {
|
||||
return returnResult(n, this.bufState);
|
||||
}
|
||||
return returnResult(n, null);
|
||||
}
|
||||
|
||||
close(): void {}
|
||||
|
||||
private contentDisposition: string;
|
||||
private contentDispositionParams: { [key: string]: string };
|
||||
|
||||
private getContentDispositionParams(): { [key: string]: string } {
|
||||
if (this.contentDispositionParams) return this.contentDispositionParams;
|
||||
const cd = this.headers.get("content-disposition");
|
||||
const params = {};
|
||||
const comps = cd.split(";");
|
||||
this.contentDisposition = comps[0];
|
||||
comps
|
||||
.slice(1)
|
||||
.map(v => v.trim())
|
||||
.map(kv => {
|
||||
const [k, v] = kv.split("=");
|
||||
if (v) {
|
||||
const s = v.charAt(0);
|
||||
const e = v.charAt(v.length - 1);
|
||||
if ((s === e && s === '"') || s === "'") {
|
||||
params[k] = v.substr(1, v.length - 2);
|
||||
} else {
|
||||
params[k] = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
return (this.contentDispositionParams = params);
|
||||
}
|
||||
|
||||
get fileName(): string {
|
||||
return this.getContentDispositionParams()["filename"];
|
||||
}
|
||||
|
||||
get formName(): string {
|
||||
const p = this.getContentDispositionParams();
|
||||
if (this.contentDisposition === "form-data") {
|
||||
return p["name"];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function skipLWSPChar(u: Uint8Array): Uint8Array {
|
||||
const ret = new Uint8Array(u.length);
|
||||
const sp = " ".charCodeAt(0);
|
||||
const ht = "\t".charCodeAt(0);
|
||||
let j = 0;
|
||||
for (let i = 0; i < u.length; i++) {
|
||||
if (u[i] === sp || u[i] === ht) continue;
|
||||
ret[j++] = u[i];
|
||||
}
|
||||
return ret.slice(0, j);
|
||||
}
|
||||
|
||||
/** Reader for parsing multipart/form-data */
|
||||
export class MultipartReader {
|
||||
readonly newLine = encoder.encode("\r\n");
|
||||
|
@ -125,7 +304,7 @@ export class MultipartReader {
|
|||
break;
|
||||
}
|
||||
if (state) {
|
||||
throw new Error("aa" + state.toString());
|
||||
throw new Error(`aa${state.toString()}`);
|
||||
}
|
||||
if (this.isBoundaryDelimiterLine(line)) {
|
||||
this.partsRead++;
|
||||
|
@ -155,7 +334,7 @@ export class MultipartReader {
|
|||
}
|
||||
}
|
||||
|
||||
private isFinalBoundary(line: Uint8Array) {
|
||||
private isFinalBoundary(line: Uint8Array): boolean {
|
||||
if (!bytesHasPrefix(line, this.dashBoundaryDash)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -163,7 +342,7 @@ export class MultipartReader {
|
|||
return rest.length === 0 || bytesEqual(skipLWSPChar(rest), this.newLine);
|
||||
}
|
||||
|
||||
private isBoundaryDelimiterLine(line: Uint8Array) {
|
||||
private isBoundaryDelimiterLine(line: Uint8Array): boolean {
|
||||
if (!bytesHasPrefix(line, this.dashBoundary)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -172,183 +351,6 @@ export class MultipartReader {
|
|||
}
|
||||
}
|
||||
|
||||
function skipLWSPChar(u: Uint8Array): Uint8Array {
|
||||
const ret = new Uint8Array(u.length);
|
||||
const sp = " ".charCodeAt(0);
|
||||
const ht = "\t".charCodeAt(0);
|
||||
let j = 0;
|
||||
for (let i = 0; i < u.length; i++) {
|
||||
if (u[i] === sp || u[i] === ht) continue;
|
||||
ret[j++] = u[i];
|
||||
}
|
||||
return ret.slice(0, j);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
class PartReader implements Reader, Closer {
|
||||
n: number = 0;
|
||||
total: number = 0;
|
||||
bufState: BufState = null;
|
||||
index = i++;
|
||||
|
||||
constructor(private mr: MultipartReader, public readonly headers: Headers) {}
|
||||
|
||||
async read(p: Uint8Array): Promise<ReadResult> {
|
||||
const br = this.mr.bufReader;
|
||||
const returnResult = (nread: number, bufState: BufState): ReadResult => {
|
||||
if (bufState && bufState !== "EOF") {
|
||||
throw bufState;
|
||||
}
|
||||
return { nread, eof: bufState === "EOF" };
|
||||
};
|
||||
if (this.n === 0 && !this.bufState) {
|
||||
const [peek] = await br.peek(br.buffered());
|
||||
const [n, state] = scanUntilBoundary(
|
||||
peek,
|
||||
this.mr.dashBoundary,
|
||||
this.mr.newLineDashBoundary,
|
||||
this.total,
|
||||
this.bufState
|
||||
);
|
||||
this.n = n;
|
||||
this.bufState = state;
|
||||
if (this.n === 0 && !this.bufState) {
|
||||
const [_, state] = await br.peek(peek.length + 1);
|
||||
this.bufState = state;
|
||||
if (this.bufState === "EOF") {
|
||||
this.bufState = new RangeError("unexpected eof");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.n === 0) {
|
||||
return returnResult(0, this.bufState);
|
||||
}
|
||||
|
||||
let n = 0;
|
||||
if (p.byteLength > this.n) {
|
||||
n = this.n;
|
||||
}
|
||||
const buf = p.slice(0, n);
|
||||
const [nread] = await this.mr.bufReader.readFull(buf);
|
||||
p.set(buf);
|
||||
this.total += nread;
|
||||
this.n -= nread;
|
||||
if (this.n === 0) {
|
||||
return returnResult(n, this.bufState);
|
||||
}
|
||||
return returnResult(n, null);
|
||||
}
|
||||
|
||||
close(): void {}
|
||||
|
||||
private contentDisposition: string;
|
||||
private contentDispositionParams: { [key: string]: string };
|
||||
|
||||
private getContentDispositionParams() {
|
||||
if (this.contentDispositionParams) return this.contentDispositionParams;
|
||||
const cd = this.headers.get("content-disposition");
|
||||
const params = {};
|
||||
const comps = cd.split(";");
|
||||
this.contentDisposition = comps[0];
|
||||
comps
|
||||
.slice(1)
|
||||
.map(v => v.trim())
|
||||
.map(kv => {
|
||||
const [k, v] = kv.split("=");
|
||||
if (v) {
|
||||
const s = v.charAt(0);
|
||||
const e = v.charAt(v.length - 1);
|
||||
if ((s === e && s === '"') || s === "'") {
|
||||
params[k] = v.substr(1, v.length - 2);
|
||||
} else {
|
||||
params[k] = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
return (this.contentDispositionParams = params);
|
||||
}
|
||||
|
||||
get fileName(): string {
|
||||
return this.getContentDispositionParams()["filename"];
|
||||
}
|
||||
|
||||
get formName(): string {
|
||||
const p = this.getContentDispositionParams();
|
||||
if (this.contentDisposition === "form-data") {
|
||||
return p["name"];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function scanUntilBoundary(
|
||||
buf: Uint8Array,
|
||||
dashBoundary: Uint8Array,
|
||||
newLineDashBoundary: Uint8Array,
|
||||
total: number,
|
||||
state: BufState
|
||||
): [number, BufState] {
|
||||
if (total === 0) {
|
||||
if (bytesHasPrefix(buf, dashBoundary)) {
|
||||
switch (matchAfterPrefix(buf, dashBoundary, state)) {
|
||||
case -1:
|
||||
return [dashBoundary.length, null];
|
||||
case 0:
|
||||
return [0, null];
|
||||
case 1:
|
||||
return [0, "EOF"];
|
||||
}
|
||||
if (bytesHasPrefix(dashBoundary, buf)) {
|
||||
return [0, state];
|
||||
}
|
||||
}
|
||||
}
|
||||
const i = bytesFindIndex(buf, newLineDashBoundary);
|
||||
if (i >= 0) {
|
||||
switch (matchAfterPrefix(buf.slice(i), newLineDashBoundary, state)) {
|
||||
case -1:
|
||||
return [i + newLineDashBoundary.length, null];
|
||||
case 0:
|
||||
return [i, null];
|
||||
case 1:
|
||||
return [i, "EOF"];
|
||||
}
|
||||
}
|
||||
if (bytesHasPrefix(newLineDashBoundary, buf)) {
|
||||
return [0, state];
|
||||
}
|
||||
const j = bytesFindLastIndex(buf, newLineDashBoundary.slice(0, 1));
|
||||
if (j >= 0 && bytesHasPrefix(newLineDashBoundary, buf.slice(j))) {
|
||||
return [j, null];
|
||||
}
|
||||
return [buf.length, state];
|
||||
}
|
||||
|
||||
export function matchAfterPrefix(
|
||||
a: Uint8Array,
|
||||
prefix: Uint8Array,
|
||||
bufState: BufState
|
||||
): number {
|
||||
if (a.length === prefix.length) {
|
||||
if (bufState) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const c = a[prefix.length];
|
||||
if (
|
||||
c === " ".charCodeAt(0) ||
|
||||
c === "\t".charCodeAt(0) ||
|
||||
c === "\r".charCodeAt(0) ||
|
||||
c === "\n".charCodeAt(0) ||
|
||||
c === "-".charCodeAt(0)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class PartWriter implements Writer {
|
||||
closed = false;
|
||||
private readonly partHeader: string;
|
||||
|
@ -389,9 +391,9 @@ class PartWriter implements Writer {
|
|||
}
|
||||
}
|
||||
|
||||
function checkBoundary(b: string) {
|
||||
function checkBoundary(b: string): string {
|
||||
if (b.length < 1 || b.length > 70) {
|
||||
throw new Error("invalid boundary length: " + b.length);
|
||||
throw new Error(`invalid boundary length: ${b.length}`);
|
||||
}
|
||||
const end = b.length - 1;
|
||||
for (let i = 0; i < end; i++) {
|
||||
|
@ -407,7 +409,7 @@ function checkBoundary(b: string) {
|
|||
export class MultipartWriter {
|
||||
private readonly _boundary: string;
|
||||
|
||||
get boundary() {
|
||||
get boundary(): string {
|
||||
return this._boundary;
|
||||
}
|
||||
|
||||
|
@ -462,12 +464,16 @@ export class MultipartWriter {
|
|||
return this.createPart(h);
|
||||
}
|
||||
|
||||
async writeField(field: string, value: string) {
|
||||
async writeField(field: string, value: string): Promise<void> {
|
||||
const f = await this.createFormField(field);
|
||||
await f.write(encoder.encode(value));
|
||||
}
|
||||
|
||||
async writeFile(field: string, filename: string, file: Reader) {
|
||||
async writeFile(
|
||||
field: string,
|
||||
filename: string,
|
||||
file: Reader
|
||||
): Promise<void> {
|
||||
const f = await this.createFormFile(field, filename);
|
||||
await copy(f, file);
|
||||
}
|
||||
|
@ -477,7 +483,7 @@ export class MultipartWriter {
|
|||
}
|
||||
|
||||
/** Close writer. No additional data can be writen to stream */
|
||||
async close() {
|
||||
async close(): Promise<void> {
|
||||
if (this.isClosed) {
|
||||
throw new Error("multipart: writer is closed");
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { FormFile, isFormFile } from "./formfile.ts";
|
|||
import { StringWriter } from "../io/writers.ts";
|
||||
|
||||
const e = new TextEncoder();
|
||||
const d = new TextDecoder();
|
||||
const boundary = "--abcde";
|
||||
const dashBoundary = e.encode("--" + boundary);
|
||||
const nlDashBoundary = e.encode("\r\n--" + boundary);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
// This script formats the given source files. If the files are omitted, it
|
||||
// formats the all files in the repository.
|
||||
const { args, platform, readAll, lstat, exit, run, readFile, writeFile } = Deno;
|
||||
const { args, readAll, lstat, exit, readFile, writeFile } = Deno;
|
||||
import { xrun } from "./util.ts";
|
||||
import { parse } from "../flags/mod.ts";
|
||||
import { prettier, prettierPlugins } from "./prettier.ts";
|
||||
|
@ -67,7 +67,7 @@ async function getSourceFiles(args: string[]): Promise<string[]> {
|
|||
}
|
||||
|
||||
// Filters out the files which contains any pattern in the given ignoreList.
|
||||
function filterIgnoreList(files: string[], ignoreList: string[]) {
|
||||
function filterIgnoreList(files: string[], ignoreList: string[]): string[] {
|
||||
return files.filter(path =>
|
||||
ignoreList.every(pattern => !path.includes(pattern))
|
||||
);
|
||||
|
@ -203,7 +203,7 @@ async function formatSourceFiles(
|
|||
exit(0);
|
||||
}
|
||||
|
||||
async function main(opts) {
|
||||
async function main(opts): Promise<void> {
|
||||
const { help, ignore, check, _: args } = opts;
|
||||
|
||||
if (help) {
|
||||
|
|
|
@ -5,7 +5,9 @@ const { readAll } = Deno;
|
|||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
async function run(args: string[]) {
|
||||
async function run(
|
||||
args: string[]
|
||||
): Promise<{ stdout: string; code: number | undefined }> {
|
||||
const p = xrun({ args, stdout: "piped" });
|
||||
|
||||
const stdout = decoder.decode(await readAll(p.stdout));
|
||||
|
@ -33,7 +35,7 @@ function normalizeOutput(output: string): string {
|
|||
.join("\n");
|
||||
}
|
||||
|
||||
async function clearTestdataChanges() {
|
||||
async function clearTestdataChanges(): Promise<void> {
|
||||
await xrun({ args: ["git", "checkout", testdata] }).status();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import "./vendor/parser_babylon.js";
|
|||
import "./vendor/parser_markdown.js";
|
||||
|
||||
// TODO: provide decent type declarions for these
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { prettier, prettierPlugins } = window as any;
|
||||
|
||||
export { prettier, prettierPlugins };
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
const { platform, run } = Deno;
|
||||
|
||||
// Runs a command in cross-platform way
|
||||
export function xrun(opts) {
|
||||
export function xrun(opts): Deno.Process {
|
||||
return run({
|
||||
...opts,
|
||||
args: platform.os === "win" ? ["cmd.exe", "/c", ...opts.args] : opts.args
|
||||
|
|
187
testing/diff.ts
187
testing/diff.ts
|
@ -15,7 +15,7 @@ const REMOVED = 1;
|
|||
const COMMON = 2;
|
||||
const ADDED = 3;
|
||||
|
||||
function createCommon<T>(A: T[], B: T[], reverse?: boolean) {
|
||||
function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] {
|
||||
const common = [];
|
||||
if (A.length === 0 || B.length === 0) return [];
|
||||
for (let i = 0; i < Math.min(A.length, B.length); i += 1) {
|
||||
|
@ -30,97 +30,7 @@ function createCommon<T>(A: T[], B: T[], reverse?: boolean) {
|
|||
return common;
|
||||
}
|
||||
|
||||
export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
|
||||
function backTrace<T>(
|
||||
A: T[],
|
||||
B: T[],
|
||||
current: FarthestPoint,
|
||||
swapped: boolean
|
||||
) {
|
||||
const M = A.length;
|
||||
const N = B.length;
|
||||
const result = [];
|
||||
let a = M - 1;
|
||||
let b = N - 1;
|
||||
let j = routes[current.id];
|
||||
let type = routes[current.id + diffTypesPtrOffset];
|
||||
while (true) {
|
||||
if (!j && !type) break;
|
||||
const prev = j;
|
||||
if (type === REMOVED) {
|
||||
result.unshift({
|
||||
type: (swapped ? "removed" : "added") as DiffType,
|
||||
value: B[b]
|
||||
});
|
||||
b -= 1;
|
||||
} else if (type === ADDED) {
|
||||
result.unshift({
|
||||
type: (swapped ? "added" : "removed") as DiffType,
|
||||
value: A[a]
|
||||
});
|
||||
a -= 1;
|
||||
} else {
|
||||
result.unshift({ type: "common" as DiffType, value: A[a] });
|
||||
a -= 1;
|
||||
b -= 1;
|
||||
}
|
||||
j = routes[prev];
|
||||
type = routes[prev + diffTypesPtrOffset];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function createFP(
|
||||
slide: FarthestPoint,
|
||||
down: FarthestPoint,
|
||||
k: number,
|
||||
M: number,
|
||||
N: number
|
||||
): FarthestPoint {
|
||||
if (slide && slide.y === -1 && (down && down.y === -1))
|
||||
return { y: 0, id: 0 };
|
||||
if (
|
||||
(down && down.y === -1) ||
|
||||
k === M ||
|
||||
(slide && slide.y) > (down && down.y) + 1
|
||||
) {
|
||||
const prev = slide.id;
|
||||
ptr++;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = ADDED;
|
||||
return { y: slide.y, id: ptr };
|
||||
} else {
|
||||
const prev = down.id;
|
||||
ptr++;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = REMOVED;
|
||||
return { y: down.y + 1, id: ptr };
|
||||
}
|
||||
}
|
||||
|
||||
function snake<T>(
|
||||
k: number,
|
||||
slide: FarthestPoint,
|
||||
down: FarthestPoint,
|
||||
offset: number,
|
||||
A: T[],
|
||||
B: T[]
|
||||
) {
|
||||
const M = A.length;
|
||||
const N = B.length;
|
||||
if (k < -N || M < k) return { y: -1 };
|
||||
const fp = createFP(slide, down, k, M, N);
|
||||
while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) {
|
||||
const prev = fp.id;
|
||||
ptr++;
|
||||
fp.id = ptr;
|
||||
fp.y += 1;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = COMMON;
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
export default function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
|
||||
const prefixCommon = createCommon(A, B);
|
||||
const suffixCommon = createCommon(
|
||||
A.slice(prefixCommon.length),
|
||||
|
@ -159,6 +69,99 @@ export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
|
|||
const diffTypesPtrOffset = routes.length / 2;
|
||||
let ptr = 0;
|
||||
let p = -1;
|
||||
|
||||
function backTrace<T>(
|
||||
A: T[],
|
||||
B: T[],
|
||||
current: FarthestPoint,
|
||||
swapped: boolean
|
||||
): Array<{
|
||||
type: DiffType;
|
||||
value: T;
|
||||
}> {
|
||||
const M = A.length;
|
||||
const N = B.length;
|
||||
const result = [];
|
||||
let a = M - 1;
|
||||
let b = N - 1;
|
||||
let j = routes[current.id];
|
||||
let type = routes[current.id + diffTypesPtrOffset];
|
||||
while (true) {
|
||||
if (!j && !type) break;
|
||||
const prev = j;
|
||||
if (type === REMOVED) {
|
||||
result.unshift({
|
||||
type: (swapped ? "removed" : "added") as DiffType,
|
||||
value: B[b]
|
||||
});
|
||||
b -= 1;
|
||||
} else if (type === ADDED) {
|
||||
result.unshift({
|
||||
type: (swapped ? "added" : "removed") as DiffType,
|
||||
value: A[a]
|
||||
});
|
||||
a -= 1;
|
||||
} else {
|
||||
result.unshift({ type: "common" as DiffType, value: A[a] });
|
||||
a -= 1;
|
||||
b -= 1;
|
||||
}
|
||||
j = routes[prev];
|
||||
type = routes[prev + diffTypesPtrOffset];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function createFP(
|
||||
slide: FarthestPoint,
|
||||
down: FarthestPoint,
|
||||
k: number,
|
||||
M: number
|
||||
): FarthestPoint {
|
||||
if (slide && slide.y === -1 && (down && down.y === -1))
|
||||
return { y: 0, id: 0 };
|
||||
if (
|
||||
(down && down.y === -1) ||
|
||||
k === M ||
|
||||
(slide && slide.y) > (down && down.y) + 1
|
||||
) {
|
||||
const prev = slide.id;
|
||||
ptr++;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = ADDED;
|
||||
return { y: slide.y, id: ptr };
|
||||
} else {
|
||||
const prev = down.id;
|
||||
ptr++;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = REMOVED;
|
||||
return { y: down.y + 1, id: ptr };
|
||||
}
|
||||
}
|
||||
|
||||
function snake<T>(
|
||||
k: number,
|
||||
slide: FarthestPoint,
|
||||
down: FarthestPoint,
|
||||
_offset: number,
|
||||
A: T[],
|
||||
B: T[]
|
||||
): FarthestPoint {
|
||||
const M = A.length;
|
||||
const N = B.length;
|
||||
if (k < -N || M < k) return { y: -1, id: -1 };
|
||||
const fp = createFP(slide, down, k, M);
|
||||
while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) {
|
||||
const prev = fp.id;
|
||||
ptr++;
|
||||
fp.id = ptr;
|
||||
fp.y += 1;
|
||||
routes[ptr] = prev;
|
||||
routes[ptr + diffTypesPtrOffset] = COMMON;
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
while (fp[delta + offset].y < N) {
|
||||
p = p + 1;
|
||||
for (let k = -p; k < delta; ++k) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Refs = any[];
|
||||
export type Optional<T> = { [K in keyof T]?: T[K] };
|
||||
|
||||
|
@ -32,7 +33,7 @@ export interface Config {
|
|||
}
|
||||
|
||||
export type Printer = (
|
||||
val: any,
|
||||
val: unknown,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
depth: number,
|
||||
|
@ -66,12 +67,15 @@ interface BasicValueOptions {
|
|||
* Explicitly comparing typeof constructor to function avoids undefined as name
|
||||
* when mock identity-obj-proxy returns the key as the value for any key.
|
||||
*/
|
||||
const getConstructorName = (val: new (...args: any[]) => any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const getConstructorName = (val: new (...args: any[]) => any): string =>
|
||||
(typeof val.constructor === "function" && val.constructor.name) || "Object";
|
||||
|
||||
/* global window */
|
||||
/** Is val is equal to global window object? Works even if it does not exist :) */
|
||||
const isWindow = (val: any) => typeof window !== "undefined" && val === window;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isWindow = (val: any): val is Window =>
|
||||
typeof window !== "undefined" && val === window;
|
||||
|
||||
const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
|
||||
|
||||
|
@ -116,11 +120,12 @@ function printError(val: Error): string {
|
|||
* data-types in JS.
|
||||
*/
|
||||
function printBasicValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
val: any,
|
||||
{ printFunctionName, escapeRegex, escapeString }: BasicValueOptions
|
||||
): string | null {
|
||||
if (val === true || val === false) {
|
||||
return "" + val;
|
||||
return String(val);
|
||||
}
|
||||
if (val === undefined) {
|
||||
return "undefined";
|
||||
|
@ -136,9 +141,9 @@ function printBasicValue(
|
|||
}
|
||||
if (typeOf === "string") {
|
||||
if (escapeString) {
|
||||
return '"' + val.replace(/"|\\/g, "\\$&") + '"';
|
||||
return `"${val.replace(/"|\\/g, "\\$&")}"`;
|
||||
}
|
||||
return '"' + val + '"';
|
||||
return `"${val}"`;
|
||||
}
|
||||
if (typeOf === "function") {
|
||||
return printFunction(val, printFunctionName);
|
||||
|
@ -185,95 +190,8 @@ function printBasicValue(
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles more complex objects ( such as objects with circular references.
|
||||
* maps and sets etc )
|
||||
*/
|
||||
function printComplexValue(
|
||||
val: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
depth: number,
|
||||
refs: Refs,
|
||||
hasCalledToJSON?: boolean
|
||||
): string {
|
||||
if (refs.indexOf(val) !== -1) {
|
||||
return "[Circular]";
|
||||
}
|
||||
refs = refs.slice();
|
||||
refs.push(val);
|
||||
|
||||
const hitMaxDepth = ++depth > config.maxDepth;
|
||||
const { min, callToJSON } = config;
|
||||
|
||||
if (
|
||||
callToJSON &&
|
||||
!hitMaxDepth &&
|
||||
val.toJSON &&
|
||||
typeof val.toJSON === "function" &&
|
||||
!hasCalledToJSON
|
||||
) {
|
||||
return printer(val.toJSON(), config, indentation, depth, refs, true);
|
||||
}
|
||||
|
||||
const toStringed = toString.call(val);
|
||||
if (toStringed === "[object Arguments]") {
|
||||
return hitMaxDepth
|
||||
? "[Arguments]"
|
||||
: (min ? "" : "Arguments ") +
|
||||
"[" +
|
||||
printListItems(val, config, indentation, depth, refs, printer) +
|
||||
"]";
|
||||
}
|
||||
if (isToStringedArrayType(toStringed)) {
|
||||
return hitMaxDepth
|
||||
? "[" + val.constructor.name + "]"
|
||||
: (min ? "" : val.constructor.name + " ") +
|
||||
"[" +
|
||||
printListItems(val, config, indentation, depth, refs, printer) +
|
||||
"]";
|
||||
}
|
||||
if (toStringed === "[object Map]") {
|
||||
return hitMaxDepth
|
||||
? "[Map]"
|
||||
: "Map {" +
|
||||
printIteratorEntries(
|
||||
val.entries(),
|
||||
config,
|
||||
indentation,
|
||||
depth,
|
||||
refs,
|
||||
printer,
|
||||
" => "
|
||||
) +
|
||||
"}";
|
||||
}
|
||||
if (toStringed === "[object Set]") {
|
||||
return hitMaxDepth
|
||||
? "[Set]"
|
||||
: "Set {" +
|
||||
printIteratorValues(
|
||||
val.values(),
|
||||
config,
|
||||
indentation,
|
||||
depth,
|
||||
refs,
|
||||
printer
|
||||
) +
|
||||
"}";
|
||||
}
|
||||
|
||||
// Avoid failure to serialize global window object in jsdom test environment.
|
||||
// For example, not even relevant if window is prop of React element.
|
||||
return hitMaxDepth || isWindow(val)
|
||||
? "[" + getConstructorName(val) + "]"
|
||||
: (min ? "" : getConstructorName(val) + " ") +
|
||||
"{" +
|
||||
printObjectProperties(val, config, indentation, depth, refs, printer) +
|
||||
"}";
|
||||
}
|
||||
|
||||
function printer(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
val: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
|
@ -285,6 +203,7 @@ function printer(
|
|||
if (basicResult !== null) {
|
||||
return basicResult;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
return printComplexValue(
|
||||
val,
|
||||
config,
|
||||
|
@ -295,30 +214,44 @@ function printer(
|
|||
);
|
||||
}
|
||||
|
||||
const getConfig = (options: Options): Config => ({
|
||||
...options,
|
||||
indent: options.min ? "" : createIndent(options.indent),
|
||||
spacingInner: options.min ? " " : "\n",
|
||||
spacingOuter: options.min ? "" : "\n"
|
||||
});
|
||||
/**
|
||||
* Return items (for example, of an array)
|
||||
* with spacing, indentation, and comma
|
||||
* without surrounding punctuation (for example, brackets)
|
||||
*/
|
||||
function printListItems(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
list: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
depth: number,
|
||||
refs: Refs,
|
||||
printer: Printer
|
||||
): string {
|
||||
let result = "";
|
||||
|
||||
function createIndent(indent: number): string {
|
||||
return new Array(indent + 1).join(" ");
|
||||
}
|
||||
if (list.length) {
|
||||
result += config.spacingOuter;
|
||||
|
||||
const getKeysOfEnumerableProperties = (object: {}) => {
|
||||
const keys: Array<string | symbol> = Object.keys(object).sort();
|
||||
const indentationNext = indentation + config.indent;
|
||||
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
Object.getOwnPropertySymbols(object).forEach(symbol => {
|
||||
if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) {
|
||||
keys.push(symbol);
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
result +=
|
||||
indentationNext +
|
||||
printer(list[i], config, indentationNext, depth, refs);
|
||||
|
||||
if (i < list.length - 1) {
|
||||
result += "," + config.spacingInner;
|
||||
} else if (!config.min) {
|
||||
result += ",";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
result += config.spacingOuter + indentation;
|
||||
}
|
||||
|
||||
return keys;
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return entries (for example, of a map)
|
||||
|
@ -326,6 +259,7 @@ const getKeysOfEnumerableProperties = (object: {}) => {
|
|||
* without surrounding punctuation (for example, braces)
|
||||
*/
|
||||
function printIteratorEntries(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
iterator: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
|
@ -384,6 +318,7 @@ function printIteratorEntries(
|
|||
* without surrounding punctuation (braces or brackets)
|
||||
*/
|
||||
function printIteratorValues(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
iterator: Iterator<any>,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
|
@ -419,43 +354,19 @@ function printIteratorValues(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return items (for example, of an array)
|
||||
* with spacing, indentation, and comma
|
||||
* without surrounding punctuation (for example, brackets)
|
||||
*/
|
||||
function printListItems(
|
||||
list: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
depth: number,
|
||||
refs: Refs,
|
||||
printer: Printer
|
||||
): string {
|
||||
let result = "";
|
||||
const getKeysOfEnumerableProperties = (object: {}): Array<string | symbol> => {
|
||||
const keys: Array<string | symbol> = Object.keys(object).sort();
|
||||
|
||||
if (list.length) {
|
||||
result += config.spacingOuter;
|
||||
|
||||
const indentationNext = indentation + config.indent;
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
result +=
|
||||
indentationNext +
|
||||
printer(list[i], config, indentationNext, depth, refs);
|
||||
|
||||
if (i < list.length - 1) {
|
||||
result += "," + config.spacingInner;
|
||||
} else if (!config.min) {
|
||||
result += ",";
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
Object.getOwnPropertySymbols(object).forEach(symbol => {
|
||||
if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) {
|
||||
keys.push(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
result += config.spacingOuter + indentation;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return properties of an object
|
||||
|
@ -504,11 +415,113 @@ function printObjectProperties(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles more complex objects ( such as objects with circular references.
|
||||
* maps and sets etc )
|
||||
*/
|
||||
function printComplexValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
val: any,
|
||||
config: Config,
|
||||
indentation: string,
|
||||
depth: number,
|
||||
refs: Refs,
|
||||
hasCalledToJSON?: boolean
|
||||
): string {
|
||||
if (refs.indexOf(val) !== -1) {
|
||||
return "[Circular]";
|
||||
}
|
||||
refs = refs.slice();
|
||||
refs.push(val);
|
||||
|
||||
const hitMaxDepth = ++depth > config.maxDepth;
|
||||
const { min, callToJSON } = config;
|
||||
|
||||
if (
|
||||
callToJSON &&
|
||||
!hitMaxDepth &&
|
||||
val.toJSON &&
|
||||
typeof val.toJSON === "function" &&
|
||||
!hasCalledToJSON
|
||||
) {
|
||||
return printer(val.toJSON(), config, indentation, depth, refs, true);
|
||||
}
|
||||
|
||||
const toStringed = toString.call(val);
|
||||
if (toStringed === "[object Arguments]") {
|
||||
return hitMaxDepth
|
||||
? "[Arguments]"
|
||||
: (min ? "" : "Arguments ") +
|
||||
"[" +
|
||||
printListItems(val, config, indentation, depth, refs, printer) +
|
||||
"]";
|
||||
}
|
||||
if (isToStringedArrayType(toStringed)) {
|
||||
return hitMaxDepth
|
||||
? `[${val.constructor.name}]`
|
||||
: (min ? "" : `${val.constructor.name} `) +
|
||||
"[" +
|
||||
printListItems(val, config, indentation, depth, refs, printer) +
|
||||
"]";
|
||||
}
|
||||
if (toStringed === "[object Map]") {
|
||||
return hitMaxDepth
|
||||
? "[Map]"
|
||||
: "Map {" +
|
||||
printIteratorEntries(
|
||||
val.entries(),
|
||||
config,
|
||||
indentation,
|
||||
depth,
|
||||
refs,
|
||||
printer,
|
||||
" => "
|
||||
) +
|
||||
"}";
|
||||
}
|
||||
if (toStringed === "[object Set]") {
|
||||
return hitMaxDepth
|
||||
? "[Set]"
|
||||
: "Set {" +
|
||||
printIteratorValues(
|
||||
val.values(),
|
||||
config,
|
||||
indentation,
|
||||
depth,
|
||||
refs,
|
||||
printer
|
||||
) +
|
||||
"}";
|
||||
}
|
||||
|
||||
// Avoid failure to serialize global window object in jsdom test environment.
|
||||
// For example, not even relevant if window is prop of React element.
|
||||
return hitMaxDepth || isWindow(val)
|
||||
? "[" + getConstructorName(val) + "]"
|
||||
: (min ? "" : getConstructorName(val) + " ") +
|
||||
"{" +
|
||||
printObjectProperties(val, config, indentation, depth, refs, printer) +
|
||||
"}";
|
||||
}
|
||||
|
||||
// TODO this is better done with `.padStart()`
|
||||
function createIndent(indent: number): string {
|
||||
return new Array(indent + 1).join(" ");
|
||||
}
|
||||
|
||||
const getConfig = (options: Options): Config => ({
|
||||
...options,
|
||||
indent: options.min ? "" : createIndent(options.indent),
|
||||
spacingInner: options.min ? " " : "\n",
|
||||
spacingOuter: options.min ? "" : "\n"
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a presentation string of your `val` object
|
||||
* @param val any potential JavaScript object
|
||||
* @param options Custom settings
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function format(val: any, options: Optional<Options> = {}): string {
|
||||
const opts = Object.keys(DEFAULT_OPTIONS).reduce(
|
||||
(acc: Options, k: keyof Options) => {
|
||||
|
|
|
@ -9,17 +9,19 @@
|
|||
import { test, assertEqual } from "./mod.ts";
|
||||
import { format } from "./format.ts";
|
||||
|
||||
function returnArguments(..._args: Array<unknown>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any
|
||||
function returnArguments(...args: any[]): IArguments {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
function MyObject(value: unknown) {
|
||||
function MyObject(value: unknown): void {
|
||||
// @ts-ignore
|
||||
this.name = value;
|
||||
}
|
||||
|
||||
class MyArray<T> extends Array<T> {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
const createVal = () => [
|
||||
{
|
||||
id: "8658c1d0-9eda-4a90-95e1-8001e8eb6036",
|
||||
|
@ -32,6 +34,7 @@ const createVal = () => [
|
|||
}
|
||||
];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
const createExpected = () =>
|
||||
[
|
||||
"Array [",
|
||||
|
@ -58,7 +61,7 @@ test({
|
|||
test({
|
||||
name: "prints an empty array",
|
||||
fn() {
|
||||
const val: any[] = [];
|
||||
const val: unknown[] = [];
|
||||
assertEqual(format(val), "Array []");
|
||||
}
|
||||
});
|
||||
|
@ -151,7 +154,7 @@ test({
|
|||
name: "prints an anonymous callback function",
|
||||
fn() {
|
||||
let val;
|
||||
function f(cb: () => void) {
|
||||
function f(cb: () => void): void {
|
||||
val = cb;
|
||||
}
|
||||
// tslint:disable-next-line:no-empty
|
||||
|
@ -164,7 +167,7 @@ test({
|
|||
name: "prints an anonymous assigned function",
|
||||
fn() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
const val = () => {};
|
||||
const val = (): void => {};
|
||||
const formatted = format(val);
|
||||
assertEqual(
|
||||
formatted === "[Function anonymous]" || formatted === "[Function val]",
|
||||
|
@ -177,7 +180,7 @@ test({
|
|||
name: "prints a named function",
|
||||
fn() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
const val = function named() {};
|
||||
const val = function named(): void {};
|
||||
assertEqual(format(val), "[Function named]");
|
||||
}
|
||||
});
|
||||
|
@ -185,7 +188,7 @@ test({
|
|||
test({
|
||||
name: "prints a named generator function",
|
||||
fn() {
|
||||
const val = function* generate() {
|
||||
const val = function* generate(): IterableIterator<number> {
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
|
@ -198,7 +201,7 @@ test({
|
|||
name: "can customize function names",
|
||||
fn() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
const val = function named() {};
|
||||
const val = function named(): void {};
|
||||
assertEqual(
|
||||
format(val, {
|
||||
printFunctionName: false
|
||||
|
@ -248,6 +251,7 @@ test({
|
|||
test({
|
||||
name: "prints a map with non-string keys",
|
||||
fn() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const val = new Map<any, any>([
|
||||
[false, "boolean"],
|
||||
["false", "string"],
|
||||
|
@ -373,6 +377,7 @@ test({
|
|||
test({
|
||||
name: "prints an object with properties and symbols",
|
||||
fn() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const val: any = {};
|
||||
val[Symbol("symbol1")] = "value2";
|
||||
val[Symbol("symbol2")] = "value3";
|
||||
|
@ -388,7 +393,7 @@ test({
|
|||
name:
|
||||
"prints an object without non-enumerable properties which have string key",
|
||||
fn() {
|
||||
const val: any = {
|
||||
const val = {
|
||||
enumerable: true
|
||||
};
|
||||
const key = "non-enumerable";
|
||||
|
@ -404,7 +409,7 @@ test({
|
|||
name:
|
||||
"prints an object without non-enumerable properties which have symbol key",
|
||||
fn() {
|
||||
const val: any = {
|
||||
const val = {
|
||||
enumerable: true
|
||||
};
|
||||
const key = Symbol("non-enumerable");
|
||||
|
@ -609,6 +614,7 @@ test({
|
|||
test({
|
||||
name: "prints circular references",
|
||||
fn() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const val: any = {};
|
||||
val.prop = val;
|
||||
assertEqual(format(val), 'Object {\n "prop": [Circular],\n}');
|
||||
|
@ -787,6 +793,7 @@ test({
|
|||
name: "calls toJSON on Sets",
|
||||
fn() {
|
||||
const set = new Set([1]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(set as any).toJSON = () => "map";
|
||||
assertEqual(format(set), '"map"');
|
||||
}
|
||||
|
|
101
testing/mod.ts
101
testing/mod.ts
|
@ -3,9 +3,37 @@
|
|||
import { green, red } from "../colors/mod.ts";
|
||||
|
||||
interface Constructor {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
new (...args: any[]): any;
|
||||
}
|
||||
|
||||
export function equal(c: unknown, d: unknown): boolean {
|
||||
const seen = new Map();
|
||||
return (function compare(a: unknown, b: unknown) {
|
||||
if (Object.is(a, 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;
|
||||
}
|
||||
const merged = { ...a, ...b };
|
||||
for (const key in merged) {
|
||||
type Key = keyof typeof merged;
|
||||
if (!compare(a && a[key as Key], b && b[key as Key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
seen.set(a, b);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})(c, d);
|
||||
}
|
||||
|
||||
const assertions = {
|
||||
/** Make an assertion, if not `true`, then throw. */
|
||||
assert(expr: boolean, msg = ""): void {
|
||||
|
@ -78,6 +106,7 @@ const assertions = {
|
|||
* Forcefully throws a failed assertion
|
||||
*/
|
||||
fail(msg?: string): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`);
|
||||
},
|
||||
|
||||
|
@ -163,33 +192,6 @@ export const assert = assertions.assert as Assert;
|
|||
*/
|
||||
export const assertEqual = assert.equal;
|
||||
|
||||
export function equal(c: unknown, d: unknown): boolean {
|
||||
const seen = new Map();
|
||||
return (function compare(a: unknown, b: unknown) {
|
||||
if (Object.is(a, 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;
|
||||
}
|
||||
const merged = { ...a, ...b };
|
||||
for (const key in merged) {
|
||||
type Key = keyof typeof merged;
|
||||
if (!compare(a && a[key as Key], b && b[key as Key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
seen.set(a, b);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})(c, d);
|
||||
}
|
||||
|
||||
export type TestFunction = () => void | Promise<void>;
|
||||
|
||||
export interface TestDefinition {
|
||||
|
@ -203,14 +205,20 @@ let filterRegExp: RegExp | null;
|
|||
const tests: TestDefinition[] = [];
|
||||
|
||||
let filtered = 0;
|
||||
const ignored = 0;
|
||||
const measured = 0;
|
||||
|
||||
// Must be called before any test() that needs to be filtered.
|
||||
export function setFilter(s: string): void {
|
||||
filterRegExp = new RegExp(s, "i");
|
||||
}
|
||||
|
||||
function filter(name: string): boolean {
|
||||
if (filterRegExp) {
|
||||
return filterRegExp.test(name);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function test(t: TestDefinition | TestFunction): void {
|
||||
const fn: TestFunction = typeof t === "function" ? t : t.fn;
|
||||
const name: string = t.name;
|
||||
|
@ -225,21 +233,8 @@ export function test(t: TestDefinition | TestFunction): void {
|
|||
}
|
||||
}
|
||||
|
||||
function filter(name: string): boolean {
|
||||
if (filterRegExp) {
|
||||
return filterRegExp.test(name);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function red_failed() {
|
||||
return red("FAILED");
|
||||
}
|
||||
|
||||
function green_ok() {
|
||||
return green("ok");
|
||||
}
|
||||
const RED_FAILED = red("FAILED");
|
||||
const GREEN_OK = green("ok");
|
||||
|
||||
interface TestStats {
|
||||
filtered: number;
|
||||
|
@ -261,7 +256,7 @@ interface TestResults {
|
|||
cases: Map<number, TestResult>;
|
||||
}
|
||||
|
||||
function createTestResults(tests: Array<TestDefinition>): TestResults {
|
||||
function createTestResults(tests: TestDefinition[]): TestResults {
|
||||
return tests.reduce(
|
||||
(acc: TestResults, { name }: TestDefinition, i: number): TestResults => {
|
||||
acc.keys.set(name, i);
|
||||
|
@ -274,10 +269,10 @@ function createTestResults(tests: Array<TestDefinition>): TestResults {
|
|||
|
||||
function report(result: TestResult): void {
|
||||
if (result.ok) {
|
||||
console.log(`test ${result.name} ... ${green_ok()}`);
|
||||
console.log(`test ${result.name} ... ${GREEN_OK}`);
|
||||
} else if (result.error) {
|
||||
console.error(
|
||||
`test ${result.name} ... ${red_failed()}\n${result.error.stack}`
|
||||
`test ${result.name} ... ${RED_FAILED}\n${result.error.stack}`
|
||||
);
|
||||
} else {
|
||||
console.log(`test ${result.name} ... unresolved`);
|
||||
|
@ -302,7 +297,7 @@ function printResults(
|
|||
}
|
||||
// Attempting to match the output of Rust's test runner.
|
||||
console.log(
|
||||
`\ntest result: ${stats.failed ? red_failed() : green_ok()}. ` +
|
||||
`\ntest result: ${stats.failed ? RED_FAILED : GREEN_OK}. ` +
|
||||
`${stats.passed} passed; ${stats.failed} failed; ` +
|
||||
`${stats.ignored} ignored; ${stats.measured} measured; ` +
|
||||
`${stats.filtered} filtered out\n`
|
||||
|
@ -342,7 +337,7 @@ async function createTestCase(
|
|||
function initTestCases(
|
||||
stats: TestStats,
|
||||
results: TestResults,
|
||||
tests: Array<TestDefinition>
|
||||
tests: TestDefinition[]
|
||||
): Array<Promise<void>> {
|
||||
return tests.map(createTestCase.bind(null, stats, results));
|
||||
}
|
||||
|
@ -350,7 +345,7 @@ function initTestCases(
|
|||
async function runTestsParallel(
|
||||
stats: TestStats,
|
||||
results: TestResults,
|
||||
tests: Array<TestDefinition>
|
||||
tests: TestDefinition[]
|
||||
): Promise<void> {
|
||||
try {
|
||||
await Promise.all(initTestCases(stats, results, tests));
|
||||
|
@ -362,7 +357,7 @@ async function runTestsParallel(
|
|||
|
||||
async function runTestsSerial(
|
||||
stats: TestStats,
|
||||
tests: Array<TestDefinition>
|
||||
tests: TestDefinition[]
|
||||
): Promise<void> {
|
||||
for (const { fn, name } of tests) {
|
||||
// See https://github.com/denoland/deno/pull/1452
|
||||
|
@ -371,10 +366,10 @@ async function runTestsSerial(
|
|||
try {
|
||||
await fn();
|
||||
stats.passed++;
|
||||
console.log("...", green_ok());
|
||||
console.log("...", GREEN_OK);
|
||||
console.groupEnd();
|
||||
} catch (err) {
|
||||
console.log("...", red_failed());
|
||||
console.log("...", RED_FAILED);
|
||||
console.groupEnd();
|
||||
console.error(err.stack);
|
||||
stats.failed++;
|
||||
|
|
|
@ -15,7 +15,7 @@ function createStr(v: unknown): string {
|
|||
}
|
||||
}
|
||||
|
||||
function createColor(diffType: DiffType) {
|
||||
function createColor(diffType: DiffType): (s: string) => string {
|
||||
switch (diffType) {
|
||||
case "added":
|
||||
return (s: string) => green(bold(s));
|
||||
|
@ -26,7 +26,7 @@ function createColor(diffType: DiffType) {
|
|||
}
|
||||
}
|
||||
|
||||
function createSign(diffType: DiffType) {
|
||||
function createSign(diffType: DiffType): string {
|
||||
switch (diffType) {
|
||||
case "added":
|
||||
return "+ ";
|
||||
|
@ -37,7 +37,7 @@ function createSign(diffType: DiffType) {
|
|||
}
|
||||
}
|
||||
|
||||
function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>) {
|
||||
function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>): string[] {
|
||||
const messages = [];
|
||||
messages.push("");
|
||||
messages.push("");
|
||||
|
@ -55,7 +55,7 @@ function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>) {
|
|||
return messages;
|
||||
}
|
||||
|
||||
export function assertEqual(actual: unknown, expected: unknown) {
|
||||
export function assertEqual(actual: unknown, expected: unknown): void {
|
||||
if (equal(actual, expected)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { test, assert } from "./mod.ts";
|
|||
import { red, green, white, gray, bold } from "../colors/mod.ts";
|
||||
import { assertEqual } from "./pretty.ts";
|
||||
|
||||
const createHeader = () => [
|
||||
const createHeader = (): string[] => [
|
||||
"",
|
||||
"",
|
||||
` ${gray(bold("[Diff]"))} ${red(bold("Left"))} / ${green(bold("Right"))}`,
|
||||
|
@ -12,8 +12,8 @@ const createHeader = () => [
|
|||
""
|
||||
];
|
||||
|
||||
const added = (s: string) => green(bold(s));
|
||||
const removed = (s: string) => red(bold(s));
|
||||
const added: (s: string) => string = (s: string): string => green(bold(s));
|
||||
const removed: (s: string) => string = (s: string): string => red(bold(s));
|
||||
|
||||
test({
|
||||
name: "pass case",
|
||||
|
|
|
@ -35,8 +35,6 @@ test(function testingAssertEqual() {
|
|||
});
|
||||
|
||||
test(function testingAssertFail() {
|
||||
let didThrow = false;
|
||||
|
||||
assert.throws(assert.fail, Error, "Failed assertion.");
|
||||
assert.throws(
|
||||
() => {
|
||||
|
|
|
@ -22,6 +22,17 @@ export class ProtocolError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
export function append(a: Uint8Array, b: Uint8Array): Uint8Array {
|
||||
if (a == null) {
|
||||
return b;
|
||||
} else {
|
||||
const output = new Uint8Array(a.length + b.length);
|
||||
output.set(a, 0);
|
||||
output.set(b, a.length);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export class TextProtoReader {
|
||||
constructor(readonly r: BufReader) {}
|
||||
|
||||
|
@ -137,14 +148,3 @@ export class TextProtoReader {
|
|||
return [line, null];
|
||||
}
|
||||
}
|
||||
|
||||
export function append(a: Uint8Array, b: Uint8Array): Uint8Array {
|
||||
if (a == null) {
|
||||
return b;
|
||||
} else {
|
||||
const output = new Uint8Array(a.length + b.length);
|
||||
output.set(a, 0);
|
||||
output.set(b, a.length);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ test(async function textprotoAppend() {
|
|||
|
||||
test(async function textprotoReadEmpty() {
|
||||
let r = reader("");
|
||||
let [m, err] = await r.readMIMEHeader();
|
||||
let [, err] = await r.readMIMEHeader();
|
||||
// Should not crash!
|
||||
assertEqual(err, "EOF");
|
||||
});
|
||||
|
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"noEmit": true,
|
||||
"noLib": true,
|
||||
"pretty": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"target": "esnext"
|
||||
},
|
||||
"include": ["./**/*.ts"]
|
||||
}
|
Loading…
Reference in a new issue