source: trip-planner-front/node_modules/karma/lib/file-list.js@ 6a80231

Last change on this file since 6a80231 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 6.7 KB
Line 
1'use strict'
2
3const { promisify } = require('util')
4const mm = require('minimatch')
5const Glob = require('glob').Glob
6const fs = require('graceful-fs')
7const statAsync = promisify(fs.stat.bind(fs))
8const pathLib = require('path')
9const _ = require('lodash')
10
11const File = require('./file')
12const Url = require('./url')
13const helper = require('./helper')
14const log = require('./logger').create('filelist')
15const createPatternObject = require('./config').createPatternObject
16
17class FileList {
18 constructor (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
19 this._patterns = patterns || []
20 this._excludes = excludes || []
21 this._emitter = emitter
22 this._preprocess = preprocess
23
24 this.buckets = new Map()
25
26 // A promise that is pending if and only if we are active in this.refresh_()
27 this._refreshing = null
28
29 const emit = () => {
30 this._emitter.emit('file_list_modified', this.files)
31 }
32
33 const debouncedEmit = _.debounce(emit, autoWatchBatchDelay)
34 this._emitModified = (immediate) => {
35 immediate ? emit() : debouncedEmit()
36 }
37 }
38
39 _findExcluded (path) {
40 return this._excludes.find((pattern) => mm(path, pattern))
41 }
42
43 _findIncluded (path) {
44 return this._patterns.find((pattern) => mm(path, pattern.pattern))
45 }
46
47 _findFile (path, pattern) {
48 if (!path || !pattern) return
49 return this._getFilesByPattern(pattern.pattern).find((file) => file.originalPath === path)
50 }
51
52 _exists (path) {
53 return !!this._patterns.find((pattern) => mm(path, pattern.pattern) && this._findFile(path, pattern))
54 }
55
56 _getFilesByPattern (pattern) {
57 return this.buckets.get(pattern) || []
58 }
59
60 _refresh () {
61 const matchedFiles = new Set()
62
63 let lastCompletedRefresh = this._refreshing
64 lastCompletedRefresh = Promise.all(
65 this._patterns.map(async ({ pattern, type, nocache, isBinary }) => {
66 if (helper.isUrlAbsolute(pattern)) {
67 this.buckets.set(pattern, [new Url(pattern, type)])
68 return
69 }
70
71 const mg = new Glob(pathLib.normalize(pattern), { cwd: '/', follow: true, nodir: true, sync: true })
72
73 const files = mg.found
74 .filter((path) => {
75 if (this._findExcluded(path)) {
76 log.debug(`Excluded file "${path}"`)
77 return false
78 } else if (matchedFiles.has(path)) {
79 return false
80 } else {
81 matchedFiles.add(path)
82 return true
83 }
84 })
85 .map((path) => new File(path, mg.statCache[path].mtime, nocache, type, isBinary))
86
87 if (nocache) {
88 log.debug(`Not preprocessing "${pattern}" due to nocache`)
89 } else {
90 await Promise.all(files.map((file) => this._preprocess(file)))
91 }
92
93 this.buckets.set(pattern, files)
94
95 if (_.isEmpty(mg.found)) {
96 log.warn(`Pattern "${pattern}" does not match any file.`)
97 } else if (_.isEmpty(files)) {
98 log.warn(`All files matched by "${pattern}" were excluded or matched by prior matchers.`)
99 }
100 })
101 )
102 .then(() => {
103 // When we return from this function the file processing chain will be
104 // complete. In the case of two fast refresh() calls, the second call
105 // will overwrite this._refreshing, and we want the status to reflect
106 // the second call and skip the modification event from the first call.
107 if (this._refreshing !== lastCompletedRefresh) {
108 return this._refreshing
109 }
110 this._emitModified(true)
111 return this.files
112 })
113
114 return lastCompletedRefresh
115 }
116
117 get files () {
118 const served = []
119 const included = {}
120 const lookup = {}
121 this._patterns.forEach((p) => {
122 // This needs to be here sadly, as plugins are modifiying
123 // the _patterns directly resulting in elements not being
124 // instantiated properly
125 if (p.constructor.name !== 'Pattern') {
126 p = createPatternObject(p)
127 }
128
129 const files = this._getFilesByPattern(p.pattern)
130 files.sort((a, b) => {
131 if (a.path > b.path) return 1
132 if (a.path < b.path) return -1
133
134 return 0
135 })
136
137 if (p.served) {
138 served.push(...files)
139 }
140
141 files.forEach((file) => {
142 if (lookup[file.path] && lookup[file.path].compare(p) < 0) return
143
144 lookup[file.path] = p
145 if (p.included) {
146 included[file.path] = file
147 } else {
148 delete included[file.path]
149 }
150 })
151 })
152
153 return {
154 served: _.uniq(served, 'path'),
155 included: _.values(included)
156 }
157 }
158
159 refresh () {
160 this._refreshing = this._refresh()
161 return this._refreshing
162 }
163
164 reload (patterns, excludes) {
165 this._patterns = patterns || []
166 this._excludes = excludes || []
167
168 return this.refresh()
169 }
170
171 async addFile (path) {
172 const excluded = this._findExcluded(path)
173 if (excluded) {
174 log.debug(`Add file "${path}" ignored. Excluded by "${excluded}".`)
175 return this.files
176 }
177
178 const pattern = this._findIncluded(path)
179 if (!pattern) {
180 log.debug(`Add file "${path}" ignored. Does not match any pattern.`)
181 return this.files
182 }
183
184 if (this._exists(path)) {
185 log.debug(`Add file "${path}" ignored. Already in the list.`)
186 return this.files
187 }
188
189 const file = new File(path)
190 this._getFilesByPattern(pattern.pattern).push(file)
191
192 const [stat] = await Promise.all([statAsync(path), this._refreshing])
193 file.mtime = stat.mtime
194 await this._preprocess(file)
195
196 log.info(`Added file "${path}".`)
197 this._emitModified()
198 return this.files
199 }
200
201 async changeFile (path, force) {
202 const pattern = this._findIncluded(path)
203 const file = this._findFile(path, pattern)
204
205 if (!file) {
206 log.debug(`Changed file "${path}" ignored. Does not match any file in the list.`)
207 return this.files
208 }
209
210 const [stat] = await Promise.all([statAsync(path), this._refreshing])
211 if (force || stat.mtime > file.mtime) {
212 file.mtime = stat.mtime
213 await this._preprocess(file)
214 log.info(`Changed file "${path}".`)
215 this._emitModified(force)
216 }
217 return this.files
218 }
219
220 async removeFile (path) {
221 const pattern = this._findIncluded(path)
222 const file = this._findFile(path, pattern)
223
224 if (file) {
225 helper.arrayRemove(this._getFilesByPattern(pattern.pattern), file)
226 log.info(`Removed file "${path}".`)
227
228 this._emitModified()
229 } else {
230 log.debug(`Removed file "${path}" ignored. Does not match any file in the list.`)
231 }
232 return this.files
233 }
234}
235
236FileList.factory = function (config, emitter, preprocess) {
237 return new FileList(config.files, config.exclude, emitter, preprocess, config.autoWatchBatchDelay)
238}
239
240FileList.factory.$inject = ['config', 'emitter', 'preprocess']
241
242module.exports = FileList
Note: See TracBrowser for help on using the repository browser.