(written by Grok and reviewed by Max Milbers)

Audience: Extension and template developers who load JavaScript in VirtueMart 5
Status: VirtueMart 5 (4.9 test line)
Related: VM Init Registry · The VM JavaScript Handler (VM5)

1. Why this exists

VirtueMart 4 queued scripts with vmJsApi::addJScript and hoped document.ready ran in the right order. Modules download asynchronously. AJAX replaces a cart or an admin tab. A library that must exist as window.Sortable is not ready just because you put a <script> in the footer.

VirtueMart’s answer: one loader (vmJsApi), ES modules in the frontend asset tree, and a dual load for libraries that both classic code and modules must wait for. VMInit is when to run. This article is how the file gets to the browser.

2. How to load a script

All shop and admin JS lives under:

media/com_virtuemart/js/

Filename without .js. Directory without a trailing slash. Minified files are produced by npm run minify, not by hand. Hidden config noMinified=1 (or debug) loads the readable source.

Call What it does
vmJsApi::addvScriptModule($name, $path = '') ES module (type="module") through the Web Asset Manager. Subfolder goes in the name: sortable/sortable-global.
vmJsApi::addJScript($name, $script = false) Classic script or inline string. First registration wins if the same name is used twice.
vmJsApi::css($name, $path) CSS + min CSS, same path rules.
vmJsApi::addvScriptModule('be/myfeature.module');
vmJsApi::addJScript('my-inline', 'window.Virtuemart = window.Virtuemart || {};');
vmJsApi::css('choices.min');

3. Dual load (header + import)

For Sortable, AutoComplete, Mustache:

  1. addvScriptModule in the header so the browser starts downloading early.
  2. A tiny *-global.js imports the library, assigns window.Sortable (etc.), and registers a VMInit name such as Sortable Ready.
  3. Your module imports the registry (or waits with VMInit.waitForGlobal('Sortable')) so execution continues only when the library exists.

The same module URL is one instance. You do not get two Sortables. import is not fetch; the module loader waits for the graph.

vmJsApi::addvScriptModule('sortable/sortable.core.esm.min');
vmJsApi::addvScriptModule('sortable/sortable-global');

4. Developer win

You do not invent a second loader. Template overrides of the same filename still win. AJAX pages re-run handlers that opted into reInit — see the Init Registry article. Priorities: core at or below 500, extensions 550+.

5. Quick start

  1. Put myfeature.module.js next to core modules (or in your plugin assets and pass the path).
  2. Register it with addvScriptModule.
  3. import vmInitRegistry from 'virtuemart/vm-init-registry.module.js' and VMInit.add('My Feature', init, 550, { reInit: true }).
  4. Do not call VMInit.run() yourself.

6. Do’s and don’ts

  • Do not put .js in the first argument.
  • Do not add a trailing slash on the directory.
  • Do not load the same library only as classic addJScript if a module must wait — use the globalizer.
  • Do not edit *.min.js by hand.
  • Do not assume a singleton: the same class can appear three times on product edit.

7. Paths

  • administrator/components/com_virtuemart/helpers/vmjsapi.php
  • media/com_virtuemart/js/vm-init-registry.module.js
  • Globalizers: sortable/sortable-global.js, autocomplete/autoComplete-global.js, mustache/mustache-global.js