Search by

oocscommerce / module-requirejs-optimize

oocscommerce-dev

Browserless RequireJS bundling for Magento 2 storefronts

Package info

github.com/oocscommerce/module-requirejs-optimize

Language:JavaScript

Type:magento2-module

pkg:composer/oocscommerce/module-requirejs-optimize

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

Stars: 7

Open Issues: 0

1.2.0 2026-10-31 05:40 UTC

This package is not auto-updated.

Last update: 2026-10-31 05:47:27 UTC


README

oocscommerce/module-requirejs-optimize

Bundles the RequireJS modules of a Magento 2 storefront into a few chunks per page type, without a browser: like Vite, it finds what each page needs by reading the code, not by recording page loads.

Static analysis of layout XML, templates, PHP blocks, CMS content and widgets → JS entry points per page type → module graph → chunks per page type, with lazy modules merged per lazy root. The Magento module collects the site context with Magento APIs; the Node engine in tools/ parses JS and writes the chunks.

Features

  • No browser, no crawling: entry points come from layout, templates, PHP blocks, CMS pages/blocks and widgets
  • Chunks per page type (common, product, cart_checkout, ...); mixins and single-use dependencies merged with the module that uses them
  • Lazy modules stay lazy, merged per lazy root with their dependencies, mixins and knockout templates
  • ifconfig blocks follow the store configuration
  • Works with merge_files, minify_files (chunks become *.min.js) and Magento_Csp subresource integrity (2.4.7+): hashes of the changed files are saved through Magento's own SRI storage
  • Falls back to single files when a chunk fails to load; rebuilds do not break pages being loaded
  • No npm install on the site: the engine ships as one file

Benchmark

JS files loaded on Magento 2.4 Open Source, Luma with sample data, production mode, cold cache:

Page Before After
Home 194 12
Category 195 11
Product 220 19
Search 200 15
Cart 202 11
Checkout 255 11

Only these main pages were measured. Other pages (customer account, CMS pages, wishlist...) load fewer files too: the code every page shares comes from one common chunk.

Installation

Requirements: production mode, Node.js 18+ on the machine that runs bin/magento, dev/js/enable_js_bundling off.

composer require oocscommerce/module-requirejs-optimize
bin/magento module:enable OOCS_RequireJsOptimize
bin/magento setup:upgrade && bin/magento setup:di:compile
bin/magento setup:static-content:deploy
bin/magento static:optimize:init       # write static-optimize.json (page types, exclude, entries) to edit
bin/magento static:optimize:run        # build every theme/locale used by an active store view

Usage

Run bin/magento static:optimize:run after every setup:static-content:deploy: the deploy rewrites requirejs-config.js and its subresource integrity hash, which removes the loader.

build and clean end with a new static content version, as setup:static-content:deploy --refresh-content-version-only does, so browsers and CDNs load the new requirejs-config.js, unless no theme/locale was built or --no-version-refresh is passed. The full page cache and Varnish/CDN are not purged: do it after the run.

bin/magento static:optimize:run [build|check|clean] [--theme Vendor/theme] [--locale xx_XX]
        [--min-chunk 0] [--max-chunk 0] [--progressive] [--json report.json] [--node /path/to/node] [--no-version-refresh] [-v]
bin/magento static:optimize:init [--force]   # write static-optimize.json with the default page types
  • build (default) writes the chunks and the loader, check only prints the analysis, clean removes them.
  • --theme, --locale: only this theme/locale; by default every one used by an active store view.
  • --min-chunk N: chunks shared by several page types and smaller than N KB are merged into common. Fewer requests, but every page then downloads that code, needed or not. 0 (default) never merges.
  • --max-chunk N: chunks larger than N KB of source are written as several files, requested together. Each file is parsed and run in a shorter main-thread task (lower Total Blocking Time) at the cost of a few more requests. 0 (default) never splits.
  • --progressive: bundled modules are defined in slices of about 15 ms, giving the main thread back to the browser in between, instead of in one long task (lower Total Blocking Time). The chunks are still downloaded at once; only running their modules is spread out. Off by default.
  • --json report.json (check): full analysis as JSON.
  • --node: Node.js binary when node is not in the PATH.
  • --no-version-refresh: keep the static content version (see above). Refresh it yourself after the run: with dev/js/merge_files on, pages keep their merged JS file (and the old loader) until the version changes, as Magento names merged files after it.
  • -v: engine details, and keeps the context file for debugging.

Configuration: static-optimize.json

Optional, in the Magento root; bin/magento static:optimize:init writes it with the defaults.

{
    "minChunk": 0,
    "maxChunk": 0,
    "progressive": false,
    "exclude": ["Vendor_Slider/js/*"],
    "entries": {"home": ["Magento_PageBuilder/js/widget-initializer"]},
    "pages": {
        "home": ["default", "cms_index_index", "cms_page_view"],
        "wishlist": ["default", "wishlist_index_index"]
    }
}
  • pages: page types and the layout handles Magento loads for each. Without the file the defaults are used (home, category, product simple/configurable/bundle/grouped, search, cart, checkout, success, login, account). Page types whose handles have no layout on the site are skipped.
  • Blocks with ifconfig="..." in layout XML follow the store configuration of the store views using the theme/locale: flag on -> bundled like any block, off -> not rendered so its JS is ignored, different values between those store views -> its JS stays single files. Run static:optimize:run again after changing such a setting.
  • minChunk, maxChunk: defaults of --min-chunk and --max-chunk (KB, 0 = off); the command line options win when given.
  • progressive: true builds as with --progressive.
  • exclude: module ids never bundled (* matches anything, / included); they load as single files, e.g. a third-party module that breaks inside a bundle.
  • entries: extra entry modules per page type for JS the static analysis cannot find (added by PHP at runtime, Page Builder...); they and their dependencies are bundled for that page type.

With -v the context file handed to the engine is kept in var/tmp/oocs_requirejsoptimize/.

Customizing the engine

PHP (Console/, Model/) collects the site context with Magento APIs; the Node engine in tools/ does the analysis and writes the chunks. The command runs tools/dist/rjs-optimize.js, a single file with all dependencies bundled, so sites need no npm install.

Path Role
tools/bin/rjs-optimize.js CLI: check, build, clean
tools/src/project.js reads the context file written by PHP
tools/src/requirejs.js requirejs-config.js, module id → file resolution, mixins
tools/src/layout.js layout XML, templates, PHP blocks, CMS → JS entry points
tools/src/graph.js module dependency graph (acorn)
tools/src/analyze.js modules per page type, chunk proposal
tools/src/build.js chunk files, loader, clean

After changing tools/src or tools/bin, rebuild the single file and commit it:

cd tools
npm ci            # once: acorn, fast-xml-parser, terser, esbuild
npm run dist      # writes dist/rjs-optimize.js

When dist/rjs-optimize.js is missing, the command runs tools/bin/rjs-optimize.js from the sources instead, which needs npm ci. Delete the dist file while developing to skip the rebuild step.

Debugging: -v keeps the context file and prints its path, so the engine can be run on it directly (module in vendor/oocscommerce/module-requirejs-optimize, or app/code/OOCS/RequireJsOptimize):

bin/magento static:optimize:run check -v
node vendor/oocscommerce/module-requirejs-optimize/tools/bin/rjs-optimize.js check \
    --context var/tmp/oocs_requirejsoptimize/context-Magento_luma-en_US.json --json report.json

What build does

  • Groups modules by the set of page types that need them (common, product, cart_checkout, ...); mixins and single-use dependencies end up next to the module that uses them. Pages that may load a module through an ifconfig block count for the grouping.
  • Lazy modules (uiComponent children, require() on demand) stay lazy but are merged per lazy root with their dependencies, mixins and templates (lazy-checkmo-method); modules shared by several roots get one shared lazy chunk (lazy-payment-default-shared: place-order + mixins). A lazy group of a single module keeps loading as its own file.
  • Chunks contain named defines; text templates are inlined; they are minified with terser when the store has dev/js/minify_files on, like the rest of its JS.
  • Prepends a loader to requirejs-config.js that wraps require.load: requesting a bundled module loads its chunk once. require.config({bundles}) is not used because it makes require.toUrl() return the bundle URL, which breaks mage/requirejs/mixins.
  • Not bundled, loaded as single files like before: non-AMD files, modules listed in exclude, modules used only by ifconfig blocks whose flag differs between store views, and anything the static analysis did not find. A chunk that fails to load falls back to single files.

--pages overrides the analysed page types, e.g. {"product": ["default", "catalog_product_view"]}.

Known gaps

  • knockout templates whose path is computed at runtime
  • Page Builder content types are not analysed
  • JS added only by PHP code paths the static scan cannot follow loads as single files

License

MIT