Skip to main content

Generator architecture

yarn generate is the registry compiler for Open Saasframe. It scans enabled modules, applies built-in generator extensions, loads optional module-declared plugins, and emits the generated files consumed by routing, DI bootstrap, CLI dispatch, widgets, notifications, search, and other runtime registries.

High-level flow

Two extension layers

Open Saasframe uses two additive extension layers during generation:

  • Built-in generator extensions in packages/cli/src/lib/generators/extensions/
  • Module-declared generator plugins exposed from a module's optional generators.ts

Built-in extensions

These are framework-owned registries that every app can rely on without extra plugin authoring:

  • search configs
  • notifications and notification renderers
  • payments client notification adapters
  • notification handlers
  • message types, objects, and client renderers
  • AI tools
  • events
  • analytics widgets/config
  • translatable fields
  • enrichers
  • interceptors
  • component overrides
  • inbox actions
  • guards
  • command interceptors
  • frontend/backend page middleware
  • dashboard widgets
  • injection widgets and injection tables

Module-declared plugins

Modules can extend generation without editing the generator package by exporting generatorPlugins from generators.ts.

Plugin contract summary:

FieldMeaning
idUnique registry/plugin family ID
conventionFileModule-local file to scan for plugin entries
importPrefixPrefix for generated import variable names
configExpr(importName, moduleId)Expression that turns a discovered module file into one generated entry
outputFileNameGenerated file produced by the plugin
buildOutput(...)Function that renders the generated TypeScript source
bootstrapRegistrationOptional bootstrap-time registration contribution

This is what keeps generator architecture additive: new registry families can ship as module-owned plugin definitions rather than as hard-coded cases in the root generator.

Core generated files

These are the primary generated files emitted by the core generator pipeline.

Generated filePurpose
modules.generated.tsFull discovered module graph, including rich runtime metadata used by framework registries and compatibility paths.
modules.runtime.generated.tsRuntime-focused module registry used by the current lightweight bootstrap/runtime path.
modules.app.generated.tsApp bootstrap registry used by application startup without route-heavy coupling.
modules.cli.generated.tsCLI module registry loaded by yarn saasframe for module-owned commands.
cli-modules.generated.tsConvenience export/wrapper around the generated CLI module list.
bootstrap-modules.generated.tsBootstrap-oriented module registry helpers used during startup assembly.
bootstrap-registrations.generated.tsGenerated bootstrap registration calls, including optional plugin-contributed registrations.
entities.generated.tsMikroORM entity registry for discovered modules.
entities.ids.generated.tsStable generated entity ID constants.
entity-fields-registry.tsGenerated entity-fields registry used by encryption and related metadata consumers.
di.generated.tsDiscovered DI registrars for enabled modules.
frontend-routes.generated.tsFrontend route manifest with lazy route-loading metadata.
backend-routes.generated.tsBackend route manifest with lazy route-loading metadata.
api-routes.generated.tsAPI route manifest with method/path metadata and lazy handlers.
subscribers.generated.tsLegacy/generated subscriber registry compatibility file.
openapi.generated.jsonGenerated OpenAPI document built from discovered API routes and route metadata.
module-package-sources.cssGenerated stylesheet that tracks module package source markers for runtime styling/diagnostics.

Built-in extension outputs

These generated files come from built-in generator extensions.

Generated fileSource extensionPurpose
search.generated.tssearchSearch module config registry.
notifications.generated.tsnotificationsNotification type registry.
notifications.client.generated.tsnotificationsClient-side notification renderers.
payments.client.generated.tsnotificationsPayment-related notification/client adapters.
notification-handlers.generated.tsnotificationsReactive notification side-effect handlers.
message-types.generated.tsmessagesMessage type registry.
message-objects.generated.tsmessagesMessage object registry.
messages.client.generated.tsmessagesClient-side message object renderers/adapters.
ai-tools.generated.tsai-toolsAI/MCP tool registry.
events.generated.tseventsEvent definitions and event registry metadata.
analytics.generated.tsanalyticsAnalytics/dashboard configuration registry.
translations-fields.generated.tstranslatable-fieldsTranslatable field declarations by module/entity.
enrichers.generated.tsenrichersResponse enricher registry.
interceptors.generated.tsinterceptorsAPI interceptor registry.
component-overrides.generated.tscomponent-overridesUI component override registry.
inbox-actions.generated.tsinbox-actionsInbox action registry.
guards.generated.tsguardsMutation/page/runtime guard registry.
command-interceptors.generated.tscommand-interceptorsCommand interceptor registry.
frontend-middleware.generated.tspage-middlewareFrontend page middleware registry.
backend-middleware.generated.tspage-middlewareBackend page middleware registry.
dashboard-widgets.generated.tsdashboard-widgetsDashboard widget registry.
injection-widgets.generated.tsinjection-widgetsInjection widget registry.
injection-tables.generated.tsinjection-widgetsInjection table/slot registry.

Why this architecture exists

  • Module owners keep CLI, UI, API, and generation logic close to the same feature boundary.
  • Disabling a module naturally removes its discovered registries and plugin output.
  • Standalone apps and monorepos share the same mental model, even though one scans source trees and the other scans compiled package output.
  • The generator remains additive: new registry families can be introduced without destabilizing the root CLI entrypoint.

Relationship to cache CLI

The cache command is a normal cli.ts discovery case, not a generator plugin. It is documented separately in Cache CLI architecture. That page focuses on cache-specific runtime behavior, while this page documents the broader generator system that makes module-owned commands discoverable.