Skip to content

JavaScript Modules v2 - Support for more backend patterns #554

Description

@jahia-ci

JavaScript modules can now implement the server-side extension points that previously required a Java OSGi module — choicelist initializers, server-side validators, actions (both .do-bound and client-callable), render filters, and content patches — plus a third-party SDK so other bundles can consume them. Delivered across two reviewable PRs: #687 (extension points, actions, SDK) and #697 (content patches). Design decisions are recorded as ADRs 0001–0008 in docs/adr/.

What shipped

Capability Sub-issue Notes
Extensible registrar mechanism (+ render filters) #691 Covers the original "Filters" scope
Choicelist initializers #692
Server-side node validators #693 Beyond original scope
Legacy node actions (org.jahia.bin.Action parity) #694
Actions (client-callable server functions) #588
Content patches #695 PR #697 — covers the original "Migration script" nice-to-have
Third-party SDK #696
Async callbacks for all extension points #688
Engine package hygiene #689

Follow-up (stays open after #687 merges): #690 — actions endpoint v2 (dedicated servlet transport, non-breaking).

Explicitly out of scope of this epic's delivery: Rules/Listeners in JS (the registrar mechanism makes this a one-subclass addition whenever prioritized — see the POC's moduleGlobalObject mapper in npm-modules-engine#125) and UI extensions.

Developer experience

Every capability is a single typed function from @jahia/javascript-modules-library, called once at module init — no XML, no OSGi component boilerplate, no separate Java module. Each accepts idiomatic JS/TS signatures with the underlying Java objects still reachable as an escape hatch, and each supports async callbacks (microtask-based — the server runtime has no timers or async I/O).

Choicelist initializer — populates a Content Editor dropdown, referenced from CND as choicelist[key]:

registerChoiceListInitializer({ key: "myModuleColors" }, ({ locale }) => [
  { label: locale === "fr" ? "Rouge" : "Red", value: "red" },
  { label: locale === "fr" ? "Vert" : "Green", value: "green" },
]);

Server-side validator — rejects invalid saves with a field-level or node-level error in Content Editor and GraphQL:

registerNodeValidator({ nodeType: "mymodule:article" }, (node) => {
  const email = node.getPropertyAsString("email");
  if (email && !email.includes("@")) {
    return { message: "Please provide a valid email address", propertyName: "email" };
  }
});

Legacy node action — an HTTP endpoint bound to a content node (<nodeUrl>.<name>.do), for Java-module parity and existing .do integrations:

registerNodeLegacyAction({ name: "myModuleGreet", requiredMethods: ["GET"] }, ({ parameters, resource }) => ({
  json: { greeting: `Hello ${parameters.who?.[0] ?? "world"}`, path: resource.getNode().getPath() },
}));

Actions — server functions callable from a client island as a plain typed await, with optional input validation via any Standard Schema-compatible library:

// rates.action.ts — compiled once for the server, once into a client fetch stub
export const getExchangeRate = action(z.object({ currency: z.string() }), ({ currency }) => lookupRate(currency));
// Rate.client.tsx
import { getExchangeRate } from "../actions/rates.action";
<button onClick={async () => setRate(await getExchangeRate("EUR"))}>Refresh</button>

Render filter — participates in the render chain like a Java AbstractFilter:

registerRenderFilter(
  { key: "myModuleUppercaseTitles", priority: 50, applyOnNodeTypes: "mymodule:title" },
  { execute: (previousOutput) => previousOutput.toUpperCase() },
);

Content patch — versioned, run-once content transformation, tracked by core's patch status store so it runs exactly once per environment.

Third-party SDK — for other OSGi bundles that need to consume JS-declared extensions without a polyglot dependency (org.jahia.modules.javascript.modules.engine.sdk, the only package the engine bundle exports).

Full guides: docs/2-guides/4-legacy-node-actions, docs/2-guides/5-choicelist-initializers, docs/2-guides/6-content-patches, docs/2-guides/7-actions, docs/3-reference/3-node-validators.


Original epic description (imported from Jira)

Minimum scope :

  • Choicelist initializers
  • Actions

Optionnal:

  • Filters (cached only ?)
  • UI extensions

Nice to have:

  • Rules / Listeners in JS
  • Migration script

Imported from Jira, on Thu Jan 30, 2025
Issue: BACKLOG-21005 in project: DX BACKLOG
Priority: Unspecified Type: Epic
Reporter: @romain-pm (Romain Gauthier)
Assignee: @Fgerthoffert (team-goat)
Created: Mon May 15, 2023, last updated: Fri Jan 17, 2025
Status: Open

Links:

Issues in Epic:

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions