1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-26 00:59:24 -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": "^_" }
]
}
}

6
.gitignore vendored
View file

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

View file

@ -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

View file

@ -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();
}
});

View file

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

View file

@ -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();
}
});

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 **/
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;

View file

@ -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;

View file

@ -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!");
}

View file

@ -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) {

View file

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

View file

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

View file

@ -211,7 +211,7 @@ export class BufReader implements Reader {
* delim.
* For simple uses, a Scanner may be more convenient.
*/
async readString(delim: string): Promise<string> {
async readString(_delim: string): Promise<string> {
throw new Error("Not implemented");
}

View file

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

View file

@ -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,

View file

@ -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");

View file

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

View file

@ -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";

View file

@ -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);
}
}

View file

@ -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");

View file

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

View file

@ -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);
}
}

View file

@ -42,7 +42,7 @@ export const types = new Map<string, string>();
function populateMaps(
extensions: Map<string, string[]>,
types: Map<string, string>
) {
): void {
const preference = ["nginx", "apache", undefined, "iana"];
for (const type of Object.keys(db)) {
@ -98,6 +98,17 @@ export function charset(type: string): string | undefined {
}
}
/** Given an extension, lookup the appropriate media type for that extension.
* Likely you should be using `contentType()` though instead.
*/
export function lookup(path: string): string | undefined {
const extension = extname("x." + path)
.toLowerCase()
.substr(1);
return types.get(extension);
}
/** Given an extension or media type, return the full `Content-Type` header
* string. Returns `undefined` if not resolvable.
*/
@ -136,14 +147,3 @@ export function extension(type: string): string | undefined {
return exts[0];
}
/** Given an extension, lookup the appropriate media type for that extension.
* Likely you should be using `contentType()` though instead.
*/
export function lookup(path: string): string | undefined {
const extension = extname("x." + path)
.toLowerCase()
.substr(1);
return types.get(extension);
}

View file

@ -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") &&

View file

@ -17,7 +17,7 @@ import { TextProtoReader } from "../textproto/mod.ts";
import { encoder } from "../strings/strings.ts";
import * as path from "../fs/path.ts";
function randomBoundary() {
function randomBoundary(): string {
let boundary = "--------------------------";
for (let i = 0; i < 24; i++) {
boundary += Math.floor(Math.random() * 10).toString(16);
@ -25,6 +25,185 @@ function randomBoundary() {
return boundary;
}
export function matchAfterPrefix(
a: Uint8Array,
prefix: Uint8Array,
bufState: BufState
): number {
if (a.length === prefix.length) {
if (bufState) {
return 1;
}
return 0;
}
const c = a[prefix.length];
if (
c === " ".charCodeAt(0) ||
c === "\t".charCodeAt(0) ||
c === "\r".charCodeAt(0) ||
c === "\n".charCodeAt(0) ||
c === "-".charCodeAt(0)
) {
return 1;
}
return -1;
}
export function scanUntilBoundary(
buf: Uint8Array,
dashBoundary: Uint8Array,
newLineDashBoundary: Uint8Array,
total: number,
state: BufState
): [number, BufState] {
if (total === 0) {
if (bytesHasPrefix(buf, dashBoundary)) {
switch (matchAfterPrefix(buf, dashBoundary, state)) {
case -1:
return [dashBoundary.length, null];
case 0:
return [0, null];
case 1:
return [0, "EOF"];
}
if (bytesHasPrefix(dashBoundary, buf)) {
return [0, state];
}
}
}
const i = bytesFindIndex(buf, newLineDashBoundary);
if (i >= 0) {
switch (matchAfterPrefix(buf.slice(i), newLineDashBoundary, state)) {
case -1:
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
return [i + newLineDashBoundary.length, null];
case 0:
return [i, null];
case 1:
return [i, "EOF"];
}
}
if (bytesHasPrefix(newLineDashBoundary, buf)) {
return [0, state];
}
const j = bytesFindLastIndex(buf, newLineDashBoundary.slice(0, 1));
if (j >= 0 && bytesHasPrefix(newLineDashBoundary, buf.slice(j))) {
return [j, null];
}
return [buf.length, state];
}
let i = 0;
class PartReader implements Reader, Closer {
n: number = 0;
total: number = 0;
bufState: BufState = null;
index = i++;
constructor(private mr: MultipartReader, public readonly headers: Headers) {}
async read(p: Uint8Array): Promise<ReadResult> {
const br = this.mr.bufReader;
const returnResult = (nread: number, bufState: BufState): ReadResult => {
if (bufState && bufState !== "EOF") {
throw bufState;
}
return { nread, eof: bufState === "EOF" };
};
if (this.n === 0 && !this.bufState) {
const [peek] = await br.peek(br.buffered());
const [n, state] = scanUntilBoundary(
peek,
this.mr.dashBoundary,
this.mr.newLineDashBoundary,
this.total,
this.bufState
);
this.n = n;
this.bufState = state;
if (this.n === 0 && !this.bufState) {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
const [, state] = await br.peek(peek.length + 1);
this.bufState = state;
if (this.bufState === "EOF") {
this.bufState = new RangeError("unexpected eof");
}
}
}
if (this.n === 0) {
return returnResult(0, this.bufState);
}
let n = 0;
if (p.byteLength > this.n) {
n = this.n;
}
const buf = p.slice(0, n);
const [nread] = await this.mr.bufReader.readFull(buf);
p.set(buf);
this.total += nread;
this.n -= nread;
if (this.n === 0) {
return returnResult(n, this.bufState);
}
return returnResult(n, null);
}
close(): void {}
private contentDisposition: string;
private contentDispositionParams: { [key: string]: string };
private getContentDispositionParams(): { [key: string]: string } {
if (this.contentDispositionParams) return this.contentDispositionParams;
const cd = this.headers.get("content-disposition");
const params = {};
const comps = cd.split(";");
this.contentDisposition = comps[0];
comps
.slice(1)
.map(v => v.trim())
.map(kv => {
const [k, v] = kv.split("=");
if (v) {
const s = v.charAt(0);
const e = v.charAt(v.length - 1);
if ((s === e && s === '"') || s === "'") {
params[k] = v.substr(1, v.length - 2);
} else {
params[k] = v;
}
}
});
return (this.contentDispositionParams = params);
}
get fileName(): string {
return this.getContentDispositionParams()["filename"];
}
get formName(): string {
const p = this.getContentDispositionParams();
if (this.contentDisposition === "form-data") {
return p["name"];
}
return "";
}
}
function skipLWSPChar(u: Uint8Array): Uint8Array {
const ret = new Uint8Array(u.length);
const sp = " ".charCodeAt(0);
const ht = "\t".charCodeAt(0);
let j = 0;
for (let i = 0; i < u.length; i++) {
if (u[i] === sp || u[i] === ht) continue;
ret[j++] = u[i];
}
return ret.slice(0, j);
}
/** Reader for parsing multipart/form-data */
export class MultipartReader {
readonly newLine = encoder.encode("\r\n");
@ -125,7 +304,7 @@ export class MultipartReader {
break;
}
if (state) {
throw new Error("aa" + state.toString());
throw new Error(`aa${state.toString()}`);
}
if (this.isBoundaryDelimiterLine(line)) {
this.partsRead++;
@ -155,7 +334,7 @@ export class MultipartReader {
}
}
private isFinalBoundary(line: Uint8Array) {
private isFinalBoundary(line: Uint8Array): boolean {
if (!bytesHasPrefix(line, this.dashBoundaryDash)) {
return false;
}
@ -163,7 +342,7 @@ export class MultipartReader {
return rest.length === 0 || bytesEqual(skipLWSPChar(rest), this.newLine);
}
private isBoundaryDelimiterLine(line: Uint8Array) {
private isBoundaryDelimiterLine(line: Uint8Array): boolean {
if (!bytesHasPrefix(line, this.dashBoundary)) {
return false;
}
@ -172,183 +351,6 @@ export class MultipartReader {
}
}
function skipLWSPChar(u: Uint8Array): Uint8Array {
const ret = new Uint8Array(u.length);
const sp = " ".charCodeAt(0);
const ht = "\t".charCodeAt(0);
let j = 0;
for (let i = 0; i < u.length; i++) {
if (u[i] === sp || u[i] === ht) continue;
ret[j++] = u[i];
}
return ret.slice(0, j);
}
let i = 0;
class PartReader implements Reader, Closer {
n: number = 0;
total: number = 0;
bufState: BufState = null;
index = i++;
constructor(private mr: MultipartReader, public readonly headers: Headers) {}
async read(p: Uint8Array): Promise<ReadResult> {
const br = this.mr.bufReader;
const returnResult = (nread: number, bufState: BufState): ReadResult => {
if (bufState && bufState !== "EOF") {
throw bufState;
}
return { nread, eof: bufState === "EOF" };
};
if (this.n === 0 && !this.bufState) {
const [peek] = await br.peek(br.buffered());
const [n, state] = scanUntilBoundary(
peek,
this.mr.dashBoundary,
this.mr.newLineDashBoundary,
this.total,
this.bufState
);
this.n = n;
this.bufState = state;
if (this.n === 0 && !this.bufState) {
const [_, state] = await br.peek(peek.length + 1);
this.bufState = state;
if (this.bufState === "EOF") {
this.bufState = new RangeError("unexpected eof");
}
}
}
if (this.n === 0) {
return returnResult(0, this.bufState);
}
let n = 0;
if (p.byteLength > this.n) {
n = this.n;
}
const buf = p.slice(0, n);
const [nread] = await this.mr.bufReader.readFull(buf);
p.set(buf);
this.total += nread;
this.n -= nread;
if (this.n === 0) {
return returnResult(n, this.bufState);
}
return returnResult(n, null);
}
close(): void {}
private contentDisposition: string;
private contentDispositionParams: { [key: string]: string };
private getContentDispositionParams() {
if (this.contentDispositionParams) return this.contentDispositionParams;
const cd = this.headers.get("content-disposition");
const params = {};
const comps = cd.split(";");
this.contentDisposition = comps[0];
comps
.slice(1)
.map(v => v.trim())
.map(kv => {
const [k, v] = kv.split("=");
if (v) {
const s = v.charAt(0);
const e = v.charAt(v.length - 1);
if ((s === e && s === '"') || s === "'") {
params[k] = v.substr(1, v.length - 2);
} else {
params[k] = v;
}
}
});
return (this.contentDispositionParams = params);
}
get fileName(): string {
return this.getContentDispositionParams()["filename"];
}
get formName(): string {
const p = this.getContentDispositionParams();
if (this.contentDisposition === "form-data") {
return p["name"];
}
return "";
}
}
export function scanUntilBoundary(
buf: Uint8Array,
dashBoundary: Uint8Array,
newLineDashBoundary: Uint8Array,
total: number,
state: BufState
): [number, BufState] {
if (total === 0) {
if (bytesHasPrefix(buf, dashBoundary)) {
switch (matchAfterPrefix(buf, dashBoundary, state)) {
case -1:
return [dashBoundary.length, null];
case 0:
return [0, null];
case 1:
return [0, "EOF"];
}
if (bytesHasPrefix(dashBoundary, buf)) {
return [0, state];
}
}
}
const i = bytesFindIndex(buf, newLineDashBoundary);
if (i >= 0) {
switch (matchAfterPrefix(buf.slice(i), newLineDashBoundary, state)) {
case -1:
return [i + newLineDashBoundary.length, null];
case 0:
return [i, null];
case 1:
return [i, "EOF"];
}
}
if (bytesHasPrefix(newLineDashBoundary, buf)) {
return [0, state];
}
const j = bytesFindLastIndex(buf, newLineDashBoundary.slice(0, 1));
if (j >= 0 && bytesHasPrefix(newLineDashBoundary, buf.slice(j))) {
return [j, null];
}
return [buf.length, state];
}
export function matchAfterPrefix(
a: Uint8Array,
prefix: Uint8Array,
bufState: BufState
): number {
if (a.length === prefix.length) {
if (bufState) {
return 1;
}
return 0;
}
const c = a[prefix.length];
if (
c === " ".charCodeAt(0) ||
c === "\t".charCodeAt(0) ||
c === "\r".charCodeAt(0) ||
c === "\n".charCodeAt(0) ||
c === "-".charCodeAt(0)
) {
return 1;
}
return -1;
}
class PartWriter implements Writer {
closed = false;
private readonly partHeader: string;
@ -389,9 +391,9 @@ class PartWriter implements Writer {
}
}
function checkBoundary(b: string) {
function checkBoundary(b: string): string {
if (b.length < 1 || b.length > 70) {
throw new Error("invalid boundary length: " + b.length);
throw new Error(`invalid boundary length: ${b.length}`);
}
const end = b.length - 1;
for (let i = 0; i < end; i++) {
@ -407,7 +409,7 @@ function checkBoundary(b: string) {
export class MultipartWriter {
private readonly _boundary: string;
get boundary() {
get boundary(): string {
return this._boundary;
}
@ -462,12 +464,16 @@ export class MultipartWriter {
return this.createPart(h);
}
async writeField(field: string, value: string) {
async writeField(field: string, value: string): Promise<void> {
const f = await this.createFormField(field);
await f.write(encoder.encode(value));
}
async writeFile(field: string, filename: string, file: Reader) {
async writeFile(
field: string,
filename: string,
file: Reader
): Promise<void> {
const f = await this.createFormFile(field, filename);
await copy(f, file);
}
@ -477,7 +483,7 @@ export class MultipartWriter {
}
/** Close writer. No additional data can be writen to stream */
async close() {
async close(): Promise<void> {
if (this.isClosed) {
throw new Error("multipart: writer is closed");
}

View file

@ -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);

View file

@ -2,7 +2,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// This script formats the given source files. If the files are omitted, it
// formats the all files in the repository.
const { args, platform, readAll, lstat, exit, run, readFile, writeFile } = Deno;
const { args, readAll, lstat, exit, readFile, writeFile } = Deno;
import { xrun } from "./util.ts";
import { parse } from "../flags/mod.ts";
import { prettier, prettierPlugins } from "./prettier.ts";
@ -67,7 +67,7 @@ async function getSourceFiles(args: string[]): Promise<string[]> {
}
// Filters out the files which contains any pattern in the given ignoreList.
function filterIgnoreList(files: string[], ignoreList: string[]) {
function filterIgnoreList(files: string[], ignoreList: string[]): string[] {
return files.filter(path =>
ignoreList.every(pattern => !path.includes(pattern))
);
@ -203,7 +203,7 @@ async function formatSourceFiles(
exit(0);
}
async function main(opts) {
async function main(opts): Promise<void> {
const { help, ignore, check, _: args } = opts;
if (help) {

View file

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

View file

@ -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 };

View file

@ -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

View file

@ -15,7 +15,7 @@ const REMOVED = 1;
const COMMON = 2;
const ADDED = 3;
function createCommon<T>(A: T[], B: T[], reverse?: boolean) {
function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] {
const common = [];
if (A.length === 0 || B.length === 0) return [];
for (let i = 0; i < Math.min(A.length, B.length); i += 1) {
@ -30,97 +30,7 @@ function createCommon<T>(A: T[], B: T[], reverse?: boolean) {
return common;
}
export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
function backTrace<T>(
A: T[],
B: T[],
current: FarthestPoint,
swapped: boolean
) {
const M = A.length;
const N = B.length;
const result = [];
let a = M - 1;
let b = N - 1;
let j = routes[current.id];
let type = routes[current.id + diffTypesPtrOffset];
while (true) {
if (!j && !type) break;
const prev = j;
if (type === REMOVED) {
result.unshift({
type: (swapped ? "removed" : "added") as DiffType,
value: B[b]
});
b -= 1;
} else if (type === ADDED) {
result.unshift({
type: (swapped ? "added" : "removed") as DiffType,
value: A[a]
});
a -= 1;
} else {
result.unshift({ type: "common" as DiffType, value: A[a] });
a -= 1;
b -= 1;
}
j = routes[prev];
type = routes[prev + diffTypesPtrOffset];
}
return result;
}
function createFP(
slide: FarthestPoint,
down: FarthestPoint,
k: number,
M: number,
N: number
): FarthestPoint {
if (slide && slide.y === -1 && (down && down.y === -1))
return { y: 0, id: 0 };
if (
(down && down.y === -1) ||
k === M ||
(slide && slide.y) > (down && down.y) + 1
) {
const prev = slide.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = ADDED;
return { y: slide.y, id: ptr };
} else {
const prev = down.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = REMOVED;
return { y: down.y + 1, id: ptr };
}
}
function snake<T>(
k: number,
slide: FarthestPoint,
down: FarthestPoint,
offset: number,
A: T[],
B: T[]
) {
const M = A.length;
const N = B.length;
if (k < -N || M < k) return { y: -1 };
const fp = createFP(slide, down, k, M, N);
while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) {
const prev = fp.id;
ptr++;
fp.id = ptr;
fp.y += 1;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = COMMON;
}
return fp;
}
export default function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
const prefixCommon = createCommon(A, B);
const suffixCommon = createCommon(
A.slice(prefixCommon.length),
@ -159,6 +69,99 @@ export default function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
const diffTypesPtrOffset = routes.length / 2;
let ptr = 0;
let p = -1;
function backTrace<T>(
A: T[],
B: T[],
current: FarthestPoint,
swapped: boolean
): Array<{
type: DiffType;
value: T;
}> {
const M = A.length;
const N = B.length;
const result = [];
let a = M - 1;
let b = N - 1;
let j = routes[current.id];
let type = routes[current.id + diffTypesPtrOffset];
while (true) {
if (!j && !type) break;
const prev = j;
if (type === REMOVED) {
result.unshift({
type: (swapped ? "removed" : "added") as DiffType,
value: B[b]
});
b -= 1;
} else if (type === ADDED) {
result.unshift({
type: (swapped ? "added" : "removed") as DiffType,
value: A[a]
});
a -= 1;
} else {
result.unshift({ type: "common" as DiffType, value: A[a] });
a -= 1;
b -= 1;
}
j = routes[prev];
type = routes[prev + diffTypesPtrOffset];
}
return result;
}
function createFP(
slide: FarthestPoint,
down: FarthestPoint,
k: number,
M: number
): FarthestPoint {
if (slide && slide.y === -1 && (down && down.y === -1))
return { y: 0, id: 0 };
if (
(down && down.y === -1) ||
k === M ||
(slide && slide.y) > (down && down.y) + 1
) {
const prev = slide.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = ADDED;
return { y: slide.y, id: ptr };
} else {
const prev = down.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = REMOVED;
return { y: down.y + 1, id: ptr };
}
}
function snake<T>(
k: number,
slide: FarthestPoint,
down: FarthestPoint,
_offset: number,
A: T[],
B: T[]
): FarthestPoint {
const M = A.length;
const N = B.length;
if (k < -N || M < k) return { y: -1, id: -1 };
const fp = createFP(slide, down, k, M);
while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) {
const prev = fp.id;
ptr++;
fp.id = ptr;
fp.y += 1;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = COMMON;
}
return fp;
}
while (fp[delta + offset].y < N) {
p = p + 1;
for (let k = -p; k < delta; ++k) {

View file

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

View file

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

View file

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

View file

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

View file

@ -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",

View file

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

View file

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

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