Mobile editors: fix not-loading + ES2022 target / i18next crash#156
Conversation
Ports grunt's deploy-app-mobile task to the deploy-*.js family. Copies mobile build output (index.html → index.html + index_loader.html, dist/, css/*.css, locale/, resources/img/) from the source tree into BUILD_ROOT for documenteditor, spreadsheeteditor, presentationeditor, and visioeditor. Soft-skips any editor without mobile/index.html. Copy-merge only — never wipes BUILD_ROOT. Runs before deploy-theme-images so theme images overlay-win on resources/img/ (deploy-theme-images.js:69). Fixes #258. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Insert deploy-mobile as Phase 2 (conditional on !SKIP_MOBILE) between the Phase 1 mobile webpack builds and the Phase 3 deploy-resources + deploy-theme-images pair. deploy-theme-images must run after deploy-mobile because both write to <editor>/mobile/resources/img/ and theme images must win the overlay. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Add SKIP_MOBILE gate and per-editor mobile assertions: - checkFile for index.html and index_loader.html - checkDir for dist/, css/, locale/ - parse deployed index.html and assert each hashed CSS href exists (defense-in-depth against a copy glob too narrow to bring the content-hashed stylesheet across) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The esbuild swap (f0e1837) set target:'es2015', downleveling the whole bundle — including i18next 25's class-based Translator/Interpolator. esbuild's ES2015 class-field lowering mis-compiled an internal → TypeError: we is not a function on first t(). The same swap dropped Terser's drop_console, leaking console logs into prod. Root cause is the target, not webpack-vs-grunt or the dep bump — i18next 25 ran fine under grunt's Terser build; mobile has always been a webpack build, only the minifier changed. Collapse the four scattered target declarations (babel >0.25%, esbuild es2015, postcss Chrome 49, decided floor) into build/browser-floor.mjs — single source at ES2022 (Nextcloud floor: iOS 17/Safari 17; Android 9 = updatable WebView). ES2022 keeps class fields native so the crash transform no longer runs; restore drop_console for production. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Fail the build if any webpack/babel/postcss config hardcodes a browser target instead of importing from build/browser-floor.mjs. Wired into the pipeline gates phase. Stops the target drifting back into a buried minifier option as es2015 did. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Mobile webpack defaulted NODE_ENV to 'development', desktop to 'production' — so an unset env produced a dev mobile bundle (esbuild off), which is how a prod-only crash can look "fixed" in a dev rebuild. Default NODE_ENV to production once in CHILD_ENV (honours an explicit NODE_ENV=development), echo the resolved mode in the banner (green prod / red DEV warning), and decouple mobile watch from NODE_ENV (WATCH=1 opt-in) so a dev build is a one-shot. See Euro-Office/DocumentServer#259 for the companion Makefile target cleanup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The pipeline grew from 2 to 5 phases (deploy-mobile = Phase 2, gates = Phase 5). Bring the reference in line: 5-phase overview + matching YAML AI-context block, deploy-mobile.js and verify-*.mjs in the script reference, browser-floor.mjs noted, NODE_ENV/WATCH env vars, corrected deploy-app-mobile mapping, sprites.sh -> deploy-sprites.js, mobile-builds-to-source-tree + single-browser-target invariants, and mark the done "future improvements" (CI build-pipeline, Makefile). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Two gates over-claimed their coverage: - verify-browser-target.mjs: the target/targets regexes only caught ES-year string literals, so target:'esnext', "es6", array literals (['safari15']), and any array/object babel targets sailed through. Broaden to reject any string OR array/object literal after target:/targets: (allowed form is the bare ESBUILD_TARGET / BROWSERSLIST identifier). Header comment corrected to state it is a static-source check, not proof the imported value is wired in. - verify-deploy.mjs: checkDir(dist) passed on a dist/ holding only a chunk or .map files, while a comment claimed the JS was covered. Assert the stable dist/js/app.js explicitly and fix the comment. Verified: gate still passes clean, now trips on planted 'esnext' + array targets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
ccc0e89 to
cd345e3
Compare
verify-browser-target only required a safari* entry in ESBUILD_TARGET (and iOS/Safari in BROWSERSLIST), so it guarded only the iOS half of the floor. Dropping the chrome entry would pass while silently un-guarding Android — esbuild would then assume Chrome matches Safari's feature set. Add symmetric Android/Chrome presence checks. Presence only (not version consistency): and_chr is latest-only in caniuse, so pinning Android versions is meaningless. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…anspiled
Record the scope so nobody "unifies" desktop onto this floor: desktop editors
ship the legacy source un-transpiled (Terser minify-only, mangle:false, no
babel/esbuild target, no autoprefixer) and have no explicit floor — transpiling
them would risk the dynamic property-access / `var Common = Common || {}`
patterns documented in build/README.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The "AI context — structured data" yaml block mirrored the prose above it (phases, env vars, invariants, build outputs) in a second format inside the same file — two copies that drift. It just demonstrated the cost: the 2→5 phase update had to be made twice. Nothing consumes it (no tool parses the README), and modern AI reads the prose fine. Keep the prose as the single source. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
The README documents the current build system; the grunt→webpack migration history is noise here (it lives in docs/webpack-migration.md and git). Remove the "What this replaced" task-mapping table, the obsolete "grunt ran before webpack" invariant (renumber the rest), and the "Replaces grunt X" lead-ins from the script descriptions and intro. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
verify-browser-target hardcoded the per-config regexes and per-engine presence
checks in its logic — target knowledge living outside the single source of truth.
Move the contracts into browser-floor.mjs as data (TARGET_CONTRACTS,
REQUIRED_ENGINES); the gate is now a generic runner. Adding a config consumer or a
required engine (e.g. Firefox) is a new row in the source, not a gate code change.
Also flips the wiring check from negative literal-detection ("no target literal",
which had to enumerate es-year/esnext/array shapes) to positive assertion ("must
wire `target: ESBUILD_TARGET`") — catches every hardcode form without the gate
knowing what a target looks like.
Verified: passes clean; trips on a planted hardcode (es2015 and esnext) and a
dropped engine.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…ing configs Applies the review of dedd4f4: - Move TARGET_CONTRACTS / REQUIRED_ENGINES out of browser-floor.mjs (pure values again) into a sibling browser-floor.manifest.mjs — matching the repo's existing values↔manifest↔verifier pattern (theme.config.mjs ↔ replacements.manifest.mjs ↔ verify-replacements.mjs). A values file shouldn't encode config paths or how a plugin spells its options. - verify-browser-target.mjs: clean-fail (not a raw ENOENT crash) when a contract's config path is missing or moved. Verified: passes clean; missing config → clean fail message; planted hardcode and dropped engine still fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
|
Hey @moodyjmz reviewed it with help of Claude and I share it's findings with you Reviewed the branch diff directly against the grunt configs it replaces and ran the browserslist query live. Two core fixes are correct. One real bug. One new behaviour worth flagging. Core fix 1: deploy-mobile.js — correct Cross-checked every copy step against
The Phase ordering is correct: Core fix 2: ES2022 target / i18next crash — correct
Bug:
export const BROWSERSLIST = ['iOS >= 17', 'Safari >= 17', 'and_chr >= 111', 'not dead'];Ran this live against the installed browserslist: browserslist does not track historical The Functionally harmless today (current Chrome >> 111, both support ES2022 natively, so babel/postcss output is identical). But the intent isn't landing. Fix: New behaviour: -new EsbuildPlugin({ target: 'es2015', css: true })
+new EsbuildPlugin({ target: ESBUILD_TARGET, css: true, drop: env === 'production' ? ['console', 'debugger'] : [] })This was not in the webpack config on Verdict: approve. Fix |
browserslist tracks `and_chr` as latest-only, so `and_chr >= 111` resolves to a single current version (`and_chr 132`), not a Chrome-111 floor. `chrome >= 111` resolves historically (23 entries → 111) and is the standard proxy for the Android Chrome / updatable-WebView floor. Harmless today — Safari 17 is the binding ES2022 constraint — but the Android intent wasn't encoded, and the gate's presence-only check couldn't catch it. Consistent with bb1e85c's own note that pinning `and_chr` versions is meaningless. Reported-by: MonaAghili (PR #156 review) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
|
Thanks @MonaAghili — the One point I'd push back on: the The side-effect caveat ( Evidence
new TerserPlugin({ terserOptions: { compress: { drop_console: env === 'production' } } })The swap replaced Terser with Comment prepared with Claude (Opus), verified against the branch. |
…atest-only" The Check-3 comment rationalised the presence-only check via "and_chr is latest-only" — which is now stale (the floor uses `chrome >= 111` since 700138d) and was the reasoning that masked the and_chr bug in the first place. State the gate's actual limitation instead: it checks engine presence, not the version values in the queries. Reported-by: MonaAghili (PR #156 review) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Mirror the code fix (700138d) in the migration doc — the BROWSERSLIST snippet still quoted the old `and_chr >= 111`, which resolves to a single current version rather than a Chrome-111 floor. Reported-by: MonaAghili (PR #156 review) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
|
LGTM! |
Phase 1 mobile installs/builds, Phase 2 deploy-mobile, and the verify-deploy mobile assertions all ran unconditionally already in CI once #156 landed — this just deletes the dead SKIP_MOBILE branches so there's no escape hatch left to flip back on. Refs #157 Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
CI never built mobile, so #258 (mobile shipped resources/-only) and the es2015 i18next crash both shipped with green CI. This arms the mobile deploy + verify-deploy gate that Euro-Office#156 added. Refs Euro-Office#157 Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Two mobile fixes that landed together.
resources/(404). Ported asdeploy-mobile.js+ verify-deploy assertions.target:'es2015'throwsTypeError: we is not a functionon launch. Fixed by centralising the browser target to ES2022.Root cause + validation
Crash cause. The esbuild minifier swap (
f0e183749c) settarget:'es2015', which downlevels the whole bundle — including i18next 25's class-based internals. esbuild's ES2015 class-field lowering mis-compiles a Translator internal →we is not a function. Not webpack-vs-grunt and not the i18next dep bump (it ran fine under grunt's Terser build) — mobile was always a webpack build; only the minifier changed.Fix. Collapse the four scattered, contradictory browser targets (babel
>0.25%, esbuildes2015, postcss Chrome 49, decided floor) into a single source of truthbuild/browser-floor.mjsat ES2022 (Nextcloud floor: iOS 17 / Safari 17; Android 9 = updatable WebView). ES2022 keeps class fields native, so the breaking transform never runs. Also: restoredrop_console(the esbuild swap silently dropped Terser's), make build mode explicit + echoed in the pipeline banner, decouple mobilewatchtoWATCH=1, and add averify-browser-targetgate that fails the build if a target literal (string/array/object) is hardcoded in a config instead of imported frombrowser-floor.mjs. (It's a static-source check — it asserts the import + absence of a literal, not that the imported value is the one wired in.)Validation (production build in the
eodev container):__publicField(esbuild class-field lowering helper) = 0; 79 native#private fields retained → the es2015 transform is gone.i18next is ready= 0 in all four mobile bundles →drop_consolerestored.verify-browser-targetpasses, and fails on a planted hardcoded target (string,esnext, and array-literal shapes).Review follow-ups (addressed): the two verify gates were hardened after review —
verify-browser-targetnow rejects array/object/esnexttarget literals (not justES-year strings), and
verify-deployasserts the actualdist/js/app.jsentry ratherthan only the directory. The build/README pipeline reference was also brought up to the
current 5-phase structure.
Fixes Euro-Office/DocumentServer#258
Follow-ups: #154 (CI mobile validation), Euro-Office/DocumentServer#259 (Makefile dev/prod targets)
Commits are AI-co-authored (Claude Sonnet 4.6) and adversarially reviewed/verified (Claude Opus); the fix was validated against a real production build + browser load. Human diff review still wanted.