1 | const { createHash } = require('crypto')
|
2 | const flatten = require('arr-flatten')
|
3 | const unique = require('array-unique')
|
4 | const { resolve } = require('path')
|
5 | const ignore = require('ignore')
|
6 | const _glob = require('glob')
|
7 | const fs = require('fs-extra')
|
8 |
|
9 | const hash = async (files) => {
|
10 | const map = new Map()
|
11 | await Promise.all(
|
12 | files.map(async name => {
|
13 | const data = await fs.readFile(name)
|
14 | const h = createHash('sha1').update(data).digest('hex')
|
15 | const entry = map.get(h)
|
16 | if (entry) {
|
17 | entry.names.push(name)
|
18 | } else {
|
19 | map.set(createHash('sha1').update(data).digest('hex'), { names: [name], data })
|
20 | }
|
21 | })
|
22 | )
|
23 | return map
|
24 | }
|
25 |
|
26 | const getFiles = async (dir) => {
|
27 | const fileList = await getFilesPaths(dir)
|
28 | const hashes = await hash(fileList)
|
29 |
|
30 | const files = await Promise.all(Array.prototype.concat.apply([],
|
31 | await Promise.all(Array.from(hashes).map(async ([sha, { data, names }]) =>
|
32 | names.map(async name => ({
|
33 | data,
|
34 | path: toRelative(name, dir)
|
35 | }))
|
36 | ))
|
37 | ))
|
38 | return files
|
39 | }
|
40 |
|
41 | const toRelative = (_path, base) => {
|
42 | const SEP = process.platform.startsWith('win') ? '\\' : '/'
|
43 | const fullBase = base.endsWith(SEP) ? base : base + SEP
|
44 | let relative = _path.substr(fullBase.length)
|
45 |
|
46 | if (relative.startsWith(SEP)) {
|
47 | relative = relative.substr(1)
|
48 | }
|
49 |
|
50 | return relative.replace(/\\/g, '/')
|
51 | }
|
52 |
|
53 |
|
54 |
|
55 | const IGNORED = `.hg
|
56 | .git
|
57 | .kyso
|
58 | .merge
|
59 | .gitmodules
|
60 | .svn
|
61 | .npmignore
|
62 | .dockerignore
|
63 | .gitignore
|
64 | .jupyter
|
65 | .local
|
66 | .cache
|
67 | .ipython
|
68 | .ipynb_checkpoints
|
69 | .*.swp
|
70 | .DS_Store
|
71 | .wafpicke-*
|
72 | .lock-wscript
|
73 | npm-debug.log
|
74 | config.gypi
|
75 | node_modules
|
76 | CVS
|
77 | .kyso.json
|
78 | .kyso-dev.json
|
79 | Icon?
|
80 | Icon^M
|
81 | Icon*`
|
82 |
|
83 | const glob = async (pattern, options) =>
|
84 | new Promise((resolve, reject) => {
|
85 | _glob(pattern, options, (error, files) => {
|
86 | if (error) {
|
87 | reject(error)
|
88 | } else {
|
89 | resolve(files)
|
90 | }
|
91 | })
|
92 | })
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | const clearRelative = (str) => str.replace(/(\n|^)\.\//g, '$1')
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | const maybeRead = async (path, default_ = '') => {
|
109 | try {
|
110 | return await fs.readFile(path, 'utf8')
|
111 | } catch (err) {
|
112 | return default_
|
113 | }
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | const asAbsolute = (path, parent) => {
|
125 | if (path[0] === '/') {
|
126 | return path
|
127 | }
|
128 |
|
129 | return resolve(parent, path)
|
130 | }
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | async function getFilesPaths(path) {
|
146 |
|
147 | const search = Array.prototype.concat.apply(
|
148 | [],
|
149 | await Promise.all(
|
150 | ['.'].map(file => glob(file, { cwd: path, absolute: true, dot: true }))
|
151 | )
|
152 | )
|
153 |
|
154 |
|
155 | const kysoIgnore = await maybeRead(resolve(path, '.kysoignore'), null)
|
156 | const gitIgnore = kysoIgnore === null
|
157 | ? await maybeRead(resolve(path, '.gitignore'))
|
158 | : null
|
159 |
|
160 | let clr = ''
|
161 | if (kysoIgnore || gitIgnore) {
|
162 | clr = clearRelative(kysoIgnore === null ? gitIgnore : kysoIgnore)
|
163 | }
|
164 |
|
165 | const filter = ignore()
|
166 | .add(`${IGNORED}\n${clr}`)
|
167 | .createFilter()
|
168 |
|
169 | const prefixLength = path.length + 1
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | const overrideIgnores = false
|
176 | const accepts = overrideIgnores
|
177 | ? () => true
|
178 | : file => {
|
179 | const relativePath = file.substr(prefixLength)
|
180 | if (relativePath === '') {
|
181 | return true
|
182 | }
|
183 | const accepted = filter(relativePath)
|
184 | return accepted
|
185 | }
|
186 |
|
187 |
|
188 | const files = await explode(search, {
|
189 | accepts
|
190 | })
|
191 |
|
192 |
|
193 | return unique(files)
|
194 | }
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | async function explode(paths, { accepts }) {
|
211 | const list = async file => {
|
212 | let path = file
|
213 | let s
|
214 |
|
215 | if (!accepts(file)) {
|
216 | return null
|
217 | }
|
218 |
|
219 | try {
|
220 | s = await fs.stat(path)
|
221 | } catch (e) {
|
222 |
|
223 |
|
224 | path = `${file}.js`
|
225 |
|
226 | try {
|
227 | s = await fs.stat(path)
|
228 | } catch (e2) {
|
229 | return null
|
230 | }
|
231 | }
|
232 |
|
233 | if (s.isDirectory()) {
|
234 | const all = await fs.readdir(file)
|
235 |
|
236 | return many(all.map(subdir => asAbsolute(subdir, file)))
|
237 |
|
238 | } else if (!s.isFile()) {
|
239 | return null
|
240 | }
|
241 |
|
242 | return path
|
243 | }
|
244 |
|
245 | const many = all => Promise.all(all.map(file => list(file)))
|
246 | return flatten(await many(paths)).filter(v => v !== null)
|
247 | }
|
248 |
|
249 | module.exports = getFiles
|