airborne-devkit 0.34.2 → 0.35.0
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/CHANGELOG.md +84 -0
- package/package.json +1 -1
- package/src/index.js +9 -3
- package/src/utils/common.js +8 -0
- package/src/utils/hermes.js +191 -0
- package/src/utils/release.js +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,90 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
|
3
3
|
|
|
4
4
|
- - -
|
|
5
|
+
## airborne_cli-v0.4.0 - 2026-05-11
|
|
6
|
+
#### Features
|
|
7
|
+
- add hermes build capability - (1131f49) - Yash Rajput
|
|
8
|
+
|
|
9
|
+
- - -
|
|
10
|
+
|
|
11
|
+
## v0.34.2 - 2026-05-11
|
|
12
|
+
#### Miscellaneous Chores
|
|
13
|
+
- **(version)** v0.34.2 [skip ci] - (da14a3e) - Airborne Bot
|
|
14
|
+
|
|
15
|
+
- - -
|
|
16
|
+
|
|
17
|
+
## v0.34.1 - 2026-05-11
|
|
18
|
+
#### Miscellaneous Chores
|
|
19
|
+
- **(version)** v0.34.1 [skip ci] - (0bbc8f3) - Airborne Bot
|
|
20
|
+
|
|
21
|
+
- - -
|
|
22
|
+
|
|
23
|
+
## v0.34.0 - 2026-05-11
|
|
24
|
+
#### Miscellaneous Chores
|
|
25
|
+
- **(version)** v0.34.0 [skip ci] - (0c761ce) - Airborne Bot
|
|
26
|
+
|
|
27
|
+
- - -
|
|
28
|
+
|
|
29
|
+
## v0.33.0 - 2026-05-11
|
|
30
|
+
#### Miscellaneous Chores
|
|
31
|
+
- **(version)** v0.33.0 [skip ci] - (41d1b1c) - Airborne Bot
|
|
32
|
+
|
|
33
|
+
- - -
|
|
34
|
+
|
|
35
|
+
## v0.32.0 - 2026-05-11
|
|
36
|
+
#### Miscellaneous Chores
|
|
37
|
+
- **(version)** v0.32.0 [skip ci] - (6fcfb70) - Airborne Bot
|
|
38
|
+
|
|
39
|
+
- - -
|
|
40
|
+
|
|
41
|
+
## v0.31.2 - 2026-05-11
|
|
42
|
+
#### Miscellaneous Chores
|
|
43
|
+
- **(version)** v0.31.2 [skip ci] - (1776935) - Airborne Bot
|
|
44
|
+
|
|
45
|
+
- - -
|
|
46
|
+
|
|
47
|
+
## v0.31.1 - 2026-05-11
|
|
48
|
+
#### Miscellaneous Chores
|
|
49
|
+
- **(version)** v0.31.1 [skip ci] - (fd3e886) - Airborne Bot
|
|
50
|
+
|
|
51
|
+
- - -
|
|
52
|
+
|
|
53
|
+
## v0.31.0 - 2026-05-11
|
|
54
|
+
#### Miscellaneous Chores
|
|
55
|
+
- **(version)** v0.31.0 [skip ci] - (9f47c56) - Airborne Bot
|
|
56
|
+
|
|
57
|
+
- - -
|
|
58
|
+
|
|
59
|
+
## v0.30.1 - 2026-05-11
|
|
60
|
+
#### Miscellaneous Chores
|
|
61
|
+
- **(version)** v0.30.1 [skip ci] - (4a81918) - Airborne Bot
|
|
62
|
+
|
|
63
|
+
- - -
|
|
64
|
+
|
|
65
|
+
## v0.30.0 - 2026-05-11
|
|
66
|
+
#### Miscellaneous Chores
|
|
67
|
+
- **(version)** v0.30.0 [skip ci] - (e345581) - Airborne Bot
|
|
68
|
+
|
|
69
|
+
- - -
|
|
70
|
+
|
|
71
|
+
## v0.29.2 - 2026-05-11
|
|
72
|
+
#### Miscellaneous Chores
|
|
73
|
+
- **(version)** v0.29.2 [skip ci] - (8cd1d49) - Airborne Bot
|
|
74
|
+
|
|
75
|
+
- - -
|
|
76
|
+
|
|
77
|
+
## v0.29.1 - 2026-05-11
|
|
78
|
+
#### Miscellaneous Chores
|
|
79
|
+
- **(version)** v0.29.1 [skip ci] - (e9670ef) - Airborne Bot
|
|
80
|
+
|
|
81
|
+
- - -
|
|
82
|
+
|
|
83
|
+
## v0.29.0 - 2026-05-11
|
|
84
|
+
#### Miscellaneous Chores
|
|
85
|
+
- **(version)** v0.29.0 [skip ci] - (4d13783) - Airborne Bot
|
|
86
|
+
|
|
87
|
+
- - -
|
|
88
|
+
|
|
5
89
|
## airborne_cli-v0.3.0 - 2026-03-26
|
|
6
90
|
#### Features
|
|
7
91
|
- add platform-specific configuration and improve file processing and update smithy-cli-generator submodule - (823cfe5) - Yash Rajput
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -37,7 +37,7 @@ try{
|
|
|
37
37
|
program
|
|
38
38
|
.name("airborne-devkit")
|
|
39
39
|
.description("Command-line interface for Airborne operations")
|
|
40
|
-
.version("0.
|
|
40
|
+
.version("0.35.0");
|
|
41
41
|
|
|
42
42
|
coreCli.commands.forEach((cmd, i) => {
|
|
43
43
|
if (cmd._name !== "PostLogin") {
|
|
@@ -66,7 +66,8 @@ program
|
|
|
66
66
|
-j <js-entry-file> \\
|
|
67
67
|
-a <android-index-file> \\
|
|
68
68
|
-i <ios-index-file> \\
|
|
69
|
-
-e
|
|
69
|
+
-e \\
|
|
70
|
+
--hermes-enabled
|
|
70
71
|
|
|
71
72
|
Parameters:
|
|
72
73
|
[directoryPath] (optional) : Directory where config will be created (defaults to current directory)
|
|
@@ -78,6 +79,7 @@ program
|
|
|
78
79
|
-a, --android-index-file <string> (optional) : Path to the Android bundle output file
|
|
79
80
|
-i, --ios-index-file <string> (optional) : Path to the iOS bundle output file
|
|
80
81
|
-e, --expo (optional) : Indicates if the project is using Expo
|
|
82
|
+
--hermes-enabled (optional) : Enable Hermes engine for the project
|
|
81
83
|
|
|
82
84
|
`
|
|
83
85
|
)
|
|
@@ -104,6 +106,7 @@ program
|
|
|
104
106
|
)
|
|
105
107
|
.option("-i, --ios-index-file <path>", "Path to the iOS bundle output file")
|
|
106
108
|
.option("-e, --expo", "Indicates if the project is using Expo")
|
|
109
|
+
.option("--hermes-enabled", "Enable Hermes engine for the project")
|
|
107
110
|
.addHelpText(
|
|
108
111
|
"after",
|
|
109
112
|
`
|
|
@@ -135,7 +138,10 @@ Examples:
|
|
|
135
138
|
5. Create config for an Expo project:
|
|
136
139
|
$ airborne-devkit create-local-airborne-config -e
|
|
137
140
|
|
|
138
|
-
6. Create config
|
|
141
|
+
6. Create config with Hermes enabled:
|
|
142
|
+
$ airborne-devkit create-local-airborne-config --hermes-enabled
|
|
143
|
+
|
|
144
|
+
7. Create config for Expo project with all options:
|
|
139
145
|
$ airborne-devkit create-local-airborne-config \\
|
|
140
146
|
--android-organisation "MyCompany" \\
|
|
141
147
|
--ios-organisation "MyCompany" \\
|
package/src/utils/common.js
CHANGED
|
@@ -82,6 +82,7 @@ const cliToConfigMap = {
|
|
|
82
82
|
bootTimeout: "boot_timeout",
|
|
83
83
|
releaseConfigTimeout: "release_config_timeout",
|
|
84
84
|
expo: "expo",
|
|
85
|
+
hermesEnabled: "hermes_enabled",
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
export function normalizeOptions(options = {}) {
|
|
@@ -120,6 +121,7 @@ export async function writeAirborneConfig(options) {
|
|
|
120
121
|
try {
|
|
121
122
|
const filledOptions = await fillAirborneConfigOptions(options);
|
|
122
123
|
const config = {
|
|
124
|
+
hermes_enabled: filledOptions.hermes_enabled,
|
|
123
125
|
expo: filledOptions.expo,
|
|
124
126
|
js_entry_file: filledOptions.js_entry_file,
|
|
125
127
|
android: {
|
|
@@ -173,6 +175,12 @@ export async function fillAirborneConfigOptions(options = {}) {
|
|
|
173
175
|
const isExpo = getNested(result, "expo");
|
|
174
176
|
|
|
175
177
|
const questions = [
|
|
178
|
+
{
|
|
179
|
+
key: "hermes_enabled",
|
|
180
|
+
question: "\n Enable Hermes engine? (y/n, default: no): ",
|
|
181
|
+
expectedType: "boolean",
|
|
182
|
+
defaultValue: false,
|
|
183
|
+
},
|
|
176
184
|
{
|
|
177
185
|
key: "android.organisation",
|
|
178
186
|
question: "\n Please enter the Android organisation name: ",
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import os from "os";
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function isExecutable(p) {
|
|
11
|
+
try {
|
|
12
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getPlatformBinaryNames() {
|
|
20
|
+
switch (process.platform) {
|
|
21
|
+
case "darwin":
|
|
22
|
+
return ["osx-bin/hermesc"];
|
|
23
|
+
case "linux":
|
|
24
|
+
return ["linux64-bin/hermesc", "linux-bin/hermesc"];
|
|
25
|
+
case "win32":
|
|
26
|
+
return ["win64-bin/hermesc.exe"];
|
|
27
|
+
default:
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolvePackageRoot(pkgName, projectRoot) {
|
|
33
|
+
const nodeModulesRoot = path.join(projectRoot, "node_modules");
|
|
34
|
+
try {
|
|
35
|
+
const pkgJsonPath = require.resolve(`${pkgName}/package.json`, {
|
|
36
|
+
paths: [nodeModulesRoot],
|
|
37
|
+
});
|
|
38
|
+
return path.dirname(pkgJsonPath);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildCandidatePaths(packageRoot) {
|
|
45
|
+
const bins = getPlatformBinaryNames();
|
|
46
|
+
const isWin = process.platform === "win32";
|
|
47
|
+
const hermescBin = isWin ? "hermesc.exe" : "hermesc";
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
...bins.map((b) => path.join(packageRoot, "hermesc", b)),
|
|
51
|
+
...bins.map((b) => path.join(packageRoot, b)),
|
|
52
|
+
...bins.map((b) => path.join(packageRoot, "sdks", "hermesc", b)),
|
|
53
|
+
path.join(
|
|
54
|
+
packageRoot,
|
|
55
|
+
"sdks",
|
|
56
|
+
"hermes-engine",
|
|
57
|
+
"destroot",
|
|
58
|
+
"bin",
|
|
59
|
+
hermescBin
|
|
60
|
+
),
|
|
61
|
+
].filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
const PRUNE_DIRS = new Set([
|
|
66
|
+
".git", "build", "dist", ".gradle",
|
|
67
|
+
".cache", ".yarn", "__tests__", "test", "docs", "example", "examples",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
function findFirstExecutableInTree(startDir, fileName, maxDepth = 6) {
|
|
72
|
+
function walk(dir, depth) {
|
|
73
|
+
if (depth > maxDepth) return null;
|
|
74
|
+
|
|
75
|
+
let entries;
|
|
76
|
+
try {
|
|
77
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (entry.isDirectory()) {
|
|
84
|
+
if (PRUNE_DIRS.has(entry.name)) continue;
|
|
85
|
+
const found = walk(path.join(dir, entry.name), depth + 1);
|
|
86
|
+
if (found) return found;
|
|
87
|
+
} else if (entry.isFile() && entry.name === fileName) {
|
|
88
|
+
const fullPath = path.join(dir, entry.name);
|
|
89
|
+
if (isExecutable(fullPath)) return fullPath;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return walk(startDir, 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const KNOWN_PACKAGES = ["hermes-compiler", "hermes-engine", "react-native"];
|
|
101
|
+
|
|
102
|
+
export function findHermesCompiler(projectRoot) {
|
|
103
|
+
const nodeModulesDir = path.join(projectRoot, "node_modules");
|
|
104
|
+
|
|
105
|
+
for (const pkg of KNOWN_PACKAGES) {
|
|
106
|
+
const packageRoot = resolvePackageRoot(pkg, projectRoot);
|
|
107
|
+
|
|
108
|
+
if (!packageRoot) continue;
|
|
109
|
+
|
|
110
|
+
if (!packageRoot.startsWith(nodeModulesDir + path.sep)) continue;
|
|
111
|
+
|
|
112
|
+
const candidates = buildCandidatePaths(packageRoot);
|
|
113
|
+
|
|
114
|
+
for (const candidate of candidates) {
|
|
115
|
+
if (isExecutable(candidate)) {
|
|
116
|
+
return candidate;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const hermescBin =
|
|
122
|
+
process.platform === "win32" ? "hermesc.exe" : "hermesc";
|
|
123
|
+
|
|
124
|
+
return findFirstExecutableInTree(nodeModulesDir, hermescBin);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
export async function compileHermesBundle(projectRoot, bundlePath) {
|
|
129
|
+
const hermescPath = findHermesCompiler(projectRoot);
|
|
130
|
+
|
|
131
|
+
if (!hermescPath) {
|
|
132
|
+
console.error("[Hermes] Error: Hermes compiler not found.");
|
|
133
|
+
console.error("[Hermes] Make sure hermes-compiler, hermes-engine, or react-native is installed.");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const tempFile = path.join(os.tmpdir(), `${Date.now()}-${path.basename(bundlePath)}.hbc`);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await runHermesCompiler(hermescPath, bundlePath, tempFile);
|
|
141
|
+
fs.renameSync(tempFile, bundlePath);
|
|
142
|
+
console.log(`[Hermes] Compiled ${bundlePath}`);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error(`[Hermes] Compilation failed: ${error.message}`);
|
|
145
|
+
if (fs.existsSync(tempFile)) {
|
|
146
|
+
fs.unlinkSync(tempFile);
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function runHermesCompiler(hermescPath, inputPath, outputPath) {
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
const args = ["-emit-binary", "-out", outputPath, inputPath];
|
|
155
|
+
|
|
156
|
+
const child = spawn(hermescPath, args, {
|
|
157
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let stderr = "";
|
|
161
|
+
let stdout = "";
|
|
162
|
+
let isSettled = false;
|
|
163
|
+
|
|
164
|
+
const settle = (fn, arg) => {
|
|
165
|
+
if (isSettled) return;
|
|
166
|
+
isSettled = true;
|
|
167
|
+
fn(arg);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
child.stdout.on("data", (data) => {
|
|
171
|
+
stdout += data.toString();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
child.stderr.on("data", (data) => {
|
|
175
|
+
stderr += data.toString();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
child.on("close", (code) => {
|
|
179
|
+
if (code !== 0) {
|
|
180
|
+
settle(reject, new Error(`hermesc exited with code ${code}: ${stderr || stdout || "(no output)"}`));
|
|
181
|
+
} else {
|
|
182
|
+
settle(resolve, undefined);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
child.on("error", (err) => {
|
|
187
|
+
settle(reject, new Error(`Failed to spawn hermesc: ${err.message}`));
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
package/src/utils/release.js
CHANGED
|
@@ -8,6 +8,7 @@ import fs from "fs";
|
|
|
8
8
|
import path from "path";
|
|
9
9
|
import { promptWithType } from "./prompt.js";
|
|
10
10
|
import { execSync } from "child_process";
|
|
11
|
+
import { compileHermesBundle } from "./hermes.js";
|
|
11
12
|
|
|
12
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
14
|
const __dirname = dirname(__filename);
|
|
@@ -124,6 +125,10 @@ export async function createLocalReleaseConfig(
|
|
|
124
125
|
`React Native bundle command failed: ${bundleResult.error}`
|
|
125
126
|
);
|
|
126
127
|
}
|
|
128
|
+
|
|
129
|
+
if (airborneConfig.hermes_enabled) {
|
|
130
|
+
await compileHermesBundle(options.directory_path, `${build_folder}/${index_file_path}`);
|
|
131
|
+
}
|
|
127
132
|
|
|
128
133
|
const baseDir = path.isAbsolute(options.directory_path)
|
|
129
134
|
? options.directory_path
|
|
@@ -268,6 +273,10 @@ export async function updateLocalReleaseConfig(
|
|
|
268
273
|
`React Native bundle command failed: ${bundleResult.error}`
|
|
269
274
|
);
|
|
270
275
|
}
|
|
276
|
+
|
|
277
|
+
if (airborneConfig.hermes_enabled) {
|
|
278
|
+
await compileHermesBundle(options.directory_path, `${build_folder}/${index_file_path}`);
|
|
279
|
+
}
|
|
271
280
|
|
|
272
281
|
const baseDir = path.isAbsolute(options.directory_path)
|
|
273
282
|
? options.directory_path
|