diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..9f113730f7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +/flags/ +/fs/ +/http/ +/ws/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..43405e21eb --- /dev/null +++ b/.eslintrc.json @@ -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": "^_" } + ] + } +} diff --git a/.gitignore b/.gitignore index b2941c3c2b..bb7de9d001 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store .idea -tsconfig.json -deno.d.ts \ No newline at end of file +deno.d.ts +node_modules +package.json +package-lock.json diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2d6f318a4c..861159e845 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -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 diff --git a/benching/example.ts b/benching/example.ts index 1018dd9e1a..1563379bfc 100644 --- a/benching/example.ts +++ b/benching/example.ts @@ -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(); } }); diff --git a/benching/mod.ts b/benching/mod.ts index 4d2f5d41ec..6fa442d032 100644 --- a/benching/mod.ts +++ b/benching/mod.ts @@ -14,10 +14,10 @@ export interface BenchmarkTimer { } /** Defines a benchmark through a named function. */ -export type BenchmarkFunction = { +export interface BenchmarkFunction { (b: BenchmarkTimer): void | Promise; name: string; -}; +} /** Defines a benchmark definition with configurable runs. */ export interface BenchmarkDefinition { @@ -69,7 +69,7 @@ function createBenchmarkTimer(clock: BenchmarkClock): BenchmarkTimer { }; } -const candidates: Array = []; +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 { // Filtering candidates by the "only" and "skip" constraint - const benchmarks: Array = 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 diff --git a/benching/test.ts b/benching/test.ts index 81cbc5e6f6..34f7b00d29 100644 --- a/benching/test.ts +++ b/benching/test.ts @@ -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(); } }); diff --git a/bytes/bytes.ts b/bytes/bytes.ts index ef333288e7..080ec4445d 100644 --- a/bytes/bytes.ts +++ b/bytes/bytes.ts @@ -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; diff --git a/colors/mod.ts b/colors/mod.ts index 7a45905ce0..d0b4bc3200 100644 --- a/colors/mod.ts +++ b/colors/mod.ts @@ -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; diff --git a/datetime/mod.ts b/datetime/mod.ts index f30fc89c99..96ed3a860a 100644 --- a/datetime/mod.ts +++ b/datetime/mod.ts @@ -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!"); } diff --git a/datetime/test.ts b/datetime/test.ts index 3329e9cd1d..7182c27c41 100644 --- a/datetime/test.ts +++ b/datetime/test.ts @@ -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) { diff --git a/examples/gist.ts b/examples/gist.ts index 8ce68a946e..f64f692262 100755 --- a/examples/gist.ts +++ b/examples/gist.ts @@ -9,7 +9,7 @@ function pathBase(p: string): string { return parts[parts.length - 1]; } -async function main() { +async function main(): Promise { 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 }); diff --git a/examples/ws.ts b/examples/ws.ts index bc2a7bd0be..f25a156875 100644 --- a/examples/ws.ts +++ b/examples/ws.ts @@ -6,7 +6,7 @@ import { isWebSocketPingEvent } from "https://deno.land/x/ws/mod.ts"; -async function main() { +async function main(): Promise { 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)) { diff --git a/io/bufio.ts b/io/bufio.ts index 05e6b0a0c7..33cddea2b0 100644 --- a/io/bufio.ts +++ b/io/bufio.ts @@ -211,7 +211,7 @@ export class BufReader implements Reader { * delim. * For simple uses, a Scanner may be more convenient. */ - async readString(delim: string): Promise { + async readString(_delim: string): Promise { throw new Error("Not implemented"); } diff --git a/io/bufio_test.ts b/io/bufio_test.ts index 0a261daea7..41151b6812 100644 --- a/io/bufio_test.ts +++ b/io/bufio_test.ts @@ -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 { const b = new Uint8Array(1000); @@ -71,7 +62,10 @@ async function reads(buf: BufReader, m: number): Promise { return decoder.decode(b.subarray(0, nb)); } -type NamedBufReader = { name: string; fn: (r: BufReader) => Promise }; +interface NamedBufReader { + name: string; + fn: (r: BufReader) => Promise; +} const bufreaders: NamedBufReader[] = [ { name: "1", fn: (b: BufReader) => reads(b, 1) }, @@ -187,6 +181,7 @@ async function testReadLine(input: Uint8Array): Promise { 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(" "))); } diff --git a/io/ioutil_test.ts b/io/ioutil_test.ts index 168aad52a0..08d0d677e8 100644 --- a/io/ioutil_test.ts +++ b/io/ioutil_test.ts @@ -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, diff --git a/io/writers_test.ts b/io/writers_test.ts index 94c58417df..ba4d7ed89b 100644 --- a/io/writers_test.ts +++ b/io/writers_test.ts @@ -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"); diff --git a/log/handlers.ts b/log/handlers.ts index 96d8c056fd..c1f664cc09 100644 --- a/log/handlers.ts +++ b/log/handlers.ts @@ -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 {} + async destroy(): Promise {} } 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 { // open file in append mode - write only this._file = await open(this._filename, "a"); this._writer = this._file; } - async destroy() { + async destroy(): Promise { await this._file.close(); } } diff --git a/log/handlers_test.ts b/log/handlers_test.ts index 52eca16ea5..7dc4e2089d 100644 --- a/log/handlers_test.ts +++ b/log/handlers_test.ts @@ -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"; diff --git a/log/logger.ts b/log/logger.ts index 678d5ed94d..d92c34a582 100644 --- a/log/logger.ts +++ b/log/logger.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); } } diff --git a/log/logger_test.ts b/log/logger_test.ts index a2275a00bd..8ff328c484 100644 --- a/log/logger_test.ts +++ b/log/logger_test.ts @@ -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"); diff --git a/log/mod.ts b/log/mod.ts index 96dc81ff1a..ef5ca5a89b 100644 --- a/log/mod.ts +++ b/log/mod.ts @@ -36,8 +36,8 @@ const DEFAULT_CONFIG: LogConfig = { }; const state = { - handlers: new Map(), - loggers: new Map(), + handlers: new Map(), + loggers: new Map(), 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 { 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)) { diff --git a/log/test.ts b/log/test.ts index 918368b97f..3599247bd2 100644 --- a/log/test.ts +++ b/log/test.ts @@ -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); } } diff --git a/media_types/mod.ts b/media_types/mod.ts index 3b74427ba3..b7c75fb223 100644 --- a/media_types/mod.ts +++ b/media_types/mod.ts @@ -42,7 +42,7 @@ export const types = new Map(); function populateMaps( extensions: Map, types: Map -) { +): 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); -} diff --git a/multipart/formfile.ts b/multipart/formfile.ts index b1b63eb157..8bbdcffbca 100644 --- a/multipart/formfile.ts +++ b/multipart/formfile.ts @@ -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") && diff --git a/multipart/multipart.ts b/multipart/multipart.ts index 70874ed85b..68785c7c6c 100644 --- a/multipart/multipart.ts +++ b/multipart/multipart.ts @@ -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 { + 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 { - 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 { 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 { 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 { if (this.isClosed) { throw new Error("multipart: writer is closed"); } diff --git a/multipart/multipart_test.ts b/multipart/multipart_test.ts index a87db60d70..b4c0f761d7 100644 --- a/multipart/multipart_test.ts +++ b/multipart/multipart_test.ts @@ -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); diff --git a/prettier/main.ts b/prettier/main.ts index 70c2250868..286cba58c1 100755 --- a/prettier/main.ts +++ b/prettier/main.ts @@ -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 { } // 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 { const { help, ignore, check, _: args } = opts; if (help) { diff --git a/prettier/main_test.ts b/prettier/main_test.ts index 165081e002..b6c0489eee 100644 --- a/prettier/main_test.ts +++ b/prettier/main_test.ts @@ -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 { await xrun({ args: ["git", "checkout", testdata] }).status(); } diff --git a/prettier/prettier.ts b/prettier/prettier.ts index 67099c5ca5..98f36a78dc 100644 --- a/prettier/prettier.ts +++ b/prettier/prettier.ts @@ -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 }; diff --git a/prettier/util.ts b/prettier/util.ts index 0be583f757..425cbfe966 100644 --- a/prettier/util.ts +++ b/prettier/util.ts @@ -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 diff --git a/testing/diff.ts b/testing/diff.ts index a1385b88a0..4fab75e4a8 100644 --- a/testing/diff.ts +++ b/testing/diff.ts @@ -15,7 +15,7 @@ const REMOVED = 1; const COMMON = 2; const ADDED = 3; -function createCommon(A: T[], B: T[], reverse?: boolean) { +function createCommon(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(A: T[], B: T[], reverse?: boolean) { return common; } -export default function diff(A: T[], B: T[]): DiffResult[] { - function backTrace( - 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( - 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(A: T[], B: T[]): Array> { const prefixCommon = createCommon(A, B); const suffixCommon = createCommon( A.slice(prefixCommon.length), @@ -159,6 +69,99 @@ export default function diff(A: T[], B: T[]): DiffResult[] { const diffTypesPtrOffset = routes.length / 2; let ptr = 0; let p = -1; + + function backTrace( + 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( + 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) { diff --git a/testing/format.ts b/testing/format.ts index 8434db1c2d..9a8b02ac6a 100644 --- a/testing/format.ts +++ b/testing/format.ts @@ -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 = { [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 = 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, 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 => { + const keys: Array = 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 = {}): string { const opts = Object.keys(DEFAULT_OPTIONS).reduce( (acc: Options, k: keyof Options) => { diff --git a/testing/format_test.ts b/testing/format_test.ts index 3e6da44802..7ca0235a50 100644 --- a/testing/format_test.ts +++ b/testing/format_test.ts @@ -9,17 +9,19 @@ import { test, assertEqual } from "./mod.ts"; import { format } from "./format.ts"; -function returnArguments(..._args: Array) { +// 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 extends Array {} +// 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 { 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([ [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"'); } diff --git a/testing/mod.ts b/testing/mod.ts index 41ae61bea3..c1bf2015b1 100644 --- a/testing/mod.ts +++ b/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; 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; } -function createTestResults(tests: Array): 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): 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 + tests: TestDefinition[] ): Array> { return tests.map(createTestCase.bind(null, stats, results)); } @@ -350,7 +345,7 @@ function initTestCases( async function runTestsParallel( stats: TestStats, results: TestResults, - tests: Array + tests: TestDefinition[] ): Promise { try { await Promise.all(initTestCases(stats, results, tests)); @@ -362,7 +357,7 @@ async function runTestsParallel( async function runTestsSerial( stats: TestStats, - tests: Array + tests: TestDefinition[] ): Promise { 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++; diff --git a/testing/pretty.ts b/testing/pretty.ts index aa90f24693..b27b3ccd75 100644 --- a/testing/pretty.ts +++ b/testing/pretty.ts @@ -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>) { +function buildMessage(diffResult: ReadonlyArray>): string[] { const messages = []; messages.push(""); messages.push(""); @@ -55,7 +55,7 @@ function buildMessage(diffResult: ReadonlyArray>) { return messages; } -export function assertEqual(actual: unknown, expected: unknown) { +export function assertEqual(actual: unknown, expected: unknown): void { if (equal(actual, expected)) { return; } diff --git a/testing/pretty_test.ts b/testing/pretty_test.ts index f3b087aff9..89b35c0882 100644 --- a/testing/pretty_test.ts +++ b/testing/pretty_test.ts @@ -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", diff --git a/testing/test.ts b/testing/test.ts index 0d79c22184..7182a5783e 100644 --- a/testing/test.ts +++ b/testing/test.ts @@ -35,8 +35,6 @@ test(function testingAssertEqual() { }); test(function testingAssertFail() { - let didThrow = false; - assert.throws(assert.fail, Error, "Failed assertion."); assert.throws( () => { diff --git a/textproto/mod.ts b/textproto/mod.ts index ee76472967..28eac4609a 100644 --- a/textproto/mod.ts +++ b/textproto/mod.ts @@ -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; - } -} diff --git a/textproto/test.ts b/textproto/test.ts index 0f8bee2275..b6a4c93c9b 100644 --- a/textproto/test.ts +++ b/textproto/test.ts @@ -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"); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..5a8dca33d0 --- /dev/null +++ b/tsconfig.json @@ -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"] +}