Files
pinnacle/bin/postprocess-nc.js
neonoverlord 06b2c4f8a8
build / build (push) Successful in 3m11s
fix: the dead themes — inline script died on unsubstituted process.env.BASEPATH
The inline script is built by its own rollup pass (build-inline-script),
not webpack, and that pass only substituted THEME_COLORS. The fork's
basepath.js (imported via themeEngine) put process.env.BASEPATH into the
inline bundle, so line 1 threw 'process is not defined' in the browser
and the ENTIRE inline script has been dead since the basepath feature
landed: no window.__themeColors, no boot theme (permanent royal-blue),
and every runtime switchToTheme() — theme radios, grayscale's color
half — threw on the missing global AFTER saving state. Caught live by
an instrumented headless-Firefox probe against the deployed bytes:
radio click saved instanceThemes correctly, then threw in
switchToTheme; typeof window.__themeColors === 'undefined'; eval of the
served inline script reproduced 'process is not defined' at line 1.

Fixes, all one build:
- build-inline-script: substitute process.env.BASEPATH (the fix)
- postprocess-nc: DROP the `/theme-${` text rewrite — theme URLs go
  through compiled bp() now, so it had become a double-prefixer
- postprocess-nc: prune the 404-crawl wreckage tree export/apps/ — the
  crawler follows bp()-prefixed links into dead 404 shells (error:1, no
  router) whose links the attribute rewrite then double-prefixed; users
  wandering in there found every control dead
- inline-script: one-time instanceThemes 'default'->'bawnet' migration
  (marker store_themeMigratedToBawnet so a deliberate Royal pick sticks)
  — pre-bawnet builds stamped 'default' at OAuth time
2026-08-06 11:32:49 -07:00

88 lines
3.8 KiB
JavaScript

// Subpath adaptation for the Nextcloud-wrapped build (ops/features#30).
//
// Why this exists: the for-pinafore-26 sapper's `export --basepath` is broken —
// it lays files out under the basepath but prerenders every page through its
// 404 handler (__SAPPER__ carries error:1, baseUrl:"") — so we export at ROOT
// (the healthy, tunnel-proven mode) and adapt the artifacts here instead:
//
// 1. every absolute href=/... and src=/... in exported HTML gets the
// basepath prefix (covers the <base> tag, scripts, styles, manifest,
// icons — the lot),
// 2. the inline __SAPPER__ bootstrap gets baseUrl:"<BASEPATH>" so the
// client router matches routes under the subpath,
// 3. the service-worker registration is pointed under the basepath AND the
// worker itself is replaced with a self-unregistering stub — offline
// shell caching inside a Nextcloud iframe is pure liability (it serves
// stale builds after every upgrade and buys nothing).
//
// Webpack 5's default publicPath ("auto") makes dynamic chunk loading
// self-locating, so JS bundles need no rewriting.
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const BASEPATH = process.env.BASEPATH
if (!BASEPATH || !BASEPATH.startsWith('/')) {
console.error('postprocess-nc: BASEPATH env var required (leading slash)')
process.exit(1)
}
const EXPORT_DIR = path.join(__dirname, '..', '__sapper__', 'export')
const SW_STUB = "self.addEventListener('install',function(){self.skipWaiting()});" +
"self.addEventListener('activate',function(){self.registration.unregister()});\n"
function walk (dir) {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => {
const p = path.join(dir, e.name)
return e.isDirectory() ? walk(p) : [p]
})
}
// The root-export crawl follows the bp()-prefixed internal links into
// /apps/<app>/client/... and exports them as 404 shells: dead copies of the
// app (error:1 baked in, router never starts) whose already-prefixed hrefs
// the attribute rewrite below would then DOUBLE-prefix — a self-contained
// graveyard the user can wander into and where nothing works. Never ship it.
const wreckage = path.join(EXPORT_DIR, 'apps')
if (fs.existsSync(wreckage)) {
fs.rmSync(wreckage, { recursive: true })
console.log('postprocess-nc: pruned the 404-crawl wreckage tree export/apps/')
}
let htmlCount = 0
let swCount = 0
for (const file of walk(EXPORT_DIR)) {
if (path.basename(file) === 'service-worker.js') {
fs.writeFileSync(file, SW_STUB)
swCount++
continue
}
if (!file.endsWith('.html')) continue
let html = fs.readFileSync(file, 'utf8')
const before = html
// absolute refs -> under the basepath ((?!\/) spares protocol-relative //)
html = html.replace(/(href|src)=(["']?)\/(?!\/)/g, `$1=$2${BASEPATH}/`)
// the client router's basepath
html = html.replace('baseUrl:""', `baseUrl:"${BASEPATH}"`)
// service-worker registration inside the inline bootstrap
html = html.replace(/register\('\/service-worker\.js'/g, `register('${BASEPATH}/service-worker.js'`)
// NOTE: no rewriting inside inline-script JS — its theme URLs go through
// the compiled bp() (BASEPATH is substituted by build-inline-script.js),
// so a text rewrite here would DOUBLE the prefix. The old `/theme-${`
// rewrite did exactly that; it was compensating for the inline script
// crashing wholesale on an unsubstituted process.env.BASEPATH, which is
// fixed at the source now.
if (html !== before) htmlCount++
fs.writeFileSync(file, html)
}
if (htmlCount === 0) {
console.error('postprocess-nc: rewrote 0 html files — export layout changed?')
process.exit(1)
}
console.log(`postprocess-nc: rewrote ${htmlCount} html files, stubbed ${swCount} service worker(s), basepath ${BASEPATH}`)