2023-03-07 20:36:34 -05:00
import { load } from "cheerio" ;
2024-12-21 14:06:48 -05:00
import { readFile , writeFileSync , mkdirSync , cpSync , rmSync } from "fs" ;
2023-10-11 21:37:28 -04:00
import process from "child_process" ;
2024-07-08 19:13:48 -04:00
import stores from "./stores.json" with { type : "json" } ;
2017-05-28 18:54:01 -04:00
2024-12-21 14:06:48 -05:00
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 , "" ) ;
}
2024-12-21 14:52:57 -05:00
function metaDescription ( { name , meta , description } ) {
if ( meta . length > 155 ) {
console . log ( ` warning: meta tag for ${ name } is too long: ${ meta . length } ` )
}
2024-12-21 14:06:48 -05:00
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."
}
2023-10-11 21:37:28 -04:00
function GetRecentChanges ( ) {
const res = process
2023-10-11 21:51:08 -04:00
. execSync ( 'git log -15 --pretty=format:"%ct %s"' )
2023-10-11 21:37:28 -04:00
. toString ( ) ;
return res . split ( "\n" ) ;
}
function ChangeLog ( logs ) {
let res = "\n" ;
2023-10-11 21:51:08 -04:00
let i = 0 ;
logs . forEach ( ( l ) => {
if (
i > 3 ||
l . includes ( "[skip]" ) ||
2024-02-21 21:53:42 -05:00
l . includes ( "[ignore]" ) ||
2023-10-11 21:51:08 -04:00
l . includes ( "caddy" ) ||
l . includes ( "renovate" )
) {
return ;
}
i ++ ;
2023-10-11 21:37:28 -04:00
const s = l . split ( " " ) ;
const date = new Date ( s [ 0 ] * 1000 ) . toLocaleDateString ( "en-US" , {
year : "numeric" ,
month : "long" ,
day : "numeric" ,
} ) ;
res = res + ` <li> ${ date } - ${ s . slice ( 1 ) . join ( " " ) } </li> \n ` ;
} ) ;
return res ;
}
2023-03-07 17:56:58 -05:00
function TableViewTemplate ( rows ) {
2023-03-07 20:36:34 -05:00
let table = "<table>" ;
2023-03-07 17:56:58 -05:00
rows . forEach ( ( row , key ) => {
row . rowNumber = key ;
table = table + TableRowTemplate ( row ) ;
} ) ;
return table + "</table>" ;
}
2024-12-21 14:06:48 -05:00
function TableRowTemplate ( { rowNumber , name , slug , address , city } ) {
2023-03-07 17:56:58 -05:00
return `
< tr id = "${rowNumber}" class = "spotRow" >
2024-12-21 14:06:48 -05:00
< td class = "name" > < a href = "/${slugify(name)}/" > $ { name } < / a > < / t d >
< td > < a href = "/${slugify(name)}/" > $ { address } , $ { city } < / a > < / t d >
2023-03-07 17:56:58 -05:00
< / t r > ` ;
}
2024-12-21 14:06:48 -05:00
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 `
< h2 > $ { name } < / h 2 >
< p class = "address" > $ { address } < / p >
< p > < / p >
< p class = "address" >
$ { city } , NY $ { postcode }
< / p >
< p >
View in :
< a
href = " https : //maps.google.com/maps?q=${encodeURIComponent(
name
) } + $ { address } , $ { city } , NY "
target = "_blank"
> Google Maps < / a
>
< a
href = " http : //maps.apple.com/?q=${encodeURIComponent(
name
) } & address = $ { address } , $ { city } , NY "
target = "_blank"
> Apple Maps < / a
>
< / p >
< ul >
$ {
website
? ` <li><a href=" ${ website } " target="_blank"> ${ cleanWebsite (
website
) } < / a > < / l i > `
: ""
}
< li class = "storeDetails" > Events : $ { events } < / l i >
< li class = "storeDetails" > Caf & eacute ; : $ { cafe } < / l i >
< / u l >
$ { description ? ` <p class="description"> ${ description } </p> ` : "" } ` ;
}
readFile ( "./index.tmpl.html" , function ( err , data ) {
2023-10-11 21:37:28 -04:00
const changeList = GetRecentChanges ( ) ;
2022-03-19 11:45:05 -04:00
if ( err ) {
throw err ;
}
2023-03-07 20:36:34 -05:00
const $ = load ( data ) ;
2017-05-28 18:54:01 -04:00
2022-03-19 11:45:05 -04:00
stores . sort ( function ( a , b ) {
var aname = a . name . toLowerCase ( ) ;
var bname = b . name . toLowerCase ( ) ;
return aname === bname ? 0 : + ( aname > bname ) || - 1 ;
} ) ;
2017-05-28 18:54:01 -04:00
2023-03-07 17:56:58 -05:00
$ ( "#Stores" ) . html ( TableViewTemplate ( stores ) ) ;
2022-03-19 11:45:05 -04:00
$ ( "#storeCount" ) . html ( stores . length ) ;
2022-03-19 16:28:26 -04:00
$ ( "#updatedOn" ) . html (
new Date ( ) . toLocaleDateString ( "en-US" , {
year : "numeric" ,
month : "long" ,
day : "numeric" ,
} )
) ;
2023-10-11 21:37:28 -04:00
$ ( "#changesList" ) . html ( ChangeLog ( changeList ) ) ;
2023-03-07 19:06:13 -05:00
const cssurl = $ ( "link[type='text/css']" ) . attr ( "href" ) . split ( "?" ) [ 0 ] ;
2022-03-20 10:43:25 -04:00
$ ( "link[type='text/css']" ) . attr ( "href" , cssurl + "?" + new Date ( ) . getTime ( ) ) ;
2024-12-21 14:06:48 -05:00
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 ( ) ) ;
2017-05-28 18:54:01 -04:00
} ) ;
2022-03-19 11:45:05 -04:00
} ) ;