1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-27 01:29:14 -05:00

Add eslint for linting (#235)

This commit is contained in:
Kitson Kelly 2019-03-05 11:53:35 +11:00 committed by Ryan Dahl
parent 385f866a54
commit c0390ade3d
41 changed files with 734 additions and 649 deletions

4
.eslintignore Normal file
View file

@ -0,0 +1,4 @@
/flags/
/fs/
/http/
/ws/

23
.eslintrc.json Normal file
View 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": "^_" }
]
}
}

4
.gitignore vendored
View file

@ -1,4 +1,6 @@
.DS_Store .DS_Store
.idea .idea
tsconfig.json
deno.d.ts deno.d.ts
node_modules
package.json
package-lock.json

View file

@ -1,31 +1,39 @@
variables: 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: 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' - job: "Mac"
pool: pool:
vmImage: 'Ubuntu-16.04' vmImage: "macOS-10.13"
steps: steps:
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION) - script: npm -g install eslint typescript@$(TS_VERSION) @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/' - script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION)
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read - script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/'
- script: deno format.ts --allow-run --allow-write --allow-read --check - 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' - job: "Windows"
pool: pool:
vmImage: 'macOS-10.13' vmImage: "vs2017-win2016"
steps: steps:
- script: curl -L https://deno.land/x/install/install.sh | sh -s $(DENO_VERSION) - bash: npm install eslint typescript@$(TS_VERSION) @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier
- script: echo '##vso[task.prependpath]$(HOME)/.deno/bin/' - powershell: iwr https://deno.land/x/install/install.ps1 -out install.ps1; .\install.ps1 $(DENO_VERSION)
- script: deno test.ts --allow-run --allow-net --allow-write --allow-read - bash: echo "##vso[task.prependpath]C:\Users\VssAdministrator\.deno\\bin"
- script: deno format.ts --allow-run --allow-write --allow-read --check - bash: npx eslint **/*.ts
- bash: deno.exe format.ts --allow-run --allow-write --allow-read --check
- job: 'Windows' - bash: deno.exe test.ts --allow-run --allow-net --allow-write --allow-read
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

View file

@ -14,7 +14,7 @@ bench({
runs: 100, runs: 100,
func(b: BenchmarkTimer) { func(b: BenchmarkTimer) {
b.start(); b.start();
for (let i: number = 0; i < 1e6; i++); for (let i = 0; i < 1e6; i++);
b.stop(); b.stop();
} }
}); });

View file

@ -14,10 +14,10 @@ export interface BenchmarkTimer {
} }
/** Defines a benchmark through a named function. */ /** Defines a benchmark through a named function. */
export type BenchmarkFunction = { export interface BenchmarkFunction {
(b: BenchmarkTimer): void | Promise<void>; (b: BenchmarkTimer): void | Promise<void>;
name: string; name: string;
}; }
/** Defines a benchmark definition with configurable runs. */ /** Defines a benchmark definition with configurable runs. */
export interface BenchmarkDefinition { 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. */ /** Registers a benchmark as a candidate for the runBenchmarks executor. */
export function bench( export function bench(
@ -95,27 +95,27 @@ export async function runBenchmarks({
skip = /^\s*$/ skip = /^\s*$/
}: BenchmarkRunOptions = {}): Promise<void> { }: BenchmarkRunOptions = {}): Promise<void> {
// Filtering candidates by the "only" and "skip" constraint // 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) ({ name }) => only.test(name) && !skip.test(name)
); );
// Init main counters and error flag // Init main counters and error flag
const filtered: number = candidates.length - benchmarks.length; const filtered = candidates.length - benchmarks.length;
let measured: number = 0; let measured = 0;
let failed: boolean = false; let failed = false;
// Setting up a shared benchmark clock and timer // Setting up a shared benchmark clock and timer
const clock: BenchmarkClock = { start: NaN, stop: NaN }; const clock: BenchmarkClock = { start: NaN, stop: NaN };
const b: BenchmarkTimer = createBenchmarkTimer(clock); const b = createBenchmarkTimer(clock);
// Iterating given benchmark definitions (await-in-loop) // Iterating given benchmark definitions (await-in-loop)
console.log( console.log(
"running", "running",
benchmarks.length, benchmarks.length,
`benchmark${benchmarks.length === 1 ? " ..." : "s ..."}` `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 // See https://github.com/denoland/deno/pull/1452 about groupCollapsed
console.groupCollapsed(`benchmark ${name} ... `); console.groupCollapsed(`benchmark ${name} ... `);
// Trying benchmark.func // Trying benchmark.func
let result: string; let result = "";
try { try {
if (runs === 1) { if (runs === 1) {
// b is a benchmark timer interfacing an unset (NaN) benchmark clock // b is a benchmark timer interfacing an unset (NaN) benchmark clock
@ -126,7 +126,7 @@ export async function runBenchmarks({
} else if (runs > 1) { } else if (runs > 1) {
// Averaging runs // Averaging runs
let pendingRuns = runs; let pendingRuns = runs;
let totalMs: number = 0; let totalMs = 0;
// Would be better 2 not run these serially // Would be better 2 not run these serially
while (true) { while (true) {
// b is a benchmark timer interfacing an unset (NaN) benchmark clock // b is a benchmark timer interfacing an unset (NaN) benchmark clock

View file

@ -6,19 +6,19 @@ import "example.ts";
test(async function benching() { test(async function benching() {
bench(function forIncrementX1e9(b: BenchmarkTimer) { bench(function forIncrementX1e9(b: BenchmarkTimer) {
b.start(); b.start();
for (let i: number = 0; i < 1e9; i++); for (let i = 0; i < 1e9; i++);
b.stop(); b.stop();
}); });
bench(function forDecrementX1e9(b: BenchmarkTimer) { bench(function forDecrementX1e9(b: BenchmarkTimer) {
b.start(); b.start();
for (let i: number = 1e9; i > 0; i--); for (let i = 1e9; i > 0; i--);
b.stop(); b.stop();
}); });
bench(async function forAwaitFetchDenolandX10(b: BenchmarkTimer) { bench(async function forAwaitFetchDenolandX10(b: BenchmarkTimer) {
b.start(); b.start();
for (let i: number = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
await fetch("https://deno.land/"); await fetch("https://deno.land/");
} }
b.stop(); b.stop();
@ -36,7 +36,7 @@ test(async function benching() {
runs: 100, runs: 100,
func(b: BenchmarkTimer) { func(b: BenchmarkTimer) {
b.start(); b.start();
for (let i: number = 0; i < 1e6; i++); for (let i = 0; i < 1e6; i++);
b.stop(); b.stop();
} }
}); });

View file

@ -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 **/ /** 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]; const e = pat[pat.length - 1];
for (let i = a.length - 1; i >= 0; i--) { for (let i = a.length - 1; i >= 0; i--) {
if (a[i] !== e) continue; if (a[i] !== e) continue;

View file

@ -9,7 +9,7 @@ interface Code {
let enabled = !noColor; let enabled = !noColor;
export function setEnabled(value: boolean) { export function setEnabled(value: boolean): void {
if (noColor) { if (noColor) {
return; 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 return enabled
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
: str; : str;

View file

@ -13,13 +13,13 @@ export function parseDate(dateStr: string, format: DateFormat): Date {
if (format === "mm-dd-yyyy") { if (format === "mm-dd-yyyy") {
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/; 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") { } else if (format === "dd-mm-yyyy") {
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/; 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") { } else if (format === "yyyy-mm-dd") {
const datePattern = /^(\d{4})-(\d{2})-(\d{2})$/; const datePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
[, y, m, d] = datePattern.exec(dateStr); [, y, m, d] = datePattern.exec(dateStr)!;
} else { } else {
throw new Error("Invalid date format!"); throw new Error("Invalid date format!");
} }
@ -50,22 +50,22 @@ export function parseDateTime(
if (format === "mm-dd-yyyy hh:mm") { if (format === "mm-dd-yyyy hh:mm") {
const datePattern = /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2})$/; 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") { } else if (format === "dd-mm-yyyy hh:mm") {
const datePattern = /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2})$/; 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") { } else if (format === "yyyy-mm-dd hh:mm") {
const datePattern = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/; 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") { } else if (format === "hh:mm mm-dd-yyyy") {
const datePattern = /^(\d{2}):(\d{2}) (\d{2})-(\d{2})-(\d{4})$/; 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") { } else if (format === "hh:mm dd-mm-yyyy") {
const datePattern = /^(\d{2}):(\d{2}) (\d{2})-(\d{2})-(\d{4})$/; 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") { } else if (format === "hh:mm yyyy-mm-dd") {
const datePattern = /^(\d{2}):(\d{2}) (\d{4})-(\d{2})-(\d{2})$/; 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 { } else {
throw new Error("Invalid datetime format!"); throw new Error("Invalid datetime format!");
} }

View file

@ -31,6 +31,7 @@ test(function parseDateTime() {
test(function invalidParseDateTimeFormatThrows() { test(function invalidParseDateTimeFormatThrows() {
try { try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(datetime as any).parseDateTime("2019-01-01 00:00", "x-y-z"); (datetime as any).parseDateTime("2019-01-01 00:00", "x-y-z");
assert(false, "no exception was thrown"); assert(false, "no exception was thrown");
} catch (e) { } catch (e) {
@ -55,6 +56,7 @@ test(function parseDate() {
test(function invalidParseDateFormatThrows() { test(function invalidParseDateFormatThrows() {
try { try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(datetime as any).parseDate("2019-01-01", "x-y-z"); (datetime as any).parseDate("2019-01-01", "x-y-z");
assert(false, "no exception was thrown"); assert(false, "no exception was thrown");
} catch (e) { } catch (e) {

View file

@ -9,7 +9,7 @@ function pathBase(p: string): string {
return parts[parts.length - 1]; return parts[parts.length - 1];
} }
async function main() { async function main(): Promise<void> {
const token = env()["GIST_TOKEN"]; const token = env()["GIST_TOKEN"];
if (!token) { if (!token) {
console.error("GIST_TOKEN environmental variable not set."); console.error("GIST_TOKEN environmental variable not set.");
@ -46,7 +46,7 @@ async function main() {
headers: [ headers: [
["Content-Type", "application/json"], ["Content-Type", "application/json"],
["User-Agent", "Deno-Gist"], ["User-Agent", "Deno-Gist"],
["Authorization", "token " + token] ["Authorization", `token ${token}`]
], ],
body body
}); });

View file

@ -6,7 +6,7 @@ import {
isWebSocketPingEvent isWebSocketPingEvent
} from "https://deno.land/x/ws/mod.ts"; } 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"); console.log("websocket server is running on 0.0.0.0:8080");
for await (const req of serve("0.0.0.0:8080")) { for await (const req of serve("0.0.0.0:8080")) {
if (req.url === "/ws") { if (req.url === "/ws") {
@ -22,7 +22,7 @@ async function main() {
// binary message // binary message
console.log("ws:Binary", ev); console.log("ws:Binary", ev);
} else if (isWebSocketPingEvent(ev)) { } else if (isWebSocketPingEvent(ev)) {
const [_, body] = ev; const [, body] = ev;
// ping // ping
console.log("ws:Ping", body); console.log("ws:Ping", body);
} else if (isWebSocketCloseEvent(ev)) { } else if (isWebSocketCloseEvent(ev)) {

View file

@ -211,7 +211,7 @@ export class BufReader implements Reader {
* delim. * delim.
* For simple uses, a Scanner may be more convenient. * 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"); throw new Error("Not implemented");
} }

View file

@ -6,7 +6,7 @@
const { Buffer } = Deno; const { Buffer } = Deno;
import { Reader, ReadResult } from "deno"; import { Reader, ReadResult } from "deno";
import { test, assert, assertEqual } from "../testing/mod.ts"; 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 * as iotest from "./iotest.ts";
import { charCode, copyBytes, stringsReader } from "./util.ts"; import { charCode, copyBytes, stringsReader } from "./util.ts";
@ -34,7 +34,10 @@ test(async function bufioReaderSimple() {
assert.equal(s, data); assert.equal(s, data);
}); });
type ReadMaker = { name: string; fn: (r: Reader) => Reader }; interface ReadMaker {
name: string;
fn: (r: Reader) => Reader;
}
const readMakers: ReadMaker[] = [ const readMakers: ReadMaker[] = [
{ name: "full", fn: r => r }, { name: "full", fn: r => r },
@ -44,18 +47,6 @@ const readMakers: ReadMaker[] = [
// { name: "timeout", fn: r => new iotest.TimeoutReader(r) }, // { 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 // Call read to accumulate the text of a file
async function reads(buf: BufReader, m: number): Promise<string> { async function reads(buf: BufReader, m: number): Promise<string> {
const b = new Uint8Array(1000); 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)); 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[] = [ const bufreaders: NamedBufReader[] = [
{ name: "1", fn: (b: BufReader) => reads(b, 1) }, { name: "1", fn: (b: BufReader) => reads(b, 1) },
@ -187,6 +181,7 @@ async function testReadLine(input: Uint8Array): Promise<void> {
if (err == "EOF") { if (err == "EOF") {
break; break;
} }
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
let want = testOutput.subarray(done, done + line.byteLength); let want = testOutput.subarray(done, done + line.byteLength);
assertEqual( assertEqual(
line, line,
@ -290,6 +285,7 @@ test(async function bufioWriter() {
const data = new Uint8Array(8192); const data = new Uint8Array(8192);
for (let i = 0; i < data.byteLength; i++) { for (let i = 0; i < data.byteLength; i++) {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
data[i] = charCode(" ") + (i % (charCode("~") - charCode(" "))); data[i] = charCode(" ") + (i % (charCode("~") - charCode(" ")));
} }

View file

@ -1,7 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
const { Buffer } = Deno; const { Buffer } = Deno;
import { Reader, ReadResult } from "deno"; import { Reader, ReadResult } from "deno";
import { assert, assertEqual, runTests, test } from "../testing/mod.ts"; import { assert, assertEqual, test } from "../testing/mod.ts";
import { import {
copyN, copyN,
readInt, readInt,

View file

@ -7,7 +7,7 @@ import { copyN } from "./ioutil.ts";
test(async function ioStringWriter() { test(async function ioStringWriter() {
const w = new StringWriter("base"); const w = new StringWriter("base");
const r = new StringReader("0123456789"); const r = new StringReader("0123456789");
const n = await copyN(w, r, 4); await copyN(w, r, 4);
assert.equal(w.toString(), "base0123"); assert.equal(w.toString(), "base0123");
await copy(w, r); await copy(w, r);
assert.equal(w.toString(), "base0123456789"); assert.equal(w.toString(), "base0123456789");

View file

@ -24,7 +24,7 @@ export class BaseHandler {
this.formatter = options.formatter || DEFAULT_FORMATTER; this.formatter = options.formatter || DEFAULT_FORMATTER;
} }
handle(logRecord: LogRecord) { handle(logRecord: LogRecord): void {
if (this.level > logRecord.level) return; if (this.level > logRecord.level) return;
const msg = this.format(logRecord); const msg = this.format(logRecord);
@ -48,9 +48,9 @@ export class BaseHandler {
}); });
} }
log(msg: string) {} log(_msg: string): void {}
async setup() {} async setup(): Promise<void> {}
async destroy() {} async destroy(): Promise<void> {}
} }
export class ConsoleHandler extends BaseHandler { export class ConsoleHandler extends BaseHandler {
@ -77,7 +77,7 @@ export class ConsoleHandler extends BaseHandler {
return msg; return msg;
} }
log(msg: string) { log(msg: string): void {
console.log(msg); console.log(msg);
} }
} }
@ -86,7 +86,7 @@ export abstract class WriterHandler extends BaseHandler {
protected _writer: Writer; protected _writer: Writer;
private _encoder = new TextEncoder(); private _encoder = new TextEncoder();
log(msg: string) { log(msg: string): void {
this._writer.write(this._encoder.encode(msg + "\n")); this._writer.write(this._encoder.encode(msg + "\n"));
} }
} }
@ -104,13 +104,13 @@ export class FileHandler extends WriterHandler {
this._filename = options.filename; this._filename = options.filename;
} }
async setup() { async setup(): Promise<void> {
// open file in append mode - write only // open file in append mode - write only
this._file = await open(this._filename, "a"); this._file = await open(this._filename, "a");
this._writer = this._file; this._writer = this._file;
} }
async destroy() { async destroy(): Promise<void> {
await this._file.close(); await this._file.close();
} }
} }

View file

@ -1,6 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { assertEqual, test } from "../testing/mod.ts"; import { assertEqual, test } from "../testing/mod.ts";
import { LogRecord, Logger } from "./logger.ts";
import { LogLevel, getLevelName, getLevelByName } from "./levels.ts"; import { LogLevel, getLevelName, getLevelByName } from "./levels.ts";
import { BaseHandler } from "./handlers.ts"; import { BaseHandler } from "./handlers.ts";

View file

@ -4,6 +4,7 @@ import { BaseHandler } from "./handlers.ts";
export interface LogRecord { export interface LogRecord {
msg: string; msg: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args: any[]; args: any[];
datetime: Date; datetime: Date;
level: number; level: number;
@ -13,6 +14,7 @@ export interface LogRecord {
export class Logger { export class Logger {
level: number; level: number;
levelName: string; levelName: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handlers: any[]; handlers: any[];
constructor(levelName: string, handlers?: BaseHandler[]) { constructor(levelName: string, handlers?: BaseHandler[]) {
@ -22,7 +24,8 @@ export class Logger {
this.handlers = handlers || []; 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; if (this.level > level) return;
// TODO: it'd be a good idea to make it immutable, so // TODO: it'd be a good idea to make it immutable, so
@ -41,23 +44,28 @@ export class Logger {
}); });
} }
debug(msg: string, ...args: any[]) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
return this._log(LogLevel.DEBUG, msg, ...args); debug(msg: string, ...args: any[]): void {
this._log(LogLevel.DEBUG, msg, ...args);
} }
info(msg: string, ...args: any[]) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
return this._log(LogLevel.INFO, msg, ...args); info(msg: string, ...args: any[]): void {
this._log(LogLevel.INFO, msg, ...args);
} }
warning(msg: string, ...args: any[]) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
return this._log(LogLevel.WARNING, msg, ...args); warning(msg: string, ...args: any[]): void {
this._log(LogLevel.WARNING, msg, ...args);
} }
error(msg: string, ...args: any[]) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
return this._log(LogLevel.ERROR, msg, ...args); error(msg: string, ...args: any[]): void {
this._log(LogLevel.ERROR, msg, ...args);
} }
critical(msg: string, ...args: any[]) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
return this._log(LogLevel.CRITICAL, msg, ...args); critical(msg: string, ...args: any[]): void {
this._log(LogLevel.CRITICAL, msg, ...args);
} }
} }

View file

@ -53,7 +53,7 @@ test(function customHandler() {
test(function logFunctions() { test(function logFunctions() {
let handler: TestHandler; let handler: TestHandler;
const doLog = (level: string) => { const doLog = (level: string): void => {
handler = new TestHandler(level); handler = new TestHandler(level);
let logger = new Logger(level, [handler]); let logger = new Logger(level, [handler]);
logger.debug("foo"); logger.debug("foo");

View file

@ -36,8 +36,8 @@ const DEFAULT_CONFIG: LogConfig = {
}; };
const state = { const state = {
handlers: new Map(), handlers: new Map<string, BaseHandler>(),
loggers: new Map(), loggers: new Map<string, Logger>(),
config: DEFAULT_CONFIG config: DEFAULT_CONFIG
}; };
@ -48,18 +48,7 @@ export const handlers = {
FileHandler FileHandler
}; };
export const debug = (msg: string, ...args: any[]) => export function getLogger(name?: string): Logger {
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) {
if (!name) { if (!name) {
return state.loggers.get("default"); return state.loggers.get("default");
} }
@ -73,11 +62,27 @@ export function getLogger(name?: string) {
return state.loggers.get(name); 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); return state.handlers.get(name);
} }
export async function setup(config: LogConfig) { export async function setup(config: LogConfig): Promise<void> {
state.config = { state.config = {
handlers: { ...DEFAULT_CONFIG.handlers, ...config.handlers }, handlers: { ...DEFAULT_CONFIG.handlers, ...config.handlers },
loggers: { ...DEFAULT_CONFIG.loggers, ...config.loggers } loggers: { ...DEFAULT_CONFIG.loggers, ...config.loggers }
@ -106,7 +111,7 @@ export async function setup(config: LogConfig) {
for (const loggerName in loggers) { for (const loggerName in loggers) {
const loggerConfig = loggers[loggerName]; const loggerConfig = loggers[loggerName];
const handlerNames = loggerConfig.handlers || []; const handlerNames = loggerConfig.handlers || [];
const handlers = []; const handlers: BaseHandler[] = [];
handlerNames.forEach(handlerName => { handlerNames.forEach(handlerName => {
if (state.handlers.has(handlerName)) { if (state.handlers.has(handlerName)) {

View file

@ -1,8 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { assertEqual, test } from "../testing/mod.ts"; import { assertEqual, test } from "../testing/mod.ts";
import * as log from "./mod.ts"; import * as log from "./mod.ts";
import { BaseHandler } from "./handlers.ts";
import { LogRecord } from "./logger.ts";
import { LogLevel } from "./levels.ts"; import { LogLevel } from "./levels.ts";
import "./handlers_test.ts"; import "./handlers_test.ts";
@ -18,7 +16,7 @@ import "./logger_test.ts";
class TestHandler extends log.handlers.BaseHandler { class TestHandler extends log.handlers.BaseHandler {
public messages: string[] = []; public messages: string[] = [];
log(msg: string) { log(msg: string): void {
this.messages.push(msg); this.messages.push(msg);
} }
} }

View file

@ -42,7 +42,7 @@ export const types = new Map<string, string>();
function populateMaps( function populateMaps(
extensions: Map<string, string[]>, extensions: Map<string, string[]>,
types: Map<string, string> types: Map<string, string>
) { ): void {
const preference = ["nginx", "apache", undefined, "iana"]; const preference = ["nginx", "apache", undefined, "iana"];
for (const type of Object.keys(db)) { 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 /** Given an extension or media type, return the full `Content-Type` header
* string. Returns `undefined` if not resolvable. * string. Returns `undefined` if not resolvable.
*/ */
@ -136,14 +147,3 @@ export function extension(type: string): string | undefined {
return exts[0]; 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);
}

View file

@ -1,7 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
/** FormFile object */ /** FormFile object */
export type FormFile = { export interface FormFile {
/** filename */ /** filename */
filename: string; filename: string;
/** content-type header value of file */ /** content-type header value of file */
@ -12,10 +12,11 @@ export type FormFile = {
content?: Uint8Array; content?: Uint8Array;
/** temporal file path. Set if file size is bigger than specified max-memory size at reading form */ /** temporal file path. Set if file size is bigger than specified max-memory size at reading form */
tempfile?: string; tempfile?: string;
}; }
/** Type guard for FormFile */ /** 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 ( return (
typeof x === "object" && typeof x === "object" &&
x.hasOwnProperty("filename") && x.hasOwnProperty("filename") &&

View file

@ -17,7 +17,7 @@ import { TextProtoReader } from "../textproto/mod.ts";
import { encoder } from "../strings/strings.ts"; import { encoder } from "../strings/strings.ts";
import * as path from "../fs/path.ts"; import * as path from "../fs/path.ts";
function randomBoundary() { function randomBoundary(): string {
let boundary = "--------------------------"; let boundary = "--------------------------";
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
boundary += Math.floor(Math.random() * 10).toString(16); boundary += Math.floor(Math.random() * 10).toString(16);
@ -25,6 +25,185 @@ function randomBoundary() {
return boundary; 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 */ /** Reader for parsing multipart/form-data */
export class MultipartReader { export class MultipartReader {
readonly newLine = encoder.encode("\r\n"); readonly newLine = encoder.encode("\r\n");
@ -125,7 +304,7 @@ export class MultipartReader {
break; break;
} }
if (state) { if (state) {
throw new Error("aa" + state.toString()); throw new Error(`aa${state.toString()}`);
} }
if (this.isBoundaryDelimiterLine(line)) { if (this.isBoundaryDelimiterLine(line)) {
this.partsRead++; this.partsRead++;
@ -155,7 +334,7 @@ export class MultipartReader {
} }
} }
private isFinalBoundary(line: Uint8Array) { private isFinalBoundary(line: Uint8Array): boolean {
if (!bytesHasPrefix(line, this.dashBoundaryDash)) { if (!bytesHasPrefix(line, this.dashBoundaryDash)) {
return false; return false;
} }
@ -163,7 +342,7 @@ export class MultipartReader {
return rest.length === 0 || bytesEqual(skipLWSPChar(rest), this.newLine); return rest.length === 0 || bytesEqual(skipLWSPChar(rest), this.newLine);
} }
private isBoundaryDelimiterLine(line: Uint8Array) { private isBoundaryDelimiterLine(line: Uint8Array): boolean {
if (!bytesHasPrefix(line, this.dashBoundary)) { if (!bytesHasPrefix(line, this.dashBoundary)) {
return false; 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 { class PartWriter implements Writer {
closed = false; closed = false;
private readonly partHeader: string; 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) { 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; const end = b.length - 1;
for (let i = 0; i < end; i++) { for (let i = 0; i < end; i++) {
@ -407,7 +409,7 @@ function checkBoundary(b: string) {
export class MultipartWriter { export class MultipartWriter {
private readonly _boundary: string; private readonly _boundary: string;
get boundary() { get boundary(): string {
return this._boundary; return this._boundary;
} }
@ -462,12 +464,16 @@ export class MultipartWriter {
return this.createPart(h); return this.createPart(h);
} }
async writeField(field: string, value: string) { async writeField(field: string, value: string): Promise<void> {
const f = await this.createFormField(field); const f = await this.createFormField(field);
await f.write(encoder.encode(value)); 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); const f = await this.createFormFile(field, filename);
await copy(f, file); await copy(f, file);
} }
@ -477,7 +483,7 @@ export class MultipartWriter {
} }
/** Close writer. No additional data can be writen to stream */ /** Close writer. No additional data can be writen to stream */
async close() { async close(): Promise<void> {
if (this.isClosed) { if (this.isClosed) {
throw new Error("multipart: writer is closed"); throw new Error("multipart: writer is closed");
} }

View file

@ -13,7 +13,6 @@ import { FormFile, isFormFile } from "./formfile.ts";
import { StringWriter } from "../io/writers.ts"; import { StringWriter } from "../io/writers.ts";
const e = new TextEncoder(); const e = new TextEncoder();
const d = new TextDecoder();
const boundary = "--abcde"; const boundary = "--abcde";
const dashBoundary = e.encode("--" + boundary); const dashBoundary = e.encode("--" + boundary);
const nlDashBoundary = e.encode("\r\n--" + boundary); const nlDashBoundary = e.encode("\r\n--" + boundary);

View file

@ -2,7 +2,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// This script formats the given source files. If the files are omitted, it // This script formats the given source files. If the files are omitted, it
// formats the all files in the repository. // 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 { xrun } from "./util.ts";
import { parse } from "../flags/mod.ts"; import { parse } from "../flags/mod.ts";
import { prettier, prettierPlugins } from "./prettier.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. // 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 => return files.filter(path =>
ignoreList.every(pattern => !path.includes(pattern)) ignoreList.every(pattern => !path.includes(pattern))
); );
@ -203,7 +203,7 @@ async function formatSourceFiles(
exit(0); exit(0);
} }
async function main(opts) { async function main(opts): Promise<void> {
const { help, ignore, check, _: args } = opts; const { help, ignore, check, _: args } = opts;
if (help) { if (help) {

View file

@ -5,7 +5,9 @@ const { readAll } = Deno;
const decoder = new TextDecoder(); 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 p = xrun({ args, stdout: "piped" });
const stdout = decoder.decode(await readAll(p.stdout)); const stdout = decoder.decode(await readAll(p.stdout));
@ -33,7 +35,7 @@ function normalizeOutput(output: string): string {
.join("\n"); .join("\n");
} }
async function clearTestdataChanges() { async function clearTestdataChanges(): Promise<void> {
await xrun({ args: ["git", "checkout", testdata] }).status(); await xrun({ args: ["git", "checkout", testdata] }).status();
} }

View file

@ -5,6 +5,7 @@ import "./vendor/parser_babylon.js";
import "./vendor/parser_markdown.js"; import "./vendor/parser_markdown.js";
// TODO: provide decent type declarions for these // TODO: provide decent type declarions for these
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { prettier, prettierPlugins } = window as any; const { prettier, prettierPlugins } = window as any;
export { prettier, prettierPlugins }; export { prettier, prettierPlugins };

View file

@ -2,7 +2,7 @@
const { platform, run } = Deno; const { platform, run } = Deno;
// Runs a command in cross-platform way // Runs a command in cross-platform way
export function xrun(opts) { export function xrun(opts): Deno.Process {
return run({ return run({
...opts, ...opts,
args: platform.os === "win" ? ["cmd.exe", "/c", ...opts.args] : opts.args args: platform.os === "win" ? ["cmd.exe", "/c", ...opts.args] : opts.args

View file

@ -15,7 +15,7 @@ const REMOVED = 1;
const COMMON = 2; const COMMON = 2;
const ADDED = 3; const ADDED = 3;
function createCommon<T>(A: T[], B: T[], reverse?: boolean) { function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] {
const common = []; const common = [];
if (A.length === 0 || B.length === 0) return []; if (A.length === 0 || B.length === 0) return [];
for (let i = 0; i < Math.min(A.length, B.length); i += 1) { 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; return common;
} }
export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] { export default function diff<T>(A: T[], B: T[]): Array<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;
}
const prefixCommon = createCommon(A, B); const prefixCommon = createCommon(A, B);
const suffixCommon = createCommon( const suffixCommon = createCommon(
A.slice(prefixCommon.length), A.slice(prefixCommon.length),
@ -159,6 +69,99 @@ export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
const diffTypesPtrOffset = routes.length / 2; const diffTypesPtrOffset = routes.length / 2;
let ptr = 0; let ptr = 0;
let p = -1; 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) { while (fp[delta + offset].y < N) {
p = p + 1; p = p + 1;
for (let k = -p; k < delta; ++k) { for (let k = -p; k < delta; ++k) {

View file

@ -6,6 +6,7 @@
* LICENSE file in the root directory of this source tree. * 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 Refs = any[];
export type Optional<T> = { [K in keyof T]?: T[K] }; export type Optional<T> = { [K in keyof T]?: T[K] };
@ -32,7 +33,7 @@ export interface Config {
} }
export type Printer = ( export type Printer = (
val: any, val: unknown,
config: Config, config: Config,
indentation: string, indentation: string,
depth: number, depth: number,
@ -66,12 +67,15 @@ interface BasicValueOptions {
* Explicitly comparing typeof constructor to function avoids undefined as name * Explicitly comparing typeof constructor to function avoids undefined as name
* when mock identity-obj-proxy returns the key as the value for any key. * 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"; (typeof val.constructor === "function" && val.constructor.name) || "Object";
/* global window */ /* global window */
/** Is val is equal to global window object? Works even if it does not exist :) */ /** 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\((.*)\)(.*)$/; const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
@ -116,11 +120,12 @@ function printError(val: Error): string {
* data-types in JS. * data-types in JS.
*/ */
function printBasicValue( function printBasicValue(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
val: any, val: any,
{ printFunctionName, escapeRegex, escapeString }: BasicValueOptions { printFunctionName, escapeRegex, escapeString }: BasicValueOptions
): string | null { ): string | null {
if (val === true || val === false) { if (val === true || val === false) {
return "" + val; return String(val);
} }
if (val === undefined) { if (val === undefined) {
return "undefined"; return "undefined";
@ -136,9 +141,9 @@ function printBasicValue(
} }
if (typeOf === "string") { if (typeOf === "string") {
if (escapeString) { if (escapeString) {
return '"' + val.replace(/"|\\/g, "\\$&") + '"'; return `"${val.replace(/"|\\/g, "\\$&")}"`;
} }
return '"' + val + '"'; return `"${val}"`;
} }
if (typeOf === "function") { if (typeOf === "function") {
return printFunction(val, printFunctionName); return printFunction(val, printFunctionName);
@ -185,95 +190,8 @@ function printBasicValue(
return null; 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( function printer(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
val: any, val: any,
config: Config, config: Config,
indentation: string, indentation: string,
@ -285,6 +203,7 @@ function printer(
if (basicResult !== null) { if (basicResult !== null) {
return basicResult; return basicResult;
} }
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return printComplexValue( return printComplexValue(
val, val,
config, config,
@ -295,30 +214,44 @@ function printer(
); );
} }
const getConfig = (options: Options): Config => ({ /**
...options, * Return items (for example, of an array)
indent: options.min ? "" : createIndent(options.indent), * with spacing, indentation, and comma
spacingInner: options.min ? " " : "\n", * without surrounding punctuation (for example, brackets)
spacingOuter: options.min ? "" : "\n" */
}); 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 { if (list.length) {
return new Array(indent + 1).join(" "); result += config.spacingOuter;
}
const getKeysOfEnumerableProperties = (object: {}) => { const indentationNext = indentation + config.indent;
const keys: Array<string | symbol> = Object.keys(object).sort();
if (Object.getOwnPropertySymbols) { for (let i = 0; i < list.length; i++) {
Object.getOwnPropertySymbols(object).forEach(symbol => { result +=
if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) { indentationNext +
keys.push(symbol); 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) * Return entries (for example, of a map)
@ -326,6 +259,7 @@ const getKeysOfEnumerableProperties = (object: {}) => {
* without surrounding punctuation (for example, braces) * without surrounding punctuation (for example, braces)
*/ */
function printIteratorEntries( function printIteratorEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
iterator: any, iterator: any,
config: Config, config: Config,
indentation: string, indentation: string,
@ -384,6 +318,7 @@ function printIteratorEntries(
* without surrounding punctuation (braces or brackets) * without surrounding punctuation (braces or brackets)
*/ */
function printIteratorValues( function printIteratorValues(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
iterator: Iterator<any>, iterator: Iterator<any>,
config: Config, config: Config,
indentation: string, indentation: string,
@ -419,43 +354,19 @@ function printIteratorValues(
return result; return result;
} }
/** const getKeysOfEnumerableProperties = (object: {}): Array<string | symbol> => {
* Return items (for example, of an array) const keys: Array<string | symbol> = Object.keys(object).sort();
* 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 = "";
if (list.length) { if (Object.getOwnPropertySymbols) {
result += config.spacingOuter; Object.getOwnPropertySymbols(object).forEach(symbol => {
if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) {
const indentationNext = indentation + config.indent; 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 result; return keys;
} };
/** /**
* Return properties of an object * Return properties of an object
@ -504,11 +415,113 @@ function printObjectProperties(
return result; 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 * Returns a presentation string of your `val` object
* @param val any potential JavaScript object * @param val any potential JavaScript object
* @param options Custom settings * @param options Custom settings
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function format(val: any, options: Optional<Options> = {}): string { export function format(val: any, options: Optional<Options> = {}): string {
const opts = Object.keys(DEFAULT_OPTIONS).reduce( const opts = Object.keys(DEFAULT_OPTIONS).reduce(
(acc: Options, k: keyof Options) => { (acc: Options, k: keyof Options) => {

View file

@ -9,17 +9,19 @@
import { test, assertEqual } from "./mod.ts"; import { test, assertEqual } from "./mod.ts";
import { format } from "./format.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; return arguments;
} }
function MyObject(value: unknown) { function MyObject(value: unknown): void {
// @ts-ignore // @ts-ignore
this.name = value; this.name = value;
} }
class MyArray<T> extends Array<T> {} class MyArray<T> extends Array<T> {}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const createVal = () => [ const createVal = () => [
{ {
id: "8658c1d0-9eda-4a90-95e1-8001e8eb6036", id: "8658c1d0-9eda-4a90-95e1-8001e8eb6036",
@ -32,6 +34,7 @@ const createVal = () => [
} }
]; ];
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const createExpected = () => const createExpected = () =>
[ [
"Array [", "Array [",
@ -58,7 +61,7 @@ test({
test({ test({
name: "prints an empty array", name: "prints an empty array",
fn() { fn() {
const val: any[] = []; const val: unknown[] = [];
assertEqual(format(val), "Array []"); assertEqual(format(val), "Array []");
} }
}); });
@ -151,7 +154,7 @@ test({
name: "prints an anonymous callback function", name: "prints an anonymous callback function",
fn() { fn() {
let val; let val;
function f(cb: () => void) { function f(cb: () => void): void {
val = cb; val = cb;
} }
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
@ -164,7 +167,7 @@ test({
name: "prints an anonymous assigned function", name: "prints an anonymous assigned function",
fn() { fn() {
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
const val = () => {}; const val = (): void => {};
const formatted = format(val); const formatted = format(val);
assertEqual( assertEqual(
formatted === "[Function anonymous]" || formatted === "[Function val]", formatted === "[Function anonymous]" || formatted === "[Function val]",
@ -177,7 +180,7 @@ test({
name: "prints a named function", name: "prints a named function",
fn() { fn() {
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
const val = function named() {}; const val = function named(): void {};
assertEqual(format(val), "[Function named]"); assertEqual(format(val), "[Function named]");
} }
}); });
@ -185,7 +188,7 @@ test({
test({ test({
name: "prints a named generator function", name: "prints a named generator function",
fn() { fn() {
const val = function* generate() { const val = function* generate(): IterableIterator<number> {
yield 1; yield 1;
yield 2; yield 2;
yield 3; yield 3;
@ -198,7 +201,7 @@ test({
name: "can customize function names", name: "can customize function names",
fn() { fn() {
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
const val = function named() {}; const val = function named(): void {};
assertEqual( assertEqual(
format(val, { format(val, {
printFunctionName: false printFunctionName: false
@ -248,6 +251,7 @@ test({
test({ test({
name: "prints a map with non-string keys", name: "prints a map with non-string keys",
fn() { fn() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const val = new Map<any, any>([ const val = new Map<any, any>([
[false, "boolean"], [false, "boolean"],
["false", "string"], ["false", "string"],
@ -373,6 +377,7 @@ test({
test({ test({
name: "prints an object with properties and symbols", name: "prints an object with properties and symbols",
fn() { fn() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const val: any = {}; const val: any = {};
val[Symbol("symbol1")] = "value2"; val[Symbol("symbol1")] = "value2";
val[Symbol("symbol2")] = "value3"; val[Symbol("symbol2")] = "value3";
@ -388,7 +393,7 @@ test({
name: name:
"prints an object without non-enumerable properties which have string key", "prints an object without non-enumerable properties which have string key",
fn() { fn() {
const val: any = { const val = {
enumerable: true enumerable: true
}; };
const key = "non-enumerable"; const key = "non-enumerable";
@ -404,7 +409,7 @@ test({
name: name:
"prints an object without non-enumerable properties which have symbol key", "prints an object without non-enumerable properties which have symbol key",
fn() { fn() {
const val: any = { const val = {
enumerable: true enumerable: true
}; };
const key = Symbol("non-enumerable"); const key = Symbol("non-enumerable");
@ -609,6 +614,7 @@ test({
test({ test({
name: "prints circular references", name: "prints circular references",
fn() { fn() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const val: any = {}; const val: any = {};
val.prop = val; val.prop = val;
assertEqual(format(val), 'Object {\n "prop": [Circular],\n}'); assertEqual(format(val), 'Object {\n "prop": [Circular],\n}');
@ -787,6 +793,7 @@ test({
name: "calls toJSON on Sets", name: "calls toJSON on Sets",
fn() { fn() {
const set = new Set([1]); const set = new Set([1]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(set as any).toJSON = () => "map"; (set as any).toJSON = () => "map";
assertEqual(format(set), '"map"'); assertEqual(format(set), '"map"');
} }

View file

@ -3,9 +3,37 @@
import { green, red } from "../colors/mod.ts"; import { green, red } from "../colors/mod.ts";
interface Constructor { interface Constructor {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new (...args: any[]): 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 = { const assertions = {
/** Make an assertion, if not `true`, then throw. */ /** Make an assertion, if not `true`, then throw. */
assert(expr: boolean, msg = ""): void { assert(expr: boolean, msg = ""): void {
@ -78,6 +106,7 @@ const assertions = {
* Forcefully throws a failed assertion * Forcefully throws a failed assertion
*/ */
fail(msg?: string): void { fail(msg?: string): void {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`); assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`);
}, },
@ -163,33 +192,6 @@ export const assert = assertions.assert as Assert;
*/ */
export const assertEqual = assert.equal; 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 type TestFunction = () => void | Promise<void>;
export interface TestDefinition { export interface TestDefinition {
@ -203,14 +205,20 @@ let filterRegExp: RegExp | null;
const tests: TestDefinition[] = []; const tests: TestDefinition[] = [];
let filtered = 0; let filtered = 0;
const ignored = 0;
const measured = 0;
// Must be called before any test() that needs to be filtered. // Must be called before any test() that needs to be filtered.
export function setFilter(s: string): void { export function setFilter(s: string): void {
filterRegExp = new RegExp(s, "i"); 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 { export function test(t: TestDefinition | TestFunction): void {
const fn: TestFunction = typeof t === "function" ? t : t.fn; const fn: TestFunction = typeof t === "function" ? t : t.fn;
const name: string = t.name; const name: string = t.name;
@ -225,21 +233,8 @@ export function test(t: TestDefinition | TestFunction): void {
} }
} }
function filter(name: string): boolean { const RED_FAILED = red("FAILED");
if (filterRegExp) { const GREEN_OK = green("ok");
return filterRegExp.test(name);
} else {
return true;
}
}
function red_failed() {
return red("FAILED");
}
function green_ok() {
return green("ok");
}
interface TestStats { interface TestStats {
filtered: number; filtered: number;
@ -261,7 +256,7 @@ interface TestResults {
cases: Map<number, TestResult>; cases: Map<number, TestResult>;
} }
function createTestResults(tests: Array<TestDefinition>): TestResults { function createTestResults(tests: TestDefinition[]): TestResults {
return tests.reduce( return tests.reduce(
(acc: TestResults, { name }: TestDefinition, i: number): TestResults => { (acc: TestResults, { name }: TestDefinition, i: number): TestResults => {
acc.keys.set(name, i); acc.keys.set(name, i);
@ -274,10 +269,10 @@ function createTestResults(tests: Array<TestDefinition>): TestResults {
function report(result: TestResult): void { function report(result: TestResult): void {
if (result.ok) { if (result.ok) {
console.log(`test ${result.name} ... ${green_ok()}`); console.log(`test ${result.name} ... ${GREEN_OK}`);
} else if (result.error) { } else if (result.error) {
console.error( console.error(
`test ${result.name} ... ${red_failed()}\n${result.error.stack}` `test ${result.name} ... ${RED_FAILED}\n${result.error.stack}`
); );
} else { } else {
console.log(`test ${result.name} ... unresolved`); console.log(`test ${result.name} ... unresolved`);
@ -302,7 +297,7 @@ function printResults(
} }
// Attempting to match the output of Rust's test runner. // Attempting to match the output of Rust's test runner.
console.log( 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.passed} passed; ${stats.failed} failed; ` +
`${stats.ignored} ignored; ${stats.measured} measured; ` + `${stats.ignored} ignored; ${stats.measured} measured; ` +
`${stats.filtered} filtered out\n` `${stats.filtered} filtered out\n`
@ -342,7 +337,7 @@ async function createTestCase(
function initTestCases( function initTestCases(
stats: TestStats, stats: TestStats,
results: TestResults, results: TestResults,
tests: Array<TestDefinition> tests: TestDefinition[]
): Array<Promise<void>> { ): Array<Promise<void>> {
return tests.map(createTestCase.bind(null, stats, results)); return tests.map(createTestCase.bind(null, stats, results));
} }
@ -350,7 +345,7 @@ function initTestCases(
async function runTestsParallel( async function runTestsParallel(
stats: TestStats, stats: TestStats,
results: TestResults, results: TestResults,
tests: Array<TestDefinition> tests: TestDefinition[]
): Promise<void> { ): Promise<void> {
try { try {
await Promise.all(initTestCases(stats, results, tests)); await Promise.all(initTestCases(stats, results, tests));
@ -362,7 +357,7 @@ async function runTestsParallel(
async function runTestsSerial( async function runTestsSerial(
stats: TestStats, stats: TestStats,
tests: Array<TestDefinition> tests: TestDefinition[]
): Promise<void> { ): Promise<void> {
for (const { fn, name } of tests) { for (const { fn, name } of tests) {
// See https://github.com/denoland/deno/pull/1452 // See https://github.com/denoland/deno/pull/1452
@ -371,10 +366,10 @@ async function runTestsSerial(
try { try {
await fn(); await fn();
stats.passed++; stats.passed++;
console.log("...", green_ok()); console.log("...", GREEN_OK);
console.groupEnd(); console.groupEnd();
} catch (err) { } catch (err) {
console.log("...", red_failed()); console.log("...", RED_FAILED);
console.groupEnd(); console.groupEnd();
console.error(err.stack); console.error(err.stack);
stats.failed++; stats.failed++;

View file

@ -15,7 +15,7 @@ function createStr(v: unknown): string {
} }
} }
function createColor(diffType: DiffType) { function createColor(diffType: DiffType): (s: string) => string {
switch (diffType) { switch (diffType) {
case "added": case "added":
return (s: string) => green(bold(s)); 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) { switch (diffType) {
case "added": case "added":
return "+ "; return "+ ";
@ -37,7 +37,7 @@ function createSign(diffType: DiffType) {
} }
} }
function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>) { function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>): string[] {
const messages = []; const messages = [];
messages.push(""); messages.push("");
messages.push(""); messages.push("");
@ -55,7 +55,7 @@ function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>) {
return messages; return messages;
} }
export function assertEqual(actual: unknown, expected: unknown) { export function assertEqual(actual: unknown, expected: unknown): void {
if (equal(actual, expected)) { if (equal(actual, expected)) {
return; return;
} }

View file

@ -4,7 +4,7 @@ import { test, assert } from "./mod.ts";
import { red, green, white, gray, bold } from "../colors/mod.ts"; import { red, green, white, gray, bold } from "../colors/mod.ts";
import { assertEqual } from "./pretty.ts"; import { assertEqual } from "./pretty.ts";
const createHeader = () => [ const createHeader = (): string[] => [
"", "",
"", "",
` ${gray(bold("[Diff]"))} ${red(bold("Left"))} / ${green(bold("Right"))}`, ` ${gray(bold("[Diff]"))} ${red(bold("Left"))} / ${green(bold("Right"))}`,
@ -12,8 +12,8 @@ const createHeader = () => [
"" ""
]; ];
const added = (s: string) => green(bold(s)); const added: (s: string) => string = (s: string): string => green(bold(s));
const removed = (s: string) => red(bold(s)); const removed: (s: string) => string = (s: string): string => red(bold(s));
test({ test({
name: "pass case", name: "pass case",

View file

@ -35,8 +35,6 @@ test(function testingAssertEqual() {
}); });
test(function testingAssertFail() { test(function testingAssertFail() {
let didThrow = false;
assert.throws(assert.fail, Error, "Failed assertion."); assert.throws(assert.fail, Error, "Failed assertion.");
assert.throws( assert.throws(
() => { () => {

View file

@ -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 { export class TextProtoReader {
constructor(readonly r: BufReader) {} constructor(readonly r: BufReader) {}
@ -137,14 +148,3 @@ export class TextProtoReader {
return [line, null]; 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;
}
}

View file

@ -92,7 +92,7 @@ test(async function textprotoAppend() {
test(async function textprotoReadEmpty() { test(async function textprotoReadEmpty() {
let r = reader(""); let r = reader("");
let [m, err] = await r.readMIMEHeader(); let [, err] = await r.readMIMEHeader();
// Should not crash! // Should not crash!
assertEqual(err, "EOF"); assertEqual(err, "EOF");
}); });

15
tsconfig.json Normal file
View 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"]
}