@blotch/angular-lynx-build

The Angular CLI builders behind ng build, ng serve and ng test on Lynx. It's the only build dependency your app needs, as @angular/build is for a web app. RSpeedy (the Lynx build tool), the AngularLynx plugin and the native app builder come with it.

Usage

ng add @blotch/angular-lynx installs it and points your angular.json targets at it:

angular.json
"architect": {
  "build": {
    "builder": "@blotch/angular-lynx-build:application",
    "options": {
      "browser": "src/main.ts",
      "tsConfig": "tsconfig.app.json"
    }
  },
  "serve": {
    "builder": "@blotch/angular-lynx-build:dev-server"
  },
  "test": {
    "builder": "@blotch/angular-lynx-build:unit-test"
  }
}
BuilderCommandWhat it does
applicationng buildBuilds the bundles into dist/, and with --platform, the iOS and Android apps
dev-serverng serveServes the app to Lynx Explorer and the web preview, and prints the QR code that Lynx Explorer scans
unit-testng testRuns your unit tests with Angular's own unit-test builder, set up for Lynx

They work like Angular's builders:

  • Configurations and flags override options, so -c development and --source-map work.
  • optimization picks the build. A build that optimizes scripts is a production build. Any other build is a development build, with dev mode on. There's no mode option.

application

ng build compiles your app into bundles, the files the Lynx engine runs. For a walkthrough, see Build your app.

Build options

OptionTypeDefaultDescription
platform"ios" | "android" | "all"—Also build the native apps into dist/<platform>/. Builds only the lynx environment
watchbooleanfalseRebuild the bundles when a file changes. Can't be combined with platform
configstringlynx.config.ts, if anyThe RSpeedy config file, relative to the project root. .js, .mts and .mjs are found too. See lynx.config.ts
environmentstring[]AllOnly build these RSpeedy environments, e.g. ["lynx"]. RSpeedy builds lynx for iOS and Android, and web for the web preview

Angular options

These are Angular's own application builder options, and they work as they do in a web app:

browser, tsConfig, polyfills, styles, inlineStyleLanguage, fileReplacements, aot (default true), sourceMap, outputHashing, i18nMissingTranslation

A few work differently on Lynx:

OptionOn Lynx
browserThe entry point, and RSpeedy's source.entry unless lynx.config.ts sets one
optimizationAlso picks a production build (scripts optimized, the default) or a development build
budgetsChecked against each entry's bundle (each is its own initial budget) and each component stylesheet. The web preview isn't checked
outputPathDefaults to dist/
assets, scriptsOnly ng test uses them. Bundles load assets through imports
index, extractLicensesAccepted so ng new's config stays valid. They do nothing

Native app options

ng build --platform reads these. Run on iOS and Android and Publish to the app stores explain how to set them.

OptionTypeDefaultDescription
appNamestringThe project nameThe name under the app icon
appIdstring—The app ID for both stores, e.g. com.example.myapp
versionstringpackage.json's versionThe version users see, e.g. 1.2.0
buildNumberinteger (1 or more)—The store build number. It must increase with every upload
orientation"portrait" | "landscape" | "all""portrait"The screen orientations the app supports
iconstring—A 1024×1024 PNG without transparency, relative to the project root
permissionsobject—The reason shown for each permission the app uses: camera, microphone, location, photos, contacts, notifications
deepLinkSchemesstring[]—URL schemes that open the app, e.g. ["myapp"]
iosobject—teamId: your Apple Developer team ID. signing: see below
androidobject—signing: see below

The signing fields name environment variables, so no secret goes in angular.json. See signing.

FieldHolds
ios.signing.keyIdEnvThe App Store Connect API key's ID
ios.signing.issuerIdEnvThe API key's issuer ID
ios.signing.keyPathEnvThe path to the API key file
android.signing.storeFileEnvThe path to the upload keystore
android.signing.storePasswordEnvThe keystore's password
android.signing.keyAliasThe key's alias (the value itself, not a variable name)
android.signing.keyPasswordEnvThe key's password

dev-server

ng serve runs RSpeedy's dev server. Lynx Explorer and the web preview load your app from it.

OptionTypeDefaultDescription
buildTargetstringThe project's build:developmentThe build target to serve, as project:target[:configuration]. Its options configure the build
portnumberRSpeedy'sThe port to serve on. Overrides server.port in lynx.config.ts
hoststringRSpeedy'sThe host to listen on. Overrides server.host in lynx.config.ts
environmentstring[]The build target's, or allOnly build these RSpeedy environments

unit-test

ng test runs Angular's @angular/build:unit-test builder with Vitest. It takes the same options (include, watch, coverage, setupFiles, providersFile, ...). include defaults to both **/*.spec.ts and **/*.test.ts.

For Lynx, it also:

  • defines the compile-time flags the AngularLynx runtime reads;
  • loads the setup of @blotch/angular-lynx-testing-library when your project has it, so render() works without a setup file;
  • loads imported images, fonts and media as file URLs, as RSpeedy does in the app.

It runs your project's own @angular/build, which must match your Angular version. See Testing for writing tests.

lynx.config.ts

Your app doesn't need one. Without it, the builders configure RSpeedy from angular.json:

RSpeedy settingComes from
source.entryThe build target's browser option
environmentslynx (iOS and Android) and web (the web preview)
modeThe build target's optimization
pluginspluginAngularLynx(), plus the Lynx Explorer QR code plugin for ng serve

Add a lynx.config.ts to the project root to change anything else RSpeedy supports, or to pass the AngularLynx plugin options. Your settings replace the defaults above, except mode, which always follows optimization.

lynx.config.ts
import { defineConfig, pluginAngularLynx } from '@blotch/angular-lynx-build';

export default defineConfig({
  plugins: [pluginAngularLynx({ enableCSSInheritance: true })],
});

The builders use the plugin you list instead of adding their own. Import from @blotch/angular-lynx-build, not @lynx-js/rspeedy or @blotch/rsbuild-plugin-angular-lynx. Those are its dependencies, not your app's, so strict package managers (pnpm, Yarn PnP) can't resolve them.

Plugin options

pluginAngularLynx() compiles Angular for Lynx. All of its options are optional.

Build

OptionTypeDefaultDescription
pagesstring[] | 'all'—Build these angular.json projects as separate pages, each with its own bundle. 'all' builds every application project. See Multi-page apps
enableSSRbooleanfalseSupport Lynx's snapshot SSR, which renders the first screen ahead of time. See the SSR example
targetSdkVersionstring'3.2'The oldest Lynx engine version the bundle runs on
experimental_isLazyBundlebooleanfalseExperimental. Emit each lazy route and @defer block as its own lazy bundle, loaded at runtime. By default, they're built into the main bundle
debugInfoOutsidebooleantrueWrite debug info to a separate file instead of into the bundle, which keeps the bundle smaller

CSS

OptionTypeDefaultDescription
enableCSSSelectorbooleantrueMatch CSS selectors in the Lynx engine. Component styles need it
enableCSSInheritancebooleanfalseLet text properties (color, font-size, line-height, ...) inherit from parent elements, as on the web. Lynx doesn't inherit them by default
customCSSInheritanceListstring[]—Inherit only these properties. Needs enableCSSInheritance
enableCSSInvalidationbooleanfalseRe-match combinator selectors such as .dark .title when a class changes at runtime. Without it, they only match when classes are first set. Costs some performance
defaultDisplayLinearbooleantrueGive elements Lynx's display: linear unless they set display. With false, flex is the default
enableRemoveCSSScopebooleanfalseStop scoping CSS to the components that import it, so every rule applies app-wide
removeDescendantSelectorScopebooleanfalseLet descendant selectors match across CSS scopes

Gestures and accessibility

OptionTypeDefaultDescription
enableNewGesturebooleantrueTurn on Lynx's gesture system. Gestures silently don't fire without it
enableA11ybooleantrueUse Android's own accessibility views while a screen reader is on. Set it to false so UI test tools such as Maestro and Appium always see the elements
enableAccessibilityElementbooleanfalseMake every element an accessibility element by default, as if it set accessibility-element

Accepted but not used yet

The plugin accepts these options, but doesn't read them yet: defaultOverflowVisible (default true), enableICU (false), enableParallelElement (true) and pipelineSchedulerConfig (65536).

Without the Angular CLI

To run RSpeedy yourself, install @blotch/rsbuild-plugin-angular-lynx and import the plugin from there. Nothing adds it for you, so list it in lynx.config.ts and set source.entry.

Exports

ExportKindDescription
pluginAngularLynxFunctionCreates the AngularLynx plugin. Takes the plugin options
PluginAngularLynxOptionsTypeThe plugin options
defineConfigFunctionRSpeedy's config helper, for lynx.config.ts
ConfigTypeRSpeedy's config type
pluginAngularCliContext, AngularCliContextFunction, typeHow the builders pass angular.json's options to the plugin. Apps don't need them
/clientTypesTypes for asset and CSS module imports and import.meta.env. ng add references them in src/rspeedy-env.d.ts