@teardown/cli 1.2.39 → 2.0.41
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/bin/teardown.js +11 -1
- package/package.json +77 -57
- package/src/cli/commands/init.ts +254 -0
- package/src/cli/commands/plugins.ts +93 -0
- package/src/cli/commands/prebuild.ts +168 -0
- package/src/cli/commands/run.ts +727 -0
- package/src/cli/commands/start.ts +87 -0
- package/src/cli/commands/validate.ts +62 -0
- package/src/cli/index.ts +59 -0
- package/src/config/index.ts +45 -0
- package/src/config/loader.ts +366 -0
- package/src/config/schema.ts +235 -0
- package/src/config/types.ts +322 -0
- package/src/index.ts +177 -0
- package/src/pipeline/cache.ts +179 -0
- package/src/pipeline/index.ts +10 -0
- package/src/pipeline/stages.ts +692 -0
- package/src/plugins/base.ts +370 -0
- package/src/plugins/capabilities/biometrics.ts +64 -0
- package/src/plugins/capabilities/bluetooth.ts +86 -0
- package/src/plugins/capabilities/calendar.ts +57 -0
- package/src/plugins/capabilities/camera.ts +77 -0
- package/src/plugins/capabilities/contacts.ts +57 -0
- package/src/plugins/capabilities/deep-linking.ts +124 -0
- package/src/plugins/capabilities/firebase.ts +138 -0
- package/src/plugins/capabilities/index.ts +96 -0
- package/src/plugins/capabilities/location.ts +87 -0
- package/src/plugins/capabilities/photo-library.ts +80 -0
- package/src/plugins/capabilities/push-notifications.ts +98 -0
- package/src/plugins/capabilities/sign-in-with-apple.ts +53 -0
- package/src/plugins/context.ts +220 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/resolver.ts +321 -0
- package/src/templates/generator.ts +507 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/paths.ts +25 -0
- package/src/transformers/android/gradle.ts +400 -0
- package/src/transformers/android/index.ts +19 -0
- package/src/transformers/android/manifest.ts +506 -0
- package/src/transformers/index.ts +39 -0
- package/src/transformers/ios/entitlements.ts +283 -0
- package/src/transformers/ios/index.ts +10 -0
- package/src/transformers/ios/pbxproj.ts +267 -0
- package/src/transformers/ios/plist.ts +198 -0
- package/src/utils/fs.ts +429 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/logger.ts +203 -0
- package/templates/.gitignore +63 -0
- package/templates/Gemfile +3 -0
- package/templates/android/app/build.gradle.kts +97 -0
- package/templates/android/app/proguard-rules.pro +10 -0
- package/templates/android/app/src/main/AndroidManifest.xml +26 -0
- package/templates/android/app/src/main/java/com/appname/MainActivity.kt +22 -0
- package/templates/android/app/src/main/java/com/appname/MainApplication.kt +44 -0
- package/templates/android/app/src/main/res/values/strings.xml +3 -0
- package/templates/android/app/src/main/res/values/styles.xml +7 -0
- package/templates/android/build.gradle.kts +44 -0
- package/templates/android/gradle.properties +39 -0
- package/templates/android/settings.gradle.kts +12 -0
- package/templates/babel.config.js +15 -0
- package/templates/index.js +7 -0
- package/templates/ios/.xcode.env +11 -0
- package/templates/ios/AppName/AppDelegate.swift +25 -0
- package/templates/ios/AppName/AppName-Bridging-Header.h +4 -0
- package/templates/ios/AppName/AppName.entitlements +6 -0
- package/templates/ios/AppName/Images.xcassets/AppIcon.appiconset/Contents.json +35 -0
- package/templates/ios/AppName/Images.xcassets/Contents.json +6 -0
- package/templates/ios/AppName/Info.plist +49 -0
- package/templates/ios/AppName/LaunchScreen.storyboard +38 -0
- package/templates/ios/AppName.xcodeproj/project.pbxproj +402 -0
- package/templates/ios/AppName.xcodeproj/xcshareddata/xcschemes/AppName.xcscheme +78 -0
- package/templates/ios/Podfile +35 -0
- package/templates/metro.config.js +41 -0
- package/templates/package.json +57 -0
- package/templates/react-native.config.js +8 -0
- package/templates/src/app/index.tsx +34 -0
- package/templates/src/assets/fonts/.gitkeep +1 -0
- package/templates/src/assets/images/.gitkeep +1 -0
- package/templates/src/components/ui/accordion.tsx +114 -0
- package/templates/src/components/ui/avatar.tsx +75 -0
- package/templates/src/components/ui/button.tsx +93 -0
- package/templates/src/components/ui/card.tsx +120 -0
- package/templates/src/components/ui/checkbox.tsx +133 -0
- package/templates/src/components/ui/chip.tsx +95 -0
- package/templates/src/components/ui/dialog.tsx +134 -0
- package/templates/src/components/ui/divider.tsx +67 -0
- package/templates/src/components/ui/error-view.tsx +82 -0
- package/templates/src/components/ui/form-field.tsx +101 -0
- package/templates/src/components/ui/index.ts +100 -0
- package/templates/src/components/ui/popover.tsx +92 -0
- package/templates/src/components/ui/pressable-feedback.tsx +88 -0
- package/templates/src/components/ui/radio-group.tsx +153 -0
- package/templates/src/components/ui/scroll-shadow.tsx +108 -0
- package/templates/src/components/ui/select.tsx +165 -0
- package/templates/src/components/ui/skeleton-group.tsx +97 -0
- package/templates/src/components/ui/skeleton.tsx +87 -0
- package/templates/src/components/ui/spinner.tsx +87 -0
- package/templates/src/components/ui/surface.tsx +95 -0
- package/templates/src/components/ui/switch.tsx +124 -0
- package/templates/src/components/ui/tabs.tsx +154 -0
- package/templates/src/components/ui/text-field.tsx +106 -0
- package/templates/src/components/ui/toast.tsx +129 -0
- package/templates/src/contexts/.gitkeep +2 -0
- package/templates/src/core/clients/api/api.client.ts +113 -0
- package/templates/src/core/clients/api/index.ts +1 -0
- package/templates/src/core/clients/storage/index.ts +1 -0
- package/templates/src/core/clients/storage/storage.client.ts +121 -0
- package/templates/src/core/constants/index.ts +19 -0
- package/templates/src/core/core.ts +40 -0
- package/templates/src/core/index.ts +10 -0
- package/templates/src/global.css +87 -0
- package/templates/src/hooks/index.ts +6 -0
- package/templates/src/hooks/use-debounce.ts +23 -0
- package/templates/src/hooks/use-mounted.ts +21 -0
- package/templates/src/index.ts +28 -0
- package/templates/src/lib/index.ts +5 -0
- package/templates/src/lib/utils.ts +115 -0
- package/templates/src/modules/.gitkeep +6 -0
- package/templates/src/navigation/index.ts +8 -0
- package/templates/src/navigation/navigation-provider.tsx +36 -0
- package/templates/src/navigation/router.tsx +137 -0
- package/templates/src/providers/app.provider.tsx +29 -0
- package/templates/src/providers/index.ts +5 -0
- package/templates/src/routes/(tabs)/_layout.tsx +42 -0
- package/templates/src/routes/(tabs)/explore.tsx +161 -0
- package/templates/src/routes/(tabs)/home.tsx +138 -0
- package/templates/src/routes/(tabs)/profile.tsx +151 -0
- package/templates/src/routes/_layout.tsx +18 -0
- package/templates/src/routes/settings.tsx +194 -0
- package/templates/src/screens/auth/index.ts +6 -0
- package/templates/src/screens/auth/login.tsx +165 -0
- package/templates/src/screens/auth/register.tsx +203 -0
- package/templates/src/screens/home.tsx +204 -0
- package/templates/src/screens/index.ts +17 -0
- package/templates/src/screens/profile.tsx +210 -0
- package/templates/src/screens/settings.tsx +216 -0
- package/templates/src/screens/welcome.tsx +101 -0
- package/templates/src/styles/index.ts +103 -0
- package/templates/src/types/common.ts +71 -0
- package/templates/src/types/index.ts +5 -0
- package/templates/tsconfig.json +14 -0
- package/README.md +0 -15
- package/assets/favicon.ico +0 -0
- package/dist/commands/dev/dev.d.ts +0 -22
- package/dist/commands/dev/dev.js +0 -56
- package/dist/commands/dev/dev.js.map +0 -1
- package/dist/commands/init/init-teardown.d.ts +0 -9
- package/dist/commands/init/init-teardown.js +0 -27
- package/dist/commands/init/init-teardown.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -21
- package/dist/index.js.map +0 -1
- package/dist/modules/dev/dev-menu/keyboard-handler.d.ts +0 -21
- package/dist/modules/dev/dev-menu/keyboard-handler.js +0 -139
- package/dist/modules/dev/dev-menu/keyboard-handler.js.map +0 -1
- package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.d.ts +0 -18
- package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js +0 -106
- package/dist/modules/dev/dev-menu/open-debugger-keyboard-handler.js.map +0 -1
- package/dist/modules/dev/dev-server/cdp/cdp.adapter.d.ts +0 -6
- package/dist/modules/dev/dev-server/cdp/cdp.adapter.js +0 -13
- package/dist/modules/dev/dev-server/cdp/cdp.adapter.js.map +0 -1
- package/dist/modules/dev/dev-server/cdp/index.d.ts +0 -2
- package/dist/modules/dev/dev-server/cdp/index.js +0 -19
- package/dist/modules/dev/dev-server/cdp/index.js.map +0 -1
- package/dist/modules/dev/dev-server/cdp/types.d.ts +0 -107
- package/dist/modules/dev/dev-server/cdp/types.js +0 -3
- package/dist/modules/dev/dev-server/cdp/types.js.map +0 -1
- package/dist/modules/dev/dev-server/dev-server-checker.d.ts +0 -22
- package/dist/modules/dev/dev-server/dev-server-checker.js +0 -73
- package/dist/modules/dev/dev-server/dev-server-checker.js.map +0 -1
- package/dist/modules/dev/dev-server/dev-server.d.ts +0 -74
- package/dist/modules/dev/dev-server/dev-server.js +0 -272
- package/dist/modules/dev/dev-server/dev-server.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/device.d.ts +0 -46
- package/dist/modules/dev/dev-server/inspector/device.event-reporter.d.ts +0 -37
- package/dist/modules/dev/dev-server/inspector/device.event-reporter.js +0 -166
- package/dist/modules/dev/dev-server/inspector/device.event-reporter.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/device.js +0 -578
- package/dist/modules/dev/dev-server/inspector/device.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/inspector.d.ts +0 -27
- package/dist/modules/dev/dev-server/inspector/inspector.js +0 -225
- package/dist/modules/dev/dev-server/inspector/inspector.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/types.d.ts +0 -156
- package/dist/modules/dev/dev-server/inspector/types.js +0 -3
- package/dist/modules/dev/dev-server/inspector/types.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.d.ts +0 -14
- package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js +0 -63
- package/dist/modules/dev/dev-server/inspector/wss/servers/debugger-connection.server.js.map +0 -1
- package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.d.ts +0 -19
- package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js +0 -66
- package/dist/modules/dev/dev-server/inspector/wss/servers/device-connection.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/devtools.plugin.d.ts +0 -1
- package/dist/modules/dev/dev-server/plugins/devtools.plugin.js +0 -51
- package/dist/modules/dev/dev-server/plugins/devtools.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/favicon.plugin.d.ts +0 -1
- package/dist/modules/dev/dev-server/plugins/favicon.plugin.js +0 -19
- package/dist/modules/dev/dev-server/plugins/favicon.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/multipart.plugin.d.ts +0 -1
- package/dist/modules/dev/dev-server/plugins/multipart.plugin.js +0 -63
- package/dist/modules/dev/dev-server/plugins/multipart.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/systrace.plugin.d.ts +0 -1
- package/dist/modules/dev/dev-server/plugins/systrace.plugin.js +0 -29
- package/dist/modules/dev/dev-server/plugins/systrace.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/types.d.ts +0 -11
- package/dist/modules/dev/dev-server/plugins/types.js +0 -3
- package/dist/modules/dev/dev-server/plugins/types.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/index.d.ts +0 -3
- package/dist/modules/dev/dev-server/plugins/wss/index.js +0 -20
- package/dist/modules/dev/dev-server/plugins/wss/index.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.d.ts +0 -37
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js +0 -67
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-api.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.d.ts +0 -63
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js +0 -129
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-debugger.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.d.ts +0 -32
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js +0 -76
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-dev-client.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.d.ts +0 -75
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js +0 -199
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-events.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.d.ts +0 -44
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js +0 -121
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-hmr.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.d.ts +0 -135
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js +0 -364
- package/dist/modules/dev/dev-server/plugins/wss/servers/web-socket-message.server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/types.d.ts +0 -6
- package/dist/modules/dev/dev-server/plugins/wss/types.js +0 -3
- package/dist/modules/dev/dev-server/plugins/wss/types.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.d.ts +0 -32
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js +0 -58
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-router.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.d.ts +0 -13
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js +0 -27
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server-adapter.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.d.ts +0 -39
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js +0 -47
- package/dist/modules/dev/dev-server/plugins/wss/web-socket-server.js.map +0 -1
- package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.d.ts +0 -24
- package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js +0 -56
- package/dist/modules/dev/dev-server/plugins/wss/wss.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.d.ts +0 -7
- package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js +0 -41
- package/dist/modules/dev/dev-server/sybmolicate/sybmolicate.plugin.js.map +0 -1
- package/dist/modules/dev/dev-server/sybmolicate/types.d.ts +0 -64
- package/dist/modules/dev/dev-server/sybmolicate/types.js +0 -3
- package/dist/modules/dev/dev-server/sybmolicate/types.js.map +0 -1
- package/dist/modules/dev/terminal/base.terminal.reporter.d.ts +0 -25
- package/dist/modules/dev/terminal/base.terminal.reporter.js +0 -79
- package/dist/modules/dev/terminal/base.terminal.reporter.js.map +0 -1
- package/dist/modules/dev/terminal/terminal.reporter.d.ts +0 -13
- package/dist/modules/dev/terminal/terminal.reporter.js +0 -83
- package/dist/modules/dev/terminal/terminal.reporter.js.map +0 -1
- package/dist/modules/dev/types.d.ts +0 -20
- package/dist/modules/dev/types.js +0 -3
- package/dist/modules/dev/types.js.map +0 -1
- package/dist/modules/dev/utils/log.d.ts +0 -23
- package/dist/modules/dev/utils/log.js +0 -74
- package/dist/modules/dev/utils/log.js.map +0 -1
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { loadConfig, validateConfig } from "../config";
|
|
5
|
+
import type { FileChange, PrebuildError, PrebuildOptions, PrebuildWarning, ProcessedAppConfig } from "../config/types";
|
|
6
|
+
import type { BasePlugin, PluginConstructor } from "../plugins/base";
|
|
7
|
+
import { createPluginContext, type PluginContext } from "../plugins/context";
|
|
8
|
+
import { DependencyResolver } from "../plugins/resolver";
|
|
9
|
+
import { TemplateGenerator } from "../templates";
|
|
10
|
+
import { GradleTransformer } from "../transformers/android/gradle";
|
|
11
|
+
import { AndroidManifestTransformer } from "../transformers/android/manifest";
|
|
12
|
+
import { EntitlementsTransformer } from "../transformers/ios/entitlements";
|
|
13
|
+
import { PbxprojTransformer } from "../transformers/ios/pbxproj";
|
|
14
|
+
import { PlistTransformer } from "../transformers/ios/plist";
|
|
15
|
+
import { createVirtualFileSystem } from "../utils/fs";
|
|
16
|
+
import { createLogger, type Logger } from "../utils/logger";
|
|
17
|
+
import { IncrementalBuildCache } from "./cache";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pipeline stage interface
|
|
21
|
+
*/
|
|
22
|
+
export interface PipelineStage {
|
|
23
|
+
name: string;
|
|
24
|
+
execute(context: PipelineContext): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pipeline execution context
|
|
29
|
+
*/
|
|
30
|
+
export interface PipelineContext {
|
|
31
|
+
config: ProcessedAppConfig;
|
|
32
|
+
pluginContext: PluginContext;
|
|
33
|
+
options: PrebuildOptions;
|
|
34
|
+
logger: Logger;
|
|
35
|
+
resolver: DependencyResolver;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Pipeline result
|
|
40
|
+
*/
|
|
41
|
+
export interface PipelineResult {
|
|
42
|
+
success: boolean;
|
|
43
|
+
config?: ProcessedAppConfig;
|
|
44
|
+
changes?: FileChange[];
|
|
45
|
+
warnings: PrebuildWarning[];
|
|
46
|
+
errors: PrebuildError[];
|
|
47
|
+
duration: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Main prebuild pipeline
|
|
52
|
+
*/
|
|
53
|
+
export class Pipeline {
|
|
54
|
+
private stages: PipelineStage[] = [];
|
|
55
|
+
private resolver: DependencyResolver;
|
|
56
|
+
private logger: Logger;
|
|
57
|
+
private cache: IncrementalBuildCache;
|
|
58
|
+
|
|
59
|
+
constructor(options: { logger?: Logger } = {}) {
|
|
60
|
+
this.logger = options.logger ?? createLogger({ prefix: "pipeline" });
|
|
61
|
+
this.resolver = new DependencyResolver(this.logger);
|
|
62
|
+
this.cache = new IncrementalBuildCache(this.logger);
|
|
63
|
+
|
|
64
|
+
// Initialize default stages
|
|
65
|
+
this.stages = [
|
|
66
|
+
new ConfigValidationStage(),
|
|
67
|
+
new PluginResolutionStage(),
|
|
68
|
+
new TemplateGenerationStage(),
|
|
69
|
+
new PluginPreProcessStage(),
|
|
70
|
+
new PluginTransformStage(),
|
|
71
|
+
new PluginPostProcessStage(),
|
|
72
|
+
new iOSGenerationStage(),
|
|
73
|
+
new IOSPostInstallStage(),
|
|
74
|
+
new AndroidGenerationStage(),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register a plugin with configuration
|
|
80
|
+
*/
|
|
81
|
+
registerPlugin<TConfig>(plugin: BasePlugin<TConfig>, config?: TConfig): this {
|
|
82
|
+
this.resolver.registerPlugin(plugin, config);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Register a plugin from constructor
|
|
88
|
+
*/
|
|
89
|
+
registerPluginFromConstructor<TConfig>(Constructor: PluginConstructor<TConfig>, config?: TConfig): this {
|
|
90
|
+
this.resolver.registerPluginFromConstructor(Constructor, config);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Add a custom stage to the pipeline
|
|
96
|
+
*/
|
|
97
|
+
addStage(stage: PipelineStage): this {
|
|
98
|
+
this.stages.push(stage);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Execute the pipeline
|
|
104
|
+
*/
|
|
105
|
+
async execute(options: PrebuildOptions): Promise<PipelineResult> {
|
|
106
|
+
const startTime = Date.now();
|
|
107
|
+
const warnings: PrebuildWarning[] = [];
|
|
108
|
+
const errors: PrebuildError[] = [];
|
|
109
|
+
|
|
110
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
111
|
+
|
|
112
|
+
this.logger.info("Starting prebuild pipeline...");
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Load configuration
|
|
116
|
+
const configResult = await loadConfig(projectRoot);
|
|
117
|
+
|
|
118
|
+
if (!configResult.success || !configResult.config) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
warnings,
|
|
122
|
+
errors: configResult.errors?.map((e) => ({
|
|
123
|
+
code: "CONFIG_ERROR",
|
|
124
|
+
message: e.message,
|
|
125
|
+
})) ?? [{ code: "CONFIG_ERROR", message: "Failed to load configuration" }],
|
|
126
|
+
duration: Date.now() - startTime,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check cache for incremental build
|
|
131
|
+
if (!options.clean && (await this.cache.shouldSkip(configResult.config, projectRoot))) {
|
|
132
|
+
this.logger.info("No changes detected, skipping prebuild");
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
warnings,
|
|
136
|
+
errors,
|
|
137
|
+
duration: Date.now() - startTime,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Create virtual file system
|
|
142
|
+
const fs = createVirtualFileSystem({
|
|
143
|
+
dryRun: options.dryRun ?? false,
|
|
144
|
+
projectRoot,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Create plugin context
|
|
148
|
+
const pluginContext = createPluginContext({
|
|
149
|
+
projectRoot,
|
|
150
|
+
platform: options.platform === "all" ? "both" : options.platform,
|
|
151
|
+
config: configResult.config,
|
|
152
|
+
fs,
|
|
153
|
+
log: this.logger,
|
|
154
|
+
isDryRun: options.dryRun ?? false,
|
|
155
|
+
isVerbose: options.verbose ?? false,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Create pipeline context
|
|
159
|
+
const pipelineContext: PipelineContext = {
|
|
160
|
+
config: pluginContext.config,
|
|
161
|
+
pluginContext,
|
|
162
|
+
options,
|
|
163
|
+
logger: this.logger,
|
|
164
|
+
resolver: this.resolver,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Execute stages
|
|
168
|
+
for (const stage of this.stages) {
|
|
169
|
+
// Skip platform-specific stages if not targeting that platform
|
|
170
|
+
if (stage.name === "ios-generation" && options.platform === "android") {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (stage.name === "ios-postinstall" && options.platform === "android") {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (stage.name === "android-generation" && options.platform === "ios") {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.logger.debug(`Executing stage: ${stage.name}`);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
await stage.execute(pipelineContext);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
errors.push({
|
|
186
|
+
code: "STAGE_ERROR",
|
|
187
|
+
message: `Stage "${stage.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
188
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
success: false,
|
|
193
|
+
config: pipelineContext.config,
|
|
194
|
+
changes: fs.getChanges(),
|
|
195
|
+
warnings: [...warnings, ...pluginContext.getWarnings()],
|
|
196
|
+
errors,
|
|
197
|
+
duration: Date.now() - startTime,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Get final changes
|
|
203
|
+
const changes = fs.getChanges();
|
|
204
|
+
|
|
205
|
+
// Update cache
|
|
206
|
+
if (!options.dryRun) {
|
|
207
|
+
await this.cache.update(
|
|
208
|
+
configResult.config,
|
|
209
|
+
projectRoot,
|
|
210
|
+
changes.map((c) => c.path)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.logger.success(`Prebuild completed in ${Date.now() - startTime}ms`);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
config: pipelineContext.config,
|
|
219
|
+
changes,
|
|
220
|
+
warnings: [...warnings, ...pluginContext.getWarnings()],
|
|
221
|
+
errors,
|
|
222
|
+
duration: Date.now() - startTime,
|
|
223
|
+
};
|
|
224
|
+
} catch (error) {
|
|
225
|
+
errors.push({
|
|
226
|
+
code: "PIPELINE_ERROR",
|
|
227
|
+
message: error instanceof Error ? error.message : String(error),
|
|
228
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
warnings,
|
|
234
|
+
errors,
|
|
235
|
+
duration: Date.now() - startTime,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get the dependency resolver
|
|
242
|
+
*/
|
|
243
|
+
getResolver(): DependencyResolver {
|
|
244
|
+
return this.resolver;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Clear the build cache
|
|
249
|
+
*/
|
|
250
|
+
async clearCache(projectRoot: string): Promise<void> {
|
|
251
|
+
await this.cache.clear(projectRoot);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Stage 1: Config Validation
|
|
257
|
+
*/
|
|
258
|
+
class ConfigValidationStage implements PipelineStage {
|
|
259
|
+
name = "config-validation";
|
|
260
|
+
|
|
261
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
262
|
+
const { logger, options } = context;
|
|
263
|
+
|
|
264
|
+
if (options.skipValidation) {
|
|
265
|
+
logger.debug("Skipping config validation");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
logger.debug("Validating configuration...");
|
|
270
|
+
|
|
271
|
+
const validation = validateConfig(context.config);
|
|
272
|
+
|
|
273
|
+
if (!validation.success) {
|
|
274
|
+
const errorMessages = validation.errors?.map((e) => e.message).join(", ");
|
|
275
|
+
throw new Error(`Configuration validation failed: ${errorMessages}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Stage 2: Plugin Resolution
|
|
282
|
+
*/
|
|
283
|
+
class PluginResolutionStage implements PipelineStage {
|
|
284
|
+
name = "plugin-resolution";
|
|
285
|
+
|
|
286
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
287
|
+
const { logger, resolver } = context;
|
|
288
|
+
|
|
289
|
+
logger.debug("Resolving plugin dependencies...");
|
|
290
|
+
|
|
291
|
+
// Validate all plugin configurations
|
|
292
|
+
const validationErrors = resolver.validateAllConfigs();
|
|
293
|
+
if (validationErrors.length > 0) {
|
|
294
|
+
const messages = validationErrors.map((e) => `${e.plugin}: ${e.error}`);
|
|
295
|
+
throw new Error(`Plugin configuration errors: ${messages.join("; ")}`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Resolve execution order (this will throw if there are cycles or missing deps)
|
|
299
|
+
const orderedPlugins = resolver.resolveOrder();
|
|
300
|
+
logger.debug(`Plugin execution order: ${orderedPlugins.map((p) => p.name).join(" -> ")}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Stage 3: Template Generation
|
|
306
|
+
*
|
|
307
|
+
* Generates native iOS and Android project files from templates
|
|
308
|
+
* if they don't already exist.
|
|
309
|
+
*/
|
|
310
|
+
class TemplateGenerationStage implements PipelineStage {
|
|
311
|
+
name = "template-generation";
|
|
312
|
+
|
|
313
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
314
|
+
const { logger, config, options } = context;
|
|
315
|
+
|
|
316
|
+
logger.debug("Checking if native projects need to be generated...");
|
|
317
|
+
|
|
318
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
319
|
+
|
|
320
|
+
// Create template variables from config
|
|
321
|
+
const variables = {
|
|
322
|
+
// Common
|
|
323
|
+
slug: config.slug,
|
|
324
|
+
|
|
325
|
+
// iOS
|
|
326
|
+
appName: config.name,
|
|
327
|
+
bundleIdentifier:
|
|
328
|
+
config.ios?.bundleIdentifier ?? `com.example.${config.name.toLowerCase().replace(/[^a-z0-9]/g, "")}`,
|
|
329
|
+
version: config.version ?? "1.0.0",
|
|
330
|
+
buildNumber: config.ios?.buildNumber ?? 1,
|
|
331
|
+
|
|
332
|
+
// Android
|
|
333
|
+
packageName: config.android?.packageName ?? `com.example.${config.name.toLowerCase().replace(/[^a-z0-9]/g, "")}`,
|
|
334
|
+
versionCode: config.android?.versionCode ?? 1,
|
|
335
|
+
versionName: config.version ?? "1.0.0",
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const generator = new TemplateGenerator({
|
|
339
|
+
projectRoot,
|
|
340
|
+
variables,
|
|
341
|
+
logger,
|
|
342
|
+
dryRun: options.dryRun ?? false,
|
|
343
|
+
force: false, // Don't overwrite existing files
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Check if we should generate iOS
|
|
347
|
+
const shouldGenerateiOS = options.platform !== "android";
|
|
348
|
+
const shouldGenerateAndroid = options.platform !== "ios";
|
|
349
|
+
|
|
350
|
+
if (shouldGenerateiOS) {
|
|
351
|
+
const iosExists = await generator.iosProjectExists();
|
|
352
|
+
if (!iosExists) {
|
|
353
|
+
logger.info("iOS project not found, generating from template...");
|
|
354
|
+
const result = await generator.generateiOS();
|
|
355
|
+
if (result.success) {
|
|
356
|
+
logger.success(`Generated iOS project (${result.filesCreated.length} files)`);
|
|
357
|
+
} else {
|
|
358
|
+
logger.warn(`iOS generation had errors: ${result.errors.join(", ")}`);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
logger.debug("iOS project already exists, skipping template generation");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (shouldGenerateAndroid) {
|
|
366
|
+
const androidExists = await generator.androidProjectExists();
|
|
367
|
+
if (!androidExists) {
|
|
368
|
+
logger.info("Android project not found, generating from template...");
|
|
369
|
+
const result = await generator.generateAndroid();
|
|
370
|
+
if (result.success) {
|
|
371
|
+
logger.success(`Generated Android project (${result.filesCreated.length} files)`);
|
|
372
|
+
} else {
|
|
373
|
+
logger.warn(`Android generation had errors: ${result.errors.join(", ")}`);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
logger.debug("Android project already exists, skipping template generation");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Generate package.json if it doesn't exist
|
|
381
|
+
const packageExists = await generator.packageJsonExists();
|
|
382
|
+
if (!packageExists) {
|
|
383
|
+
logger.info("package.json not found, generating from template...");
|
|
384
|
+
const result = await generator.generatePackageJson();
|
|
385
|
+
if (result.success && result.filesCreated.length > 0) {
|
|
386
|
+
logger.success("Generated package.json");
|
|
387
|
+
} else if (result.errors.length > 0) {
|
|
388
|
+
logger.warn(`package.json generation had errors: ${result.errors.join(", ")}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Generate src folder if it doesn't exist
|
|
393
|
+
const srcExists = await generator.srcFolderExists();
|
|
394
|
+
if (!srcExists) {
|
|
395
|
+
logger.info("src/ folder not found, generating from template...");
|
|
396
|
+
const result = await generator.generateSrcFolder();
|
|
397
|
+
if (result.success) {
|
|
398
|
+
logger.success(`Generated src/ folder (${result.filesCreated.length} files)`);
|
|
399
|
+
} else {
|
|
400
|
+
logger.warn(`src/ generation had errors: ${result.errors.join(", ")}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Generate root config files if they don't exist
|
|
405
|
+
const configResult = await generator.generateRootConfigFiles();
|
|
406
|
+
if (configResult.filesCreated.length > 0) {
|
|
407
|
+
logger.success(`Generated config files: ${configResult.filesCreated.map((f) => f.split("/").pop()).join(", ")}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Stage 4: Plugin Pre-Process
|
|
414
|
+
*/
|
|
415
|
+
class PluginPreProcessStage implements PipelineStage {
|
|
416
|
+
name = "plugin-preprocess";
|
|
417
|
+
|
|
418
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
419
|
+
const { logger, resolver, pluginContext, options } = context;
|
|
420
|
+
|
|
421
|
+
logger.debug("Running plugin pre-process hooks...");
|
|
422
|
+
|
|
423
|
+
const plugins = resolver.resolveOrder();
|
|
424
|
+
|
|
425
|
+
for (const plugin of plugins) {
|
|
426
|
+
// Skip plugins that don't support the target platform
|
|
427
|
+
if (options.platform !== "all") {
|
|
428
|
+
if (!plugin.supportsPlatform(options.platform)) {
|
|
429
|
+
logger.debug(`Skipping ${plugin.name} (doesn't support ${options.platform})`);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
logger.debug(`Pre-processing: ${plugin.name}`);
|
|
435
|
+
await plugin.preProcess(pluginContext);
|
|
436
|
+
|
|
437
|
+
// Register capabilities
|
|
438
|
+
for (const capability of plugin.provides) {
|
|
439
|
+
pluginContext.registerCapability(capability);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Stage 4: Plugin Transform
|
|
447
|
+
*/
|
|
448
|
+
class PluginTransformStage implements PipelineStage {
|
|
449
|
+
name = "plugin-transform";
|
|
450
|
+
|
|
451
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
452
|
+
const { logger, resolver, pluginContext, options } = context;
|
|
453
|
+
|
|
454
|
+
logger.debug("Running plugin transformations...");
|
|
455
|
+
|
|
456
|
+
const plugins = resolver.resolveOrder();
|
|
457
|
+
|
|
458
|
+
for (const plugin of plugins) {
|
|
459
|
+
// Skip plugins that don't support the target platform
|
|
460
|
+
if (options.platform !== "all") {
|
|
461
|
+
if (!plugin.supportsPlatform(options.platform)) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const pluginConfig = resolver.getPluginConfig(plugin.name);
|
|
467
|
+
logger.debug(`Transforming: ${plugin.name}`);
|
|
468
|
+
|
|
469
|
+
context.config = plugin.transform(context.config, pluginConfig, pluginContext);
|
|
470
|
+
pluginContext.config = context.config;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Stage 5: Plugin Post-Process
|
|
477
|
+
*/
|
|
478
|
+
class PluginPostProcessStage implements PipelineStage {
|
|
479
|
+
name = "plugin-postprocess";
|
|
480
|
+
|
|
481
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
482
|
+
const { logger, resolver, pluginContext, options } = context;
|
|
483
|
+
|
|
484
|
+
logger.debug("Running plugin post-process hooks...");
|
|
485
|
+
|
|
486
|
+
const plugins = resolver.resolveOrder();
|
|
487
|
+
|
|
488
|
+
for (const plugin of plugins) {
|
|
489
|
+
// Skip plugins that don't support the target platform
|
|
490
|
+
if (options.platform !== "all") {
|
|
491
|
+
if (!plugin.supportsPlatform(options.platform)) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
logger.debug(`Post-processing: ${plugin.name}`);
|
|
497
|
+
await plugin.postProcess(pluginContext);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Stage 6: iOS Generation
|
|
504
|
+
*/
|
|
505
|
+
class iOSGenerationStage implements PipelineStage {
|
|
506
|
+
name = "ios-generation";
|
|
507
|
+
|
|
508
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
509
|
+
const { logger, pluginContext, config } = context;
|
|
510
|
+
|
|
511
|
+
logger.debug("Generating iOS native files...");
|
|
512
|
+
|
|
513
|
+
// Transform Info.plist
|
|
514
|
+
const plistTransformer = new PlistTransformer();
|
|
515
|
+
await plistTransformer.transform(pluginContext, config);
|
|
516
|
+
|
|
517
|
+
// Transform entitlements
|
|
518
|
+
const entitlementsTransformer = new EntitlementsTransformer();
|
|
519
|
+
await entitlementsTransformer.transform(pluginContext, config);
|
|
520
|
+
|
|
521
|
+
// Transform pbxproj (Xcode project)
|
|
522
|
+
const pbxprojTransformer = new PbxprojTransformer();
|
|
523
|
+
await pbxprojTransformer.transform(pluginContext, config);
|
|
524
|
+
|
|
525
|
+
logger.debug("iOS generation complete");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Stage 7: iOS Post-Install
|
|
531
|
+
*
|
|
532
|
+
* Runs `pod install` after iOS project generation to install CocoaPods dependencies
|
|
533
|
+
* and create the Xcode workspace with proper schemes.
|
|
534
|
+
*/
|
|
535
|
+
class IOSPostInstallStage implements PipelineStage {
|
|
536
|
+
name = "ios-postinstall";
|
|
537
|
+
|
|
538
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
539
|
+
const { logger, options } = context;
|
|
540
|
+
|
|
541
|
+
// Skip in dry-run mode
|
|
542
|
+
if (options.dryRun) {
|
|
543
|
+
logger.debug("Skipping pod install (dry-run mode)");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
548
|
+
const iosDir = join(projectRoot, "ios");
|
|
549
|
+
const podfilePath = join(iosDir, "Podfile");
|
|
550
|
+
const podfileLockPath = join(iosDir, "Podfile.lock");
|
|
551
|
+
|
|
552
|
+
// Check if Podfile exists
|
|
553
|
+
if (!existsSync(podfilePath)) {
|
|
554
|
+
logger.debug("No Podfile found, skipping pod install");
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Check if pod command is available
|
|
559
|
+
const podAvailable = await this.isPodAvailable();
|
|
560
|
+
if (!podAvailable) {
|
|
561
|
+
logger.warn("CocoaPods not installed. Run 'pod install' manually in the ios/ directory.");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Use --repo-update for fresh installs (no Podfile.lock)
|
|
566
|
+
const isFreshInstall = !existsSync(podfileLockPath);
|
|
567
|
+
if (isFreshInstall) {
|
|
568
|
+
logger.info("Installing CocoaPods dependencies (first time, updating repos)...");
|
|
569
|
+
} else {
|
|
570
|
+
logger.info("Installing CocoaPods dependencies...");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
try {
|
|
574
|
+
await this.runPodInstall(iosDir, logger, isFreshInstall);
|
|
575
|
+
logger.success("CocoaPods dependencies installed");
|
|
576
|
+
} catch (error) {
|
|
577
|
+
// Surface the error clearly - this is important for debugging
|
|
578
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
579
|
+
logger.error(`pod install failed: ${errorMessage}`);
|
|
580
|
+
logger.error("To fix this, try running manually:");
|
|
581
|
+
logger.error(" cd ios && pod install --repo-update");
|
|
582
|
+
// Don't fail the pipeline, but make it very visible
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Check if CocoaPods is installed
|
|
588
|
+
*/
|
|
589
|
+
private async isPodAvailable(): Promise<boolean> {
|
|
590
|
+
return new Promise((resolve) => {
|
|
591
|
+
const child = spawn("which", ["pod"], {
|
|
592
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
child.on("close", (code) => {
|
|
596
|
+
resolve(code === 0);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
child.on("error", () => {
|
|
600
|
+
resolve(false);
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Run pod install in the iOS directory
|
|
607
|
+
*/
|
|
608
|
+
private async runPodInstall(iosDir: string, logger: Logger, repoUpdate = false): Promise<void> {
|
|
609
|
+
return new Promise((resolve, reject) => {
|
|
610
|
+
// Use --repo-update for fresh installs to ensure specs are up to date
|
|
611
|
+
const args = repoUpdate ? ["install", "--repo-update"] : ["install"];
|
|
612
|
+
|
|
613
|
+
const child = spawn("pod", args, {
|
|
614
|
+
cwd: iosDir,
|
|
615
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
616
|
+
env: {
|
|
617
|
+
...process.env,
|
|
618
|
+
// Required for CocoaPods UTF-8 support
|
|
619
|
+
LANG: "en_US.UTF-8",
|
|
620
|
+
LC_ALL: "en_US.UTF-8",
|
|
621
|
+
},
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
let stdout = "";
|
|
625
|
+
let stderr = "";
|
|
626
|
+
|
|
627
|
+
child.stdout?.on("data", (data) => {
|
|
628
|
+
stdout += data.toString();
|
|
629
|
+
// Log progress for verbose output
|
|
630
|
+
const lines = data.toString().split("\n");
|
|
631
|
+
for (const line of lines) {
|
|
632
|
+
if (line.trim()) {
|
|
633
|
+
logger.debug(`[pod] ${line.trim()}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
child.stderr?.on("data", (data) => {
|
|
639
|
+
stderr += data.toString();
|
|
640
|
+
// Also log stderr for visibility
|
|
641
|
+
const lines = data.toString().split("\n");
|
|
642
|
+
for (const line of lines) {
|
|
643
|
+
if (line.trim()) {
|
|
644
|
+
logger.debug(`[pod:err] ${line.trim()}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
child.on("close", (code) => {
|
|
650
|
+
if (code === 0) {
|
|
651
|
+
resolve();
|
|
652
|
+
} else {
|
|
653
|
+
reject(new Error(`pod install exited with code ${code}: ${stderr || stdout}`));
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
child.on("error", (err) => {
|
|
658
|
+
reject(err);
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Stage 8: Android Generation
|
|
666
|
+
*/
|
|
667
|
+
class AndroidGenerationStage implements PipelineStage {
|
|
668
|
+
name = "android-generation";
|
|
669
|
+
|
|
670
|
+
async execute(context: PipelineContext): Promise<void> {
|
|
671
|
+
const { logger, pluginContext, config } = context;
|
|
672
|
+
|
|
673
|
+
logger.debug("Generating Android native files...");
|
|
674
|
+
|
|
675
|
+
// Transform AndroidManifest.xml
|
|
676
|
+
const manifestTransformer = new AndroidManifestTransformer();
|
|
677
|
+
await manifestTransformer.transform(pluginContext, config);
|
|
678
|
+
|
|
679
|
+
// Transform build.gradle
|
|
680
|
+
const gradleTransformer = new GradleTransformer();
|
|
681
|
+
await gradleTransformer.transform(pluginContext, config);
|
|
682
|
+
|
|
683
|
+
logger.debug("Android generation complete");
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Create a new pipeline instance
|
|
689
|
+
*/
|
|
690
|
+
export function createPipeline(options?: { logger?: Logger }): Pipeline {
|
|
691
|
+
return new Pipeline(options);
|
|
692
|
+
}
|