Jonatan Nilsson
3bd8d4ba51
Make the printError slightly more robust as well as well as make it print better when obscure errors occur when programmers fiddle with the internal mechanics of node, for example the Error.prepareStack among other things.
178 lines
5.3 KiB
JavaScript
178 lines
5.3 KiB
JavaScript
import path from 'path'
|
|
import fs from 'fs'
|
|
|
|
export function CLI(e) {
|
|
this.e = e
|
|
this.reporter = 'list'
|
|
this.targets = ['test/**']
|
|
this.files = []
|
|
this.errored = false
|
|
}
|
|
|
|
CLI.prototype.parseOptions = function(args) {
|
|
if (!args || !args.length) {
|
|
this.targets.push('test/**')
|
|
this.errored = false
|
|
return
|
|
}
|
|
|
|
this.errored = false
|
|
this.targets.splice(0, this.targets.length)
|
|
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (args[i] === '-r' || args[i] === '--reporter') {
|
|
if (!args[i + 1] || (args[i + 1] !== 'list' && args[i + 1] !== 'dot')) {
|
|
this.errored = true
|
|
return
|
|
}
|
|
this.reporter = args[i + 1]
|
|
i++
|
|
} else if (args[i][0] === '-') {
|
|
this.errored = true
|
|
return
|
|
} else {
|
|
this.targets.push(args[i])
|
|
}
|
|
}
|
|
|
|
if (!this.targets.length) {
|
|
this.targets.push('test/**')
|
|
}
|
|
}
|
|
|
|
CLI.prototype.processTargets = function() {
|
|
this.files.splice(0, this.files.length)
|
|
|
|
if (!this.targets.length) {
|
|
return Promise.resolve()
|
|
}
|
|
|
|
return Promise.all(this.targets.map((target) => {
|
|
return getFiles(this.files, target)
|
|
})).then(() => {
|
|
if (!this.files.length) {
|
|
this.errored = 'empty'
|
|
}
|
|
})
|
|
}
|
|
|
|
CLI.prototype.loadFiles = async function() {
|
|
let cwd = process.cwd()
|
|
|
|
for (let i = 0; i < this.files.length; i++) {
|
|
if (this.files[i].endsWith('.mjs') || this.files[i].endsWith('.js')) {
|
|
try {
|
|
this.e.setFilename(this.files[i])
|
|
await import('file:///' + path.join(cwd, this.files[i]))
|
|
this.e.resetFilename()
|
|
} catch (e) {
|
|
let newError = new Error(`Error while loading ${this.files[i]}`)
|
|
newError.inner = e
|
|
throw newError
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function traverseFolder(files, curr, match, insidePath, grabAll, insideStar, includeFiles) {
|
|
return new Promise(function(resolve, reject) {
|
|
return fs.readdir(curr, function(err, data) {
|
|
if (err) return reject(new Error('unable to read directory ' + curr + ': ' + err.message))
|
|
|
|
resolve(Promise.all(data.map(function(file) {
|
|
return new Promise(function(res, rej) {
|
|
fs.lstat(path.join(curr, file), function(err, stat) {
|
|
if (err) return rej(new Error('unable to read file or directory ' + path.join(curr, file) + ': ' + err.message))
|
|
|
|
if ((includeFiles || grabAll) && stat.isFile()) {
|
|
if (!match || fileMatches(file, match)) {
|
|
files.push(path.join(insidePath, file).replace(/\\/g, '/'))
|
|
return res(files)
|
|
}
|
|
}
|
|
if (stat.isDirectory() && grabAll) {
|
|
return res(traverseFolder(files, path.join(curr, file), match, path.join(insidePath, file), grabAll, insideStar, includeFiles))
|
|
} else if (stat.isDirectory() && match) {
|
|
return res(getFiles(files, match, path.join(insidePath, file), grabAll, insideStar))
|
|
}
|
|
res(null)
|
|
})
|
|
})
|
|
})).then(function() { return files }))
|
|
})
|
|
})
|
|
}
|
|
|
|
export function fileMatches(filename, match) {
|
|
return Boolean(filename.match(new RegExp(match.replace(/\./, '\\.').replace(/\*/, '.*'))))
|
|
}
|
|
|
|
export function getFiles(files, match, insidePath, grabAll, insideStar) {
|
|
let isGrabbingAll = grabAll || false
|
|
let isStarred = insideStar || false
|
|
let cwd = process.cwd()
|
|
let currPath = insidePath || ''
|
|
let curr = path.join(cwd, currPath || '')
|
|
|
|
return new Promise(function(res, rej) {
|
|
let start = 0
|
|
let splitted = match.split('/')
|
|
if (splitted[start] === '.') {
|
|
start++
|
|
}
|
|
if (splitted[splitted.length - 1] === '') {
|
|
splitted[splitted.length - 1] === '*'
|
|
}
|
|
|
|
let first = splitted[start]
|
|
|
|
if (splitted.length > start + 1) {
|
|
if (first === '**') {
|
|
isGrabbingAll = isStarred = true
|
|
return traverseFolder(files, curr, splitted.slice(start + 1).join('/'), currPath, isGrabbingAll, isStarred, false)
|
|
.then(res, rej)
|
|
} else if (first === '*') {
|
|
isStarred = true
|
|
return traverseFolder(files, curr, splitted.slice(start + 1).join('/'), currPath, isGrabbingAll, isStarred, false)
|
|
.then(res, rej)
|
|
}
|
|
return getFiles(files, splitted.slice(start + 1).join('/'), path.join(currPath, first), grabAll, isStarred)
|
|
.then(res, rej)
|
|
} else if (first.indexOf('*') >= 0) {
|
|
if (first === '**') {
|
|
isGrabbingAll = isStarred = true
|
|
}
|
|
return traverseFolder(files, curr, first === '*' || first === '**'? '' : first, currPath, isGrabbingAll, isStarred, true)
|
|
.then(res, rej)
|
|
}
|
|
|
|
fs.lstat(path.join(curr, first), function(err, stat) {
|
|
if (err) {
|
|
// If we're inside a star, we ignore files we cannot find
|
|
if (isStarred) {
|
|
return res(files)
|
|
}
|
|
return rej(new Error('file ' + path.join(insidePath, first) + ' could not be found: ' + err.message))
|
|
}
|
|
|
|
if (stat.isDirectory()) {
|
|
return traverseFolder(files, path.join(curr, first), '', path.join(currPath, first), true, true, true)
|
|
.then(res, rej)
|
|
}
|
|
|
|
files.push(path.join(currPath, match).replace(/\\/g, '/'))
|
|
return res(files)
|
|
})
|
|
})
|
|
}
|
|
|
|
export function printError(err, msg) {
|
|
let before = msg || ''
|
|
console.error('')
|
|
console.error('\x1b[31m '
|
|
+ before + err.toString()
|
|
+ '\x1b[0m\n \x1b[90m'
|
|
+ (err.stack || '').replace(err.toString(), ''))
|
|
console.error('\x1b[0m')
|
|
}
|