From 17adf6235c30f0649b773a82ce25afa6fb04819e Mon Sep 17 00:00:00 2001
From: Nayeem Rahman <muhammed.9939@gmail.com>
Date: Sun, 29 Sep 2019 01:34:59 +0100
Subject: [PATCH] Move exclude handling inside of expandGlob()

Adds an exclude field to ExpandGlopOptions. This should be the final
API.
---
 fs/glob.ts        | 29 +++++++++++++++++++++--------
 prettier/main.ts  | 28 +++++-----------------------
 testing/runner.ts | 40 ++++++++++++----------------------------
 3 files changed, 38 insertions(+), 59 deletions(-)

diff --git a/fs/glob.ts b/fs/glob.ts
index 05cd1785b6f8..921c9b565b79 100644
--- a/fs/glob.ts
+++ b/fs/glob.ts
@@ -80,6 +80,7 @@ export function isGlob(str: string): boolean {
 
 export interface ExpandGlobOptions extends GlobOptions {
   root?: string;
+  exclude?: string[];
   includeDirs?: boolean;
 }
 
@@ -94,17 +95,23 @@ export async function* expandGlob(
   globString: string,
   {
     root = cwd(),
+    exclude = [],
     includeDirs = true,
     extended = false,
     globstar = false,
     strict = false
   }: ExpandGlobOptions = {}
 ): AsyncIterableIterator<WalkInfo> {
-  const absoluteGlob = isAbsolute(globString)
-    ? globString
-    : join(root, globString);
+  const absGlob = isAbsolute(globString) ? globString : join(root, globString);
+  const absExclude = exclude.map((s: string): string =>
+    isAbsolute(s) ? s : join(root, s)
+  );
+  const globOptions: GlobOptions = { extended, globstar, strict };
   yield* walk(root, {
-    match: [globToRegExp(absoluteGlob, { extended, globstar, strict })],
+    match: [globToRegExp(absGlob, globOptions)],
+    skip: absExclude.map(
+      (s: string): RegExp => globToRegExp(s, { ...globOptions, flags: "g" })
+    ),
     includeDirs
   });
 }
@@ -115,17 +122,23 @@ export function* expandGlobSync(
   globString: string,
   {
     root = cwd(),
+    exclude = [],
     includeDirs = true,
     extended = false,
     globstar = false,
     strict = false
   }: ExpandGlobOptions = {}
 ): IterableIterator<WalkInfo> {
-  const absoluteGlob = isAbsolute(globString)
-    ? globString
-    : join(root, globString);
+  const absGlob = isAbsolute(globString) ? globString : join(root, globString);
+  const absExclude = exclude.map((s: string): string =>
+    isAbsolute(s) ? s : join(root, s)
+  );
+  const globOptions: GlobOptions = { extended, globstar, strict };
   yield* walkSync(root, {
-    match: [globToRegExp(absoluteGlob, { extended, globstar, strict })],
+    match: [globToRegExp(absGlob, globOptions)],
+    skip: absExclude.map(
+      (s: string): RegExp => globToRegExp(s, { ...globOptions, flags: "g" })
+    ),
     includeDirs
   });
 }
diff --git a/prettier/main.ts b/prettier/main.ts
index 0ffd7000e4ea..fc94171a72c0 100755
--- a/prettier/main.ts
+++ b/prettier/main.ts
@@ -24,13 +24,7 @@
 // This script formats the given source files. If the files are omitted, it
 // formats the all files in the repository.
 import { parse } from "../flags/mod.ts";
-import {
-  ExpandGlobOptions,
-  WalkInfo,
-  expandGlob,
-  globToRegExp
-} from "../fs/mod.ts";
-import { isAbsolute, join } from "../fs/path/mod.ts";
+import { ExpandGlobOptions, WalkInfo, expandGlob } from "../fs/mod.ts";
 import { prettier, prettierPlugins } from "./prettier.ts";
 const { args, cwd, exit, readAll, readFile, stdin, stdout, writeFile } = Deno;
 
@@ -310,32 +304,20 @@ async function* getTargetFiles(
 ): AsyncIterableIterator<WalkInfo> {
   const expandGlobOpts: ExpandGlobOptions = {
     root,
+    exclude,
+    includeDirs: true,
     extended: true,
     globstar: true,
     strict: false
   };
 
-  // TODO: We use the `g` flag here to support path prefixes when specifying
-  // excludes. Replace with a solution that does this more correctly.
-  const excludePathPatterns = exclude.map(
-    (s: string): RegExp =>
-      globToRegExp(isAbsolute(s) ? s : join(root, s), {
-        ...expandGlobOpts,
-        flags: "g"
-      })
-  );
-  const shouldInclude = ({ filename }: WalkInfo): boolean =>
-    !excludePathPatterns.some((p: RegExp): boolean => !!filename.match(p));
-
   async function* expandDirectory(d: string): AsyncIterableIterator<WalkInfo> {
     for await (const walkInfo of expandGlob("**/*", {
       ...expandGlobOpts,
       root: d,
       includeDirs: false
     })) {
-      if (shouldInclude(walkInfo)) {
-        yield walkInfo;
-      }
+      yield walkInfo;
     }
   }
 
@@ -343,7 +325,7 @@ async function* getTargetFiles(
     for await (const walkInfo of expandGlob(globString, expandGlobOpts)) {
       if (walkInfo.info.isDirectory()) {
         yield* expandDirectory(walkInfo.filename);
-      } else if (shouldInclude(walkInfo)) {
+      } else {
         yield walkInfo;
       }
     }
diff --git a/testing/runner.ts b/testing/runner.ts
index d81c7479896e..409102db571d 100755
--- a/testing/runner.ts
+++ b/testing/runner.ts
@@ -1,14 +1,9 @@
 #!/usr/bin/env -S deno -A
 // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
 import { parse } from "../flags/mod.ts";
-import {
-  ExpandGlobOptions,
-  WalkInfo,
-  expandGlob,
-  globToRegExp
-} from "../fs/mod.ts";
+import { ExpandGlobOptions, expandGlob } from "../fs/mod.ts";
 import { isWindows } from "../fs/path/constants.ts";
-import { isAbsolute, join } from "../fs/path/mod.ts";
+import { join } from "../fs/path/mod.ts";
 import { RunTestsOptions, runTests } from "./mod.ts";
 const { DenoError, ErrorKind, args, cwd, exit } = Deno;
 
@@ -79,28 +74,13 @@ export async function* findTestModules(
 
   const expandGlobOpts: ExpandGlobOptions = {
     root,
+    exclude: excludePaths,
+    includeDirs: true,
     extended: true,
     globstar: true,
     strict: false
   };
 
-  // TODO: We use the `g` flag here to support path prefixes when specifying
-  // excludes. Replace with a solution that does this more correctly.
-  const excludePathPatterns = excludePaths.map(
-    (s: string): RegExp =>
-      globToRegExp(isAbsolute(s) ? s : join(root, s), {
-        ...expandGlobOpts,
-        flags: "g"
-      })
-  );
-  const excludeUrlPatterns = excludeUrls.map(
-    (url: string): RegExp => RegExp(url)
-  );
-  const shouldIncludePath = ({ filename }: WalkInfo): boolean =>
-    !excludePathPatterns.some((p: RegExp): boolean => !!filename.match(p));
-  const shouldIncludeUrl = (url: string): boolean =>
-    !excludeUrlPatterns.some((p: RegExp): boolean => !!url.match(p));
-
   async function* expandDirectory(d: string): AsyncIterableIterator<string> {
     for (const dirGlob of DIR_GLOBS) {
       for await (const walkInfo of expandGlob(dirGlob, {
@@ -108,9 +88,7 @@ export async function* findTestModules(
         root: d,
         includeDirs: false
       })) {
-        if (shouldIncludePath(walkInfo)) {
-          yield filePathToUrl(walkInfo.filename);
-        }
+        yield filePathToUrl(walkInfo.filename);
       }
     }
   }
@@ -119,12 +97,18 @@ export async function* findTestModules(
     for await (const walkInfo of expandGlob(globString, expandGlobOpts)) {
       if (walkInfo.info.isDirectory()) {
         yield* expandDirectory(walkInfo.filename);
-      } else if (shouldIncludePath(walkInfo)) {
+      } else {
         yield filePathToUrl(walkInfo.filename);
       }
     }
   }
 
+  const excludeUrlPatterns = excludeUrls.map(
+    (url: string): RegExp => RegExp(url)
+  );
+  const shouldIncludeUrl = (url: string): boolean =>
+    !excludeUrlPatterns.some((p: RegExp): boolean => !!url.match(p));
+
   yield* includeUrls.filter(shouldIncludeUrl);
 }