From e8758cfefda8671d2e9c084387fb95f2729d09f7 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Sat, 21 Dec 2019 02:57:23 -0500 Subject: [PATCH] . --- packages/glob/__tests__/glob-search.test.ts | 1 + packages/glob/src/glob.ts | 94 ++++++++++--------- ...match-result.ts => internal-match-kind.ts} | 2 +- packages/glob/src/internal-path.ts | 1 + packages/glob/src/internal-pattern-helper.ts | 6 +- packages/glob/src/internal-pattern.ts | 16 ++-- 6 files changed, 63 insertions(+), 57 deletions(-) rename packages/glob/src/{internal-match-result.ts => internal-match-kind.ts} (90%) diff --git a/packages/glob/__tests__/glob-search.test.ts b/packages/glob/__tests__/glob-search.test.ts index d701a86b..31f9258c 100644 --- a/packages/glob/__tests__/glob-search.test.ts +++ b/packages/glob/__tests__/glob-search.test.ts @@ -370,6 +370,7 @@ describe('glob (search)', () => { await fs.mkdir(root, {recursive: true}) await fs.writeFile(path.join(root, 'file'), 'test file content') await createSymlinkDir(root, path.join(root, 'symDir')) + await fs.stat(path.join(root, 'symDir')) const itemPaths = await glob.glob(path.join(root, 'symDir')) expect(itemPaths).toHaveLength(2) diff --git a/packages/glob/src/glob.ts b/packages/glob/src/glob.ts index 64aa97e4..229dee13 100644 --- a/packages/glob/src/glob.ts +++ b/packages/glob/src/glob.ts @@ -3,7 +3,7 @@ import * as fs from 'fs' import * as path from 'path' import * as patternHelper from './internal-pattern-helper' import {IGlobOptions} from './internal-glob-options' -import {MatchResult} from './internal-match-result' +import {MatchKind} from './internal-match-kind' import {Pattern} from './internal-pattern' import {SearchState} from './internal-search-state' @@ -24,12 +24,9 @@ export async function glob( // Parse patterns const patterns: Pattern[] = patternHelper.parse([pattern], options) - // Get search paths - const searchPaths: string[] = patternHelper.getSearchPaths(patterns) - - // Search - const result: string[] = [] - for (const searchPath of searchPaths) { + // Push the search paths + const stack: SearchState[] = [] + for (const searchPath of patternHelper.getSearchPaths(patterns)) { // Exists? Note, intentionally using lstat. Detection for broken symlink // will be performed later (if following symlinks). try { @@ -41,49 +38,56 @@ export async function glob( throw err } - // Push the first item - const stack: SearchState[] = [new SearchState(searchPath, 1)] - const traversalChain: string[] = [] // used to detect cycles + stack.unshift(new SearchState(searchPath, 1)) + } - while (stack.length) { - // Pop - const item = stack.pop() as SearchState - const stats: fs.Stats | undefined = await stat( - item, - options, - traversalChain - ) + const result: string[] = [] - // Broken symlink or symlink cycle detected - if (!stats) { + // Search + const traversalChain: string[] = [] // used to detect cycles + while (stack.length) { + // Pop + const item = stack.pop() as SearchState + + // Match + const match = patternHelper.match(patterns, item.path) + if (!match) { + continue + } + + // Stat + const stats: fs.Stats | undefined = await stat( + item, + options, + traversalChain + ) + + // Broken symlink, or symlink cycle detected, or no longer exists + if (!stats) { + continue + } + + // Directory + if (stats.isDirectory()) { + // Matched + if (match & MatchKind.Directory) { + result.push(item.path) + } + // Descend? + else if (!patternHelper.partialMatch(patterns, item.path)) { continue } - // Match - const matchResult = patternHelper.match(patterns, item.path) - - // Directory - if (stats.isDirectory()) { - // Matched - if (matchResult & MatchResult.Directory) { - result.push(item.path) - } - // Descend? - else if (!patternHelper.partialMatch(patterns, item.path)) { - continue - } - - // Push the child items in reverse - const childLevel = item.level + 1 - const childItems = (await fs.promises.readdir(item.path)).map( - x => new SearchState(path.join(item.path, x), childLevel) - ) - stack.push(...childItems.reverse()) - } - // File - else if (matchResult & MatchResult.File) { - result.push(item.path) - } + // Push the child items in reverse + const childLevel = item.level + 1 + const childItems = (await fs.promises.readdir(item.path)).map( + x => new SearchState(path.join(item.path, x), childLevel) + ) + stack.push(...childItems.reverse()) + } + // File + else if (match & MatchKind.File) { + result.push(item.path) } } diff --git a/packages/glob/src/internal-match-result.ts b/packages/glob/src/internal-match-kind.ts similarity index 90% rename from packages/glob/src/internal-match-result.ts rename to packages/glob/src/internal-match-kind.ts index 21005053..75802da5 100644 --- a/packages/glob/src/internal-match-result.ts +++ b/packages/glob/src/internal-match-kind.ts @@ -1,7 +1,7 @@ /** * Indicates whether a pattern matches a path */ -export enum MatchResult { +export enum MatchKind { /** Not matched */ None = 0, diff --git a/packages/glob/src/internal-path.ts b/packages/glob/src/internal-path.ts index 791830e5..fe3a49e4 100644 --- a/packages/glob/src/internal-path.ts +++ b/packages/glob/src/internal-path.ts @@ -76,6 +76,7 @@ export class Path { !segment.includes(path.sep), `Parameter 'itemPath' contains unexpected path separators` ) + this.segments.push(segment) } } } diff --git a/packages/glob/src/internal-pattern-helper.ts b/packages/glob/src/internal-pattern-helper.ts index 2a91bc02..ac0b8646 100644 --- a/packages/glob/src/internal-pattern-helper.ts +++ b/packages/glob/src/internal-pattern-helper.ts @@ -1,6 +1,6 @@ import * as pathHelper from './internal-path-helper' import {IGlobOptions} from './internal-glob-options' -import {MatchResult} from './internal-match-result' +import {MatchKind} from './internal-match-kind' import {Pattern} from './internal-pattern' const IS_WINDOWS = process.platform === 'win32' @@ -60,8 +60,8 @@ export function getSearchPaths(patterns: Pattern[]): string[] { /** * Matches the patterns against the path */ -export function match(patterns: Pattern[], itemPath: string): MatchResult { - let result: MatchResult = MatchResult.None +export function match(patterns: Pattern[], itemPath: string): MatchKind { + let result: MatchKind = MatchKind.None for (const pattern of patterns) { if (pattern.comment) { diff --git a/packages/glob/src/internal-pattern.ts b/packages/glob/src/internal-pattern.ts index 55dca136..e41fe845 100644 --- a/packages/glob/src/internal-pattern.ts +++ b/packages/glob/src/internal-pattern.ts @@ -2,7 +2,7 @@ import * as assert from 'assert' import * as path from 'path' import * as pathHelper from './internal-path-helper' import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch' -import {MatchResult} from './internal-match-result' +import {MatchKind} from './internal-match-kind' import {Path} from './internal-path' const IS_WINDOWS = process.platform === 'win32' @@ -64,16 +64,16 @@ export class Pattern { /** * Matches the pattern against the specified path */ - match(itemPath: string): MatchResult { + match(itemPath: string): MatchKind { if (this.comment) { - return MatchResult.None + return MatchKind.None } if (this.minimatch.match(itemPath)) { - return this.trailingSlash ? MatchResult.Directory : MatchResult.All + return this.trailingSlash ? MatchKind.Directory : MatchKind.All } - return MatchResult.None + return MatchKind.None } /** @@ -146,11 +146,11 @@ export class Pattern { private initializePaths(pattern: string): void { // Build the search path const searchSegments: string[] = [] - for (const literalSegment of this.getLiterals(pattern)) { - if (!literalSegment) { + for (const literal of this.getLiterals(pattern)) { + if (!literal) { break } - searchSegments.push(literalSegment) + searchSegments.push(literal) } this.searchPath = new Path(searchSegments).toString()