eltro/lib/cli.mjs
Jonatan Nilsson 3bd8d4ba51 Make eltro slightly more robust
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.
2021-06-02 21:28:51 +00:00

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')
}