perf: combined performance enhancements (FORMS-24968/24969/24970/24971/24975)#207
Open
dmaurya929 wants to merge 15 commits into
Open
perf: combined performance enhancements (FORMS-24968/24969/24970/24971/24975)#207dmaurya929 wants to merge 15 commits into
dmaurya929 wants to merge 15 commits into
Conversation
…module hops Port the HDFC-validated bundle optimization to the boilerplate so all downstream EDS Forms projects inherit the performance gains: - 12 JS fetches → 1 bundle + 2 shims (−11 requests) - 5 serial import hops → 2 hops (parallel preload) - Worker startup: −31% (afb-runtime.min.js instead of full source) New files: rollup/form.rollup.config.js, blocks/form/form.source.js, blocks/form/rules/index.source.js, scripts/swap-shims.js Modified: form.js and rules/index.js → 1-line shims; head.html adds 5 modulepreloads; package.json adds build/build:dev/build:form-bundle scripts; pre-commit shim guard added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FORMS-24970 — Critical CSS split convention: - loadFormCustomStyles now probes for <name>-critical.css via HEAD request. If the companion file exists, critical CSS loads immediately and the full stylesheet is deferred to window.load (post-LCP) via deferLoadCSS. If no companion file exists, full CSS loads immediately (old behaviour, no FOUC). - Any form opting into the split ships <name>-critical.css alongside properties.style. FORMS-24971 — Font CLS prevention: - styles/fonts.css: font-display swap → optional for all 4 Roboto @font-face rules. Metric-matched fallback faces (roboto-fallback, roboto-condensed-fallback with size-adjust) already in styles.css make the swap visually imperceptible. - head.html: preload for styles.css (eliminates parse-to-fetch gap) and roboto-regular.woff2 (ensures primary weight is available before LCP with optional). Tests: updated custom-styles.test.js to mock HEAD requests as Promise.resolve({ok:false}) and flush the async probe chain before assertions. All 249 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es.lazyRendering
When enabled, defers initial render of inactive wizard panels, hidden panels,
and hidden non-panel field componentDecorators. Wrappers are still appended
(so fieldChanged events resolve them) but children/decorators run only when
the field becomes active or visible. Reconciles against the live model in
loadRuleEngine to handle rule-driven visibility post-restore.
Opt-in via formDef.properties.lazyRendering = true. Existing forms and the
public boilerplate's component tests are unchanged when the flag is absent.
- form.js: generateFormRendition accepts options={lazyPanels,lazyComponents};
createForm reads formDef.properties.lazyRendering and conditionally creates
the Maps + attaches them to form._lazyPanels/_lazyComponents.
- rules/index.js:
- getLivePanelState queries live model so deferred panels render with
post-rules state, not stale init-time fieldData.
- handleActiveChild triggers lazy panel render on wizard tab activation.
- case 'visible': dedup no-op guard; lazy component decorator-then-show;
lazy panel pre-render wait or render-now/queue.
- loadRuleEngine pre-renders lazy panels visible=true in live model
(via _preRenderPromises), flushes _pendingLazyRenders, runs thunks for
live-visible components, flushes _pendingLazyComponents.
Ported from forms-engine d8b6ff4f (HDFC fork) — adapted for the public
boilerplate (no ruleEngine.js shim; worker already has property-sync;
subscribe already fires immediately on registered formModels).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
|
Shims (form.js, rules/index.js) only re-export the public surface (DELAY_MS, createForm, subscribe…) from form-bundle.min.js, which is a browser bundle and unavailable in the Node.js Mocha environment. Tests that import loadRuleEngine, generateFormRendition, fieldChanged, etc. must import from the source files directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds opt-in build tooling that splits a form's custom functions into an eager bundle (loaded at init) and a lazy bundle (deferred to the 3s mark) to remove non-critical JS from the critical path. - scripts/build-custom-functions.js — analyses form JSON / live page for load-time vs interaction functions, backs up the source, writes eager (.min.js) and lazy (-lazy.min.js) bundles, updates functions-registry.json, and replaces the entry file with a transparent shim - rollup/custom-functions.rollup.config.js — per-form rollup pipeline; reads split manifest from functions-registry.json, inlines eager impls, stubs lazy fns, and exports loadLazyBundle for the 3s warmer - rollup/functions.rollup.config.js — builds the OOTB functions.min.js bundles - functionRegistration.js — modulepreloads eager bundle + speculative prefetch for lazy bundle; stores loadLazyBundle on window.hlx when split is active - scripts.js — calls window.hlx.loadLazyBundle() at 3s mark - package.json — adds build:functions and build:custom-functions scripts - test/unit/functionRegistration.test.js — unit tests for preload and split detection behaviour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port model bundle upgrades from eds-forms-perf. Key change in af-core: _pendingViewEvents queue — custom events fired before a lazy component's subscriber registers are queued and replayed on subscribe(), fixing cases where rule-triggered events were silently dropped on lazy panels. af-formatters adds a calendar date validation guard (rejects e.g. Feb 30). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two missed-event cases when a modal panel's visible=false at load time: 1. modal.js subscribe callback: fires after the triggering visible=true has already been processed, so fieldModel.subscribe only catches future events. Added fieldModel.visible check at subscribe time to catch the missed open. 2. index.source.js fieldChanged: when a rule sets visible=true on a panel wrapping a <dialog>, call dialog.showModal() and add modal-open class — previously only the close path was handled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Custom components subscribe callbacks run once at decoration time and query nested elements via querySelector. If child panels were deferred via _lazyPanels, those elements wouldn't be in the DOM yet, breaking the callbacks. For custom components that have nested panel children, pass lazyPanels:null to the recursive generateFormRendition call so their children render immediately instead of being deferred. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…convention
Rollup outputs {name}-bundle-eager.min.js and {name}-bundle-lazy.min.js but
preloadFunctionScripts was computing {name}.min.js (eager modulepreload) and
{name}-lazy.min.js (lazy prefetch) — both wrong, causing 404s.
Also unifies --form flag in build-custom-functions.js (replaces --page /
--form-json with a single flag that auto-detects EDS page URL, .model.json
URL, or local file path) and updates rollup config output filenames to
match. Updates unit test assertions to the new naming convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Picks up: custom component child-panel eager-render fix (form.source.js), lazy modal showModal via fieldChanged (index.source.js), and the getCustomComponents import added to form.source.js. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor parseAnnotations regexes to require a JSDoc /** ... */ block
delimiter and make the export keyword optional. This supports block-export
syntax (export { funcName } at EOF) in addition to inline exports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…bundle Both modules are loaded independently via modulepreload so they arrive in the browser cache before form-bundle executes. Inlining them duplicates parse cost and inflates the bundle; externalizing removes both from the form-bundle output and lets the browser share a single parsed copy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ineWorker When Worker is available, fire the worker init and custom-function registration concurrently via Promise.all — both are independent and previously ran sequentially, stalling form render while functions loaded. Keep sequential fallback for environments without Worker support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oads - Move aem.js + scripts.js before modulepreload hints so the browser dispatches the critical script fetches first - Remove afb-formatters.min.js preload (discovered transitively; no benefit after bundle externalization) - Add util.js + functionRegistration.js preloads — both are now external to form-bundle and need explicit wave-1 hints to avoid late fetches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…functionRegistration.js Regenerated bundles now import util.js and functionRegistration.js as external modules instead of inlining them. Net reduction ~470 lines in form-bundle.js; form-bundle.min.js updated accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five performance enhancements bundled into a single PR:
FORMS-24968 — Form-bundle rollup pipeline
form.js→rules/index.js→ workers + runtime) into a single pre-bundled file (form-bundle.min.js) via Rollup.form.jsandrules/index.jsbecome thin re-export shims; original source preserved inform.source.jsandrules/index.source.js.scripts/swap-shims.js+.husky/pre-commit.mjskeep the bundle in sync on commit.FORMS-24969 — Eager/lazy custom-functions split
build:custom-functionsCLI analyses which custom functions fire at load-time vs on user interaction (from live page fetch or local form JSON).myfn.min.js) with real impls for load-time functions and async/sync stubs for the rest, plus a lazy bundle (myfn-lazy.min.js) with full implementations deferred to the 3s mark.myfn.js) becomes a transparent shim — form JSON unchanged.functionRegistration.jsmodulepreloads the eager bundle and speculatively prefetches the lazy bundle; storesloadLazyBundleonwindow.hlx.scripts.jstriggerswindow.hlx.loadLazyBundle()at 3s — before typical first user interaction — so stubs are already backed by real impls.rollup/functions.rollup.config.jsbuilds the OOTBfunctions.min.jsbundles referenced by eager bundles.FORMS-24970 — Critical CSS split convention
loadFormCustomStylesprobes for a<name>-critical.csscompanion file via HEAD request.window.load(post-LCP).FORMS-24971 — Font CLS prevention
styles/fonts.css:font-display: optionalfor all 4 Roboto@font-facerules — eliminates layout shift from late font swap.head.html: preload forstyles.cssandroboto-regular.woff2to close the parse-to-fetch gap.FORMS-24975 — Lazy panel rendering
formDef.properties.lazyRendering = true.fieldChangedevents resolve them) but children/decorators run only when the field becomes active or visible.loadRuleEngineto handle rule-driven visibility post-restore.Test plan
npm run lint— no new errorsnpm run test:unit— all 257 tests pass (includes newfunctionRegistration.test.js)npm run build— producesform-bundle.min.jsandfunctions.min.jsnpm run build:custom-functions -- --functions blocks/form/functions.js— single bundle modenpm run build:custom-functions -- --functions <path> --form-json <path>— eager/lazy split, updatesfunctions-registry.jsonlazyRenderingrenders identically to beforelazyRendering: truedefers inactive wizard panels<name>-critical.csscompanion exists🤖 Generated with Claude Code