Introduction
A frontend feature is an extension that lives outside the core, in its own directory, and adds to Clarive without rebuilding the core:
- React pages (their own routes), menu entries (side and top menus) and their own side menu sections.
- Components painted inside core screens, such as the body of a topic revision.
- Permissions (actions that show up in the role editor).
- Backend endpoints in Perl, protected by those permissions.
- Their own translations and stylesheets.
The feature is built on its own with Vite. The libraries that must be the core's own instances (React, antd...) are left as unresolved imports. At runtime the browser resolves them against the core through an import map. The feature and the core therefore share a single React instance, and adding or rebuilding a feature never requires rebuilding the core.
feature (npm run build) core (already built) ─────────────────────── ──────────────────── import React from 'react' ── import map ──► /static/dist/sdk/react.js ──► core React import { Button } from 'antd' ────────────► /static/dist/sdk/antd.js ──► core antd import { useSdk } from '@clarive/sdk' ────► /static/dist/sdk/clarive-sdk.js ──► core SDK
The examples in these pages come from a sample feature called hello-react.
Directory structure¶
CLARIVE_BASE/features/<id>/ <- <id> = directory name, e.g. hello-react ├── feature.yml <- manifest: declares the frontend ├── package.json <- build devDependencies only (vite, postcss-prefix-selector) ├── vite.config.mjs <- standalone build ├── src/ <- frontend source code │ ├── index.jsx <- entry: export default register(sdk) │ ├── HelloPage.jsx <- pages (loaded on demand) │ ├── actions.js <- action (permission) names │ └── hello.css <- plain CSS (prefixed at build time) ├── public/ <- WHAT THE CORE SERVES at /feature/<id>/... │ └── dist/ <- build output (index.js, chunks/, style.css) ├── i18n/ │ └── es.po <- the feature's own translations └── lib/BaselinerX/ <- Perl backend (it is in @INC) ├── HelloReact.pm <- action (permission) registration └── Controller/HelloReact.pm <- /feature/<id>/api/... endpoints
Rules:
- The feature id is the directory name. It is used in urls (
/feature/<id>/...), in the CSS wrapper (data-feature="<id>") and in the namespace of the Perl controller. - A directory whose name starts with
#is ignored. Renaming a feature directory to#<id>disables it without deleting it. - Features are looked up in
CLARIVE_BASE/featuresandCLARIVE_HOME/features. - Only
public/is served over HTTP.src/,lib/,node_modules/... are never reachable.
feature.yml¶
name: Hello React version: 0.1.0 author: Clarive Team frontend: entry: dist/index.js # relative to public/ -> /feature/hello-react/dist/index.js css: dist/style.css # optional; a file or a list of files, also relative to public/ sdk: "^1" # SDK major the feature is built for (a minor too if it needs one: "^1.2")
| Key | Required | What it does |
|---|---|---|
name |
no | Human name of the feature. If present, it must be a text. |
frontend.entry |
yes | ES module the core imports at startup. Must be a .js (or .mjs) file inside public/. |
frontend.css |
no | Stylesheets the core adds to the <head>: a file or a list of files. A declared file that does not exist or escapes public/ is skipped with a warning in the log. |
frontend.sdk |
yes | SDK compatibility. "^1" (or "1", "1.x") = any 1.x. "^1.2" (or "1.2") = same major (1) and minor >= 2. If the core does not match, the feature is skipped with a warning in the browser console. |
The server validates the frontend part of feature.yml. A feature that breaks the schema
(invalid YAML, a missing entry or sdk, a wrong type, an entry that has not been built...) is
left out with an error in the server log, and the rest of the features load normally:
Feature hello-react: feature.yml frontend.sdk is required, e.g. sdk: "^1", frontend not loaded Feature hello-react: feature.yml frontend.entry dist/index.js not found in public/ (not built?), frontend not loaded
An unknown key under frontend (usually a typo, such as entyr) is only a warning:
feature.yml frontend.entyr is not a known key, ignored. A feature without a frontend key is
backend-only and is not announced to the browser.
The server reads feature.yml on every page load (it is not cached), so changing it or
dropping a new frontend-only feature in shows up after reloading the browser.
What the server sends to the browser (in appData.features) for each feature is:
{ id: 'hello-react', name: 'Hello React', sdk: '^1', entry: '/feature/hello-react/dist/index.js?v=1790668000', // ?v=<mtime>: busts the cache on rebuild css: ['/feature/hello-react/dist/style.css?v=1790668000'], i18n: { 'Save': 'Grabar', ... } // dictionary of the user's language }
Development workflow¶
| When you change... | Do this |
|---|---|
Code in src/ or CSS |
npm run build in the feature and reload the browser. The core is not touched. |
feature.yml |
Reload the browser (it is read on every page load). |
i18n/<lang>.po |
Reload the browser. |
Perl in lib/ (actions, controllers, CIs) |
Restart the web server (cla web-stop and cla web-start). |
| A new feature | Create the directory and build it. Restart the web server if it has Perl code; if it only has a frontend, reloading is enough. |
Checklist for a new feature:
- Create
CLARIVE_BASE/features/<id>withfeature.yml,package.jsonandvite.config.mjs(setFEATURE_IDto<id>). src/index.jsxwithexport default function register(sdk); pages withReact.lazy.src/actions.jsandlib/BaselinerX/<Name>.pmwith the actions; permissions on routes and menus.- If there is a backend:
lib/BaselinerX/Controller/<Name>.pmwithnamespace => 'feature/<id>/api'andDoes('ACL') : ACL(...)on every action. - Texts with
sdk._()andi18n/<lang>.pofiles. - If it paints inside core screens:
sdk.addComponentandviewer_componentsin the repository CI. - Plain CSS in
src/, declared infrontend.css; colors withvar(--cla-...). feature.ymlwithsdk: "^1"(or"^1.<minor>"if it uses something added in that minor).npm install && npm run build, restart the server if there is Perl, grant the actions in a role and check__CLARIVE_SDK__.featuresshowsloadedin the browser console.