fix(build): make bundle:ui build what actually ships, and stop verify clobbering it
Closes #132. `npm run bundle:ui` bundled src/ui/App.jsx with --global-name=PearCalApp and a manual JSX factory. Everything that actually ships - the CLAUDE.md Android and iOS commands, release.sh, and therefore the committed assets/app-ui.bundle - bundles src/ui/main.jsx as a plain IIFE with automatic JSX. Both wrote to the same path, so running the documented command replaced the shipping bundle with a different artifact that has no entry point wired, and git status said only "modified". It bit twice on 2026-07-23. Measured before and after rather than assumed: old bundle:ui 784,534 bytes (App.jsx + global name, no entry point) what ships 1,414,532 bytes new bundle:ui 1,414,532 bytes md5 5484d83d… - identical, git sees no change The quoting is the part worth not repeating. Writing the define as --define:process.env.NODE_ENV=\"production\" inside a package.json script does NOT survive: npm hands the line to sh, sh strips the bare double quotes, and esbuild receives the identifier `production` rather than the string. That produced a third distinct artifact - correct entrypoint, wrong output. Single quotes around the escaped pair are what make it reach esbuild intact. Also split the verify gate off the shipped artifact entirely. `npm run verify` now builds the UI to node_modules/.cache (gitignored, so it can never surface in git status) purely to prove it compiles. A gate that rewrites a tracked build output is the other half of what made this bug invisible. Deleted assets/index.html, the only consumer of window.PearCalApp and the one thing that made the old form look load-bearing. It is dead: the mobile WebView builds its own HTML inline in app/index.tsx and injects the bundle, which self-mounts via main.jsx's createRoot; the Electron renderer has its own index.html that never mentions PearCalApp; and nothing in the repo, any build script or app.json references the file. It had not been touched since the initial commit. release.sh keeps its inlined bundle commands - what a release builds should be visible where it runs - but its comment claiming bundle:ui differs is no longer true, so it now says what is actually the case. No release command changed. Verified: `npm run bundle:ui` reproduces the committed bundle byte-for-byte, and `npm run verify` leaves assets/ untouched. 266 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NEd2k9F1HgR3JHdB8spury
This commit is contained in:
co-authored by
Claude Opus 5
parent
1149d8a99e
commit
3d15740b59
@@ -1,85 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#111111" />
|
||||
<title>PearCal</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body, #root {
|
||||
height: 100%; width: 100%;
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
||||
<script src="app.bundle.js"></script>
|
||||
<script>
|
||||
// ── IPC bridge (WebView → RN → Bare) ────────────────────────────────────
|
||||
let _nextId = 1
|
||||
const _pending = new Map()
|
||||
|
||||
window.__pearDB = {
|
||||
call (method, ...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = _nextId++
|
||||
_pending.set(id, msg => {
|
||||
if (msg.error) reject(new Error(msg.error))
|
||||
else resolve(msg.result)
|
||||
})
|
||||
window.ReactNativeWebView.postMessage(JSON.stringify({ id, method, args }))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Responses from RN → WebView
|
||||
window.__pearResponse = function (msg) {
|
||||
const resolve = _pending.get(msg.id)
|
||||
if (resolve) { _pending.delete(msg.id); resolve(msg) }
|
||||
}
|
||||
|
||||
// Events from RN → WebView (P2P sync etc.)
|
||||
window.__pearEvent = function (event, data) {
|
||||
window.dispatchEvent(new CustomEvent('pear:' + event, { detail: data }))
|
||||
}
|
||||
|
||||
// ── DB proxy object passed as props to App ───────────────────────────────
|
||||
const db = {
|
||||
getProfile: () => window.__pearDB.call('getProfile'),
|
||||
updateProfile: (u) => window.__pearDB.call('updateProfile', u),
|
||||
listEvents: (opts) => window.__pearDB.call('listEvents', opts),
|
||||
putEvent: (ev) => window.__pearDB.call('putEvent', ev),
|
||||
deleteEvent: (d, id) => window.__pearDB.call('deleteEvent', d, id),
|
||||
listGroups: () => window.__pearDB.call('listGroups'),
|
||||
putGroup: (g) => window.__pearDB.call('putGroup', g),
|
||||
deleteGroup: (id) => window.__pearDB.call('deleteGroup', id),
|
||||
listMembers: (gid) => window.__pearDB.call('listMembers', gid),
|
||||
putMember: (gid, m) => window.__pearDB.call('putMember', gid, m),
|
||||
removeMember: (gid, mid) => window.__pearDB.call('removeMember', gid, mid),
|
||||
}
|
||||
|
||||
const notifs = {
|
||||
scheduleForEvent: (ev) => window.__pearDB.call('scheduleForEvent', ev),
|
||||
cancelForEvent: (id) => window.__pearDB.call('cancelForEvent', id),
|
||||
restoreAll: () => window.__pearDB.call('restoreAll'),
|
||||
}
|
||||
|
||||
const sync = {
|
||||
joinGroup: (g) => window.__pearDB.call('joinGroup', g),
|
||||
leaveGroup: (id) => window.__pearDB.call('leaveGroup', id),
|
||||
putEvent: (gid, ev) => window.__pearDB.call('putEvent:sync', gid, ev),
|
||||
deleteEvent: (gid, id, d) => window.__pearDB.call('deleteEvent:sync', gid, id, d),
|
||||
putGroup: (g) => window.__pearDB.call('putGroup:sync', g),
|
||||
}
|
||||
|
||||
// ── Mount React app ──────────────────────────────────────────────────────
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'))
|
||||
root.render(React.createElement(PearCalApp.default, { db, notifs, sync }))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+3
-2
@@ -7,9 +7,10 @@
|
||||
"android": "expo run:android",
|
||||
"ios": "expo run:ios",
|
||||
"bundle:bare": "bare-pack --linked --defer fs --defer path src/bare.js -o assets/bare-universal.bundle",
|
||||
"bundle:ui": "esbuild src/ui/App.jsx --bundle --format=iife --global-name=PearCalApp --jsx-factory=React.createElement --jsx-fragment=React.Fragment --outfile=assets/app-ui.bundle",
|
||||
"bundle:ui": "esbuild src/ui/main.jsx --bundle --format=iife --jsx=automatic --define:process.env.NODE_ENV='\"production\"' --outfile=assets/app-ui.bundle",
|
||||
"test": "node --test test/*.test.js",
|
||||
"verify": "npm test && npm run bundle:bare && npm run bundle:ui",
|
||||
"bundle:ui:check": "esbuild src/ui/main.jsx --bundle --format=iife --jsx=automatic --define:process.env.NODE_ENV='\"production\"' --outfile=node_modules/.cache/pearcal-ui-verify.bundle",
|
||||
"verify": "npm test && npm run bundle:bare && npm run bundle:ui:check",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"author": "Holepunch",
|
||||
|
||||
+6
-6
@@ -1198,12 +1198,12 @@ _confirm "app.json version looks correct — proceed with bundle builds?"
|
||||
# Constitution §5 gate: the unit tests run first and a red suite aborts the
|
||||
# release via set -e, then every bundle the release ships is rebuilt.
|
||||
#
|
||||
# The bundle commands are inlined rather than delegated to `npm run verify`
|
||||
# because package.json's bundle:ui still points at the old src/ui/App.jsx
|
||||
# entrypoint with the manual JSX factory, while the release has been shipping
|
||||
# src/ui/main.jsx with automatic JSX. Calling verify here would silently change
|
||||
# the shipped UI bundle. Reconciling package.json is tracked separately; a
|
||||
# consistency pass is the wrong place to change an artifact.
|
||||
# The bundle commands stay inlined rather than delegated to `npm run verify`,
|
||||
# so what a release builds is visible right here rather than one indirection
|
||||
# away. They are no longer DIFFERENT, though: as of #132, package.json's
|
||||
# bundle:ui builds this exact command and reproduces the shipped bundle
|
||||
# byte-for-byte, and `npm run verify` no longer writes to the tracked artifact
|
||||
# at all (it builds the UI to a throwaway path purely to prove it compiles).
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "==> Running unit tests..."
|
||||
npm test
|
||||
|
||||
Reference in New Issue
Block a user