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