desirae.js/lib/node-adapters/fsapi.js

368 lines
9.5 KiB
JavaScript
Raw Normal View History

2020-11-09 03:01:26 +00:00
"use strict";
var fs = require("fs").promises;
var path = require("path");
var forEachAsync = require("foreachasync").forEachAsync;
var walk = require("walk");
var escapeRegExp = require("escape-string-regexp");
var safeResolve = require("../utils").safeResolve;
var sha1sum = function (str) {
return require("secret-utils").hashsum("sha1", str);
};
2020-11-09 03:48:56 +00:00
var copyAll = require("util").promisify(require("fs.extra").copy);
2020-11-09 03:01:26 +00:00
//, tmpdir = require('os').tmpdir()
2015-01-06 06:07:14 +00:00
function strip(prefix, pathname) {
return pathname.substr(prefix.length + 1);
}
function walkDir(parent, sub, opts) {
opts = opts || {};
2015-01-10 19:43:25 +00:00
if (false !== opts.sha1sum) {
opts.sha1sum = true;
}
2015-01-06 06:07:14 +00:00
var prefix = path.resolve(parent);
var trueRoot = path.resolve(prefix, sub);
var files = [];
2015-01-06 06:07:14 +00:00
function filter(name) {
if (!name) {
return false;
}
2020-11-09 03:01:26 +00:00
if (!opts.dotfiles && "." === name[0]) {
2015-01-06 06:07:14 +00:00
return false;
}
if (opts.extensions && opts.extensions.length) {
2020-11-09 03:01:26 +00:00
if (
!opts.extensions.some(function (ext) {
return new RegExp("\\." + escapeRegExp(ext) + "$").test(name);
})
) {
2015-01-06 06:07:14 +00:00
return false;
}
}
return true;
}
2020-11-09 03:48:56 +00:00
return new Promise(function (resolve) {
2020-11-09 03:01:26 +00:00
var walker = walk.walk(trueRoot);
walker.on("nodeError", function (filepath, stat, next) {
2015-01-06 06:07:14 +00:00
//stats.forEach(function (stat) {
if (!filter(stat.name)) {
return;
}
stat.error.path = path.join(strip(prefix, filepath), stat.name);
files.push({
2020-11-09 03:01:26 +00:00
name: stat.name,
relativePath: strip(prefix, filepath),
path: path.join(strip(prefix, filepath), stat.name),
2015-01-10 19:43:25 +00:00
2020-11-09 03:01:26 +00:00
type: undefined,
error: stat.error,
2015-01-06 06:07:14 +00:00
});
//});
next();
});
2020-11-09 03:01:26 +00:00
walker.on("files", function (root, stats, next) {
var dirname = strip(prefix, root);
2015-01-06 06:07:14 +00:00
function eachFile(stat) {
2020-11-09 03:01:26 +00:00
var file;
2015-01-06 06:07:14 +00:00
if (!filter(stat.name)) {
2020-11-09 03:48:56 +00:00
return Promise.resolve();
2015-01-06 06:07:14 +00:00
}
file = {
2020-11-09 03:01:26 +00:00
name: stat.name,
relativePath: dirname,
path: path.join(dirname, stat.name),
2015-01-10 19:43:25 +00:00
2020-11-09 03:01:26 +00:00
createdDate: (stat.birthtime || stat.ctime).toISOString(),
lastModifiedDate: stat.mtime.toISOString(),
2015-01-10 19:43:25 +00:00
2020-11-09 03:01:26 +00:00
size: stat.size,
type: undefined, // TODO include mimetype
2015-01-06 06:07:14 +00:00
};
files.push(file);
2015-01-10 19:43:25 +00:00
if (!(opts.sha1sum || opts.content)) {
2020-11-09 03:48:56 +00:00
return Promise.resolve();
2015-01-10 19:43:25 +00:00
}
// TODO stream sha1 (for assets)
2020-11-09 03:01:26 +00:00
return fs
2020-11-09 04:04:35 +00:00
.readFile(path.join(root, stat.name), null)
2020-11-09 03:01:26 +00:00
.then(function (buffer) {
var contents = buffer.toString("utf8");
file.sha1 = sha1sum(contents);
file.type = undefined;
if (opts.contents) {
file.contents = contents;
}
});
2015-01-06 06:07:14 +00:00
}
if (!opts.contents) {
stats.forEach(eachFile);
next();
} else {
2015-01-15 05:36:59 +00:00
forEachAsync(stats, eachFile).then(function () {
next();
});
2015-01-06 06:07:14 +00:00
}
});
2020-11-09 03:01:26 +00:00
walker.on("end", function () {
2015-01-06 06:07:14 +00:00
resolve(files);
});
});
}
function walkDirs(parent, subs, opts) {
opts = opts || {};
2020-11-09 03:01:26 +00:00
var collections = {};
2015-01-06 06:07:14 +00:00
return forEachAsync(subs, function (sub) {
return walkDir(parent, sub, opts).then(function (results) {
collections[sub] = results;
});
}).then(function () {
return collections;
});
}
function getfs(blogdir, filepaths) {
2020-11-09 03:01:26 +00:00
var files = [];
2015-01-06 06:07:14 +00:00
return forEachAsync(filepaths, function (filepath) {
2020-11-09 03:01:26 +00:00
var pathname = safeResolve(blogdir, filepath);
return fs
2020-11-09 04:04:35 +00:00
.lstat(pathname)
2020-11-09 03:01:26 +00:00
.then(function (stat) {
return fs.readFile(pathname, null).then(function (buffer) {
2020-11-09 03:01:26 +00:00
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,
});
2015-01-06 06:07:14 +00:00
});
2020-11-09 03:01:26 +00:00
})
.catch(function (e) {
files.push({ path: filepath, error: e.message });
2015-01-06 06:07:14 +00:00
});
}).then(function () {
return files;
});
}
2015-01-09 08:49:14 +00:00
function makeAllDirs(dirpaths) {
2020-11-09 03:01:26 +00:00
var errors = [];
2015-01-09 08:49:14 +00:00
return forEachAsync(dirpaths, function (pathname) {
return fs.mkdir(pathname, { recursive: true }).catch(function (e) {
2015-01-09 08:49:14 +00:00
// TODO exclude attempting to write files to this dir?
errors.push({
2020-11-09 03:01:26 +00:00
type: "directory",
2015-01-09 08:49:14 +00:00
2020-11-09 03:01:26 +00:00
directory: pathname,
2015-01-09 08:49:14 +00:00
2020-11-09 03:01:26 +00:00
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
2015-01-09 08:49:14 +00:00
});
});
}).then(function () {
return errors;
});
}
function copyfs(blogdir, files) {
// TODO switch format to { source: ..., dest: ..., opts: ... } ?
2020-11-09 03:01:26 +00:00
var results = { errors: [] },
dirpaths = {},
sources = Object.keys(files);
2015-01-09 08:49:14 +00:00
return forEachAsync(sources, function (source) {
/*
var nsource = safeResolve(blogdir, source)
;
*/
2020-11-09 03:01:26 +00:00
var dest = safeResolve(blogdir, files[source]),
pathname = path.dirname(dest);
//, filename = path.basename(dest)
2015-01-09 08:49:14 +00:00
dirpaths[pathname] = true;
2020-11-09 03:01:26 +00:00
2020-11-09 03:48:56 +00:00
return Promise.resolve();
2020-11-09 03:01:26 +00:00
})
.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);
2015-01-09 08:49:14 +00:00
});
});
2020-11-09 03:01:26 +00:00
})
.then(function () {
// TODO allow delete?
return forEachAsync(sources, function (source) {
return fsExtra
2020-11-09 03:48:56 +00:00
.copyAll(
2020-11-09 03:01:26 +00:00
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;
2015-01-09 08:49:14 +00:00
});
}
2015-01-16 00:19:25 +00:00
function putfs(blogdir, files, options) {
options = options || {};
2020-11-09 03:01:26 +00:00
var putfsResults = { errors: [] },
dirpaths = {};
return forEachAsync(files, function (file) {
2020-11-09 03:01:26 +00:00
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;
2020-11-09 03:01:26 +00:00
2020-11-09 03:48:56 +00:00
return Promise.resolve();
2020-11-09 03:01:26 +00:00
})
.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 fs.mkdir(pathname, { recursive: true }).catch(function (e) {
2020-11-09 03:01:26 +00:00
// 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,
});
});
});
2020-11-09 03:01:26 +00:00
})
.then(function () {
// TODO sort deletes last
return forEachAsync(files, function (file) {
// TODO use lastModifiedDate as per client request?
// TODO compare sha1 sums for integrity
return fs
2020-11-09 04:04:35 +00:00
.access(file.realPath)
2020-11-09 03:01:26 +00:00
.then(function () {
if (file.delete || !file.contents) {
return fs.unlink(file.realPath);
2020-11-09 03:01:26 +00:00
}
if (false === options.replace || false === options.overwrite) {
throw new Error("EEXIST: the file already exists");
}
return fs.writeFile(file.realPath, file.contents, "utf8");
2020-11-09 03:01:26 +00:00
})
2020-11-09 04:04:35 +00:00
.catch(function () {
return fs.writeFile(file.realPath, file.contents, "utf8");
})
2020-11-09 03:01:26 +00:00
.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,
});
});
});
2020-11-09 03:01:26 +00:00
})
.catch(function (e) {
putfsResults.error = {
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
};
})
.then(function () {
return putfsResults;
});
}
2015-01-06 06:07:14 +00:00
/*
walkDirs('blog', ['posts'], { contents: false }).then(function (stats) {
console.log(JSON.stringify(stats, null, ' '));
});
*/
module.exports.walk = { walkDirs: walkDirs, walkDir: walkDir };
2015-01-09 08:49:14 +00:00
module.exports.copyfs = copyfs;
2015-01-06 06:07:14 +00:00
module.exports.getfs = getfs;
module.exports.putfs = putfs;
2015-01-06 06:07:14 +00:00
module.exports.walkDir = walkDir;
module.exports.walkDirs = walkDirs;
2015-01-13 21:49:11 +00:00
module.exports.fsapi = module.exports;