import fs from 'fs/promises' import fsSync from 'fs' import path from 'path' import { fileURLToPath } from 'url' const __dirname = path.dirname(fileURLToPath(import.meta.url)); let structure = ` home/ a/ file1 file2 b/ file1 file2 c/ bb/ file1 file2 d/ file1 file2 e/ file1 file2 sub/ deep_node_modules/ ma/ file1 file2 mb/ file1 file2 mc/ ` let code = structure .split('\n') .filter(line => line) .map(function(line) { return { indent: line.length - line.replace(/^\s+/,'').length, type: /\/$/.test(line) ? 'dir': 'file', text: line.replace(/^\s+|\s*\/\s*|\s+$/g, '') } }) function join(arr) { return arr.join('/') } function transform(arr) { let result = [] let temp = [] let indent = 0 arr.forEach(function(line) { if (!line.text) { return } else if (!line.indent) { temp.push(line.text) result.push({type: line.type, text: join(temp) }) } else if (indent < line.indent) { temp.push(line.text) result[result.length - 1].type = 'dir' result.push({type: line.type, text: join(temp) }) } else if (indent === line.indent) { temp.pop() temp.push(line.text) result.push({type: line.type, text: join(temp) }) } else if(indent > line.indent) { temp.pop() temp.pop() temp.push(line.text) result.push({type: line.type, text: join(temp) }) } indent = line.indent }) return result } function gracefully(promise) { return promise.catch(function(err) { console.log(err) }) } function emptyFile(target) { let folder = target.slice(0, target.lastIndexOf('/')) /*return fs.mkdir(folder, { recursive: true, force: true }) .then(() => fs.open(target, 'w').then(fd => fd.close()))*/ return fs.stat(folder) .catch(() => fs.mkdir(folder, { recursive: true, force: true })) .then(() => { return fs.open(target, 'w').then(fd => fd.close()) }) } export function Builder() { this.root = path.join(__dirname, '__TREE__') this.transformed = transform(code) } Builder.prototype.init = async function() { await Promise.all( this.transformed.filter(line => line.type === 'dir').map(line => { let target = path.join(this.root, line.text) return gracefully(fs.mkdir(target, { recursive: true })) }) ) await Promise.all( this.transformed.filter(line => line.type !== 'dir').map(line => { let target = path.join(this.root, line.text) return gracefully(emptyFile(target)) }) ) } Builder.prototype.getPath = function(fpath, sub) { return path.join(this.root, fpath, sub || '') } Builder.prototype.modify = function(fpath, delay) { if (delay) throw new Error('remove delay') let filePath = this.getPath(fpath) return fs.appendFile(filePath, 'hello') } Builder.prototype.remove = function(fpath) { let filePath = this.getPath(fpath) return fs.rm(filePath, { recursive: true, force: true }) .catch(() => this.delay(100) .then(() => fs.rm(filePath, { recursive: true, force: true })) ) } Builder.prototype.removeSync = function(fpath) { let filePath = this.getPath(fpath) return fsSync.rmSync(filePath, { recursive: true, force: true }) } Builder.prototype.newFile = function(fpath, delay) { if (delay) throw new Error('remove delay') let filePath = this.getPath(fpath) return emptyFile(filePath) } Builder.prototype.createDirectory = function(fpath) { let filePath = this.getPath(fpath) return fs.mkdir(filePath, { recursive: true }) } Builder.prototype.randomName = function() { return Math.random().toString(36).substring(2, 14) } Builder.prototype.newRandomFiles = function(fpath, count) { return Promise.all( new Array(count).fill(0).map(() => { let name = this.randomName() let filePath = this.getPath(fpath, name) return emptyFile(filePath).then(() => name) }) ) } Builder.prototype.cleanup = function() { return fs.rm(this.root, { recursive: true, force: true }) } Builder.prototype.getAllDirectories = function() { function walk(dir) { let ret = [] fsSync.readdirSync(dir).forEach(function(d) { let fpath = path.join(dir, d) if (fsSync.statSync(fpath).isDirectory()) { ret.push(fpath) ret = ret.concat(walk(fpath)) } }) return ret } return walk(this.root) } Builder.prototype.delay = function(delay) { return new Promise(res => { setTimeout(res, delay) }) } export function Counter(res, countTotal, waitForSignal = false) { this._res = res this.counter = 0 this._countTotal = countTotal this._startCount = !waitForSignal this._gotSignal = false this._signal = null } Counter.prototype.waitForCount = function(promise) { if (!promise) { promise = Promise.resolve() } return new Promise(res => { promise.then(() => { this._signal = res this.hasSignal() }) }) } Counter.prototype.updateRes = function(res) { this._res = res } Counter.prototype.hasSignal = function() { if (this._gotSignal && this._signal) { this._gotSignal = false let temp = this._signal this._signal = null temp() } } Counter.prototype.startCounting = function() { this._startCount = true } Counter.prototype.count = function() { if (!this._startCount) return this._gotSignal = true this.counter++ if (this.counter === this._countTotal && this._res) { this._res() } this.hasSignal() }