Appearance
Workspace setup and publishing
This repository is an Nx monorepo of publishable plugin libraries. Use this page when you need to work on an existing package, add a new plugin, or build a package for publication.
Prerequisites
- access to the Investec Azure Artifacts npm feed;
- a working Node/npm setup compatible with the repository lockfile;
- permission to publish to the shared package feed when releasing a package.
Clone and install
sh
git clone https://dev.azure.com/investec/client-channel-tech/_git/cxt-channel-investec-plugins
cd cxt-channel-investec-plugins
npm installUnderstand the workspace
Each plugin lives at the repository root:
text
menu-builder/
portfolio-builder/
feature-library/
...A typical package contains:
src/index.tsfor public exports;src/lib/for implementation and types;package.jsonfor the published package name and version;project.jsonfor the Nx build target;.azure-pipelines.ymlfor package publication.
Build a package
Build an individual plugin with Nx:
sh
npx nx build menu-builderThe build output is written to dist/<plugin-name>. For example:
text
dist/menu-builderTo inspect the targets available for a project:
sh
npx nx show project menu-builderRun the docs site
sh
npm run docs:devThis serves the VitePress documentation from docs/.
Add a new plugin
Generate a new Nx library with the library generator your team has standardised on, then align it with the conventions used by the existing publishable packages:
- place the library at the repository root;
- expose the public contract from
src/index.ts; - set the published package name in
package.jsonto the form@investec/plugins-my-plugin; - ensure the library has a
buildtarget that outputs todist/my-plugin; - add a package-specific
.azure-pipelines.yml.
Package contract guidance
Before publishing a plugin:
- export only the supported API from
src/index.ts; - keep package names stable after publication;
- document mutable state, environment requirements, and reset behaviour;
- prefer package-root imports in examples and consumers.
When updating a package, be explicit about its extension boundary:
- what a consumer can vary through inputs, callbacks, or subjects;
- what a consumer is expected to do after the plugin emits a result;
- what still requires a package change because it alters shared behaviour.
Shared plugin change standard
Shared plugins are platform contracts, not app-local helpers. When you expand an existing plugin or add a new one, preserve that boundary deliberately.
Treat the plugin as owning:
- the package-root public API exposed from
src/index.ts; - shared merge rules, endpoint selection, caches, and state objects;
- any
activeEnvironmentswitch or documented mutable module-level state; - any reset mechanism or lifecycle requirement documented for consumers.
Treat the consumer as owning:
- application-specific presentation and UI policy;
- when the plugin runs and what context is injected;
- how plugin output is persisted into app-owned state;
- app-only rules that should not automatically affect every consumer.
If the behaviour must remain consistent across multiple consumers, it belongs in the plugin. If it only exists for one application surface, prefer composing around the plugin instead of changing the shared package.
Agent-oriented implementation rules
Agents and contributors should follow these rules when changing a plugin:
- preserve the public contract unless the change intentionally introduces a new supported API;
- export supported additions from
src/index.tsand do not require consumers to import internal file paths; - avoid app-specific business logic in shared packages when the rule belongs in the consuming shell or feature;
- document new mutable state, environment requirements, reset behaviour, and extension points in the package page;
- prefer extending existing package hooks, inputs, or outputs before adding a parallel helper or duplicate state path;
- update examples and related docs whenever the supported usage changes.
Deciding whether to change the plugin
Change the shared package when you need to:
- add or modify a reusable public export;
- change shared aggregation, routing, eligibility, or merge behaviour;
- add a new upstream dependency or package-owned endpoint contract;
- introduce a reusable extension point needed by more than one consumer;
- fix a shared bug that consumers should not work around independently.
Do not change the shared package when the requirement is only:
- page-specific filtering or presentation;
- one app's analytics naming or UI wording;
- app-local navigation policy layered on top of the plugin result;
- one consumer's temporary workaround that should not become the shared default.
Publication model
Each plugin publishes independently through its own Azure pipeline. The usual pattern is:
npm installnpx nx build <plugin>- publish
./dist/<plugin>to the Investec npm feed
Check the package's own .azure-pipelines.yml before assuming branch filters or publication steps, because they differ slightly between packages.
Verification
Before opening a release PR:
- the target package builds successfully;
src/index.tsmatches the intended public contract;- the package version is correct for the release;
- the package documentation reflects the shipped API.
- any changed extension boundary is described clearly enough that another consumer or agent can follow it without reading implementation files first.