mirror of
https://github.com/denoland/deno.git
synced 2025-01-12 17:09:00 -05:00
fix: eslint errors (#265)
This commit is contained in:
parent
4de86f04de
commit
61af419bbc
20 changed files with 355 additions and 349 deletions
|
@ -1,3 +0,0 @@
|
||||||
/flags/
|
|
||||||
/fs/
|
|
||||||
/ws/
|
|
|
@ -17,7 +17,7 @@
|
||||||
"@typescript-eslint/no-parameter-properties": ["off"],
|
"@typescript-eslint/no-parameter-properties": ["off"],
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
{ "argsIgnorePattern": "^_" }
|
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "_" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
92
flags/mod.ts
92
flags/mod.ts
|
@ -1,12 +1,12 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
export interface ArgParsingOptions {
|
export interface ArgParsingOptions {
|
||||||
unknown?: Function;
|
unknown?: Function;
|
||||||
boolean?: Boolean | string | string[];
|
boolean?: boolean | string | string[];
|
||||||
alias?: { [key: string]: string | string[] };
|
alias?: { [key: string]: string | string[] };
|
||||||
string?: string | string[];
|
string?: string | string[];
|
||||||
default?: { [key: string]: any };
|
default?: { [key: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
"--"?: Boolean;
|
"--"?: boolean;
|
||||||
stopEarly?: Boolean;
|
stopEarly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
|
@ -19,10 +19,27 @@ const DEFAULT_OPTIONS = {
|
||||||
stopEarly: false
|
stopEarly: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isNumber(x: unknown): boolean {
|
||||||
|
if (typeof x === "number") return true;
|
||||||
|
if (/^0x[0-9a-f]+$/i.test(String(x))) return true;
|
||||||
|
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasKey(obj, keys): boolean {
|
||||||
|
let o = obj;
|
||||||
|
keys.slice(0, -1).forEach(function(key) {
|
||||||
|
o = o[key] || {};
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = keys[keys.length - 1];
|
||||||
|
return key in o;
|
||||||
|
}
|
||||||
|
|
||||||
export function parse(
|
export function parse(
|
||||||
args,
|
args,
|
||||||
initialOptions?: ArgParsingOptions
|
initialOptions?: ArgParsingOptions
|
||||||
): { [key: string]: any } {
|
): { [key: string]: any } {
|
||||||
|
// eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
const options: ArgParsingOptions = {
|
const options: ArgParsingOptions = {
|
||||||
...DEFAULT_OPTIONS,
|
...DEFAULT_OPTIONS,
|
||||||
...(initialOptions || {})
|
...(initialOptions || {})
|
||||||
|
@ -72,18 +89,8 @@ export function parse(
|
||||||
const defaults = options.default!;
|
const defaults = options.default!;
|
||||||
|
|
||||||
const argv = { _: [] };
|
const argv = { _: [] };
|
||||||
Object.keys(flags.bools).forEach(function(key) {
|
|
||||||
setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
|
||||||
});
|
|
||||||
|
|
||||||
let notFlags = [];
|
function argDefined(key, arg): boolean {
|
||||||
|
|
||||||
if (args.indexOf("--") !== -1) {
|
|
||||||
notFlags = args.slice(args.indexOf("--") + 1);
|
|
||||||
args = args.slice(0, args.indexOf("--"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function argDefined(key, arg) {
|
|
||||||
return (
|
return (
|
||||||
(flags.allBools && /^--[^=]+$/.test(arg)) ||
|
(flags.allBools && /^--[^=]+$/.test(arg)) ||
|
||||||
flags.strings[key] ||
|
flags.strings[key] ||
|
||||||
|
@ -92,19 +99,6 @@ export function parse(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setArg(key, val, arg = null): void {
|
|
||||||
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
|
||||||
if (flags.unknownFn(arg) === false) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = !flags.strings[key] && isNumber(val) ? Number(val) : val;
|
|
||||||
setKey(argv, key.split("."), value);
|
|
||||||
|
|
||||||
(aliases[key] || []).forEach(function(x) {
|
|
||||||
setKey(argv, x.split("."), value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setKey(obj, keys, value): void {
|
function setKey(obj, keys, value): void {
|
||||||
let o = obj;
|
let o = obj;
|
||||||
keys.slice(0, -1).forEach(function(key) {
|
keys.slice(0, -1).forEach(function(key) {
|
||||||
|
@ -126,12 +120,36 @@ export function parse(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setArg(key, val, arg = null): void {
|
||||||
|
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
||||||
|
if (flags.unknownFn(arg) === false) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = !flags.strings[key] && isNumber(val) ? Number(val) : val;
|
||||||
|
setKey(argv, key.split("."), value);
|
||||||
|
|
||||||
|
(aliases[key] || []).forEach(function(x) {
|
||||||
|
setKey(argv, x.split("."), value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function aliasIsBoolean(key): boolean {
|
function aliasIsBoolean(key): boolean {
|
||||||
return aliases[key].some(function(x) {
|
return aliases[key].some(function(x) {
|
||||||
return flags.bools[x];
|
return flags.bools[x];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.keys(flags.bools).forEach(function(key) {
|
||||||
|
setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
let notFlags = [];
|
||||||
|
|
||||||
|
if (args.indexOf("--") !== -1) {
|
||||||
|
notFlags = args.slice(args.indexOf("--") + 1);
|
||||||
|
args = args.slice(0, args.indexOf("--"));
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
const arg = args[i];
|
const arg = args[i];
|
||||||
|
|
||||||
|
@ -242,7 +260,7 @@ export function parse(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options["--"]) {
|
if (options["--"]) {
|
||||||
argv["--"] = new Array();
|
argv["--"] = [];
|
||||||
notFlags.forEach(function(key) {
|
notFlags.forEach(function(key) {
|
||||||
argv["--"].push(key);
|
argv["--"].push(key);
|
||||||
});
|
});
|
||||||
|
@ -254,19 +272,3 @@ export function parse(
|
||||||
|
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasKey(obj, keys) {
|
|
||||||
let o = obj;
|
|
||||||
keys.slice(0, -1).forEach(function(key) {
|
|
||||||
o = o[key] || {};
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = keys[keys.length - 1];
|
|
||||||
return key in o;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNumber(x: any): boolean {
|
|
||||||
if (typeof x === "number") return true;
|
|
||||||
if (/^0x[0-9a-f]+$/i.test(x)) return true;
|
|
||||||
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
|
|
||||||
}
|
|
||||||
|
|
|
@ -39,9 +39,6 @@ test(function booleanGroups() {
|
||||||
test(function booleanAndAliasWithChainableApi() {
|
test(function booleanAndAliasWithChainableApi() {
|
||||||
const aliased = ["-h", "derp"];
|
const aliased = ["-h", "derp"];
|
||||||
const regular = ["--herp", "derp"];
|
const regular = ["--herp", "derp"];
|
||||||
const opts = {
|
|
||||||
herp: { alias: "h", boolean: true }
|
|
||||||
};
|
|
||||||
const aliasedArgv = parse(aliased, {
|
const aliasedArgv = parse(aliased, {
|
||||||
boolean: "herp",
|
boolean: "herp",
|
||||||
alias: { h: "herp" }
|
alias: { h: "herp" }
|
||||||
|
|
|
@ -178,7 +178,7 @@ test(function nestedDottedObjects() {
|
||||||
"4",
|
"4",
|
||||||
"--foo.quux.quibble",
|
"--foo.quux.quibble",
|
||||||
"5",
|
"5",
|
||||||
"--foo.quux.o_O",
|
"--foo.quux.oO",
|
||||||
"--beep.boop"
|
"--beep.boop"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ test(function nestedDottedObjects() {
|
||||||
baz: 4,
|
baz: 4,
|
||||||
quux: {
|
quux: {
|
||||||
quibble: 5,
|
quibble: 5,
|
||||||
o_O: true
|
oO: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertEquals(argv.beep, { boop: true });
|
assertEquals(argv.beep, { boop: true });
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { parse } from "../mod.ts";
|
||||||
|
|
||||||
test(function booleanAndAliasIsNotUnknown() {
|
test(function booleanAndAliasIsNotUnknown() {
|
||||||
const unknown = [];
|
const unknown = [];
|
||||||
function unknownFn(arg) {
|
function unknownFn(arg): boolean {
|
||||||
unknown.push(arg);
|
unknown.push(arg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,15 @@ test(function booleanAndAliasIsNotUnknown() {
|
||||||
boolean: "h",
|
boolean: "h",
|
||||||
unknown: unknownFn
|
unknown: unknownFn
|
||||||
};
|
};
|
||||||
const aliasedArgv = parse(aliased, opts);
|
parse(aliased, opts);
|
||||||
const propertyArgv = parse(regular, opts);
|
parse(regular, opts);
|
||||||
|
|
||||||
assertEquals(unknown, ["--derp", "-d"]);
|
assertEquals(unknown, ["--derp", "-d"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown() {
|
test(function flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown() {
|
||||||
const unknown = [];
|
const unknown = [];
|
||||||
function unknownFn(arg) {
|
function unknownFn(arg): boolean {
|
||||||
unknown.push(arg);
|
unknown.push(arg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ test(function flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown() {
|
||||||
|
|
||||||
test(function stringAndAliasIsNotUnkown() {
|
test(function stringAndAliasIsNotUnkown() {
|
||||||
const unknown = [];
|
const unknown = [];
|
||||||
function unknownFn(arg) {
|
function unknownFn(arg): boolean {
|
||||||
unknown.push(arg);
|
unknown.push(arg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -52,15 +52,15 @@ test(function stringAndAliasIsNotUnkown() {
|
||||||
string: "h",
|
string: "h",
|
||||||
unknown: unknownFn
|
unknown: unknownFn
|
||||||
};
|
};
|
||||||
const aliasedArgv = parse(aliased, opts);
|
parse(aliased, opts);
|
||||||
const propertyArgv = parse(regular, opts);
|
parse(regular, opts);
|
||||||
|
|
||||||
assertEquals(unknown, ["--derp", "-d"]);
|
assertEquals(unknown, ["--derp", "-d"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function defaultAndAliasIsNotUnknown() {
|
test(function defaultAndAliasIsNotUnknown() {
|
||||||
const unknown = [];
|
const unknown = [];
|
||||||
function unknownFn(arg) {
|
function unknownFn(arg): boolean {
|
||||||
unknown.push(arg);
|
unknown.push(arg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -71,15 +71,15 @@ test(function defaultAndAliasIsNotUnknown() {
|
||||||
alias: { h: "herp" },
|
alias: { h: "herp" },
|
||||||
unknown: unknownFn
|
unknown: unknownFn
|
||||||
};
|
};
|
||||||
const aliasedArgv = parse(aliased, opts);
|
parse(aliased, opts);
|
||||||
const propertyArgv = parse(regular, opts);
|
parse(regular, opts);
|
||||||
|
|
||||||
assertEquals(unknown, []);
|
assertEquals(unknown, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function valueFollowingDoubleHyphenIsNotUnknown() {
|
test(function valueFollowingDoubleHyphenIsNotUnknown() {
|
||||||
const unknown = [];
|
const unknown = [];
|
||||||
function unknownFn(arg) {
|
function unknownFn(arg): boolean {
|
||||||
unknown.push(arg);
|
unknown.push(arg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ const { exit, args, execPath } = Deno;
|
||||||
import { parse } from "./flags/mod.ts";
|
import { parse } from "./flags/mod.ts";
|
||||||
import { xrun } from "./prettier/util.ts";
|
import { xrun } from "./prettier/util.ts";
|
||||||
|
|
||||||
async function main(opts) {
|
async function main(opts): Promise<void> {
|
||||||
const args = [
|
const args = [
|
||||||
execPath,
|
execPath,
|
||||||
"--allow-write",
|
"--allow-write",
|
||||||
|
|
|
@ -14,16 +14,16 @@ async function touch(path: string): Promise<void> {
|
||||||
async function walkArray(
|
async function walkArray(
|
||||||
dirname: string = ".",
|
dirname: string = ".",
|
||||||
options: WalkOptions = {}
|
options: WalkOptions = {}
|
||||||
): Promise<Array<string>> {
|
): Promise<string[]> {
|
||||||
const arr: string[] = [];
|
const arr: string[] = [];
|
||||||
for await (const f of walk(dirname, { ...options })) {
|
for await (const f of walk(dirname, { ...options })) {
|
||||||
arr.push(f.path.replace(/\\/g, "/"));
|
arr.push(f.path.replace(/\\/g, "/"));
|
||||||
}
|
}
|
||||||
arr.sort();
|
arr.sort();
|
||||||
const arr_sync = Array.from(walkSync(dirname, options), (f: FileInfo) =>
|
const arrSync = Array.from(walkSync(dirname, options), (f: FileInfo) =>
|
||||||
f.path.replace(/\\/g, "/")
|
f.path.replace(/\\/g, "/")
|
||||||
).sort();
|
).sort();
|
||||||
assertEquals(arr, arr_sync);
|
assertEquals(arr, arrSync);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,15 @@ const WILDCARD = `([^/]*)`;
|
||||||
const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`;
|
const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`;
|
||||||
const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`;
|
const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`;
|
||||||
|
|
||||||
|
export interface GlobrexResult {
|
||||||
|
regex: RegExp;
|
||||||
|
path?: {
|
||||||
|
regex: string | RegExp;
|
||||||
|
segments: RegExp[];
|
||||||
|
globstar?: RegExp;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert any glob pattern to a JavaScript Regexp object
|
* Convert any glob pattern to a JavaScript Regexp object
|
||||||
* @param {String} glob Glob pattern to convert
|
* @param {String} glob Glob pattern to convert
|
||||||
|
@ -32,12 +41,12 @@ export function globrex(
|
||||||
filepath = false,
|
filepath = false,
|
||||||
flags = ""
|
flags = ""
|
||||||
}: GlobOptions = {}
|
}: GlobOptions = {}
|
||||||
) {
|
): GlobrexResult {
|
||||||
let regex = "";
|
let regex = "";
|
||||||
let segment = "";
|
let segment = "";
|
||||||
let path: {
|
let path: {
|
||||||
regex: string | RegExp;
|
regex: string | RegExp;
|
||||||
segments: Array<RegExp>;
|
segments: RegExp[];
|
||||||
globstar?: RegExp;
|
globstar?: RegExp;
|
||||||
} = { regex: "", segments: [] };
|
} = { regex: "", segments: [] };
|
||||||
|
|
||||||
|
@ -59,7 +68,7 @@ export function globrex(
|
||||||
function add(
|
function add(
|
||||||
str,
|
str,
|
||||||
options: AddOptions = { split: false, last: false, only: "" }
|
options: AddOptions = { split: false, last: false, only: "" }
|
||||||
) {
|
): void {
|
||||||
const { split, last, only } = options;
|
const { split, last, only } = options;
|
||||||
if (only !== "path") regex += str;
|
if (only !== "path") regex += str;
|
||||||
if (filepath && only !== "regex") {
|
if (filepath && only !== "regex") {
|
||||||
|
@ -283,14 +292,7 @@ export function globrex(
|
||||||
if (filepath) path.regex = `^${path.regex}$`;
|
if (filepath) path.regex = `^${path.regex}$`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: {
|
const result: GlobrexResult = { regex: new RegExp(regex, flags) };
|
||||||
regex: RegExp;
|
|
||||||
path?: {
|
|
||||||
regex: string | RegExp;
|
|
||||||
segments: Array<RegExp>;
|
|
||||||
globstar?: RegExp;
|
|
||||||
};
|
|
||||||
} = { regex: new RegExp(regex, flags) };
|
|
||||||
|
|
||||||
// Push the last segment
|
// Push the last segment
|
||||||
if (filepath) {
|
if (filepath) {
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
import { test } from "../testing/mod.ts";
|
import { test } from "../testing/mod.ts";
|
||||||
import { assertEquals } from "../testing/asserts.ts";
|
import { assertEquals } from "../testing/asserts.ts";
|
||||||
import { globrex } from "./globrex.ts";
|
import { globrex, GlobrexResult } from "./globrex.ts";
|
||||||
|
|
||||||
const isWin = Deno.build.os === "win";
|
const isWin = Deno.build.os === "win";
|
||||||
const t = { equal: assertEquals, is: assertEquals };
|
const t = { equal: assertEquals, is: assertEquals };
|
||||||
|
|
||||||
function match(glob, strUnix, strWin?, opts = {}) {
|
function match(glob, strUnix, strWin?, opts = {}): boolean {
|
||||||
if (typeof strWin === "object") {
|
if (typeof strWin === "object") {
|
||||||
opts = strWin;
|
opts = strWin;
|
||||||
strWin = false;
|
strWin = false;
|
||||||
|
@ -18,14 +18,14 @@ function match(glob, strUnix, strWin?, opts = {}) {
|
||||||
return res.regex.test(isWin && strWin ? strWin : strUnix);
|
return res.regex.test(isWin && strWin ? strWin : strUnix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchRegex(t, pattern, ifUnix, ifWin, opts) {
|
function matchRegex(t, pattern, ifUnix, ifWin, opts): GlobrexResult {
|
||||||
const res = globrex(pattern, opts);
|
const res = globrex(pattern, opts);
|
||||||
const { regex } = opts.filepath ? res.path : res;
|
const { regex } = opts.filepath ? res.path : res;
|
||||||
t.is(regex.toString(), isWin ? ifWin : ifUnix, "~> regex matches expectant");
|
t.is(regex.toString(), isWin ? ifWin : ifUnix, "~> regex matches expectant");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchSegments(t, pattern, ifUnix, ifWin, opts) {
|
function matchSegments(t, pattern, ifUnix, ifWin, opts): GlobrexResult {
|
||||||
const res = globrex(pattern, { filepath: true, ...opts });
|
const res = globrex(pattern, { filepath: true, ...opts });
|
||||||
const str = res.path.segments.join(" ");
|
const str = res.path.segments.join(" ");
|
||||||
const exp = (isWin ? ifWin : ifUnix).join(" ");
|
const exp = (isWin ? ifWin : ifUnix).join(" ");
|
||||||
|
@ -191,7 +191,7 @@ test({
|
||||||
t.equal(match("f?o", "fooo", { extended: true }), false);
|
t.equal(match("f?o", "fooo", { extended: true }), false);
|
||||||
t.equal(match("f?oo", "foo", { extended: true }), false);
|
t.equal(match("f?oo", "foo", { extended: true }), false);
|
||||||
|
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
t.equal(
|
t.equal(
|
||||||
match("f?o", "foo", { extended: true, globstar, flags: "g" }),
|
match("f?o", "foo", { extended: true, globstar, flags: "g" }),
|
||||||
true
|
true
|
||||||
|
@ -235,7 +235,7 @@ test({
|
||||||
t.equal(match("fo[!tz]", "fot", { extended: true }), false);
|
t.equal(match("fo[!tz]", "fot", { extended: true }), false);
|
||||||
t.equal(match("fo[!tz]", "fob", { extended: true }), true);
|
t.equal(match("fo[!tz]", "fob", { extended: true }), true);
|
||||||
|
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
t.equal(
|
t.equal(
|
||||||
match("fo[oz]", "foo", { extended: true, globstar, flags: "g" }),
|
match("fo[oz]", "foo", { extended: true, globstar, flags: "g" }),
|
||||||
true
|
true
|
||||||
|
@ -321,7 +321,7 @@ test({
|
||||||
t.equal(match("foo{bar,baaz}", "foobuzz", { extended: true }), false);
|
t.equal(match("foo{bar,baaz}", "foobuzz", { extended: true }), false);
|
||||||
t.equal(match("foo{bar,b*z}", "foobuzz", { extended: true }), true);
|
t.equal(match("foo{bar,b*z}", "foobuzz", { extended: true }), true);
|
||||||
|
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
t.equal(
|
t.equal(
|
||||||
match("foo{bar,baaz}", "foobaaz", {
|
match("foo{bar,baaz}", "foobaaz", {
|
||||||
extended: true,
|
extended: true,
|
||||||
|
@ -405,7 +405,7 @@ test({
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
t.equal(
|
t.equal(
|
||||||
match(
|
match(
|
||||||
"http://?o[oz].b*z.com/{*.js,*.html}",
|
"http://?o[oz].b*z.com/{*.js,*.html}",
|
||||||
|
@ -456,7 +456,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: "globrex: standard globstar",
|
name: "globrex: standard globstar",
|
||||||
fn() {
|
fn() {
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
t.equal(
|
t.equal(
|
||||||
match(
|
match(
|
||||||
"http://foo.com/**/{*.js,*.html}",
|
"http://foo.com/**/{*.js,*.html}",
|
||||||
|
@ -491,7 +491,7 @@ test({
|
||||||
test({
|
test({
|
||||||
name: "globrex: remaining chars should match themself",
|
name: "globrex: remaining chars should match themself",
|
||||||
fn() {
|
fn() {
|
||||||
const tester = globstar => {
|
const tester = (globstar): void => {
|
||||||
const testExtStr = "\\/$^+.()=!|,.*";
|
const testExtStr = "\\/$^+.()=!|,.*";
|
||||||
t.equal(match(testExtStr, testExtStr, { extended: true }), true);
|
t.equal(match(testExtStr, testExtStr, { extended: true }), true);
|
||||||
t.equal(
|
t.equal(
|
||||||
|
@ -849,7 +849,6 @@ test({
|
||||||
name: "globrex: filepath path segments",
|
name: "globrex: filepath path segments",
|
||||||
fn() {
|
fn() {
|
||||||
let opts = { extended: true },
|
let opts = { extended: true },
|
||||||
res,
|
|
||||||
win,
|
win,
|
||||||
unix;
|
unix;
|
||||||
|
|
||||||
|
|
|
@ -847,7 +847,7 @@ export const win32 = {
|
||||||
parse(path: string): ParsedPath {
|
parse(path: string): ParsedPath {
|
||||||
assertPath(path);
|
assertPath(path);
|
||||||
|
|
||||||
let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath;
|
let ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" };
|
||||||
|
|
||||||
const len = path.length;
|
const len = path.length;
|
||||||
if (len === 0) return ret;
|
if (len === 0) return ret;
|
||||||
|
@ -1324,7 +1324,7 @@ export const posix = {
|
||||||
parse(path: string): ParsedPath {
|
parse(path: string): ParsedPath {
|
||||||
assertPath(path);
|
assertPath(path);
|
||||||
|
|
||||||
let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath;
|
let ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" };
|
||||||
if (path.length === 0) return ret;
|
if (path.length === 0) return ret;
|
||||||
let isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
let isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
||||||
let start: number;
|
let start: number;
|
||||||
|
|
|
@ -79,6 +79,39 @@ const unixSpecialCaseFormatTests = [
|
||||||
[{}, ""]
|
[{}, ""]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function checkParseFormat(path, paths): void {
|
||||||
|
paths.forEach(function(p) {
|
||||||
|
const element = p[0];
|
||||||
|
const output = path.parse(element);
|
||||||
|
assertEquals(typeof output.root, "string");
|
||||||
|
assertEquals(typeof output.dir, "string");
|
||||||
|
assertEquals(typeof output.base, "string");
|
||||||
|
assertEquals(typeof output.ext, "string");
|
||||||
|
assertEquals(typeof output.name, "string");
|
||||||
|
assertEquals(path.format(output), element);
|
||||||
|
assertEquals(output.rooroot, undefined);
|
||||||
|
assertEquals(output.dir, output.dir ? path.dirname(element) : "");
|
||||||
|
assertEquals(output.base, path.basename(element));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSpecialCaseParseFormat(path, testCases): void {
|
||||||
|
testCases.forEach(function(testCase) {
|
||||||
|
const element = testCase[0];
|
||||||
|
const expect = testCase[1];
|
||||||
|
const output = path.parse(element);
|
||||||
|
Object.keys(expect).forEach(function(key) {
|
||||||
|
assertEquals(output[key], expect[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFormat(path, testCases): void {
|
||||||
|
testCases.forEach(function(testCase) {
|
||||||
|
assertEquals(path.format(testCase[0]), testCase[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test(function parseWin32() {
|
test(function parseWin32() {
|
||||||
checkParseFormat(path.win32, winPaths);
|
checkParseFormat(path.win32, winPaths);
|
||||||
checkSpecialCaseParseFormat(path.win32, winSpecialCaseParseTests);
|
checkSpecialCaseParseFormat(path.win32, winSpecialCaseParseTests);
|
||||||
|
@ -116,6 +149,7 @@ const windowsTrailingTests = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
const posixTrailingTests = [
|
const posixTrailingTests = [
|
||||||
["./", { root: "", dir: "", base: ".", ext: "", name: "." }],
|
["./", { root: "", dir: "", base: ".", ext: "", name: "." }],
|
||||||
["//", { root: "/", dir: "/", base: "", ext: "", name: "" }],
|
["//", { root: "/", dir: "/", base: "", ext: "", name: "" }],
|
||||||
|
@ -127,39 +161,6 @@ const posixTrailingTests = [
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
function checkParseFormat(path, paths) {
|
|
||||||
paths.forEach(function(p) {
|
|
||||||
const element = p[0];
|
|
||||||
const output = path.parse(element);
|
|
||||||
assertEquals(typeof output.root, "string");
|
|
||||||
assertEquals(typeof output.dir, "string");
|
|
||||||
assertEquals(typeof output.base, "string");
|
|
||||||
assertEquals(typeof output.ext, "string");
|
|
||||||
assertEquals(typeof output.name, "string");
|
|
||||||
assertEquals(path.format(output), element);
|
|
||||||
assertEquals(output.rooroot, undefined);
|
|
||||||
assertEquals(output.dir, output.dir ? path.dirname(element) : "");
|
|
||||||
assertEquals(output.base, path.basename(element));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSpecialCaseParseFormat(path, testCases) {
|
|
||||||
testCases.forEach(function(testCase) {
|
|
||||||
const element = testCase[0];
|
|
||||||
const expect = testCase[1];
|
|
||||||
const output = path.parse(element);
|
|
||||||
Object.keys(expect).forEach(function(key) {
|
|
||||||
assertEquals(output[key], expect[key]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkFormat(path, testCases) {
|
|
||||||
testCases.forEach(function(testCase) {
|
|
||||||
assertEquals(path.format(testCase[0]), testCase[1]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test(function parseTrailingWin32() {
|
test(function parseTrailingWin32() {
|
||||||
windowsTrailingTests.forEach(function(p) {
|
windowsTrailingTests.forEach(function(p) {
|
||||||
const actual = path.win32.parse(p[0] as string);
|
const actual = path.win32.parse(p[0] as string);
|
||||||
|
|
93
fs/walk.ts
93
fs/walk.ts
|
@ -6,9 +6,52 @@ export interface WalkOptions {
|
||||||
exts?: string[];
|
exts?: string[];
|
||||||
match?: RegExp[];
|
match?: RegExp[];
|
||||||
skip?: RegExp[];
|
skip?: RegExp[];
|
||||||
// FIXME don't use `any` here?
|
onError?: (err: Error) => void;
|
||||||
onError?: (err: any) => void;
|
followSymlinks?: boolean;
|
||||||
followSymlinks?: Boolean;
|
}
|
||||||
|
|
||||||
|
function patternTest(patterns: RegExp[], path: string): boolean {
|
||||||
|
// Forced to reset last index on regex while iterating for have
|
||||||
|
// consistent results.
|
||||||
|
// See: https://stackoverflow.com/a/1520853
|
||||||
|
return patterns.some(pattern => {
|
||||||
|
let r = pattern.test(path);
|
||||||
|
pattern.lastIndex = 0;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function include(f: FileInfo, options: WalkOptions): boolean {
|
||||||
|
if (options.exts && !options.exts.some(ext => f.path.endsWith(ext))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (options.match && !patternTest(options.match, f.path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (options.skip && patternTest(options.skip, f.path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolve(f: FileInfo): Promise<FileInfo> {
|
||||||
|
// This is the full path, unfortunately if we were to make it relative
|
||||||
|
// it could resolve to a symlink and cause an infinite loop.
|
||||||
|
const fpath = await readlink(f.path);
|
||||||
|
f = await stat(fpath);
|
||||||
|
// workaround path not being returned by stat
|
||||||
|
f.path = fpath;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSync(f: FileInfo): FileInfo {
|
||||||
|
// This is the full path, unfortunately if we were to make it relative
|
||||||
|
// it could resolve to a symlink and cause an infinite loop.
|
||||||
|
const fpath = readlinkSync(f.path);
|
||||||
|
f = statSync(fpath);
|
||||||
|
// workaround path not being returned by stat
|
||||||
|
f.path = fpath;
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate all files in a directory recursively.
|
/** Generate all files in a directory recursively.
|
||||||
|
@ -94,47 +137,3 @@ export function* walkSync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function include(f: FileInfo, options: WalkOptions): Boolean {
|
|
||||||
if (options.exts && !options.exts.some(ext => f.path.endsWith(ext))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (options.match && !patternTest(options.match, f.path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (options.skip && patternTest(options.skip, f.path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function patternTest(patterns: RegExp[], path: string) {
|
|
||||||
// Forced to reset last index on regex while iterating for have
|
|
||||||
// consistent results.
|
|
||||||
// See: https://stackoverflow.com/a/1520853
|
|
||||||
return patterns.some(pattern => {
|
|
||||||
let r = pattern.test(path);
|
|
||||||
pattern.lastIndex = 0;
|
|
||||||
return r;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resolve(f: FileInfo): Promise<FileInfo> {
|
|
||||||
// This is the full path, unfortunately if we were to make it relative
|
|
||||||
// it could resolve to a symlink and cause an infinite loop.
|
|
||||||
const fpath = await readlink(f.path);
|
|
||||||
f = await stat(fpath);
|
|
||||||
// workaround path not being returned by stat
|
|
||||||
f.path = fpath;
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveSync(f: FileInfo): FileInfo {
|
|
||||||
// This is the full path, unfortunately if we were to make it relative
|
|
||||||
// it could resolve to a symlink and cause an infinite loop.
|
|
||||||
const fpath = readlinkSync(f.path);
|
|
||||||
f = statSync(fpath);
|
|
||||||
// workaround path not being returned by stat
|
|
||||||
f.path = fpath;
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,15 +11,15 @@ export async function testWalk(
|
||||||
t: TestFunction
|
t: TestFunction
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const name = t.name;
|
const name = t.name;
|
||||||
async function fn() {
|
async function fn(): Promise<void> {
|
||||||
const orig_cwd = cwd();
|
const origCwd = cwd();
|
||||||
const d = await makeTempDir();
|
const d = await makeTempDir();
|
||||||
chdir(d);
|
chdir(d);
|
||||||
try {
|
try {
|
||||||
await setup(d);
|
await setup(d);
|
||||||
await t();
|
await t();
|
||||||
} finally {
|
} finally {
|
||||||
chdir(orig_cwd);
|
chdir(origCwd);
|
||||||
remove(d, { recursive: true });
|
remove(d, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,23 +29,23 @@ export async function testWalk(
|
||||||
async function walkArray(
|
async function walkArray(
|
||||||
dirname: string = ".",
|
dirname: string = ".",
|
||||||
options: WalkOptions = {}
|
options: WalkOptions = {}
|
||||||
): Promise<Array<string>> {
|
): Promise<string[]> {
|
||||||
const arr: string[] = [];
|
const arr: string[] = [];
|
||||||
for await (const f of walk(dirname, { ...options })) {
|
for await (const f of walk(dirname, { ...options })) {
|
||||||
arr.push(f.path.replace(/\\/g, "/"));
|
arr.push(f.path.replace(/\\/g, "/"));
|
||||||
}
|
}
|
||||||
arr.sort();
|
arr.sort();
|
||||||
const arr_sync = Array.from(walkSync(dirname, options), (f: FileInfo) =>
|
const arrSync = Array.from(walkSync(dirname, options), (f: FileInfo) =>
|
||||||
f.path.replace(/\\/g, "/")
|
f.path.replace(/\\/g, "/")
|
||||||
).sort();
|
).sort();
|
||||||
assertEquals(arr, arr_sync);
|
assertEquals(arr, arrSync);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function touch(path: string): Promise<void> {
|
async function touch(path: string): Promise<void> {
|
||||||
await open(path, "w");
|
await open(path, "w");
|
||||||
}
|
}
|
||||||
function assertReady(expectedLength: number) {
|
function assertReady(expectedLength: number): void {
|
||||||
const arr = Array.from(walkSync(), (f: FileInfo) => f.path);
|
const arr = Array.from(walkSync(), (f: FileInfo) => f.path);
|
||||||
assertEquals(arr.length, expectedLength);
|
assertEquals(arr.length, expectedLength);
|
||||||
}
|
}
|
||||||
|
@ -77,11 +77,11 @@ testWalk(
|
||||||
},
|
},
|
||||||
async function iteratable() {
|
async function iteratable() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const f of walkSync()) {
|
for (const _ of walkSync()) {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
assertEquals(count, 1);
|
assertEquals(count, 1);
|
||||||
for await (const f of walk()) {
|
for await (const _ of walk()) {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
assertEquals(count, 2);
|
assertEquals(count, 2);
|
||||||
|
@ -107,11 +107,11 @@ testWalk(
|
||||||
},
|
},
|
||||||
async function depth() {
|
async function depth() {
|
||||||
assertReady(1);
|
assertReady(1);
|
||||||
const arr_3 = await walkArray(".", { maxDepth: 3 });
|
const arr3 = await walkArray(".", { maxDepth: 3 });
|
||||||
assertEquals(arr_3.length, 0);
|
assertEquals(arr3.length, 0);
|
||||||
const arr_5 = await walkArray(".", { maxDepth: 5 });
|
const arr5 = await walkArray(".", { maxDepth: 5 });
|
||||||
assertEquals(arr_5.length, 1);
|
assertEquals(arr5.length, 1);
|
||||||
assertEquals(arr_5[0], "./a/b/c/d/x");
|
assertEquals(arr5[0], "./a/b/c/d/x");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -214,12 +214,12 @@ testWalk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
testWalk(async (d: string) => {}, async function onError() {
|
testWalk(async (_d: string) => {}, async function onError() {
|
||||||
assertReady(0);
|
assertReady(0);
|
||||||
const ignored = await walkArray("missing");
|
const ignored = await walkArray("missing");
|
||||||
assertEquals(ignored.length, 0);
|
assertEquals(ignored.length, 0);
|
||||||
let errors = 0;
|
let errors = 0;
|
||||||
const arr = await walkArray("missing", { onError: e => (errors += 1) });
|
await walkArray("missing", { onError: _e => (errors += 1) });
|
||||||
// It's 2 since walkArray iterates over both sync and async.
|
// It's 2 since walkArray iterates over both sync and async.
|
||||||
assertEquals(errors, 2);
|
assertEquals(errors, 2);
|
||||||
});
|
});
|
||||||
|
|
|
@ -188,7 +188,7 @@ async function serveDir(
|
||||||
async function serveFallback(req: ServerRequest, e: Error): Promise<Response> {
|
async function serveFallback(req: ServerRequest, e: Error): Promise<Response> {
|
||||||
if (
|
if (
|
||||||
e instanceof Deno.DenoError &&
|
e instanceof Deno.DenoError &&
|
||||||
(e as Deno.DenoError<any>).kind === ErrorKind.NotFound
|
(e as Deno.DenoError<Deno.ErrorKind.NotFound>).kind === ErrorKind.NotFound
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
status: 404,
|
status: 404,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { BufReader } from "../io/bufio.ts";
|
||||||
import { TextProtoReader } from "../textproto/mod.ts";
|
import { TextProtoReader } from "../textproto/mod.ts";
|
||||||
|
|
||||||
let fileServer;
|
let fileServer;
|
||||||
async function startFileServer() {
|
async function startFileServer(): Promise<void> {
|
||||||
fileServer = run({
|
fileServer = run({
|
||||||
args: [
|
args: [
|
||||||
"deno",
|
"deno",
|
||||||
|
@ -25,7 +25,7 @@ async function startFileServer() {
|
||||||
assert(err == null);
|
assert(err == null);
|
||||||
assert(s.includes("server listening"));
|
assert(s.includes("server listening"));
|
||||||
}
|
}
|
||||||
function killFileServer() {
|
function killFileServer(): void {
|
||||||
fileServer.close();
|
fileServer.close();
|
||||||
fileServer.stdout.close();
|
fileServer.stdout.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export function setContentLength(r: Response): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function writeChunkedBody(w: Writer, r: Reader) {
|
async function writeChunkedBody(w: Writer, r: Reader): Promise<void> {
|
||||||
const writer = bufWriter(w);
|
const writer = bufWriter(w);
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ export class ServerRequest {
|
||||||
r: BufReader;
|
r: BufReader;
|
||||||
w: BufWriter;
|
w: BufWriter;
|
||||||
|
|
||||||
public async *bodyStream() {
|
public async *bodyStream(): AsyncIterableIterator<Uint8Array> {
|
||||||
if (this.headers.has("content-length")) {
|
if (this.headers.has("content-length")) {
|
||||||
const len = +this.headers.get("content-length");
|
const len = +this.headers.get("content-length");
|
||||||
if (Number.isNaN(len)) {
|
if (Number.isNaN(len)) {
|
||||||
|
@ -263,7 +263,11 @@ async function readRequest(
|
||||||
return [req, err];
|
return [req, err];
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeHandleReq(env: ServeEnv, conn: Conn, maybeReq: any) {
|
function maybeHandleReq(
|
||||||
|
env: ServeEnv,
|
||||||
|
conn: Conn,
|
||||||
|
maybeReq: [ServerRequest, BufState]
|
||||||
|
): void {
|
||||||
const [req, _err] = maybeReq;
|
const [req, _err] = maybeReq;
|
||||||
if (_err) {
|
if (_err) {
|
||||||
conn.close(); // assume EOF for now...
|
conn.close(); // assume EOF for now...
|
||||||
|
@ -273,11 +277,13 @@ function maybeHandleReq(env: ServeEnv, conn: Conn, maybeReq: any) {
|
||||||
env.serveDeferred.resolve(); // signal while loop to process it
|
env.serveDeferred.resolve(); // signal while loop to process it
|
||||||
}
|
}
|
||||||
|
|
||||||
function serveConn(env: ServeEnv, conn: Conn, bufr?: BufReader) {
|
function serveConn(env: ServeEnv, conn: Conn, bufr?: BufReader): void {
|
||||||
readRequest(conn, bufr).then(maybeHandleReq.bind(null, env, conn));
|
readRequest(conn, bufr).then(maybeHandleReq.bind(null, env, conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* serve(addr: string) {
|
export async function* serve(
|
||||||
|
addr: string
|
||||||
|
): AsyncIterableIterator<ServerRequest> {
|
||||||
const listener = listen("tcp", addr);
|
const listener = listen("tcp", addr);
|
||||||
const env: ServeEnv = {
|
const env: ServeEnv = {
|
||||||
reqQueue: [], // in case multiple promises are ready
|
reqQueue: [], // in case multiple promises are ready
|
||||||
|
@ -285,9 +291,9 @@ export async function* serve(addr: string) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Routine that keeps calling accept
|
// Routine that keeps calling accept
|
||||||
let handleConn = (_conn: Conn) => {};
|
let handleConn = (_conn: Conn): void => {};
|
||||||
let scheduleAccept = () => {};
|
let scheduleAccept = (): void => {};
|
||||||
const acceptRoutine = () => {
|
const acceptRoutine = (): void => {
|
||||||
scheduleAccept = () => {
|
scheduleAccept = () => {
|
||||||
listener.accept().then(handleConn);
|
listener.accept().then(handleConn);
|
||||||
};
|
};
|
||||||
|
@ -320,7 +326,7 @@ export async function* serve(addr: string) {
|
||||||
export async function listenAndServe(
|
export async function listenAndServe(
|
||||||
addr: string,
|
addr: string,
|
||||||
handler: (req: ServerRequest) => void
|
handler: (req: ServerRequest) => void
|
||||||
) {
|
): Promise<void> {
|
||||||
const server = serve(addr);
|
const server = serve(addr);
|
||||||
|
|
||||||
for await (const request of server) {
|
for await (const request of server) {
|
||||||
|
|
|
@ -161,7 +161,7 @@ export function assertArrayContains(
|
||||||
actual: unknown[],
|
actual: unknown[],
|
||||||
expected: unknown[],
|
expected: unknown[],
|
||||||
msg?: string
|
msg?: string
|
||||||
) {
|
): void {
|
||||||
let missing = [];
|
let missing = [];
|
||||||
for (let i = 0; i < expected.length; i++) {
|
for (let i = 0; i < expected.length; i++) {
|
||||||
let found = false;
|
let found = false;
|
||||||
|
|
281
ws/mod.ts
281
ws/mod.ts
|
@ -23,10 +23,10 @@ export type WebSocketEvent =
|
||||||
| WebSocketPingEvent
|
| WebSocketPingEvent
|
||||||
| WebSocketPongEvent;
|
| WebSocketPongEvent;
|
||||||
|
|
||||||
export type WebSocketCloseEvent = {
|
export interface WebSocketCloseEvent {
|
||||||
code: number;
|
code: number;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function isWebSocketCloseEvent(a): a is WebSocketCloseEvent {
|
export function isWebSocketCloseEvent(a): a is WebSocketCloseEvent {
|
||||||
return a && typeof a["code"] === "number";
|
return a && typeof a["code"] === "number";
|
||||||
|
@ -47,7 +47,7 @@ export function isWebSocketPongEvent(a): a is WebSocketPongEvent {
|
||||||
export type WebSocketMessage = string | Uint8Array;
|
export type WebSocketMessage = string | Uint8Array;
|
||||||
|
|
||||||
// TODO move this to common/util module
|
// TODO move this to common/util module
|
||||||
export function append(a: Uint8Array, b: Uint8Array) {
|
export function append(a: Uint8Array, b: Uint8Array): Uint8Array {
|
||||||
if (a == null || !a.length) {
|
if (a == null || !a.length) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
@ -62,20 +62,145 @@ export function append(a: Uint8Array, b: Uint8Array) {
|
||||||
|
|
||||||
export class SocketClosedError extends Error {}
|
export class SocketClosedError extends Error {}
|
||||||
|
|
||||||
export type WebSocketFrame = {
|
export interface WebSocketFrame {
|
||||||
isLastFrame: boolean;
|
isLastFrame: boolean;
|
||||||
opcode: OpCode;
|
opcode: OpCode;
|
||||||
mask?: Uint8Array;
|
mask?: Uint8Array;
|
||||||
payload: Uint8Array;
|
payload: Uint8Array;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type WebSocket = {
|
export interface WebSocket {
|
||||||
readonly isClosed: boolean;
|
readonly isClosed: boolean;
|
||||||
receive(): AsyncIterableIterator<WebSocketEvent>;
|
receive(): AsyncIterableIterator<WebSocketEvent>;
|
||||||
send(data: WebSocketMessage): Promise<void>;
|
send(data: WebSocketMessage): Promise<void>;
|
||||||
ping(data?: WebSocketMessage): Promise<void>;
|
ping(data?: WebSocketMessage): Promise<void>;
|
||||||
close(code: number, reason?: string): Promise<void>;
|
close(code: number, reason?: string): Promise<void>;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export function unmask(payload: Uint8Array, mask?: Uint8Array): void {
|
||||||
|
if (mask) {
|
||||||
|
for (let i = 0, len = payload.length; i < len; i++) {
|
||||||
|
payload[i] ^= mask![i & 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function writeFrame(
|
||||||
|
frame: WebSocketFrame,
|
||||||
|
writer: Writer
|
||||||
|
): Promise<void> {
|
||||||
|
const payloadLength = frame.payload.byteLength;
|
||||||
|
let header: Uint8Array;
|
||||||
|
const hasMask = frame.mask ? 0x80 : 0;
|
||||||
|
if (payloadLength < 126) {
|
||||||
|
header = new Uint8Array([0x80 | frame.opcode, hasMask | payloadLength]);
|
||||||
|
} else if (payloadLength < 0xffff) {
|
||||||
|
header = new Uint8Array([
|
||||||
|
0x80 | frame.opcode,
|
||||||
|
hasMask | 0b01111110,
|
||||||
|
payloadLength >>> 8,
|
||||||
|
payloadLength & 0x00ff
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
header = new Uint8Array([
|
||||||
|
0x80 | frame.opcode,
|
||||||
|
hasMask | 0b01111111,
|
||||||
|
...sliceLongToBytes(payloadLength)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
unmask(frame.payload, frame.mask);
|
||||||
|
const bytes = append(header, frame.payload);
|
||||||
|
const w = new BufWriter(writer);
|
||||||
|
await w.write(bytes);
|
||||||
|
await w.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
|
||||||
|
let b = await buf.readByte();
|
||||||
|
let isLastFrame = false;
|
||||||
|
switch (b >>> 4) {
|
||||||
|
case 0b1000:
|
||||||
|
isLastFrame = true;
|
||||||
|
break;
|
||||||
|
case 0b0000:
|
||||||
|
isLastFrame = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("invalid signature");
|
||||||
|
}
|
||||||
|
const opcode = b & 0x0f;
|
||||||
|
// has_mask & payload
|
||||||
|
b = await buf.readByte();
|
||||||
|
const hasMask = b >>> 7;
|
||||||
|
let payloadLength = b & 0b01111111;
|
||||||
|
if (payloadLength === 126) {
|
||||||
|
payloadLength = await readShort(buf);
|
||||||
|
} else if (payloadLength === 127) {
|
||||||
|
payloadLength = await readLong(buf);
|
||||||
|
}
|
||||||
|
// mask
|
||||||
|
let mask;
|
||||||
|
if (hasMask) {
|
||||||
|
mask = new Uint8Array(4);
|
||||||
|
await buf.readFull(mask);
|
||||||
|
}
|
||||||
|
// payload
|
||||||
|
const payload = new Uint8Array(payloadLength);
|
||||||
|
await buf.readFull(payload);
|
||||||
|
return {
|
||||||
|
isLastFrame,
|
||||||
|
opcode,
|
||||||
|
mask,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function* receiveFrame(
|
||||||
|
conn: Conn
|
||||||
|
): AsyncIterableIterator<WebSocketFrame> {
|
||||||
|
let receiving = true;
|
||||||
|
const isLastFrame = true;
|
||||||
|
const reader = new BufReader(conn);
|
||||||
|
while (receiving) {
|
||||||
|
const frame = await readFrame(reader);
|
||||||
|
const { opcode, payload } = frame;
|
||||||
|
switch (opcode) {
|
||||||
|
case OpCode.TextFrame:
|
||||||
|
case OpCode.BinaryFrame:
|
||||||
|
case OpCode.Continue:
|
||||||
|
yield frame;
|
||||||
|
break;
|
||||||
|
case OpCode.Close:
|
||||||
|
await writeFrame(
|
||||||
|
{
|
||||||
|
opcode,
|
||||||
|
payload,
|
||||||
|
isLastFrame
|
||||||
|
},
|
||||||
|
conn
|
||||||
|
);
|
||||||
|
conn.close();
|
||||||
|
yield frame;
|
||||||
|
receiving = false;
|
||||||
|
break;
|
||||||
|
case OpCode.Ping:
|
||||||
|
await writeFrame(
|
||||||
|
{
|
||||||
|
payload,
|
||||||
|
isLastFrame,
|
||||||
|
opcode: OpCode.Pong
|
||||||
|
},
|
||||||
|
conn
|
||||||
|
);
|
||||||
|
yield frame;
|
||||||
|
break;
|
||||||
|
case OpCode.Pong:
|
||||||
|
yield frame;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WebSocketImpl implements WebSocket {
|
class WebSocketImpl implements WebSocket {
|
||||||
encoder = new TextEncoder();
|
encoder = new TextEncoder();
|
||||||
|
@ -163,7 +288,7 @@ class WebSocketImpl implements WebSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _isClosed = false;
|
private _isClosed = false;
|
||||||
get isClosed() {
|
get isClosed(): boolean {
|
||||||
return this._isClosed;
|
return this._isClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,88 +335,6 @@ class WebSocketImpl implements WebSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* receiveFrame(
|
|
||||||
conn: Conn
|
|
||||||
): AsyncIterableIterator<WebSocketFrame> {
|
|
||||||
let receiving = true;
|
|
||||||
const isLastFrame = true;
|
|
||||||
const reader = new BufReader(conn);
|
|
||||||
while (receiving) {
|
|
||||||
const frame = await readFrame(reader);
|
|
||||||
const { opcode, payload } = frame;
|
|
||||||
switch (opcode) {
|
|
||||||
case OpCode.TextFrame:
|
|
||||||
case OpCode.BinaryFrame:
|
|
||||||
case OpCode.Continue:
|
|
||||||
yield frame;
|
|
||||||
break;
|
|
||||||
case OpCode.Close:
|
|
||||||
await writeFrame(
|
|
||||||
{
|
|
||||||
opcode,
|
|
||||||
payload,
|
|
||||||
isLastFrame
|
|
||||||
},
|
|
||||||
conn
|
|
||||||
);
|
|
||||||
conn.close();
|
|
||||||
yield frame;
|
|
||||||
receiving = false;
|
|
||||||
break;
|
|
||||||
case OpCode.Ping:
|
|
||||||
await writeFrame(
|
|
||||||
{
|
|
||||||
payload,
|
|
||||||
isLastFrame,
|
|
||||||
opcode: OpCode.Pong
|
|
||||||
},
|
|
||||||
conn
|
|
||||||
);
|
|
||||||
yield frame;
|
|
||||||
break;
|
|
||||||
case OpCode.Pong:
|
|
||||||
yield frame;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function writeFrame(frame: WebSocketFrame, writer: Writer) {
|
|
||||||
const payloadLength = frame.payload.byteLength;
|
|
||||||
let header: Uint8Array;
|
|
||||||
const hasMask = frame.mask ? 0x80 : 0;
|
|
||||||
if (payloadLength < 126) {
|
|
||||||
header = new Uint8Array([0x80 | frame.opcode, hasMask | payloadLength]);
|
|
||||||
} else if (payloadLength < 0xffff) {
|
|
||||||
header = new Uint8Array([
|
|
||||||
0x80 | frame.opcode,
|
|
||||||
hasMask | 0b01111110,
|
|
||||||
payloadLength >>> 8,
|
|
||||||
payloadLength & 0x00ff
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
header = new Uint8Array([
|
|
||||||
0x80 | frame.opcode,
|
|
||||||
hasMask | 0b01111111,
|
|
||||||
...sliceLongToBytes(payloadLength)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
unmask(frame.payload, frame.mask);
|
|
||||||
const bytes = append(header, frame.payload);
|
|
||||||
const w = new BufWriter(writer);
|
|
||||||
await w.write(bytes);
|
|
||||||
await w.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unmask(payload: Uint8Array, mask?: Uint8Array) {
|
|
||||||
if (mask) {
|
|
||||||
for (let i = 0, len = payload.length; i < len; i++) {
|
|
||||||
payload[i] ^= mask![i & 3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function acceptable(req: { headers: Headers }): boolean {
|
export function acceptable(req: { headers: Headers }): boolean {
|
||||||
return (
|
return (
|
||||||
req.headers.get("upgrade") === "websocket" &&
|
req.headers.get("upgrade") === "websocket" &&
|
||||||
|
@ -300,6 +343,15 @@ export function acceptable(req: { headers: Headers }): boolean {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const kGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
|
|
||||||
|
export function createSecAccept(nonce: string): string {
|
||||||
|
const sha1 = new Sha1();
|
||||||
|
sha1.update(nonce + kGUID);
|
||||||
|
const bytes = sha1.digest();
|
||||||
|
return btoa(String.fromCharCode.apply(String, bytes));
|
||||||
|
}
|
||||||
|
|
||||||
export async function acceptWebSocket(req: {
|
export async function acceptWebSocket(req: {
|
||||||
conn: Conn;
|
conn: Conn;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
@ -321,52 +373,3 @@ export async function acceptWebSocket(req: {
|
||||||
}
|
}
|
||||||
throw new Error("request is not acceptable");
|
throw new Error("request is not acceptable");
|
||||||
}
|
}
|
||||||
|
|
||||||
const kGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
||||||
|
|
||||||
export function createSecAccept(nonce: string) {
|
|
||||||
const sha1 = new Sha1();
|
|
||||||
sha1.update(nonce + kGUID);
|
|
||||||
const bytes = sha1.digest();
|
|
||||||
return btoa(String.fromCharCode.apply(String, bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
|
|
||||||
let b = await buf.readByte();
|
|
||||||
let isLastFrame = false;
|
|
||||||
switch (b >>> 4) {
|
|
||||||
case 0b1000:
|
|
||||||
isLastFrame = true;
|
|
||||||
break;
|
|
||||||
case 0b0000:
|
|
||||||
isLastFrame = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("invalid signature");
|
|
||||||
}
|
|
||||||
const opcode = b & 0x0f;
|
|
||||||
// has_mask & payload
|
|
||||||
b = await buf.readByte();
|
|
||||||
const hasMask = b >>> 7;
|
|
||||||
let payloadLength = b & 0b01111111;
|
|
||||||
if (payloadLength === 126) {
|
|
||||||
payloadLength = await readShort(buf);
|
|
||||||
} else if (payloadLength === 127) {
|
|
||||||
payloadLength = await readLong(buf);
|
|
||||||
}
|
|
||||||
// mask
|
|
||||||
let mask;
|
|
||||||
if (hasMask) {
|
|
||||||
mask = new Uint8Array(4);
|
|
||||||
await buf.readFull(mask);
|
|
||||||
}
|
|
||||||
// payload
|
|
||||||
const payload = new Uint8Array(payloadLength);
|
|
||||||
await buf.readFull(payload);
|
|
||||||
return {
|
|
||||||
isLastFrame,
|
|
||||||
opcode,
|
|
||||||
mask,
|
|
||||||
payload
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
16
ws/sha1.ts
16
ws/sha1.ts
|
@ -47,7 +47,7 @@ export class Sha1 {
|
||||||
this._finalized = this._hashed = false;
|
this._finalized = this._hashed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: string | ArrayBuffer | ArrayBufferView) {
|
update(data: string | ArrayBuffer | ArrayBufferView): void {
|
||||||
if (this._finalized) {
|
if (this._finalized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ export class Sha1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finalize() {
|
finalize(): void {
|
||||||
if (this._finalized) {
|
if (this._finalized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ export class Sha1 {
|
||||||
this.hash();
|
this.hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
hash() {
|
hash(): void {
|
||||||
let a = this._h0;
|
let a = this._h0;
|
||||||
let b = this._h1;
|
let b = this._h1;
|
||||||
let c = this._h2;
|
let c = this._h2;
|
||||||
|
@ -271,7 +271,7 @@ export class Sha1 {
|
||||||
this._h4 = (this._h4 + e) >>> 0;
|
this._h4 = (this._h4 + e) >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hex() {
|
hex(): string {
|
||||||
this.finalize();
|
this.finalize();
|
||||||
|
|
||||||
const h0 = this._h0;
|
const h0 = this._h0;
|
||||||
|
@ -324,11 +324,11 @@ export class Sha1 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString(): string {
|
||||||
return this.hex();
|
return this.hex();
|
||||||
}
|
}
|
||||||
|
|
||||||
digest() {
|
digest(): number[] {
|
||||||
this.finalize();
|
this.finalize();
|
||||||
|
|
||||||
const h0 = this._h0;
|
const h0 = this._h0;
|
||||||
|
@ -361,11 +361,11 @@ export class Sha1 {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
array() {
|
array(): number[] {
|
||||||
return this.digest();
|
return this.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayBuffer() {
|
arrayBuffer(): ArrayBuffer {
|
||||||
this.finalize();
|
this.finalize();
|
||||||
return Uint32Array.of(this._h0, this._h1, this._h2, this._h3, this._h4)
|
return Uint32Array.of(this._h0, this._h1, this._h2, this._h3, this._h4)
|
||||||
.buffer;
|
.buffer;
|
||||||
|
|
Loading…
Reference in a new issue