Skip to content

perf(build): cut npm run build RAM, time, and output size - #769

Open
SudoThijn wants to merge 1 commit into
mainfrom
hotfix/performance
Open

perf(build): cut npm run build RAM, time, and output size#769
SudoThijn wants to merge 1 commit into
mainfrom
hotfix/performance

Conversation

@SudoThijn

Copy link
Copy Markdown

Ports the build-performance fixes from openregister PR #108 to opencatalogi.

Results

Clean production build (npm run build), peak RAM sampled across the whole webpack process tree:

Metric Before After Improvement
Build time 61.6 s 13.6 s 4.5× faster
Peak RAM 9.56 GB 3.16 GB 3.0× less
js/ output size 162 MB 23 MB 86% smaller

What changed

webpack.config.js

  • esbuild minifier in place of Terser (TerserPlugin.esbuildMinify, parallel: false, legalComments: 'eof'). esbuild parallelises internally, so webpack-worker parallelism is disabled to avoid redundant Node processes.
  • No production source maps. The config was forcing devtool = 'inline-source-map' unconditionally (a later override that beat the isDev ? … : … line), embedding base64 source maps into every bundle. Now false in production — this accounts for most of the size drop.
  • cache = false in production — the in-memory cache is unused in a single-shot build and only added heap pressure.
  • splitChunks vendor cacheGroup. node_modules was being bundled into all 5 entrypoints (each 54–92 MB). It now lands once in a shared opencatalogi-vendor.js (18 MB), shrinking the per-entry chunks to KBs.
  • Entrypoints manifest emitter — writes js/opencatalogi-entrypoints.json mapping each entry to its ordered initial chunks (vendor → shared → entry).

PHP (required so the shared vendor chunk loads before each entry)

  • New lib/Service/ScriptManifestLoader.php reads the manifest and enqueues each chunk via Util::addScript, falling back to the legacy single-script name when the manifest is absent.
  • Updated the 5 script-loading callsites to use it: templates/index.php, templates/settings/admin.php, and the three lib/Dashboard/*Widget.php files.
  • New tests/Unit/Service/ScriptManifestLoaderTest.php covering every manifest fallback branch (missing file, unknown entry, invalid JSON, non-array JSON, happy path).

package.json — adds esbuild and terser-webpack-plugin to devDependencies.

Drive-by fix

The committed config could not build locally at all: the @nextcloud/dialogs alias lacked a $, making it a prefix alias that hijacked @nextcloud/dialogs/style.css → a raw path bypassing the package's exports map. Made it exact (@nextcloud/dialogs$), matching the other singleton aliases (vue$, pinia$, @nextcloud/vue$). The baseline numbers above were taken with this fix applied, so the comparison is apples-to-apples.

Verification

  • npm run build succeeds; manifest and opencatalogi-vendor.js emitted as expected.
  • phpcs, phpstan, psalm clean on all changed PHP files.
  • phpunitScriptManifestLoaderTest 5/5 passing.

Notes for reviewers

  • No runtime behaviour change for users: the loader emits the same scripts, just split and ordered, with a graceful fallback.
  • js/ build output (including the manifest) is gitignored and produced fresh by CI.
  • Remaining build warnings are benign: standard webpack "entrypoint size exceeds 244 KiB" hints (present before too) and one Duplicate key "euro" notice esbuild surfaces from a third-party dependency that Terser silently ignored.

Apply the openregister PR #108 build-performance fixes to opencatalogi:

- Minify with esbuild (TerserPlugin.esbuildMinify, parallel:false,
  legalComments:'eof') instead of Terser.
- Disable production source maps (a stray unconditional
  devtool='inline-source-map' was embedding base64 maps in every bundle).
- Disable the in-memory build cache in production.
- splitChunks vendor cacheGroup so node_modules is bundled once into a
  shared opencatalogi-vendor.js instead of duplicated across all 5 entries.
- Emit js/opencatalogi-entrypoints.json and load it via the new
  ScriptManifestLoader so each entry pulls its split chunks in order,
  with a fallback to the legacy single-script name.
- Fix a pre-existing build blocker: make the @nextcloud/dialogs alias exact
  (@nextcloud/dialogs$) so subpath imports like /style.css resolve via the
  package exports map.

Result: build time 61.6s -> 13.6s, peak RAM 9.56GB -> 3.16GB,
js/ output 162MB -> 23MB.

Adds ScriptManifestLoaderTest covering all manifest fallback branches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant