import { load } from "cheerio"; import { readFile, writeFileSync, mkdirSync, cpSync, rmSync } from "fs"; import process from "child_process"; import stores from "./stores.json" with { type: "json" }; function mkDir(path) { try { return mkdirSync(path) } catch (err) { if (err.code !== 'EEXIST') throw err } } function writeFile(path, content) { try { writeFileSync(path, content); } catch (err) { if (err) throw err; } console.log(`${path} updated.`); } function slugify(str) { return str .toLowerCase() .replace(/é/g, "e") .replace(/&/g, " and ") .replace(/ /g, "-") .replace(/[']+/g, "") .replace(/[^\w-]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); } function cleanWebsite(str) { return str .toLowerCase() .replace(/^https?:\/\//g, "") .replace(/^www./g, "") .replace(/\/$/g, ""); } function metaDescription({ name, meta, description }) { if (meta.length > 155 || meta.length < 145 && meta.length !== 0) { console.log(`warning: meta tag for ${name} is invalid: 145/${meta.length}/155`) } return meta || description.length > 155 ? description.slice(0, 153) + "..." : description || "A guide to and map of every independent bookstore in New York City. We have a complete list of community bookstores in NYC with locations and descriptions." } function GetRecentChanges() { const res = process .execSync('git log -15 --pretty=format:"%ct %s"') .toString(); return res.split("\n"); } function ChangeLog(logs) { let res = "\n"; let i = 0; logs.forEach((l) => { if ( i > 3 || l.includes("[skip]") || l.includes("[ignore]") || l.includes("caddy") || l.includes("renovate") ) { return; } i++; const s = l.split(" "); const date = new Date(s[0] * 1000).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); res = res + `
  • ${date} - ${s.slice(1).join(" ")}
  • \n`; }); return res; } function TableViewTemplate(rows) { let table = ""; rows.forEach((row, key) => { row.rowNumber = key; table = table + TableRowTemplate(row); }); return table + "
    "; } function TableRowTemplate({ rowNumber, name, slug, address, city }) { return ` ${name} ${address}, ${city} `; } function TitleTemplate({ name }) { return `${name} | Independent Bookstores in New York City - Best Community Bookstores in NYC`; } function SelectedStoreTemplate({ name, address, city, postcode, website, events, cafe, description, }) { return `

    ${name}

    ${address}

    ${city}, NY ${postcode}

    View in: Google Maps Apple Maps

    ${description ? `

    ${description}

    ` : ""}`; } readFile("./index.tmpl.html", function (err, data) { const changeList = GetRecentChanges(); if (err) { throw err; } const $ = load(data); stores.sort(function (a, b) { var aname = a.name.toLowerCase(); var bname = b.name.toLowerCase(); return aname === bname ? 0 : +(aname > bname) || -1; }); $("#Stores").html(TableViewTemplate(stores)); $("#storeCount").html(stores.length); $("#updatedOn").html( new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }) ); $("#changesList").html(ChangeLog(changeList)); const cssurl = $("link[type='text/css']").attr("href").split("?")[0]; $("link[type='text/css']").attr("href", cssurl + "?" + new Date().getTime()); rmSync("./build", { recursive: true, force: true }); mkDir("./build") writeFile("./build/index.html", $.html()) cpSync("./site.css", "./build/site.css"); cpSync("./robots.txt", "./build/robots.txt"); cpSync("./img", "./build/img", {recursive: true}); cpSync("./stores.json", "./build/stores.json"); stores.forEach((store) => { $("#selected").html(SelectedStoreTemplate(store)); $("#info").addClass("hidden"); let title = TitleTemplate(store); $("title").html(title); $("meta[name='title']").attr("content", title); $("meta[name='description']").attr("content", metaDescription(store)); mkDir(`./build/${slugify(store.name)}`); writeFile(`./build/${slugify(store.name)}/index.html`, $.html()); }); });