UNPKG

bookworms

Version:

A cli tool for centralising and generating bookmarks

157 lines (139 loc) 4.41 kB
// TODO this is a bit messy - will rewrite when I'm not feeling sick // This is a utility to convert existing browser bookmarks into YAML // It can't pull descriptions as they are no required in browsers import events from "events"; import readline from "readline"; import stringify from "json-stringify-safe"; import { createReadStream } from "fs"; import { v4 as uuidv4 } from "uuid"; import { xml2json } from "xml2json-light"; import yaml from "js-yaml"; import { writeBookmark } from "./save-bookmarks.js"; const parseHTMLtoJSON = async (path) => { let bookmarks = {}; let currentNode = bookmarks; let latestNode = bookmarks; let idQueue = []; let subId = uuidv4().replace(/-/g, ""); try { const rl = readline.createInterface({ input: createReadStream(path), crlfDelay: Infinity, }); // rewrite this to make it cleaner and more testable // This was heavily borrowed from https://github.com/CCharlieLi/bookmark-parser rl.on("line", (line) => { line = line .replace(/(<DT>.+)/g, "$1</DT>") .replace(/(<!DOCTYPE.+)/g, "") .replace(/(<META.+)/g, "") .replace(/<p>/g, "") .replace(/<HR>/g, ""); let str; if (line.match(/^<TITLE>(.+)<\/TITLE>$/)) { str = line.match(/^<TITLE>(.+)<\/TITLE>$/); latestNode = currentNode[str[1]] = {}; } if (/<DL>/.test(line)) { const prevNode = currentNode; currentNode = latestNode; currentNode.prevNode = prevNode; currentNode.children = []; idQueue.push(subId); subId = 0; } if (/<\/DL>/.test(line)) { currentNode = currentNode.prevNode; subId = parseInt(idQueue[idQueue.length - 1], 10); idQueue.pop(); } if (line.match(/<DT>(<H3.+<\/H3>)<\/DT>/)) { str = line.match(/<DT>(<H3.+<\/H3>)<\/DT>/); const tmp = xml2json(str[1]); currentNode.children.push({ label: tmp["H3"]["_@ttribute"], type: "folder", }); latestNode = currentNode.children[currentNode.children.length - 1]; } if (line.match(/<DT>(<A.+<\/A>)<\/DT>/)) { str = line.match(/<DT>(<A.+<\/A>)<\/DT>/); const tmp = xml2json(str[1].replace(/&/g, "%26")); currentNode.children.push({ label: tmp["A"]["_@ttribute"], type: "bookmark", href: tmp["A"]["HREF"], }); latestNode = currentNode.children[currentNode.children.length - 1]; } if (line.match(/<DD>(.+)/)) { str = line.match(/<DD>(.+)/); latestNode.description = str[1]; } }); await events.once(rl, "close"); // this returns the entire bookmarks object // so far only really tested chrome return JSON.parse(stringify(bookmarks)).Bookmarks.children[0].children; } catch (err) { // need to write tests here console.error(err); } }; const generateBookwormJSON = (json) => { return { label: "Bookworms", description: "These bookmarks were generated by a browser export", ...traverseStructure(json), }; }; // I need to clean this up but it was so gross making it work // will come back to it const traverseStructure = (json) => { const arr = {}; json.forEach((entry) => { const { type, label } = entry; if (type === "folder") { if (!arr.folders) { arr.folders = []; } const response = traverseStructure(entry.children); const folder = { label, }; if (response.folders) { folder.folders = response.folders; } if (response.bookmarks) { folder.bookmarks = response.bookmarks; } arr.folders.push(folder); } if (type === "bookmark") { if (!arr.bookmarks) { arr.bookmarks = []; } arr.bookmarks.push({ label, href: entry.href, }); } }); return arr; }; const convertHTMLtoYAML = async (path) => { const parsedJSON = await parseHTMLtoJSON(path); const bookwormJSON = generateBookwormJSON(parsedJSON); return yaml.dump(bookwormJSON); }; const createBookmarks = async (path, directory, filename) => { console.log(`Writing ${filename} to ${directory}`); writeBookmark(await convertHTMLtoYAML(path), directory, filename); }; export { parseHTMLtoJSON, generateBookwormJSON, traverseStructure, convertHTMLtoYAML, createBookmarks, };