@@ -32,6 +32,22 @@ type FsWalkOptions = { entryFilter :: Entry -> Effect Boolean, deepFilter :: Ent
3232foreign import data DirEnt :: Type
3333foreign import isFile :: DirEnt -> Boolean
3434
35+ -- https://www.npmjs.com/package/picomatch#scan
36+ foreign import scanPattern :: String -> PatternInfo
37+ type PatternInfo =
38+ { prefix :: String
39+ , input :: String
40+ , start :: Int
41+ , base :: String
42+ , glob :: String
43+ , isBrace :: Boolean
44+ , isBracket :: Boolean
45+ , isGlob :: Boolean
46+ , isExtglob :: Boolean
47+ , isGlobstar :: Boolean
48+ , negated :: Boolean
49+ }
50+
3551foreign import fsWalkImpl
3652 :: (forall a b . a -> Either a b )
3753 -> (forall a b . b -> Either a b )
@@ -55,7 +71,7 @@ gitignoreFileToGlob base =
5571 >>> (\{ left, right } -> { ignore: left, include: right })
5672
5773 where
58- isComment = isJust <<< String .stripPrefix (String.Pattern " #" )
74+ isComment = isPrefix (String.Pattern " #" )
5975 dropSuffixSlash str = fromMaybe str $ String .stripSuffix (String.Pattern " /" ) str
6076 dropPrefixSlash str = fromMaybe str $ String .stripPrefix (String.Pattern " /" ) str
6177
@@ -116,13 +132,56 @@ fsWalk cwd ignorePatterns includePatterns = Aff.makeAff \cb -> do
116132
117133 Ref .modify_ addMatcher ignoreMatcherRef
118134
135+ -- The base of every includePattern
136+ -- The base of a pattern is its longest non-glob prefix.
137+ -- For example: foo/bar/*/*.purs => foo/bar
138+ -- **/spago.yaml => ""
139+ includePatternBases :: Array String
140+ includePatternBases = map (_.base <<< scanPattern) includePatterns
141+
142+ matchesAnyPatternBase :: String -> Boolean
143+ matchesAnyPatternBase relDirPath = any matchesPatternBase includePatternBases
144+ where
145+ matchesPatternBase :: String -> Boolean
146+ matchesPatternBase " " =
147+ -- Patterns which have no base, for example **/spago.yaml, match every directory.
148+ true
149+ matchesPatternBase patternBase | String .length relDirPath < String .length patternBase =
150+ -- The directoryPath is shorter than the patterns base, so in order for this pattern to
151+ -- match anything in this directory, the directories path must be a prefix of the patterns base.
152+ -- For example: pattern = .spago/p/unfoldable-6.0.0/src/**/*.purs
153+ -- patternBase = .spago/p/unfoldable-6.0.0/src
154+ -- relDirPath = .spago/p/
155+ -- => relDirPath is a prefix of patternBase => the directory matches
156+ --
157+ -- Or in the negative case:
158+ -- pattern = .spago/p/unfoldable-6.0.0/src/**/*.purs
159+ -- patternBase = .spago/p/unfoldable-6.0.0/src
160+ -- relDirPath = .spago/p/arrays-7.3.0
161+ -- => relDirPath is not a prefix of patternBase => the directory does not match
162+ String.Pattern relDirPath `isPrefix` patternBase
163+ matchesPatternBase patternBase | otherwise =
164+ -- The directoryPath is longer than the patterns base, so the directoryPath is more specific.
165+ -- In order for this pattern to match anything in this directory, the patterns base must be a
166+ -- prefix of the directories path.
167+ -- For example: pattern = .spago/p/unfoldable-6.0.0/src/**/*.purs
168+ -- patternBase = .spago/p/unfoldable-6.0.0/src
169+ -- relDirPath = .spago/p/unfoldable-6.0.0/src/Data
170+ -- => patternBase is a prefix of relDirPath => the directory matches
171+ String.Pattern patternBase `isPrefix` relDirPath
172+
119173 -- Should `fsWalk` recurse into this directory?
120174 deepFilter :: Entry -> Effect Boolean
121175 deepFilter entry = fromMaybe false <$> runMaybeT do
122176 isCanceled <- lift $ Ref .read canceled
123177 guard $ not isCanceled
178+ let relPath = withForwardSlashes $ Path .relative cwd entry.path
124179 shouldIgnore <- lift $ Ref .read ignoreMatcherRef
125- pure $ not $ shouldIgnore $ Path .relative cwd entry.path
180+ guard $ not $ shouldIgnore relPath
181+
182+ -- Only if the path of this directory matches any of the patterns base path,
183+ -- can anything in this directory possibly match the corresponding full pattern.
184+ pure $ matchesAnyPatternBase relPath
126185
127186 -- Should `fsWalk` retain this entry for the result array?
128187 entryFilter :: Entry -> Effect Boolean
0 commit comments