Skip to content

Document BuildJsScope model and split tracking scripts - #601

Draft
promptless-for-oss wants to merge 2 commits into
mautic:7.2from
Promptless:promptless/pr-16660-mtc-script-split-7.2
Draft

Document BuildJsScope model and split tracking scripts#601
promptless-for-oss wants to merge 2 commits into
mautic:7.2from
Promptless:promptless/pr-16660-mtc-script-split-7.2

Conversation

@promptless-for-oss

@promptless-for-oss promptless-for-oss commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Updates the MauticJS tracking script developer page (docs/mauticjs_api/tracking_script.rst) to document the JS build scope model introduced in mautic/mautic#16660 ("Split mtc.js into essential and tracking scripts", MTC-11100).

New developer-facing surface documented:

  • The Mautic\CoreBundle\Event\BuildJsScope enum (RUNTIME, ESSENTIAL, TRACKING).
  • BuildJsEvent::appendJsForScope($js, BuildJsScope $scope, $section = '') and acceptsScope(BuildJsScope $scope): bool, with plugin-style PHP subscriber examples.
  • A note that legacy appendJs() now delegates to appendJsForScope() with BuildJsScope::TRACKING, plus a backward-compatibility warning (with argument-position migration guidance) that appendJs()-only subscribers are excluded from /mautic-essential.js.
  • The generated endpoints (/mautic-essential.js, /mautic-tracking.js, /mtc.js) and their scope mappings.
  • Client-side runtime globals (MauticJS.runtimeReady, trackingEnabled, requestWithCredentials) and the mauticEssentialReady event, with a safe dual-path readiness guard example.

Source PR: mautic/mautic#16660


Review response (@patrykgruszka)

Thanks for the review. All three comments applied:

  1. ESSENTIAL script capabilities narrowed — applied. The ESSENTIAL scope bullet and the /mautic-essential.js endpoint-table row no longer say "Dynamic Content rendering and Form injection." They now describe it as preserving existing Dynamic Content fallback content without making a new request, and initializing Forms already embedded in that fallback content — matching MauticScriptSplitCest.php (essential-only mode keeps the DWC fallback without a DWC request) and PublicControllerFunctionalTest.php (standalone forms are independent of all tracking scripts).
  2. RUNTIME no longer offered as a plugin target — applied. Removed the "or BuildJsScope::RUNTIME" alternative from the migration warning; plugin pre-consent code is now directed to appendJsForScope() with BuildJsScope::ESSENTIAL (with optional acceptsScope() gating). RUNTIME remains described only as the core bootstrap the other scopes depend on, consistent with its single use in CoreBundle/EventListener/BuildJsSubscriber.php.
  3. mauticEssentialReady dependency on the shipped loader made explicit — applied. A dedicated paragraph after the readiness example now states the event is dispatched by the shipped consent-managed loader snippet, not by /mautic-essential.js itself, and tells custom-loader users to run their code from their own script tag's load/onload callback once MauticJS.runtimeReady === true, or dispatch an equivalent event themselves.

All edits applied verbatim to intent; the only deviations from the literal comment wording were style-driven (dropped the (DWC) abbreviation in favor of "Dynamic Content" per the Mautic Vale FeatureList rule, replaced an "and/or" with an explicit required-action/optional-gate split, and moved the custom-loader caveat into its own paragraph after the code block for findability). No new Vale errors introduced; the one pre-existing baseline finding on unchanged content remains.

This documentation update was prepared by Promptless.

Documents the JS build scope model from mautic/mautic#16660: the BuildJsScope
enum, BuildJsEvent::appendJsForScope()/acceptsScope(), the /mautic-essential.js
and /mautic-tracking.js endpoints, client-side runtime globals, and the
backward-compatibility impact on legacy appendJs() subscribers.
@adiati98 adiati98 added this to the 7.2 milestone Jul 24, 2026

@patrykgruszka patrykgruszka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scoped extension API and appendJs() migration guidance are useful additions. Before merging, the essential script’s capabilities should be narrowed to match the tested DWC fallback and form behavior, RUNTIME should not be presented as interchangeable with ESSENTIAL, and the readiness example should clearly state that mauticEssentialReady depends on the shipped consent-managed loader rather than the endpoint itself. With those reductions, this documentation will provide useful and accurate plugin guidance.

Comment thread docs/mauticjs_api/tracking_script.rst Outdated
Mautic generates its JavaScript under a scope model defined by ``Mautic\CoreBundle\Event\BuildJsScope``, an ``enum`` with three cases:

* ``RUNTIME`` - the anonymous bootstrap runtime that the other scopes depend on. It performs no tracking.
* ``ESSENTIAL`` - pre-consent features that need no identity, such as Dynamic Content rendering and Form injection.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The essential script's capabilities are narrower than "Dynamic Content rendering and Form injection." Essential-only mode preserves existing DWC fallback content without making a DWC request (tests/acceptance/MauticScriptSplitCest.php:190-221), and standalone forms are explicitly independent of all tracking scripts (app/bundles/FormBundle/Tests/Controller/PublicControllerFunctionalTest.php:18-38). Please describe this as DWC fallback handling and initialization of forms already embedded in fallback content. The same wording should be corrected in the endpoint table below.

Comment thread docs/mauticjs_api/tracking_script.rst Outdated

.. warning::

A subscriber that only calls the legacy ``appendJs()`` now contributes ``TRACKING`` scoped code and is therefore excluded from ``/mautic-essential.js``. If a Plugin's code must run in the essential, pre-consent context, the subscriber must call ``appendJsForScope()`` with ``BuildJsScope::ESSENTIAL`` - or ``BuildJsScope::RUNTIME`` - and/or gate on ``acceptsScope()``.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUNTIME should not be presented as interchangeable with ESSENTIAL for plugin features. The implementation uses RUNTIME only for the core bootstrap (CoreBundle/EventListener/BuildJsSubscriber.php:225-229) and classifies pre-consent DWC functionality as ESSENTIAL (DynamicContentBundle/EventListener/BuildJsSubscriber.php:118-132). Please remove the RUNTIME alternative, or limit it explicitly to bootstrap code required by other scopes.

* ``MauticJS.requestWithCredentials`` - ``false`` by default in the essential or runtime build and ``true`` once tracking loads.
* ``mauticEssentialReady`` - a convention event, not something the generated runtime emits on its own. The authoritative readiness flag is ``MauticJS.runtimeReady``; the consent-managed essential loader snippet - the copy-paste snippet that loads ``/mautic-essential.js`` - dispatches ``mauticEssentialReady`` once ``MauticJS.runtimeReady === true`` by calling ``MauticJS.dispatchEvent('mauticEssentialReady')``. That helper builds a native ``CustomEvent`` and dispatches it on ``document``, so browser-side code following the split-script loader pattern can rely on it as a readiness hook and listen with ``document.addEventListener('mauticEssentialReady', ...)``.

Because the essential script may have finished loading before your code runs - in which case the event has already fired and a late listener would never run - guard on ``MauticJS.runtimeReady`` first and run immediately when it's already ``true``, falling back to the listener only when the runtime isn't ready yet. The shipped tracking add-on snippet follows this same dual path. This handles both cases safely:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is safe only when the shipped consent-managed loader is used. /mautic-essential.js does not emit mauticEssentialReady; the Configuration-page loader snippet emits it from its load callback (PageBundle/Resources/views/FormTheme/Config/_config_trackingconfig_widget.html.twig:38-39). With a custom loader, this fallback listener may never run. Please make the condition explicit and tell custom loaders to use their script's load callback or dispatch an equivalent event.

…lt, clarify mauticEssentialReady loader dependency
@promptless-for-oss

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review, @patrykgruszka — all three points are addressed and pushed to PR #601:

  1. The ESSENTIAL scope description (both the scope list and the endpoint table) now reflects that it preserves existing Dynamic Content fallback content without making a new DWC request, and initializes Forms already embedded in that fallback content — rather than general DWC rendering / form injection.
  2. RUNTIME is no longer presented as interchangeable with ESSENTIAL for plugin features — it's described only as the core bootstrap; plugin pre-consent code targets ESSENTIAL (with optional acceptsScope() gating).
  3. The readiness example now makes explicit that mauticEssentialReady is dispatched by the shipped consent-managed loader snippet (not by /mautic-essential.js itself), with guidance for custom loaders to use their own load callback or dispatch an equivalent event.

All claims were re-verified against the PR #16660 source, and no new Vale issues were introduced. Ready for another look.

@patrykgruszka patrykgruszka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go from my perspective 👍

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.

3 participants