@tamer4lynx/cli 0.0.1 → 0.0.3

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 +10 -0
  2. package/dist/index.js +220 -237
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -243,6 +243,16 @@ Extensions are discovered via **lynx.ext.json** (RFC standard) or **tamer.json**
243
243
 
244
244
  ---
245
245
 
246
+ ## Limitations
247
+
248
+ - **tamer-transports** — Fetch, WebSocket, and EventSource polyfills are not fully tested. Report issues on GitHub.
249
+ - **t4l add** — Does not yet track installed versions for compatibility (Expo-style). Planned for a future release.
250
+ - **tamer-router** — Designed for @lynx-js/react (Stack, Tabs, react-router). Other bindings (VueLynx, miso-lynx) can use native modules and tooling but not the router.
251
+ - **Host dependencies** — Networking (fetch), font loading, and native modules depend on the Lynx host implementation.
252
+ - **iOS development** — Requires macOS and Xcode for building and running.
253
+
254
+ ---
255
+
246
256
  ## Roadmap
247
257
 
248
258
  * [x] Fix iOS linking
package/dist/index.js CHANGED
@@ -1,12 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/suppress-punycode-warning.ts
4
+ process.on("warning", (w) => {
5
+ if (w.name === "DeprecationWarning" && /punycode/i.test(w.message)) return;
6
+ console.warn(w.toString());
7
+ });
8
+
3
9
  // index.ts
4
- import fs25 from "fs";
10
+ import fs24 from "fs";
5
11
  import path25 from "path";
6
12
  import { program } from "commander";
7
13
 
8
14
  // package.json
9
- var version = "0.0.1";
15
+ var version = "0.0.3";
10
16
 
11
17
  // src/android/create.ts
12
18
  import fs3 from "fs";
@@ -207,6 +213,7 @@ function findRepoRoot(start2) {
207
213
  }
208
214
  function findDevAppPackage(projectRoot) {
209
215
  const candidates = [
216
+ path2.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-app"),
210
217
  path2.join(projectRoot, "node_modules", "tamer-dev-app"),
211
218
  path2.join(projectRoot, "packages", "tamer-dev-app"),
212
219
  path2.join(path2.dirname(projectRoot), "tamer-dev-app")
@@ -220,6 +227,7 @@ function findDevAppPackage(projectRoot) {
220
227
  }
221
228
  function findDevClientPackage(projectRoot) {
222
229
  const candidates = [
230
+ path2.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-client"),
223
231
  path2.join(projectRoot, "node_modules", "tamer-dev-client"),
224
232
  path2.join(projectRoot, "packages", "tamer-dev-client"),
225
233
  path2.join(path2.dirname(projectRoot), "tamer-dev-client")
@@ -315,17 +323,25 @@ function resolveIconPaths(projectRoot, config) {
315
323
  }
316
324
  return Object.keys(out).length ? out : null;
317
325
  }
318
- function resolveDevAppPaths(repoRoot) {
319
- const devAppDir = path2.join(repoRoot, "packages", "tamer-dev-app");
326
+ function resolveDevAppPaths(searchRoot) {
327
+ const devAppDir = findDevAppPackage(searchRoot) ?? findDevAppPackage(findRepoRoot(searchRoot));
328
+ if (!devAppDir) {
329
+ throw new Error("tamer-dev-app not found. Add @tamer4lynx/tamer-dev-app to dependencies, or run from the tamer4lynx monorepo.");
330
+ }
320
331
  const configPath = path2.join(devAppDir, "tamer.config.json");
321
332
  if (!fs2.existsSync(configPath)) {
322
- throw new Error("packages/tamer-dev-app/tamer.config.json not found.");
333
+ throw new Error(`tamer.config.json not found in ${devAppDir}`);
323
334
  }
324
335
  const config = JSON.parse(fs2.readFileSync(configPath, "utf8"));
325
336
  const packageName = config.android?.packageName ?? "com.nanofuxion.tamerdevapp";
326
337
  const androidDirRel = config.paths?.androidDir ?? "android";
327
338
  const androidDir = path2.join(devAppDir, androidDirRel);
328
- const devClientDir = findDevClientPackage(repoRoot) ?? path2.join(repoRoot, "packages", "tamer-dev-client");
339
+ const inDevAppScoped = path2.join(devAppDir, "node_modules", "@tamer4lynx", "tamer-dev-client");
340
+ const inDevAppFlat = path2.join(devAppDir, "node_modules", "tamer-dev-client");
341
+ const devClientDir = findDevClientPackage(searchRoot) ?? findDevClientPackage(findRepoRoot(searchRoot)) ?? (fs2.existsSync(path2.join(inDevAppScoped, "package.json")) ? inDevAppScoped : null) ?? (fs2.existsSync(path2.join(inDevAppFlat, "package.json")) ? inDevAppFlat : null);
342
+ if (!devClientDir || !fs2.existsSync(devClientDir)) {
343
+ throw new Error("tamer-dev-client not found. Add @tamer4lynx/tamer-dev-client (or tamer-dev-app pulls it in).");
344
+ }
329
345
  const lynxBundlePath = path2.join(devClientDir, DEFAULT_BUNDLE_ROOT, "dev-client.lynx.bundle");
330
346
  return {
331
347
  projectRoot: devAppDir,
@@ -913,30 +929,13 @@ function readAndSubstituteTemplate(templatePath, vars) {
913
929
  raw
914
930
  );
915
931
  }
916
- function findRepoRoot2(start2) {
917
- let dir = path3.resolve(start2);
918
- const root = path3.parse(dir).root;
919
- while (dir !== root) {
920
- const pkgPath = path3.join(dir, "package.json");
921
- if (fs3.existsSync(pkgPath)) {
922
- try {
923
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
924
- if (pkg.workspaces) return dir;
925
- } catch {
926
- }
927
- }
928
- dir = path3.dirname(dir);
929
- }
930
- return start2;
931
- }
932
932
  var create = async (opts = {}) => {
933
933
  const target = opts.target ?? "host";
934
934
  const origCwd = process.cwd();
935
935
  if (target === "dev-app") {
936
- const repoRoot = findRepoRoot2(origCwd);
937
- const devAppDir = path3.join(repoRoot, "packages", "tamer-dev-app");
938
- if (!fs3.existsSync(path3.join(devAppDir, "tamer.config.json"))) {
939
- console.error("\u274C packages/tamer-dev-app/tamer.config.json not found.");
936
+ const devAppDir = findDevAppPackage(origCwd) ?? findDevAppPackage(findRepoRoot(origCwd));
937
+ if (!devAppDir || !fs3.existsSync(path3.join(devAppDir, "tamer.config.json"))) {
938
+ console.error("\u274C tamer-dev-app not found. Add @tamer4lynx/tamer-dev-app to dependencies.");
940
939
  process.exit(1);
941
940
  }
942
941
  process.chdir(devAppDir);
@@ -2087,22 +2086,6 @@ $1$2`);
2087
2086
  var syncDevClient_default = syncDevClient;
2088
2087
 
2089
2088
  // src/android/bundle.ts
2090
- function findRepoRoot3(start2) {
2091
- let dir = path9.resolve(start2);
2092
- const root = path9.parse(dir).root;
2093
- while (dir !== root) {
2094
- const pkgPath = path9.join(dir, "package.json");
2095
- if (fs9.existsSync(pkgPath)) {
2096
- try {
2097
- const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf8"));
2098
- if (pkg.workspaces) return dir;
2099
- } catch {
2100
- }
2101
- }
2102
- dir = path9.dirname(dir);
2103
- }
2104
- return start2;
2105
- }
2106
2089
  async function bundleAndDeploy(opts = {}) {
2107
2090
  const target = opts.target ?? "host";
2108
2091
  const release = opts.release === true;
@@ -2110,8 +2093,7 @@ async function bundleAndDeploy(opts = {}) {
2110
2093
  let resolved;
2111
2094
  try {
2112
2095
  if (target === "dev-app") {
2113
- const repoRoot = findRepoRoot3(origCwd);
2114
- resolved = resolveDevAppPaths(repoRoot);
2096
+ resolved = resolveDevAppPaths(origCwd);
2115
2097
  const devAppDir = resolved.projectRoot;
2116
2098
  const androidDir = resolved.androidDir;
2117
2099
  if (!fs9.existsSync(androidDir)) {
@@ -2183,28 +2165,22 @@ async function bundleAndDeploy(opts = {}) {
2183
2165
  var bundle_default = bundleAndDeploy;
2184
2166
 
2185
2167
  // src/android/build.ts
2186
- import fs10 from "fs";
2187
2168
  import path10 from "path";
2188
2169
  import { execSync as execSync3 } from "child_process";
2189
- function findRepoRoot4(start2) {
2190
- let dir = path10.resolve(start2);
2191
- const root = path10.parse(dir).root;
2192
- while (dir !== root) {
2193
- const pkgPath = path10.join(dir, "package.json");
2194
- if (fs10.existsSync(pkgPath)) {
2195
- try {
2196
- const pkg = JSON.parse(fs10.readFileSync(pkgPath, "utf8"));
2197
- if (pkg.workspaces) return dir;
2198
- } catch {
2199
- }
2200
- }
2201
- dir = path10.dirname(dir);
2202
- }
2203
- return start2;
2204
- }
2205
2170
  async function buildApk(opts = {}) {
2206
2171
  const target = opts.target ?? "host";
2207
- const resolved = target === "dev-app" ? resolveDevAppPaths(findRepoRoot4(process.cwd())) : resolveHostPaths();
2172
+ let resolved;
2173
+ try {
2174
+ resolved = target === "dev-app" ? resolveDevAppPaths(process.cwd()) : resolveHostPaths();
2175
+ } catch (error) {
2176
+ const msg = error instanceof Error ? error.message : String(error);
2177
+ if (target === "dev-app") {
2178
+ console.error(`\u274C ${msg}`);
2179
+ console.error(" Add @tamer4lynx/tamer-dev-app to dependencies, or use -t host to build your app.");
2180
+ process.exit(1);
2181
+ }
2182
+ throw error;
2183
+ }
2208
2184
  await bundle_default({ target, release: opts.release });
2209
2185
  const androidDir = resolved.androidDir;
2210
2186
  const gradlew = path10.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
@@ -2232,12 +2208,12 @@ async function buildApk(opts = {}) {
2232
2208
  var build_default = buildApk;
2233
2209
 
2234
2210
  // src/ios/create.ts
2235
- import fs12 from "fs";
2211
+ import fs11 from "fs";
2236
2212
  import path12 from "path";
2237
2213
 
2238
2214
  // src/ios/getPod.ts
2239
2215
  import { execSync as execSync4 } from "child_process";
2240
- import fs11 from "fs";
2216
+ import fs10 from "fs";
2241
2217
  import path11 from "path";
2242
2218
  function isCocoaPodsInstalled() {
2243
2219
  try {
@@ -2261,7 +2237,7 @@ async function setupCocoaPods(rootDir) {
2261
2237
  try {
2262
2238
  console.log("\u{1F4E6} CocoaPods is installed. Proceeding with dependency installation...");
2263
2239
  const podfilePath = path11.join(rootDir, "Podfile");
2264
- if (!fs11.existsSync(podfilePath)) {
2240
+ if (!fs10.existsSync(podfilePath)) {
2265
2241
  throw new Error(`Podfile not found at ${podfilePath}`);
2266
2242
  }
2267
2243
  console.log(`\u{1F680} Executing pod install in: ${rootDir}`);
@@ -2279,7 +2255,7 @@ async function setupCocoaPods(rootDir) {
2279
2255
  // src/ios/create.ts
2280
2256
  import { randomBytes } from "crypto";
2281
2257
  function readAndSubstituteTemplate3(templatePath, vars) {
2282
- const raw = fs12.readFileSync(templatePath, "utf-8");
2258
+ const raw = fs11.readFileSync(templatePath, "utf-8");
2283
2259
  return Object.entries(vars).reduce(
2284
2260
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2285
2261
  raw
@@ -2307,12 +2283,12 @@ var create2 = () => {
2307
2283
  const xcodeprojDir = path12.join(rootDir, `${appName}.xcodeproj`);
2308
2284
  const bridgingHeader = `${appName}-Bridging-Header.h`;
2309
2285
  function writeFile3(filePath, content) {
2310
- fs12.mkdirSync(path12.dirname(filePath), { recursive: true });
2311
- fs12.writeFileSync(filePath, content.trimStart(), "utf8");
2286
+ fs11.mkdirSync(path12.dirname(filePath), { recursive: true });
2287
+ fs11.writeFileSync(filePath, content.trimStart(), "utf8");
2312
2288
  }
2313
- if (fs12.existsSync(rootDir)) {
2289
+ if (fs11.existsSync(rootDir)) {
2314
2290
  console.log(`\u{1F9F9} Removing existing directory: ${rootDir}`);
2315
- fs12.rmSync(rootDir, { recursive: true, force: true });
2291
+ fs11.rmSync(rootDir, { recursive: true, force: true });
2316
2292
  }
2317
2293
  console.log(`\u{1F680} Creating a new Tamer4Lynx project in: ${rootDir}`);
2318
2294
  const ids = {
@@ -2411,7 +2387,7 @@ end
2411
2387
  const templateDir = path12.join(hostPkg, "ios", "templates");
2412
2388
  for (const f of ["AppDelegate.swift", "SceneDelegate.swift", "ViewController.swift", "LynxProvider.swift", "LynxInitProcessor.swift"]) {
2413
2389
  const srcPath = path12.join(templateDir, f);
2414
- if (fs12.existsSync(srcPath)) {
2390
+ if (fs11.existsSync(srcPath)) {
2415
2391
  writeFile3(path12.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2416
2392
  }
2417
2393
  }
@@ -2642,23 +2618,23 @@ final class LynxInitProcessor {
2642
2618
  </plist>
2643
2619
  `);
2644
2620
  const appIconDir = path12.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
2645
- fs12.mkdirSync(appIconDir, { recursive: true });
2621
+ fs11.mkdirSync(appIconDir, { recursive: true });
2646
2622
  const iconPaths = resolveIconPaths(process.cwd(), config);
2647
2623
  if (iconPaths?.ios) {
2648
- const entries = fs12.readdirSync(iconPaths.ios, { withFileTypes: true });
2624
+ const entries = fs11.readdirSync(iconPaths.ios, { withFileTypes: true });
2649
2625
  for (const e of entries) {
2650
2626
  const dest = path12.join(appIconDir, e.name);
2651
2627
  if (e.isDirectory()) {
2652
- fs12.cpSync(path12.join(iconPaths.ios, e.name), dest, { recursive: true });
2628
+ fs11.cpSync(path12.join(iconPaths.ios, e.name), dest, { recursive: true });
2653
2629
  } else {
2654
- fs12.copyFileSync(path12.join(iconPaths.ios, e.name), dest);
2630
+ fs11.copyFileSync(path12.join(iconPaths.ios, e.name), dest);
2655
2631
  }
2656
2632
  }
2657
2633
  console.log("\u2705 Copied iOS icon from tamer.config.json icon.ios");
2658
2634
  } else if (iconPaths?.source) {
2659
2635
  const ext = path12.extname(iconPaths.source) || ".png";
2660
2636
  const icon1024 = `Icon-1024${ext}`;
2661
- fs12.copyFileSync(iconPaths.source, path12.join(appIconDir, icon1024));
2637
+ fs11.copyFileSync(iconPaths.source, path12.join(appIconDir, icon1024));
2662
2638
  writeFile3(path12.join(appIconDir, "Contents.json"), JSON.stringify({
2663
2639
  images: [{ filename: icon1024, idiom: "universal", platform: "ios", size: "1024x1024" }],
2664
2640
  info: { author: "xcode", version: 1 }
@@ -2672,7 +2648,7 @@ final class LynxInitProcessor {
2672
2648
  }
2673
2649
  `);
2674
2650
  }
2675
- fs12.mkdirSync(xcodeprojDir, { recursive: true });
2651
+ fs11.mkdirSync(xcodeprojDir, { recursive: true });
2676
2652
  writeFile3(path12.join(xcodeprojDir, "project.pbxproj"), `
2677
2653
  // !$*UTF8*$!
2678
2654
  {
@@ -2959,7 +2935,7 @@ final class LynxInitProcessor {
2959
2935
  var create_default2 = create2;
2960
2936
 
2961
2937
  // src/ios/autolink.ts
2962
- import fs13 from "fs";
2938
+ import fs12 from "fs";
2963
2939
  import path13 from "path";
2964
2940
  import { execSync as execSync5 } from "child_process";
2965
2941
  var autolink2 = () => {
@@ -2973,11 +2949,11 @@ var autolink2 = () => {
2973
2949
  const projectRoot = resolved.projectRoot;
2974
2950
  const iosProjectPath = resolved.iosDir;
2975
2951
  function updateGeneratedSection(filePath, newContent, startMarker, endMarker) {
2976
- if (!fs13.existsSync(filePath)) {
2952
+ if (!fs12.existsSync(filePath)) {
2977
2953
  console.warn(`\u26A0\uFE0F File not found, skipping update: ${filePath}`);
2978
2954
  return;
2979
2955
  }
2980
- let fileContent = fs13.readFileSync(filePath, "utf8");
2956
+ let fileContent = fs12.readFileSync(filePath, "utf8");
2981
2957
  const escapedStartMarker = startMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2982
2958
  const escapedEndMarker = endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2983
2959
  const regex = new RegExp(`${escapedStartMarker}[\\s\\S]*?${escapedEndMarker}`, "g");
@@ -3002,15 +2978,15 @@ ${replacementBlock}
3002
2978
  ${replacementBlock}
3003
2979
  `;
3004
2980
  }
3005
- fs13.writeFileSync(filePath, fileContent, "utf8");
2981
+ fs12.writeFileSync(filePath, fileContent, "utf8");
3006
2982
  console.log(`\u2705 Updated autolinked section in ${path13.basename(filePath)}`);
3007
2983
  }
3008
2984
  function resolvePodName(pkg) {
3009
2985
  const podspecDir = pkg.config.ios?.podspecPath || ".";
3010
2986
  const fullPodspecDir = path13.join(pkg.packagePath, podspecDir);
3011
- if (fs13.existsSync(fullPodspecDir)) {
2987
+ if (fs12.existsSync(fullPodspecDir)) {
3012
2988
  try {
3013
- const files = fs13.readdirSync(fullPodspecDir);
2989
+ const files = fs12.readdirSync(fullPodspecDir);
3014
2990
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
3015
2991
  if (podspecFile) return podspecFile.replace(".podspec", "");
3016
2992
  } catch {
@@ -3044,7 +3020,7 @@ ${replacementBlock}
3044
3020
  candidatePaths.push(path13.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
3045
3021
  }
3046
3022
  candidatePaths.push(path13.join(iosProjectPath, "LynxInitProcessor.swift"));
3047
- const found = candidatePaths.find((p) => fs13.existsSync(p));
3023
+ const found = candidatePaths.find((p) => fs12.existsSync(p));
3048
3024
  const lynxInitPath = found ?? candidatePaths[0];
3049
3025
  const iosPackages = packages.filter((p) => getIosModuleClassNames(p.config.ios).length > 0 || Object.keys(getIosElements(p.config.ios)).length > 0);
3050
3026
  function updateImportsSection(filePath, pkgs) {
@@ -3059,7 +3035,7 @@ ${replacementBlock}
3059
3035
  const podName = resolvePodName(pkg);
3060
3036
  return `import ${podName}`;
3061
3037
  }).join("\n");
3062
- const fileContent = fs13.readFileSync(filePath, "utf8");
3038
+ const fileContent = fs12.readFileSync(filePath, "utf8");
3063
3039
  if (fileContent.indexOf(startMarker) !== -1) {
3064
3040
  updateGeneratedSection(filePath, imports, startMarker, endMarker);
3065
3041
  return;
@@ -3096,7 +3072,7 @@ ${after}`;
3096
3072
  ${fileContent}`;
3097
3073
  }
3098
3074
  }
3099
- fs13.writeFileSync(filePath, newContent, "utf8");
3075
+ fs12.writeFileSync(filePath, newContent, "utf8");
3100
3076
  console.log(`\u2705 Updated imports in ${path13.basename(filePath)}`);
3101
3077
  }
3102
3078
  updateImportsSection(lynxInitPath, iosPackages);
@@ -3127,10 +3103,10 @@ ${fileContent}`;
3127
3103
  candidates.push(path13.join(iosProjectPath, appNameFromConfig, "Info.plist"));
3128
3104
  }
3129
3105
  candidates.push(path13.join(iosProjectPath, "Info.plist"));
3130
- return candidates.find((p) => fs13.existsSync(p)) ?? null;
3106
+ return candidates.find((p) => fs12.existsSync(p)) ?? null;
3131
3107
  }
3132
3108
  function readPlistXml(plistPath) {
3133
- return fs13.readFileSync(plistPath, "utf8");
3109
+ return fs12.readFileSync(plistPath, "utf8");
3134
3110
  }
3135
3111
  function syncInfoPlistPermissions(packages) {
3136
3112
  const plistPath = findInfoPlist();
@@ -3161,7 +3137,7 @@ ${fileContent}`;
3161
3137
  added++;
3162
3138
  }
3163
3139
  if (added > 0) {
3164
- fs13.writeFileSync(plistPath, plist, "utf8");
3140
+ fs12.writeFileSync(plistPath, plist, "utf8");
3165
3141
  console.log(`\u2705 Synced ${added} Info.plist permission description(s)`);
3166
3142
  }
3167
3143
  }
@@ -3208,12 +3184,12 @@ ${schemesXml}
3208
3184
  $1`
3209
3185
  );
3210
3186
  }
3211
- fs13.writeFileSync(plistPath, plist, "utf8");
3187
+ fs12.writeFileSync(plistPath, plist, "utf8");
3212
3188
  console.log(`\u2705 Synced ${urlSchemes.length} iOS URL scheme(s) into Info.plist`);
3213
3189
  }
3214
3190
  function runPodInstall(forcePath) {
3215
3191
  const podfilePath = forcePath ?? path13.join(iosProjectPath, "Podfile");
3216
- if (!fs13.existsSync(podfilePath)) {
3192
+ if (!fs12.existsSync(podfilePath)) {
3217
3193
  console.log("\u2139\uFE0F No Podfile found in ios directory; skipping `pod install`.");
3218
3194
  return;
3219
3195
  }
@@ -3242,7 +3218,7 @@ $1`
3242
3218
  const appNameFromConfig = resolved.config.ios?.appName;
3243
3219
  if (appNameFromConfig) {
3244
3220
  const appPodfile = path13.join(iosProjectPath, appNameFromConfig, "Podfile");
3245
- if (fs13.existsSync(appPodfile)) {
3221
+ if (fs12.existsSync(appPodfile)) {
3246
3222
  runPodInstall(appPodfile);
3247
3223
  console.log("\u2728 Autolinking complete for iOS.");
3248
3224
  return;
@@ -3256,12 +3232,12 @@ $1`
3256
3232
  var autolink_default2 = autolink2;
3257
3233
 
3258
3234
  // src/ios/bundle.ts
3259
- import fs15 from "fs";
3235
+ import fs14 from "fs";
3260
3236
  import path15 from "path";
3261
3237
  import { execSync as execSync6 } from "child_process";
3262
3238
 
3263
3239
  // src/ios/syncHost.ts
3264
- import fs14 from "fs";
3240
+ import fs13 from "fs";
3265
3241
  import path14 from "path";
3266
3242
  import crypto from "crypto";
3267
3243
  function deterministicUUID(seed) {
@@ -3310,7 +3286,7 @@ function getLaunchScreenStoryboard() {
3310
3286
  `;
3311
3287
  }
3312
3288
  function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3313
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3289
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3314
3290
  if (content.includes("LaunchScreen.storyboard")) return;
3315
3291
  const baseFileRefUUID = deterministicUUID(`launchScreenBase:${appName}`);
3316
3292
  const variantGroupUUID = deterministicUUID(`launchScreenGroup:${appName}`);
@@ -3347,11 +3323,11 @@ function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3347
3323
  );
3348
3324
  content = content.replace(groupPattern, `$1
3349
3325
  ${variantGroupUUID} /* LaunchScreen.storyboard */,`);
3350
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3326
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3351
3327
  console.log("\u2705 Registered LaunchScreen.storyboard in Xcode project");
3352
3328
  }
3353
3329
  function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3354
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3330
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3355
3331
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3356
3332
  if (new RegExp(`path = ${escaped};`).test(content)) return;
3357
3333
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3376,11 +3352,11 @@ function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3376
3352
  );
3377
3353
  content = content.replace(groupPattern, `$1
3378
3354
  ${fileRefUUID} /* ${filename} */,`);
3379
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3355
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3380
3356
  console.log(`\u2705 Registered ${filename} in Xcode project sources`);
3381
3357
  }
3382
3358
  function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3383
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3359
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3384
3360
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3385
3361
  if (new RegExp(`path = ${escaped};`).test(content)) return;
3386
3362
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3405,12 +3381,12 @@ function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3405
3381
  );
3406
3382
  content = content.replace(groupPattern, `$1
3407
3383
  ${fileRefUUID} /* ${filename} */,`);
3408
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3384
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3409
3385
  console.log(`\u2705 Registered ${filename} in Xcode project resources`);
3410
3386
  }
3411
3387
  function writeFile(filePath, content) {
3412
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
3413
- fs14.writeFileSync(filePath, content, "utf8");
3388
+ fs13.mkdirSync(path14.dirname(filePath), { recursive: true });
3389
+ fs13.writeFileSync(filePath, content, "utf8");
3414
3390
  }
3415
3391
  function getAppDelegateSwift() {
3416
3392
  return `import UIKit
@@ -3620,8 +3596,8 @@ class ViewController: UIViewController {
3620
3596
  `;
3621
3597
  }
3622
3598
  function patchInfoPlist(infoPlistPath) {
3623
- if (!fs14.existsSync(infoPlistPath)) return;
3624
- let content = fs14.readFileSync(infoPlistPath, "utf8");
3599
+ if (!fs13.existsSync(infoPlistPath)) return;
3600
+ let content = fs13.readFileSync(infoPlistPath, "utf8");
3625
3601
  content = content.replace(/\s*<key>UIMainStoryboardFile<\/key>\s*<string>[^<]*<\/string>/g, "");
3626
3602
  if (!content.includes("UILaunchStoryboardName")) {
3627
3603
  content = content.replace("</dict>\n</plist>", ` <key>UILaunchStoryboardName</key>
@@ -3653,7 +3629,7 @@ function patchInfoPlist(infoPlistPath) {
3653
3629
  </plist>`);
3654
3630
  console.log("\u2705 Added UIApplicationSceneManifest to Info.plist");
3655
3631
  }
3656
- fs14.writeFileSync(infoPlistPath, content, "utf8");
3632
+ fs13.writeFileSync(infoPlistPath, content, "utf8");
3657
3633
  }
3658
3634
  function getSimpleLynxProviderSwift() {
3659
3635
  return `import Foundation
@@ -3679,8 +3655,8 @@ class LynxProvider: NSObject, LynxTemplateProvider {
3679
3655
  function readTemplateOrFallback(devClientPkg, templateName, fallback, vars = {}) {
3680
3656
  if (devClientPkg) {
3681
3657
  const tplPath = path14.join(devClientPkg, "ios", "templates", templateName);
3682
- if (fs14.existsSync(tplPath)) {
3683
- let content = fs14.readFileSync(tplPath, "utf8");
3658
+ if (fs13.existsSync(tplPath)) {
3659
+ let content = fs13.readFileSync(tplPath, "utf8");
3684
3660
  for (const [k, v] of Object.entries(vars)) {
3685
3661
  content = content.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
3686
3662
  }
@@ -3700,7 +3676,7 @@ function syncHostIos(opts) {
3700
3676
  }
3701
3677
  const projectDir = path14.join(resolved.iosDir, appName);
3702
3678
  const infoPlistPath = path14.join(projectDir, "Info.plist");
3703
- if (!fs14.existsSync(projectDir)) {
3679
+ if (!fs13.existsSync(projectDir)) {
3704
3680
  throw new Error(`iOS project not found at ${projectDir}. Run \`tamer ios create\` first.`);
3705
3681
  }
3706
3682
  const pbxprojPath = path14.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
@@ -3709,8 +3685,8 @@ function syncHostIos(opts) {
3709
3685
  patchInfoPlist(infoPlistPath);
3710
3686
  writeFile(path14.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3711
3687
  writeFile(path14.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3712
- if (!fs14.existsSync(launchScreenPath)) {
3713
- fs14.mkdirSync(baseLprojDir, { recursive: true });
3688
+ if (!fs13.existsSync(launchScreenPath)) {
3689
+ fs13.mkdirSync(baseLprojDir, { recursive: true });
3714
3690
  writeFile(launchScreenPath, getLaunchScreenStoryboard());
3715
3691
  addLaunchScreenToXcodeProject(pbxprojPath, appName);
3716
3692
  }
@@ -3786,11 +3762,11 @@ function bundleAndDeploy2(opts = {}) {
3786
3762
  process.exit(1);
3787
3763
  }
3788
3764
  try {
3789
- if (!fs15.existsSync(sourceBundlePath)) {
3765
+ if (!fs14.existsSync(sourceBundlePath)) {
3790
3766
  console.error(`\u274C Build output not found at: ${sourceBundlePath}`);
3791
3767
  process.exit(1);
3792
3768
  }
3793
- if (!fs15.existsSync(destinationDir)) {
3769
+ if (!fs14.existsSync(destinationDir)) {
3794
3770
  console.error(`Destination directory not found at: ${destinationDir}`);
3795
3771
  process.exit(1);
3796
3772
  }
@@ -3799,10 +3775,10 @@ function bundleAndDeploy2(opts = {}) {
3799
3775
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
3800
3776
  console.log(`\u2728 Successfully copied bundle to: ${destinationBundlePath}`);
3801
3777
  const pbxprojPath = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3802
- if (fs15.existsSync(pbxprojPath)) {
3778
+ if (fs14.existsSync(pbxprojPath)) {
3803
3779
  const skip = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
3804
- for (const entry of fs15.readdirSync(distDir)) {
3805
- if (skip.has(entry) || fs15.statSync(path15.join(distDir, entry)).isDirectory()) continue;
3780
+ for (const entry of fs14.readdirSync(distDir)) {
3781
+ if (skip.has(entry) || fs14.statSync(path15.join(distDir, entry)).isDirectory()) continue;
3806
3782
  addResourceToXcodeProject(pbxprojPath, appName, entry);
3807
3783
  }
3808
3784
  }
@@ -3818,18 +3794,18 @@ function bundleAndDeploy2(opts = {}) {
3818
3794
  console.warn("\u26A0\uFE0F dev-client build failed; skipping dev-client bundle");
3819
3795
  }
3820
3796
  const builtBundle = path15.join(devClientPkg, "dist", "dev-client.lynx.bundle");
3821
- if (fs15.existsSync(builtBundle)) {
3822
- fs15.copyFileSync(builtBundle, devClientBundle);
3797
+ if (fs14.existsSync(builtBundle)) {
3798
+ fs14.copyFileSync(builtBundle, devClientBundle);
3823
3799
  console.log("\u2728 Copied dev-client.lynx.bundle to iOS project");
3824
3800
  const pbxprojPath2 = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3825
- if (fs15.existsSync(pbxprojPath2)) {
3801
+ if (fs14.existsSync(pbxprojPath2)) {
3826
3802
  addResourceToXcodeProject(pbxprojPath2, appName, "dev-client.lynx.bundle");
3827
3803
  }
3828
3804
  }
3829
3805
  }
3830
3806
  } else {
3831
- if (!fs15.existsSync(devClientBundle)) {
3832
- fs15.writeFileSync(devClientBundle, "");
3807
+ if (!fs14.existsSync(devClientBundle)) {
3808
+ fs14.writeFileSync(devClientBundle, "");
3833
3809
  }
3834
3810
  console.log("\u2139\uFE0F Skipped dev-client bundle (release build)");
3835
3811
  }
@@ -3843,17 +3819,17 @@ function bundleAndDeploy2(opts = {}) {
3843
3819
  var bundle_default2 = bundleAndDeploy2;
3844
3820
 
3845
3821
  // src/ios/build.ts
3846
- import fs17 from "fs";
3822
+ import fs16 from "fs";
3847
3823
  import path17 from "path";
3848
3824
  import { execSync as execSync8 } from "child_process";
3849
3825
 
3850
3826
  // src/ios/syncDevClient.ts
3851
- import fs16 from "fs";
3827
+ import fs15 from "fs";
3852
3828
  import path16 from "path";
3853
3829
  import { execSync as execSync7 } from "child_process";
3854
3830
  import { randomBytes as randomBytes2 } from "crypto";
3855
3831
  function readAndSubstituteTemplate4(templatePath, vars) {
3856
- const raw = fs16.readFileSync(templatePath, "utf-8");
3832
+ const raw = fs15.readFileSync(templatePath, "utf-8");
3857
3833
  return Object.entries(vars).reduce(
3858
3834
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
3859
3835
  raw
@@ -3866,8 +3842,8 @@ function generateId() {
3866
3842
  return randomBytes2(12).toString("hex").toUpperCase();
3867
3843
  }
3868
3844
  function writeFile2(filePath, content) {
3869
- fs16.mkdirSync(path16.dirname(filePath), { recursive: true });
3870
- fs16.writeFileSync(filePath, content, "utf8");
3845
+ fs15.mkdirSync(path16.dirname(filePath), { recursive: true });
3846
+ fs15.writeFileSync(filePath, content, "utf8");
3871
3847
  }
3872
3848
  function getAppDelegateSwift2() {
3873
3849
  return `import UIKit
@@ -4880,8 +4856,8 @@ function generatePbxproj(ids) {
4880
4856
  async function createDevAppProject(iosDir, repoRoot) {
4881
4857
  const projectDir = path16.join(iosDir, APP_NAME);
4882
4858
  const xcodeprojDir = path16.join(iosDir, `${APP_NAME}.xcodeproj`);
4883
- if (fs16.existsSync(iosDir)) {
4884
- fs16.rmSync(iosDir, { recursive: true, force: true });
4859
+ if (fs15.existsSync(iosDir)) {
4860
+ fs15.rmSync(iosDir, { recursive: true, force: true });
4885
4861
  }
4886
4862
  console.log(`\u{1F680} Creating TamerDevApp iOS project at: ${iosDir}`);
4887
4863
  const ids = {};
@@ -4947,7 +4923,7 @@ async function createDevAppProject(iosDir, repoRoot) {
4947
4923
  ];
4948
4924
  for (const f of templateFiles) {
4949
4925
  const src = templateDir ? path16.join(templateDir, f) : null;
4950
- if (src && fs16.existsSync(src)) {
4926
+ if (src && fs15.existsSync(src)) {
4951
4927
  writeFile2(path16.join(projectDir, f), readAndSubstituteTemplate4(src, templateVars));
4952
4928
  } else {
4953
4929
  const fallback = (() => {
@@ -4984,7 +4960,7 @@ async function createDevAppProject(iosDir, repoRoot) {
4984
4960
  JSON.stringify({ info: { author: "xcode", version: 1 } }, null, 2)
4985
4961
  );
4986
4962
  writeFile2(path16.join(projectDir, "dev-client.lynx.bundle"), "");
4987
- fs16.mkdirSync(xcodeprojDir, { recursive: true });
4963
+ fs15.mkdirSync(xcodeprojDir, { recursive: true });
4988
4964
  writeFile2(path16.join(xcodeprojDir, "project.pbxproj"), generatePbxproj(ids));
4989
4965
  console.log(`\u2705 TamerDevApp iOS project created at ${iosDir}`);
4990
4966
  await setupCocoaPods(iosDir);
@@ -4993,8 +4969,8 @@ async function syncDevClientIos() {
4993
4969
  let resolved;
4994
4970
  let repoRoot;
4995
4971
  try {
4996
- repoRoot = findRepoRoot(process.cwd());
4997
- resolved = resolveDevAppPaths(repoRoot);
4972
+ resolved = resolveDevAppPaths(process.cwd());
4973
+ repoRoot = resolved.projectRoot;
4998
4974
  } catch (e) {
4999
4975
  console.error(`\u274C ${e.message}`);
5000
4976
  process.exit(1);
@@ -5002,10 +4978,10 @@ async function syncDevClientIos() {
5002
4978
  const iosDir = resolved.iosDir;
5003
4979
  const workspacePath = path16.join(iosDir, `${APP_NAME}.xcworkspace`);
5004
4980
  const projectDir = path16.join(iosDir, APP_NAME);
5005
- const hasCommittedSource = fs16.existsSync(path16.join(projectDir, "AppDelegate.swift"));
4981
+ const hasCommittedSource = fs15.existsSync(path16.join(projectDir, "AppDelegate.swift"));
5006
4982
  if (!hasCommittedSource) {
5007
4983
  await createDevAppProject(iosDir, repoRoot);
5008
- } else if (!fs16.existsSync(workspacePath)) {
4984
+ } else if (!fs15.existsSync(workspacePath)) {
5009
4985
  await setupCocoaPods(iosDir);
5010
4986
  console.log(`\u2139\uFE0F iOS dev-app project exists; ran pod install`);
5011
4987
  } else {
@@ -5023,8 +4999,8 @@ async function syncDevClientIos() {
5023
4999
  execSync7("npm run build", { stdio: "inherit", cwd: devClientDir });
5024
5000
  const bundleSrc = resolved.lynxBundlePath;
5025
5001
  const bundleDst = path16.join(iosDir, APP_NAME, "dev-client.lynx.bundle");
5026
- if (fs16.existsSync(bundleSrc)) {
5027
- fs16.copyFileSync(bundleSrc, bundleDst);
5002
+ if (fs15.existsSync(bundleSrc)) {
5003
+ fs15.copyFileSync(bundleSrc, bundleDst);
5028
5004
  console.log(`\u2728 Copied dev-client.lynx.bundle to iOS project`);
5029
5005
  } else {
5030
5006
  console.warn(`\u26A0\uFE0F Bundle not found at ${bundleSrc}`);
@@ -5066,7 +5042,7 @@ async function buildIpa(opts = {}) {
5066
5042
  const scheme = appName;
5067
5043
  const workspacePath = path17.join(iosDir, `${appName}.xcworkspace`);
5068
5044
  const projectPath = path17.join(iosDir, `${appName}.xcodeproj`);
5069
- const xcproject = fs17.existsSync(workspacePath) ? workspacePath : projectPath;
5045
+ const xcproject = fs16.existsSync(workspacePath) ? workspacePath : projectPath;
5070
5046
  const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
5071
5047
  const derivedDataPath = path17.join(iosDir, "build");
5072
5048
  const sdk = opts.install ? "iphonesimulator" : "iphoneos";
@@ -5085,7 +5061,7 @@ async function buildIpa(opts = {}) {
5085
5061
  `${configuration}-iphonesimulator`,
5086
5062
  `${appName}.app`
5087
5063
  );
5088
- if (!fs17.existsSync(appGlob)) {
5064
+ if (!fs16.existsSync(appGlob)) {
5089
5065
  console.error(`\u274C Built app not found at: ${appGlob}`);
5090
5066
  process.exit(1);
5091
5067
  }
@@ -5106,14 +5082,13 @@ async function buildIpa(opts = {}) {
5106
5082
  }
5107
5083
  }
5108
5084
  async function buildIosDevApp(install, release) {
5109
- const repoRoot = findRepoRoot(process.cwd());
5110
- const resolved = resolveDevAppPaths(repoRoot);
5085
+ const resolved = resolveDevAppPaths(process.cwd());
5111
5086
  const iosDir = resolved.iosDir;
5112
5087
  const configuration = release ? "Release" : "Debug";
5113
5088
  await syncDevClient_default2();
5114
5089
  const workspacePath = path17.join(iosDir, `${DEV_APP_NAME}.xcworkspace`);
5115
5090
  const projectPath = path17.join(iosDir, `${DEV_APP_NAME}.xcodeproj`);
5116
- const xcproject = fs17.existsSync(workspacePath) ? workspacePath : projectPath;
5091
+ const xcproject = fs16.existsSync(workspacePath) ? workspacePath : projectPath;
5117
5092
  const flag = xcproject.endsWith(".xcworkspace") ? "workspace" : "project";
5118
5093
  console.log(`
5119
5094
  \u{1F528} Building TamerDevApp for simulator (${configuration})...`);
@@ -5138,12 +5113,13 @@ async function buildIosDevApp(install, release) {
5138
5113
  var build_default2 = buildIpa;
5139
5114
 
5140
5115
  // src/common/init.ts
5141
- import fs18 from "fs";
5116
+ import fs17 from "fs";
5142
5117
  import path18 from "path";
5143
5118
  import readline from "readline";
5144
5119
  var rl = readline.createInterface({
5145
5120
  input: process.stdin,
5146
- output: process.stdout
5121
+ output: process.stdout,
5122
+ terminal: false
5147
5123
  });
5148
5124
  function ask(question) {
5149
5125
  return new Promise((resolve) => {
@@ -5151,7 +5127,6 @@ function ask(question) {
5151
5127
  });
5152
5128
  }
5153
5129
  async function init() {
5154
- process.removeAllListeners("warning");
5155
5130
  console.log("Tamer4Lynx Init: Let's set up your tamer.config.json\n");
5156
5131
  const androidAppName = await ask("Android app name: ");
5157
5132
  const androidPackageName = await ask("Android package name (e.g. com.example.app): ");
@@ -5191,7 +5166,7 @@ async function init() {
5191
5166
  };
5192
5167
  if (lynxProject) config.lynxProject = lynxProject;
5193
5168
  const configPath = path18.join(process.cwd(), "tamer.config.json");
5194
- fs18.writeFileSync(configPath, JSON.stringify(config, null, 2));
5169
+ fs17.writeFileSync(configPath, JSON.stringify(config, null, 2));
5195
5170
  console.log(`
5196
5171
  \u2705 Generated tamer.config.json at ${configPath}`);
5197
5172
  rl.close();
@@ -5199,10 +5174,10 @@ async function init() {
5199
5174
  var init_default = init;
5200
5175
 
5201
5176
  // src/common/create.ts
5202
- import fs19 from "fs";
5177
+ import fs18 from "fs";
5203
5178
  import path19 from "path";
5204
5179
  import readline2 from "readline";
5205
- var rl2 = readline2.createInterface({ input: process.stdin, output: process.stdout });
5180
+ var rl2 = readline2.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
5206
5181
  function ask2(question) {
5207
5182
  return new Promise((resolve) => rl2.question(question, (answer) => resolve(answer.trim())));
5208
5183
  }
@@ -5231,12 +5206,12 @@ async function create3() {
5231
5206
  const fullModuleClassName = `${packageName}.${simpleModuleName}`;
5232
5207
  const cwd = process.cwd();
5233
5208
  const root = path19.join(cwd, extName);
5234
- if (fs19.existsSync(root)) {
5209
+ if (fs18.existsSync(root)) {
5235
5210
  console.error(`\u274C Directory ${extName} already exists.`);
5236
5211
  rl2.close();
5237
5212
  process.exit(1);
5238
5213
  }
5239
- fs19.mkdirSync(root, { recursive: true });
5214
+ fs18.mkdirSync(root, { recursive: true });
5240
5215
  const lynxExt = {
5241
5216
  platforms: {
5242
5217
  android: {
@@ -5251,7 +5226,7 @@ async function create3() {
5251
5226
  web: {}
5252
5227
  }
5253
5228
  };
5254
- fs19.writeFileSync(path19.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5229
+ fs18.writeFileSync(path19.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5255
5230
  const pkg = {
5256
5231
  name: extName,
5257
5232
  version: "0.0.1",
@@ -5264,17 +5239,17 @@ async function create3() {
5264
5239
  engines: { node: ">=18" }
5265
5240
  };
5266
5241
  if (includeModule) pkg.types = "src/index.d.ts";
5267
- fs19.writeFileSync(path19.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5242
+ fs18.writeFileSync(path19.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5268
5243
  const pkgPath = packageName.replace(/\./g, "/");
5269
5244
  if (includeModule) {
5270
- fs19.mkdirSync(path19.join(root, "src"), { recursive: true });
5271
- fs19.writeFileSync(path19.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5245
+ fs18.mkdirSync(path19.join(root, "src"), { recursive: true });
5246
+ fs18.writeFileSync(path19.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5272
5247
  export declare class ${simpleModuleName} {
5273
5248
  // Add your module methods here
5274
5249
  }
5275
5250
  `);
5276
- fs19.mkdirSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5277
- fs19.writeFileSync(path19.join(root, "android", "build.gradle.kts"), `plugins {
5251
+ fs18.mkdirSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5252
+ fs18.writeFileSync(path19.join(root, "android", "build.gradle.kts"), `plugins {
5278
5253
  id("com.android.library")
5279
5254
  id("org.jetbrains.kotlin.android")
5280
5255
  }
@@ -5295,7 +5270,7 @@ dependencies {
5295
5270
  implementation(libs.lynx.jssdk)
5296
5271
  }
5297
5272
  `);
5298
- fs19.writeFileSync(path19.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5273
+ fs18.writeFileSync(path19.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5299
5274
  <manifest />
5300
5275
  `);
5301
5276
  const ktContent = `package ${packageName}
@@ -5312,8 +5287,8 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5312
5287
  }
5313
5288
  }
5314
5289
  `;
5315
- fs19.writeFileSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5316
- fs19.mkdirSync(path19.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5290
+ fs18.writeFileSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5291
+ fs18.mkdirSync(path19.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5317
5292
  const podspec = `Pod::Spec.new do |s|
5318
5293
  s.name = '${extName}'
5319
5294
  s.version = '0.0.1'
@@ -5327,7 +5302,7 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5327
5302
  s.dependency 'Lynx'
5328
5303
  end
5329
5304
  `;
5330
- fs19.writeFileSync(path19.join(root, "ios", extName, `${extName}.podspec`), podspec);
5305
+ fs18.writeFileSync(path19.join(root, "ios", extName, `${extName}.podspec`), podspec);
5331
5306
  const swiftContent = `import Foundation
5332
5307
 
5333
5308
  @objc public class ${simpleModuleName}: NSObject {
@@ -5336,16 +5311,16 @@ end
5336
5311
  }
5337
5312
  }
5338
5313
  `;
5339
- fs19.writeFileSync(path19.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5314
+ fs18.writeFileSync(path19.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5340
5315
  }
5341
- fs19.writeFileSync(path19.join(root, "index.js"), `'use strict';
5316
+ fs18.writeFileSync(path19.join(root, "index.js"), `'use strict';
5342
5317
  module.exports = {};
5343
5318
  `);
5344
- fs19.writeFileSync(path19.join(root, "tsconfig.json"), JSON.stringify({
5319
+ fs18.writeFileSync(path19.join(root, "tsconfig.json"), JSON.stringify({
5345
5320
  compilerOptions: { target: "ES2020", module: "ESNext", moduleResolution: "bundler", strict: true },
5346
5321
  include: ["src"]
5347
5322
  }, null, 2));
5348
- fs19.writeFileSync(path19.join(root, "README.md"), `# ${extName}
5323
+ fs18.writeFileSync(path19.join(root, "README.md"), `# ${extName}
5349
5324
 
5350
5325
  Lynx extension for ${extName}.
5351
5326
 
@@ -5370,7 +5345,7 @@ This package uses \`lynx.ext.json\` (RFC-compliant) for autolinking.
5370
5345
  var create_default3 = create3;
5371
5346
 
5372
5347
  // src/common/codegen.ts
5373
- import fs20 from "fs";
5348
+ import fs19 from "fs";
5374
5349
  import path20 from "path";
5375
5350
  function codegen() {
5376
5351
  const cwd = process.cwd();
@@ -5381,7 +5356,7 @@ function codegen() {
5381
5356
  }
5382
5357
  const srcDir = path20.join(cwd, "src");
5383
5358
  const generatedDir = path20.join(cwd, "generated");
5384
- fs20.mkdirSync(generatedDir, { recursive: true });
5359
+ fs19.mkdirSync(generatedDir, { recursive: true });
5385
5360
  const dtsFiles = findDtsFiles(srcDir);
5386
5361
  const modules = extractLynxModules(dtsFiles);
5387
5362
  if (modules.length === 0) {
@@ -5392,25 +5367,25 @@ function codegen() {
5392
5367
  const tsContent = `export type { ${mod} } from '../src/index.js';
5393
5368
  `;
5394
5369
  const outPath = path20.join(generatedDir, `${mod}.ts`);
5395
- fs20.writeFileSync(outPath, tsContent);
5370
+ fs19.writeFileSync(outPath, tsContent);
5396
5371
  console.log(`\u2705 Generated ${outPath}`);
5397
5372
  }
5398
5373
  if (config.android) {
5399
5374
  const androidGenerated = path20.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5400
- fs20.mkdirSync(androidGenerated, { recursive: true });
5375
+ fs19.mkdirSync(androidGenerated, { recursive: true });
5401
5376
  console.log(`\u2139\uFE0F Android generated dir: ${androidGenerated} (spec generation coming soon)`);
5402
5377
  }
5403
5378
  if (config.ios) {
5404
5379
  const iosGenerated = path20.join(cwd, "ios", "generated");
5405
- fs20.mkdirSync(iosGenerated, { recursive: true });
5380
+ fs19.mkdirSync(iosGenerated, { recursive: true });
5406
5381
  console.log(`\u2139\uFE0F iOS generated dir: ${iosGenerated} (spec generation coming soon)`);
5407
5382
  }
5408
5383
  console.log("\u2728 Codegen complete.");
5409
5384
  }
5410
5385
  function findDtsFiles(dir) {
5411
5386
  const result = [];
5412
- if (!fs20.existsSync(dir)) return result;
5413
- const entries = fs20.readdirSync(dir, { withFileTypes: true });
5387
+ if (!fs19.existsSync(dir)) return result;
5388
+ const entries = fs19.readdirSync(dir, { withFileTypes: true });
5414
5389
  for (const e of entries) {
5415
5390
  const full = path20.join(dir, e.name);
5416
5391
  if (e.isDirectory()) result.push(...findDtsFiles(full));
@@ -5422,7 +5397,7 @@ function extractLynxModules(files) {
5422
5397
  const modules = [];
5423
5398
  const seen = /* @__PURE__ */ new Set();
5424
5399
  for (const file of files) {
5425
- const content = fs20.readFileSync(file, "utf8");
5400
+ const content = fs19.readFileSync(file, "utf8");
5426
5401
  const regex = /\/\*\*\s*@lynxmodule\s*\*\/\s*export\s+declare\s+class\s+(\w+)/g;
5427
5402
  let m;
5428
5403
  while ((m = regex.exec(content)) !== null) {
@@ -5438,7 +5413,7 @@ var codegen_default = codegen;
5438
5413
 
5439
5414
  // src/common/devServer.ts
5440
5415
  import { spawn } from "child_process";
5441
- import fs21 from "fs";
5416
+ import fs20 from "fs";
5442
5417
  import http from "http";
5443
5418
  import os3 from "os";
5444
5419
  import path21 from "path";
@@ -5462,11 +5437,19 @@ async function startDevServer(opts) {
5462
5437
  const distDir = path21.dirname(lynxBundlePath);
5463
5438
  const port = config.devServer?.port ?? config.devServer?.httpPort ?? DEFAULT_PORT;
5464
5439
  let buildProcess = null;
5440
+ function detectPackageManager2(cwd) {
5441
+ const dir = path21.resolve(cwd);
5442
+ if (fs20.existsSync(path21.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5443
+ if (fs20.existsSync(path21.join(dir, "bun.lockb")) || fs20.existsSync(path21.join(dir, "bun.lock"))) return { cmd: "bun", args: ["run", "build"] };
5444
+ return { cmd: "npm", args: ["run", "build"] };
5445
+ }
5465
5446
  function runBuild() {
5466
5447
  return new Promise((resolve, reject) => {
5467
- buildProcess = spawn("npm", ["run", "build"], {
5448
+ const { cmd, args } = detectPackageManager2(lynxProjectDir);
5449
+ buildProcess = spawn(cmd, args, {
5468
5450
  cwd: lynxProjectDir,
5469
- stdio: "pipe"
5451
+ stdio: "pipe",
5452
+ shell: process.platform === "win32"
5470
5453
  });
5471
5454
  let stderr = "";
5472
5455
  buildProcess.stderr?.on("data", (d) => {
@@ -5483,14 +5466,14 @@ async function startDevServer(opts) {
5483
5466
  const basePath = `/${projectName}`;
5484
5467
  const iconPaths = resolveIconPaths(projectRoot, config);
5485
5468
  let iconFilePath = null;
5486
- if (iconPaths?.source && fs21.statSync(iconPaths.source).isFile()) {
5469
+ if (iconPaths?.source && fs20.statSync(iconPaths.source).isFile()) {
5487
5470
  iconFilePath = iconPaths.source;
5488
5471
  } else if (iconPaths?.android) {
5489
5472
  const androidIcon = path21.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5490
- if (fs21.existsSync(androidIcon)) iconFilePath = androidIcon;
5473
+ if (fs20.existsSync(androidIcon)) iconFilePath = androidIcon;
5491
5474
  } else if (iconPaths?.ios) {
5492
5475
  const iosIcon = path21.join(iconPaths.ios, "Icon-1024.png");
5493
- if (fs21.existsSync(iosIcon)) iconFilePath = iosIcon;
5476
+ if (fs20.existsSync(iosIcon)) iconFilePath = iosIcon;
5494
5477
  }
5495
5478
  const iconExt = iconFilePath ? path21.extname(iconFilePath) || ".png" : "";
5496
5479
  const iconMime = {
@@ -5531,7 +5514,7 @@ async function startDevServer(opts) {
5531
5514
  return;
5532
5515
  }
5533
5516
  if (iconFilePath && (reqPath === `${basePath}/icon` || reqPath === `${basePath}/icon${iconExt}`)) {
5534
- fs21.readFile(iconFilePath, (err, data) => {
5517
+ fs20.readFile(iconFilePath, (err, data) => {
5535
5518
  if (err) {
5536
5519
  res.writeHead(404);
5537
5520
  res.end();
@@ -5556,7 +5539,7 @@ async function startDevServer(opts) {
5556
5539
  res.end();
5557
5540
  return;
5558
5541
  }
5559
- fs21.readFile(filePath, (err, data) => {
5542
+ fs20.readFile(filePath, (err, data) => {
5560
5543
  if (err) {
5561
5544
  res.writeHead(404);
5562
5545
  res.end("Not found");
@@ -5613,7 +5596,7 @@ async function startDevServer(opts) {
5613
5596
  path21.join(lynxProjectDir, "src"),
5614
5597
  path21.join(lynxProjectDir, "lynx.config.ts"),
5615
5598
  path21.join(lynxProjectDir, "lynx.config.js")
5616
- ].filter((p) => fs21.existsSync(p));
5599
+ ].filter((p) => fs20.existsSync(p));
5617
5600
  if (watchPaths.length > 0) {
5618
5601
  const watcher = chokidar.watch(watchPaths, { ignoreInitial: true });
5619
5602
  watcher.on("change", async () => {
@@ -5692,10 +5675,10 @@ async function start(opts) {
5692
5675
  var start_default = start;
5693
5676
 
5694
5677
  // src/common/injectHost.ts
5695
- import fs22 from "fs";
5678
+ import fs21 from "fs";
5696
5679
  import path22 from "path";
5697
5680
  function readAndSubstitute(templatePath, vars) {
5698
- const raw = fs22.readFileSync(templatePath, "utf-8");
5681
+ const raw = fs21.readFileSync(templatePath, "utf-8");
5699
5682
  return Object.entries(vars).reduce(
5700
5683
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
5701
5684
  raw
@@ -5720,7 +5703,7 @@ async function injectHostAndroid(opts) {
5720
5703
  const packagePath = packageName.replace(/\./g, "/");
5721
5704
  const javaDir = path22.join(rootDir, "app", "src", "main", "java", packagePath);
5722
5705
  const kotlinDir = path22.join(rootDir, "app", "src", "main", "kotlin", packagePath);
5723
- if (!fs22.existsSync(javaDir) || !fs22.existsSync(kotlinDir)) {
5706
+ if (!fs21.existsSync(javaDir) || !fs21.existsSync(kotlinDir)) {
5724
5707
  console.error("\u274C Android project not found. Run `t4l android create` first or ensure android/ exists.");
5725
5708
  process.exit(1);
5726
5709
  }
@@ -5733,14 +5716,14 @@ async function injectHostAndroid(opts) {
5733
5716
  ];
5734
5717
  for (const { src, dst } of files) {
5735
5718
  const srcPath = path22.join(templateDir, src);
5736
- if (!fs22.existsSync(srcPath)) continue;
5737
- if (fs22.existsSync(dst) && !opts?.force) {
5719
+ if (!fs21.existsSync(srcPath)) continue;
5720
+ if (fs21.existsSync(dst) && !opts?.force) {
5738
5721
  console.log(`\u23ED\uFE0F Skipping ${path22.basename(dst)} (use --force to overwrite)`);
5739
5722
  continue;
5740
5723
  }
5741
5724
  const content = readAndSubstitute(srcPath, vars);
5742
- fs22.mkdirSync(path22.dirname(dst), { recursive: true });
5743
- fs22.writeFileSync(dst, content);
5725
+ fs21.mkdirSync(path22.dirname(dst), { recursive: true });
5726
+ fs21.writeFileSync(dst, content);
5744
5727
  console.log(`\u2705 Injected ${path22.basename(dst)}`);
5745
5728
  }
5746
5729
  }
@@ -5761,7 +5744,7 @@ async function injectHostIos(opts) {
5761
5744
  const iosDir = config.paths?.iosDir ?? "ios";
5762
5745
  const rootDir = path22.join(projectRoot, iosDir);
5763
5746
  const projectDir = path22.join(rootDir, appName);
5764
- if (!fs22.existsSync(projectDir)) {
5747
+ if (!fs21.existsSync(projectDir)) {
5765
5748
  console.error("\u274C iOS project not found. Run `t4l ios create` first or ensure ios/ exists.");
5766
5749
  process.exit(1);
5767
5750
  }
@@ -5777,19 +5760,19 @@ async function injectHostIos(opts) {
5777
5760
  for (const f of files) {
5778
5761
  const srcPath = path22.join(templateDir, f);
5779
5762
  const dstPath = path22.join(projectDir, f);
5780
- if (!fs22.existsSync(srcPath)) continue;
5781
- if (fs22.existsSync(dstPath) && !opts?.force) {
5763
+ if (!fs21.existsSync(srcPath)) continue;
5764
+ if (fs21.existsSync(dstPath) && !opts?.force) {
5782
5765
  console.log(`\u23ED\uFE0F Skipping ${f} (use --force to overwrite)`);
5783
5766
  continue;
5784
5767
  }
5785
5768
  const content = readAndSubstitute(srcPath, vars);
5786
- fs22.writeFileSync(dstPath, content);
5769
+ fs21.writeFileSync(dstPath, content);
5787
5770
  console.log(`\u2705 Injected ${f}`);
5788
5771
  }
5789
5772
  }
5790
5773
 
5791
5774
  // src/common/buildEmbeddable.ts
5792
- import fs23 from "fs";
5775
+ import fs22 from "fs";
5793
5776
  import path23 from "path";
5794
5777
  import { execSync as execSync9 } from "child_process";
5795
5778
  var EMBEDDABLE_DIR = "embeddable";
@@ -5870,9 +5853,9 @@ function generateAndroidLibrary(outDir, androidDir, projectRoot, lynxBundlePath,
5870
5853
  const assetsDir = path23.join(libSrcMain, "assets");
5871
5854
  const kotlinDir = path23.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
5872
5855
  const generatedDir = path23.join(kotlinDir, "generated");
5873
- fs23.mkdirSync(path23.join(androidDir, "gradle"), { recursive: true });
5874
- fs23.mkdirSync(generatedDir, { recursive: true });
5875
- fs23.mkdirSync(assetsDir, { recursive: true });
5856
+ fs22.mkdirSync(path23.join(androidDir, "gradle"), { recursive: true });
5857
+ fs22.mkdirSync(generatedDir, { recursive: true });
5858
+ fs22.mkdirSync(assetsDir, { recursive: true });
5876
5859
  const androidModules = modules.filter((m) => m.config.android);
5877
5860
  const abiList = abiFilters.map((a) => `"${a}"`).join(", ");
5878
5861
  const settingsContent = `pluginManagement {
@@ -5939,9 +5922,9 @@ dependencies {
5939
5922
  ${libDeps}
5940
5923
  }
5941
5924
  `;
5942
- fs23.writeFileSync(path23.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
5943
- fs23.writeFileSync(path23.join(androidDir, "settings.gradle.kts"), settingsContent);
5944
- fs23.writeFileSync(
5925
+ fs22.writeFileSync(path23.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
5926
+ fs22.writeFileSync(path23.join(androidDir, "settings.gradle.kts"), settingsContent);
5927
+ fs22.writeFileSync(
5945
5928
  path23.join(androidDir, "build.gradle.kts"),
5946
5929
  `plugins {
5947
5930
  alias(libs.plugins.android.library) apply false
@@ -5950,25 +5933,25 @@ ${libDeps}
5950
5933
  }
5951
5934
  `
5952
5935
  );
5953
- fs23.writeFileSync(
5936
+ fs22.writeFileSync(
5954
5937
  path23.join(androidDir, "gradle.properties"),
5955
5938
  `org.gradle.jvmargs=-Xmx2048m
5956
5939
  android.useAndroidX=true
5957
5940
  kotlin.code.style=official
5958
5941
  `
5959
5942
  );
5960
- fs23.writeFileSync(path23.join(libDir, "build.gradle.kts"), libBuildContent);
5961
- fs23.writeFileSync(
5943
+ fs22.writeFileSync(path23.join(libDir, "build.gradle.kts"), libBuildContent);
5944
+ fs22.writeFileSync(
5962
5945
  path23.join(libSrcMain, "AndroidManifest.xml"),
5963
5946
  '<?xml version="1.0" encoding="utf-8"?>\n<manifest />'
5964
5947
  );
5965
- fs23.copyFileSync(lynxBundlePath, path23.join(assetsDir, lynxBundleFile));
5966
- fs23.writeFileSync(path23.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
5967
- fs23.writeFileSync(
5948
+ fs22.copyFileSync(lynxBundlePath, path23.join(assetsDir, lynxBundleFile));
5949
+ fs22.writeFileSync(path23.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
5950
+ fs22.writeFileSync(
5968
5951
  path23.join(generatedDir, "GeneratedLynxExtensions.kt"),
5969
5952
  generateLynxExtensionsKotlin(modules, LIB_PACKAGE)
5970
5953
  );
5971
- fs23.writeFileSync(
5954
+ fs22.writeFileSync(
5972
5955
  path23.join(generatedDir, "GeneratedActivityLifecycle.kt"),
5973
5956
  generateActivityLifecycleKotlin(modules, LIB_PACKAGE)
5974
5957
  );
@@ -5978,20 +5961,20 @@ async function buildEmbeddable(opts = {}) {
5978
5961
  const { lynxProjectDir, lynxBundlePath, lynxBundleFile, projectRoot, config } = resolved;
5979
5962
  console.log("\u{1F4E6} Building Lynx project (release)...");
5980
5963
  execSync9("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
5981
- if (!fs23.existsSync(lynxBundlePath)) {
5964
+ if (!fs22.existsSync(lynxBundlePath)) {
5982
5965
  console.error(`\u274C Bundle not found at ${lynxBundlePath}`);
5983
5966
  process.exit(1);
5984
5967
  }
5985
5968
  const outDir = path23.join(projectRoot, EMBEDDABLE_DIR);
5986
- fs23.mkdirSync(outDir, { recursive: true });
5969
+ fs22.mkdirSync(outDir, { recursive: true });
5987
5970
  const distDir = path23.dirname(lynxBundlePath);
5988
5971
  copyDistAssets(distDir, outDir, lynxBundleFile);
5989
5972
  const modules = discoverModules(projectRoot);
5990
5973
  const androidModules = modules.filter((m) => m.config.android);
5991
5974
  const abiFilters = resolveAbiFilters(config);
5992
5975
  const androidDir = path23.join(outDir, "android");
5993
- if (fs23.existsSync(androidDir)) fs23.rmSync(androidDir, { recursive: true });
5994
- fs23.mkdirSync(androidDir, { recursive: true });
5976
+ if (fs22.existsSync(androidDir)) fs22.rmSync(androidDir, { recursive: true });
5977
+ fs22.mkdirSync(androidDir, { recursive: true });
5995
5978
  generateAndroidLibrary(
5996
5979
  outDir,
5997
5980
  androidDir,
@@ -6009,15 +5992,15 @@ async function buildEmbeddable(opts = {}) {
6009
5992
  ].filter(Boolean);
6010
5993
  let hasWrapper = false;
6011
5994
  for (const d of existingGradleDirs) {
6012
- if (fs23.existsSync(path23.join(d, "gradlew"))) {
5995
+ if (fs22.existsSync(path23.join(d, "gradlew"))) {
6013
5996
  for (const name of ["gradlew", "gradlew.bat", "gradle"]) {
6014
5997
  const src = path23.join(d, name);
6015
- if (fs23.existsSync(src)) {
5998
+ if (fs22.existsSync(src)) {
6016
5999
  const dest = path23.join(androidDir, name);
6017
- if (fs23.statSync(src).isDirectory()) {
6018
- fs23.cpSync(src, dest, { recursive: true });
6000
+ if (fs22.statSync(src).isDirectory()) {
6001
+ fs22.cpSync(src, dest, { recursive: true });
6019
6002
  } else {
6020
- fs23.copyFileSync(src, dest);
6003
+ fs22.copyFileSync(src, dest);
6021
6004
  }
6022
6005
  }
6023
6006
  }
@@ -6038,8 +6021,8 @@ async function buildEmbeddable(opts = {}) {
6038
6021
  }
6039
6022
  const aarSrc = path23.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6040
6023
  const aarDest = path23.join(outDir, "tamer-embeddable.aar");
6041
- if (fs23.existsSync(aarSrc)) {
6042
- fs23.copyFileSync(aarSrc, aarDest);
6024
+ if (fs22.existsSync(aarSrc)) {
6025
+ fs22.copyFileSync(aarSrc, aarDest);
6043
6026
  console.log(` - tamer-embeddable.aar`);
6044
6027
  }
6045
6028
  const snippetAndroid = `// Add to your app's build.gradle:
@@ -6050,7 +6033,7 @@ async function buildEmbeddable(opts = {}) {
6050
6033
  // LynxEmbeddable.init(applicationContext)
6051
6034
  // val lynxView = LynxEmbeddable.buildLynxView(containerViewGroup)
6052
6035
  `;
6053
- fs23.writeFileSync(path23.join(outDir, "snippet-android.kt"), snippetAndroid);
6036
+ fs22.writeFileSync(path23.join(outDir, "snippet-android.kt"), snippetAndroid);
6054
6037
  generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules);
6055
6038
  const readme = `# Embeddable Lynx Bundle
6056
6039
 
@@ -6081,7 +6064,7 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6081
6064
 
6082
6065
  - [Embedding LynxView](https://lynxjs.org/guide/embed-lynx-to-native)
6083
6066
  `;
6084
- fs23.writeFileSync(path23.join(outDir, "README.md"), readme);
6067
+ fs22.writeFileSync(path23.join(outDir, "README.md"), readme);
6085
6068
  console.log(`
6086
6069
  \u2705 Embeddable output at ${outDir}/`);
6087
6070
  console.log(" - main.lynx.bundle");
@@ -6096,14 +6079,14 @@ function generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, mod
6096
6079
  const iosDir = path23.join(outDir, "ios");
6097
6080
  const podDir = path23.join(iosDir, "TamerEmbeddable");
6098
6081
  const resourcesDir = path23.join(podDir, "Resources");
6099
- fs23.mkdirSync(resourcesDir, { recursive: true });
6100
- fs23.copyFileSync(lynxBundlePath, path23.join(resourcesDir, lynxBundleFile));
6082
+ fs22.mkdirSync(resourcesDir, { recursive: true });
6083
+ fs22.copyFileSync(lynxBundlePath, path23.join(resourcesDir, lynxBundleFile));
6101
6084
  const iosModules = modules.filter((m) => m.config.ios);
6102
6085
  const podDeps = iosModules.map((p) => {
6103
6086
  const podspecPath = p.config.ios?.podspecPath || ".";
6104
6087
  const podspecDir = path23.join(p.packagePath, podspecPath);
6105
- if (!fs23.existsSync(podspecDir)) return null;
6106
- const files = fs23.readdirSync(podspecDir);
6088
+ if (!fs22.existsSync(podspecDir)) return null;
6089
+ const files = fs22.readdirSync(podspecDir);
6107
6090
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6108
6091
  const podName = podspecFile ? podspecFile.replace(".podspec", "") : p.name.split("/").pop().replace(/-/g, "");
6109
6092
  const absPath = path23.resolve(podspecDir);
@@ -6147,8 +6130,8 @@ end
6147
6130
  const swiftImports = iosModules.map((p) => {
6148
6131
  const podspecPath = p.config.ios?.podspecPath || ".";
6149
6132
  const podspecDir = path23.join(p.packagePath, podspecPath);
6150
- if (!fs23.existsSync(podspecDir)) return null;
6151
- const files = fs23.readdirSync(podspecDir);
6133
+ if (!fs22.existsSync(podspecDir)) return null;
6134
+ const files = fs22.readdirSync(podspecDir);
6152
6135
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6153
6136
  return podspecFile ? podspecFile.replace(".podspec", "") : null;
6154
6137
  }).filter(Boolean);
@@ -6167,16 +6150,16 @@ ${regBlock}
6167
6150
  }
6168
6151
  }
6169
6152
  `;
6170
- fs23.writeFileSync(path23.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6171
- fs23.writeFileSync(path23.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6153
+ fs22.writeFileSync(path23.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6154
+ fs22.writeFileSync(path23.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6172
6155
  const absIosDir = path23.resolve(iosDir);
6173
6156
  const podfileSnippet = `# Paste into your app target in Podfile:
6174
6157
 
6175
6158
  pod 'TamerEmbeddable', :path => '${absIosDir}'
6176
6159
  ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6177
6160
  `;
6178
- fs23.writeFileSync(path23.join(iosDir, "Podfile.snippet"), podfileSnippet);
6179
- fs23.writeFileSync(
6161
+ fs22.writeFileSync(path23.join(iosDir, "Podfile.snippet"), podfileSnippet);
6162
+ fs22.writeFileSync(
6180
6163
  path23.join(outDir, "snippet-ios.swift"),
6181
6164
  `// Add LynxEmbeddable.initEnvironment() in your AppDelegate/SceneDelegate before presenting LynxView.
6182
6165
  // Then create LynxView with your bundle URL (main.lynx.bundle is in the pod resources).
@@ -6185,7 +6168,7 @@ ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6185
6168
  }
6186
6169
 
6187
6170
  // src/common/add.ts
6188
- import fs24 from "fs";
6171
+ import fs23 from "fs";
6189
6172
  import path24 from "path";
6190
6173
  import { execSync as execSync10 } from "child_process";
6191
6174
  var CORE_PACKAGES = [
@@ -6200,8 +6183,8 @@ var CORE_PACKAGES = [
6200
6183
  ];
6201
6184
  function detectPackageManager(cwd) {
6202
6185
  const dir = path24.resolve(cwd);
6203
- if (fs24.existsSync(path24.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6204
- if (fs24.existsSync(path24.join(dir, "bun.lockb"))) return "bun";
6186
+ if (fs23.existsSync(path24.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6187
+ if (fs23.existsSync(path24.join(dir, "bun.lockb"))) return "bun";
6205
6188
  return "npm";
6206
6189
  }
6207
6190
  function runInstall(cwd, packages, pm) {
@@ -6253,12 +6236,12 @@ android.command("create").option("-t, --target <target>", "Create target: host (
6253
6236
  android.command("link").description("Link native modules to the Android project").action(() => {
6254
6237
  autolink_default();
6255
6238
  });
6256
- android.command("bundle").option("-t, --target <target>", "Bundle target: host (default) or dev-app", "host").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to Android assets (runs autolink first)").action(async (opts) => {
6239
+ android.command("bundle").option("-t, --target <target>", "Bundle target: dev-app (default) or host", "dev-app").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to Android assets (runs autolink first)").action(async (opts) => {
6257
6240
  validateDebugRelease(opts.debug, opts.release);
6258
6241
  const release = opts.release === true;
6259
6242
  await bundle_default({ target: opts.target, release });
6260
6243
  });
6261
- var androidBuildCmd = android.command("build").option("-i, --install", "Install APK to connected device and launch app after building").option("-t, --target <target>", "Build target: host (default) or dev-app", "host").option("-e, --embeddable", "Build for embedding in existing app (host only). Use with --release for production-ready embeddable.").option("-d, --debug", "Build debug (development) APK").option("-r, --release", "Build release (production) APK").description("Build APK (autolink + bundle + gradle)").action(async () => {
6244
+ var androidBuildCmd = android.command("build").option("-i, --install", "Install APK to connected device and launch app after building").option("-t, --target <target>", "Build target: dev-app (default) or host", "dev-app").option("-e, --embeddable", "Build for embedding in existing app (host only). Use with --release for production-ready embeddable.").option("-d, --debug", "Build debug (development) APK").option("-r, --release", "Build release (production) APK").description("Build APK (autolink + bundle + gradle)").action(async () => {
6262
6245
  const opts = androidBuildCmd.opts();
6263
6246
  validateDebugRelease(opts.debug, opts.release);
6264
6247
  const release = opts.release === true;
@@ -6284,12 +6267,12 @@ ios.command("inject").option("-f, --force", "Overwrite existing files").descript
6284
6267
  ios.command("link").description("Link native modules to the iOS project").action(() => {
6285
6268
  autolink_default2();
6286
6269
  });
6287
- ios.command("bundle").option("-t, --target <target>", "Bundle target: host (default) or dev-app", "host").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to iOS project (runs autolink first)").action((opts) => {
6270
+ ios.command("bundle").option("-t, --target <target>", "Bundle target: dev-app (default) or host", "dev-app").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to iOS project (runs autolink first)").action((opts) => {
6288
6271
  validateDebugRelease(opts.debug, opts.release);
6289
6272
  const release = opts.release === true;
6290
6273
  bundle_default2({ target: opts.target, release });
6291
6274
  });
6292
- var iosBuildCmd = ios.command("build").option("-t, --target <target>", "Build target: host (default) or dev-app", "host").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-i, --install", "Install and launch on booted simulator after building").option("-d, --debug", "Build debug (development) configuration").option("-r, --release", "Build release (production) configuration").description("Build iOS app (autolink + bundle + xcodebuild)").action(async () => {
6275
+ var iosBuildCmd = ios.command("build").option("-t, --target <target>", "Build target: dev-app (default) or host", "dev-app").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-i, --install", "Install and launch on booted simulator after building").option("-d, --debug", "Build debug (development) configuration").option("-r, --release", "Build release (production) configuration").description("Build iOS app (autolink + bundle + xcodebuild)").action(async () => {
6293
6276
  const opts = iosBuildCmd.opts();
6294
6277
  validateDebugRelease(opts.debug, opts.release);
6295
6278
  const release = opts.release === true;
@@ -6361,8 +6344,8 @@ program.command("codegen").description("Generate code from @lynxmodule declarati
6361
6344
  program.command("autolink-toggle").alias("autolink").description("Toggle autolink on/off in tamer.config.json (controls postinstall linking)").action(async () => {
6362
6345
  const configPath = path25.join(process.cwd(), "tamer.config.json");
6363
6346
  let config = {};
6364
- if (fs25.existsSync(configPath)) {
6365
- config = JSON.parse(fs25.readFileSync(configPath, "utf8"));
6347
+ if (fs24.existsSync(configPath)) {
6348
+ config = JSON.parse(fs24.readFileSync(configPath, "utf8"));
6366
6349
  }
6367
6350
  if (config.autolink) {
6368
6351
  delete config.autolink;
@@ -6371,7 +6354,7 @@ program.command("autolink-toggle").alias("autolink").description("Toggle autolin
6371
6354
  config.autolink = true;
6372
6355
  console.log("Autolink enabled in tamer.config.json");
6373
6356
  }
6374
- fs25.writeFileSync(configPath, JSON.stringify(config, null, 2));
6357
+ fs24.writeFileSync(configPath, JSON.stringify(config, null, 2));
6375
6358
  console.log(`Updated ${configPath}`);
6376
6359
  });
6377
6360
  if (process.argv.length <= 2 || process.argv.length === 3 && process.argv[2] === "init") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamer4lynx/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "description": "A CLI tool for managing LynxJS native modules.",
@@ -59,6 +59,7 @@
59
59
  "android:build": "node dist/index.js android build",
60
60
  "android:build:install": "node dist/index.js android build --install",
61
61
  "docs:ci": "npm ci --ignore-scripts && npm run build --workspace=docs",
62
+ "prepare": "node -e \"const fs=require('fs');if(!fs.existsSync('dist/index.js'))require('child_process').execSync('npm run build',{stdio:'inherit'})\"",
62
63
  "prepublishOnly": "npm run build",
63
64
  "publish:all": "node scripts/publish-all.mjs"
64
65
  },