1
0
Fork 0
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:
Yoshiya Hinosawa 2019-03-12 14:51:51 +09:00 committed by Ryan Dahl
parent 4de86f04de
commit 61af419bbc
20 changed files with 355 additions and 349 deletions

View file

@ -1,3 +0,0 @@
/flags/
/fs/
/ws/

View file

@ -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": "_" }
] ]
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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