You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
4.8 KiB

const path = require("path");
const fs = require("fs");
const defaultSettings = {
directory: "./",
defaultExtension: "txt"
};
function listNotesInDirectory(directoryPath = "") {
return new Promise(function(resolve, reject) {
fs.readdir(directoryPath, function(err, files) {
if (err) return reject(err);
resolve(
files
.map(fPath => ({
ext: path.extname(fPath),
name: fPath,
friendlyName: path.basename(fPath, path.extname(fPath)),
fullPath: path.join(directoryPath, fPath)
}))
.filter(
({ ext }) =>
ext === ".md" || ext === ".txt" || ext === ".utf8"
)
);
});
});
}
function getNoteTime(fullPath) {
return new Promise((a, r) => {
fs.stat(fullPath, (err, stats) => {
if (err) return r(err);
a({
lastModified: stats.mtime,
created: stats.ctime,
size: stats.size
});
});
});
}
function getNoteContent(fullPath) {
return new Promise((a, r) =>
fs.readFile(fullPath, "utf8", (err, data) => {
if (err) return r(err);
a({ content: data });
})
);
}
function loadTags(directoryPath) {
return new Promise((a, r) =>
fs.readFile(
path.join(directoryPath, "meta.json"),
"utf8",
(err, data) => {
if (err) {
if (!err.message.includes("ENOENT")) return r(err);
return a(saveTags(directoryPath, {}));
}
a(JSON.parse(data));
}
)
);
}
function saveTags(directoryPath, tags = {}) {
return new Promise((a, r) =>
fs.writeFile(
path.join(directoryPath, "meta.json"),
JSON.stringify(tags),
"utf8",
err => {
if (err) return r(err);
a({});
}
)
);
}
let tagsCache = {};
function getNoteTags(directoryPath, friendlyName) {
return Promise.resolve(tagsCache[friendlyName] || []);
}
async function loadNotesInDirectory(directoryPath = "") {
const tagCache = await loadTags(directoryPath);
const files = await listNotesInDirectory(directoryPath);
return await Promise.all(
files.map(async ({ fullPath, friendlyName, ext }) => {
const [noteTime, noteContent] = await Promise.all([
getNoteTime(fullPath),
getNoteContent(fullPath),
getNoteTags(directoryPath, friendlyName)
]);
return {
path: fullPath,
title: friendlyName,
tags: tagsCache[friendlyName] || [],
...noteTime,
...noteContent
};
})
);
}
async function saveNoteInDirectory(
{ directory, defaultExtension },
{ title, content, tags }
) {
let fullPath = path.join(directory, `${title}.${defaultExtension}`);
const match = (await listNotesInDirectory(directory)).filter(
f => f.friendlyName === title
);
if (match && match.length > 0) fullPath = match[0].path;
tagsCache[title] = tags;
return Promise.all([
new Promise((a, r) => {
fs.writeFile(fullPath, content, "utf8", err => {
if (err) return r(err);
a();
});
}),
saveTags(directory, tagsCache)
]);
}
function newNoteBackend(initSettings) {
let settings = { ...defaultSettings, ...initSettings };
async function onNoteCommand({ type, payload }) {
console.log(`got command: ${type} -n ${JSON.stringify(payload)}`);
const { directory } = settings;
switch (type) {
case "getNotes":
const files = await loadNotesInDirectory(directory);
return {
type: "getNotes",
payload: {
notes: files.map(f => ({ ...f, tags: [] }))
}
};
case "saveNote":
const { title, content, tags } = payload;
const result = await saveNoteInDirectory(settings, {
title,
content,
tags
});
return { type: "saveNote", payload: result };
default:
return { type: "noop" };
}
}
function updateSettings({ directory }) {
settings.directory = directory;
}
return {
onNoteCommand,
updateSettings
};
}
module.exports = { createBackend: newNoteBackend };