Your first module
In ten minutes: write a module, call it from a screen, change it, and read a compile error.
-
Write the module
Section titled “Write the module”greet.lucent.ts export function greet(name: string): string {const trimmed = name.trim();return trimmed.length > 0 ? `Hello, ${trimmed}!` : "Hello, stranger!";}See the C++
m_greet.cpp // Generated by Lucent from greet.lucent.ts. Do not edit.#include "m_greet.h"#pragma push_macro("greet")#undef greet#pragma push_macro("trimmed")#undef trimmednamespace lucent_app {lucent::String m_greet::greet(lucent::String p0_) {#line 2 "greet.lucent.ts"lucent::String trimmed = p0_.trim();#line 3 "greet.lucent.ts"double v3_ = static_cast<double>(trimmed.length());bool v5_ = v3_ > 0.0;lucent::String v12_{};if (v5_) {v12_ = lucent::String(lucent::String(LUCENT_STR("Hello, ")) + trimmed) + LUCENT_STR("!");} else {v12_ = LUCENT_STR("Hello, stranger!");}#line 3 "greet.lucent.ts"return v12_;}void m_greet::init() {}} // namespace lucent_app#pragma pop_macro("trimmed")#pragma pop_macro("greet")Save it as
src/greet.lucent.ts. Any*.lucent.tsfile is a module, and its exports are what JavaScript can call. -
Call it from a screen
Section titled “Call it from a screen”App.tsx import { Text } from "react-native";import { greet } from "./src/greet.lucent";export default function App() {return <Text>{greet(" Ada ")}</Text>;}The import is typed from the source, so your editor checks the call.
-
Run the app
Section titled “Run the app”terminal npx expo run:iosThe config plugin builds your modules during prebuild. Xcode then compiles the C++ into the app.
terminal npx lucent buildcd ios && pod install && cd ..npx react-native run-iosOn Android, skip the first two lines: the Gradle task runs
lucent builditself.The screen shows
Hello, Ada!, computed in C++. -
Change it
Section titled “Change it”Change
HellotoHiand save. While Metro runs,lucent devrebuilds the native package and prints one line in Metro's output:terminal [14:02:11] ✓ 2 modules 36 ms · rebuild the appThe C++ is part of the app binary, so reloading JavaScript doesn't replace it. Run the app again to see
Hi, Ada!. -
Read an error
Section titled “Read an error”Change the parameter to
name: anyand save:terminal [14:03:40] ✗ 1 errorsrc/greet.lucent.ts:1:23 LUCENT2001 `any` has no native representation; give this value a concrete typefix: use a concrete type, a union, or a generic parameterEach error has a code, a place and a fix.
npx lucent explain LUCENT2001shows why the rule exists, with a wrong and a right example. Putstringback.
What happened
Section titled “What happened”- The TypeScript checker checked
greet.lucent.tsagainst Lucent's rules. - Lucent compiled it to C++ and wrote the native package to
.lucent/native. - Xcode or Gradle compiled the native package into your app.
- Metro replaced the
greet.lucentimport with a proxy. - Calling
greetwent through JSI to the C++ and back.
How Lucent works follows each step.