@tamer4lynx/cli 0.0.14 → 0.0.16

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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/dist/index.js +542 -434
  3. package/package.json +9 -1
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ process.on("warning", (w) => {
7
7
  });
8
8
 
9
9
  // index.ts
10
- import fs28 from "fs";
11
- import path29 from "path";
10
+ import fs29 from "fs";
11
+ import path30 from "path";
12
12
  import { fileURLToPath } from "url";
13
13
  import { program } from "commander";
14
14
 
@@ -2229,8 +2229,8 @@ $1$2`
2229
2229
  var autolink_default = autolink;
2230
2230
 
2231
2231
  // src/android/bundle.ts
2232
- import fs10 from "fs";
2233
- import path10 from "path";
2232
+ import fs11 from "fs";
2233
+ import path11 from "path";
2234
2234
  import { execSync as execSync3 } from "child_process";
2235
2235
 
2236
2236
  // src/common/copyDistAssets.ts
@@ -2256,19 +2256,117 @@ function copyDistAssets(distDir, destDir, bundleFile) {
2256
2256
  }
2257
2257
  }
2258
2258
 
2259
- // src/android/syncDevClient.ts
2259
+ // src/common/tsconfigUtils.ts
2260
2260
  import fs9 from "fs";
2261
2261
  import path9 from "path";
2262
+ function stripJsonCommentsAndTrailingCommas(str) {
2263
+ return str.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/,\s*([\]}])/g, "$1");
2264
+ }
2265
+ function parseTsconfigJson(raw) {
2266
+ try {
2267
+ return JSON.parse(raw);
2268
+ } catch {
2269
+ return JSON.parse(stripJsonCommentsAndTrailingCommas(raw));
2270
+ }
2271
+ }
2272
+ function readTsconfig(filePath) {
2273
+ if (!fs9.existsSync(filePath)) return null;
2274
+ try {
2275
+ return parseTsconfigJson(fs9.readFileSync(filePath, "utf-8"));
2276
+ } catch {
2277
+ return null;
2278
+ }
2279
+ }
2280
+ function resolveExtends(tsconfig, dir) {
2281
+ if (!tsconfig.extends) return tsconfig;
2282
+ const basePath = path9.resolve(dir, tsconfig.extends);
2283
+ const base = readTsconfig(basePath);
2284
+ if (!base) return tsconfig;
2285
+ const baseDir = path9.dirname(basePath);
2286
+ const resolved = resolveExtends(base, baseDir);
2287
+ return {
2288
+ ...resolved,
2289
+ ...tsconfig,
2290
+ compilerOptions: { ...resolved.compilerOptions, ...tsconfig.compilerOptions }
2291
+ };
2292
+ }
2293
+ function fixTsconfigReferencesForBuild(tsconfigPath) {
2294
+ const dir = path9.dirname(tsconfigPath);
2295
+ const tsconfig = readTsconfig(tsconfigPath);
2296
+ if (!tsconfig?.references?.length) return false;
2297
+ const refsWithNoEmit = [];
2298
+ for (const ref of tsconfig.references) {
2299
+ const refPath = path9.resolve(dir, ref.path);
2300
+ const refConfigPath = refPath.endsWith(".json") ? refPath : path9.join(refPath, "tsconfig.json");
2301
+ const refConfig = readTsconfig(refConfigPath);
2302
+ if (refConfig?.compilerOptions?.noEmit === true) {
2303
+ refsWithNoEmit.push(refConfigPath);
2304
+ }
2305
+ }
2306
+ if (refsWithNoEmit.length === 0) return false;
2307
+ const merged = {
2308
+ ...tsconfig,
2309
+ references: void 0,
2310
+ files: void 0
2311
+ };
2312
+ const includes = [];
2313
+ const compilerOpts = { ...tsconfig.compilerOptions };
2314
+ for (const ref of tsconfig.references) {
2315
+ const refPath = path9.resolve(dir, ref.path);
2316
+ const refConfigPath = refPath.endsWith(".json") ? refPath : path9.join(refPath, "tsconfig.json");
2317
+ const refConfig = readTsconfig(refConfigPath);
2318
+ if (!refConfig) continue;
2319
+ const refDir = path9.dirname(refConfigPath);
2320
+ const resolved = resolveExtends(refConfig, refDir);
2321
+ const inc = resolved.include;
2322
+ if (inc) {
2323
+ const arr = Array.isArray(inc) ? inc : [inc];
2324
+ const baseDir = path9.relative(dir, refDir);
2325
+ for (const p of arr) {
2326
+ const clean = typeof p === "string" && p.startsWith("./") ? p.slice(2) : p;
2327
+ includes.push(!baseDir || baseDir === "." ? clean : `${baseDir}/${clean}`);
2328
+ }
2329
+ }
2330
+ const opts = resolved.compilerOptions;
2331
+ if (opts) {
2332
+ for (const [k, v] of Object.entries(opts)) {
2333
+ if (k !== "composite" && k !== "noEmit" && compilerOpts[k] === void 0) {
2334
+ compilerOpts[k] = v;
2335
+ }
2336
+ }
2337
+ }
2338
+ }
2339
+ if (includes.length > 0) merged.include = [...new Set(includes)];
2340
+ compilerOpts.noEmit = true;
2341
+ merged.compilerOptions = compilerOpts;
2342
+ fs9.writeFileSync(tsconfigPath, JSON.stringify(merged, null, 2));
2343
+ return true;
2344
+ }
2345
+ function addTamerTypesInclude(tsconfigPath, tamerTypesInclude) {
2346
+ const tsconfig = readTsconfig(tsconfigPath);
2347
+ if (!tsconfig) return false;
2348
+ const include = tsconfig.include ?? [];
2349
+ const arr = Array.isArray(include) ? include : [include];
2350
+ if (arr.some((p) => (typeof p === "string" ? p : "").includes("tamer-"))) return false;
2351
+ arr.push(tamerTypesInclude);
2352
+ tsconfig.include = arr;
2353
+ fs9.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
2354
+ return true;
2355
+ }
2356
+
2357
+ // src/android/syncDevClient.ts
2358
+ import fs10 from "fs";
2359
+ import path10 from "path";
2262
2360
  function readAndSubstituteTemplate2(templatePath, vars) {
2263
- const raw = fs9.readFileSync(templatePath, "utf-8");
2361
+ const raw = fs10.readFileSync(templatePath, "utf-8");
2264
2362
  return Object.entries(vars).reduce(
2265
2363
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2266
2364
  raw
2267
2365
  );
2268
2366
  }
2269
2367
  function patchAppLogService(appPath) {
2270
- if (!fs9.existsSync(appPath)) return;
2271
- const raw = fs9.readFileSync(appPath, "utf-8");
2368
+ if (!fs10.existsSync(appPath)) return;
2369
+ const raw = fs10.readFileSync(appPath, "utf-8");
2272
2370
  const patched = raw.replace(
2273
2371
  /private void initLynxService\(\)\s*\{[\s\S]*?\n\s*}\s*\n\s*private void initFresco\(\)/,
2274
2372
  `private void initLynxService() {
@@ -2287,7 +2385,7 @@ function patchAppLogService(appPath) {
2287
2385
  private void initFresco()`
2288
2386
  );
2289
2387
  if (patched !== raw) {
2290
- fs9.writeFileSync(appPath, patched);
2388
+ fs10.writeFileSync(appPath, patched);
2291
2389
  }
2292
2390
  }
2293
2391
  async function syncDevClient(opts) {
@@ -2302,9 +2400,9 @@ async function syncDevClient(opts) {
2302
2400
  const packageName = config.android?.packageName;
2303
2401
  const appName = config.android?.appName;
2304
2402
  const packagePath = packageName.replace(/\./g, "/");
2305
- const javaDir = path9.join(rootDir, "app", "src", "main", "java", packagePath);
2306
- const kotlinDir = path9.join(rootDir, "app", "src", "main", "kotlin", packagePath);
2307
- if (!fs9.existsSync(javaDir) || !fs9.existsSync(kotlinDir)) {
2403
+ const javaDir = path10.join(rootDir, "app", "src", "main", "java", packagePath);
2404
+ const kotlinDir = path10.join(rootDir, "app", "src", "main", "kotlin", packagePath);
2405
+ if (!fs10.existsSync(javaDir) || !fs10.existsSync(kotlinDir)) {
2308
2406
  console.error("\u274C Android project not found. Run `tamer android create` first.");
2309
2407
  process.exit(1);
2310
2408
  }
@@ -2320,14 +2418,14 @@ async function syncDevClient(opts) {
2320
2418
  const [templateProviderSource] = await Promise.all([
2321
2419
  fetchAndPatchTemplateProvider(vars)
2322
2420
  ]);
2323
- fs9.writeFileSync(path9.join(javaDir, "TemplateProvider.java"), templateProviderSource);
2324
- fs9.writeFileSync(path9.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
2325
- patchAppLogService(path9.join(javaDir, "App.java"));
2326
- const appDir = path9.join(rootDir, "app");
2327
- const mainDir = path9.join(appDir, "src", "main");
2328
- const manifestPath = path9.join(mainDir, "AndroidManifest.xml");
2421
+ fs10.writeFileSync(path10.join(javaDir, "TemplateProvider.java"), templateProviderSource);
2422
+ fs10.writeFileSync(path10.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
2423
+ patchAppLogService(path10.join(javaDir, "App.java"));
2424
+ const appDir = path10.join(rootDir, "app");
2425
+ const mainDir = path10.join(appDir, "src", "main");
2426
+ const manifestPath = path10.join(mainDir, "AndroidManifest.xml");
2329
2427
  if (hasDevClient) {
2330
- const templateDir = path9.join(devClientPkg, "android", "templates");
2428
+ const templateDir = path10.join(devClientPkg, "android", "templates");
2331
2429
  const templateVars = { PACKAGE_NAME: packageName, APP_NAME: appName };
2332
2430
  const devClientFiles = [
2333
2431
  "DevClientManager.kt",
@@ -2336,13 +2434,13 @@ async function syncDevClient(opts) {
2336
2434
  "PortraitCaptureActivity.kt"
2337
2435
  ];
2338
2436
  for (const f of devClientFiles) {
2339
- const src = path9.join(templateDir, f);
2340
- if (fs9.existsSync(src)) {
2437
+ const src = path10.join(templateDir, f);
2438
+ if (fs10.existsSync(src)) {
2341
2439
  const content = readAndSubstituteTemplate2(src, templateVars);
2342
- fs9.writeFileSync(path9.join(kotlinDir, f), content);
2440
+ fs10.writeFileSync(path10.join(kotlinDir, f), content);
2343
2441
  }
2344
2442
  }
2345
- let manifest = fs9.readFileSync(manifestPath, "utf-8");
2443
+ let manifest = fs10.readFileSync(manifestPath, "utf-8");
2346
2444
  const projectActivityEntry = ' <activity android:name=".ProjectActivity" android:exported="false" android:taskAffinity="" android:launchMode="singleTask" android:documentLaunchMode="always" android:windowSoftInputMode="adjustResize" />';
2347
2445
  const portraitCaptureEntry = ' <activity android:name=".PortraitCaptureActivity" android:screenOrientation="portrait" android:stateNotNeeded="true" android:theme="@style/zxing_CaptureTheme" android:windowSoftInputMode="stateAlwaysHidden" />';
2348
2446
  if (!manifest.includes("ProjectActivity")) {
@@ -2364,16 +2462,16 @@ $1$2`);
2364
2462
  '$1 android:windowSoftInputMode="adjustResize"$2'
2365
2463
  );
2366
2464
  }
2367
- fs9.writeFileSync(manifestPath, manifest);
2465
+ fs10.writeFileSync(manifestPath, manifest);
2368
2466
  console.log("\u2705 Synced dev client (TemplateProvider, MainActivity, ProjectActivity, DevClientManager)");
2369
2467
  } else {
2370
2468
  for (const f of ["DevClientManager.kt", "DevServerPrefs.kt", "ProjectActivity.kt", "PortraitCaptureActivity.kt", "DevLauncherActivity.kt"]) {
2371
2469
  try {
2372
- fs9.rmSync(path9.join(kotlinDir, f));
2470
+ fs10.rmSync(path10.join(kotlinDir, f));
2373
2471
  } catch {
2374
2472
  }
2375
2473
  }
2376
- let manifest = fs9.readFileSync(manifestPath, "utf-8");
2474
+ let manifest = fs10.readFileSync(manifestPath, "utf-8");
2377
2475
  manifest = manifest.replace(/\s*<activity android:name="\.ProjectActivity"[^\/]*\/>\n?/g, "");
2378
2476
  manifest = manifest.replace(/\s*<activity android:name="\.PortraitCaptureActivity"[^\/]*\/>\n?/g, "");
2379
2477
  const mainActivityTag = manifest.match(/<activity[^>]*android:name="\.MainActivity"[^>]*>/);
@@ -2383,7 +2481,7 @@ $1$2`);
2383
2481
  '$1 android:windowSoftInputMode="adjustResize"$2'
2384
2482
  );
2385
2483
  }
2386
- fs9.writeFileSync(manifestPath, manifest);
2484
+ fs10.writeFileSync(manifestPath, manifest);
2387
2485
  console.log("\u2705 Synced (dev client disabled - use -d for debug build with dev client)");
2388
2486
  }
2389
2487
  }
@@ -2407,13 +2505,17 @@ async function bundleAndDeploy(opts = {}) {
2407
2505
  await syncDevClient_default({ includeDevClient });
2408
2506
  const iconPaths = resolveIconPaths(projectRoot, resolved.config);
2409
2507
  if (iconPaths) {
2410
- const resDir = path10.join(resolved.androidAppDir, "src", "main", "res");
2508
+ const resDir = path11.join(resolved.androidAppDir, "src", "main", "res");
2411
2509
  if (applyAndroidLauncherIcons(resDir, iconPaths)) {
2412
2510
  console.log("\u2705 Synced Android launcher icon(s) from tamer.config.json");
2413
- ensureAndroidManifestLauncherIcon(path10.join(resolved.androidAppDir, "src", "main", "AndroidManifest.xml"));
2511
+ ensureAndroidManifestLauncherIcon(path11.join(resolved.androidAppDir, "src", "main", "AndroidManifest.xml"));
2414
2512
  }
2415
2513
  }
2416
2514
  try {
2515
+ const lynxTsconfig = path11.join(lynxProjectDir, "tsconfig.json");
2516
+ if (fs11.existsSync(lynxTsconfig)) {
2517
+ fixTsconfigReferencesForBuild(lynxTsconfig);
2518
+ }
2417
2519
  console.log("\u{1F4E6} Building Lynx bundle...");
2418
2520
  execSync3("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
2419
2521
  console.log("\u2705 Build completed successfully.");
@@ -2421,8 +2523,8 @@ async function bundleAndDeploy(opts = {}) {
2421
2523
  console.error("\u274C Build process failed.");
2422
2524
  process.exit(1);
2423
2525
  }
2424
- if (includeDevClient && devClientBundlePath && !fs10.existsSync(devClientBundlePath)) {
2425
- const devClientDir = path10.dirname(path10.dirname(devClientBundlePath));
2526
+ if (includeDevClient && devClientBundlePath && !fs11.existsSync(devClientBundlePath)) {
2527
+ const devClientDir = path11.dirname(path11.dirname(devClientBundlePath));
2426
2528
  try {
2427
2529
  console.log("\u{1F4E6} Building dev launcher (tamer-dev-client)...");
2428
2530
  execSync3("npm run build", { stdio: "inherit", cwd: devClientDir });
@@ -2433,22 +2535,22 @@ async function bundleAndDeploy(opts = {}) {
2433
2535
  }
2434
2536
  }
2435
2537
  try {
2436
- fs10.mkdirSync(destinationDir, { recursive: true });
2538
+ fs11.mkdirSync(destinationDir, { recursive: true });
2437
2539
  if (release) {
2438
- const devClientAsset = path10.join(destinationDir, "dev-client.lynx.bundle");
2439
- if (fs10.existsSync(devClientAsset)) {
2440
- fs10.rmSync(devClientAsset);
2540
+ const devClientAsset = path11.join(destinationDir, "dev-client.lynx.bundle");
2541
+ if (fs11.existsSync(devClientAsset)) {
2542
+ fs11.rmSync(devClientAsset);
2441
2543
  console.log(`\u2728 Removed dev-client.lynx.bundle from assets (production build)`);
2442
2544
  }
2443
- } else if (includeDevClient && devClientBundlePath && fs10.existsSync(devClientBundlePath)) {
2444
- fs10.copyFileSync(devClientBundlePath, path10.join(destinationDir, "dev-client.lynx.bundle"));
2545
+ } else if (includeDevClient && devClientBundlePath && fs11.existsSync(devClientBundlePath)) {
2546
+ fs11.copyFileSync(devClientBundlePath, path11.join(destinationDir, "dev-client.lynx.bundle"));
2445
2547
  console.log(`\u2728 Copied dev-client.lynx.bundle to assets`);
2446
2548
  }
2447
- if (!fs10.existsSync(lynxBundlePath)) {
2549
+ if (!fs11.existsSync(lynxBundlePath)) {
2448
2550
  console.error(`\u274C Build output not found at: ${lynxBundlePath}`);
2449
2551
  process.exit(1);
2450
2552
  }
2451
- const distDir = path10.dirname(lynxBundlePath);
2553
+ const distDir = path11.dirname(lynxBundlePath);
2452
2554
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
2453
2555
  console.log(`\u2728 Copied ${resolved.lynxBundleFile} to assets`);
2454
2556
  } catch (error) {
@@ -2459,7 +2561,7 @@ async function bundleAndDeploy(opts = {}) {
2459
2561
  var bundle_default = bundleAndDeploy;
2460
2562
 
2461
2563
  // src/android/build.ts
2462
- import path11 from "path";
2564
+ import path12 from "path";
2463
2565
  import { execSync as execSync4 } from "child_process";
2464
2566
  async function buildApk(opts = {}) {
2465
2567
  let resolved;
@@ -2471,7 +2573,7 @@ async function buildApk(opts = {}) {
2471
2573
  const release = opts.release === true || opts.production === true;
2472
2574
  await bundle_default({ release, production: opts.production });
2473
2575
  const androidDir = resolved.androidDir;
2474
- const gradlew = path11.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2576
+ const gradlew = path12.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2475
2577
  const variant = release ? "Release" : "Debug";
2476
2578
  const task = opts.install ? `install${variant}` : `assemble${variant}`;
2477
2579
  console.log(`
@@ -2496,13 +2598,13 @@ async function buildApk(opts = {}) {
2496
2598
  var build_default = buildApk;
2497
2599
 
2498
2600
  // src/ios/create.ts
2499
- import fs12 from "fs";
2500
- import path13 from "path";
2601
+ import fs13 from "fs";
2602
+ import path14 from "path";
2501
2603
 
2502
2604
  // src/ios/getPod.ts
2503
2605
  import { execSync as execSync5 } from "child_process";
2504
- import fs11 from "fs";
2505
- import path12 from "path";
2606
+ import fs12 from "fs";
2607
+ import path13 from "path";
2506
2608
  function isCocoaPodsInstalled() {
2507
2609
  try {
2508
2610
  execSync5("command -v pod >/dev/null 2>&1");
@@ -2524,8 +2626,8 @@ async function setupCocoaPods(rootDir) {
2524
2626
  }
2525
2627
  try {
2526
2628
  console.log("\u{1F4E6} CocoaPods is installed. Proceeding with dependency installation...");
2527
- const podfilePath = path12.join(rootDir, "Podfile");
2528
- if (!fs11.existsSync(podfilePath)) {
2629
+ const podfilePath = path13.join(rootDir, "Podfile");
2630
+ if (!fs12.existsSync(podfilePath)) {
2529
2631
  throw new Error(`Podfile not found at ${podfilePath}`);
2530
2632
  }
2531
2633
  console.log(`\u{1F680} Executing pod install in: ${rootDir}`);
@@ -2551,7 +2653,7 @@ async function setupCocoaPods(rootDir) {
2551
2653
  // src/ios/create.ts
2552
2654
  import { randomBytes } from "crypto";
2553
2655
  function readAndSubstituteTemplate3(templatePath, vars) {
2554
- const raw = fs12.readFileSync(templatePath, "utf-8");
2656
+ const raw = fs13.readFileSync(templatePath, "utf-8");
2555
2657
  return Object.entries(vars).reduce(
2556
2658
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2557
2659
  raw
@@ -2574,17 +2676,17 @@ var create2 = () => {
2574
2676
  process.exit(1);
2575
2677
  }
2576
2678
  const iosDir = config.paths?.iosDir ?? "ios";
2577
- const rootDir = path13.join(process.cwd(), iosDir);
2578
- const projectDir = path13.join(rootDir, appName);
2579
- const xcodeprojDir = path13.join(rootDir, `${appName}.xcodeproj`);
2679
+ const rootDir = path14.join(process.cwd(), iosDir);
2680
+ const projectDir = path14.join(rootDir, appName);
2681
+ const xcodeprojDir = path14.join(rootDir, `${appName}.xcodeproj`);
2580
2682
  const bridgingHeader = `${appName}-Bridging-Header.h`;
2581
2683
  function writeFile2(filePath, content) {
2582
- fs12.mkdirSync(path13.dirname(filePath), { recursive: true });
2583
- fs12.writeFileSync(filePath, content.trimStart(), "utf8");
2684
+ fs13.mkdirSync(path14.dirname(filePath), { recursive: true });
2685
+ fs13.writeFileSync(filePath, content.trimStart(), "utf8");
2584
2686
  }
2585
- if (fs12.existsSync(rootDir)) {
2687
+ if (fs13.existsSync(rootDir)) {
2586
2688
  console.log(`\u{1F9F9} Removing existing directory: ${rootDir}`);
2587
- fs12.rmSync(rootDir, { recursive: true, force: true });
2689
+ fs13.rmSync(rootDir, { recursive: true, force: true });
2588
2690
  }
2589
2691
  console.log(`\u{1F680} Creating a new Tamer4Lynx project in: ${rootDir}`);
2590
2692
  const ids = {
@@ -2620,7 +2722,7 @@ var create2 = () => {
2620
2722
  targetDebugConfig: generateId(),
2621
2723
  targetReleaseConfig: generateId()
2622
2724
  };
2623
- writeFile2(path13.join(rootDir, "Podfile"), `
2725
+ writeFile2(path14.join(rootDir, "Podfile"), `
2624
2726
  source 'https://cdn.cocoapods.org/'
2625
2727
 
2626
2728
  platform :ios, '13.0'
@@ -2705,15 +2807,15 @@ end
2705
2807
  const hostPkg = findTamerHostPackage(process.cwd());
2706
2808
  const templateVars = { PACKAGE_NAME: bundleId, APP_NAME: appName, BUNDLE_ID: bundleId };
2707
2809
  if (hostPkg) {
2708
- const templateDir = path13.join(hostPkg, "ios", "templates");
2810
+ const templateDir = path14.join(hostPkg, "ios", "templates");
2709
2811
  for (const f of ["AppDelegate.swift", "SceneDelegate.swift", "ViewController.swift", "LynxProvider.swift", "LynxInitProcessor.swift"]) {
2710
- const srcPath = path13.join(templateDir, f);
2711
- if (fs12.existsSync(srcPath)) {
2712
- writeFile2(path13.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2812
+ const srcPath = path14.join(templateDir, f);
2813
+ if (fs13.existsSync(srcPath)) {
2814
+ writeFile2(path14.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2713
2815
  }
2714
2816
  }
2715
2817
  } else {
2716
- writeFile2(path13.join(projectDir, "AppDelegate.swift"), `
2818
+ writeFile2(path14.join(projectDir, "AppDelegate.swift"), `
2717
2819
  import UIKit
2718
2820
 
2719
2821
  @UIApplicationMain
@@ -2728,7 +2830,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
2728
2830
  }
2729
2831
  }
2730
2832
  `);
2731
- writeFile2(path13.join(projectDir, "SceneDelegate.swift"), `
2833
+ writeFile2(path14.join(projectDir, "SceneDelegate.swift"), `
2732
2834
  import UIKit
2733
2835
 
2734
2836
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -2742,7 +2844,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2742
2844
  }
2743
2845
  }
2744
2846
  `);
2745
- writeFile2(path13.join(projectDir, "ViewController.swift"), `
2847
+ writeFile2(path14.join(projectDir, "ViewController.swift"), `
2746
2848
  import UIKit
2747
2849
  import Lynx
2748
2850
  import tamerinsets
@@ -2758,7 +2860,9 @@ class ViewController: UIViewController {
2758
2860
  additionalSafeAreaInsets = .zero
2759
2861
  view.insetsLayoutMarginsFromSafeArea = false
2760
2862
  view.preservesSuperviewLayoutMargins = false
2761
- viewRespectsSystemMinimumLayoutMargins = false
2863
+ if #available(iOS 15.0, *) {
2864
+ viewRespectsSystemMinimumLayoutMargins = false
2865
+ }
2762
2866
  }
2763
2867
 
2764
2868
  override func viewDidLayoutSubviews() {
@@ -2813,7 +2917,7 @@ class ViewController: UIViewController {
2813
2917
  }
2814
2918
  }
2815
2919
  `);
2816
- writeFile2(path13.join(projectDir, "LynxProvider.swift"), `
2920
+ writeFile2(path14.join(projectDir, "LynxProvider.swift"), `
2817
2921
  import Foundation
2818
2922
 
2819
2923
  class LynxProvider: NSObject, LynxTemplateProvider {
@@ -2832,7 +2936,7 @@ class LynxProvider: NSObject, LynxTemplateProvider {
2832
2936
  }
2833
2937
  }
2834
2938
  `);
2835
- writeFile2(path13.join(projectDir, "LynxInitProcessor.swift"), `
2939
+ writeFile2(path14.join(projectDir, "LynxInitProcessor.swift"), `
2836
2940
  // Copyright 2024 The Lynx Authors. All rights reserved.
2837
2941
  // Licensed under the Apache License Version 2.0 that can be found in the
2838
2942
  // LICENSE file in the root directory of this source tree.
@@ -2872,7 +2976,7 @@ final class LynxInitProcessor {
2872
2976
  }
2873
2977
  `);
2874
2978
  }
2875
- writeFile2(path13.join(projectDir, bridgingHeader), `
2979
+ writeFile2(path14.join(projectDir, bridgingHeader), `
2876
2980
  #import <Lynx/LynxConfig.h>
2877
2981
  #import <Lynx/LynxEnv.h>
2878
2982
  #import <Lynx/LynxTemplateProvider.h>
@@ -2881,7 +2985,7 @@ final class LynxInitProcessor {
2881
2985
  #import <SDWebImage/SDWebImage.h>
2882
2986
  #import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
2883
2987
  `);
2884
- writeFile2(path13.join(projectDir, "Info.plist"), `
2988
+ writeFile2(path14.join(projectDir, "Info.plist"), `
2885
2989
  <?xml version="1.0" encoding="UTF-8"?>
2886
2990
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2887
2991
  <plist version="1.0">
@@ -2939,21 +3043,21 @@ final class LynxInitProcessor {
2939
3043
  </dict>
2940
3044
  </plist>
2941
3045
  `);
2942
- const appIconDir = path13.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
2943
- fs12.mkdirSync(appIconDir, { recursive: true });
3046
+ const appIconDir = path14.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
3047
+ fs13.mkdirSync(appIconDir, { recursive: true });
2944
3048
  const iconPaths = resolveIconPaths(process.cwd(), config);
2945
3049
  if (applyIosAppIconAssets(appIconDir, iconPaths)) {
2946
3050
  console.log(iconPaths?.ios ? "\u2705 Copied iOS icon from tamer.config.json icon.ios" : "\u2705 Copied app icon from tamer.config.json icon.source");
2947
3051
  } else {
2948
- writeFile2(path13.join(appIconDir, "Contents.json"), `
3052
+ writeFile2(path14.join(appIconDir, "Contents.json"), `
2949
3053
  {
2950
3054
  "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ],
2951
3055
  "info" : { "author" : "xcode", "version" : 1 }
2952
3056
  }
2953
3057
  `);
2954
3058
  }
2955
- fs12.mkdirSync(xcodeprojDir, { recursive: true });
2956
- writeFile2(path13.join(xcodeprojDir, "project.pbxproj"), `
3059
+ fs13.mkdirSync(xcodeprojDir, { recursive: true });
3060
+ writeFile2(path14.join(xcodeprojDir, "project.pbxproj"), `
2957
3061
  // !$*UTF8*$!
2958
3062
  {
2959
3063
  archiveVersion = 1;
@@ -3239,8 +3343,8 @@ final class LynxInitProcessor {
3239
3343
  var create_default2 = create2;
3240
3344
 
3241
3345
  // src/ios/autolink.ts
3242
- import fs14 from "fs";
3243
- import path15 from "path";
3346
+ import fs15 from "fs";
3347
+ import path16 from "path";
3244
3348
  import { execSync as execSync6 } from "child_process";
3245
3349
 
3246
3350
  // src/common/hostNativeModulesManifest.ts
@@ -3251,8 +3355,8 @@ function buildHostNativeModulesManifestJson(moduleClassNames) {
3251
3355
  }
3252
3356
 
3253
3357
  // src/ios/syncHost.ts
3254
- import fs13 from "fs";
3255
- import path14 from "path";
3358
+ import fs14 from "fs";
3359
+ import path15 from "path";
3256
3360
  import crypto from "crypto";
3257
3361
  function deterministicUUID(seed) {
3258
3362
  return crypto.createHash("sha256").update(seed).digest("hex").substring(0, 24).toUpperCase();
@@ -3300,7 +3404,7 @@ function getLaunchScreenStoryboard() {
3300
3404
  `;
3301
3405
  }
3302
3406
  function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3303
- let content = fs13.readFileSync(pbxprojPath, "utf8");
3407
+ let content = fs14.readFileSync(pbxprojPath, "utf8");
3304
3408
  if (content.includes("LaunchScreen.storyboard")) return;
3305
3409
  const baseFileRefUUID = deterministicUUID(`launchScreenBase:${appName}`);
3306
3410
  const variantGroupUUID = deterministicUUID(`launchScreenGroup:${appName}`);
@@ -3337,11 +3441,11 @@ function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3337
3441
  );
3338
3442
  content = content.replace(groupPattern, `$1
3339
3443
  ${variantGroupUUID} /* LaunchScreen.storyboard */,`);
3340
- fs13.writeFileSync(pbxprojPath, content, "utf8");
3444
+ fs14.writeFileSync(pbxprojPath, content, "utf8");
3341
3445
  console.log("\u2705 Registered LaunchScreen.storyboard in Xcode project");
3342
3446
  }
3343
3447
  function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3344
- let content = fs13.readFileSync(pbxprojPath, "utf8");
3448
+ let content = fs14.readFileSync(pbxprojPath, "utf8");
3345
3449
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3346
3450
  if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
3347
3451
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3366,11 +3470,11 @@ function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3366
3470
  );
3367
3471
  content = content.replace(groupPattern, `$1
3368
3472
  ${fileRefUUID} /* ${filename} */,`);
3369
- fs13.writeFileSync(pbxprojPath, content, "utf8");
3473
+ fs14.writeFileSync(pbxprojPath, content, "utf8");
3370
3474
  console.log(`\u2705 Registered ${filename} in Xcode project sources`);
3371
3475
  }
3372
3476
  function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3373
- let content = fs13.readFileSync(pbxprojPath, "utf8");
3477
+ let content = fs14.readFileSync(pbxprojPath, "utf8");
3374
3478
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3375
3479
  if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
3376
3480
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3395,12 +3499,12 @@ function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3395
3499
  );
3396
3500
  content = content.replace(groupPattern, `$1
3397
3501
  ${fileRefUUID} /* ${filename} */,`);
3398
- fs13.writeFileSync(pbxprojPath, content, "utf8");
3502
+ fs14.writeFileSync(pbxprojPath, content, "utf8");
3399
3503
  console.log(`\u2705 Registered ${filename} in Xcode project resources`);
3400
3504
  }
3401
3505
  function writeFile(filePath, content) {
3402
- fs13.mkdirSync(path14.dirname(filePath), { recursive: true });
3403
- fs13.writeFileSync(filePath, content, "utf8");
3506
+ fs14.mkdirSync(path15.dirname(filePath), { recursive: true });
3507
+ fs14.writeFileSync(filePath, content, "utf8");
3404
3508
  }
3405
3509
  function getAppDelegateSwift() {
3406
3510
  return `import UIKit
@@ -3460,7 +3564,9 @@ class ViewController: UIViewController {
3460
3564
  additionalSafeAreaInsets = .zero
3461
3565
  view.insetsLayoutMarginsFromSafeArea = false
3462
3566
  view.preservesSuperviewLayoutMargins = false
3463
- viewRespectsSystemMinimumLayoutMargins = false
3567
+ if #available(iOS 15.0, *) {
3568
+ viewRespectsSystemMinimumLayoutMargins = false
3569
+ }
3464
3570
  }
3465
3571
 
3466
3572
  override func viewDidLayoutSubviews() {
@@ -3521,6 +3627,7 @@ function getDevViewControllerSwift() {
3521
3627
  import Lynx
3522
3628
  import tamerdevclient
3523
3629
  import tamerinsets
3630
+ import tamersystemui
3524
3631
 
3525
3632
  class ViewController: UIViewController {
3526
3633
  private var lynxView: LynxView?
@@ -3533,7 +3640,9 @@ class ViewController: UIViewController {
3533
3640
  additionalSafeAreaInsets = .zero
3534
3641
  view.insetsLayoutMarginsFromSafeArea = false
3535
3642
  view.preservesSuperviewLayoutMargins = false
3536
- viewRespectsSystemMinimumLayoutMargins = false
3643
+ if #available(iOS 15.0, *) {
3644
+ viewRespectsSystemMinimumLayoutMargins = false
3645
+ }
3537
3646
  setupLynxView()
3538
3647
  setupDevClientModule()
3539
3648
  }
@@ -3550,7 +3659,7 @@ class ViewController: UIViewController {
3550
3659
  TamerInsetsModule.reRequestInsets()
3551
3660
  }
3552
3661
 
3553
- override var preferredStatusBarStyle: UIStatusBarStyle { TamerPreferredStatusBar.style }
3662
+ override var preferredStatusBarStyle: UIStatusBarStyle { SystemUIModule.statusBarStyleForHost }
3554
3663
 
3555
3664
  private func setupLynxView() {
3556
3665
  let size = fullscreenBounds().size
@@ -3612,8 +3721,8 @@ class ViewController: UIViewController {
3612
3721
  `;
3613
3722
  }
3614
3723
  function patchInfoPlist(infoPlistPath) {
3615
- if (!fs13.existsSync(infoPlistPath)) return;
3616
- let content = fs13.readFileSync(infoPlistPath, "utf8");
3724
+ if (!fs14.existsSync(infoPlistPath)) return;
3725
+ let content = fs14.readFileSync(infoPlistPath, "utf8");
3617
3726
  content = content.replace(/\s*<key>UIMainStoryboardFile<\/key>\s*<string>[^<]*<\/string>/g, "");
3618
3727
  if (!content.includes("UILaunchStoryboardName")) {
3619
3728
  content = content.replace("</dict>\n</plist>", ` <key>UILaunchStoryboardName</key>
@@ -3645,7 +3754,7 @@ function patchInfoPlist(infoPlistPath) {
3645
3754
  </plist>`);
3646
3755
  console.log("\u2705 Added UIApplicationSceneManifest to Info.plist");
3647
3756
  }
3648
- fs13.writeFileSync(infoPlistPath, content, "utf8");
3757
+ fs14.writeFileSync(infoPlistPath, content, "utf8");
3649
3758
  }
3650
3759
  function getSimpleLynxProviderSwift() {
3651
3760
  return `import Foundation
@@ -3670,9 +3779,9 @@ class LynxProvider: NSObject, LynxTemplateProvider {
3670
3779
  }
3671
3780
  function readTemplateOrFallback(devClientPkg, templateName, fallback, vars = {}) {
3672
3781
  if (devClientPkg) {
3673
- const tplPath = path14.join(devClientPkg, "ios", "templates", templateName);
3674
- if (fs13.existsSync(tplPath)) {
3675
- let content = fs13.readFileSync(tplPath, "utf8");
3782
+ const tplPath = path15.join(devClientPkg, "ios", "templates", templateName);
3783
+ if (fs14.existsSync(tplPath)) {
3784
+ let content = fs14.readFileSync(tplPath, "utf8");
3676
3785
  for (const [k, v] of Object.entries(vars)) {
3677
3786
  content = content.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
3678
3787
  }
@@ -3690,19 +3799,19 @@ function syncHostIos(opts) {
3690
3799
  if (!appName) {
3691
3800
  throw new Error('"ios.appName" must be defined in tamer.config.json');
3692
3801
  }
3693
- const projectDir = path14.join(resolved.iosDir, appName);
3694
- const infoPlistPath = path14.join(projectDir, "Info.plist");
3695
- if (!fs13.existsSync(projectDir)) {
3802
+ const projectDir = path15.join(resolved.iosDir, appName);
3803
+ const infoPlistPath = path15.join(projectDir, "Info.plist");
3804
+ if (!fs14.existsSync(projectDir)) {
3696
3805
  throw new Error(`iOS project not found at ${projectDir}. Run \`tamer ios create\` first.`);
3697
3806
  }
3698
- const pbxprojPath = path14.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3699
- const baseLprojDir = path14.join(projectDir, "Base.lproj");
3700
- const launchScreenPath = path14.join(baseLprojDir, "LaunchScreen.storyboard");
3807
+ const pbxprojPath = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3808
+ const baseLprojDir = path15.join(projectDir, "Base.lproj");
3809
+ const launchScreenPath = path15.join(baseLprojDir, "LaunchScreen.storyboard");
3701
3810
  patchInfoPlist(infoPlistPath);
3702
- writeFile(path14.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3703
- writeFile(path14.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3704
- if (!fs13.existsSync(launchScreenPath)) {
3705
- fs13.mkdirSync(baseLprojDir, { recursive: true });
3811
+ writeFile(path15.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3812
+ writeFile(path15.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3813
+ if (!fs14.existsSync(launchScreenPath)) {
3814
+ fs14.mkdirSync(baseLprojDir, { recursive: true });
3706
3815
  writeFile(launchScreenPath, getLaunchScreenStoryboard());
3707
3816
  addLaunchScreenToXcodeProject(pbxprojPath, appName);
3708
3817
  }
@@ -3711,33 +3820,33 @@ function syncHostIos(opts) {
3711
3820
  const devClientPkg2 = findDevClientPackage(resolved.projectRoot);
3712
3821
  const segment = resolved.lynxProjectDir.split("/").filter(Boolean).pop() ?? "";
3713
3822
  const tplVars = { PROJECT_BUNDLE_SEGMENT: segment };
3714
- writeFile(path14.join(projectDir, "ViewController.swift"), getDevViewControllerSwift());
3715
- writeFile(path14.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3823
+ writeFile(path15.join(projectDir, "ViewController.swift"), getDevViewControllerSwift());
3824
+ writeFile(path15.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3716
3825
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "LynxProvider.swift");
3717
3826
  const devTPContent = readTemplateOrFallback(devClientPkg2, "DevTemplateProvider.swift", "", tplVars);
3718
3827
  if (devTPContent) {
3719
- writeFile(path14.join(projectDir, "DevTemplateProvider.swift"), devTPContent);
3828
+ writeFile(path15.join(projectDir, "DevTemplateProvider.swift"), devTPContent);
3720
3829
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevTemplateProvider.swift");
3721
3830
  }
3722
3831
  const projectVCContent = readTemplateOrFallback(devClientPkg2, "ProjectViewController.swift", "", tplVars);
3723
3832
  if (projectVCContent) {
3724
- writeFile(path14.join(projectDir, "ProjectViewController.swift"), projectVCContent);
3833
+ writeFile(path15.join(projectDir, "ProjectViewController.swift"), projectVCContent);
3725
3834
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "ProjectViewController.swift");
3726
3835
  }
3727
3836
  const devCMContent = readTemplateOrFallback(devClientPkg2, "DevClientManager.swift", "", tplVars);
3728
3837
  if (devCMContent) {
3729
- writeFile(path14.join(projectDir, "DevClientManager.swift"), devCMContent);
3838
+ writeFile(path15.join(projectDir, "DevClientManager.swift"), devCMContent);
3730
3839
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevClientManager.swift");
3731
3840
  }
3732
3841
  const qrContent = readTemplateOrFallback(devClientPkg2, "QRScannerViewController.swift", "", tplVars);
3733
3842
  if (qrContent) {
3734
- writeFile(path14.join(projectDir, "QRScannerViewController.swift"), qrContent);
3843
+ writeFile(path15.join(projectDir, "QRScannerViewController.swift"), qrContent);
3735
3844
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "QRScannerViewController.swift");
3736
3845
  }
3737
3846
  console.log("\u2705 Synced iOS host app (embedded dev mode) \u2014 ViewController, DevTemplateProvider, ProjectViewController, DevClientManager, QRScannerViewController");
3738
3847
  } else {
3739
- writeFile(path14.join(projectDir, "ViewController.swift"), getViewControllerSwift());
3740
- writeFile(path14.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3848
+ writeFile(path15.join(projectDir, "ViewController.swift"), getViewControllerSwift());
3849
+ writeFile(path15.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3741
3850
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "LynxProvider.swift");
3742
3851
  console.log("\u2705 Synced iOS host app controller files");
3743
3852
  }
@@ -3756,11 +3865,11 @@ var autolink2 = () => {
3756
3865
  const projectRoot = resolved.projectRoot;
3757
3866
  const iosProjectPath = resolved.iosDir;
3758
3867
  function updateGeneratedSection(filePath, newContent, startMarker, endMarker) {
3759
- if (!fs14.existsSync(filePath)) {
3868
+ if (!fs15.existsSync(filePath)) {
3760
3869
  console.warn(`\u26A0\uFE0F File not found, skipping update: ${filePath}`);
3761
3870
  return;
3762
3871
  }
3763
- let fileContent = fs14.readFileSync(filePath, "utf8");
3872
+ let fileContent = fs15.readFileSync(filePath, "utf8");
3764
3873
  const escapedStartMarker = startMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3765
3874
  const escapedEndMarker = endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3766
3875
  const regex = new RegExp(`${escapedStartMarker}[\\s\\S]*?${escapedEndMarker}`, "g");
@@ -3780,33 +3889,33 @@ ${replacementBlock}
3780
3889
  `;
3781
3890
  }
3782
3891
  } else {
3783
- console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path15.basename(filePath)}. Appending to the end of the file.`);
3892
+ console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path16.basename(filePath)}. Appending to the end of the file.`);
3784
3893
  fileContent += `
3785
3894
  ${replacementBlock}
3786
3895
  `;
3787
3896
  }
3788
- fs14.writeFileSync(filePath, fileContent, "utf8");
3789
- console.log(`\u2705 Updated autolinked section in ${path15.basename(filePath)}`);
3897
+ fs15.writeFileSync(filePath, fileContent, "utf8");
3898
+ console.log(`\u2705 Updated autolinked section in ${path16.basename(filePath)}`);
3790
3899
  }
3791
3900
  function resolvePodDirectory(pkg) {
3792
- const configuredDir = path15.join(pkg.packagePath, pkg.config.ios?.podspecPath || ".");
3793
- if (fs14.existsSync(configuredDir)) {
3901
+ const configuredDir = path16.join(pkg.packagePath, pkg.config.ios?.podspecPath || ".");
3902
+ if (fs15.existsSync(configuredDir)) {
3794
3903
  return configuredDir;
3795
3904
  }
3796
- const iosDir = path15.join(pkg.packagePath, "ios");
3797
- if (fs14.existsSync(iosDir)) {
3905
+ const iosDir = path16.join(pkg.packagePath, "ios");
3906
+ if (fs15.existsSync(iosDir)) {
3798
3907
  const stack = [iosDir];
3799
3908
  while (stack.length > 0) {
3800
3909
  const current = stack.pop();
3801
3910
  try {
3802
- const entries = fs14.readdirSync(current, { withFileTypes: true });
3911
+ const entries = fs15.readdirSync(current, { withFileTypes: true });
3803
3912
  const podspec = entries.find((entry) => entry.isFile() && entry.name.endsWith(".podspec"));
3804
3913
  if (podspec) {
3805
3914
  return current;
3806
3915
  }
3807
3916
  for (const entry of entries) {
3808
3917
  if (entry.isDirectory()) {
3809
- stack.push(path15.join(current, entry.name));
3918
+ stack.push(path16.join(current, entry.name));
3810
3919
  }
3811
3920
  }
3812
3921
  } catch {
@@ -3817,9 +3926,9 @@ ${replacementBlock}
3817
3926
  }
3818
3927
  function resolvePodName(pkg) {
3819
3928
  const fullPodspecDir = resolvePodDirectory(pkg);
3820
- if (fs14.existsSync(fullPodspecDir)) {
3929
+ if (fs15.existsSync(fullPodspecDir)) {
3821
3930
  try {
3822
- const files = fs14.readdirSync(fullPodspecDir);
3931
+ const files = fs15.readdirSync(fullPodspecDir);
3823
3932
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
3824
3933
  if (podspecFile) return podspecFile.replace(".podspec", "");
3825
3934
  } catch {
@@ -3828,13 +3937,13 @@ ${replacementBlock}
3828
3937
  return pkg.name.split("/").pop().replace(/-/g, "");
3829
3938
  }
3830
3939
  function updatePodfile(packages) {
3831
- const podfilePath = path15.join(iosProjectPath, "Podfile");
3940
+ const podfilePath = path16.join(iosProjectPath, "Podfile");
3832
3941
  let scriptContent = ` # This section is automatically generated by Tamer4Lynx.
3833
3942
  # Manual edits will be overwritten.`;
3834
3943
  const iosPackages = packages.filter((p) => p.config.ios);
3835
3944
  if (iosPackages.length > 0) {
3836
3945
  iosPackages.forEach((pkg) => {
3837
- const relativePath = path15.relative(iosProjectPath, resolvePodDirectory(pkg));
3946
+ const relativePath = path16.relative(iosProjectPath, resolvePodDirectory(pkg));
3838
3947
  const podName = resolvePodName(pkg);
3839
3948
  scriptContent += `
3840
3949
  pod '${podName}', :path => '${relativePath}'`;
@@ -3846,9 +3955,9 @@ ${replacementBlock}
3846
3955
  updateGeneratedSection(podfilePath, scriptContent.trim(), "# GENERATED AUTOLINK DEPENDENCIES START", "# GENERATED AUTOLINK DEPENDENCIES END");
3847
3956
  }
3848
3957
  function ensureXElementPod() {
3849
- const podfilePath = path15.join(iosProjectPath, "Podfile");
3850
- if (!fs14.existsSync(podfilePath)) return;
3851
- let content = fs14.readFileSync(podfilePath, "utf8");
3958
+ const podfilePath = path16.join(iosProjectPath, "Podfile");
3959
+ if (!fs15.existsSync(podfilePath)) return;
3960
+ let content = fs15.readFileSync(podfilePath, "utf8");
3852
3961
  if (content.includes("pod 'XElement'")) return;
3853
3962
  const lynxVersionMatch = content.match(/pod\s+'Lynx',\s*'([^']+)'/);
3854
3963
  const lynxVersion = lynxVersionMatch?.[1] ?? "3.6.0";
@@ -3873,13 +3982,13 @@ ${replacementBlock}
3873
3982
  `;
3874
3983
  }
3875
3984
  }
3876
- fs14.writeFileSync(podfilePath, content, "utf8");
3985
+ fs15.writeFileSync(podfilePath, content, "utf8");
3877
3986
  console.log(`\u2705 Added XElement pod (v${lynxVersion}) to Podfile`);
3878
3987
  }
3879
3988
  function ensureLynxPatchInPodfile() {
3880
- const podfilePath = path15.join(iosProjectPath, "Podfile");
3881
- if (!fs14.existsSync(podfilePath)) return;
3882
- let content = fs14.readFileSync(podfilePath, "utf8");
3989
+ const podfilePath = path16.join(iosProjectPath, "Podfile");
3990
+ if (!fs15.existsSync(podfilePath)) return;
3991
+ let content = fs15.readFileSync(podfilePath, "utf8");
3883
3992
  if (content.includes("content.gsub(/\\btypeof\\(/, '__typeof__(')")) return;
3884
3993
  const patch = `
3885
3994
  Dir.glob(File.join(installer.sandbox.root, 'Lynx/platform/darwin/**/*.{m,mm}')).each do |lynx_source|
@@ -3891,13 +4000,13 @@ ${replacementBlock}
3891
4000
  end`;
3892
4001
  content = content.replace(/(\n end\s*\n)(end\s*)$/, `$1${patch}
3893
4002
  $2`);
3894
- fs14.writeFileSync(podfilePath, content, "utf8");
4003
+ fs15.writeFileSync(podfilePath, content, "utf8");
3895
4004
  console.log("\u2705 Added Lynx typeof patch to Podfile post_install.");
3896
4005
  }
3897
4006
  function ensurePodBuildSettings() {
3898
- const podfilePath = path15.join(iosProjectPath, "Podfile");
3899
- if (!fs14.existsSync(podfilePath)) return;
3900
- let content = fs14.readFileSync(podfilePath, "utf8");
4007
+ const podfilePath = path16.join(iosProjectPath, "Podfile");
4008
+ if (!fs15.existsSync(podfilePath)) return;
4009
+ let content = fs15.readFileSync(podfilePath, "utf8");
3901
4010
  let changed = false;
3902
4011
  if (!content.includes("CLANG_ENABLE_EXPLICIT_MODULES")) {
3903
4012
  content = content.replace(
@@ -3940,7 +4049,7 @@ $2`);
3940
4049
  changed = true;
3941
4050
  }
3942
4051
  if (changed) {
3943
- fs14.writeFileSync(podfilePath, content, "utf8");
4052
+ fs15.writeFileSync(podfilePath, content, "utf8");
3944
4053
  console.log("\u2705 Added Xcode compatibility build settings to Podfile post_install.");
3945
4054
  }
3946
4055
  }
@@ -3948,10 +4057,10 @@ $2`);
3948
4057
  const appNameFromConfig = resolved.config.ios?.appName;
3949
4058
  const candidatePaths = [];
3950
4059
  if (appNameFromConfig) {
3951
- candidatePaths.push(path15.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
4060
+ candidatePaths.push(path16.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
3952
4061
  }
3953
- candidatePaths.push(path15.join(iosProjectPath, "LynxInitProcessor.swift"));
3954
- const found = candidatePaths.find((p) => fs14.existsSync(p));
4062
+ candidatePaths.push(path16.join(iosProjectPath, "LynxInitProcessor.swift"));
4063
+ const found = candidatePaths.find((p) => fs15.existsSync(p));
3955
4064
  const lynxInitPath = found ?? candidatePaths[0];
3956
4065
  const iosPackages = packages.filter((p) => getIosModuleClassNames(p.config.ios).length > 0 || Object.keys(getIosElements(p.config.ios)).length > 0);
3957
4066
  const seenModules = /* @__PURE__ */ new Set();
@@ -3990,7 +4099,7 @@ $2`);
3990
4099
  const podName = resolvePodName(pkg);
3991
4100
  return `import ${podName}`;
3992
4101
  }).join("\n");
3993
- const fileContent = fs14.readFileSync(filePath, "utf8");
4102
+ const fileContent = fs15.readFileSync(filePath, "utf8");
3994
4103
  if (fileContent.indexOf(startMarker) !== -1) {
3995
4104
  updateGeneratedSection(filePath, imports, startMarker, endMarker);
3996
4105
  return;
@@ -4027,8 +4136,8 @@ ${after}`;
4027
4136
  ${fileContent}`;
4028
4137
  }
4029
4138
  }
4030
- fs14.writeFileSync(filePath, newContent, "utf8");
4031
- console.log(`\u2705 Updated imports in ${path15.basename(filePath)}`);
4139
+ fs15.writeFileSync(filePath, newContent, "utf8");
4140
+ console.log(`\u2705 Updated imports in ${path16.basename(filePath)}`);
4032
4141
  }
4033
4142
  updateImportsSection(lynxInitPath, importPackages);
4034
4143
  if (importPackages.length === 0) {
@@ -4072,7 +4181,7 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4072
4181
  } else {
4073
4182
  devClientSupportedBody = " // @tamer4lynx/tamer-dev-client not linked";
4074
4183
  }
4075
- if (fs14.readFileSync(lynxInitPath, "utf8").includes("GENERATED DEV_CLIENT_SUPPORTED START")) {
4184
+ if (fs15.readFileSync(lynxInitPath, "utf8").includes("GENERATED DEV_CLIENT_SUPPORTED START")) {
4076
4185
  updateGeneratedSection(lynxInitPath, devClientSupportedBody, "// GENERATED DEV_CLIENT_SUPPORTED START", "// GENERATED DEV_CLIENT_SUPPORTED END");
4077
4186
  }
4078
4187
  }
@@ -4080,13 +4189,13 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4080
4189
  const appNameFromConfig = resolved.config.ios?.appName;
4081
4190
  const candidates = [];
4082
4191
  if (appNameFromConfig) {
4083
- candidates.push(path15.join(iosProjectPath, appNameFromConfig, "Info.plist"));
4192
+ candidates.push(path16.join(iosProjectPath, appNameFromConfig, "Info.plist"));
4084
4193
  }
4085
- candidates.push(path15.join(iosProjectPath, "Info.plist"));
4086
- return candidates.find((p) => fs14.existsSync(p)) ?? null;
4194
+ candidates.push(path16.join(iosProjectPath, "Info.plist"));
4195
+ return candidates.find((p) => fs15.existsSync(p)) ?? null;
4087
4196
  }
4088
4197
  function readPlistXml(plistPath) {
4089
- return fs14.readFileSync(plistPath, "utf8");
4198
+ return fs15.readFileSync(plistPath, "utf8");
4090
4199
  }
4091
4200
  function syncInfoPlistPermissions(packages) {
4092
4201
  const plistPath = findInfoPlist();
@@ -4117,7 +4226,7 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4117
4226
  added++;
4118
4227
  }
4119
4228
  if (added > 0) {
4120
- fs14.writeFileSync(plistPath, plist, "utf8");
4229
+ fs15.writeFileSync(plistPath, plist, "utf8");
4121
4230
  console.log(`\u2705 Synced ${added} Info.plist permission description(s)`);
4122
4231
  }
4123
4232
  }
@@ -4164,16 +4273,16 @@ ${schemesXml}
4164
4273
  $1`
4165
4274
  );
4166
4275
  }
4167
- fs14.writeFileSync(plistPath, plist, "utf8");
4276
+ fs15.writeFileSync(plistPath, plist, "utf8");
4168
4277
  console.log(`\u2705 Synced ${urlSchemes.length} iOS URL scheme(s) into Info.plist`);
4169
4278
  }
4170
4279
  function runPodInstall(forcePath) {
4171
- const podfilePath = forcePath ?? path15.join(iosProjectPath, "Podfile");
4172
- if (!fs14.existsSync(podfilePath)) {
4280
+ const podfilePath = forcePath ?? path16.join(iosProjectPath, "Podfile");
4281
+ if (!fs15.existsSync(podfilePath)) {
4173
4282
  console.log("\u2139\uFE0F No Podfile found in ios directory; skipping `pod install`.");
4174
4283
  return;
4175
4284
  }
4176
- const cwd = path15.dirname(podfilePath);
4285
+ const cwd = path16.dirname(podfilePath);
4177
4286
  try {
4178
4287
  console.log(`\u2139\uFE0F Running \`pod install\` in ${cwd}...`);
4179
4288
  try {
@@ -4207,8 +4316,8 @@ $1`
4207
4316
  syncInfoPlistUrlSchemes();
4208
4317
  const appNameFromConfig = resolved.config.ios?.appName;
4209
4318
  if (appNameFromConfig) {
4210
- const appPodfile = path15.join(iosProjectPath, appNameFromConfig, "Podfile");
4211
- if (fs14.existsSync(appPodfile)) {
4319
+ const appPodfile = path16.join(iosProjectPath, appNameFromConfig, "Podfile");
4320
+ if (fs15.existsSync(appPodfile)) {
4212
4321
  runPodInstall(appPodfile);
4213
4322
  console.log("\u2728 Autolinking complete for iOS.");
4214
4323
  return;
@@ -4223,13 +4332,13 @@ $1`
4223
4332
  const appFolder = resolved.config.ios?.appName;
4224
4333
  if (!hasDevClient || !appFolder) return;
4225
4334
  const androidNames = getDedupedAndroidModuleClassNames(allPkgs);
4226
- const appDir = path15.join(iosProjectPath, appFolder);
4227
- fs14.mkdirSync(appDir, { recursive: true });
4228
- const manifestPath = path15.join(appDir, TAMER_HOST_NATIVE_MODULES_FILENAME);
4229
- fs14.writeFileSync(manifestPath, buildHostNativeModulesManifestJson(androidNames), "utf8");
4335
+ const appDir = path16.join(iosProjectPath, appFolder);
4336
+ fs15.mkdirSync(appDir, { recursive: true });
4337
+ const manifestPath = path16.join(appDir, TAMER_HOST_NATIVE_MODULES_FILENAME);
4338
+ fs15.writeFileSync(manifestPath, buildHostNativeModulesManifestJson(androidNames), "utf8");
4230
4339
  console.log(`\u2705 Wrote ${TAMER_HOST_NATIVE_MODULES_FILENAME} (native module ids for dev-client checks)`);
4231
- const pbxprojPath = path15.join(iosProjectPath, `${appFolder}.xcodeproj`, "project.pbxproj");
4232
- if (fs14.existsSync(pbxprojPath)) {
4340
+ const pbxprojPath = path16.join(iosProjectPath, `${appFolder}.xcodeproj`, "project.pbxproj");
4341
+ if (fs15.existsSync(pbxprojPath)) {
4233
4342
  addResourceToXcodeProject(pbxprojPath, appFolder, TAMER_HOST_NATIVE_MODULES_FILENAME);
4234
4343
  }
4235
4344
  }
@@ -4238,8 +4347,8 @@ $1`
4238
4347
  var autolink_default2 = autolink2;
4239
4348
 
4240
4349
  // src/ios/bundle.ts
4241
- import fs15 from "fs";
4242
- import path16 from "path";
4350
+ import fs16 from "fs";
4351
+ import path17 from "path";
4243
4352
  import { execSync as execSync7 } from "child_process";
4244
4353
  function bundleAndDeploy2(opts = {}) {
4245
4354
  const release = opts.release === true || opts.production === true;
@@ -4257,18 +4366,22 @@ function bundleAndDeploy2(opts = {}) {
4257
4366
  const includeDevClient = !release && !!devClientPkg;
4258
4367
  const appName = resolved.config.ios.appName;
4259
4368
  const sourceBundlePath = resolved.lynxBundlePath;
4260
- const destinationDir = path16.join(resolved.iosDir, appName);
4261
- const destinationBundlePath = path16.join(destinationDir, resolved.lynxBundleFile);
4369
+ const destinationDir = path17.join(resolved.iosDir, appName);
4370
+ const destinationBundlePath = path17.join(destinationDir, resolved.lynxBundleFile);
4262
4371
  syncHost_default({ release, includeDevClient });
4263
4372
  autolink_default2();
4264
4373
  const iconPaths = resolveIconPaths(resolved.projectRoot, resolved.config);
4265
4374
  if (iconPaths) {
4266
- const appIconDir = path16.join(destinationDir, "Assets.xcassets", "AppIcon.appiconset");
4375
+ const appIconDir = path17.join(destinationDir, "Assets.xcassets", "AppIcon.appiconset");
4267
4376
  if (applyIosAppIconAssets(appIconDir, iconPaths)) {
4268
4377
  console.log("\u2705 Synced iOS AppIcon from tamer.config.json");
4269
4378
  }
4270
4379
  }
4271
4380
  try {
4381
+ const lynxTsconfig = path17.join(resolved.lynxProjectDir, "tsconfig.json");
4382
+ if (fs16.existsSync(lynxTsconfig)) {
4383
+ fixTsconfigReferencesForBuild(lynxTsconfig);
4384
+ }
4272
4385
  console.log("\u{1F4E6} Building Lynx bundle...");
4273
4386
  execSync7("npm run build", { stdio: "inherit", cwd: resolved.lynxProjectDir });
4274
4387
  console.log("\u2705 Build completed successfully.");
@@ -4277,40 +4390,40 @@ function bundleAndDeploy2(opts = {}) {
4277
4390
  process.exit(1);
4278
4391
  }
4279
4392
  try {
4280
- if (!fs15.existsSync(sourceBundlePath)) {
4393
+ if (!fs16.existsSync(sourceBundlePath)) {
4281
4394
  console.error(`\u274C Build output not found at: ${sourceBundlePath}`);
4282
4395
  process.exit(1);
4283
4396
  }
4284
- if (!fs15.existsSync(destinationDir)) {
4397
+ if (!fs16.existsSync(destinationDir)) {
4285
4398
  console.error(`Destination directory not found at: ${destinationDir}`);
4286
4399
  process.exit(1);
4287
4400
  }
4288
- const distDir = path16.dirname(sourceBundlePath);
4401
+ const distDir = path17.dirname(sourceBundlePath);
4289
4402
  console.log(`\u{1F69A} Copying bundle and assets to iOS project...`);
4290
4403
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
4291
4404
  console.log(`\u2728 Successfully copied bundle to: ${destinationBundlePath}`);
4292
- const pbxprojPath = path16.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4293
- if (fs15.existsSync(pbxprojPath)) {
4405
+ const pbxprojPath = path17.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4406
+ if (fs16.existsSync(pbxprojPath)) {
4294
4407
  const skip = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
4295
- for (const entry of fs15.readdirSync(distDir)) {
4296
- if (skip.has(entry) || fs15.statSync(path16.join(distDir, entry)).isDirectory()) continue;
4408
+ for (const entry of fs16.readdirSync(distDir)) {
4409
+ if (skip.has(entry) || fs16.statSync(path17.join(distDir, entry)).isDirectory()) continue;
4297
4410
  addResourceToXcodeProject(pbxprojPath, appName, entry);
4298
4411
  }
4299
4412
  }
4300
4413
  if (includeDevClient && devClientPkg) {
4301
- const devClientBundle = path16.join(destinationDir, "dev-client.lynx.bundle");
4414
+ const devClientBundle = path17.join(destinationDir, "dev-client.lynx.bundle");
4302
4415
  console.log("\u{1F4E6} Building dev-client bundle...");
4303
4416
  try {
4304
4417
  execSync7("npm run build", { stdio: "inherit", cwd: devClientPkg });
4305
4418
  } catch {
4306
4419
  console.warn("\u26A0\uFE0F dev-client build failed; skipping dev-client bundle");
4307
4420
  }
4308
- const builtBundle = path16.join(devClientPkg, "dist", "dev-client.lynx.bundle");
4309
- if (fs15.existsSync(builtBundle)) {
4310
- fs15.copyFileSync(builtBundle, devClientBundle);
4421
+ const builtBundle = path17.join(devClientPkg, "dist", "dev-client.lynx.bundle");
4422
+ if (fs16.existsSync(builtBundle)) {
4423
+ fs16.copyFileSync(builtBundle, devClientBundle);
4311
4424
  console.log("\u2728 Copied dev-client.lynx.bundle to iOS project");
4312
- const pbxprojPath2 = path16.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4313
- if (fs15.existsSync(pbxprojPath2)) {
4425
+ const pbxprojPath2 = path17.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4426
+ if (fs16.existsSync(pbxprojPath2)) {
4314
4427
  addResourceToXcodeProject(pbxprojPath2, appName, "dev-client.lynx.bundle");
4315
4428
  }
4316
4429
  }
@@ -4324,8 +4437,8 @@ function bundleAndDeploy2(opts = {}) {
4324
4437
  var bundle_default2 = bundleAndDeploy2;
4325
4438
 
4326
4439
  // src/ios/build.ts
4327
- import fs16 from "fs";
4328
- import path17 from "path";
4440
+ import fs17 from "fs";
4441
+ import path18 from "path";
4329
4442
  import os3 from "os";
4330
4443
  import { execSync as execSync8 } from "child_process";
4331
4444
  function hostArch() {
@@ -4356,11 +4469,11 @@ async function buildIpa(opts = {}) {
4356
4469
  const configuration = release ? "Release" : "Debug";
4357
4470
  bundle_default2({ release, production: opts.production });
4358
4471
  const scheme = appName;
4359
- const workspacePath = path17.join(iosDir, `${appName}.xcworkspace`);
4360
- const projectPath = path17.join(iosDir, `${appName}.xcodeproj`);
4361
- const xcproject = fs16.existsSync(workspacePath) ? workspacePath : projectPath;
4472
+ const workspacePath = path18.join(iosDir, `${appName}.xcworkspace`);
4473
+ const projectPath = path18.join(iosDir, `${appName}.xcodeproj`);
4474
+ const xcproject = fs17.existsSync(workspacePath) ? workspacePath : projectPath;
4362
4475
  const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
4363
- const derivedDataPath = path17.join(iosDir, "build");
4476
+ const derivedDataPath = path18.join(iosDir, "build");
4364
4477
  const sdk = opts.install ? "iphonesimulator" : "iphoneos";
4365
4478
  const signingArgs = opts.install ? "" : " CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO";
4366
4479
  const archFlag = opts.install ? `-arch ${hostArch()} ` : "";
@@ -4376,14 +4489,14 @@ async function buildIpa(opts = {}) {
4376
4489
  );
4377
4490
  console.log(`\u2705 Build completed.`);
4378
4491
  if (opts.install) {
4379
- const appGlob = path17.join(
4492
+ const appGlob = path18.join(
4380
4493
  derivedDataPath,
4381
4494
  "Build",
4382
4495
  "Products",
4383
4496
  `${configuration}-iphonesimulator`,
4384
4497
  `${appName}.app`
4385
4498
  );
4386
- if (!fs16.existsSync(appGlob)) {
4499
+ if (!fs17.existsSync(appGlob)) {
4387
4500
  console.error(`\u274C Built app not found at: ${appGlob}`);
4388
4501
  process.exit(1);
4389
4502
  }
@@ -4406,8 +4519,8 @@ async function buildIpa(opts = {}) {
4406
4519
  var build_default2 = buildIpa;
4407
4520
 
4408
4521
  // src/common/init.tsx
4409
- import fs17 from "fs";
4410
- import path18 from "path";
4522
+ import fs18 from "fs";
4523
+ import path19 from "path";
4411
4524
  import { useState as useState4, useEffect as useEffect2, useCallback as useCallback3 } from "react";
4412
4525
  import { render, Text as Text9, Box as Box8 } from "ink";
4413
4526
 
@@ -4720,34 +4833,23 @@ function InitWizard() {
4720
4833
  paths: { androidDir: "android", iosDir: "ios" }
4721
4834
  };
4722
4835
  if (lynxProject.trim()) config.lynxProject = lynxProject.trim();
4723
- const configPath = path18.join(process.cwd(), "tamer.config.json");
4724
- fs17.writeFileSync(configPath, JSON.stringify(config, null, 2));
4836
+ const configPath = path19.join(process.cwd(), "tamer.config.json");
4837
+ fs18.writeFileSync(configPath, JSON.stringify(config, null, 2));
4725
4838
  const lines = [`Generated tamer.config.json at ${configPath}`];
4726
4839
  const tamerTypesInclude = "node_modules/@tamer4lynx/tamer-*/src/**/*.d.ts";
4727
4840
  const tsconfigCandidates = lynxProject.trim() ? [
4728
- path18.join(process.cwd(), lynxProject.trim(), "tsconfig.json"),
4729
- path18.join(process.cwd(), "tsconfig.json")
4730
- ] : [path18.join(process.cwd(), "tsconfig.json")];
4731
- function parseTsconfigJson(raw) {
4732
- try {
4733
- return JSON.parse(raw);
4734
- } catch {
4735
- const noTrailingCommas = raw.replace(/,\s*([\]}])/g, "$1");
4736
- return JSON.parse(noTrailingCommas);
4737
- }
4738
- }
4841
+ path19.join(process.cwd(), lynxProject.trim(), "tsconfig.json"),
4842
+ path19.join(process.cwd(), "tsconfig.json")
4843
+ ] : [path19.join(process.cwd(), "tsconfig.json")];
4739
4844
  for (const tsconfigPath of tsconfigCandidates) {
4740
- if (!fs17.existsSync(tsconfigPath)) continue;
4845
+ if (!fs18.existsSync(tsconfigPath)) continue;
4741
4846
  try {
4742
- const raw = fs17.readFileSync(tsconfigPath, "utf-8");
4743
- const tsconfig = parseTsconfigJson(raw);
4744
- const include = tsconfig.include ?? [];
4745
- const arr = Array.isArray(include) ? include : [include];
4746
- if (arr.some((p) => (typeof p === "string" ? p : "").includes("tamer-"))) continue;
4747
- arr.push(tamerTypesInclude);
4748
- tsconfig.include = arr;
4749
- fs17.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
4750
- lines.push(`Updated ${path18.relative(process.cwd(), tsconfigPath)} for tamer types`);
4847
+ if (fixTsconfigReferencesForBuild(tsconfigPath)) {
4848
+ lines.push(`Flattened ${path19.relative(process.cwd(), tsconfigPath)} (fixed TS6310)`);
4849
+ }
4850
+ if (addTamerTypesInclude(tsconfigPath, tamerTypesInclude)) {
4851
+ lines.push(`Updated ${path19.relative(process.cwd(), tsconfigPath)} for tamer types`);
4852
+ }
4751
4853
  break;
4752
4854
  } catch (e) {
4753
4855
  lines.push(`Could not update ${tsconfigPath}: ${e.message}`);
@@ -4783,7 +4885,8 @@ function InitWizard() {
4783
4885
  defaultValue: androidAppName,
4784
4886
  onSubmitValue: (v) => setAndroidAppName(v),
4785
4887
  onSubmit: () => setStep("android-pkg")
4786
- }
4888
+ },
4889
+ step
4787
4890
  ) });
4788
4891
  }
4789
4892
  if (step === "android-pkg") {
@@ -4804,7 +4907,8 @@ function InitWizard() {
4804
4907
  setPkgError(void 0);
4805
4908
  },
4806
4909
  onSubmit: () => setStep("android-sdk")
4807
- }
4910
+ },
4911
+ step
4808
4912
  ) });
4809
4913
  }
4810
4914
  if (step === "android-sdk") {
@@ -4820,7 +4924,8 @@ function InitWizard() {
4820
4924
  },
4821
4925
  onSubmit: () => setStep("ios-reuse"),
4822
4926
  hint: sdkHint
4823
- }
4927
+ },
4928
+ step
4824
4929
  ) });
4825
4930
  }
4826
4931
  if (step === "ios-reuse") {
@@ -4850,7 +4955,8 @@ function InitWizard() {
4850
4955
  defaultValue: iosAppName,
4851
4956
  onSubmitValue: (v) => setIosAppName(v),
4852
4957
  onSubmit: () => setStep("ios-bundle")
4853
- }
4958
+ },
4959
+ step
4854
4960
  ) });
4855
4961
  }
4856
4962
  if (step === "ios-bundle") {
@@ -4871,7 +4977,8 @@ function InitWizard() {
4871
4977
  setBundleError(void 0);
4872
4978
  },
4873
4979
  onSubmit: () => setStep("lynx-path")
4874
- }
4980
+ },
4981
+ step
4875
4982
  ) });
4876
4983
  }
4877
4984
  if (step === "lynx-path") {
@@ -4883,7 +4990,8 @@ function InitWizard() {
4883
4990
  onSubmitValue: (v) => setLynxProject(v),
4884
4991
  onSubmit: () => setStep("saving"),
4885
4992
  hint: "Press Enter with empty to skip"
4886
- }
4993
+ },
4994
+ step
4887
4995
  ) });
4888
4996
  }
4889
4997
  if (step === "saving") {
@@ -4900,8 +5008,8 @@ async function init() {
4900
5008
  }
4901
5009
 
4902
5010
  // src/common/create.ts
4903
- import fs18 from "fs";
4904
- import path19 from "path";
5011
+ import fs19 from "fs";
5012
+ import path20 from "path";
4905
5013
  import readline from "readline";
4906
5014
  var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
4907
5015
  function ask(question) {
@@ -4963,13 +5071,13 @@ async function create3(opts) {
4963
5071
  const simpleModuleName = extName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Module";
4964
5072
  const fullModuleClassName = `${packageName}.${simpleModuleName}`;
4965
5073
  const cwd = process.cwd();
4966
- const root = path19.join(cwd, extName);
4967
- if (fs18.existsSync(root)) {
5074
+ const root = path20.join(cwd, extName);
5075
+ if (fs19.existsSync(root)) {
4968
5076
  console.error(`\u274C Directory ${extName} already exists.`);
4969
5077
  rl.close();
4970
5078
  process.exit(1);
4971
5079
  }
4972
- fs18.mkdirSync(root, { recursive: true });
5080
+ fs19.mkdirSync(root, { recursive: true });
4973
5081
  const lynxExt = {
4974
5082
  platforms: {
4975
5083
  android: {
@@ -4984,7 +5092,7 @@ async function create3(opts) {
4984
5092
  web: {}
4985
5093
  }
4986
5094
  };
4987
- fs18.writeFileSync(path19.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5095
+ fs19.writeFileSync(path20.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
4988
5096
  const pkg = {
4989
5097
  name: extName,
4990
5098
  version: "0.0.1",
@@ -4997,20 +5105,20 @@ async function create3(opts) {
4997
5105
  engines: { node: ">=18" }
4998
5106
  };
4999
5107
  if (includeModule) pkg.types = "src/index.d.ts";
5000
- fs18.writeFileSync(path19.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5108
+ fs19.writeFileSync(path20.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5001
5109
  const pkgPath = packageName.replace(/\./g, "/");
5002
5110
  const hasSrc = includeModule || includeElement || includeService;
5003
5111
  if (hasSrc) {
5004
- fs18.mkdirSync(path19.join(root, "src"), { recursive: true });
5112
+ fs19.mkdirSync(path20.join(root, "src"), { recursive: true });
5005
5113
  }
5006
5114
  if (includeModule) {
5007
- fs18.writeFileSync(path19.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5115
+ fs19.writeFileSync(path20.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5008
5116
  export declare class ${simpleModuleName} {
5009
5117
  // Add your module methods here
5010
5118
  }
5011
5119
  `);
5012
- fs18.mkdirSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5013
- fs18.writeFileSync(path19.join(root, "android", "build.gradle.kts"), `plugins {
5120
+ fs19.mkdirSync(path20.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5121
+ fs19.writeFileSync(path20.join(root, "android", "build.gradle.kts"), `plugins {
5014
5122
  id("com.android.library")
5015
5123
  id("org.jetbrains.kotlin.android")
5016
5124
  }
@@ -5031,7 +5139,7 @@ dependencies {
5031
5139
  implementation(libs.lynx.jssdk)
5032
5140
  }
5033
5141
  `);
5034
- fs18.writeFileSync(path19.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5142
+ fs19.writeFileSync(path20.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5035
5143
  <manifest />
5036
5144
  `);
5037
5145
  const ktContent = `package ${packageName}
@@ -5048,8 +5156,8 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5048
5156
  }
5049
5157
  }
5050
5158
  `;
5051
- fs18.writeFileSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5052
- fs18.mkdirSync(path19.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5159
+ fs19.writeFileSync(path20.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5160
+ fs19.mkdirSync(path20.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5053
5161
  const podspec = `Pod::Spec.new do |s|
5054
5162
  s.name = '${extName}'
5055
5163
  s.version = '0.0.1'
@@ -5063,7 +5171,7 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5063
5171
  s.dependency 'Lynx'
5064
5172
  end
5065
5173
  `;
5066
- fs18.writeFileSync(path19.join(root, "ios", extName, `${extName}.podspec`), podspec);
5174
+ fs19.writeFileSync(path20.join(root, "ios", extName, `${extName}.podspec`), podspec);
5067
5175
  const swiftContent = `import Foundation
5068
5176
 
5069
5177
  @objc public class ${simpleModuleName}: NSObject {
@@ -5072,18 +5180,18 @@ end
5072
5180
  }
5073
5181
  }
5074
5182
  `;
5075
- fs18.writeFileSync(path19.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5183
+ fs19.writeFileSync(path20.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5076
5184
  }
5077
5185
  if (includeElement && !includeModule) {
5078
5186
  const elementName = extName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
5079
- fs18.writeFileSync(path19.join(root, "src", "index.tsx"), `import type { FC } from '@lynx-js/react';
5187
+ fs19.writeFileSync(path20.join(root, "src", "index.tsx"), `import type { FC } from '@lynx-js/react';
5080
5188
 
5081
5189
  export const ${elementName}: FC = () => {
5082
5190
  return null;
5083
5191
  };
5084
5192
  `);
5085
5193
  }
5086
- fs18.writeFileSync(path19.join(root, "index.js"), `'use strict';
5194
+ fs19.writeFileSync(path20.join(root, "index.js"), `'use strict';
5087
5195
  module.exports = {};
5088
5196
  `);
5089
5197
  const tsconfigCompiler = {
@@ -5096,11 +5204,11 @@ module.exports = {};
5096
5204
  tsconfigCompiler.jsx = "preserve";
5097
5205
  tsconfigCompiler.jsxImportSource = "@lynx-js/react";
5098
5206
  }
5099
- fs18.writeFileSync(path19.join(root, "tsconfig.json"), JSON.stringify({
5207
+ fs19.writeFileSync(path20.join(root, "tsconfig.json"), JSON.stringify({
5100
5208
  compilerOptions: tsconfigCompiler,
5101
5209
  include: includeElement ? ["src", "src/**/*.tsx"] : ["src"]
5102
5210
  }, null, 2));
5103
- fs18.writeFileSync(path19.join(root, "README.md"), `# ${extName}
5211
+ fs19.writeFileSync(path20.join(root, "README.md"), `# ${extName}
5104
5212
 
5105
5213
  Lynx extension for ${extName}.
5106
5214
 
@@ -5125,8 +5233,8 @@ This package uses \`lynx.ext.json\` (RFC-compliant) for autolinking.
5125
5233
  var create_default3 = create3;
5126
5234
 
5127
5235
  // src/common/codegen.ts
5128
- import fs19 from "fs";
5129
- import path20 from "path";
5236
+ import fs20 from "fs";
5237
+ import path21 from "path";
5130
5238
  function codegen() {
5131
5239
  const cwd = process.cwd();
5132
5240
  const config = loadExtensionConfig(cwd);
@@ -5134,9 +5242,9 @@ function codegen() {
5134
5242
  console.error("\u274C No lynx.ext.json or tamer.json found. Run from an extension package root.");
5135
5243
  process.exit(1);
5136
5244
  }
5137
- const srcDir = path20.join(cwd, "src");
5138
- const generatedDir = path20.join(cwd, "generated");
5139
- fs19.mkdirSync(generatedDir, { recursive: true });
5245
+ const srcDir = path21.join(cwd, "src");
5246
+ const generatedDir = path21.join(cwd, "generated");
5247
+ fs20.mkdirSync(generatedDir, { recursive: true });
5140
5248
  const dtsFiles = findDtsFiles(srcDir);
5141
5249
  const modules = extractLynxModules(dtsFiles);
5142
5250
  if (modules.length === 0) {
@@ -5146,28 +5254,28 @@ function codegen() {
5146
5254
  for (const mod of modules) {
5147
5255
  const tsContent = `export type { ${mod} } from '../src/index.js';
5148
5256
  `;
5149
- const outPath = path20.join(generatedDir, `${mod}.ts`);
5150
- fs19.writeFileSync(outPath, tsContent);
5257
+ const outPath = path21.join(generatedDir, `${mod}.ts`);
5258
+ fs20.writeFileSync(outPath, tsContent);
5151
5259
  console.log(`\u2705 Generated ${outPath}`);
5152
5260
  }
5153
5261
  if (config.android) {
5154
- const androidGenerated = path20.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5155
- fs19.mkdirSync(androidGenerated, { recursive: true });
5262
+ const androidGenerated = path21.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5263
+ fs20.mkdirSync(androidGenerated, { recursive: true });
5156
5264
  console.log(`\u2139\uFE0F Android generated dir: ${androidGenerated} (spec generation coming soon)`);
5157
5265
  }
5158
5266
  if (config.ios) {
5159
- const iosGenerated = path20.join(cwd, "ios", "generated");
5160
- fs19.mkdirSync(iosGenerated, { recursive: true });
5267
+ const iosGenerated = path21.join(cwd, "ios", "generated");
5268
+ fs20.mkdirSync(iosGenerated, { recursive: true });
5161
5269
  console.log(`\u2139\uFE0F iOS generated dir: ${iosGenerated} (spec generation coming soon)`);
5162
5270
  }
5163
5271
  console.log("\u2728 Codegen complete.");
5164
5272
  }
5165
5273
  function findDtsFiles(dir) {
5166
5274
  const result = [];
5167
- if (!fs19.existsSync(dir)) return result;
5168
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
5275
+ if (!fs20.existsSync(dir)) return result;
5276
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
5169
5277
  for (const e of entries) {
5170
- const full = path20.join(dir, e.name);
5278
+ const full = path21.join(dir, e.name);
5171
5279
  if (e.isDirectory()) result.push(...findDtsFiles(full));
5172
5280
  else if (e.name.endsWith(".d.ts")) result.push(full);
5173
5281
  }
@@ -5177,7 +5285,7 @@ function extractLynxModules(files) {
5177
5285
  const modules = [];
5178
5286
  const seen = /* @__PURE__ */ new Set();
5179
5287
  for (const file of files) {
5180
- const content = fs19.readFileSync(file, "utf8");
5288
+ const content = fs20.readFileSync(file, "utf8");
5181
5289
  const regex = /\/\*\*\s*@lynxmodule\s*\*\/\s*export\s+declare\s+class\s+(\w+)/g;
5182
5290
  let m;
5183
5291
  while ((m = regex.exec(content)) !== null) {
@@ -5194,10 +5302,10 @@ var codegen_default = codegen;
5194
5302
  // src/common/devServer.tsx
5195
5303
  import { useState as useState5, useEffect as useEffect3, useRef, useCallback as useCallback4 } from "react";
5196
5304
  import { spawn } from "child_process";
5197
- import fs20 from "fs";
5305
+ import fs21 from "fs";
5198
5306
  import http from "http";
5199
5307
  import os4 from "os";
5200
- import path21 from "path";
5308
+ import path22 from "path";
5201
5309
  import { render as render2, useInput, useApp } from "ink";
5202
5310
  import { WebSocketServer } from "ws";
5203
5311
  import { jsx as jsx10 } from "react/jsx-runtime";
@@ -5213,13 +5321,13 @@ var STATIC_MIME = {
5213
5321
  ".pdf": "application/pdf"
5214
5322
  };
5215
5323
  function sendFileFromDisk(res, absPath) {
5216
- fs20.readFile(absPath, (err, data) => {
5324
+ fs21.readFile(absPath, (err, data) => {
5217
5325
  if (err) {
5218
5326
  res.writeHead(404);
5219
5327
  res.end("Not found");
5220
5328
  return;
5221
5329
  }
5222
- const ext = path21.extname(absPath).toLowerCase();
5330
+ const ext = path22.extname(absPath).toLowerCase();
5223
5331
  res.setHeader("Content-Type", STATIC_MIME[ext] ?? "application/octet-stream");
5224
5332
  res.setHeader("Access-Control-Allow-Origin", "*");
5225
5333
  res.end(data);
@@ -5256,9 +5364,9 @@ function getLanIp() {
5256
5364
  return "localhost";
5257
5365
  }
5258
5366
  function detectPackageManager(cwd) {
5259
- const dir = path21.resolve(cwd);
5260
- if (fs20.existsSync(path21.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5261
- if (fs20.existsSync(path21.join(dir, "bun.lockb")) || fs20.existsSync(path21.join(dir, "bun.lock")))
5367
+ const dir = path22.resolve(cwd);
5368
+ if (fs21.existsSync(path22.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5369
+ if (fs21.existsSync(path22.join(dir, "bun.lockb")) || fs21.existsSync(path22.join(dir, "bun.lock")))
5262
5370
  return { cmd: "bun", args: ["run", "build"] };
5263
5371
  return { cmd: "npm", args: ["run", "build"] };
5264
5372
  }
@@ -5338,8 +5446,8 @@ function DevServerApp({ verbose }) {
5338
5446
  try {
5339
5447
  const resolved = resolveHostPaths();
5340
5448
  const { projectRoot, lynxProjectDir, lynxBundlePath, lynxBundleFile, config } = resolved;
5341
- const distDir = path21.dirname(lynxBundlePath);
5342
- const projectName = path21.basename(lynxProjectDir);
5449
+ const distDir = path22.dirname(lynxBundlePath);
5450
+ const projectName = path22.basename(lynxProjectDir);
5343
5451
  const basePath = `/${projectName}`;
5344
5452
  setUi((s) => ({ ...s, projectName, lynxBundleFile }));
5345
5453
  const preferredPort = config.devServer?.port ?? config.devServer?.httpPort ?? DEFAULT_PORT;
@@ -5349,18 +5457,18 @@ function DevServerApp({ verbose }) {
5349
5457
  }
5350
5458
  const iconPaths = resolveIconPaths(projectRoot, config);
5351
5459
  let iconFilePath = null;
5352
- if (iconPaths?.source && fs20.statSync(iconPaths.source).isFile()) {
5460
+ if (iconPaths?.source && fs21.statSync(iconPaths.source).isFile()) {
5353
5461
  iconFilePath = iconPaths.source;
5354
- } else if (iconPaths?.androidAdaptiveForeground && fs20.statSync(iconPaths.androidAdaptiveForeground).isFile()) {
5462
+ } else if (iconPaths?.androidAdaptiveForeground && fs21.statSync(iconPaths.androidAdaptiveForeground).isFile()) {
5355
5463
  iconFilePath = iconPaths.androidAdaptiveForeground;
5356
5464
  } else if (iconPaths?.android) {
5357
- const androidIcon = path21.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5358
- if (fs20.existsSync(androidIcon)) iconFilePath = androidIcon;
5465
+ const androidIcon = path22.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5466
+ if (fs21.existsSync(androidIcon)) iconFilePath = androidIcon;
5359
5467
  } else if (iconPaths?.ios) {
5360
- const iosIcon = path21.join(iconPaths.ios, "Icon-1024.png");
5361
- if (fs20.existsSync(iosIcon)) iconFilePath = iosIcon;
5468
+ const iosIcon = path22.join(iconPaths.ios, "Icon-1024.png");
5469
+ if (fs21.existsSync(iosIcon)) iconFilePath = iosIcon;
5362
5470
  }
5363
- const iconExt = iconFilePath ? path21.extname(iconFilePath) || ".png" : "";
5471
+ const iconExt = iconFilePath ? path22.extname(iconFilePath) || ".png" : "";
5364
5472
  const runBuild = () => {
5365
5473
  return new Promise((resolve, reject) => {
5366
5474
  const { cmd, args } = detectPackageManager(lynxProjectDir);
@@ -5446,7 +5554,7 @@ function DevServerApp({ verbose }) {
5446
5554
  return;
5447
5555
  }
5448
5556
  if (iconFilePath && (reqPath === `${basePath}/icon` || reqPath === `${basePath}/icon${iconExt}`)) {
5449
- fs20.readFile(iconFilePath, (err, data) => {
5557
+ fs21.readFile(iconFilePath, (err, data) => {
5450
5558
  if (err) {
5451
5559
  res.writeHead(404);
5452
5560
  res.end();
@@ -5472,20 +5580,20 @@ function DevServerApp({ verbose }) {
5472
5580
  res.end();
5473
5581
  return;
5474
5582
  }
5475
- const safe = path21.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, "");
5476
- if (path21.isAbsolute(safe) || safe.startsWith("..")) {
5583
+ const safe = path22.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, "");
5584
+ if (path22.isAbsolute(safe) || safe.startsWith("..")) {
5477
5585
  res.writeHead(403);
5478
5586
  res.end();
5479
5587
  return;
5480
5588
  }
5481
- const allowedRoot = path21.resolve(lynxProjectDir, rootSub);
5482
- const abs = path21.resolve(allowedRoot, safe);
5483
- if (!abs.startsWith(allowedRoot + path21.sep) && abs !== allowedRoot) {
5589
+ const allowedRoot = path22.resolve(lynxProjectDir, rootSub);
5590
+ const abs = path22.resolve(allowedRoot, safe);
5591
+ if (!abs.startsWith(allowedRoot + path22.sep) && abs !== allowedRoot) {
5484
5592
  res.writeHead(403);
5485
5593
  res.end();
5486
5594
  return;
5487
5595
  }
5488
- if (!fs20.existsSync(abs) || !fs20.statSync(abs).isFile()) {
5596
+ if (!fs21.existsSync(abs) || !fs21.statSync(abs).isFile()) {
5489
5597
  res.writeHead(404);
5490
5598
  res.end("Not found");
5491
5599
  return;
@@ -5499,14 +5607,14 @@ function DevServerApp({ verbose }) {
5499
5607
  reqPath = basePath + (reqPath.startsWith("/") ? reqPath : "/" + reqPath);
5500
5608
  }
5501
5609
  const relPath = reqPath.replace(basePath, "").replace(/^\//, "") || lynxBundleFile;
5502
- const filePath = path21.resolve(distDir, relPath);
5503
- const distResolved = path21.resolve(distDir);
5504
- if (!filePath.startsWith(distResolved + path21.sep) && filePath !== distResolved) {
5610
+ const filePath = path22.resolve(distDir, relPath);
5611
+ const distResolved = path22.resolve(distDir);
5612
+ if (!filePath.startsWith(distResolved + path22.sep) && filePath !== distResolved) {
5505
5613
  res.writeHead(403);
5506
5614
  res.end();
5507
5615
  return;
5508
5616
  }
5509
- fs20.readFile(filePath, (err, data) => {
5617
+ fs21.readFile(filePath, (err, data) => {
5510
5618
  if (err) {
5511
5619
  res.writeHead(404);
5512
5620
  res.end("Not found");
@@ -5567,10 +5675,10 @@ function DevServerApp({ verbose }) {
5567
5675
  }
5568
5676
  if (chokidar) {
5569
5677
  const watchPaths = [
5570
- path21.join(lynxProjectDir, "src"),
5571
- path21.join(lynxProjectDir, "lynx.config.ts"),
5572
- path21.join(lynxProjectDir, "lynx.config.js")
5573
- ].filter((p) => fs20.existsSync(p));
5678
+ path22.join(lynxProjectDir, "src"),
5679
+ path22.join(lynxProjectDir, "lynx.config.ts"),
5680
+ path22.join(lynxProjectDir, "lynx.config.js")
5681
+ ].filter((p) => fs21.existsSync(p));
5574
5682
  if (watchPaths.length > 0) {
5575
5683
  const w = chokidar.watch(watchPaths, { ignoreInitial: true });
5576
5684
  w.on("change", async () => {
@@ -5690,10 +5798,10 @@ async function start(opts) {
5690
5798
  var start_default = start;
5691
5799
 
5692
5800
  // src/common/injectHost.ts
5693
- import fs21 from "fs";
5694
- import path22 from "path";
5801
+ import fs22 from "fs";
5802
+ import path23 from "path";
5695
5803
  function readAndSubstitute(templatePath, vars) {
5696
- const raw = fs21.readFileSync(templatePath, "utf-8");
5804
+ const raw = fs22.readFileSync(templatePath, "utf-8");
5697
5805
  return Object.entries(vars).reduce(
5698
5806
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
5699
5807
  raw
@@ -5714,32 +5822,32 @@ async function injectHostAndroid(opts) {
5714
5822
  process.exit(1);
5715
5823
  }
5716
5824
  const androidDir = config.paths?.androidDir ?? "android";
5717
- const rootDir = path22.join(projectRoot, androidDir);
5825
+ const rootDir = path23.join(projectRoot, androidDir);
5718
5826
  const packagePath = packageName.replace(/\./g, "/");
5719
- const javaDir = path22.join(rootDir, "app", "src", "main", "java", packagePath);
5720
- const kotlinDir = path22.join(rootDir, "app", "src", "main", "kotlin", packagePath);
5721
- if (!fs21.existsSync(javaDir) || !fs21.existsSync(kotlinDir)) {
5827
+ const javaDir = path23.join(rootDir, "app", "src", "main", "java", packagePath);
5828
+ const kotlinDir = path23.join(rootDir, "app", "src", "main", "kotlin", packagePath);
5829
+ if (!fs22.existsSync(javaDir) || !fs22.existsSync(kotlinDir)) {
5722
5830
  console.error("\u274C Android project not found. Run `t4l android create` first or ensure android/ exists.");
5723
5831
  process.exit(1);
5724
5832
  }
5725
- const templateDir = path22.join(hostPkg, "android", "templates");
5833
+ const templateDir = path23.join(hostPkg, "android", "templates");
5726
5834
  const vars = { PACKAGE_NAME: packageName, APP_NAME: appName };
5727
5835
  const files = [
5728
- { src: "App.java", dst: path22.join(javaDir, "App.java") },
5729
- { src: "TemplateProvider.java", dst: path22.join(javaDir, "TemplateProvider.java") },
5730
- { src: "MainActivity.kt", dst: path22.join(kotlinDir, "MainActivity.kt") }
5836
+ { src: "App.java", dst: path23.join(javaDir, "App.java") },
5837
+ { src: "TemplateProvider.java", dst: path23.join(javaDir, "TemplateProvider.java") },
5838
+ { src: "MainActivity.kt", dst: path23.join(kotlinDir, "MainActivity.kt") }
5731
5839
  ];
5732
5840
  for (const { src, dst } of files) {
5733
- const srcPath = path22.join(templateDir, src);
5734
- if (!fs21.existsSync(srcPath)) continue;
5735
- if (fs21.existsSync(dst) && !opts?.force) {
5736
- console.log(`\u23ED\uFE0F Skipping ${path22.basename(dst)} (use --force to overwrite)`);
5841
+ const srcPath = path23.join(templateDir, src);
5842
+ if (!fs22.existsSync(srcPath)) continue;
5843
+ if (fs22.existsSync(dst) && !opts?.force) {
5844
+ console.log(`\u23ED\uFE0F Skipping ${path23.basename(dst)} (use --force to overwrite)`);
5737
5845
  continue;
5738
5846
  }
5739
5847
  const content = readAndSubstitute(srcPath, vars);
5740
- fs21.mkdirSync(path22.dirname(dst), { recursive: true });
5741
- fs21.writeFileSync(dst, content);
5742
- console.log(`\u2705 Injected ${path22.basename(dst)}`);
5848
+ fs22.mkdirSync(path23.dirname(dst), { recursive: true });
5849
+ fs22.writeFileSync(dst, content);
5850
+ console.log(`\u2705 Injected ${path23.basename(dst)}`);
5743
5851
  }
5744
5852
  }
5745
5853
  async function injectHostIos(opts) {
@@ -5757,13 +5865,13 @@ async function injectHostIos(opts) {
5757
5865
  process.exit(1);
5758
5866
  }
5759
5867
  const iosDir = config.paths?.iosDir ?? "ios";
5760
- const rootDir = path22.join(projectRoot, iosDir);
5761
- const projectDir = path22.join(rootDir, appName);
5762
- if (!fs21.existsSync(projectDir)) {
5868
+ const rootDir = path23.join(projectRoot, iosDir);
5869
+ const projectDir = path23.join(rootDir, appName);
5870
+ if (!fs22.existsSync(projectDir)) {
5763
5871
  console.error("\u274C iOS project not found. Run `t4l ios create` first or ensure ios/ exists.");
5764
5872
  process.exit(1);
5765
5873
  }
5766
- const templateDir = path22.join(hostPkg, "ios", "templates");
5874
+ const templateDir = path23.join(hostPkg, "ios", "templates");
5767
5875
  const vars = { PACKAGE_NAME: bundleId, APP_NAME: appName, BUNDLE_ID: bundleId };
5768
5876
  const files = [
5769
5877
  "AppDelegate.swift",
@@ -5773,22 +5881,22 @@ async function injectHostIos(opts) {
5773
5881
  "LynxInitProcessor.swift"
5774
5882
  ];
5775
5883
  for (const f of files) {
5776
- const srcPath = path22.join(templateDir, f);
5777
- const dstPath = path22.join(projectDir, f);
5778
- if (!fs21.existsSync(srcPath)) continue;
5779
- if (fs21.existsSync(dstPath) && !opts?.force) {
5884
+ const srcPath = path23.join(templateDir, f);
5885
+ const dstPath = path23.join(projectDir, f);
5886
+ if (!fs22.existsSync(srcPath)) continue;
5887
+ if (fs22.existsSync(dstPath) && !opts?.force) {
5780
5888
  console.log(`\u23ED\uFE0F Skipping ${f} (use --force to overwrite)`);
5781
5889
  continue;
5782
5890
  }
5783
5891
  const content = readAndSubstitute(srcPath, vars);
5784
- fs21.writeFileSync(dstPath, content);
5892
+ fs22.writeFileSync(dstPath, content);
5785
5893
  console.log(`\u2705 Injected ${f}`);
5786
5894
  }
5787
5895
  }
5788
5896
 
5789
5897
  // src/common/buildEmbeddable.ts
5790
- import fs22 from "fs";
5791
- import path23 from "path";
5898
+ import fs23 from "fs";
5899
+ import path24 from "path";
5792
5900
  import { execSync as execSync9 } from "child_process";
5793
5901
  var EMBEDDABLE_DIR = "embeddable";
5794
5902
  var LIB_PACKAGE = "com.tamer.embeddable";
@@ -5865,14 +5973,14 @@ object LynxEmbeddable {
5865
5973
  }
5866
5974
  `;
5867
5975
  function generateAndroidLibrary(outDir, androidDir, projectRoot, lynxBundlePath, lynxBundleFile, modules, abiFilters) {
5868
- const libDir = path23.join(androidDir, "lib");
5869
- const libSrcMain = path23.join(libDir, "src", "main");
5870
- const assetsDir = path23.join(libSrcMain, "assets");
5871
- const kotlinDir = path23.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
5872
- const generatedDir = path23.join(kotlinDir, "generated");
5873
- fs22.mkdirSync(path23.join(androidDir, "gradle"), { recursive: true });
5874
- fs22.mkdirSync(generatedDir, { recursive: true });
5875
- fs22.mkdirSync(assetsDir, { recursive: true });
5976
+ const libDir = path24.join(androidDir, "lib");
5977
+ const libSrcMain = path24.join(libDir, "src", "main");
5978
+ const assetsDir = path24.join(libSrcMain, "assets");
5979
+ const kotlinDir = path24.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
5980
+ const generatedDir = path24.join(kotlinDir, "generated");
5981
+ fs23.mkdirSync(path24.join(androidDir, "gradle"), { recursive: true });
5982
+ fs23.mkdirSync(generatedDir, { recursive: true });
5983
+ fs23.mkdirSync(assetsDir, { recursive: true });
5876
5984
  const androidModules = modules.filter((m) => m.config.android);
5877
5985
  const abiList = abiFilters.map((a) => `"${a}"`).join(", ");
5878
5986
  const settingsContent = `pluginManagement {
@@ -5892,7 +6000,7 @@ include(":lib")
5892
6000
  ${androidModules.map((p) => {
5893
6001
  const gradleName = p.name.replace(/^@/, "").replace(/\//g, "_");
5894
6002
  const sourceDir = p.config.android?.sourceDir || "android";
5895
- const absPath = path23.join(p.packagePath, sourceDir).replace(/\\/g, "/");
6003
+ const absPath = path24.join(p.packagePath, sourceDir).replace(/\\/g, "/");
5896
6004
  return `include(":${gradleName}")
5897
6005
  project(":${gradleName}").projectDir = file("${absPath}")`;
5898
6006
  }).join("\n")}
@@ -5941,10 +6049,10 @@ dependencies {
5941
6049
  ${libDeps}
5942
6050
  }
5943
6051
  `;
5944
- fs22.writeFileSync(path23.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
5945
- fs22.writeFileSync(path23.join(androidDir, "settings.gradle.kts"), settingsContent);
5946
- fs22.writeFileSync(
5947
- path23.join(androidDir, "build.gradle.kts"),
6052
+ fs23.writeFileSync(path24.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
6053
+ fs23.writeFileSync(path24.join(androidDir, "settings.gradle.kts"), settingsContent);
6054
+ fs23.writeFileSync(
6055
+ path24.join(androidDir, "build.gradle.kts"),
5948
6056
  `plugins {
5949
6057
  alias(libs.plugins.android.library) apply false
5950
6058
  alias(libs.plugins.kotlin.android) apply false
@@ -5952,26 +6060,26 @@ ${libDeps}
5952
6060
  }
5953
6061
  `
5954
6062
  );
5955
- fs22.writeFileSync(
5956
- path23.join(androidDir, "gradle.properties"),
6063
+ fs23.writeFileSync(
6064
+ path24.join(androidDir, "gradle.properties"),
5957
6065
  `org.gradle.jvmargs=-Xmx2048m
5958
6066
  android.useAndroidX=true
5959
6067
  kotlin.code.style=official
5960
6068
  `
5961
6069
  );
5962
- fs22.writeFileSync(path23.join(libDir, "build.gradle.kts"), libBuildContent);
5963
- fs22.writeFileSync(
5964
- path23.join(libSrcMain, "AndroidManifest.xml"),
6070
+ fs23.writeFileSync(path24.join(libDir, "build.gradle.kts"), libBuildContent);
6071
+ fs23.writeFileSync(
6072
+ path24.join(libSrcMain, "AndroidManifest.xml"),
5965
6073
  '<?xml version="1.0" encoding="utf-8"?>\n<manifest />'
5966
6074
  );
5967
- fs22.copyFileSync(lynxBundlePath, path23.join(assetsDir, lynxBundleFile));
5968
- fs22.writeFileSync(path23.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
5969
- fs22.writeFileSync(
5970
- path23.join(generatedDir, "GeneratedLynxExtensions.kt"),
6075
+ fs23.copyFileSync(lynxBundlePath, path24.join(assetsDir, lynxBundleFile));
6076
+ fs23.writeFileSync(path24.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
6077
+ fs23.writeFileSync(
6078
+ path24.join(generatedDir, "GeneratedLynxExtensions.kt"),
5971
6079
  generateLynxExtensionsKotlin(modules, LIB_PACKAGE)
5972
6080
  );
5973
- fs22.writeFileSync(
5974
- path23.join(generatedDir, "GeneratedActivityLifecycle.kt"),
6081
+ fs23.writeFileSync(
6082
+ path24.join(generatedDir, "GeneratedActivityLifecycle.kt"),
5975
6083
  generateActivityLifecycleKotlin(modules, LIB_PACKAGE)
5976
6084
  );
5977
6085
  }
@@ -5980,20 +6088,20 @@ async function buildEmbeddable(opts = {}) {
5980
6088
  const { lynxProjectDir, lynxBundlePath, lynxBundleFile, projectRoot, config } = resolved;
5981
6089
  console.log("\u{1F4E6} Building Lynx project (release)...");
5982
6090
  execSync9("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
5983
- if (!fs22.existsSync(lynxBundlePath)) {
6091
+ if (!fs23.existsSync(lynxBundlePath)) {
5984
6092
  console.error(`\u274C Bundle not found at ${lynxBundlePath}`);
5985
6093
  process.exit(1);
5986
6094
  }
5987
- const outDir = path23.join(projectRoot, EMBEDDABLE_DIR);
5988
- fs22.mkdirSync(outDir, { recursive: true });
5989
- const distDir = path23.dirname(lynxBundlePath);
6095
+ const outDir = path24.join(projectRoot, EMBEDDABLE_DIR);
6096
+ fs23.mkdirSync(outDir, { recursive: true });
6097
+ const distDir = path24.dirname(lynxBundlePath);
5990
6098
  copyDistAssets(distDir, outDir, lynxBundleFile);
5991
6099
  const modules = discoverModules(projectRoot);
5992
6100
  const androidModules = modules.filter((m) => m.config.android);
5993
6101
  const abiFilters = resolveAbiFilters(config);
5994
- const androidDir = path23.join(outDir, "android");
5995
- if (fs22.existsSync(androidDir)) fs22.rmSync(androidDir, { recursive: true });
5996
- fs22.mkdirSync(androidDir, { recursive: true });
6102
+ const androidDir = path24.join(outDir, "android");
6103
+ if (fs23.existsSync(androidDir)) fs23.rmSync(androidDir, { recursive: true });
6104
+ fs23.mkdirSync(androidDir, { recursive: true });
5997
6105
  generateAndroidLibrary(
5998
6106
  outDir,
5999
6107
  androidDir,
@@ -6003,23 +6111,23 @@ async function buildEmbeddable(opts = {}) {
6003
6111
  modules,
6004
6112
  abiFilters
6005
6113
  );
6006
- const gradlewPath = path23.join(androidDir, "gradlew");
6114
+ const gradlewPath = path24.join(androidDir, "gradlew");
6007
6115
  const devAppDir = findDevAppPackage(projectRoot);
6008
6116
  const existingGradleDirs = [
6009
- path23.join(projectRoot, "android"),
6010
- devAppDir ? path23.join(devAppDir, "android") : null
6117
+ path24.join(projectRoot, "android"),
6118
+ devAppDir ? path24.join(devAppDir, "android") : null
6011
6119
  ].filter(Boolean);
6012
6120
  let hasWrapper = false;
6013
6121
  for (const d of existingGradleDirs) {
6014
- if (fs22.existsSync(path23.join(d, "gradlew"))) {
6122
+ if (fs23.existsSync(path24.join(d, "gradlew"))) {
6015
6123
  for (const name of ["gradlew", "gradlew.bat", "gradle"]) {
6016
- const src = path23.join(d, name);
6017
- if (fs22.existsSync(src)) {
6018
- const dest = path23.join(androidDir, name);
6019
- if (fs22.statSync(src).isDirectory()) {
6020
- fs22.cpSync(src, dest, { recursive: true });
6124
+ const src = path24.join(d, name);
6125
+ if (fs23.existsSync(src)) {
6126
+ const dest = path24.join(androidDir, name);
6127
+ if (fs23.statSync(src).isDirectory()) {
6128
+ fs23.cpSync(src, dest, { recursive: true });
6021
6129
  } else {
6022
- fs22.copyFileSync(src, dest);
6130
+ fs23.copyFileSync(src, dest);
6023
6131
  }
6024
6132
  }
6025
6133
  }
@@ -6038,10 +6146,10 @@ async function buildEmbeddable(opts = {}) {
6038
6146
  console.error("\u274C Android AAR build failed. Run manually: cd embeddable/android && ./gradlew :lib:assembleRelease");
6039
6147
  throw e;
6040
6148
  }
6041
- const aarSrc = path23.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6042
- const aarDest = path23.join(outDir, "tamer-embeddable.aar");
6043
- if (fs22.existsSync(aarSrc)) {
6044
- fs22.copyFileSync(aarSrc, aarDest);
6149
+ const aarSrc = path24.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6150
+ const aarDest = path24.join(outDir, "tamer-embeddable.aar");
6151
+ if (fs23.existsSync(aarSrc)) {
6152
+ fs23.copyFileSync(aarSrc, aarDest);
6045
6153
  console.log(` - tamer-embeddable.aar`);
6046
6154
  }
6047
6155
  const snippetAndroid = `// Add to your app's build.gradle:
@@ -6052,7 +6160,7 @@ async function buildEmbeddable(opts = {}) {
6052
6160
  // LynxEmbeddable.init(applicationContext)
6053
6161
  // val lynxView = LynxEmbeddable.buildLynxView(containerViewGroup)
6054
6162
  `;
6055
- fs22.writeFileSync(path23.join(outDir, "snippet-android.kt"), snippetAndroid);
6163
+ fs23.writeFileSync(path24.join(outDir, "snippet-android.kt"), snippetAndroid);
6056
6164
  generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules);
6057
6165
  const readme = `# Embeddable Lynx Bundle
6058
6166
 
@@ -6083,7 +6191,7 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6083
6191
 
6084
6192
  - [Embedding LynxView](https://lynxjs.org/guide/embed-lynx-to-native)
6085
6193
  `;
6086
- fs22.writeFileSync(path23.join(outDir, "README.md"), readme);
6194
+ fs23.writeFileSync(path24.join(outDir, "README.md"), readme);
6087
6195
  console.log(`
6088
6196
  \u2705 Embeddable output at ${outDir}/`);
6089
6197
  console.log(" - main.lynx.bundle");
@@ -6095,20 +6203,20 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6095
6203
  console.log(" - README.md");
6096
6204
  }
6097
6205
  function generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules) {
6098
- const iosDir = path23.join(outDir, "ios");
6099
- const podDir = path23.join(iosDir, "TamerEmbeddable");
6100
- const resourcesDir = path23.join(podDir, "Resources");
6101
- fs22.mkdirSync(resourcesDir, { recursive: true });
6102
- fs22.copyFileSync(lynxBundlePath, path23.join(resourcesDir, lynxBundleFile));
6206
+ const iosDir = path24.join(outDir, "ios");
6207
+ const podDir = path24.join(iosDir, "TamerEmbeddable");
6208
+ const resourcesDir = path24.join(podDir, "Resources");
6209
+ fs23.mkdirSync(resourcesDir, { recursive: true });
6210
+ fs23.copyFileSync(lynxBundlePath, path24.join(resourcesDir, lynxBundleFile));
6103
6211
  const iosModules = modules.filter((m) => m.config.ios);
6104
6212
  const podDeps = iosModules.map((p) => {
6105
6213
  const podspecPath = p.config.ios?.podspecPath || ".";
6106
- const podspecDir = path23.join(p.packagePath, podspecPath);
6107
- if (!fs22.existsSync(podspecDir)) return null;
6108
- const files = fs22.readdirSync(podspecDir);
6214
+ const podspecDir = path24.join(p.packagePath, podspecPath);
6215
+ if (!fs23.existsSync(podspecDir)) return null;
6216
+ const files = fs23.readdirSync(podspecDir);
6109
6217
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6110
6218
  const podName = podspecFile ? podspecFile.replace(".podspec", "") : p.name.split("/").pop().replace(/-/g, "");
6111
- const absPath = path23.resolve(podspecDir);
6219
+ const absPath = path24.resolve(podspecDir);
6112
6220
  return { podName, absPath };
6113
6221
  }).filter(Boolean);
6114
6222
  const podDepLines = podDeps.map((d) => ` s.dependency '${d.podName}'`).join("\n");
@@ -6148,9 +6256,9 @@ end
6148
6256
  });
6149
6257
  const swiftImports = iosModules.map((p) => {
6150
6258
  const podspecPath = p.config.ios?.podspecPath || ".";
6151
- const podspecDir = path23.join(p.packagePath, podspecPath);
6152
- if (!fs22.existsSync(podspecDir)) return null;
6153
- const files = fs22.readdirSync(podspecDir);
6259
+ const podspecDir = path24.join(p.packagePath, podspecPath);
6260
+ if (!fs23.existsSync(podspecDir)) return null;
6261
+ const files = fs23.readdirSync(podspecDir);
6154
6262
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6155
6263
  return podspecFile ? podspecFile.replace(".podspec", "") : null;
6156
6264
  }).filter(Boolean);
@@ -6169,17 +6277,17 @@ ${regBlock}
6169
6277
  }
6170
6278
  }
6171
6279
  `;
6172
- fs22.writeFileSync(path23.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6173
- fs22.writeFileSync(path23.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6174
- const absIosDir = path23.resolve(iosDir);
6280
+ fs23.writeFileSync(path24.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6281
+ fs23.writeFileSync(path24.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6282
+ const absIosDir = path24.resolve(iosDir);
6175
6283
  const podfileSnippet = `# Paste into your app target in Podfile:
6176
6284
 
6177
6285
  pod 'TamerEmbeddable', :path => '${absIosDir}'
6178
6286
  ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6179
6287
  `;
6180
- fs22.writeFileSync(path23.join(iosDir, "Podfile.snippet"), podfileSnippet);
6181
- fs22.writeFileSync(
6182
- path23.join(outDir, "snippet-ios.swift"),
6288
+ fs23.writeFileSync(path24.join(iosDir, "Podfile.snippet"), podfileSnippet);
6289
+ fs23.writeFileSync(
6290
+ path24.join(outDir, "snippet-ios.swift"),
6183
6291
  `// Add LynxEmbeddable.initEnvironment() in your AppDelegate/SceneDelegate before presenting LynxView.
6184
6292
  // Then create LynxView with your bundle URL (main.lynx.bundle is in the pod resources).
6185
6293
  `
@@ -6187,8 +6295,8 @@ ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6187
6295
  }
6188
6296
 
6189
6297
  // src/common/add.ts
6190
- import fs23 from "fs";
6191
- import path24 from "path";
6298
+ import fs24 from "fs";
6299
+ import path25 from "path";
6192
6300
  import { execFile, execSync as execSync10 } from "child_process";
6193
6301
  import { promisify } from "util";
6194
6302
  import semver from "semver";
@@ -6245,9 +6353,9 @@ async function normalizeTamerInstallSpec(pkg) {
6245
6353
  return `${pkg}@prerelease`;
6246
6354
  }
6247
6355
  function detectPackageManager2(cwd) {
6248
- const dir = path24.resolve(cwd);
6249
- if (fs23.existsSync(path24.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6250
- if (fs23.existsSync(path24.join(dir, "bun.lockb"))) return "bun";
6356
+ const dir = path25.resolve(cwd);
6357
+ if (fs24.existsSync(path25.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6358
+ if (fs24.existsSync(path25.join(dir, "bun.lockb"))) return "bun";
6251
6359
  return "npm";
6252
6360
  }
6253
6361
  function runInstall(cwd, packages, pm) {
@@ -6299,13 +6407,13 @@ async function add(packages = []) {
6299
6407
  // src/common/signing.tsx
6300
6408
  import { useState as useState6, useEffect as useEffect4, useRef as useRef2 } from "react";
6301
6409
  import { render as render3, Text as Text10, Box as Box9 } from "ink";
6302
- import fs26 from "fs";
6303
- import path27 from "path";
6410
+ import fs27 from "fs";
6411
+ import path28 from "path";
6304
6412
 
6305
6413
  // src/common/androidKeystore.ts
6306
6414
  import { execFileSync } from "child_process";
6307
- import fs24 from "fs";
6308
- import path25 from "path";
6415
+ import fs25 from "fs";
6416
+ import path26 from "path";
6309
6417
  function normalizeJavaHome(raw) {
6310
6418
  if (!raw) return void 0;
6311
6419
  const t = raw.trim().replace(/^["']|["']$/g, "");
@@ -6318,7 +6426,7 @@ function discoverJavaHomeMacOs() {
6318
6426
  encoding: "utf8",
6319
6427
  stdio: ["pipe", "pipe", "pipe"]
6320
6428
  }).trim().split("\n")[0]?.trim();
6321
- if (out && fs24.existsSync(path25.join(out, "bin", "keytool"))) return out;
6429
+ if (out && fs25.existsSync(path26.join(out, "bin", "keytool"))) return out;
6322
6430
  } catch {
6323
6431
  }
6324
6432
  return void 0;
@@ -6328,13 +6436,13 @@ function resolveKeytoolPath() {
6328
6436
  const win = process.platform === "win32";
6329
6437
  const keytoolName = win ? "keytool.exe" : "keytool";
6330
6438
  if (jh) {
6331
- const p = path25.join(jh, "bin", keytoolName);
6332
- if (fs24.existsSync(p)) return p;
6439
+ const p = path26.join(jh, "bin", keytoolName);
6440
+ if (fs25.existsSync(p)) return p;
6333
6441
  }
6334
6442
  const mac = discoverJavaHomeMacOs();
6335
6443
  if (mac) {
6336
- const p = path25.join(mac, "bin", keytoolName);
6337
- if (fs24.existsSync(p)) return p;
6444
+ const p = path26.join(mac, "bin", keytoolName);
6445
+ if (fs25.existsSync(p)) return p;
6338
6446
  }
6339
6447
  return "keytool";
6340
6448
  }
@@ -6349,16 +6457,16 @@ function keytoolAvailable() {
6349
6457
  };
6350
6458
  if (tryRun("keytool")) return true;
6351
6459
  const fromJavaHome = resolveKeytoolPath();
6352
- if (fromJavaHome !== "keytool" && fs24.existsSync(fromJavaHome)) {
6460
+ if (fromJavaHome !== "keytool" && fs25.existsSync(fromJavaHome)) {
6353
6461
  return tryRun(fromJavaHome);
6354
6462
  }
6355
6463
  return false;
6356
6464
  }
6357
6465
  function generateReleaseKeystore(opts) {
6358
6466
  const keytool = resolveKeytoolPath();
6359
- const dir = path25.dirname(opts.keystoreAbsPath);
6360
- fs24.mkdirSync(dir, { recursive: true });
6361
- if (fs24.existsSync(opts.keystoreAbsPath)) {
6467
+ const dir = path26.dirname(opts.keystoreAbsPath);
6468
+ fs25.mkdirSync(dir, { recursive: true });
6469
+ if (fs25.existsSync(opts.keystoreAbsPath)) {
6362
6470
  throw new Error(`Keystore already exists: ${opts.keystoreAbsPath}`);
6363
6471
  }
6364
6472
  if (!opts.storePassword || !opts.keyPassword) {
@@ -6396,13 +6504,13 @@ function generateReleaseKeystore(opts) {
6396
6504
  }
6397
6505
 
6398
6506
  // src/common/appendEnvFile.ts
6399
- import fs25 from "fs";
6400
- import path26 from "path";
6507
+ import fs26 from "fs";
6508
+ import path27 from "path";
6401
6509
  import { parse } from "dotenv";
6402
6510
  function keysDefinedInFile(filePath) {
6403
- if (!fs25.existsSync(filePath)) return /* @__PURE__ */ new Set();
6511
+ if (!fs26.existsSync(filePath)) return /* @__PURE__ */ new Set();
6404
6512
  try {
6405
- return new Set(Object.keys(parse(fs25.readFileSync(filePath, "utf8"))));
6513
+ return new Set(Object.keys(parse(fs26.readFileSync(filePath, "utf8"))));
6406
6514
  } catch {
6407
6515
  return /* @__PURE__ */ new Set();
6408
6516
  }
@@ -6417,11 +6525,11 @@ function formatEnvLine(key, value) {
6417
6525
  function appendEnvVarsIfMissing(projectRoot, vars) {
6418
6526
  const entries = Object.entries(vars).filter(([, v]) => v !== void 0 && v !== "");
6419
6527
  if (entries.length === 0) return null;
6420
- const envLocal = path26.join(projectRoot, ".env.local");
6421
- const envDefault = path26.join(projectRoot, ".env");
6528
+ const envLocal = path27.join(projectRoot, ".env.local");
6529
+ const envDefault = path27.join(projectRoot, ".env");
6422
6530
  let target;
6423
- if (fs25.existsSync(envLocal)) target = envLocal;
6424
- else if (fs25.existsSync(envDefault)) target = envDefault;
6531
+ if (fs26.existsSync(envLocal)) target = envLocal;
6532
+ else if (fs26.existsSync(envDefault)) target = envDefault;
6425
6533
  else target = envLocal;
6426
6534
  const existing = keysDefinedInFile(target);
6427
6535
  const lines = [];
@@ -6433,20 +6541,20 @@ function appendEnvVarsIfMissing(projectRoot, vars) {
6433
6541
  }
6434
6542
  if (lines.length === 0) {
6435
6543
  return {
6436
- file: path26.basename(target),
6544
+ file: path27.basename(target),
6437
6545
  keys: [],
6438
6546
  skippedAll: entries.length > 0
6439
6547
  };
6440
6548
  }
6441
6549
  let prefix = "";
6442
- if (fs25.existsSync(target)) {
6443
- const cur = fs25.readFileSync(target, "utf8");
6550
+ if (fs26.existsSync(target)) {
6551
+ const cur = fs26.readFileSync(target, "utf8");
6444
6552
  prefix = cur.length === 0 ? "" : cur.endsWith("\n") ? cur : `${cur}
6445
6553
  `;
6446
6554
  }
6447
6555
  const block = lines.join("\n") + "\n";
6448
- fs25.writeFileSync(target, prefix + block, "utf8");
6449
- return { file: path26.basename(target), keys: appendedKeys };
6556
+ fs26.writeFileSync(target, prefix + block, "utf8");
6557
+ return { file: path27.basename(target), keys: appendedKeys };
6450
6558
  }
6451
6559
 
6452
6560
  // src/common/signing.tsx
@@ -6552,7 +6660,7 @@ function SigningWizard({ platform: initialPlatform }) {
6552
6660
  try {
6553
6661
  const resolved = resolveHostPaths();
6554
6662
  const rel = state.android.genKeystorePath.trim() || "android/release.keystore";
6555
- abs = path27.isAbsolute(rel) ? rel : path27.join(resolved.projectRoot, rel);
6663
+ abs = path28.isAbsolute(rel) ? rel : path28.join(resolved.projectRoot, rel);
6556
6664
  const alias = state.android.keyAlias.trim() || "release";
6557
6665
  const pw = state.android.genPassword;
6558
6666
  const pkg = resolved.config.android?.packageName ?? "com.example.app";
@@ -6579,7 +6687,7 @@ function SigningWizard({ platform: initialPlatform }) {
6579
6687
  }));
6580
6688
  } catch (e) {
6581
6689
  const msg = e.message;
6582
- if (abs && fs26.existsSync(abs) && (msg.includes("already exists") || msg.includes("Keystore already exists"))) {
6690
+ if (abs && fs27.existsSync(abs) && (msg.includes("already exists") || msg.includes("Keystore already exists"))) {
6583
6691
  if (cancelled || runId !== generateRunId.current) return;
6584
6692
  const rel = state.android.genKeystorePath.trim() || "android/release.keystore";
6585
6693
  const alias = state.android.keyAlias.trim() || "release";
@@ -6619,11 +6727,11 @@ function SigningWizard({ platform: initialPlatform }) {
6619
6727
  const saveConfig = async () => {
6620
6728
  try {
6621
6729
  const resolved = resolveHostPaths();
6622
- const configPath = path27.join(resolved.projectRoot, "tamer.config.json");
6730
+ const configPath = path28.join(resolved.projectRoot, "tamer.config.json");
6623
6731
  let config = {};
6624
6732
  let androidEnvAppend = null;
6625
- if (fs26.existsSync(configPath)) {
6626
- config = JSON.parse(fs26.readFileSync(configPath, "utf8"));
6733
+ if (fs27.existsSync(configPath)) {
6734
+ config = JSON.parse(fs27.readFileSync(configPath, "utf8"));
6627
6735
  }
6628
6736
  if (state.platform === "android" || state.platform === "both") {
6629
6737
  config.android = config.android || {};
@@ -6650,10 +6758,10 @@ function SigningWizard({ platform: initialPlatform }) {
6650
6758
  ...state.ios.provisioningProfileSpecifier && { provisioningProfileSpecifier: state.ios.provisioningProfileSpecifier }
6651
6759
  };
6652
6760
  }
6653
- fs26.writeFileSync(configPath, JSON.stringify(config, null, 2));
6654
- const gitignorePath = path27.join(resolved.projectRoot, ".gitignore");
6655
- if (fs26.existsSync(gitignorePath)) {
6656
- let gitignore = fs26.readFileSync(gitignorePath, "utf8");
6761
+ fs27.writeFileSync(configPath, JSON.stringify(config, null, 2));
6762
+ const gitignorePath = path28.join(resolved.projectRoot, ".gitignore");
6763
+ if (fs27.existsSync(gitignorePath)) {
6764
+ let gitignore = fs27.readFileSync(gitignorePath, "utf8");
6657
6765
  const additions = [
6658
6766
  ".env.local",
6659
6767
  "*.jks",
@@ -6666,7 +6774,7 @@ ${addition}
6666
6774
  `;
6667
6775
  }
6668
6776
  }
6669
- fs26.writeFileSync(gitignorePath, gitignore);
6777
+ fs27.writeFileSync(gitignorePath, gitignore);
6670
6778
  }
6671
6779
  setState((s) => ({
6672
6780
  ...s,
@@ -6924,13 +7032,13 @@ async function signing(platform) {
6924
7032
  }
6925
7033
 
6926
7034
  // src/common/productionSigning.ts
6927
- import fs27 from "fs";
6928
- import path28 from "path";
7035
+ import fs28 from "fs";
7036
+ import path29 from "path";
6929
7037
  function isAndroidSigningConfigured(resolved) {
6930
7038
  const signing2 = resolved.config.android?.signing;
6931
7039
  const hasConfig = Boolean(signing2?.keystoreFile?.trim() && signing2?.keyAlias?.trim());
6932
- const signingProps = path28.join(resolved.androidDir, "signing.properties");
6933
- const hasProps = fs27.existsSync(signingProps);
7040
+ const signingProps = path29.join(resolved.androidDir, "signing.properties");
7041
+ const hasProps = fs28.existsSync(signingProps);
6934
7042
  return hasConfig || hasProps;
6935
7043
  }
6936
7044
  function isIosSigningConfigured(resolved) {
@@ -6961,11 +7069,11 @@ function assertProductionSigningReady(filter) {
6961
7069
 
6962
7070
  // index.ts
6963
7071
  function readCliVersion() {
6964
- const root = path29.dirname(fileURLToPath(import.meta.url));
6965
- const here = path29.join(root, "package.json");
6966
- const parent = path29.join(root, "..", "package.json");
6967
- const pkgPath = fs28.existsSync(here) ? here : parent;
6968
- return JSON.parse(fs28.readFileSync(pkgPath, "utf8")).version;
7072
+ const root = path30.dirname(fileURLToPath(import.meta.url));
7073
+ const here = path30.join(root, "package.json");
7074
+ const parent = path30.join(root, "..", "package.json");
7075
+ const pkgPath = fs29.existsSync(here) ? here : parent;
7076
+ return JSON.parse(fs29.readFileSync(pkgPath, "utf8")).version;
6969
7077
  }
6970
7078
  var version = readCliVersion();
6971
7079
  function validateBuildMode(debug, release, production) {
@@ -7183,10 +7291,10 @@ program.command("ios <subcommand>").description("(Legacy) Use: t4l <command> ios
7183
7291
  process.exit(1);
7184
7292
  });
7185
7293
  program.command("autolink-toggle").alias("autolink").description("Toggle autolink on/off in tamer.config.json (controls postinstall linking)").action(async () => {
7186
- const configPath = path29.join(process.cwd(), "tamer.config.json");
7294
+ const configPath = path30.join(process.cwd(), "tamer.config.json");
7187
7295
  let config = {};
7188
- if (fs28.existsSync(configPath)) {
7189
- config = JSON.parse(fs28.readFileSync(configPath, "utf8"));
7296
+ if (fs29.existsSync(configPath)) {
7297
+ config = JSON.parse(fs29.readFileSync(configPath, "utf8"));
7190
7298
  }
7191
7299
  if (config.autolink) {
7192
7300
  delete config.autolink;
@@ -7195,7 +7303,7 @@ program.command("autolink-toggle").alias("autolink").description("Toggle autolin
7195
7303
  config.autolink = true;
7196
7304
  console.log("Autolink enabled in tamer.config.json");
7197
7305
  }
7198
- fs28.writeFileSync(configPath, JSON.stringify(config, null, 2));
7306
+ fs29.writeFileSync(configPath, JSON.stringify(config, null, 2));
7199
7307
  console.log(`Updated ${configPath}`);
7200
7308
  });
7201
7309
  if (process.argv.length <= 2 || process.argv.length === 3 && process.argv[2] === "init") {