@aligent/nx-appbuilder
v0.5.0
Published
The `@aligent/nx-appbuilder` package provides Nx generators for Adobe App Builder development. It scaffolds a workspace shell and individual App Builder apps inside an Nx monorepo, wired up against Aligent's `@adobe/aio-sdk` / `@adobe/aio-commerce-lib-*`
Keywords
Readme
Nx App Builder
The @aligent/nx-appbuilder package provides Nx generators for Adobe App Builder development. It scaffolds a workspace shell and individual App Builder apps inside an Nx monorepo, wired up against Aligent's @adobe/aio-sdk / @adobe/aio-commerce-lib-* conventions.
Generators
Preset Generator
The preset generator initialises a new App Builder monorepo. It is invoked indirectly via either @aligent/create-workspace (the Aligent-flavoured wrapper) or vanilla create-nx-workspace, and produces the workspace shell only — apps are added afterwards with the app generator.
Usage
# Recommended: the Aligent wrapper. Requires corepack to be enabled.
npx @aligent/create-workspace@latest --preset=@aligent/nx-appbuilder
# Or directly via create-nx-workspace (will prompt for nodeVersion if omitted):
npx create-nx-workspace@latest --preset=@aligent/nx-appbuilderThe
@aligent/create-workspacewrapper callscreate-nx-workspaceunder the hood, prompts for the preset's options (workspace name, target Node.js version), and removes the npm-bootstrap artefacts (package-lock.json,node_modules,.nx) afterwards so the workspace is ready foryarn install.
Options
| Option | Type | Required | Default | Description |
|--------|--------|----------|---------|---------------------------------------------------------------------------------------------------------------|
| name | string | Yes | - | Workspace name (kebab-case). Used as the @aligent/<name> npm package name and the workspace directory name. |
Post-generation setup
After running the preset generator, configure the npm registry token before the first npm install:
NPM_TOKEN— the generated.npmrcpoints@aligentathttps://npm.corp.aligent.consulting/and reads${NPM_TOKEN}for auth. Generate a token by signing in to the registry with your GitHub account, then export it in your shell:export NPM_TOKEN=<your-token>
What it creates
The preset generator scaffolds:
- Root configuration files:
package.json- Workspace manifest withlint/check-types/test/buildscripts (affected + run-many variants)nx.json- Nx workspace configuration with cachedlint/check-types/test/buildtarget defaults.npmrc- Aligent private registry configuration.nvmrc- Pinned Node.js version (v22).gitignore- Standard ignores for Node, Nx, Parcel and Adobe AppBuilderREADME.md- Workspace-level usage guide
The preset does not scaffold any apps — see the app generator below.
App Generator
The app generator creates a new Adobe App Builder app inside an existing workspace. The generator drives a series of prompts (or accepts CLI flags) that toggle which feature subtrees are rendered into the new app's directory.
Usage
npx nx g @aligent/nx-appbuilder:app <app-name>Pass any of the feature flags below to skip the corresponding prompt, e.g.:
npx nx g @aligent/nx-appbuilder:app my-app \
--hasAdminUI=true \
--sidebarCategory=sales \
--hasCommerceWebhooks=trueOptions
| Option | Type | Required | Default | Description |
|-------------------------|---------|----------|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | string | Yes | - | App name (kebab-case). Used as the directory name and the suffix of the @aligent/<name> package name. |
| description | string | No | '' | Short description of the app, used in package.json and the generated README. |
| displayName | string | No | Title-cased name | Human-readable display name used in app.commerce.config.ts and the generated README. |
| hasAdminUI | boolean | No | false | Generates the Commerce backend UI extension (commerce/backend-ui/1) — custom admin pages and sidebar menu items. |
| sidebarCategory | enum | No | none | Sidebar category for the admin menu item. One of catalog, sales, customers, content, none. Only used when hasAdminUI=true; prompted if omitted. |
| hasBusinessConfig | boolean | No | false | Generates the Commerce configuration extension (commerce/configuration/1) — merchant-facing fields under Stores → Configuration. |
| hasCommerceWebhooks | boolean | No | false | Adds a webhooks section to app.commerce.config.ts for binding Commerce extensibility hooks to runtime actions. |
| hasEvents | boolean | No | false | Generates a sample event handler under src/actions/ and an eventing block in app.commerce.config.ts for Commerce + external event subscriptions. |
| hasRestActions | boolean | No | false | Generates a sample REST action under src/actions/ and registers it in the runtime manifest. |
| hasScheduledActions | boolean | No | false | Generates a sample cron-triggered action under src/actions/ along with the triggers and rules entries that fire it. |
| hasCustomInstallSteps | boolean | No | false | Generates an installation block in app.commerce.config.ts and a sample step under scripts/install/. |
Setting
hasAdminUI,hasBusinessConfig, orhasCommerceWebhooksautomatically pulls in thecommerce-extensibilitysubtree and the@adobe/aio-commerce-lib-*dependencies.
What it creates
The app generator always renders a base subtree into <app-name>/:
App-level files:
app.config.yaml- App Builder manifest with the runtime package, action declarations, triggers and rules (composed from the selected flags)package.json- Pinned dependencies andlint/lint:fix/check-types/testscripts (per-target variants for actions, web and tests)package.jsonnx.targetsblock - declares the customcheck-typesanddeploytargets;lintandtestare inferred by the@nx/eslint/pluginand@nx/vitestplugins fromeslint.config.mjsandvitest.config.tstsconfig.json/tsconfig.base.json- TypeScript project configeslint.config.mjs/prettier.config.mjs- Lint and formatter config (@aligent/ts-code-standards). The eslint preset isreactwhenhasAdminUI=trueandbaseotherwise, so action-only apps don't load the React/JSX/a11y rules.vitest.config.ts- Vitest config.editorconfig,.nvmrc,.gitignore,README.md
Action and test scaffolding:
src/actions/tsconfig.json- TypeScript config for the App Builder actionssrc/actions/webpack-config.cjs- Webpack config used by theaioCLI to compile actions viaesbuild-loader(targets the workspace's Node major)tests/tsconfig.json- TypeScript config for the test suitehooks/check-action-types.sh- Wired as the application-levelpre-app-buildhook inapp.config.yamlso the action TypeScript is type-checked before every deploy. Custom (non-Adobe) actions also live underapplication.runtimeManifestinapp.config.yaml; the scaffoldedext.config.yamlfiles are left for Adobe-generated content only.global-types/@adobe/<pkg>/index.d.ts- Local module augmentations for the targeted Adobe libs. Currently augments@adobe/aio-lib-core-loggingwith a strictLogLevelstring-literal union; the layout mirrorsnode_modules/so additional augmentations can be dropped in alongside.
Root updates:
- Adds
<app-name>to the rootpackage.jsonworkspaces array
- Adds
Additional subtrees are layered on top depending on the selected flags:
| Flag | Subtree rendered | Notable additions |
|-----------------------------------------------------------------|-------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| any of hasAdminUI, hasBusinessConfig, hasCommerceWebhooks | commerce-extensibility/ | app.commerce.config.ts, install.yaml, commerce/extensibility/1 extension config; pulls in @adobe/aio-commerce-lib-app / -config. |
| hasAdminUI | commerce-backend-ui/ | React 19 + Spectrum admin UI under src/commerce-backend-ui-1/, web-src/ entry point (with module: Preserve / moduleResolution: Bundler so the webpack/esbuild pipeline sees ESM-style imports), action utils, registration, pre-app-build web type-check hook. |
| hasBusinessConfig | commerce-config/ | commerce/configuration/1 extension config wired into app.commerce.config.ts; adds a businessConfig.schema block to app.commerce.config.ts. |
| hasCommerceWebhooks | none (modifies commerce-extensibility/) | Adds a webhooks section to app.commerce.config.ts for binding Commerce extensibility hooks to runtime actions or external URLs. No new files; relies on the commerce-extensibility/ subtree being rendered. |
| hasRestActions | rest-actions/ | src/actions/rest-sample.ts registered as a web action in app.config.yaml. |
| hasEvents | events/ | src/actions/handle-sample-event.ts, sample Commerce + external event subscriptions; pulls @adobe/aio-lib-events into the app's dependencies. |
| hasScheduledActions | scheduled/ | src/actions/cron-sample.ts plus the triggers/rules entries that fire it on a cron schedule. |
| hasCustomInstallSteps | install-steps/ | scripts/install/sample-step.js and an installation.customInstallationSteps entry in app.commerce.config.ts. |
Example
# Standalone backend integration with REST actions and scheduled jobs
npx nx g @aligent/nx-appbuilder:app order-sync \
--hasRestActions=true \
--hasScheduledActions=true
# Commerce admin UI with business configuration and webhook bindings
npx nx g @aligent/nx-appbuilder:app loyalty-rules \
--hasAdminUI=true \
--sidebarCategory=customers \
--hasBusinessConfig=true \
--hasCommerceWebhooks=trueApp Builder guardrails
The generated eslint.config.mjs ships four rules on top of @aligent/ts-code-standards. Each was added to head off a pattern that has repeatedly bitten App Builder apps:
| Rule | Scope | Why |
|------------------------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| no-restricted-imports on @adobe/aio-sdk | src/**/*.{ts,tsx} | The umbrella @adobe/aio-sdk re-exports every sub-SDK (Files, State, Events, Target, Analytics, ...). Import the targeted lib instead — @adobe/aio-lib-core-logging for Logger, @adobe/aio-lib-core-config for Config, @adobe/aio-lib-events for Events, etc. |
| @typescript-eslint/no-non-null-assertion | src/**/*.{ts,tsx} | x! silently swallows runtime null/undefined, which surfaces as opaque action 500s. Aligns with the workspace's CLAUDE.md rule. |
| @typescript-eslint/prefer-nullish-coalescing | src/**/*.{ts,tsx} | params.MAX_RETRIES \|\| 3 treats a valid 0 as missing and forces the fallback; ?? only triggers on null/undefined, preserving legitimate falsy values (0, false, ''). |
| no-restricted-syntax (process.env) | src/**/actions/** | App Builder routes runtime configuration through OpenWhisk params; process.env is not reliably propagated between activations. Read from params (declared under inputs: in app.config.yaml) instead. |
To loosen any rule for a specific case, override it in the same eslint.config.mjs (later config blocks win); to disable globally, remove the entry from the block.
Because the umbrella import is banned, the generator no longer ships @adobe/aio-sdk as a dep. Instead it pins @adobe/aio-lib-core-logging unconditionally (used by every sample action) and @adobe/aio-lib-events only when --hasEvents=true. Add additional targeted libs (@adobe/aio-lib-state, @adobe/aio-lib-files, @adobe/aio-lib-core-config, ...) to your generated app's package.json as the action code that uses them is added.
Project Structure
After running the preset generator and adding one or more apps, your workspace will look like:
my-workspace/
├── my-app/
│ ├── src/
│ │ ├── actions/
│ │ │ ├── tsconfig.json
│ │ │ └── ... # rest / event / cron action samples (per flag)
│ │ ├── commerce-backend-ui-1/ # only if hasAdminUI
│ │ ├── commerce-configuration-1/ # only if hasBusinessConfig
│ │ └── commerce-extensibility-1/ # only if any commerce-lib flag
│ ├── tests/
│ ├── hooks/
│ ├── global-types/
│ ├── scripts/install/ # only if hasCustomInstallSteps
│ ├── app.config.yaml
│ ├── app.commerce.config.ts # only if any commerce-lib flag
│ ├── install.yaml # only if any commerce-lib flag
│ ├── package.json # includes nx.targets for check-types and deploy
│ └── ...
├── nx.json
├── package.json
├── .npmrc
└── .nvmrcDevelopment
Building
npx nx build nx-appbuilderThe @nx/js:tsc build executor compiles src/**/*.ts to dist/src/ and copies the templates under src/generators/<gen>/files/ verbatim. Template files use the .template suffix (e.g. app.commerce.config.ts.template) so they aren't picked up by lint or tsc; @nx/devkit's generateFiles strips the suffix at generation time.
Dependency version pins
Each generator reads the npm version specs it injects into generated files from a real package.json at src/generators/<gen>/template-package/package.json:
preset/template-package/package.jsonpins the workspace-level devDeps (nx,@nx/*plugins,eslint,prettier,typescript,vitest,@aligent/ts-code-standards).app/template-package/package.jsonpins the per-app deps (@adobe/aio-*, React + Spectrum, lint/test tooling, etc.).
Both files are tracked by Dependabot (see .github/dependabot.yml) so version bumps land via PR. To add a new pin, add it to the relevant template-package and reference it from pickVersions() in preset.ts / compose-package-json.ts. The shared helper at src/generators/helpers/template-package.ts enforces that every requested key exists in the template file, so missing pins fail loudly at runtime.
Running tests / lint / type-check
npx nx test nx-appbuilder
npx nx lint nx-appbuilder
npx nx typecheck nx-appbuilderLocal testing
To try the generator locally against a sibling workspace without publishing:
# In the consumer workspace:
npm install --save-dev /absolute/path/to/packages/nx-appbuilder
npx nx g @aligent/nx-appbuilder:app my-appOr use the local Verdaccio registry — see the root CLAUDE.md for the nx start-local-registry workflow.
License
MIT
