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:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions