From 6a9cea06a28d7dadff300627b97d5f0c5f37d600 Mon Sep 17 00:00:00 2001 From: Chris <26607885+chrisglein@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:42:29 -0700 Subject: [PATCH] Fix Windows build - Rewrite songs-to-json (python) as songs-to-json.js (node) to drop the python/shebang dependency - Replace shell 'cp' in build with a cross-platform node fs.cpSync call - Quote nodemon -e pattern with double quotes for cmd/PowerShell - Add patch-package + postinstall and patches/ for pug-pack coffeescript compilation --- package.json | 6 ++-- patches/pug-pack+1.11.0.patch | 67 +++++++++++++++++++++++++++++++++++ songs-to-json | 29 --------------- songs-to-json.js | 27 ++++++++++++++ 4 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 patches/pug-pack+1.11.0.patch delete mode 100755 songs-to-json create mode 100644 songs-to-json.js diff --git a/package.json b/package.json index e39c5a7..427d7ee 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,19 @@ { "scripts": { - "build": "./songs-to-json && pug-pack src && cp Songbook.pdf dist && cp qr-code.svg dist", + "postinstall": "patch-package", + "build": "node songs-to-json.js && pug-pack src && node -e \"const fs=require('fs');for(const f of ['Songbook.pdf','qr-code.svg'])fs.cpSync(f,'dist/'+f)\"", "booklet": "node node_modules/markdown-booklet/src/cli.js build booklet/book.yaml --out booklet/sea-shanties.html", "booklet:proof": "node node_modules/markdown-booklet/src/cli.js build booklet/book.yaml --reading --out booklet/proof.html", "booklet:single": "node node_modules/markdown-booklet/src/cli.js build booklet/book.yaml --single --out booklet/sea-shanties-single.html", "scaleL": "coffee src/transform.coffee scaleL", - "dev": "nodemon -w src -w songs -i src/songs.json -i src/by-title.json -e '*' -x npm run build" + "dev": "nodemon -w src -w songs -i src/songs.json -i src/by-title.json -e \"*\" -x npm run build" }, "dependencies": { "coffeescript": "^2.7.0", "markdown-booklet": "chrisglein/markdown-booklet", "mathjs": "^11.10.0", "nodemon": "^2.0.22", + "patch-package": "^8.0.1", "pug-pack": "brewingcode/pug-pack" } } diff --git a/patches/pug-pack+1.11.0.patch b/patches/pug-pack+1.11.0.patch new file mode 100644 index 0000000..f07fe03 --- /dev/null +++ b/patches/pug-pack+1.11.0.patch @@ -0,0 +1,67 @@ +diff --git a/node_modules/pug-pack/lib/build.coffee b/node_modules/pug-pack/lib/build.coffee +index da9318b..359c63a 100644 +--- a/node_modules/pug-pack/lib/build.coffee ++++ b/node_modules/pug-pack/lib/build.coffee +@@ -66,13 +66,18 @@ module.exports = self = + + out + ++ # compile :coffeescript blocks with our bundled compiler, so the build ++ # doesn't depend on pug hoisting jstransformer-coffeescript to top-level ++ coffeescript: (text, options) -> ++ coffeescript.compile text ++ + # replacement for path.parse(), but in the context of this module + parsename: (f) -> + parts = path.parse f + parts.ext = parts.ext.replace /^\./, '' + parts.absfile = path.resolve self.vars.basedir, f + parts.absdir = path.resolve self.vars.basedir +- parts.srcname = parts.absfile.replace(new RegExp(parts.absdir, 'i'), '').replace(/^\//, '') ++ parts.srcname = path.relative(parts.absdir, parts.absfile).split(path.sep).join('/') + self.vars.files[parts.srcname] = parts.absfile + parts + +@@ -176,23 +181,33 @@ module.exports = self = + # 1, for example + crawl: (rootDir, skipPug) -> + self.vars.basedir = path.resolve rootDir ++ gitOpts = { cwd: self.vars.basedir } ++ ++ walkFiles = (dir) -> ++ results = [] ++ for entry in fs.readdirSync dir ++ full = path.join dir, entry ++ if fs.statSync(full).isDirectory() ++ results = results.concat walkFiles full ++ else ++ results.push full ++ results + +- execAsync("cd '#{self.vars.basedir}' && git rev-parse --short HEAD").then (stdout) => ++ execAsync("git rev-parse --short HEAD", gitOpts).then (stdout) => + self.vars.src.GIT_HEAD = stdout.toString().trim() +- .catch -> ++ .catch => + self.vars.src.GIT_HEAD = null +- +- if head = self.vars.src.GIT_HEAD +- execAsync("cd '#{self.vars.basedir}' && git tag --points-at #{head}").then (stdout) => ++ .then => ++ return unless head = self.vars.src.GIT_HEAD ++ execAsync("git tag --points-at #{head}", gitOpts).then (stdout) => + self.vars.src.GIT_TAGS = stdout.toString().split('\n').filter (s) -> s.match(/\S/) +- .catch -> ++ .catch => + self.vars.src.GIT_TAGS = [] +- +- execAsync("find '#{self.vars.basedir}' -type f -print0").then (stdout) => ++ .then => + pug_files = [] + other_files = [] + +- stdout.split('\0').forEach (f) => ++ walkFiles(self.vars.basedir).forEach (f) => + return unless f + { name, ext, srcname } = self.parsename f + if name.match(/^_/) diff --git a/songs-to-json b/songs-to-json deleted file mode 100755 index b5e59f6..0000000 --- a/songs-to-json +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -import os, json, re -songs = {} -byTitle = {} -for f in os.listdir('songs'): - if not re.search(r'\.txt$', f, re.I): - continue - - try: - with open('songs/' + f) as fh: - song = fh.read() - except: - with open('songs/' + f, encoding='latin-1') as fh: - song = fh.read() - - songs[f] = song - - m = re.search(r'^T:\s*(.*)', song, re.MULTILINE) - if m: - title = m.group(1) - byTitle[title] = song - - else: - print('weird song:', f) -with open('src/songs.json', 'w') as fh: - fh.write(json.dumps(songs)) -with open('src/by-title.json', 'w') as fh: - fh.write(json.dumps(byTitle)) diff --git a/songs-to-json.js b/songs-to-json.js new file mode 100644 index 0000000..b2b0632 --- /dev/null +++ b/songs-to-json.js @@ -0,0 +1,27 @@ +const fs = require('fs'); +const path = require('path'); + +const songsDir = 'songs'; +const songs = {}; +const byTitle = {}; + +for (const f of fs.readdirSync(songsDir)) { + if (!/\.txt$/i.test(f)) continue; + + const buf = fs.readFileSync(path.join(songsDir, f)); + let song = buf.toString('utf8'); + if (song.includes('\uFFFD')) song = buf.toString('latin1'); + song = song.replace(/\r\n/g, '\n'); + + songs[f] = song; + + const m = song.match(/^T:\s*(.*)/m); + if (m) { + byTitle[m[1]] = song; + } else { + console.log('weird song:', f); + } +} + +fs.writeFileSync('src/songs.json', JSON.stringify(songs)); +fs.writeFileSync('src/by-title.json', JSON.stringify(byTitle));