What we found
While fixing the mobile editor regression (web-apps, follows DocumentServer#258),
we hit a build-mode footgun that nearly invalidated the fix's testing. Three
overlapping problems:
-
The Makefile target names lie. In develop/setup/Makefile, web-apps and
web-apps-dev produce identical output — both production. The only
difference is web-apps runs npm ci and web-apps-dev skips it
(CHECK_WEBAPPS_DEPS). So -dev means "skip dependency install," NOT
"development mode." Anyone reading the name expects a dev build and gets prod.
-
The two webpack pipelines default NODE_ENV oppositely.
- desktop
web-apps/build/webpack.editor.factory.mjs → NODE_ENV || 'production'
- mobile
web-apps/vendor/framework7-react/build/webpack.config.js → NODE_ENV || 'development'
So with NODE_ENV unset, desktop builds prod and mobile builds dev unless
something forces it. A manual mobile build silently came out dev — esbuild's
minimizer never runs in dev, so the production-only transforms (ES target
downlevel, drop_console) don't execute.
-
The validation trap. Because a dev mobile build skips the minifier, a
crash that only occurs in production (esbuild lowering i18next 25's class
internals → TypeError: we is not a function) appears fixed in a dev build
— the broken transform simply never ran. "It works in my rebuild" can mean
"esbuild didn't run," not "the fix works." This cost real debugging time.
Fixes already landed in web-apps (companion PR)
build-pipeline.js now sets NODE_ENV once in CHILD_ENV
(process.env.NODE_ENV || 'production'), passed to every child — desktop and
mobile now agree, defaulting to production, overridable with NODE_ENV=development.
build-pipeline.js echoes the build mode in its banner (green production,
red ⚠ DEV BUILD warning) so the mode is never silent again.
- Mobile
watch decoupled from NODE_ENV (WATCH=1 opt-in) so a dev-mode build
is a one-shot that exits, instead of hanging the pipeline in watch mode.
Proposed change here (DocumentServer Makefile)
Make the target suffix mean the mode, self-documenting, so the shell
environment can't silently flip it:
| target |
install |
mode |
use |
web-apps |
npm ci |
production |
canonical / first build |
web-apps-prod |
skip |
production |
fast re-test of the shipping build |
web-apps-dev |
skip |
development |
fast debuggable iteration |
web-apps-dev: NODE_ENV := development
web-apps-prod: NODE_ENV := production
web-apps-dev web-apps-prod:
$(CHECK_WEBAPPS_DEPS)
cd $(EO_DEV)/web-apps/translation && python3 merge_and_check.py && \
cd $(EO_DEV)/web-apps/build && \
BUILD_ROOT=$(EO_ROOT) PRODUCT_VERSION=$(PRODUCT_VERSION) THEME=euro-office \
SKIP_MOBILE=$(SKIP_MOBILE) NODE_ENV=$(NODE_ENV) \
node scripts/build-pipeline.js
$(FLUSH_CACHE)
Heads-up / acceptance
- Behaviour change:
make web-apps-dev flips from production → development.
The dev-container docs (and web-apps CLAUDE.md) that point at make web-apps-dev must be updated: use web-apps-prod (or web-apps) to test the
shipping build; web-apps-dev to debug.
- A dev build is unminified, keeps
console, inlines CSS via style-loader, and
does NOT exercise the production transforms — by design. Never sign off a fix on
a dev build.
web-apps-prod and web-apps produce the same bundle; web-apps-prod just
skips npm ci for speed.
What we found
While fixing the mobile editor regression (web-apps, follows DocumentServer#258),
we hit a build-mode footgun that nearly invalidated the fix's testing. Three
overlapping problems:
The Makefile target names lie. In
develop/setup/Makefile,web-appsandweb-apps-devproduce identical output — both production. The onlydifference is
web-appsrunsnpm ciandweb-apps-devskips it(
CHECK_WEBAPPS_DEPS). So-devmeans "skip dependency install," NOT"development mode." Anyone reading the name expects a dev build and gets prod.
The two webpack pipelines default
NODE_ENVoppositely.web-apps/build/webpack.editor.factory.mjs→NODE_ENV || 'production'web-apps/vendor/framework7-react/build/webpack.config.js→NODE_ENV || 'development'So with
NODE_ENVunset, desktop builds prod and mobile builds dev unlesssomething forces it. A manual mobile build silently came out dev — esbuild's
minimizer never runs in dev, so the production-only transforms (ES target
downlevel,
drop_console) don't execute.The validation trap. Because a dev mobile build skips the minifier, a
crash that only occurs in production (esbuild lowering i18next 25's class
internals →
TypeError: we is not a function) appears fixed in a dev build— the broken transform simply never ran. "It works in my rebuild" can mean
"esbuild didn't run," not "the fix works." This cost real debugging time.
Fixes already landed in web-apps (companion PR)
build-pipeline.jsnow setsNODE_ENVonce inCHILD_ENV(
process.env.NODE_ENV || 'production'), passed to every child — desktop andmobile now agree, defaulting to production, overridable with
NODE_ENV=development.build-pipeline.jsechoes the build mode in its banner (greenproduction,red
⚠ DEV BUILDwarning) so the mode is never silent again.watchdecoupled fromNODE_ENV(WATCH=1opt-in) so a dev-mode buildis a one-shot that exits, instead of hanging the pipeline in watch mode.
Proposed change here (DocumentServer Makefile)
Make the target suffix mean the mode, self-documenting, so the shell
environment can't silently flip it:
web-appsnpm ciweb-apps-prodweb-apps-devHeads-up / acceptance
make web-apps-devflips from production → development.The dev-container docs (and web-apps
CLAUDE.md) that point atmake web-apps-devmust be updated: useweb-apps-prod(orweb-apps) to test theshipping build;
web-apps-devto debug.console, inlines CSS viastyle-loader, anddoes NOT exercise the production transforms — by design. Never sign off a fix on
a dev build.
web-apps-prodandweb-appsproduce the same bundle;web-apps-prodjustskips
npm cifor speed.