@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.
- package/README.md +10 -0
- package/dist/index.js +220 -237
- 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
|
|
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.
|
|
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(
|
|
319
|
-
const devAppDir =
|
|
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(
|
|
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
|
|
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
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
2311
|
-
|
|
2286
|
+
fs11.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
2287
|
+
fs11.writeFileSync(filePath, content.trimStart(), "utf8");
|
|
2312
2288
|
}
|
|
2313
|
-
if (
|
|
2289
|
+
if (fs11.existsSync(rootDir)) {
|
|
2314
2290
|
console.log(`\u{1F9F9} Removing existing directory: ${rootDir}`);
|
|
2315
|
-
|
|
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 (
|
|
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
|
-
|
|
2621
|
+
fs11.mkdirSync(appIconDir, { recursive: true });
|
|
2646
2622
|
const iconPaths = resolveIconPaths(process.cwd(), config);
|
|
2647
2623
|
if (iconPaths?.ios) {
|
|
2648
|
-
const entries =
|
|
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
|
-
|
|
2628
|
+
fs11.cpSync(path12.join(iconPaths.ios, e.name), dest, { recursive: true });
|
|
2653
2629
|
} else {
|
|
2654
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
2987
|
+
if (fs12.existsSync(fullPodspecDir)) {
|
|
3012
2988
|
try {
|
|
3013
|
-
const files =
|
|
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) =>
|
|
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 =
|
|
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
|
-
|
|
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) =>
|
|
3106
|
+
return candidates.find((p) => fs12.existsSync(p)) ?? null;
|
|
3131
3107
|
}
|
|
3132
3108
|
function readPlistXml(plistPath) {
|
|
3133
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
3413
|
-
|
|
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 (!
|
|
3624
|
-
let content =
|
|
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
|
-
|
|
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 (
|
|
3683
|
-
let content =
|
|
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 (!
|
|
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 (!
|
|
3713
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
3778
|
+
if (fs14.existsSync(pbxprojPath)) {
|
|
3803
3779
|
const skip = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
|
|
3804
|
-
for (const entry of
|
|
3805
|
-
if (skip.has(entry) ||
|
|
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 (
|
|
3822
|
-
|
|
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 (
|
|
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 (!
|
|
3832
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
3870
|
-
|
|
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 (
|
|
4884
|
-
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
4997
|
-
|
|
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 =
|
|
4981
|
+
const hasCommittedSource = fs15.existsSync(path16.join(projectDir, "AppDelegate.swift"));
|
|
5006
4982
|
if (!hasCommittedSource) {
|
|
5007
4983
|
await createDevAppProject(iosDir, repoRoot);
|
|
5008
|
-
} else if (!
|
|
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 (
|
|
5027
|
-
|
|
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 =
|
|
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 (!
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5271
|
-
|
|
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
|
-
|
|
5277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5316
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5314
|
+
fs18.writeFileSync(path19.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
|
|
5340
5315
|
}
|
|
5341
|
-
|
|
5316
|
+
fs18.writeFileSync(path19.join(root, "index.js"), `'use strict';
|
|
5342
5317
|
module.exports = {};
|
|
5343
5318
|
`);
|
|
5344
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
5413
|
-
const entries =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 &&
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
|
5678
|
+
import fs21 from "fs";
|
|
5696
5679
|
import path22 from "path";
|
|
5697
5680
|
function readAndSubstitute(templatePath, vars) {
|
|
5698
|
-
const raw =
|
|
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 (!
|
|
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 (!
|
|
5737
|
-
if (
|
|
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
|
-
|
|
5743
|
-
|
|
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 (!
|
|
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 (!
|
|
5781
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
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
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5961
|
-
|
|
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
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
5994
|
-
|
|
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 (
|
|
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 (
|
|
5998
|
+
if (fs22.existsSync(src)) {
|
|
6016
5999
|
const dest = path23.join(androidDir, name);
|
|
6017
|
-
if (
|
|
6018
|
-
|
|
6000
|
+
if (fs22.statSync(src).isDirectory()) {
|
|
6001
|
+
fs22.cpSync(src, dest, { recursive: true });
|
|
6019
6002
|
} else {
|
|
6020
|
-
|
|
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 (
|
|
6042
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6100
|
-
|
|
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 (!
|
|
6106
|
-
const files =
|
|
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 (!
|
|
6151
|
-
const files =
|
|
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
|
-
|
|
6171
|
-
|
|
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
|
-
|
|
6179
|
-
|
|
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
|
|
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 (
|
|
6204
|
-
if (
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 (
|
|
6365
|
-
config = JSON.parse(
|
|
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
|
-
|
|
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.
|
|
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
|
},
|