369 lines
9.6 KiB
JavaScript
369 lines
9.6 KiB
JavaScript
"use strict";
|
|
|
|
var PromiseA = require("bluebird").Promise,
|
|
fs = PromiseA.promisifyAll(require("fs")),
|
|
forEachAsync = require("foreachasync").forEachAsync,
|
|
path = require("path"),
|
|
walk = require("walk"),
|
|
escapeRegExp = require("escape-string-regexp"),
|
|
safeResolve = require("../utils").safeResolve,
|
|
sha1sum = function (str) {
|
|
return require("secret-utils").hashsum("sha1", str);
|
|
},
|
|
mkdirp = PromiseA.promisify(require("mkdirp")),
|
|
fsExtra = PromiseA.promisifyAll(require("fs.extra"));
|
|
//, tmpdir = require('os').tmpdir()
|
|
function strip(prefix, pathname) {
|
|
return pathname.substr(prefix.length + 1);
|
|
}
|
|
|
|
function walkDir(parent, sub, opts) {
|
|
opts = opts || {};
|
|
if (false !== opts.sha1sum) {
|
|
opts.sha1sum = true;
|
|
}
|
|
|
|
var prefix = path.resolve(parent),
|
|
trueRoot = path.resolve(prefix, sub),
|
|
files = [];
|
|
function filter(name) {
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
|
|
if (!opts.dotfiles && "." === name[0]) {
|
|
return false;
|
|
}
|
|
|
|
if (opts.extensions && opts.extensions.length) {
|
|
if (
|
|
!opts.extensions.some(function (ext) {
|
|
return new RegExp("\\." + escapeRegExp(ext) + "$").test(name);
|
|
})
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return new PromiseA(function (resolve) {
|
|
var walker = walk.walk(trueRoot);
|
|
walker.on("nodeError", function (filepath, stat, next) {
|
|
//stats.forEach(function (stat) {
|
|
if (!filter(stat.name)) {
|
|
return;
|
|
}
|
|
|
|
stat.error.path = path.join(strip(prefix, filepath), stat.name);
|
|
files.push({
|
|
name: stat.name,
|
|
relativePath: strip(prefix, filepath),
|
|
path: path.join(strip(prefix, filepath), stat.name),
|
|
|
|
type: undefined,
|
|
error: stat.error,
|
|
});
|
|
//});
|
|
|
|
next();
|
|
});
|
|
|
|
walker.on("files", function (root, stats, next) {
|
|
var dirname = strip(prefix, root);
|
|
function eachFile(stat) {
|
|
var file;
|
|
|
|
if (!filter(stat.name)) {
|
|
return PromiseA.resolve();
|
|
}
|
|
|
|
file = {
|
|
name: stat.name,
|
|
relativePath: dirname,
|
|
path: path.join(dirname, stat.name),
|
|
|
|
createdDate: (stat.birthtime || stat.ctime).toISOString(),
|
|
lastModifiedDate: stat.mtime.toISOString(),
|
|
|
|
size: stat.size,
|
|
type: undefined, // TODO include mimetype
|
|
};
|
|
files.push(file);
|
|
|
|
if (!(opts.sha1sum || opts.content)) {
|
|
return PromiseA.resolve();
|
|
}
|
|
|
|
// TODO stream sha1 (for assets)
|
|
return fs
|
|
.readFileAsync(path.join(root, stat.name), null)
|
|
.then(function (buffer) {
|
|
var contents = buffer.toString("utf8");
|
|
file.sha1 = sha1sum(contents);
|
|
file.type = undefined;
|
|
|
|
if (opts.contents) {
|
|
file.contents = contents;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!opts.contents) {
|
|
stats.forEach(eachFile);
|
|
next();
|
|
} else {
|
|
forEachAsync(stats, eachFile).then(function () {
|
|
next();
|
|
});
|
|
}
|
|
});
|
|
|
|
walker.on("end", function () {
|
|
resolve(files);
|
|
});
|
|
});
|
|
}
|
|
|
|
function walkDirs(parent, subs, opts) {
|
|
opts = opts || {};
|
|
|
|
var collections = {};
|
|
return forEachAsync(subs, function (sub) {
|
|
return walkDir(parent, sub, opts).then(function (results) {
|
|
collections[sub] = results;
|
|
});
|
|
}).then(function () {
|
|
return collections;
|
|
});
|
|
}
|
|
|
|
function getfs(blogdir, filepaths) {
|
|
var files = [];
|
|
return forEachAsync(filepaths, function (filepath) {
|
|
var pathname = safeResolve(blogdir, filepath);
|
|
return fs
|
|
.lstatAsync(pathname)
|
|
.then(function (stat) {
|
|
return fs.readFileAsync(pathname, null).then(function (buffer) {
|
|
files.push({
|
|
name: path.basename(pathname),
|
|
relativePath: path.dirname(filepath),
|
|
path: filepath,
|
|
|
|
createdDate: (stat.birthtime || stat.ctime).toISOString(),
|
|
lastModifiedDate: stat.mtime.toISOString(),
|
|
|
|
contents: buffer.toString("utf8"),
|
|
size: buffer.length,
|
|
sha1: sha1sum(buffer),
|
|
type: undefined,
|
|
});
|
|
});
|
|
})
|
|
.catch(function (e) {
|
|
files.push({ path: filepath, error: e.message });
|
|
});
|
|
}).then(function () {
|
|
return files;
|
|
});
|
|
}
|
|
|
|
function makeAllDirs(dirpaths) {
|
|
var errors = [];
|
|
return forEachAsync(dirpaths, function (pathname) {
|
|
return mkdirp(pathname).catch(function (e) {
|
|
// TODO exclude attempting to write files to this dir?
|
|
errors.push({
|
|
type: "directory",
|
|
|
|
directory: pathname,
|
|
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
});
|
|
});
|
|
}).then(function () {
|
|
return errors;
|
|
});
|
|
}
|
|
|
|
function copyfs(blogdir, files) {
|
|
// TODO switch format to { source: ..., dest: ..., opts: ... } ?
|
|
var results = { errors: [] },
|
|
dirpaths = {},
|
|
sources = Object.keys(files);
|
|
return forEachAsync(sources, function (source) {
|
|
/*
|
|
var nsource = safeResolve(blogdir, source)
|
|
;
|
|
*/
|
|
|
|
var dest = safeResolve(blogdir, files[source]),
|
|
pathname = path.dirname(dest);
|
|
//, filename = path.basename(dest)
|
|
dirpaths[pathname] = true;
|
|
|
|
return PromiseA.resolve();
|
|
})
|
|
.then(function () {
|
|
// TODO is it better to do this lazy-like or as a batch?
|
|
// I figure as batch when there may be hundreds of files,
|
|
// likely within 2 or 3 directories
|
|
return makeAllDirs(Object.keys(dirpaths)).then(function (errors) {
|
|
errors.forEach(function (e) {
|
|
results.errors.push(e);
|
|
});
|
|
});
|
|
})
|
|
.then(function () {
|
|
// TODO allow delete?
|
|
return forEachAsync(sources, function (source) {
|
|
return fsExtra
|
|
.copyAsync(
|
|
safeResolve(blogdir, source),
|
|
safeResolve(blogdir, files[source]),
|
|
{ replace: true }
|
|
)
|
|
.catch(function (e) {
|
|
results.errors.push({
|
|
type: "file",
|
|
|
|
source: source,
|
|
destination: files[source],
|
|
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.catch(function (e) {
|
|
results.error = {
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
};
|
|
})
|
|
.then(function () {
|
|
return results;
|
|
});
|
|
}
|
|
|
|
function putfs(blogdir, files, options) {
|
|
options = options || {};
|
|
|
|
var putfsResults = { errors: [] },
|
|
dirpaths = {};
|
|
return forEachAsync(files, function (file) {
|
|
var filepath = safeResolve(
|
|
blogdir,
|
|
file.path || path.join(file.relativePath, file.name)
|
|
),
|
|
pathname = path.dirname(filepath),
|
|
filename = file.name || path.basename(filepath);
|
|
file.realPath = filepath;
|
|
file.name = filename;
|
|
|
|
dirpaths[pathname] = true;
|
|
|
|
return PromiseA.resolve();
|
|
})
|
|
.then(function () {
|
|
// TODO is it better to do this lazy-like or as a batch?
|
|
// I figure as batch when there may be hundreds of files,
|
|
// likely within 2 or 3 directories
|
|
return forEachAsync(Object.keys(dirpaths), function (pathname) {
|
|
return mkdirp(pathname).catch(function (e) {
|
|
// TODO exclude attempting to write files to this dir?
|
|
putfsResults.errors.push({
|
|
type: "directory",
|
|
|
|
directory: pathname,
|
|
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.then(function () {
|
|
// TODO sort deletes last
|
|
return forEachAsync(files, function (file) {
|
|
// TODO use lastModifiedDate as per client request?
|
|
// TODO compare sha1 sums for integrity
|
|
// NOTE existsAsync is backwards
|
|
return fs
|
|
.existsAsync(file.realPath)
|
|
.then(function () {
|
|
return fs.writeFileAsync(file.realPath, file.contents, "utf8");
|
|
})
|
|
.catch(function (/*exists*/) {
|
|
if (file.delete || !file.contents) {
|
|
return fs.unlinkAsync(file.realPath);
|
|
}
|
|
|
|
if (false === options.replace || false === options.overwrite) {
|
|
throw new Error("EEXIST: the file already exists");
|
|
}
|
|
|
|
return fs.writeFileAsync(file.realPath, file.contents, "utf8");
|
|
})
|
|
.catch(function (e) {
|
|
putfsResults.errors.push({
|
|
type: "file",
|
|
|
|
file: file.realPath,
|
|
delete: !file.contents,
|
|
path: file.path,
|
|
relativePath: file.relativePath,
|
|
name: file.name,
|
|
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.catch(function (e) {
|
|
putfsResults.error = {
|
|
message: e.message,
|
|
code: e.code,
|
|
errno: e.errno,
|
|
status: e.status,
|
|
syscall: e.syscall,
|
|
};
|
|
})
|
|
.then(function () {
|
|
return putfsResults;
|
|
});
|
|
}
|
|
/*
|
|
walkDirs('blog', ['posts'], { contents: false }).then(function (stats) {
|
|
console.log(JSON.stringify(stats, null, ' '));
|
|
});
|
|
*/
|
|
|
|
module.exports.walk = { walkDirs: walkDirs, walkDir: walkDir };
|
|
module.exports.copyfs = copyfs;
|
|
module.exports.getfs = getfs;
|
|
module.exports.putfs = putfs;
|
|
module.exports.walkDir = walkDir;
|
|
module.exports.walkDirs = walkDirs;
|
|
module.exports.fsapi = module.exports;
|