create-lynx-library-canary
v0.6.1
Published
Create native Lynx libraries
Readme
create-lynx-library
Create native Lynx libraries.
npm create lynx-libraryThe interactive flow lets you choose one or more library features:
- Native Module: platform implementations on Android, iOS, and HarmonyOS; a shared Node-API adapter registered as a native module on Lynxtron.
- NAPI Native Module: one shared C++ Node-API implementation for Android, iOS, HarmonyOS, and Lynxtron.
- Element: Android, iOS, HarmonyOS, and shared C++ scaffolds.
- Service
It also lets you choose one or more Native platforms:
- Android
- iOS
- HarmonyOS
- Lynxtron
For non-interactive usage:
npm create lynx-library -- \
--dir ./lynx-button \
--features native-module,napi-native-module,element,service \
--platforms android,ios,harmony,lynxtron \
--package-name @example/lynx-button \
--android-package com.example.button \
--module-name ButtonModule \
--element-name x-button \
--service-name ButtonServiceUse --features all to generate a package that contains all supported library
features. Use --platforms all to generate native directories for all supported
Native platforms. When --platforms is omitted in non-interactive usage,
Android, iOS, HarmonyOS, and Lynxtron are generated.
Generated libraries include lynx.lib.json, JS facade sources, selected Native
platform examples, an example app skeleton, and a codegen script powered by the
current published version of @lynx-js/autolink-codegen.
Native Module declarations live in types/platform-native-module.d.ts. NAPI
Native Module declarations live in types/napi-native-module.d.ts.
When both features are selected, the platform module keeps --module-name and
the NAPI module adds the Napi suffix. For example, --module-name
StorageModule exposes NativeModules.StorageModule and
NativeModules.StorageModuleNapi. On Lynxtron both implementations reuse the
shared Node-API source shape, but remain separate modules with separate
registration paths.
NAPI Native Module workflow
Select the shared implementation with:
create-lynx-library lynx-storage \
--features napi-native-module \
--platforms android,ios,harmony,lynxtronThe generated package depends on @lynx-js/weak-node-api and
@lynx-js/lynx-library-headers. Shared business sources use the standard
Node-API headers from @lynx-js/weak-node-api:
- Android and iOS use unsuffixed
napi_*symbols. - HarmonyOS and Lynxtron enable weak suffix remapping.
- Android, iOS, and HarmonyOS register through
napi_module_register. - Lynxtron NAPI modules also register through
napi_module_register. - A Lynxtron adapter generated for the separate
native-modulefeature useslynx_env_register_native_module.
Edit types/napi-native-module.d.ts to describe the JavaScript API:
/** @lynxmodule */
export declare class StorageModule {
setValue(key: string, value: string): void;
getValue(key: string): string | null;
}Then run:
npm run codegenThe generated files have the following responsibilities:
generated/<Module>.tsis the BTS TypeScript facade. It lazily loads the addon throughglobalThis.getNapiLoader(),globalThis.__lynxNapiLoader, orlynx.getModuleLoader(). For a NAPI Native Module, importing the package installs a JavaScriptNativeModules.<Module>shim and forwards all unrelated module names to the original native HostObject. Do not edit it directly; update the declaration file and rerun codegen instead.shared/nativeModule/<Module>.ccis the user-owned shared C++ implementation. Codegen creates it once and preserves it on later runs. Fill in the generated N-API callback method bodies, including argument parsing, validation, return value creation, and error handling. After changing the typings, manually keep this file's callbacks and exports in sync because codegen will not overwrite it.shared/nativeModule/generated/<Module>Registration.ccis overwritten by codegen and owns Android, iOS, and HarmonyOS registration boilerplate. Do not move registration macros into the user-owned implementation.shared/nativeModule/CMakeLists.txtbuilds the shared N-API sources as an object target that Android, HarmonyOS, and Lynxtron reuse. iOS compiles the same implementation through its generated CocoaPods wrapper.ios/generated/<Module>NapiWrapper.ccis an iOS CocoaPods compile entry that is generated when iOS is selected and includes the shared implementation from inside the iOS pod source root. Do not put business logic in this wrapper; keep it inshared/nativeModule/<Module>.cc.ios/addon_use.hexposesNAPI_USE(<Module>)so the generated iOS autolink registry can keep the Node-API registration symbol from being stripped by the linker.lynxtron/generated_napi_registration.ccis generated when Lynxtron is selected and invokes the generated standard Node-API registration function when the.nodebinding is required.lynxtron/generated_platform_registration.ccis generated for a platform Native Module on Lynxtron and registers its shared adapter throughlynx_env_register_native_module.harmony/src/main/cpp/harmony_entry.ccis the system OHOS N-API entry exposed to ArkTS. It only loads the HAR native library; shared business sources remain on weak Node-API symbols.
If the module class is renamed, also rename or remove the old user-owned shared
C++ file and update the addon name in lynx.lib.json. Codegen does not delete
stale C++ files or rewrite the manifest.
For Android, the generated library project can build the addon from source via
its externalNativeBuild configuration. The project resolves
org.lynxsdk.lynx:primjs with the Gradle property lynx.primjs.version,
defaulting to 4.+, extracts its native libraries, and links the addon against
libnapi_adapter.so and libnapi.so. libnapi_adapter.so contains the
non-suffixed weak-node-api implementation and injects a PrimJS-backed host table,
so shared source continues to call the standard Node-API functions. Host apps
that need a pinned PrimJS runtime should set lynx.primjs.version from the root
build so the addon and host resolve the same AAR. The default manifest omits
jniLibsDir, which tells Android AutoLink that the generated Android library
project builds the addon. Packages that distribute prebuilt artifacts instead
can set jniLibsDir explicitly; AutoLink then copies lib<Module>.so from each
ABI subdirectory.
For iOS, the generated podspec compiles the generated wrapper and uses
ios/addon_use.h for the registration-symbol reference. The iOS autolink step
adds the addon pod and a generated registry pod automatically. The addon compiles
against the standard headers and implementation from LynxWeakNodeAPI/core;
the generated registry owns the one-time PrimJS bridge initialization before it
uses any addon registration. The podspec file is generated as
ios/<pod-name>.podspec, matching its CocoaPods s.name.
For HarmonyOS, the package contains a source HAR, links the shared weak Node-API implementation, and exports an idempotent initializer. Harmony AutoLink imports and calls that initializer during AppStartup before registering any optional platform provider from the same package.
Import the package root in BTS on every selected platform, then use one call shape:
import '@example/storage-library';
NativeModules.StorageModule.getValue('key');When the lynxtron platform is selected for a Native Module, NAPI Native
Module, or Element project, generated libraries also include shared C++ sources
under shared/, a Lynxtron loader under lynxtron/, and a build:lynxtron
script. The script writes the current OS/architecture .node artifact to
dist/<platform>/<arch>/. The shared CMake entry lives at
shared/CMakeLists.txt; generated packages do not create a top-level
CMakeLists.txt. The generated ./lynxtron entry loads the dynamic library and
runs the registration entry for each selected feature.
Build dist/<platform>/<arch>/ on every Lynxtron OS/architecture that the npm
package supports before publishing. npm pack and npm publish do not compile
native artifacts. Install the published package as a normal application
dependency. A Lynxtron host configured with pluginLynxtron() discovers its
manifest, stages the matching target, and loads ./lynxtron automatically.
Application code does not import that subpath, copy native artifacts, or add
per-library packaging rules.
Lynxtron BTS imports the package root like the other platforms and calls the registered runtime module directly:
import '@example/storage-library';
NativeModules.StorageModule.getValue('key');Run npm pack --dry-run before publishing. Generated packages exclude the
local shared/third_party/ CMake header cache, but authors should still verify
that dist/ contains every intended Lynxtron artifact and that no other local
build outputs are included.
