flet-box 0.0.0-stage → 1.0.2
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/LICENSE +21 -0
- package/README.md +190 -2
- package/bin/cli.js +394 -0
- package/bin/commands/buildAndroid.js +200 -0
- package/bin/commands/buildIOS.js +237 -0
- package/bin/commands/create.js +406 -0
- package/bin/commands/createBundle.js +375 -0
- package/bin/commands/createComponent.js +115 -0
- package/bin/commands/createScreen.js +161 -0
- package/bin/commands/killServer.js +107 -0
- package/bin/commands/package.js +131 -0
- package/bin/commands/release.js +189 -0
- package/bin/commands/runBundle.js +62 -0
- package/bin/commands/runCloudflare.js +89 -0
- package/bin/commands/runNative.js +109 -0
- package/bin/commands/runServer.js +390 -0
- package/bin/ota/loader.js +361 -0
- package/bin/utils/bundleConfig.js +178 -0
- package/bin/utils/capacitor.js +137 -0
- package/bin/utils/colors.js +215 -0
- package/bin/utils/helpers.js +80 -0
- package/bin/utils/spinner.js +96 -0
- package/bin/utils/templates.js +1824 -0
- package/bin/utils/version.js +108 -0
- package/package.json +58 -4
- package/src/animations/AnimatedBox.js +607 -0
- package/src/animations/AnimatedText.js +105 -0
- package/src/animations/MatrixRain.js +220 -0
- package/src/animations/ParallaxBox.js +369 -0
- package/src/animations/animate.js +332 -0
- package/src/animations/index.js +25 -0
- package/src/app.js +41 -0
- package/src/assets/icon-1024-maskable.png +0 -0
- package/src/assets/icon-192-maskable.png +0 -0
- package/src/assets/icon-192.png +0 -0
- package/src/assets/icon-512-maskable.png +0 -0
- package/src/assets/icon-512.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/App.js +44 -0
- package/src/core/hmr-client.js +142 -0
- package/src/core/index.js +34 -0
- package/src/core/pwa.js +100 -0
- package/src/core/runApp.js +430 -0
- package/src/create-index.sh +237 -0
- package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
- package/src/fonts/icons.css +23 -0
- package/src/index.d.ts +1930 -0
- package/src/index.js +288 -0
- package/src/navigations/AdaptiveScaffold.js +149 -0
- package/src/navigations/AppBar.js +331 -0
- package/src/navigations/BottomNavigation.js +298 -0
- package/src/navigations/CollapsibleSideBar.js +139 -0
- package/src/navigations/Drawer.js +333 -0
- package/src/navigations/DrawerItem.js +260 -0
- package/src/navigations/Router.js +475 -0
- package/src/navigations/Scaffold.js +537 -0
- package/src/navigations/Tabs.js +384 -0
- package/src/navigations/index.js +57 -0
- package/src/services/RamStore.js +213 -0
- package/src/services/Session.js +310 -0
- package/src/services/Storage.js +309 -0
- package/src/services/http.js +350 -0
- package/src/services/index.js +57 -0
- package/src/tools/animation.js +42 -0
- package/src/tools/array.js +95 -0
- package/src/tools/border.js +28 -0
- package/src/tools/clipboard.js +104 -0
- package/src/tools/color.js +329 -0
- package/src/tools/createList.js +215 -0
- package/src/tools/delay.js +55 -0
- package/src/tools/device.js +138 -0
- package/src/tools/dict.js +297 -0
- package/src/tools/dimensions.js +118 -0
- package/src/tools/filter.js +41 -0
- package/src/tools/flex.js +26 -0
- package/src/tools/gradient.js +34 -0
- package/src/tools/grid.js +57 -0
- package/src/tools/index.js +38 -0
- package/src/tools/mapList.js +107 -0
- package/src/tools/margin.js +93 -0
- package/src/tools/memo.js +60 -0
- package/src/tools/os.js +104 -0
- package/src/tools/padding.js +93 -0
- package/src/tools/print.js +69 -0
- package/src/tools/random.js +299 -0
- package/src/tools/ref.js +70 -0
- package/src/tools/rgba.js +4 -0
- package/src/tools/shadow.js +23 -0
- package/src/tools/string.js +85 -0
- package/src/tools/time.js +674 -0
- package/src/tools/transform.js +68 -0
- package/src/tools/transition.js +48 -0
- package/src/tools/useState.js +268 -0
- package/src/tools/uuid.js +42 -0
- package/src/utils/TextInputValidator.js +256 -0
- package/src/utils/composeUpdate.js +65 -0
- package/src/utils/getWidgetProps.js +35 -0
- package/src/utils/index.js +76 -0
- package/src/utils/markdownParser.js +368 -0
- package/src/utils/mediaTime.js +79 -0
- package/src/utils/navigation.js +263 -0
- package/src/utils/scriptInjector.js +73 -0
- package/src/utils/stackPosition.js +31 -0
- package/src/utils/stopWebRefresh.js +77 -0
- package/src/utils/styleInjector.js +38 -0
- package/src/utils/syntaxHighlight.js +339 -0
- package/src/utils/themes.js +347 -0
- package/src/utils/units.js +87 -0
- package/src/utils/visualEffects.js +169 -0
- package/src/widget-factory/addChildren.js +114 -0
- package/src/widget-factory/assignProps.js +256 -0
- package/src/widget-factory/createWidget.js +28 -0
- package/src/widget-factory/effects.js +132 -0
- package/src/widget-factory/index.js +7 -0
- package/src/widget-factory/lifecycle.js +210 -0
- package/src/widget-factory/makeParentable.js +89 -0
- package/src/widget-factory/pixTool.js +40 -0
- package/src/widget-factory/processProps.js +122 -0
- package/src/widget-factory/reactivity.js +98 -0
- package/src/widget-factory/remTool.js +34 -0
- package/src/widget-factory/stackPosition.js +87 -0
- package/src/widget-factory/stringTool.js +26 -0
- package/src/widget-factory/tools.js +249 -0
- package/src/widget-factory/translateProps.js +1049 -0
- package/src/widget-factory/warnings.js +284 -0
- package/src/widget-factory/widgetFactory.js +200 -0
- package/src/widgets/Accordion.js +562 -0
- package/src/widgets/AdMob.js +315 -0
- package/src/widgets/AdSense.js +268 -0
- package/src/widgets/AlertDialog.js +162 -0
- package/src/widgets/Audio.js +311 -0
- package/src/widgets/Avatar.js +210 -0
- package/src/widgets/Badge.js +189 -0
- package/src/widgets/BottomSheet.js +287 -0
- package/src/widgets/Button.js +247 -0
- package/src/widgets/Card.js +33 -0
- package/src/widgets/Carousel.js +301 -0
- package/src/widgets/Chart.js +632 -0
- package/src/widgets/Checkbox.js +106 -0
- package/src/widgets/Chip.js +153 -0
- package/src/widgets/CircularBar.js +352 -0
- package/src/widgets/CircularChart.js +549 -0
- package/src/widgets/CodeViewer.js +236 -0
- package/src/widgets/Column.js +29 -0
- package/src/widgets/Container.js +31 -0
- package/src/widgets/DataTable.js +257 -0
- package/src/widgets/Divider.js +93 -0
- package/src/widgets/DraggBox.js +115 -0
- package/src/widgets/Dropdown.js +505 -0
- package/src/widgets/DroppBox.js +180 -0
- package/src/widgets/FloatingActionButton.js +91 -0
- package/src/widgets/GridView.js +20 -0
- package/src/widgets/Icon.js +33 -0
- package/src/widgets/Image.js +15 -0
- package/src/widgets/Input.js +565 -0
- package/src/widgets/Inspector.js +254 -0
- package/src/widgets/InstallButton.js +110 -0
- package/src/widgets/ListTile.js +224 -0
- package/src/widgets/ListView.js +503 -0
- package/src/widgets/Markdown.js +1236 -0
- package/src/widgets/Modal.js +496 -0
- package/src/widgets/Pagination.js +195 -0
- package/src/widgets/ProgressBar.js +178 -0
- package/src/widgets/QRCode.js +467 -0
- package/src/widgets/Radio.js +132 -0
- package/src/widgets/Rating.js +261 -0
- package/src/widgets/Row.js +29 -0
- package/src/widgets/Skeleton.js +243 -0
- package/src/widgets/Slider.js +527 -0
- package/src/widgets/SnackBar.js +227 -0
- package/src/widgets/Stack.js +29 -0
- package/src/widgets/Stepper.js +296 -0
- package/src/widgets/Switch.js +102 -0
- package/src/widgets/Text.js +98 -0
- package/src/widgets/Tooltip.js +267 -0
- package/src/widgets/TreeView.js +370 -0
- package/src/widgets/Video.js +92 -0
- package/src/widgets/index.js +52 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/commands/buildAndroid.js
|
|
3
|
+
* @description Synchronizes a FletBox project with Capacitor and builds a debug
|
|
4
|
+
* APK, an unsigned release APK, or an unsigned release AAB.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { c, banner } from "../utils/colors.js";
|
|
11
|
+
import {
|
|
12
|
+
assertCapacitorProject,
|
|
13
|
+
commandExists,
|
|
14
|
+
ensureNpmDependencies,
|
|
15
|
+
run,
|
|
16
|
+
} from "../utils/capacitor.js";
|
|
17
|
+
|
|
18
|
+
const requiredPackages = [
|
|
19
|
+
"@capacitor/android",
|
|
20
|
+
"@capacitor/cli",
|
|
21
|
+
"@capacitor/core",
|
|
22
|
+
"flet-box",
|
|
23
|
+
"sharp",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const requiredScripts = ["android:init", "android:sync"];
|
|
27
|
+
|
|
28
|
+
/** Well-known Android SDK locations per platform, most specific first. */
|
|
29
|
+
const androidSdkCandidates = () => {
|
|
30
|
+
const candidates = [
|
|
31
|
+
process.env.ANDROID_SDK_ROOT,
|
|
32
|
+
process.env.ANDROID_HOME,
|
|
33
|
+
].filter(Boolean);
|
|
34
|
+
|
|
35
|
+
if (process.platform === "darwin") {
|
|
36
|
+
candidates.push(path.join(os.homedir(), "Library", "Android", "sdk"));
|
|
37
|
+
} else if (process.platform === "win32") {
|
|
38
|
+
candidates.push(
|
|
39
|
+
path.join(process.env.LOCALAPPDATA || "", "Android", "Sdk"),
|
|
40
|
+
);
|
|
41
|
+
} else {
|
|
42
|
+
candidates.push(path.join(os.homedir(), "Android", "Sdk"));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return candidates;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const prepareAndroidEnvironment = () => {
|
|
49
|
+
const sdkPath = androidSdkCandidates().find(
|
|
50
|
+
(candidate) => candidate && fs.existsSync(candidate),
|
|
51
|
+
);
|
|
52
|
+
if (!sdkPath) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Android SDK was not found. Install Android Studio and its Android SDK, or set ANDROID_HOME/ANDROID_SDK_ROOT.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!commandExists("java", ["-version"])) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"Java was not found. Install a JDK supported by your Android Gradle project and ensure `java` is on PATH.",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
...process.env,
|
|
66
|
+
ANDROID_HOME: sdkPath,
|
|
67
|
+
ANDROID_SDK_ROOT: sdkPath,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Entry point for `flet-box build android`.
|
|
73
|
+
*
|
|
74
|
+
* @param {string[]} [rawArgs=[]] - Raw CLI flags: `--debug` (default) or
|
|
75
|
+
* `--release` (unsigned release APK/AAB, ready for your own signing).
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
export const buildAndroid = (rawArgs = []) => {
|
|
79
|
+
const supportedFlags = ["--debug", "--release", "--aab"];
|
|
80
|
+
const unknownFlags = rawArgs.filter((arg) => !supportedFlags.includes(arg));
|
|
81
|
+
if (unknownFlags.length > 0) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Unknown option for \`build android\`: ${unknownFlags.join(", ")}. Supported options: ${supportedFlags.join(", ")}.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const release = rawArgs.includes("--release");
|
|
88
|
+
const aab = rawArgs.includes("--aab");
|
|
89
|
+
if (aab && !release) {
|
|
90
|
+
throw new Error("`--aab` requires `--release`. Example: `flet-box build android --release --aab`.");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const mode = aab ? "release AAB" : release ? "unsigned release APK" : "debug APK";
|
|
94
|
+
const gradleTask = aab ? "bundleRelease" : release ? "assembleRelease" : "assembleDebug";
|
|
95
|
+
|
|
96
|
+
const { projectRoot } = assertCapacitorProject(requiredScripts);
|
|
97
|
+
|
|
98
|
+
console.log(banner("🤖 Flet-Box Android", mode));
|
|
99
|
+
ensureNpmDependencies(projectRoot, requiredPackages);
|
|
100
|
+
const androidEnv = prepareAndroidEnvironment();
|
|
101
|
+
|
|
102
|
+
const androidDir = path.join(projectRoot, "android");
|
|
103
|
+
if (!fs.existsSync(androidDir)) {
|
|
104
|
+
console.log(c("blue", "\n📱 Initializing the Android platform..."));
|
|
105
|
+
run(
|
|
106
|
+
"npm",
|
|
107
|
+
["run", "android:init"],
|
|
108
|
+
projectRoot,
|
|
109
|
+
"Android initialization",
|
|
110
|
+
androidEnv,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(c("blue", "\n🔄 Building and synchronizing the app..."));
|
|
115
|
+
run(
|
|
116
|
+
"npm",
|
|
117
|
+
["run", "android:sync"],
|
|
118
|
+
projectRoot,
|
|
119
|
+
"Android synchronization",
|
|
120
|
+
androidEnv,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const gradleWrapper =
|
|
124
|
+
process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
125
|
+
const gradlePath = path.join(androidDir, gradleWrapper);
|
|
126
|
+
if (!fs.existsSync(gradlePath)) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Gradle wrapper not found at ${gradlePath}. Check that Capacitor Android initialization completed.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(c("blue", `\n🏗️ Compiling the ${mode}...`));
|
|
133
|
+
run(
|
|
134
|
+
process.platform === "win32" ? gradleWrapper : `./${gradleWrapper}`,
|
|
135
|
+
[gradleTask],
|
|
136
|
+
androidDir,
|
|
137
|
+
"Gradle Android build",
|
|
138
|
+
androidEnv,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const outputsDir = path.join(androidDir, "app", "build", "outputs");
|
|
142
|
+
const artifact = findArtifact(outputsDir, aab, release);
|
|
143
|
+
if (!artifact) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Gradle completed, but the ${mode} was not found under ${outputsDir}.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!release) {
|
|
150
|
+
console.log(
|
|
151
|
+
c(
|
|
152
|
+
"green",
|
|
153
|
+
`\n✅ Android APK created successfully.\n📍 APK path: adb install "${path.resolve(artifact)}"`,
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const signHint = aab
|
|
160
|
+
? `Sign it for Play with your upload key: jarsigner -keystore <tu.keystore> <alias> "${path.resolve(artifact)}"\n Then upload it to the Play Console (Play App Signing applies the final key).`
|
|
161
|
+
: `Sign it yourself (already aligned by Gradle, skip zipalign):\n apksigner sign --ks <tu.keystore> --ks-key-alias <alias> --out app-firmado.apk "${path.resolve(artifact)}"\n Then verify: apksigner verify --verbose --print-certs app-firmado.apk`;
|
|
162
|
+
|
|
163
|
+
console.log(
|
|
164
|
+
c(
|
|
165
|
+
"green",
|
|
166
|
+
`\n✅ Android ${mode} created successfully (unsigned by design).\n📍 Path: ${path.resolve(artifact)}\nℹ️ ${signHint}`,
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Locates the Gradle output artifact for the selected build mode.
|
|
173
|
+
*
|
|
174
|
+
* Debug APKs land in `apk/debug/app-debug.apk`. Unsigned release APKs land in
|
|
175
|
+
* `apk/release/` and may be named `app-release.apk` or `app-release-unsigned.apk`
|
|
176
|
+
* depending on the Android Gradle Plugin version. AABs land in
|
|
177
|
+
* `bundle/release/app-release.aab`.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} outputsDir - The Gradle `app/build/outputs` directory.
|
|
180
|
+
* @param {boolean} aab - Whether the target is a bundle instead of an APK.
|
|
181
|
+
* @param {boolean} release - Whether the build is a release build.
|
|
182
|
+
* @returns {string | null} Absolute path to the artifact, or null.
|
|
183
|
+
*/
|
|
184
|
+
const findArtifact = (outputsDir, aab, release) => {
|
|
185
|
+
const candidates = [];
|
|
186
|
+
if (aab) {
|
|
187
|
+
candidates.push(path.join(outputsDir, "bundle", "release", "app-release.aab"));
|
|
188
|
+
} else if (release) {
|
|
189
|
+
const releaseDir = path.join(outputsDir, "apk", "release");
|
|
190
|
+
if (fs.existsSync(releaseDir)) {
|
|
191
|
+
const apks = fs.readdirSync(releaseDir).filter((file) => file.endsWith(".apk"));
|
|
192
|
+
candidates.push(path.join(releaseDir, "app-release.apk"));
|
|
193
|
+
candidates.push(path.join(releaseDir, "app-release-unsigned.apk"));
|
|
194
|
+
for (const apk of apks) candidates.push(path.join(releaseDir, apk));
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
candidates.push(path.join(outputsDir, "apk", "debug", "app-debug.apk"));
|
|
198
|
+
}
|
|
199
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
200
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/commands/buildIOS.js
|
|
3
|
+
* @description Synchronizes a FletBox project with Capacitor and builds a native
|
|
4
|
+
* iOS app.
|
|
5
|
+
*
|
|
6
|
+
* Two destinations are supported:
|
|
7
|
+
* - default → `App.app` for the iOS Simulator, no signing required.
|
|
8
|
+
* - `--archive` → `App.xcarchive` for real devices, unsigned. The IPA is
|
|
9
|
+
* exported from that archive in Xcode with an Apple team.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { c, banner } from "../utils/colors.js";
|
|
16
|
+
import {
|
|
17
|
+
assertCapacitorProject,
|
|
18
|
+
commandExists,
|
|
19
|
+
ensureNpmDependencies,
|
|
20
|
+
run,
|
|
21
|
+
} from "../utils/capacitor.js";
|
|
22
|
+
|
|
23
|
+
const requiredPackages = [
|
|
24
|
+
"@capacitor/cli",
|
|
25
|
+
"@capacitor/core",
|
|
26
|
+
"@capacitor/ios",
|
|
27
|
+
"flet-box",
|
|
28
|
+
"sharp",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const requiredScripts = ["ios:init", "ios:sync"];
|
|
32
|
+
|
|
33
|
+
/** Path of the active Xcode developer directory, empty when unset. */
|
|
34
|
+
const developerDir = () => {
|
|
35
|
+
const result = spawnSync("xcode-select", ["-p"], {
|
|
36
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
37
|
+
});
|
|
38
|
+
return result.stdout ? result.stdout.toString().trim() : "";
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates the Apple toolchain needed to compile an iOS app.
|
|
43
|
+
*
|
|
44
|
+
* The Xcode Command Line Tools cannot do this job: they install a `xcodebuild`
|
|
45
|
+
* stub and no simulator runtime, so the failure would only surface later as an
|
|
46
|
+
* opaque Xcode error. Detecting that selection up front lets the command point
|
|
47
|
+
* at the actual fix.
|
|
48
|
+
*
|
|
49
|
+
* @param {{ simulator: boolean }} options - `simulator` requests a simulator
|
|
50
|
+
* destination, which additionally needs `simctl`.
|
|
51
|
+
* @returns {void}
|
|
52
|
+
* @throws {Error} With an actionable message when a requirement is missing.
|
|
53
|
+
*/
|
|
54
|
+
export const requireAppleToolchain = ({ simulator }) => {
|
|
55
|
+
if (process.platform !== "darwin") {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"Building iOS requires macOS with Xcode. Run `flet-box build ios` on a Mac.",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const dir = developerDir();
|
|
62
|
+
if (!dir) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"No active Xcode developer directory. Install Xcode from the App Store, then run `xcode-select --install`.",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (dir.includes("CommandLineTools")) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`The active developer directory is the Command Line Tools (${dir}), which cannot build iOS apps. Install the full Xcode app, then run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (!commandExists("xcodebuild", ["-version"])) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"xcodebuild is not available. Open Xcode once so it finishes installing its components.",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (!commandExists("pod", ["--version"])) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"CocoaPods was not found. Capacitor links its native layer with CocoaPods, so install it with `sudo gem install cocoapods` (or `brew install cocoapods`) and run `pod setup` once.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (simulator && !commandExists("xcrun", ["--find", "simctl"])) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"Simulator tools (simctl) were not found. Install an iOS simulator runtime from Xcode → Settings → Platforms, or build for a device with `flet-box build ios --archive`.",
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Runs `xcodebuild` on the generated Capacitor project.
|
|
91
|
+
*
|
|
92
|
+
* @param {object} options - Build options.
|
|
93
|
+
* @param {string} options.iosDir - Absolute path of the `ios` directory.
|
|
94
|
+
* @param {string} options.sdk - Xcode SDK to build against.
|
|
95
|
+
* @param {string} options.destination - `xcodebuild -destination` value.
|
|
96
|
+
* @param {string} options.configuration - `Debug` or `Release`.
|
|
97
|
+
* @param {"build" | "archive"} options.action - Xcode action.
|
|
98
|
+
* @param {string} [options.archivePath] - Required when the action is `archive`.
|
|
99
|
+
* @returns {void}
|
|
100
|
+
*/
|
|
101
|
+
const xcodebuildRun = ({
|
|
102
|
+
iosDir,
|
|
103
|
+
sdk,
|
|
104
|
+
destination,
|
|
105
|
+
configuration,
|
|
106
|
+
action,
|
|
107
|
+
archivePath,
|
|
108
|
+
}) => {
|
|
109
|
+
const args = [
|
|
110
|
+
"-project",
|
|
111
|
+
"App/App.xcodeproj",
|
|
112
|
+
"-scheme",
|
|
113
|
+
"App",
|
|
114
|
+
"-configuration",
|
|
115
|
+
configuration,
|
|
116
|
+
"-sdk",
|
|
117
|
+
sdk,
|
|
118
|
+
"-destination",
|
|
119
|
+
destination,
|
|
120
|
+
"-derivedDataPath",
|
|
121
|
+
"build",
|
|
122
|
+
// Generated projects ship without a signing team, so signing is disabled.
|
|
123
|
+
// Without this, the build fails while looking for an identity instead of
|
|
124
|
+
// compiling anything.
|
|
125
|
+
"CODE_SIGNING_ALLOWED=NO",
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
if (action === "archive") {
|
|
129
|
+
args.push("-archivePath", archivePath, "archive");
|
|
130
|
+
} else {
|
|
131
|
+
args.push("build");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
run("xcodebuild", args, iosDir, `Xcode ${action} (${sdk})`);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Entry point for `flet-box build ios`.
|
|
139
|
+
*
|
|
140
|
+
* @param {string[]} [rawArgs=[]] - Raw CLI flags: `--simulator` (default) or `--archive`.
|
|
141
|
+
* @returns {void}
|
|
142
|
+
*/
|
|
143
|
+
export const buildIOS = (rawArgs = []) => {
|
|
144
|
+
const supportedFlags = ["--simulator", "--archive"];
|
|
145
|
+
const unknownFlags = rawArgs.filter((arg) => !supportedFlags.includes(arg));
|
|
146
|
+
if (unknownFlags.length > 0) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Unknown option for \`build ios\`: ${unknownFlags.join(", ")}. Supported options: ${supportedFlags.join(", ")}.`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const archive = rawArgs.includes("--archive");
|
|
152
|
+
|
|
153
|
+
const { projectRoot } = assertCapacitorProject(requiredScripts);
|
|
154
|
+
|
|
155
|
+
console.log(
|
|
156
|
+
banner(
|
|
157
|
+
"🍎 Flet-Box iOS",
|
|
158
|
+
archive ? "device archive" : "iOS Simulator app",
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
requireAppleToolchain({ simulator: !archive });
|
|
162
|
+
ensureNpmDependencies(projectRoot, requiredPackages);
|
|
163
|
+
|
|
164
|
+
const iosDir = path.join(projectRoot, "ios");
|
|
165
|
+
if (!fs.existsSync(iosDir)) {
|
|
166
|
+
console.log(c("blue", "\n📱 Initializing the iOS platform..."));
|
|
167
|
+
run("npm", ["run", "ios:init"], projectRoot, "iOS initialization");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(c("blue", "\n🔄 Building and synchronizing the app..."));
|
|
171
|
+
run("npm", ["run", "ios:sync"], projectRoot, "iOS synchronization");
|
|
172
|
+
|
|
173
|
+
const xcodeProject = path.join(iosDir, "App", "App.xcodeproj");
|
|
174
|
+
if (!fs.existsSync(xcodeProject)) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Xcode project not found at ${xcodeProject}. Check that Capacitor iOS initialization completed.`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const derivedDataPath = path.join(iosDir, "build");
|
|
181
|
+
|
|
182
|
+
if (archive) {
|
|
183
|
+
const archivePath = path.join(derivedDataPath, "App.xcarchive");
|
|
184
|
+
console.log(c("blue", "\n🏗️ Archiving for real devices..."));
|
|
185
|
+
xcodebuildRun({
|
|
186
|
+
iosDir,
|
|
187
|
+
sdk: "iphoneos",
|
|
188
|
+
destination: "generic/platform=iOS",
|
|
189
|
+
configuration: "Release",
|
|
190
|
+
action: "archive",
|
|
191
|
+
archivePath,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (!fs.existsSync(archivePath)) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Xcode completed, but no archive was found at ${archivePath}.`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log(
|
|
201
|
+
c(
|
|
202
|
+
"green",
|
|
203
|
+
`\n✅ iOS archive created successfully.\n📍 Archive path: ${path.resolve(archivePath)}\nℹ️ The archive is unsigned. Export an IPA from it in Xcode (Product → Archive) with your Apple signing team, or with \`xcodebuild -exportArchive\` plus an ExportOptions.plist.`,
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(c("blue", "\n🏗️ Compiling for the iOS Simulator..."));
|
|
210
|
+
xcodebuildRun({
|
|
211
|
+
iosDir,
|
|
212
|
+
sdk: "iphonesimulator",
|
|
213
|
+
destination: "generic/platform=iOS Simulator",
|
|
214
|
+
configuration: "Debug",
|
|
215
|
+
action: "build",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const appPath = path.join(
|
|
219
|
+
derivedDataPath,
|
|
220
|
+
"Build",
|
|
221
|
+
"Products",
|
|
222
|
+
"Debug-iphonesimulator",
|
|
223
|
+
"App.app",
|
|
224
|
+
);
|
|
225
|
+
if (!fs.existsSync(appPath)) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Xcode completed, but the simulator app was not found at ${appPath}.`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(
|
|
232
|
+
c(
|
|
233
|
+
"green",
|
|
234
|
+
`\n✅ iOS Simulator app created successfully.\n📍 App path: ${path.resolve(appPath)}\nℹ️ Install it on a running simulator with: xcrun simctl install booted ${path.resolve(appPath)}\nℹ️ For a signed IPA, run \`flet-box build ios --archive\`.`,
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
};
|