majo
Version:
A minimal module to manipulate files.
213 lines (206 loc) • 6.59 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var path = _interopDefault(require('path'));
var fs = _interopDefault(require('fs'));
var util = require('util');
var glob = _interopDefault(require('fast-glob'));
var rimraf = _interopDefault(require('rimraf'));
var ensureDir = _interopDefault(require('mkdirp'));
class Wares {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares = this.middlewares.concat(middleware);
return this;
}
run(context) {
return this.middlewares.reduce((current, next) => {
return current.then(() => Promise.resolve(next(context)));
}, Promise.resolve());
}
}
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const remove = util.promisify(rimraf);
class Majo {
constructor() {
this.middlewares = [];
this.meta = {};
this.files = {};
}
/**
* Find files from specific directory
* @param source Glob patterns
* @param opts
* @param opts.baseDir The base directory to find files
* @param opts.dotFiles Including dot files
*/
source(patterns, options = {}) {
const { baseDir = '.', dotFiles = true, onWrite } = options;
this.baseDir = path.resolve(baseDir);
this.sourcePatterns = Array.isArray(patterns) ? patterns : [patterns];
this.dotFiles = dotFiles;
this.onWrite = onWrite;
return this;
}
/**
* Use a middleware
*/
use(middleware) {
this.middlewares.push(middleware);
return this;
}
/**
* Process middlewares against files
*/
async process() {
if (!this.sourcePatterns || !this.baseDir) {
throw new Error(`[majo] You need to call .source first`);
}
const allEntries = await glob(this.sourcePatterns, {
cwd: this.baseDir,
dot: this.dotFiles,
stats: true
});
await Promise.all(allEntries.map(entry => {
const absolutePath = path.resolve(this.baseDir, entry.path);
return readFile(absolutePath).then(contents => {
const file = {
contents,
stats: entry.stats,
path: absolutePath
};
// Use relative path as key
this.files[entry.path] = file;
});
}));
await new Wares().use(this.middlewares).run(this);
return this;
}
/**
* Filter files
* @param fn Filter handler
*/
filter(fn) {
return this.use(context => {
for (const relativePath in context.files) {
if (!fn(relativePath, context.files[relativePath])) {
delete context.files[relativePath];
}
}
});
}
/**
* Transform file at given path
* @param relativePath Relative path
* @param fn Transform handler
*/
async transform(relativePath, fn) {
const contents = this.files[relativePath].contents.toString();
const newContents = await fn(contents);
this.files[relativePath].contents = Buffer.from(newContents);
}
/**
* Run middlewares and write processed files to disk
* @param dest Target directory
* @param opts
* @param opts.baseDir Base directory to resolve target directory
* @param opts.clean Clean directory before writing
*/
async dest(dest, options = {}) {
const { baseDir = '.', clean = false } = options;
const destPath = path.resolve(baseDir, dest);
await this.process();
if (clean) {
await remove(destPath);
}
await Promise.all(Object.keys(this.files).map(filename => {
const { contents } = this.files[filename];
const target = path.join(destPath, filename);
if (this.onWrite) {
this.onWrite(filename, target);
}
return ensureDir(path.dirname(target)).then(() => writeFile(target, contents));
}));
return this;
}
/**
* Get file contents as a UTF-8 string
* @param relativePath Relative path
*/
fileContents(relativePath) {
return this.file(relativePath).contents.toString();
}
/**
* Write contents to specific file
* @param relativePath Relative path
* @param string File content as a UTF-8 string
*/
writeContents(relativePath, contents) {
this.files[relativePath].contents = Buffer.from(contents);
return this;
}
/**
* Get the fs.Stats object of specified file
* @para relativePath Relative path
*/
fileStats(relativePath) {
return this.file(relativePath).stats;
}
/**
* Get a file by relativePath path
* @param relativePath Relative path
*/
file(relativePath) {
return this.files[relativePath];
}
/**
* Delete a file
* @param relativePath Relative path
*/
deleteFile(relativePath) {
delete this.files[relativePath];
return this;
}
/**
* Create a new file
* @param relativePath Relative path
* @param file
*/
createFile(relativePath, file) {
this.files[relativePath] = file;
return this;
}
/**
* Get an array of sorted file paths
*/
get fileList() {
return Object.keys(this.files).sort();
}
rename(fromPath, toPath) {
if (!this.baseDir) {
return this;
}
const file = this.files[fromPath];
this.createFile(toPath, {
path: path.resolve(this.baseDir, toPath),
stats: file.stats,
contents: file.contents
});
this.deleteFile(fromPath);
return this;
}
}
const majo = () => new Majo();
/**
* Ensure directory exists before writing file
*/
const outputFile = (filepath, data, options) => ensureDir(path.dirname(filepath)).then(() => writeFile(filepath, data, options));
exports.glob = glob;
exports.ensureDir = ensureDir;
exports.Majo = Majo;
exports.majo = majo;
exports.outputFile = outputFile;
exports.remove = remove;