@tamer4lynx/cli 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -29
- package/dist/index.js +261 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,17 +8,12 @@ Inspired by [Expo](https://expo.dev) and [Expo Go](https://expo.dev/go).
|
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
|
-
All Tamer packages are published under the `@tamer4lynx` scope on npm. Install the CLI globally:
|
|
11
|
+
All Tamer packages are published under the `@tamer4lynx` scope on npm. Install the CLI globally (use `@prerelease` for latest):
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm i -g @tamer4lynx/cli
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
With pnpm or Bun:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
pnpm add -g @tamer4lynx/cli
|
|
21
|
-
bun add -g @tamer4lynx/cli
|
|
14
|
+
npm i -g @tamer4lynx/cli@prerelease
|
|
15
|
+
pnpm add -g @tamer4lynx/cli@prerelease
|
|
16
|
+
bun add -g @tamer4lynx/cli@prerelease
|
|
22
17
|
```
|
|
23
18
|
|
|
24
19
|
Or from GitHub (run `npm uninstall -g @tamer4lynx/cli` first if switching):
|
|
@@ -175,7 +170,7 @@ Builds your app. Dev client (QR scan, HMR) is included when you use **debug** (`
|
|
|
175
170
|
| Command | Flags | Description |
|
|
176
171
|
|---------|-------|-------------|
|
|
177
172
|
| `t4l add [packages...]` | — | Add @tamer4lynx packages. Future: version tracking (Expo-style). |
|
|
178
|
-
| `t4l add-core` | — | Add core packages (app-shell, screen, router, insets, transports,
|
|
173
|
+
| `t4l add-core` | — | Add core packages (app-shell, screen, router, insets, transports, input, system-ui, icons). |
|
|
179
174
|
| `t4l start` | `-v, --verbose` | Dev server with HMR. `--verbose` shows native + JS logs. |
|
|
180
175
|
| `t4l link` | `-i, --ios`, `-a, --android`, `-s, --silent` | Link modules. `--ios`/`--android` limit to one platform. `--silent` for CI/postinstall. |
|
|
181
176
|
| `t4l autolink-toggle` | — | Toggle `autolink` in tamer.config.json (postinstall linking). |
|
|
@@ -266,29 +261,29 @@ Extensions are discovered via **lynx.ext.json** (RFC standard) or **tamer.json**
|
|
|
266
261
|
|
|
267
262
|
## Native Module References
|
|
268
263
|
|
|
269
|
-
Install from npm and run `t4l link` after adding to your app:
|
|
264
|
+
Install from npm (use `@prerelease` for latest) and run `t4l link` after adding to your app. Also: `pnpm add @tamer4lynx/<pkg>@prerelease` | `bun add @tamer4lynx/<pkg>@prerelease`
|
|
270
265
|
|
|
271
266
|
| Package | Install | Description |
|
|
272
267
|
|---------|---------|-------------|
|
|
273
|
-
| [@tamer4lynx/jiggle](https://www.npmjs.com/package/@tamer4lynx/jiggle) | `npm i @tamer4lynx/jiggle` | Vibration/haptic |
|
|
274
|
-
| [@tamer4lynx/lynxwebsockets](https://www.npmjs.com/package/@tamer4lynx/lynxwebsockets) | `npm i @tamer4lynx/lynxwebsockets` | WebSocket native bridge |
|
|
275
|
-
| [@tamer4lynx/tamer-host](https://www.npmjs.com/package/@tamer4lynx/tamer-host) | `npm i @tamer4lynx/tamer-host` | Production Lynx host templates |
|
|
276
|
-
| [@tamer4lynx/tamer-dev-client](https://www.npmjs.com/package/@tamer4lynx/tamer-dev-client) | `npm i @tamer4lynx/tamer-dev-client` | Dev launcher UI (QR scan, HMR). Add to your app and build with `-d` for a dev build; `-r` omits it. |
|
|
268
|
+
| [@tamer4lynx/jiggle](https://www.npmjs.com/package/@tamer4lynx/jiggle) | `npm i @tamer4lynx/jiggle@prerelease` | Vibration/haptic |
|
|
269
|
+
| [@tamer4lynx/lynxwebsockets](https://www.npmjs.com/package/@tamer4lynx/lynxwebsockets) | `npm i @tamer4lynx/lynxwebsockets@prerelease` | WebSocket native bridge |
|
|
270
|
+
| [@tamer4lynx/tamer-host](https://www.npmjs.com/package/@tamer4lynx/tamer-host) | `npm i @tamer4lynx/tamer-host@prerelease` | Production Lynx host templates |
|
|
271
|
+
| [@tamer4lynx/tamer-dev-client](https://www.npmjs.com/package/@tamer4lynx/tamer-dev-client) | `npm i @tamer4lynx/tamer-dev-client@prerelease` | Dev launcher UI (QR scan, HMR). Add to your app and build with `-d` for a dev build; `-r` omits it. |
|
|
277
272
|
| [@tamer4lynx/tamer-dev-app](https://www.npmjs.com/package/@tamer4lynx/tamer-dev-app) | workspace / npm | Standalone dev app (store build). Your app can use tamer-dev-client for dev builds instead. |
|
|
278
|
-
| [@tamer4lynx/tamer-plugin](https://www.npmjs.com/package/@tamer4lynx/tamer-plugin) | `npm i @tamer4lynx/tamer-plugin` | Rsbuild plugin middleman |
|
|
279
|
-
| [@tamer4lynx/tamer-router](https://www.npmjs.com/package/@tamer4lynx/tamer-router) | `npm i @tamer4lynx/tamer-router` | File-based routing, Stack/Tabs |
|
|
280
|
-
| [@tamer4lynx/tamer-icons](https://www.npmjs.com/package/@tamer4lynx/tamer-icons) | `npm i @tamer4lynx/tamer-icons` | Icon fonts (Material, Font Awesome) |
|
|
281
|
-
| [@tamer4lynx/tamer-insets](https://www.npmjs.com/package/@tamer4lynx/tamer-insets) | `npm i @tamer4lynx/tamer-insets` | System insets, keyboard state |
|
|
282
|
-
| [@tamer4lynx/tamer-system-ui](https://www.npmjs.com/package/@tamer4lynx/tamer-system-ui) | `npm i @tamer4lynx/tamer-system-ui` | Status bar, navigation bar |
|
|
283
|
-
| [@tamer4lynx/tamer-app-shell](https://www.npmjs.com/package/@tamer4lynx/tamer-app-shell) | `npm i @tamer4lynx/tamer-app-shell` | AppBar, TabBar, Content layout |
|
|
284
|
-
| [@tamer4lynx/tamer-text-input](https://www.npmjs.com/package/@tamer4lynx/tamer-text-input) | `npm i @tamer4lynx/tamer-text-input` | React TextInput |
|
|
285
|
-
| [@tamer4lynx/tamer-auth](https://www.npmjs.com/package/@tamer4lynx/tamer-auth) | `npm i @tamer4lynx/tamer-auth` | OAuth 2.0 / OIDC |
|
|
286
|
-
| [@tamer4lynx/tamer-biometric](https://www.npmjs.com/package/@tamer4lynx/tamer-biometric) | `npm i @tamer4lynx/tamer-biometric` | Fingerprint, Face ID |
|
|
287
|
-
| [@tamer4lynx/tamer-display-browser](https://www.npmjs.com/package/@tamer4lynx/tamer-display-browser) | `npm i @tamer4lynx/tamer-display-browser` | Open URLs in system browser |
|
|
288
|
-
| [@tamer4lynx/tamer-linking](https://www.npmjs.com/package/@tamer4lynx/tamer-linking) | `npm i @tamer4lynx/tamer-linking` | Deep linking |
|
|
289
|
-
| [@tamer4lynx/tamer-screen](https://www.npmjs.com/package/@tamer4lynx/tamer-screen) | `npm i @tamer4lynx/tamer-screen` | SafeArea, Screen, AvoidKeyboard |
|
|
290
|
-
| [@tamer4lynx/tamer-secure-store](https://www.npmjs.com/package/@tamer4lynx/tamer-secure-store) | `npm i @tamer4lynx/tamer-secure-store` | Secure key-value storage |
|
|
291
|
-
| [@tamer4lynx/tamer-transports](https://www.npmjs.com/package/@tamer4lynx/tamer-transports) | `npm i @tamer4lynx/tamer-transports` | Fetch, WebSocket, EventSource polyfills |
|
|
273
|
+
| [@tamer4lynx/tamer-plugin](https://www.npmjs.com/package/@tamer4lynx/tamer-plugin) | `npm i @tamer4lynx/tamer-plugin@prerelease` | Rsbuild plugin middleman |
|
|
274
|
+
| [@tamer4lynx/tamer-router](https://www.npmjs.com/package/@tamer4lynx/tamer-router) | `npm i @tamer4lynx/tamer-router@prerelease` | File-based routing, Stack/Tabs |
|
|
275
|
+
| [@tamer4lynx/tamer-icons](https://www.npmjs.com/package/@tamer4lynx/tamer-icons) | `npm i @tamer4lynx/tamer-icons@prerelease` | Icon fonts (Material, Font Awesome) |
|
|
276
|
+
| [@tamer4lynx/tamer-insets](https://www.npmjs.com/package/@tamer4lynx/tamer-insets) | `npm i @tamer4lynx/tamer-insets@prerelease` | System insets, keyboard state |
|
|
277
|
+
| [@tamer4lynx/tamer-system-ui](https://www.npmjs.com/package/@tamer4lynx/tamer-system-ui) | `npm i @tamer4lynx/tamer-system-ui@prerelease` | Status bar, navigation bar |
|
|
278
|
+
| [@tamer4lynx/tamer-app-shell](https://www.npmjs.com/package/@tamer4lynx/tamer-app-shell) | `npm i @tamer4lynx/tamer-app-shell@prerelease` | AppBar, TabBar, Content layout |
|
|
279
|
+
| [@tamer4lynx/tamer-text-input](https://www.npmjs.com/package/@tamer4lynx/tamer-text-input) | `npm i @tamer4lynx/tamer-text-input@prerelease` | React TextInput |
|
|
280
|
+
| [@tamer4lynx/tamer-auth](https://www.npmjs.com/package/@tamer4lynx/tamer-auth) | `npm i @tamer4lynx/tamer-auth@prerelease` | OAuth 2.0 / OIDC |
|
|
281
|
+
| [@tamer4lynx/tamer-biometric](https://www.npmjs.com/package/@tamer4lynx/tamer-biometric) | `npm i @tamer4lynx/tamer-biometric@prerelease` | Fingerprint, Face ID |
|
|
282
|
+
| [@tamer4lynx/tamer-display-browser](https://www.npmjs.com/package/@tamer4lynx/tamer-display-browser) | `npm i @tamer4lynx/tamer-display-browser@prerelease` | Open URLs in system browser |
|
|
283
|
+
| [@tamer4lynx/tamer-linking](https://www.npmjs.com/package/@tamer4lynx/tamer-linking) | `npm i @tamer4lynx/tamer-linking@prerelease` | Deep linking |
|
|
284
|
+
| [@tamer4lynx/tamer-screen](https://www.npmjs.com/package/@tamer4lynx/tamer-screen) | `npm i @tamer4lynx/tamer-screen@prerelease` | SafeArea, Screen, AvoidKeyboard |
|
|
285
|
+
| [@tamer4lynx/tamer-secure-store](https://www.npmjs.com/package/@tamer4lynx/tamer-secure-store) | `npm i @tamer4lynx/tamer-secure-store@prerelease` | Secure key-value storage |
|
|
286
|
+
| [@tamer4lynx/tamer-transports](https://www.npmjs.com/package/@tamer4lynx/tamer-transports) | `npm i @tamer4lynx/tamer-transports@prerelease` | Fetch, WebSocket, EventSource polyfills |
|
|
292
287
|
|
|
293
288
|
The iOS autolinking feature runs `pod install` automatically.
|
|
294
289
|
|
|
@@ -305,6 +300,7 @@ Contributions are welcome! To develop on Tamer4Lynx:
|
|
|
305
300
|
git clone https://github.com/tamer4lynx/tamer4lynx.git
|
|
306
301
|
cd tamer4lynx
|
|
307
302
|
npm install
|
|
303
|
+
# or: pnpm install | bun install
|
|
308
304
|
```
|
|
309
305
|
|
|
310
306
|
Please feel free to submit issues or pull requests.
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import path24 from "path";
|
|
|
12
12
|
import { program } from "commander";
|
|
13
13
|
|
|
14
14
|
// package.json
|
|
15
|
-
var version = "0.0.
|
|
15
|
+
var version = "0.0.8";
|
|
16
16
|
|
|
17
17
|
// src/android/create.ts
|
|
18
18
|
import fs3 from "fs";
|
|
@@ -679,6 +679,7 @@ ${reloadMethod}
|
|
|
679
679
|
private fun buildLynxView(): LynxView {
|
|
680
680
|
val viewBuilder = LynxViewBuilder()
|
|
681
681
|
viewBuilder.setTemplateProvider(TemplateProvider(this))
|
|
682
|
+
GeneratedLynxExtensions.configureViewBuilder(viewBuilder)
|
|
682
683
|
return viewBuilder.build(this)
|
|
683
684
|
}
|
|
684
685
|
}
|
|
@@ -844,6 +845,7 @@ ${devClientField} private var lynxView: LynxView? = null${!hasDevClient ? "\n
|
|
|
844
845
|
private fun buildLynxView(): LynxView {
|
|
845
846
|
val viewBuilder = LynxViewBuilder()
|
|
846
847
|
viewBuilder.setTemplateProvider(TemplateProvider(this))
|
|
848
|
+
GeneratedLynxExtensions.configureViewBuilder(viewBuilder)
|
|
847
849
|
return viewBuilder.build(this)
|
|
848
850
|
}${standaloneLifecycle}${devClientCleanup}
|
|
849
851
|
}
|
|
@@ -1007,6 +1009,8 @@ lynx-service-http = { module = "org.lynxsdk.lynx:lynx-service-http", version.ref
|
|
|
1007
1009
|
lynx-service-image = { module = "org.lynxsdk.lynx:lynx-service-image", version.ref = "lynx" }
|
|
1008
1010
|
lynx-service-log = { module = "org.lynxsdk.lynx:lynx-service-log", version.ref = "lynx" }
|
|
1009
1011
|
lynx-trace = { module = "org.lynxsdk.lynx:lynx-trace", version.ref = "lynx" }
|
|
1012
|
+
lynx-xelement = { module = "org.lynxsdk.lynx:xelement", version.ref = "lynx" }
|
|
1013
|
+
lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref = "lynx" }
|
|
1010
1014
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
|
1011
1015
|
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
|
1012
1016
|
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
|
@@ -1155,6 +1159,8 @@ dependencies {
|
|
|
1155
1159
|
implementation(libs.animated.base)
|
|
1156
1160
|
implementation(libs.lynx.service.log)
|
|
1157
1161
|
implementation(libs.lynx.service.http)
|
|
1162
|
+
implementation(libs.lynx.xelement)
|
|
1163
|
+
implementation(libs.lynx.xelement.input)
|
|
1158
1164
|
implementation(libs.okhttp)
|
|
1159
1165
|
implementation(libs.zxing)
|
|
1160
1166
|
kapt(libs.lynx.processor)
|
|
@@ -1578,12 +1584,18 @@ ${hostViewLines}
|
|
|
1578
1584
|
|
|
1579
1585
|
import android.content.Context
|
|
1580
1586
|
import com.lynx.tasm.LynxEnv
|
|
1587
|
+
import com.lynx.tasm.LynxViewBuilder
|
|
1588
|
+
import com.lynx.xelement.XElementBehaviors
|
|
1581
1589
|
${moduleImports}
|
|
1582
1590
|
${elementImports}
|
|
1583
1591
|
|
|
1584
1592
|
object GeneratedLynxExtensions {
|
|
1585
1593
|
fun register(context: Context) {
|
|
1586
1594
|
${allRegistrations}
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
fun configureViewBuilder(viewBuilder: LynxViewBuilder) {
|
|
1598
|
+
viewBuilder.addBehaviors(XElementBehaviors().create())
|
|
1587
1599
|
}${hostViewMethod}
|
|
1588
1600
|
}
|
|
1589
1601
|
`;
|
|
@@ -1850,6 +1862,7 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
|
|
|
1850
1862
|
syncManifestPermissions(packages);
|
|
1851
1863
|
syncDeepLinkIntentFilters();
|
|
1852
1864
|
syncVersionCatalog(packages);
|
|
1865
|
+
ensureXElementDeps();
|
|
1853
1866
|
ensureReleaseSigning();
|
|
1854
1867
|
console.log("\u2728 Autolinking complete.");
|
|
1855
1868
|
}
|
|
@@ -1900,6 +1913,40 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
|
|
|
1900
1913
|
console.log("\u2705 Synced version catalog (libs.versions.toml) for linked modules.");
|
|
1901
1914
|
}
|
|
1902
1915
|
}
|
|
1916
|
+
function ensureXElementDeps() {
|
|
1917
|
+
const libsTomlPath = path6.join(appAndroidPath, "gradle", "libs.versions.toml");
|
|
1918
|
+
if (fs6.existsSync(libsTomlPath)) {
|
|
1919
|
+
let toml = fs6.readFileSync(libsTomlPath, "utf8");
|
|
1920
|
+
let updated = false;
|
|
1921
|
+
if (!toml.includes("lynx-xelement =")) {
|
|
1922
|
+
toml = toml.replace(
|
|
1923
|
+
/(lynx-trace\s*=\s*\{[^\n]*\n)/,
|
|
1924
|
+
`$1lynx-xelement = { module = "org.lynxsdk.lynx:xelement", version.ref = "lynx" }
|
|
1925
|
+
lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref = "lynx" }
|
|
1926
|
+
`
|
|
1927
|
+
);
|
|
1928
|
+
updated = true;
|
|
1929
|
+
}
|
|
1930
|
+
if (updated) {
|
|
1931
|
+
fs6.writeFileSync(libsTomlPath, toml);
|
|
1932
|
+
console.log("\u2705 Added XElement entries to version catalog.");
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
const appBuildPath = path6.join(appAndroidPath, "app", "build.gradle.kts");
|
|
1936
|
+
if (fs6.existsSync(appBuildPath)) {
|
|
1937
|
+
let content = fs6.readFileSync(appBuildPath, "utf8");
|
|
1938
|
+
if (!content.includes("lynx.xelement")) {
|
|
1939
|
+
content = content.replace(
|
|
1940
|
+
/(implementation\(libs\.lynx\.service\.http\))/,
|
|
1941
|
+
`$1
|
|
1942
|
+
implementation(libs.lynx.xelement)
|
|
1943
|
+
implementation(libs.lynx.xelement.input)`
|
|
1944
|
+
);
|
|
1945
|
+
fs6.writeFileSync(appBuildPath, content);
|
|
1946
|
+
console.log("\u2705 Added XElement dependencies to app build.gradle.kts.");
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1903
1950
|
function ensureReleaseSigning() {
|
|
1904
1951
|
const appBuildPath = path6.join(appAndroidPath, "app", "build.gradle.kts");
|
|
1905
1952
|
if (!fs6.existsSync(appBuildPath)) return;
|
|
@@ -2247,10 +2294,18 @@ async function setupCocoaPods(rootDir) {
|
|
|
2247
2294
|
throw new Error(`Podfile not found at ${podfilePath}`);
|
|
2248
2295
|
}
|
|
2249
2296
|
console.log(`\u{1F680} Executing pod install in: ${rootDir}`);
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2297
|
+
try {
|
|
2298
|
+
execSync4("pod install", {
|
|
2299
|
+
cwd: rootDir,
|
|
2300
|
+
stdio: "inherit"
|
|
2301
|
+
});
|
|
2302
|
+
} catch {
|
|
2303
|
+
console.log("\u2139\uFE0F Retrying CocoaPods install with repo update...");
|
|
2304
|
+
execSync4("pod install --repo-update", {
|
|
2305
|
+
cwd: rootDir,
|
|
2306
|
+
stdio: "inherit"
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2254
2309
|
console.log("\u2705 CocoaPods dependencies installed successfully.");
|
|
2255
2310
|
} catch (err) {
|
|
2256
2311
|
console.error("\u274C Failed to install CocoaPods dependencies.", err.message);
|
|
@@ -2350,6 +2405,8 @@ target '${appName}' do
|
|
|
2350
2405
|
pod 'SDWebImage','5.15.5'
|
|
2351
2406
|
pod 'SDWebImageWebPCoder', '0.11.0'
|
|
2352
2407
|
|
|
2408
|
+
pod 'XElement', '3.6.0'
|
|
2409
|
+
|
|
2353
2410
|
# GENERATED AUTOLINK DEPENDENCIES START
|
|
2354
2411
|
# This section is automatically generated by Tamer4Lynx.
|
|
2355
2412
|
# Manual edits will be overwritten.
|
|
@@ -2361,6 +2418,8 @@ post_install do |installer|
|
|
|
2361
2418
|
target.build_configurations.each do |config|
|
|
2362
2419
|
config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17'
|
|
2363
2420
|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
|
|
2421
|
+
config.build_settings['CLANG_ENABLE_EXPLICIT_MODULES'] = 'NO'
|
|
2422
|
+
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'
|
|
2364
2423
|
end
|
|
2365
2424
|
|
|
2366
2425
|
if target.name == 'Lynx'
|
|
@@ -2371,10 +2430,12 @@ post_install do |installer|
|
|
|
2371
2430
|
'-Wno-error=vla-extension',
|
|
2372
2431
|
'-Wno-deprecated-declarations',
|
|
2373
2432
|
'-Wno-deprecated',
|
|
2433
|
+
'-Wno-deprecated-implementations',
|
|
2374
2434
|
'-Wno-macro-redefined',
|
|
2375
2435
|
'-Wno-enum-compare',
|
|
2376
2436
|
'-Wno-enum-compare-conditional',
|
|
2377
|
-
'-Wno-enum-conversion'
|
|
2437
|
+
'-Wno-enum-conversion',
|
|
2438
|
+
'-Wno-error'
|
|
2378
2439
|
].join(' ')
|
|
2379
2440
|
|
|
2380
2441
|
config.build_settings['OTHER_CPLUSPLUSFLAGS'] = "$(inherited) #{flags}"
|
|
@@ -2385,6 +2446,19 @@ post_install do |installer|
|
|
|
2385
2446
|
end
|
|
2386
2447
|
end
|
|
2387
2448
|
end
|
|
2449
|
+
Dir.glob(File.join(installer.sandbox.root, 'Target Support Files', 'Lynx', '*.xcconfig')).each do |xcconfig_path|
|
|
2450
|
+
next unless File.file?(xcconfig_path)
|
|
2451
|
+
content = File.read(xcconfig_path)
|
|
2452
|
+
next unless content.include?('-Werror')
|
|
2453
|
+
File.write(xcconfig_path, content.gsub('-Werror', ''))
|
|
2454
|
+
end
|
|
2455
|
+
Dir.glob(File.join(installer.sandbox.root, 'Lynx/platform/darwin/**/*.{m,mm}')).each do |lynx_source|
|
|
2456
|
+
next unless File.file?(lynx_source)
|
|
2457
|
+
content = File.read(lynx_source)
|
|
2458
|
+
next unless content.match?(/\\btypeof\\(/)
|
|
2459
|
+
File.chmod(0644, lynx_source) rescue nil
|
|
2460
|
+
File.write(lynx_source, content.gsub(/\\btypeof\\(/, '__typeof__('))
|
|
2461
|
+
end
|
|
2388
2462
|
end
|
|
2389
2463
|
`);
|
|
2390
2464
|
const hostPkg = findTamerHostPackage(process.cwd());
|
|
@@ -2987,9 +3061,35 @@ ${replacementBlock}
|
|
|
2987
3061
|
fs12.writeFileSync(filePath, fileContent, "utf8");
|
|
2988
3062
|
console.log(`\u2705 Updated autolinked section in ${path13.basename(filePath)}`);
|
|
2989
3063
|
}
|
|
3064
|
+
function resolvePodDirectory(pkg) {
|
|
3065
|
+
const configuredDir = path13.join(pkg.packagePath, pkg.config.ios?.podspecPath || ".");
|
|
3066
|
+
if (fs12.existsSync(configuredDir)) {
|
|
3067
|
+
return configuredDir;
|
|
3068
|
+
}
|
|
3069
|
+
const iosDir = path13.join(pkg.packagePath, "ios");
|
|
3070
|
+
if (fs12.existsSync(iosDir)) {
|
|
3071
|
+
const stack = [iosDir];
|
|
3072
|
+
while (stack.length > 0) {
|
|
3073
|
+
const current = stack.pop();
|
|
3074
|
+
try {
|
|
3075
|
+
const entries = fs12.readdirSync(current, { withFileTypes: true });
|
|
3076
|
+
const podspec = entries.find((entry) => entry.isFile() && entry.name.endsWith(".podspec"));
|
|
3077
|
+
if (podspec) {
|
|
3078
|
+
return current;
|
|
3079
|
+
}
|
|
3080
|
+
for (const entry of entries) {
|
|
3081
|
+
if (entry.isDirectory()) {
|
|
3082
|
+
stack.push(path13.join(current, entry.name));
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
} catch {
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
return configuredDir;
|
|
3090
|
+
}
|
|
2990
3091
|
function resolvePodName(pkg) {
|
|
2991
|
-
const
|
|
2992
|
-
const fullPodspecDir = path13.join(pkg.packagePath, podspecDir);
|
|
3092
|
+
const fullPodspecDir = resolvePodDirectory(pkg);
|
|
2993
3093
|
if (fs12.existsSync(fullPodspecDir)) {
|
|
2994
3094
|
try {
|
|
2995
3095
|
const files = fs12.readdirSync(fullPodspecDir);
|
|
@@ -3007,8 +3107,7 @@ ${replacementBlock}
|
|
|
3007
3107
|
const iosPackages = packages.filter((p) => p.config.ios);
|
|
3008
3108
|
if (iosPackages.length > 0) {
|
|
3009
3109
|
iosPackages.forEach((pkg) => {
|
|
3010
|
-
const
|
|
3011
|
-
const relativePath = path13.relative(iosProjectPath, path13.join(pkg.packagePath, podspecPath));
|
|
3110
|
+
const relativePath = path13.relative(iosProjectPath, resolvePodDirectory(pkg));
|
|
3012
3111
|
const podName = resolvePodName(pkg);
|
|
3013
3112
|
scriptContent += `
|
|
3014
3113
|
pod '${podName}', :path => '${relativePath}'`;
|
|
@@ -3019,6 +3118,83 @@ ${replacementBlock}
|
|
|
3019
3118
|
}
|
|
3020
3119
|
updateGeneratedSection(podfilePath, scriptContent.trim(), "# GENERATED AUTOLINK DEPENDENCIES START", "# GENERATED AUTOLINK DEPENDENCIES END");
|
|
3021
3120
|
}
|
|
3121
|
+
function ensureXElementPod() {
|
|
3122
|
+
const podfilePath = path13.join(iosProjectPath, "Podfile");
|
|
3123
|
+
if (!fs12.existsSync(podfilePath)) return;
|
|
3124
|
+
let content = fs12.readFileSync(podfilePath, "utf8");
|
|
3125
|
+
if (content.includes("pod 'XElement'")) return;
|
|
3126
|
+
const lynxVersionMatch = content.match(/pod\s+'Lynx',\s*'([^']+)'/);
|
|
3127
|
+
const lynxVersion = lynxVersionMatch?.[1] ?? "3.6.0";
|
|
3128
|
+
const xelementLine = `
|
|
3129
|
+
pod 'XElement', '${lynxVersion}'`;
|
|
3130
|
+
const insertAfter = /pod\s+'LynxService'[^\n]*(?:\n\s*'[^']*',?\s*)*/;
|
|
3131
|
+
const serviceMatch = content.match(insertAfter);
|
|
3132
|
+
if (serviceMatch) {
|
|
3133
|
+
const idx = serviceMatch.index + serviceMatch[0].length;
|
|
3134
|
+
content = content.slice(0, idx) + xelementLine + content.slice(idx);
|
|
3135
|
+
} else {
|
|
3136
|
+
content = content.replace(
|
|
3137
|
+
/(# GENERATED AUTOLINK DEPENDENCIES START)/,
|
|
3138
|
+
`pod 'XElement', '${lynxVersion}'
|
|
3139
|
+
|
|
3140
|
+
$1`
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
fs12.writeFileSync(podfilePath, content, "utf8");
|
|
3144
|
+
console.log(`\u2705 Added XElement pod (v${lynxVersion}) to Podfile`);
|
|
3145
|
+
}
|
|
3146
|
+
function ensureLynxPatchInPodfile() {
|
|
3147
|
+
const podfilePath = path13.join(iosProjectPath, "Podfile");
|
|
3148
|
+
if (!fs12.existsSync(podfilePath)) return;
|
|
3149
|
+
let content = fs12.readFileSync(podfilePath, "utf8");
|
|
3150
|
+
if (content.includes("content.gsub(/\\btypeof\\(/, '__typeof__(')")) return;
|
|
3151
|
+
const patch = `
|
|
3152
|
+
Dir.glob(File.join(installer.sandbox.root, 'Lynx/platform/darwin/**/*.{m,mm}')).each do |lynx_source|
|
|
3153
|
+
next unless File.file?(lynx_source)
|
|
3154
|
+
content = File.read(lynx_source)
|
|
3155
|
+
next unless content.match?(/\\btypeof\\(/)
|
|
3156
|
+
File.chmod(0644, lynx_source) rescue nil
|
|
3157
|
+
File.write(lynx_source, content.gsub(/\\btypeof\\(/, '__typeof__('))
|
|
3158
|
+
end`;
|
|
3159
|
+
content = content.replace(/(\n end\s*\n)(end\s*)$/, `$1${patch}
|
|
3160
|
+
$2`);
|
|
3161
|
+
fs12.writeFileSync(podfilePath, content, "utf8");
|
|
3162
|
+
console.log("\u2705 Added Lynx typeof patch to Podfile post_install.");
|
|
3163
|
+
}
|
|
3164
|
+
function ensurePodBuildSettings() {
|
|
3165
|
+
const podfilePath = path13.join(iosProjectPath, "Podfile");
|
|
3166
|
+
if (!fs12.existsSync(podfilePath)) return;
|
|
3167
|
+
let content = fs12.readFileSync(podfilePath, "utf8");
|
|
3168
|
+
let changed = false;
|
|
3169
|
+
if (!content.includes("CLANG_ENABLE_EXPLICIT_MODULES")) {
|
|
3170
|
+
content = content.replace(
|
|
3171
|
+
/config\.build_settings\['IPHONEOS_DEPLOYMENT_TARGET'\]\s*=\s*'[^']*'/,
|
|
3172
|
+
`$&
|
|
3173
|
+
config.build_settings['CLANG_ENABLE_EXPLICIT_MODULES'] = 'NO'
|
|
3174
|
+
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'`
|
|
3175
|
+
);
|
|
3176
|
+
changed = true;
|
|
3177
|
+
}
|
|
3178
|
+
if (!content.includes("gsub('-Werror'")) {
|
|
3179
|
+
const xcconfigStrip = `
|
|
3180
|
+
Dir.glob(File.join(installer.sandbox.root, 'Target Support Files', 'Lynx', '*.xcconfig')).each do |xcconfig_path|
|
|
3181
|
+
next unless File.file?(xcconfig_path)
|
|
3182
|
+
content = File.read(xcconfig_path)
|
|
3183
|
+
next unless content.include?('-Werror')
|
|
3184
|
+
File.write(xcconfig_path, content.gsub('-Werror', ''))
|
|
3185
|
+
end`;
|
|
3186
|
+
content = content.replace(
|
|
3187
|
+
/(Dir\.glob.*?Lynx\/platform\/darwin)/s,
|
|
3188
|
+
`${xcconfigStrip}
|
|
3189
|
+
$1`
|
|
3190
|
+
);
|
|
3191
|
+
changed = true;
|
|
3192
|
+
}
|
|
3193
|
+
if (changed) {
|
|
3194
|
+
fs12.writeFileSync(podfilePath, content, "utf8");
|
|
3195
|
+
console.log("\u2705 Added Xcode compatibility build settings to Podfile post_install.");
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3022
3198
|
function updateLynxInitProcessor(packages) {
|
|
3023
3199
|
const appNameFromConfig = resolved.config.ios?.appName;
|
|
3024
3200
|
const candidatePaths = [];
|
|
@@ -3029,6 +3205,30 @@ ${replacementBlock}
|
|
|
3029
3205
|
const found = candidatePaths.find((p) => fs12.existsSync(p));
|
|
3030
3206
|
const lynxInitPath = found ?? candidatePaths[0];
|
|
3031
3207
|
const iosPackages = packages.filter((p) => getIosModuleClassNames(p.config.ios).length > 0 || Object.keys(getIosElements(p.config.ios)).length > 0);
|
|
3208
|
+
const seenModules = /* @__PURE__ */ new Set();
|
|
3209
|
+
const seenElements = /* @__PURE__ */ new Set();
|
|
3210
|
+
const packagesWithContributions = /* @__PURE__ */ new Set();
|
|
3211
|
+
for (const pkg of iosPackages) {
|
|
3212
|
+
let hasUnique = false;
|
|
3213
|
+
for (const cls of getIosModuleClassNames(pkg.config.ios)) {
|
|
3214
|
+
if (!seenModules.has(cls)) {
|
|
3215
|
+
seenModules.add(cls);
|
|
3216
|
+
hasUnique = true;
|
|
3217
|
+
} else {
|
|
3218
|
+
console.warn(`\u26A0\uFE0F Skipping duplicate module "${cls}" from ${pkg.name} (already registered by another package)`);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
for (const tag of Object.keys(getIosElements(pkg.config.ios))) {
|
|
3222
|
+
if (!seenElements.has(tag)) {
|
|
3223
|
+
seenElements.add(tag);
|
|
3224
|
+
hasUnique = true;
|
|
3225
|
+
} else {
|
|
3226
|
+
console.warn(`\u26A0\uFE0F Skipping duplicate element "${tag}" from ${pkg.name} (already registered by another package)`);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
if (hasUnique) packagesWithContributions.add(pkg);
|
|
3230
|
+
}
|
|
3231
|
+
const importPackages = iosPackages.filter((p) => packagesWithContributions.has(p));
|
|
3032
3232
|
function updateImportsSection(filePath, pkgs) {
|
|
3033
3233
|
const startMarker = "// GENERATED IMPORTS START";
|
|
3034
3234
|
const endMarker = "// GENERATED IMPORTS END";
|
|
@@ -3081,19 +3281,29 @@ ${fileContent}`;
|
|
|
3081
3281
|
fs12.writeFileSync(filePath, newContent, "utf8");
|
|
3082
3282
|
console.log(`\u2705 Updated imports in ${path13.basename(filePath)}`);
|
|
3083
3283
|
}
|
|
3084
|
-
updateImportsSection(lynxInitPath,
|
|
3085
|
-
if (
|
|
3284
|
+
updateImportsSection(lynxInitPath, importPackages);
|
|
3285
|
+
if (importPackages.length === 0) {
|
|
3086
3286
|
const placeholder = " // No native modules found by Tamer4Lynx autolinker.";
|
|
3087
3287
|
updateGeneratedSection(lynxInitPath, placeholder, "// GENERATED AUTOLINK START", "// GENERATED AUTOLINK END");
|
|
3088
3288
|
return;
|
|
3089
3289
|
}
|
|
3090
|
-
const
|
|
3290
|
+
const seenModules2 = /* @__PURE__ */ new Set();
|
|
3291
|
+
const seenElements2 = /* @__PURE__ */ new Set();
|
|
3292
|
+
const blocks = importPackages.flatMap((pkg) => {
|
|
3091
3293
|
const classNames = getIosModuleClassNames(pkg.config.ios);
|
|
3092
|
-
const moduleBlocks = classNames.
|
|
3294
|
+
const moduleBlocks = classNames.filter((cls) => {
|
|
3295
|
+
if (seenModules2.has(cls)) return false;
|
|
3296
|
+
seenModules2.add(cls);
|
|
3297
|
+
return true;
|
|
3298
|
+
}).map((classNameRaw) => [
|
|
3093
3299
|
` // Register module from package: ${pkg.name}`,
|
|
3094
3300
|
` globalConfig.register(${classNameRaw}.self)`
|
|
3095
3301
|
].join("\n"));
|
|
3096
|
-
const elementBlocks = Object.entries(getIosElements(pkg.config.ios)).
|
|
3302
|
+
const elementBlocks = Object.entries(getIosElements(pkg.config.ios)).filter(([tagName]) => {
|
|
3303
|
+
if (seenElements2.has(tagName)) return false;
|
|
3304
|
+
seenElements2.add(tagName);
|
|
3305
|
+
return true;
|
|
3306
|
+
}).map(([tagName, classNameRaw]) => [
|
|
3097
3307
|
` // Register element from package: ${pkg.name}`,
|
|
3098
3308
|
` globalConfig.registerUI(${classNameRaw}.self, withName: "${tagName}")`
|
|
3099
3309
|
].join("\n"));
|
|
@@ -3202,7 +3412,12 @@ $1`
|
|
|
3202
3412
|
const cwd = path13.dirname(podfilePath);
|
|
3203
3413
|
try {
|
|
3204
3414
|
console.log(`\u2139\uFE0F Running \`pod install\` in ${cwd}...`);
|
|
3205
|
-
|
|
3415
|
+
try {
|
|
3416
|
+
execSync5("pod install", { cwd, stdio: "inherit" });
|
|
3417
|
+
} catch {
|
|
3418
|
+
console.log("\u2139\uFE0F Retrying `pod install` with repo update...");
|
|
3419
|
+
execSync5("pod install --repo-update", { cwd, stdio: "inherit" });
|
|
3420
|
+
}
|
|
3206
3421
|
console.log("\u2705 `pod install` completed successfully.");
|
|
3207
3422
|
} catch (e) {
|
|
3208
3423
|
console.warn(`\u26A0\uFE0F 'pod install' failed: ${e.message}`);
|
|
@@ -3218,6 +3433,9 @@ $1`
|
|
|
3218
3433
|
console.log("\u2139\uFE0F No Tamer4Lynx native packages found.");
|
|
3219
3434
|
}
|
|
3220
3435
|
updatePodfile(packages);
|
|
3436
|
+
ensureXElementPod();
|
|
3437
|
+
ensureLynxPatchInPodfile();
|
|
3438
|
+
ensurePodBuildSettings();
|
|
3221
3439
|
updateLynxInitProcessor(packages);
|
|
3222
3440
|
syncInfoPlistPermissions(packages);
|
|
3223
3441
|
syncInfoPlistUrlSchemes();
|
|
@@ -3335,7 +3553,7 @@ function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
|
|
|
3335
3553
|
function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
|
|
3336
3554
|
let content = fs13.readFileSync(pbxprojPath, "utf8");
|
|
3337
3555
|
const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3338
|
-
if (new RegExp(`path =
|
|
3556
|
+
if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
|
|
3339
3557
|
const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
|
|
3340
3558
|
const buildFileUUID = deterministicUUID(`buildFile:${appName}:${filename}`);
|
|
3341
3559
|
content = content.replace(
|
|
@@ -3364,7 +3582,7 @@ function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
|
|
|
3364
3582
|
function addResourceToXcodeProject(pbxprojPath, appName, filename) {
|
|
3365
3583
|
let content = fs13.readFileSync(pbxprojPath, "utf8");
|
|
3366
3584
|
const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3367
|
-
if (new RegExp(`path =
|
|
3585
|
+
if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
|
|
3368
3586
|
const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
|
|
3369
3587
|
const buildFileUUID = deterministicUUID(`buildFile:${appName}:${filename}`);
|
|
3370
3588
|
content = content.replace(
|
|
@@ -3813,7 +4031,11 @@ var bundle_default2 = bundleAndDeploy2;
|
|
|
3813
4031
|
// src/ios/build.ts
|
|
3814
4032
|
import fs15 from "fs";
|
|
3815
4033
|
import path16 from "path";
|
|
4034
|
+
import os3 from "os";
|
|
3816
4035
|
import { execSync as execSync7 } from "child_process";
|
|
4036
|
+
function hostArch() {
|
|
4037
|
+
return os3.arch() === "arm64" ? "arm64" : "x86_64";
|
|
4038
|
+
}
|
|
3817
4039
|
function findBootedSimulator() {
|
|
3818
4040
|
try {
|
|
3819
4041
|
const out = execSync7("xcrun simctl list devices --json", { encoding: "utf8" });
|
|
@@ -3844,10 +4066,16 @@ async function buildIpa(opts = {}) {
|
|
|
3844
4066
|
const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
|
|
3845
4067
|
const derivedDataPath = path16.join(iosDir, "build");
|
|
3846
4068
|
const sdk = opts.install ? "iphonesimulator" : "iphoneos";
|
|
4069
|
+
const signingArgs = opts.install ? "" : " CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO";
|
|
4070
|
+
const archFlag = opts.install ? `-arch ${hostArch()} ` : "";
|
|
4071
|
+
const extraSettings = [
|
|
4072
|
+
"ONLY_ACTIVE_ARCH=YES",
|
|
4073
|
+
"CLANG_ENABLE_EXPLICIT_MODULES=NO"
|
|
4074
|
+
].join(" ");
|
|
3847
4075
|
console.log(`
|
|
3848
4076
|
\u{1F528} Building ${configuration} (${sdk})...`);
|
|
3849
4077
|
execSync7(
|
|
3850
|
-
`xcodebuild ${flag} "${xcproject}" -scheme "${scheme}" -configuration ${configuration} -sdk ${sdk} -derivedDataPath "${derivedDataPath}"`,
|
|
4078
|
+
`xcodebuild ${flag} "${xcproject}" -scheme "${scheme}" -configuration ${configuration} -sdk ${sdk} ${archFlag}-derivedDataPath "${derivedDataPath}" ${extraSettings}${signingArgs}`,
|
|
3851
4079
|
{ stdio: "inherit", cwd: iosDir }
|
|
3852
4080
|
);
|
|
3853
4081
|
console.log(`\u2705 Build completed.`);
|
|
@@ -4184,12 +4412,12 @@ var codegen_default = codegen;
|
|
|
4184
4412
|
import { spawn } from "child_process";
|
|
4185
4413
|
import fs19 from "fs";
|
|
4186
4414
|
import http from "http";
|
|
4187
|
-
import
|
|
4415
|
+
import os4 from "os";
|
|
4188
4416
|
import path20 from "path";
|
|
4189
4417
|
import { WebSocketServer } from "ws";
|
|
4190
4418
|
var DEFAULT_PORT = 3e3;
|
|
4191
4419
|
function getLanIp() {
|
|
4192
|
-
const nets =
|
|
4420
|
+
const nets = os4.networkInterfaces();
|
|
4193
4421
|
for (const name of Object.keys(nets)) {
|
|
4194
4422
|
const addrs = nets[name];
|
|
4195
4423
|
if (!addrs) continue;
|
|
@@ -4558,6 +4786,8 @@ lynx = { module = "org.lynxsdk.lynx:lynx", version.ref = "lynx" }
|
|
|
4558
4786
|
lynx-jssdk = { module = "org.lynxsdk.lynx:lynx-jssdk", version.ref = "lynx" }
|
|
4559
4787
|
lynx-processor = { module = "org.lynxsdk.lynx:lynx-processor", version.ref = "lynx" }
|
|
4560
4788
|
lynx-trace = { module = "org.lynxsdk.lynx:lynx-trace", version.ref = "lynx" }
|
|
4789
|
+
lynx-xelement = { module = "org.lynxsdk.lynx:xelement", version.ref = "lynx" }
|
|
4790
|
+
lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref = "lynx" }
|
|
4561
4791
|
lynx-service-http = { module = "org.lynxsdk.lynx:lynx-service-http", version.ref = "lynx" }
|
|
4562
4792
|
lynx-service-image = { module = "org.lynxsdk.lynx:lynx-service-image", version.ref = "lynx" }
|
|
4563
4793
|
lynx-service-log = { module = "org.lynxsdk.lynx:lynx-service-log", version.ref = "lynx" }
|
|
@@ -4677,6 +4907,8 @@ dependencies {
|
|
|
4677
4907
|
implementation(libs.lynx)
|
|
4678
4908
|
implementation(libs.lynx.jssdk)
|
|
4679
4909
|
implementation(libs.lynx.trace)
|
|
4910
|
+
implementation(libs.lynx.xelement)
|
|
4911
|
+
implementation(libs.lynx.xelement.input)
|
|
4680
4912
|
implementation(libs.primjs)
|
|
4681
4913
|
implementation(libs.lynx.service.image)
|
|
4682
4914
|
implementation(libs.lynx.service.http)
|
|
@@ -4950,6 +5182,9 @@ var CORE_PACKAGES = [
|
|
|
4950
5182
|
"@tamer4lynx/tamer-system-ui",
|
|
4951
5183
|
"@tamer4lynx/tamer-icons"
|
|
4952
5184
|
];
|
|
5185
|
+
var PACKAGE_ALIASES = {
|
|
5186
|
+
input: "@tamer4lynx/tamer-text-input"
|
|
5187
|
+
};
|
|
4953
5188
|
function detectPackageManager(cwd) {
|
|
4954
5189
|
const dir = path23.resolve(cwd);
|
|
4955
5190
|
if (fs22.existsSync(path23.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
@@ -4979,9 +5214,10 @@ function add(packages = []) {
|
|
|
4979
5214
|
}
|
|
4980
5215
|
const { lynxProjectDir } = resolveHostPaths();
|
|
4981
5216
|
const pm = detectPackageManager(lynxProjectDir);
|
|
4982
|
-
const normalized = list.map(
|
|
4983
|
-
(p
|
|
4984
|
-
|
|
5217
|
+
const normalized = list.map((p) => {
|
|
5218
|
+
if (p.startsWith("@")) return p;
|
|
5219
|
+
return PACKAGE_ALIASES[p] ?? `@tamer4lynx/${p}`;
|
|
5220
|
+
});
|
|
4985
5221
|
console.log(`Adding ${normalized.join(", ")} to ${lynxProjectDir} (using ${pm})...`);
|
|
4986
5222
|
runInstall(lynxProjectDir, normalized, pm);
|
|
4987
5223
|
console.log("\u2705 Packages installed. Run `t4l link` to link native modules.");
|
|
@@ -5104,7 +5340,7 @@ program.command("build-dev-app").option("-p, --platform <platform>", "Platform:
|
|
|
5104
5340
|
}
|
|
5105
5341
|
});
|
|
5106
5342
|
program.command("add [packages...]").description("Add @tamer4lynx packages to the Lynx project. Future: will track versions for compatibility (Expo-style).").action((packages) => add(packages));
|
|
5107
|
-
program.command("add-core").description("Add core packages (app-shell, screen, router, insets, transports, text-input, system-ui, icons)").action(() => addCore());
|
|
5343
|
+
program.command("add-core").description("Add core packages (app-shell, screen, router, insets, transports, input/text-input, system-ui, icons)").action(() => addCore());
|
|
5108
5344
|
program.command("create").description("Create a new Lynx extension project (RFC-compliant)").action(() => create_default3());
|
|
5109
5345
|
program.command("codegen").description("Generate code from @lynxmodule declarations").action(() => {
|
|
5110
5346
|
codegen_default();
|