build: replace Grunt+r.js with webpack 5 — Phase E complete#109
Conversation
IE11 reached EOL June 2023. This removes all IE compatibility output: - Delete fix-ie-compat.js polyfill (fetch shim) - Remove terser:iecompat and babel task configs from Gruntfile.js and appforms.js - Remove grunt-babel loadNpmTasks - Remove iecompat copy task from common.json - Collapse all isIEBrowser require/app-loading branches in *.html.deploy to unconditionally load app/code bundles (12 HTML files across 6 editors) - Remove isIEBrowser code_path ternaries in app.js (6 editors) Expected baseline diff: 12 ie/*.js files absent, fix-ie-compat.js absent, built index.html content changes. Re-baseline after a clean build. Note: develop/setup/Makefile still references --skip-babel for web-apps-dev target; that flag is now a no-op and will be removed in a parent-repo commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Re-captures build output baseline after: - Step 4.0: IE compat bundles removed (12 ie/*.js files gone, fix-ie-compat.js gone) - Upstream: terser mangling disabled (bundle sizes increased) New state: 16,706 files, 20 JS bundles (was 32), self-diff PASSED. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…magemin - Update NODE_VERSION from stale 16 to 20 (matches bake Dockerfile) - Add THEME=euro-office to grunt step (previously unset; CI was building without euro-office branding tokens applied) - Add --skip-imagemin to match baseline (captured on ARM64/skip-imagemin; imagemin changes PNG sizes by 20-50%, which breaks the ±15% size gate) - Add "Baseline diff" step: diffs grunt output against build/scripts/baseline.json, fails the build if any bundle drops modules or expected files go missing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
bower.json and .bowerrc have no effect — all listed packages (backbone, underscore, jquery, requirejs, etc.) are already in web-apps/vendor/ and managed directly. Bower is not installed or referenced anywhere in the build pipeline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Dead code on Linux — the win32 block that called chcp.exe to detect the console codepage and load iconv-lite has never run in this environment. iconv_lite stays declared (used by _encode/_themVal) but will remain undefined, making _encode a no-op as before. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Documents that sdkjs is the only sibling-repo dep for the web-apps Grunt build (core/dictionaries/document-templates are not referenced here). Adds a preflight IIFE at Gruntfile startup that fails loudly if sdkjs is missing, rather than silently producing a broken build. Also documents the imagemin baseline gap: CI/dev baselines are captured with --skip-imagemin; x86 production bake runs imagemin and must not be diffed against the same baseline. webpack migration note in docs/multi-repo-deps.md: sdkjs is the only resolve.alias needed; a missing alias produces a smaller bundle with no webpack error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Build numbers incremented by grunt increment-build during baseline re-capture runs. NC assistant SVG icon updated to stroked outline style (fill:none, stroke:currentColor) to match Nextcloud design language. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
- build/scripts/baseline.js: module-manifest + hash + file-presence diff script referenced by CI gate (accidentally omitted from Step 1 commit) - build/scripts/smoke-test.md: manual smoke test checklist per editor - build-system-report.md: full build system analysis (Step 1 output) - migration-plan.md: webpack 5 migration plan (Option D) v5 - docs/adr/0001-accessibility-phase-1-contracts.md: ADR for a11y contracts - docs/release-process-report.md: release process analysis - docs/build-system-report.md: deep-dive findings from Step 2 (grunt-inline internals, mobile webpack contract, r.js config shape, LESS var taxonomy) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Critical corrections from advisor review:
- Baseline gate is wrong for a bundler swap — module-manifest diff will
always be red after migration; switch to file-presence + smoke test for
migrated editors
- ~19 named define('id',…) modules: webpack ignores AMD module ids,
resolves by file path — must audit and add each as resolve.alias
- code.js: second webpack entry with dependOn:'app', NOT dynamic import
- require.config() in HTML must be stripped — throws at runtime without require.js
- grunt-inline: keep as post-webpack pass (a), NOT HtmlWebpackPlugin
templateParameters (loses watch, breaks relative-path resolution)
- AGPL license headers: TerserPlugin strips comments by default — must
configure extractComments:false + preserve AGPL/Copyright comments
- Added four new risk table rows for above issues
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>
Keep only code, scripts, and build artefacts in the repo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Two entries: app (LESS + AMD app.js) and code (app_pack.js with dependOn:'app'). resolve.alias mirrors the r.js paths config; externals covers the empty: paths (sdk, socketio, xregexp etc). text! AMD plugin handled via NormalModuleReplacementPlugin + asset/source rule for .template files. TerserPlugin configured with extractComments:false and comments:/AGPL|Copyright|Ascensio/ to preserve license headers. SPIKE VALIDATION ITEM: require([...], callback) in entry files may produce async chunks — verify at runtime or add eager-require preprocessor if chunk names break the packaging contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Three issues found and resolved:
1. locale.js:174 — require([polyfill], cb) inside a non-AMD IIFE body
crashes the AMD parser (addPresentationalDependency TypeError). The
polyfill path is dead in modern browsers (native fetch/Promise).
Fix: module.noParse for locale.js.
2. LaunchController.js:46 — require({waitSeconds:0}, dynamicArray, cb) —
require.js 3-arg form with computed deps. webpack AMD parser cannot
handle it. In webpack world DocumentServer loads code.js separately.
Fix: parser:{amd:false} rule for LaunchController.js.
3. AMD require([...], callback) in entry files produced async chunks
(742.chunk.js etc). DocumentServer packaging copies only app.js/code.js
by exact name; stray chunk files are silently missing from the package.
Fix: output.asyncChunks:false merges all lazy AMD chunks into their
parent entry.
Result: app.js (1.04MB) + code.js (141KB), no stray chunk files.
AGPL header preserved via TerserPlugin comments filter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…k bundle
Two issues prevented runtime loading of the webpack-compiled visioeditor:
1. AMD externals compiled to `module.exports = void 0`.
Root cause: per-entry `library: { type: 'amd' }` + per-external
`{ amd: 'sdk' }` objects don't interact correctly — webpack cannot
generate AMD define() deps from the multi-format external object in
this config combination.
Fix: global `externalsType: 'amd'` with plain string values.
Result: `define("app", ["sdk"], (sdk) => {...})` — sdk is in the
AMD deps array and the factory parameter is wired to the module stub.
2. require.js pre-config in index.html.deploy had no `paths` for
external AMD modules (sdk, socketio, xregexp, allfonts, coapisettings,
api). Without paths, require.js cannot resolve the sdk dep declared in
the webpack define() wrapper before calling the factory.
Fix: add `baseUrl` and `paths` to the `var require = {...}` pre-config.
This mirrors the require.config() call that was in app.js source and
was a no-op in webpack.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Runtime-validated in the eo container: document editor loads, SDK
resolves, Backbone.Application boots.
Identical pattern to visioeditor spike with three differences:
- SDK path: sdkjs/word/sdk-all-min
- sdk shim added to require pre-config (sdk depends on allfonts,
xregexp, socketio — require.config() in source is a no-op in
webpack so the shim must live in the HTML pre-config)
- api is also a live external (documenteditor uses it directly;
appears in define('app', ['api','sdk'], ...) wrapper)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
… presentation editors
Same pattern as documenteditor (runtime-validated). SDK paths:
- spreadsheeteditor: sdkjs/cell/sdk-all-min
- presentationeditor: sdkjs/slide/sdk-all-min
Both emit define("app",["api","sdk"],...) — api is a live external
in both editors, same as documenteditor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Scans emitted .js and .css output for unreplaced {{TOKEN}} mustache strings.
DefinePlugin cannot reach tokens inside string literals; any survivor is an
absolute build failure (no baseline needed). .json excluded — locale files
use {{...}} for runtime i18n interpolation.
Confirmed against current webpack output: 14 tokens unreplaced across
documenteditor, spreadsheeteditor, and presentationeditor bundles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…-replace-loader
DefinePlugin rewrites bare AST identifiers only — it cannot reach tokens inside
string literals like '{{PUBLISHER_NAME}}'. All three editors shipped with 14+
unreplaced brand/config tokens baked verbatim into the bundles.
Fix: add themeReplacements(productVersion) to theme.config.mjs exporting a
string-replace-loader 'multiple' array. Apply to all .js files under APPS_ROOT
in each webpack config. Tokens resolved from env vars > config.json > stock
defaults, matching Gruntfile's _themVal precedence.
Verified clean with: node build/scripts/baseline.js ... --scan-tokens
Discovered and added {{HELP_CENTER_WEB_VE}} (visioeditor-specific, absent from
Gruntfile jsreplacements as visioeditor is not in Euro Office's active set).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Mirrors the baseUrl/paths/shim block added to index.html.deploy in the
Step 5 webpack migration. Without it, the loader entry point cannot resolve
AMD externals (sdk, socketio, xregexp, allfonts, coapisettings, api) because
require.config() in the webpack bundle is a no-op — require.js reads only the
var require = {...} pre-config block before module loading begins.
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>
- Fix output path: grunt and webpack both write to web-apps/deploy/web-apps/apps/
(BUILD_ROOT defaults to ../deploy from web-apps/build/); previous path
deploy/web-apps/apps was relative to GITHUB_WORKSPACE root and never existed.
- Add webpack step: builds documenteditor, spreadsheeteditor, presentationeditor
in parallel (overwrites grunt output for those three editors). grunt continues
to build all editors so pdfeditor/appforms/visioeditor remain covered.
- Add token scan: runs --scan-tokens after webpack builds to catch any
unreplaced {{TOKEN}} strings in the webpack output.
- Replace baseline diff with capture+upload: baseline.json was captured from
a grunt-only build inside the eo container. The dual-build output (webpack
for 3 editors, grunt for the rest) has different module manifests for the
webpack editors. Upload the fresh baseline as an artifact so it can be
committed and the --diff step restored in a follow-up.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Remove migration scaffolding (token scan, baseline capture/upload) from CI. Migration validation tools (baseline.js, --scan-tokens) are local development aids used during migration work; they are not permanent project infrastructure and will be removed from the repo when migration is complete. CI now contains only what the project needs going forward: - grunt --skip-imagemin (all editors — pdfeditor, appforms, visioeditor) - webpack in parallel for documenteditor, spreadsheeteditor, presentationeditor The webpack step is the gate: it fails CI if any editor build errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Node.js 20 actions are deprecated; forced to Node.js 24 from June 16th 2026. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
buttons.svg was deleted in 482ad6d ("remove unused icons"). The commented-out <inline> tag was left as a tombstone but grunt-inline does not respect HTML comments, causing a warning on every build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
This reverts commit 6c25d46. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Add require pre-config (baseUrl, paths) to index_loader.html.deploy to match index.html.deploy and support AMD externals resolution. Add webpack.visioeditor.js to the CI webpack step for parity with grunt which builds all four editors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Runs all four webpack configs sequentially, captures build time (webpack internal + wall-clock), asset sizes (raw + gzip), and module counts. Outputs a markdown report suitable for baseline comparison. Usage: node scripts/perf-report.js [--out <file>] MIGRATION TOOL — remove at migration completion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…plates
Without an explicit 'app' entry in paths, requirejs resolved require(['app'])
against baseUrl ("../../" = /web-apps/apps/) rather than the editor's own
directory, causing a 404 on app.js at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Replace the four r.js/Grunt webpack.{editor}.js shims with proper webpack 5
ESM configs (.mjs). All four desktop editors (document, spreadsheet,
presentation, visio) now build via webpack 5 and have been runtime-tested.
Key changes:
- build/webpack.{de,sse,ppe,ve}.mjs: webpack 5 configs with AMD library
output, string-replace-loader for source patching, asyncChunks: false,
mangle: false (Backbone needs constructor names), drop_console gated on
NODE_ENV=production
- build/theme.config.mjs: add two new string-replace rules —
c_* object-literal constants and ALL_CAPS primitive constants (SCALE_MIN,
MENU_SCALE_PART etc.) — both promoted to window globals so cross-file
consumers survive webpack factory scoping; ^ anchor in multiline mode
is critical to avoid hitting indented inside-define declarations
- apps/common/main/lib/controller/LaunchController.js: use window.require
for post-launch AMD loading so require.js (runtime) is called, not
webpack's compiled require
- apps/*/main/index.html.deploy × 4: add jquery path to require.js config
(webpack externals declare jquery as AMD; require.js must know where to
find it at runtime)
- apps/*/main/index_loader.html.deploy × 4: same jquery path addition
- build/scripts/perf-report.js: update EDITORS config references .js → .mjs
- .claude/: add migration-topology, findings (6 files), icon-migration, and
other session notes; all parked issues and known gaps documented
- CLAUDE.md: add findings index, hard rules, and parked/known issues section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
onboarding.md, pr-reviews.md, pr77-fixes.md, mobile-build-index-html.md are Claude operational notes with no value to other contributors. findings/*.md, migration-topology.md and icon-migration.md stay committed as developer documentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
chrip
left a comment
There was a problem hiding this comment.
Existing comments addressed (not repeating):
.gitignore.claude/exclusion preference (juliusknorr)CLAUDE.mdparked issues scope (juliusknorr: lines 36, 40, 52)- Vendored dependencies in
vendor/(juliusknorr:apps/documenteditor/forms/index.html.deploy:266)
Summary: Solid migration — removing grunt and IE baggage is the right move. Four findings below, one being a real bug in CI (sequential steps lose the claimed speedup), two are fragility issues that will bite later, and one cleanup.
1. CI doesn't use build-pipeline.js — claimed speedup won't materialize
.github/workflows/build.yml
The PR body says "CI build time: ~7 min → ~2 min" but the CI workflow runs all steps sequentially, duplicating the pipeline logic in shell. The parallelism benefit only exists in build-pipeline.js. Either replace the individual steps with a single node scripts/build-pipeline.js step, or use GitHub Actions jobs with needs: for parallel execution. As-is, CI won't see the claimed improvement.
2. inline-svgs.js treats zero substitutions as a hard error
build/scripts/inline-svgs.js line 118
if (totalSubstitutions === beforeEditor) {
process.exitCode = 1;
}Any future template change that removes inline tags (e.g., switching to bundled sprites) will silently break the build with a confusing error. Downgrade to a warning, or make it opt-in per editor.
3. Hardcoded baseUrl: "../../" in HTML templates
apps/documenteditor/main/index.html.deploy line 416 (and all other editor templates)
baseUrl: window.customBaseUrl || "../../"This assumes a fixed deploy layout. The old code relied on implicit requirejs baseUrl resolution, which tolerated different paths. If the app is served from a different subdirectory and customBaseUrl is never set, requireJS will fail to resolve all modules. Consider computing baseUrl dynamically from document.location.href.
4. Dead vendor artifacts: fetch and es6-promise still deployed
build/scripts/deploy-common.js simpleVendors array
fix-ie-compat.js was deleted and IE conditional loading removed from HTML. The fetch and es6-promise polyfills are no longer loaded by any code path, but deploy-common.js still copies them to BUILD_ROOT. Not harmful, but unnecessary deployed bytes.
Assisted-by: OpenCode:Qwen3.6-27B
CLAUDE.md was the only Claude-related file committed in the repo; the .claude/ dir is already gitignored. Per review (juliusknorr), Claude guidance files should not live in the shared repo. Untrack and gitignore CLAUDE.md so it persists locally for the Claude workflow but is not shipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
grunt is removed by this branch. e2e.yml (from #91) called grunt — the two would collide on merge. Replace with the webpack pipeline. Drop: npm install -g grunt-cli, grunt Add: node scripts/build-pipeline.js, SKIP_MOBILE=1 SKIP_MOBILE: e2e suite is Playwright/Chromium desktop-only; no mobile editors are exercised, so skipping framework7 builds saves ~130s CI time. Tier-1 gates (verify-replacements, verify-bundles, verify-deploy) now also run inside this job — faster failure signal before Playwright spin-up. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
|
Thanks for all four findings — responses inline. Finding 1 (CI steps vs build-pipeline.js): Valid. Will switch Finding 2 (inline-svgs zero-substitutions as hard error): Intentional. Zero substitutions on a specific editor means the HTML templates didn't contain the expected inline tags — that produces blank editors (documented in the findings catalog: a missed inline pass is "Not supported version" / blank page, high blast radius). The script already has a precedent for the softer path: Finding 3 (hardcoded Finding 4 (fetch / es6-promise "dead" bytes): You're right that they're not loaded by any IE conditional bundle — that's gone. But they're not dead: if ( !window.fetch ) {
var polyfills = ['../vendor/fetch/fetch.umd'];
if ( !window.Promise ) {
require(['../vendor/es6-promise/es6-promise.auto.min'], ...);
} else require(polyfills, _requireLang);
} else _requireLang();So removing them from the manifest would break localisation on any browser that hits that path. That said — you've raised a valid broader point. The browsers that lack native Filed as a follow-up: #129 |
12 individual steps (sprites, deploy-*, webpack×6, mobile×4, verify×2)
replaced with a single call to build-pipeline.js, matching e2e.yml.
The pipeline runs all Phase 1 tasks in parallel (deploy + webpack + mobile
together), compared to the old workflow which ran them in sequential groups.
Env handling (BUILD_ROOT validation, PRODUCT_VERSION guard, BUILD_NUMBER
fallback) moves into the pipeline; the shell 'Resolve build paths' step
is removed. Translation merge kept as a separate step — not in the pipeline.
BUILD_ROOT set explicitly to ${{ github.workspace }}/web-apps/deploy.
PRODUCT_VERSION inherited from job-level env (9.2.1).
THEME: euro-office passed to pipeline.
Addresses chrip's finding 1 on PR #109.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
- Reframe CI time headline: speedup is webpack vs r.js + Phase 1 parallelism, not a single cause - Document the 2026-06-18 build.yml → build-pipeline.js change: rationale, what collapsed, what stayed separate (translation merge) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
New files:
build/replacements.manifest.mjs — single source of truth for 6
load-bearing string-replace-loader patterns + baselines
build/vendor.manifest.mjs — single source of truth for vendor
artifact src/dest paths
build/scripts/verify-replacements.mjs — Check A: preflight pattern audit
build/scripts/verify-bundles.mjs — Check B: post-build token scan
build/scripts/verify-deploy.mjs — Check C: artifact existence gate
Modified:
build/theme.config.mjs — themeReplacements() pulls from manifest
build/webpack.editor.factory.mjs — locale rule uses manifest entry
build/scripts/lib/build-utils.js — copyFile gains { required: true } mode
build/scripts/deploy-common.js — imports VENDORS manifest; generic loop
replaces per-vendor functions; deployRequireJS/deployMonaco accept entry arg
These were implemented and validated locally but never committed.
CI failure on build-pipeline.js switch exposed the gap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
…p safety, reporter mangling Bump common.json version to 9.2.1 to match the PRODUCT_VERSION stamped by CI; clear the skip-worktree flag so the canonical version is tracked in git. Previously common.json had 4.3.0, causing local builds without an explicit env var to fail the PV_MAJOR >= 6 guard immediately. Move sharp require inside writeRaster() rather than at module top-level. sharp is a native addon (libvips) — on arm64, Alpine, or any runner without a prebuilt binary, the top-level require throws MODULE_NOT_FOUND before any function runs, crashing deploy-common, deploy-resources, and deploy-reporter even when no image processing is needed. Wrap o.kill() in try/catch in the phase() abort loop. When a fast task exits before a slow sibling fails, child.kill() calls process.kill() on an already- reaped PID and throws ESRCH on Linux. The throw propagated out of Promise.all, replacing the real failure message with "Fatal: kill ESRCH". Remove mangle:false from deploy-reporter.js. With it set, Terser only strips whitespace and leaves identifiers intact, producing a bundle ~30-50% larger than necessary. This is a plain Terser call on a single file — not webpack module scope — so mangling is safe. mangle:false in webpack.editor.factory.mjs is unchanged; it remains load-bearing for the Common namespace guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
226121e to
41fcd47
Compare
…ess cause; harden verify-bundles gate - docs/webpack-migration.md gotcha #8: expand SW staleness note to explain WHY incognito is required locally — PRODUCT_VERSION is pinned in the eo Makefile so local rebuilds emit app.js at the same URL with no cache_tag change; SW serves the stale bundle. Production deploys self-bust via the version-prefix redirect + flush-cache.sh. - docs/webpack-migration.md gotcha #9: add invariant note — output.clean:false is safe only because the output set is fixed. Enabling splitChunks, async import(), or asyncChunks without content-hashed filenames or per-editor cleaning leaves stale chunks on disk that the SW will cache silently. - build/webpack.editor.factory.mjs: matching inline comments at clean:false and splitChunks:false cross-referencing the invariant and the Common scoping finding, so the constraint is visible at the call site without needing to consult docs separately. - build/scripts/verify-bundles.mjs: hard-fail (not warn) when BUILD_ROOT exists but an expected bundle is absent. Previously warn-and-continue allowed a partial build to pass the token-survivor gate; each gate should be self-sufficient even though verify-deploy also checks bundle existence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
CSS hashes in mobile index.html files updated to reflect esbuild output from the TerserPlugin → EsbuildPlugin switch. Build artifacts — no functional change. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
chrip
left a comment
There was a problem hiding this comment.
✅ Resolved
- CI sequential steps (chrip Finding 1) — fixed in
a4d8a5b0viabuild-pipeline.js .claude/andCLAUDE.mdadded to.gitignore, file removed from commit (juliusknorr)
🔴 Blocks merge
1. Missing Assisted-by: trailers — all 51 commits
Every commit has Co-Authored-By: Claude Sonnet 4.6 but none have the Assisted-by: trailer required by AGENTS.md. These are separate conventions; Co-Authored-By satisfies GitHub attribution but does not fulfil the policy requirement.
See AGENTS.md: "Add an Assisted-by: AGENT_NAME:MODEL_VERSION git trailer to every commit containing AI-assisted content."
Please rebase before merge. A one-liner covers all commits:
git rebase origin/main --exec 'git commit --amend --no-edit --trailer "Assisted-by: ClaudeCode:claude-sonnet-4-6"'
2. build/scripts/inline-svgs.js:126 — zero substitutions = hard build exit
if (totalSubstitutions === beforeEditor) {
process.exitCode = 1;
}This breaks CI the moment a template legitimately stops using inline SVG tags. Downgrade to console.warn, or add a --strict flag for pipelines that want the hard exit explicitly.
🟡 Should fix
3. Hardcoded baseUrl: "../../" in HTML deploy templates (index.html.deploy:416)
baseUrl: window.customBaseUrl || "../../",
Assumes a fixed deploy-directory depth. Suggest deriving from document.location.href at runtime, or documenting the constraint so operators know customBaseUrl is required in non-standard deployments.
4. Dead vendor artifacts (build/vendor.manifest.mjs:41–42)
fetch and es6-promise remain in the manifest after fix-ie-compat.js was deleted and IE paths removed from HTML. ~30 KB of unreachable bytes — please remove both entries.
5. Stale grunt references (build/README.md:248, 420)
Both lines still reference grunt --skip-imagemin. Rephrase as past tense or remove the command; the surrounding explanatory context can stay.
6. mobile:install outside Phase 1 banner (build/build-pipeline.js:257–273)
mobile:install runs before the Phase 1 banner; its dependents appear inside Phase 1. Output ordering is misleading in CI logs. Move mobile:install inside the phase or add a named pre-phase.
ℹ️ Investigated, not an issue
LaunchController.js window.require — code is window.require({waitSeconds: 0}, …), not a bare call; config is intact; correct.
scroller.less data-uri replacements — all 13 replacements correct; data-uri() is unavailable in webpack's less-loader.
o.kill() try/catch — adequately handles ESRCH from already-exited processes.
Assisted-by: ClaudeCode:claude-sonnet-4-6
|
package.json had grunt stripped in f0017ae but package-lock.json was never updated — npm ci failed for anyone on this branch. Lock file now reflects the webpack-only dependency set. Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
Resolves conflicts from branding PRs (#89, #136) landing on main after build/webpack-migration diverged: - presentationeditor/index.html.deploy: take {{APP_TITLE_TEXT}} token and loader logo changes from main; keep webpack require paths/baseUrl/ shim and drop IE compat block from webpack branch - spreadsheeteditor/index.html.deploy: same — also keeps the "Spreadsheet Editor" title fix from main - build/Gruntfile.js: keep deletion (webpack branch) — grunt is gone; main's tweaks to it are irrelevant to the webpack pipeline Signed-off-by: James Manuel <moodyjmz@users.noreply.github.com>
TL;DR
Grunt is gone. All build tasks now run through webpack 5 + Node.js scripts.
CI build time: ~7 minutes → ~2 minutes (cold runner, no cache).
Local build time (eo container, SKIP_MOBILE=1): ~101s.
Mobile build time per editor: ~146–179s → ~101–132s (EsbuildPlugin, see below).
All 6 editors (documenteditor, spreadsheeteditor, presentationeditor, visioeditor, pdfeditor, forms) runtime-validated. CI green.
Developed with Claude Code — AI-assisted analysis, debugging, and implementation.
Testing
Pull Euro-Office/DocumentServer#187
pull this PR, run as normal
make local, then in docker (once)make sdkjsandmake web-apps-dev, see if errors come up. Test Euro Office in browser. Look for console errors or anything else.Build system improvements (2026-06-17)
Three follow-up commits on top of the Phase E work, addressing mobile build performance and pipeline robustness.
⚡ EsbuildPlugin replaces TerserPlugin + CssMinimizerPlugin (closes #107)
Why: Mobile webpack builds were taking 146–179s per editor with TerserPlugin doing JS minification and CssMinimizerPlugin doing CSS minification in two separate passes. esbuild's minifier handles both in a single pass and is significantly faster.
What changed:
vendor/framework7-react/build/webpack.config.js— the two-entryoptimization.minimizerarray and the duplicateCssMinimizerPluginin the production plugins array are replaced with a singlenew EsbuildPlugin({ target: 'es2015', css: true }).terser-webpack-pluginandcss-minimizer-webpack-pluginremoved frompackage.json;esbuild-loaderadded.Validation — two full pipeline runs in
eocontainer:All 21 pipeline tasks green both runs. The remaining mobile build time (~100–130s) is babel-loader + bundling — the minification step is no longer the bottleneck.
Note: Further improvement is possible by replacing babel-loader with esbuild-loader for JS transforms (~30–50s additional saving). Deferred — the mobile code uses MobX
@injectlegacy decorators which requireexperimentalDecorators: trueand warrant a separate tested commit.✨ Lazy task spawn + word-wrapped phase task list
Why:
task()previously spawned child processes eagerly. Phase 1 buildsphase1Tasks(deploy ×5 + webpack ×6) eagerly, then awaitsmobile:install(~20s), then callsphase(). This meant the phase banner and task list were printed after the fast deploy tasks had already completed and printed their ✓ lines — output order was banner-after-results, which was confusing.What changed:
task()now returns a lazy spec ({ label, cmd, args, opts }).runTask(spec)spawns and returns{ promise, kill, label }.phase()prints the banner and task list first, then callsrunTask()on each spec before awaiting. Output order is now always correct: header → task completion lines.The task list in the banner is also reformatted: previously one long comma-separated line (
sprites, deploy-common, deploy-html, ...); now word-wrapped at 80 columns with·separators.🐛 Flush stderr of killed tasks on sibling failure
Why: When a phase task fails,
phase()sends SIGTERM to all sibling processes. Killed tasks resolved withcode: -1but their buffered stderr was discarded — if the killed task had begun emitting a useful error before being killed, that output was lost.What changed: One line in
runTask():stderrBufis now flushed in thesignalbranch ofchild.on('exit'), matching the existing flush in the non-zero exit branch.CI fix — e2e workflow switched from grunt to build-pipeline.js (2026-06-18)
PR #91 (merged to
main) added.github/workflows/e2e.ymlwith a build step that callsgrunt. This PR removes grunt. The two collide:e2e.ymlwould fail withgrunt: command not foundthe moment #109 landed.Fix: merged
origin/maininto the branch (clean, no conflicts —e2e.ymlwas a new file) then updated the build step:npm install -g grunt-cliandgruntnode scripts/build-pipeline.jsandSKIP_MOBILE: '1'BUILD_ROOT,THEME,PRODUCT_VERSIONenv vars; the version-read step; the overlay build step; all triggers and Playwright run stepsSKIP_MOBILE: '1'is correct here — the e2e suite runs Playwright on Chromium desktop only; the framework7 mobile editors are not exercised, so skipping them saves ~130s of CI time per run.Bonus:
build-pipeline.jsruns the Tier-1 gates (verify-replacements preflight + verify-bundles/verify-deploy final gate). The e2e job now fails fast on a broken shim, unreplaced token, or missing output artifact — before Playwright spins up the nightly image. Cheaper failure, earlier signal.What changed
Six webpack configs replace r.js AMD bundling. Six Node.js scripts replace all grunt asset tasks. A parallel orchestrator (
build/scripts/build-pipeline.js) replaces the sequential Makefile chain. Grunt, Gruntfile.js, and all grunt test fixtures are removed.Pipeline overview
Phase 1 (all parallel): sprites, deploy-common, deploy-html, deploy-reporter, deploy-theme-images, deploy-embed, webpack ×6, mobile ×4
Phase 2 (after Phase 1): deploy-resources, deploy-theme-img, inline-svgs
Local builds get webpack filesystem cache: 19s cold → 0.6s warm rebuild per editor (full pipeline wall-clock is dominated by other Phase 1 tasks).
Theme LESS overrides
grunt's
appendThemeFiles()injectedtheme/euro-office/assets/less/theme.lessas a top-level LESS entry. webpack equivalent:theme.config.mjswrites a one-line redirector stub atapps/common/main/resources/less/_theme-main.lessapp.lessimports the stub at the endThe stub is a redirector (not a copy) so
theme.less's own relative@import "overrides.less"resolves from the theme directory. Adding a new theme override is theme-only — no build changes required. Seedocs/theme-less-overrides.md.CSS outputs to
apps/<editor>/main/app.css(root ofmain/), matching grunt's baseline so allurl()references in LESS resolve at the correct depth.Gotchas worth knowing
These surfaced during migration and are documented in
docs/webpack-migration.md:mangle: falseis load-bearing — 117 files usevar Common = Common || {}as a namespace guard that breaks under name mangling in webpack's module scope. Not negotiable without a larger refactor.PRODUCT_VERSIONmust be ≥ 6 — EuroOffice rejects editors below version 6. Thecommon.jsonfallback is4.3.0. Pipeline fails fast if it's missing.locale.jscrashes webpack's AMD parser — fixed withstring-replace-loader. Do not usenoParse.deploy-html+inline-svgsare a unit — running deploy-html alone leaves broken?__inline=truetags and produces blank editors.index.html(un-built source) still uses SVGInjector but that file is never deployed.Known pre-existing issues (not caused by this migration)
warnings_s.svg404url()path depth — pre-existingFormsTab.getView()throws on plain PDFTracked follow-ups
build: evaluate esbuild as TerserPlugin replacement for mobile (framework7-react) builds #107 — Evaluate esbuild as TerserPlugin replacement for mobile builds✅ resolved in this PR.claude/findings folder to repoUpdate✅ resolved in this PR (2026-06-18)e2e.ymlbuild step from grunt tobuild-pipeline.jsDocumentServer changes
Corresponding Makefile update (removes grunt from
make web-apps-dev, callsbuild-pipeline.js): see Euro-Office/DocumentServer PR [linked below].