@tamer4lynx/cli 0.0.1

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.
@@ -0,0 +1,597 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { execSync } from "child_process";
4
+ import { setupCocoaPods } from "./getPod";
5
+ import { randomBytes } from "crypto";
6
+ import { loadHostConfig, resolveIconPaths } from "../common/hostConfig";
7
+ const create = () => {
8
+ const generateId = () => randomBytes(12).toString('hex').toUpperCase();
9
+ let appName;
10
+ let bundleId;
11
+ let config;
12
+ try {
13
+ config = loadHostConfig();
14
+ appName = config.ios?.appName;
15
+ bundleId = config.ios?.bundleId;
16
+ if (!appName && !bundleId) {
17
+ throw new Error('"ios.appName" and "ios.bundleId" must be defined in tamer.config.json');
18
+ }
19
+ }
20
+ catch (error) {
21
+ console.error(`โŒ Error loading configuration: ${error.message}`);
22
+ process.exit(1);
23
+ }
24
+ const iosDir = config.paths?.iosDir ?? "ios";
25
+ const rootDir = path.join(process.cwd(), iosDir);
26
+ const projectDir = path.join(rootDir, appName);
27
+ const xcodeprojDir = path.join(rootDir, `${appName}.xcodeproj`);
28
+ const bridgingHeader = `${appName}-Bridging-Header.h`;
29
+ function writeFile(filePath, content) {
30
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
31
+ fs.writeFileSync(filePath, content.trimStart(), "utf8");
32
+ }
33
+ // Clean up previous generation if it exists
34
+ if (fs.existsSync(rootDir)) {
35
+ console.log(`๐Ÿงน Removing existing directory: ${rootDir}`);
36
+ fs.rmSync(rootDir, { recursive: true, force: true });
37
+ }
38
+ console.log(`๐Ÿš€ Creating a new Tamer4Lynx project in: ${rootDir}`);
39
+ // --- Generate Xcode Object IDs ---
40
+ const ids = {
41
+ project: generateId(),
42
+ mainGroup: generateId(),
43
+ appGroup: generateId(),
44
+ productsGroup: generateId(),
45
+ frameworksGroup: generateId(),
46
+ appFile: generateId(),
47
+ appDelegateRef: generateId(),
48
+ sceneDelegateRef: generateId(),
49
+ viewControllerRef: generateId(),
50
+ mainStoryboardRef: generateId(),
51
+ assetsRef: generateId(),
52
+ launchStoryboardRef: generateId(),
53
+ lynxProviderRef: generateId(),
54
+ lynxInitRef: generateId(),
55
+ bridgingHeaderRef: generateId(),
56
+ mainStoryboardBaseRef: generateId(),
57
+ launchStoryboardBaseRef: generateId(),
58
+ nativeTarget: generateId(),
59
+ appDelegateBuildFile: generateId(),
60
+ sceneDelegateBuildFile: generateId(),
61
+ viewControllerBuildFile: generateId(),
62
+ lynxProviderBuildFile: generateId(),
63
+ lynxInitBuildFile: generateId(),
64
+ mainStoryboardBuildFile: generateId(),
65
+ assetsBuildFile: generateId(),
66
+ launchStoryboardBuildFile: generateId(),
67
+ frameworksBuildPhase: generateId(),
68
+ resourcesBuildPhase: generateId(),
69
+ sourcesBuildPhase: generateId(),
70
+ projectBuildConfigList: generateId(),
71
+ targetBuildConfigList: generateId(),
72
+ projectDebugConfig: generateId(),
73
+ projectReleaseConfig: generateId(),
74
+ targetDebugConfig: generateId(),
75
+ targetReleaseConfig: generateId(),
76
+ };
77
+ // --- Start File Generation ---
78
+ // Podfile
79
+ writeFile(path.join(rootDir, "Podfile"), `
80
+ source 'https://cdn.cocoapods.org/'
81
+
82
+ platform :ios, '13.0'
83
+
84
+ target '${appName}' do
85
+ pod 'Lynx', '3.3.0', :subspecs => [
86
+ 'Framework',
87
+ ], :modular_headers => true
88
+
89
+ pod 'PrimJS', '2.13.2', :subspecs => ['quickjs', 'napi']
90
+
91
+ # integrate image-service, log-service, and http-service
92
+ pod 'LynxService', '3.3.0', :subspecs => [
93
+ 'Image',
94
+ 'Log',
95
+ 'Http',
96
+ ]
97
+ pod 'SDWebImage','5.15.5'
98
+ pod 'SDWebImageWebPCoder', '0.11.0'
99
+
100
+ # GENERATED AUTOLINK DEPENDENCIES START
101
+ # This section is automatically generated by Tamer4Lynx.
102
+ # Manual edits will be overwritten.
103
+ # GENERATED AUTOLINK DEPENDENCIES END
104
+ end
105
+
106
+ post_install do |installer|
107
+ installer.pods_project.targets.each do |target|
108
+ if target.name == 'Lynx'
109
+ target.build_configurations.each do |config|
110
+ flags = [
111
+ '-Wno-vla-extension',
112
+ '-Wno-vla',
113
+ '-Wno-error=vla-extension',
114
+ '-Wno-deprecated-declarations',
115
+ '-Wno-deprecated',
116
+ '-Wno-macro-redefined',
117
+ '-Wno-enum-compare',
118
+ '-Wno-enum-compare-conditional',
119
+ '-Wno-enum-conversion'
120
+ ].join(' ')
121
+
122
+ config.build_settings['OTHER_CPLUSPLUSFLAGS'] = "$(inherited) #{flags}"
123
+ config.build_settings['OTHER_CFLAGS'] = "$(inherited) #{flags}"
124
+ config.build_settings['CLANG_WARN_VLA'] = 'NO'
125
+ config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'
126
+ config.build_settings['CLANG_WARN_ENUM_CONVERSION'] = 'NO'
127
+ end
128
+ end
129
+ end
130
+ end
131
+ `);
132
+ // --- App Files (same as before) ---
133
+ writeFile(path.join(projectDir, "AppDelegate.swift"), `
134
+ import UIKit
135
+
136
+ @UIApplicationMain
137
+ class AppDelegate: UIResponder, UIApplicationDelegate {
138
+ var window: UIWindow?
139
+
140
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
141
+ LynxInitProcessor.shared.setupEnvironment()
142
+ return true
143
+ }
144
+ }
145
+ `);
146
+ writeFile(path.join(projectDir, "ViewController.swift"), `
147
+ import UIKit
148
+
149
+ class ViewController: UIViewController {
150
+
151
+ override func viewDidLoad() {
152
+ super.viewDidLoad()
153
+
154
+ let lynxView = LynxView { builder in
155
+ builder.config = LynxConfig(provider: LynxProvider())
156
+ builder.screenSize = self.view.frame.size
157
+ builder.fontScale = 1.0
158
+ }
159
+
160
+ lynxView.preferredLayoutWidth = self.view.frame.size.width
161
+ lynxView.preferredLayoutHeight = self.view.frame.size.height
162
+ lynxView.layoutWidthMode = .exact
163
+ lynxView.layoutHeightMode = .exact
164
+ self.view.addSubview(lynxView)
165
+
166
+ lynxView.loadTemplate(fromURL: "main.lynx", initData: nil)
167
+ }
168
+ }
169
+ `);
170
+ writeFile(path.join(projectDir, "LynxProvider.swift"), `
171
+ import Foundation
172
+
173
+ class LynxProvider: NSObject, LynxTemplateProvider {
174
+ func loadTemplate(withUrl url: String!, onComplete callback: LynxTemplateLoadBlock!) {
175
+ if let filePath = Bundle.main.path(forResource: url, ofType: "bundle") {
176
+ do {
177
+ let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
178
+ callback(data, nil)
179
+ } catch {
180
+ print("Error reading file: \\(error.localizedDescription)")
181
+ callback(nil, error)
182
+ }
183
+ } else {
184
+ let urlError = NSError(domain: "com.lynx", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid URL."])
185
+ callback(nil, urlError)
186
+ }
187
+ }
188
+ }
189
+ `);
190
+ // LynxInitProcessor.swift - provides a hook for autolinked native module registrations
191
+ writeFile(path.join(projectDir, "LynxInitProcessor.swift"), `
192
+ // Copyright 2024 The Lynx Authors. All rights reserved.
193
+ // Licensed under the Apache License Version 2.0 that can be found in the
194
+ // LICENSE file in the root directory of this source tree.
195
+
196
+ import Foundation
197
+
198
+ // GENERATED IMPORTS START
199
+ // This section is automatically generated by Tamer4Lynx.
200
+ // Manual edits will be overwritten.
201
+ // GENERATED IMPORTS END
202
+
203
+ final class LynxInitProcessor {
204
+ static let shared = LynxInitProcessor()
205
+ private init() {}
206
+
207
+ func setupEnvironment() {
208
+ setupLynxEnv()
209
+ setupLynxService()
210
+ }
211
+
212
+ private func setupLynxEnv() {
213
+ // Ensure LynxEnv singleton is initialized
214
+ let env = LynxEnv.sharedInstance()
215
+
216
+ // init global config
217
+ // Assumes \`initWithProvider:\` is exposed to Swift as \`init(provider:)\`
218
+ let globalConfig = LynxConfig(provider: env.config.templateProvider)
219
+
220
+ // register global JS module
221
+ // GENERATED AUTOLINK START
222
+
223
+ // GENERATED AUTOLINK END
224
+
225
+ // prepare global config
226
+ env.prepareConfig(globalConfig)
227
+ }
228
+
229
+ private func setupLynxService() {
230
+ // prepare lynx service
231
+ // Assumes SDWebImage/SDWebImageWebPCoder exposes \`shared\` singletons to Swift
232
+ let webPCoder = SDImageWebPCoder.shared
233
+ SDImageCodersManager.shared.addCoder(webPCoder)
234
+ }
235
+ }
236
+ `);
237
+ writeFile(path.join(projectDir, bridgingHeader), `
238
+ #import <Lynx/LynxConfig.h>
239
+ #import <Lynx/LynxEnv.h>
240
+ #import <Lynx/LynxTemplateProvider.h>
241
+ #import <Lynx/LynxView.h>
242
+ #import <Lynx/LynxModule.h>
243
+ #import <SDWebImage/SDWebImage.h>
244
+ #import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
245
+ `);
246
+ const appIconDir = path.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
247
+ fs.mkdirSync(appIconDir, { recursive: true });
248
+ const iconPaths = resolveIconPaths(process.cwd(), config);
249
+ if (iconPaths?.ios) {
250
+ const entries = fs.readdirSync(iconPaths.ios, { withFileTypes: true });
251
+ for (const e of entries) {
252
+ const dest = path.join(appIconDir, e.name);
253
+ if (e.isDirectory()) {
254
+ fs.cpSync(path.join(iconPaths.ios, e.name), dest, { recursive: true });
255
+ }
256
+ else {
257
+ fs.copyFileSync(path.join(iconPaths.ios, e.name), dest);
258
+ }
259
+ }
260
+ console.log("โœ… Copied iOS icon from tamer.config.json icon.ios");
261
+ }
262
+ else if (iconPaths?.source) {
263
+ const ext = path.extname(iconPaths.source) || ".png";
264
+ const icon1024 = `Icon-1024${ext}`;
265
+ fs.copyFileSync(iconPaths.source, path.join(appIconDir, icon1024));
266
+ writeFile(path.join(appIconDir, "Contents.json"), JSON.stringify({
267
+ images: [{ filename: icon1024, idiom: "universal", platform: "ios", size: "1024x1024" }],
268
+ info: { author: "xcode", version: 1 }
269
+ }, null, 2));
270
+ console.log("โœ… Copied app icon from tamer.config.json icon.source");
271
+ }
272
+ else {
273
+ writeFile(path.join(appIconDir, "Contents.json"), `
274
+ {
275
+ "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ],
276
+ "info" : { "author" : "xcode", "version" : 1 }
277
+ }
278
+ `);
279
+ }
280
+ // --- Xcode Project File (VALID TEMPLATE) ---
281
+ fs.mkdirSync(xcodeprojDir, { recursive: true });
282
+ writeFile(path.join(xcodeprojDir, "project.pbxproj"), `
283
+ // !$*UTF8*$!
284
+ {
285
+ archiveVersion = 1;
286
+ classes = {};
287
+ objectVersion = 56;
288
+ objects = {
289
+ /* Begin PBXBuildFile section */
290
+ ${ids.appDelegateBuildFile} /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.appDelegateRef} /* AppDelegate.swift */; };
291
+ ${ids.viewControllerBuildFile} /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.viewControllerRef} /* ViewController.swift */; };
292
+ ${ids.mainStoryboardBuildFile} /* Base in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.mainStoryboardBaseRef} /* Base */; };
293
+ ${ids.assetsBuildFile} /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.assetsRef} /* Assets.xcassets */; };
294
+ ${ids.lynxProviderBuildFile} /* LynxProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.lynxProviderRef} /* LynxProvider.swift */; };
295
+ ${ids.lynxInitBuildFile} /* LynxInitProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.lynxInitRef} /* LynxInitProcessor.swift */; };
296
+ /* End PBXBuildFile section */
297
+
298
+ /* Begin PBXFileReference section */
299
+ ${ids.appFile} /* ${appName}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "${appName}.app"; sourceTree = BUILT_PRODUCTS_DIR; };
300
+ ${ids.appDelegateRef} /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate.swift"; sourceTree = "<group>"; };
301
+ ${ids.viewControllerRef} /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController.swift"; sourceTree = "<group>"; };
302
+ ${ids.mainStoryboardBaseRef} /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Base.lproj/Main.storyboard"; sourceTree = "<group>"; };
303
+ ${ids.assetsRef} /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Assets.xcassets"; sourceTree = "<group>"; };
304
+ ${ids.lynxProviderRef} /* LynxProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LynxProvider.swift"; sourceTree = "<group>"; };
305
+ ${ids.lynxInitRef} /* LynxInitProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LynxInitProcessor.swift"; sourceTree = "<group>"; };
306
+ ${ids.bridgingHeaderRef} /* ${bridgingHeader} */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "${bridgingHeader}"; sourceTree = "<group>"; };
307
+ /* End PBXFileReference section */
308
+
309
+ /* Begin PBXFrameworksBuildPhase section */
310
+ ${ids.frameworksBuildPhase} /* Frameworks */ = {
311
+ isa = PBXFrameworksBuildPhase;
312
+ buildActionMask = 2147483647;
313
+ files = (
314
+ );
315
+ runOnlyForDeploymentPostprocessing = 0;
316
+ };
317
+ /* End PBXFrameworksBuildPhase section */
318
+
319
+ /* Begin PBXGroup section */
320
+ ${ids.mainGroup} = {
321
+ isa = PBXGroup;
322
+ children = (
323
+ ${ids.appGroup} /* ${appName} */,
324
+ ${ids.productsGroup} /* Products */,
325
+ ${ids.frameworksGroup} /* Frameworks */,
326
+ );
327
+ sourceTree = "<group>";
328
+ };
329
+ ${ids.productsGroup} /* Products */ = {
330
+ isa = PBXGroup;
331
+ children = (
332
+ ${ids.appFile} /* ${appName}.app */,
333
+ );
334
+ name = Products;
335
+ sourceTree = "<group>";
336
+ };
337
+ ${ids.frameworksGroup} /* Frameworks */ = {
338
+ isa = PBXGroup;
339
+ children = (
340
+ );
341
+ name = Frameworks;
342
+ sourceTree = "<group>";
343
+ };
344
+ ${ids.appGroup} /* ${appName} */ = {
345
+ isa = PBXGroup;
346
+ children = (
347
+ ${ids.appDelegateRef} /* AppDelegate.swift */,
348
+ ${ids.viewControllerRef} /* ViewController.swift */,
349
+ ${ids.mainStoryboardRef} /* Main.storyboard */,
350
+ ${ids.assetsRef} /* Assets.xcassets */,
351
+ ${ids.lynxProviderRef} /* LynxProvider.swift */,
352
+ ${ids.lynxInitRef} /* LynxInitProcessor.swift */,
353
+ ${ids.bridgingHeaderRef} /* ${bridgingHeader} */,
354
+ );
355
+ path = "${appName}";
356
+ sourceTree = "<group>";
357
+ };
358
+ /* End PBXGroup section */
359
+
360
+ /* Begin PBXNativeTarget section */
361
+ ${ids.nativeTarget} /* ${appName} */ = {
362
+ isa = PBXNativeTarget;
363
+ buildConfigurationList = ${ids.targetBuildConfigList} /* Build configuration list for PBXNativeTarget "${appName}" */;
364
+ buildPhases = (
365
+ ${ids.sourcesBuildPhase} /* Sources */,
366
+ ${ids.frameworksBuildPhase} /* Frameworks */,
367
+ ${ids.resourcesBuildPhase} /* Resources */,
368
+ );
369
+ buildRules = (
370
+ );
371
+ dependencies = (
372
+ );
373
+ name = "${appName}";
374
+ productName = "${appName}";
375
+ productReference = ${ids.appFile} /* ${appName}.app */;
376
+ productType = "com.apple.product-type.application";
377
+ };
378
+ /* End PBXNativeTarget section */
379
+
380
+ /* Begin PBXProject section */
381
+ ${ids.project} /* Project object */ = {
382
+ isa = PBXProject;
383
+ attributes = {
384
+ LastUpgradeCheck = 1530;
385
+ };
386
+ buildConfigurationList = ${ids.projectBuildConfigList} /* Build configuration list for PBXProject "${appName}" */;
387
+ compatibilityVersion = "Xcode 14.0";
388
+ developmentRegion = en;
389
+ hasScannedForEncodings = 0;
390
+ knownRegions = (
391
+ en,
392
+ Base,
393
+ );
394
+ mainGroup = ${ids.mainGroup};
395
+ productRefGroup = ${ids.productsGroup} /* Products */;
396
+ projectDirPath = "";
397
+ projectRoot = "";
398
+ targets = (
399
+ ${ids.nativeTarget} /* ${appName} */,
400
+ );
401
+ };
402
+ /* End PBXProject section */
403
+
404
+ /* Begin PBXResourcesBuildPhase section */
405
+ ${ids.resourcesBuildPhase} /* Resources */ = {
406
+ isa = PBXResourcesBuildPhase;
407
+ buildActionMask = 2147483647;
408
+ files = (
409
+ ${ids.assetsBuildFile} /* Assets.xcassets in Resources */,
410
+ ${ids.mainStoryboardBuildFile} /* Base in Resources */,
411
+ );
412
+ runOnlyForDeploymentPostprocessing = 0;
413
+ };
414
+ /* End PBXResourcesBuildPhase section */
415
+
416
+ /* Begin PBXSourcesBuildPhase section */
417
+ ${ids.sourcesBuildPhase} /* Sources */ = {
418
+ isa = PBXSourcesBuildPhase;
419
+ buildActionMask = 2147483647;
420
+ files = (
421
+ ${ids.lynxProviderBuildFile} /* LynxProvider.swift in Sources */,
422
+ ${ids.lynxInitBuildFile} /* LynxInitProcessor.swift in Sources */,
423
+ ${ids.viewControllerBuildFile} /* ViewController.swift in Sources */,
424
+ ${ids.appDelegateBuildFile} /* AppDelegate.swift in Sources */,
425
+ );
426
+ runOnlyForDeploymentPostprocessing = 0;
427
+ };
428
+ /* End PBXSourcesBuildPhase section */
429
+
430
+ /* Begin PBXVariantGroup section */
431
+ ${ids.mainStoryboardRef} /* Main.storyboard */ = {
432
+ isa = PBXVariantGroup;
433
+ children = (
434
+ ${ids.mainStoryboardBaseRef} /* Base */,
435
+ );
436
+ name = "Main.storyboard";
437
+ sourceTree = "<group>";
438
+ };
439
+ /* End PBXVariantGroup section */
440
+
441
+ /* Begin XCBuildConfiguration section */
442
+ ${ids.projectDebugConfig} /* Debug */ = {
443
+ isa = XCBuildConfiguration;
444
+ buildSettings = {
445
+ ALWAYS_SEARCH_USER_PATHS = NO;
446
+ CLANG_ANALYZER_NONNULL = YES;
447
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
448
+ CLANG_CXX_LIBRARY = "libc++";
449
+ CLANG_ENABLE_MODULES = YES;
450
+ CLANG_ENABLE_OBJC_ARC = YES;
451
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
452
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
453
+ CLANG_WARN_EMPTY_BODY = YES;
454
+ CLANG_WARN_INT_CONVERSION = YES;
455
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
456
+ CLANG_WARN_UNREACHABLE_CODE = YES;
457
+ COPY_PHASE_STRIP = NO;
458
+ DEBUG_INFORMATION_FORMAT = dwarf;
459
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
460
+ GCC_C_LANGUAGE_STANDARD = gnu11;
461
+ GCC_NO_COMMON_BLOCKS = YES;
462
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
463
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
464
+ GCC_WARN_UNUSED_VARIABLE = YES;
465
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
466
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
467
+ SDKROOT = iphoneos;
468
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
469
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
470
+ };
471
+ name = Debug;
472
+ };
473
+ ${ids.projectReleaseConfig} /* Release */ = {
474
+ isa = XCBuildConfiguration;
475
+ buildSettings = {
476
+ ALWAYS_SEARCH_USER_PATHS = NO;
477
+ CLANG_ANALYZER_NONNULL = YES;
478
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
479
+ CLANG_CXX_LIBRARY = "libc++";
480
+ CLANG_ENABLE_MODULES = YES;
481
+ CLANG_ENABLE_OBJC_ARC = YES;
482
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
483
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
484
+ CLANG_WARN_EMPTY_BODY = YES;
485
+ CLANG_WARN_INT_CONVERSION = YES;
486
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
487
+ CLANG_WARN_UNREACHABLE_CODE = YES;
488
+ COPY_PHASE_STRIP = NO;
489
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
490
+ ENABLE_NS_ASSERTIONS = NO;
491
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
492
+ GCC_C_LANGUAGE_STANDARD = gnu11;
493
+ GCC_NO_COMMON_BLOCKS = YES;
494
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
495
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
496
+ GCC_WARN_UNUSED_VARIABLE = YES;
497
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
498
+ MTL_ENABLE_DEBUG_INFO = NO;
499
+ SDKROOT = iphoneos;
500
+ SWIFT_COMPILATION_MODE = wholemodule;
501
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
502
+ };
503
+ name = Release;
504
+ };
505
+ ${ids.targetDebugConfig} /* Debug */ = {
506
+ isa = XCBuildConfiguration;
507
+ buildSettings = {
508
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
509
+ CURRENT_PROJECT_VERSION = 1;
510
+ GENERATE_INFOPLIST_FILE = YES;
511
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
512
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
513
+ MARKETING_VERSION = "1.0";
514
+ PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}";
515
+ PRODUCT_NAME = "$(TARGET_NAME)";
516
+ SWIFT_OBJC_BRIDGING_HEADER = "${appName}/${bridgingHeader}";
517
+ SWIFT_VERSION = 5.0;
518
+ TARGETED_DEVICE_FAMILY = "1,2";
519
+ };
520
+ name = Debug;
521
+ };
522
+ ${ids.targetReleaseConfig} /* Release */ = {
523
+ isa = XCBuildConfiguration;
524
+ buildSettings = {
525
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
526
+ CURRENT_PROJECT_VERSION = 1;
527
+ GENERATE_INFOPLIST_FILE = YES;
528
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
529
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
530
+ MARKETING_VERSION = "1.0";
531
+ PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}";
532
+ PRODUCT_NAME = "$(TARGET_NAME)";
533
+ SWIFT_OBJC_BRIDGING_HEADER = "${appName}/${bridgingHeader}";
534
+ SWIFT_VERSION = 5.0;
535
+ TARGETED_DEVICE_FAMILY = "1,2";
536
+ };
537
+ name = Release;
538
+ };
539
+ /* End XCBuildConfiguration section */
540
+
541
+ /* Begin XCConfigurationList section */
542
+ ${ids.projectBuildConfigList} /* Build configuration list for PBXProject "${appName}" */ = {
543
+ isa = XCConfigurationList;
544
+ buildConfigurations = (
545
+ ${ids.projectDebugConfig} /* Debug */,
546
+ ${ids.projectReleaseConfig} /* Release */,
547
+ );
548
+ defaultConfigurationIsVisible = 0;
549
+ defaultConfigurationName = Release;
550
+ };
551
+ ${ids.targetBuildConfigList} /* Build configuration list for PBXNativeTarget "${appName}" */ = {
552
+ isa = XCConfigurationList;
553
+ buildConfigurations = (
554
+ ${ids.targetDebugConfig} /* Debug */,
555
+ ${ids.targetReleaseConfig} /* Release */,
556
+ );
557
+ defaultConfigurationIsVisible = 0;
558
+ defaultConfigurationName = Release;
559
+ };
560
+ /* End XCConfigurationList section */
561
+ };
562
+ rootObject = ${ids.project} /* Project object */;
563
+ }
564
+ `);
565
+ // Minimal Main.storyboard
566
+ fs.mkdirSync(path.join(projectDir, "Base.lproj"), { recursive: true });
567
+ writeFile(path.join(projectDir, "Base.lproj", "Main.storyboard"), `
568
+ <?xml version="1.0" encoding="UTF-8"?>
569
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
570
+ <dependencies>
571
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
572
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
573
+ </dependencies>
574
+ <scenes>
575
+ <scene sceneID="tne-QT-ifu">
576
+ <objects>
577
+ <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
578
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
579
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
580
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
581
+ <color key="backgroundColor" systemColor="systemBackgroundColor"/>
582
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
583
+ </view>
584
+ </viewController>
585
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
586
+ </objects>
587
+ </scene>
588
+ </scenes>
589
+ </document>
590
+ `);
591
+ console.log(`โœ… iOS Swift project created at ${rootDir}`);
592
+ async function finalizeProjectSetup() {
593
+ await setupCocoaPods(rootDir);
594
+ }
595
+ finalizeProjectSetup();
596
+ };
597
+ export default create;
@@ -0,0 +1,53 @@
1
+ import { execSync } from 'child_process';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ /**
5
+ * Checks if CocoaPods is installed on the system.
6
+ * @returns {boolean} True if installed, false otherwise.
7
+ */
8
+ function isCocoaPodsInstalled() {
9
+ try {
10
+ // The `command -v` command is a reliable way to check if a command exists in the shell's PATH.
11
+ // We pipe the output to /dev/null to keep the console clean.
12
+ execSync('command -v pod >/dev/null 2>&1');
13
+ return true;
14
+ }
15
+ catch (error) {
16
+ return false;
17
+ }
18
+ }
19
+ /**
20
+ * Sets up the CocoaPods environment for the project.
21
+ * @param rootDir The root directory of the iOS project.
22
+ */
23
+ export async function setupCocoaPods(rootDir) {
24
+ // First, check if CocoaPods is available.
25
+ if (!isCocoaPodsInstalled()) {
26
+ console.error("โŒ CocoaPods is not installed.");
27
+ console.log(" CocoaPods is required to manage native dependencies for iOS development.");
28
+ console.log(" Please install it using one of the following commands:");
29
+ console.log("\n Using Homebrew (Recommended):");
30
+ console.log(" brew install cocoapods");
31
+ console.log("\n Using RubyGems:");
32
+ console.log(" sudo gem install cocoapods\n");
33
+ process.exit(1); // Exit the script with an error code.
34
+ }
35
+ try {
36
+ console.log("๐Ÿ“ฆ CocoaPods is installed. Proceeding with dependency installation...");
37
+ const podfilePath = path.join(rootDir, 'Podfile');
38
+ if (!fs.existsSync(podfilePath)) {
39
+ throw new Error(`Podfile not found at ${podfilePath}`);
40
+ }
41
+ console.log(`๐Ÿš€ Executing pod install in: ${rootDir}`);
42
+ execSync(`pod install`, {
43
+ cwd: rootDir,
44
+ stdio: "inherit",
45
+ });
46
+ console.log("โœ… CocoaPods dependencies installed successfully.");
47
+ }
48
+ catch (err) {
49
+ console.error("โŒ Failed to install CocoaPods dependencies.", err.message);
50
+ // Exit the process if pod installation fails, as it's a critical step.
51
+ process.exit(1);
52
+ }
53
+ }
@@ -0,0 +1,7 @@
1
+ import fs from 'fs';
2
+ import link from "./autolink";
3
+ import path from 'path';
4
+ const iosRoot = path.join(process.cwd(), 'ios');
5
+ if (fs.existsSync(iosRoot)) {
6
+ link();
7
+ }