@lakutata/cli 3.1.4 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -8
- package/README.zh-CN.md +80 -6
- package/dist/esm/Config.js +16 -5
- package/dist/esm/controllers/CommandLineController.js +11 -0
- package/dist/esm/controllers/MigrationController.js +2 -2
- package/dist/esm/controllers/MobileController.js +677 -0
- package/dist/esm/controllers/NwjsController.js +128 -0
- package/dist/esm/controllers/PackController.js +17 -8
- package/dist/esm/dtos/BundleDTO.js +4 -0
- package/dist/esm/dtos/CreateProjectDTO.js +4 -0
- package/dist/esm/dtos/ExecutableDTO.js +4 -0
- package/dist/esm/dtos/InstallerDTO.js +9 -1
- package/dist/esm/dtos/MobileDeployDTO.js +25 -0
- package/dist/esm/dtos/MobileNodeDTO.js +21 -0
- package/dist/esm/dtos/MobileRunDTO.js +21 -0
- package/dist/esm/dtos/MobileStageDTO.js +25 -0
- package/dist/esm/dtos/NwjsRunDTO.js +25 -0
- package/dist/esm/dtos/NwjsUseDTO.js +21 -0
- package/dist/esm/dtos/NwjsVersionsDTO.js +17 -0
- package/dist/esm/lib/AddPackage.js +4 -3
- package/dist/esm/lib/Output.js +11 -2
- package/dist/esm/lib/ProjectCreator.js +18 -0
- package/dist/esm/lib/TemplateCatalog.js +4 -1
- package/dist/esm/lib/workspace/Run.js +2 -1
- package/dist/esm/locales/zh-CN.json +79 -4
- package/dist/esm/locales/zh-TW.json +79 -4
- package/package.json +9 -9
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { existsSync, readdirSync, readFileSync, statSync, watch } from 'node:fs';
|
|
12
|
+
import { cp, mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';
|
|
13
|
+
import os from 'node:os';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { Controller } from 'lakutata/com/entrypoint';
|
|
16
|
+
import { CLIAction } from 'lakutata/decorator/ctrl';
|
|
17
|
+
import { MobileDeployDTO } from '../dtos/MobileDeployDTO.js';
|
|
18
|
+
import { MobileNodeDTO } from '../dtos/MobileNodeDTO.js';
|
|
19
|
+
import { MobileRunDTO } from '../dtos/MobileRunDTO.js';
|
|
20
|
+
import { MobileStageDTO } from '../dtos/MobileStageDTO.js';
|
|
21
|
+
import { loadBuilder } from '../lib/Builder.js';
|
|
22
|
+
import { done, step, warn } from '../lib/Output.js';
|
|
23
|
+
import { runCommand, spawnCommand } from '../lib/Processes.js';
|
|
24
|
+
/**
|
|
25
|
+
* Where the native module of Android runs the Node.js project (copied there from the assets), and the marker of a copy
|
|
26
|
+
* written while developing, which the native module keeps instead of copying the assets again
|
|
27
|
+
*/
|
|
28
|
+
const ANDROID_PROJECT = 'files/nodejs';
|
|
29
|
+
const ANDROID_DEVELOPMENT = 'files/nodejs.dev';
|
|
30
|
+
/**
|
|
31
|
+
* The port of Metro, which the debug builds of the user interface load it from
|
|
32
|
+
*/
|
|
33
|
+
const METRO_PORT = 8081;
|
|
34
|
+
/**
|
|
35
|
+
* Run a command, its output given back; its input written to it (a stream of tar)
|
|
36
|
+
* @param command
|
|
37
|
+
* @param args
|
|
38
|
+
* @param input
|
|
39
|
+
*/
|
|
40
|
+
function run(command, args, input) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const child = spawn(command, args, { stdio: [input ? 'pipe' : 'ignore', 'pipe', 'pipe'] });
|
|
43
|
+
let stdout = '';
|
|
44
|
+
let stderr = '';
|
|
45
|
+
child.stdout.on('data', (chunk) => stdout += chunk.toString());
|
|
46
|
+
child.stderr.on('data', (chunk) => stderr += chunk.toString());
|
|
47
|
+
child.once('error', reject);
|
|
48
|
+
child.once('close', (code) => resolve({ code: code ?? 1, stdout: stdout, stderr: stderr }));
|
|
49
|
+
if (input)
|
|
50
|
+
input.pipe(child.stdin);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run a command, which must succeed
|
|
55
|
+
* @param what
|
|
56
|
+
* @param command
|
|
57
|
+
* @param args
|
|
58
|
+
* @param input
|
|
59
|
+
*/
|
|
60
|
+
async function must(what, command, args, input) {
|
|
61
|
+
const result = await run(command, args, input);
|
|
62
|
+
if (result.code !== 0)
|
|
63
|
+
throw new Error(`${what}: ${(result.stderr || result.stdout).trim() || `${command} exited with ${result.code}`}`);
|
|
64
|
+
return result.stdout;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Run a command whose output is shown as it runs (the builds of the native projects), which must succeed
|
|
68
|
+
* @param command
|
|
69
|
+
* @param args
|
|
70
|
+
* @param cwd
|
|
71
|
+
* @param env
|
|
72
|
+
*/
|
|
73
|
+
async function shown(command, args, cwd, env) {
|
|
74
|
+
step(`${path.basename(command)} ${args.join(' ')}`);
|
|
75
|
+
const code = await new Promise((resolve, reject) => {
|
|
76
|
+
const child = spawnCommand(command, args, { cwd: cwd, env: env, stdio: 'inherit' });
|
|
77
|
+
child.once('error', reject);
|
|
78
|
+
child.once('exit', resolve);
|
|
79
|
+
});
|
|
80
|
+
if (code !== 0)
|
|
81
|
+
throw new Error(`${path.basename(command)} ${args.join(' ')} failed (${code})`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The project of a mobile application holding a directory: the first one up holding packages/ipc/native/NodeHost.cpp
|
|
85
|
+
* and packages/app
|
|
86
|
+
* @param directory
|
|
87
|
+
*/
|
|
88
|
+
function mobileProject(directory) {
|
|
89
|
+
for (let current = directory;; current = path.dirname(current)) {
|
|
90
|
+
const packages = path.join(current, 'packages');
|
|
91
|
+
if (existsSync(path.join(packages, 'ipc', 'native', 'NodeHost.cpp')) && existsSync(path.join(packages, 'app', 'package.json'))) {
|
|
92
|
+
return { root: current, app: path.join(packages, 'app'), native: path.join(packages, 'ipc', 'native') };
|
|
93
|
+
}
|
|
94
|
+
if (current === path.dirname(current))
|
|
95
|
+
throw new Error(`${directory} is in no project of a mobile application (packages/app and packages/ipc/native/NodeHost.cpp, as the mobile-react template lays them out)`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The libnode of a system put into the project (packages/ipc/native/node/out/<platform>)
|
|
100
|
+
* @param project
|
|
101
|
+
* @param platform
|
|
102
|
+
*/
|
|
103
|
+
function nodeOf(project, platform) {
|
|
104
|
+
return path.join(project.native, 'node', 'out', platform);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Download the libnode of a system from the releases of lakutata/libnode (once: kept in the cache of lakutata) and put
|
|
108
|
+
* it into the project, where its native project takes it
|
|
109
|
+
* @param project
|
|
110
|
+
* @param platform
|
|
111
|
+
* @param abis the architectures of Android
|
|
112
|
+
* @returns where it is put
|
|
113
|
+
*/
|
|
114
|
+
async function putNode(project, platform, abis) {
|
|
115
|
+
const { downloadMobileNode } = await loadBuilder(project.app);
|
|
116
|
+
const downloaded = await downloadMobileNode({ platform: platform, abis: abis, progress: (line) => step(`Downloading ${line}`) });
|
|
117
|
+
const out = nodeOf(project, platform);
|
|
118
|
+
await rm(out, { recursive: true, force: true });
|
|
119
|
+
await mkdir(path.dirname(out), { recursive: true });
|
|
120
|
+
//The files themselves, not the links of an export given (LAKUTATA_MOBILE_NODE_DIR): CocoaPods does not follow them
|
|
121
|
+
await cp(downloaded, out, { recursive: true, dereference: true, filter: (file) => path.basename(file) !== '.complete' });
|
|
122
|
+
//The libraries of the HAP hold libnode.so, which the addons and the runtime of the native module load by its name
|
|
123
|
+
if (platform === 'harmony') {
|
|
124
|
+
await mkdir(harmonyLibraries(project), { recursive: true });
|
|
125
|
+
await cp(path.join(out, 'libs', 'arm64-v8a', 'libnode.so'), path.join(harmonyLibraries(project), 'libnode.so'));
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Where the native project of a system takes the Node.js project of the application
|
|
131
|
+
* @param project
|
|
132
|
+
* @param platform
|
|
133
|
+
*/
|
|
134
|
+
function stageOf(project, platform) {
|
|
135
|
+
if (platform === 'android')
|
|
136
|
+
return path.join(project.root, 'android', 'app', 'src', 'main', 'assets', 'nodejs');
|
|
137
|
+
if (platform === 'ios')
|
|
138
|
+
return path.join(project.root, 'ios', 'nodejs');
|
|
139
|
+
return path.join(project.root, 'harmony', 'entry', 'src', 'main', 'resources', 'resfile', 'nodejs');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The libraries of the HAP of HarmonyOS (the libs/arm64-v8a of its module): libnode.so and the native addons
|
|
143
|
+
* @param project
|
|
144
|
+
*/
|
|
145
|
+
function harmonyLibraries(project) {
|
|
146
|
+
return path.join(project.root, 'harmony', 'entry', 'libs', 'arm64-v8a');
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The id of the application on a system, as its native project gives it: the applicationId of Android, the bundle
|
|
150
|
+
* identifier of iOS, the bundleName of HarmonyOS
|
|
151
|
+
* @param project
|
|
152
|
+
* @param platform
|
|
153
|
+
*/
|
|
154
|
+
function appIdOf(project, platform) {
|
|
155
|
+
const read = (file, pattern, what) => {
|
|
156
|
+
const id = existsSync(file) ? pattern.exec(readFileSync(file, 'utf8'))?.[1] : undefined;
|
|
157
|
+
if (!id)
|
|
158
|
+
throw new Error(`The id of the application cannot be read (${what} of ${path.relative(project.root, file)})`);
|
|
159
|
+
return id;
|
|
160
|
+
};
|
|
161
|
+
if (platform === 'android')
|
|
162
|
+
return read(path.join(project.root, 'android', 'app', 'build.gradle'), /applicationId\s+["']([^"']+)["']/, 'applicationId');
|
|
163
|
+
if (platform === 'ios')
|
|
164
|
+
return read(path.join(project.root, 'ios', 'App.xcodeproj', 'project.pbxproj'), /PRODUCT_BUNDLE_IDENTIFIER = "?([^";]+)"?;/, 'PRODUCT_BUNDLE_IDENTIFIER');
|
|
165
|
+
return read(path.join(project.root, 'harmony', 'AppScope', 'app.json5'), /"bundleName"\s*:\s*"([^"]+)"/, 'bundleName');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The adb of the Android SDK (ANDROID_HOME), else the one of the PATH
|
|
169
|
+
*/
|
|
170
|
+
function adbProgram() {
|
|
171
|
+
const home = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT;
|
|
172
|
+
return home ? path.join(home, 'platform-tools', process.platform === 'win32' ? 'adb.exe' : 'adb') : 'adb';
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The emulator or the device of Android the command runs on: the one given, else the one running (adb devices)
|
|
176
|
+
* @param device
|
|
177
|
+
*/
|
|
178
|
+
async function androidDevice(device) {
|
|
179
|
+
if (device)
|
|
180
|
+
return device;
|
|
181
|
+
const serial = (await must('The devices of Android cannot be listed', adbProgram(), ['devices'])).split('\n').map((line) => line.trim().split(/\s+/)).find((parts) => parts[1] === 'device')?.[0];
|
|
182
|
+
if (!serial)
|
|
183
|
+
throw new Error('No emulator or device of Android is running (start one: emulator -avd <name>)');
|
|
184
|
+
return serial;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The architecture of an emulator or a device of Android (its ABI: arm64-v8a, x86_64…)
|
|
188
|
+
* @param serial
|
|
189
|
+
*/
|
|
190
|
+
async function androidAbi(serial) {
|
|
191
|
+
return (await must('The architecture of the device of Android cannot be read', adbProgram(), ['-s', serial, 'shell', 'getprop', 'ro.product.cpu.abi'])).trim();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Start the application installed on an emulator or a device of Android again (its launcher activity)
|
|
195
|
+
* @param serial
|
|
196
|
+
* @param appId
|
|
197
|
+
*/
|
|
198
|
+
async function restartAndroid(serial, appId) {
|
|
199
|
+
const adb = adbProgram();
|
|
200
|
+
const activity = (await must('The activity of the application cannot be found', adb, ['-s', serial, 'shell', 'cmd', 'package', 'resolve-activity', '--brief', '-c', 'android.intent.category.LAUNCHER', appId])).trim().split('\n').at(-1).trim();
|
|
201
|
+
await must('The application cannot be stopped', adb, ['-s', serial, 'shell', 'am', 'force-stop', appId]);
|
|
202
|
+
await must('The application cannot be started', adb, ['-s', serial, 'shell', 'am', 'start', '-n', activity]);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Put the Node.js project into the application installed on an emulator or a device of Android (a debug build:
|
|
206
|
+
* run-as), marked as written while developing (the native module keeps it instead of copying the assets again), and
|
|
207
|
+
* start the application again
|
|
208
|
+
* @param directory
|
|
209
|
+
* @param appId
|
|
210
|
+
* @param serial
|
|
211
|
+
*/
|
|
212
|
+
async function deployAndroid(directory, appId, serial) {
|
|
213
|
+
const adb = adbProgram();
|
|
214
|
+
const installed = await run(adb, ['-s', serial, 'shell', 'pm', 'path', appId]);
|
|
215
|
+
if (installed.code !== 0 || !installed.stdout.includes('package:'))
|
|
216
|
+
throw new Error(`The application ${appId} is not installed on ${serial} (npm run android)`);
|
|
217
|
+
const debuggable = await run(adb, ['-s', serial, 'shell', 'run-as', appId, 'ls']);
|
|
218
|
+
if (debuggable.code !== 0)
|
|
219
|
+
throw new Error(`The files of ${appId} cannot be written (run-as): a debug build of the application is deployed to (${(debuggable.stderr || debuggable.stdout).trim()})`);
|
|
220
|
+
await must('The application cannot be stopped', adb, ['-s', serial, 'shell', 'am', 'force-stop', appId]);
|
|
221
|
+
//The files of the project replaced by the ones made, through tar (the files of macOS without their extended attributes)
|
|
222
|
+
const tar = spawn('tar', ['-c', '-C', directory, '.'], { stdio: ['ignore', 'pipe', 'inherit'], env: { ...process.env, COPYFILE_DISABLE: '1' } });
|
|
223
|
+
await must('The Node.js project cannot be written into the application', adb, ['-s', serial, 'shell', 'run-as', appId, 'sh', '-c', `'rm -rf ${ANDROID_PROJECT} && mkdir -p ${ANDROID_PROJECT} && tar -x -f - -C ${ANDROID_PROJECT} && touch ${ANDROID_DEVELOPMENT}'`], tar.stdout);
|
|
224
|
+
await restartAndroid(serial, appId);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Build the debug application of Android (Gradle, for the architecture of the device), install it, make Metro reachable
|
|
228
|
+
* from the device (adb reverse) and start the application: the Node.js project of its assets replaces a copy deployed
|
|
229
|
+
* before
|
|
230
|
+
* @param project
|
|
231
|
+
* @param device
|
|
232
|
+
* @return the id of the application and the device
|
|
233
|
+
*/
|
|
234
|
+
async function runAndroid(project, device) {
|
|
235
|
+
const serial = await androidDevice(device);
|
|
236
|
+
const adb = adbProgram();
|
|
237
|
+
const appId = appIdOf(project, 'android');
|
|
238
|
+
await must('Metro cannot be made reachable from the device (adb reverse)', adb, ['-s', serial, 'reverse', `tcp:${METRO_PORT}`, `tcp:${METRO_PORT}`]);
|
|
239
|
+
const gradlew = path.join(project.root, 'android', process.platform === 'win32' ? 'gradlew.bat' : 'gradlew');
|
|
240
|
+
await shown(gradlew, ['installDebug', `-PreactNativeArchitectures=${await androidAbi(serial)}`], path.join(project.root, 'android'), { ...process.env, ANDROID_SERIAL: serial });
|
|
241
|
+
await run(adb, ['-s', serial, 'shell', 'run-as', appId, 'rm', '-f', ANDROID_DEVELOPMENT]);
|
|
242
|
+
await restartAndroid(serial, appId);
|
|
243
|
+
return { appId: appId, serial: serial };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The simulator of iOS the application runs on, booted (its UDID): the one given (its UDID or its name), else the one
|
|
247
|
+
* running, else the newest iPhone
|
|
248
|
+
* @param device
|
|
249
|
+
*/
|
|
250
|
+
async function iosSimulator(device) {
|
|
251
|
+
const listed = JSON.parse(await must('The simulators cannot be listed', 'xcrun', ['simctl', 'list', 'devices', 'available', '--json']));
|
|
252
|
+
//The ones of the runtimes of iOS, the newest first
|
|
253
|
+
const simulators = Object.entries(listed.devices)
|
|
254
|
+
.filter(([runtime]) => runtime.includes('SimRuntime.iOS-'))
|
|
255
|
+
.sort(([a], [b]) => b.localeCompare(a, undefined, { numeric: true }))
|
|
256
|
+
.flatMap(([, devices]) => devices);
|
|
257
|
+
const simulator = device
|
|
258
|
+
? simulators.find((candidate) => candidate.udid === device || candidate.name === device)
|
|
259
|
+
: simulators.find((candidate) => candidate.state === 'Booted') ?? simulators.find((candidate) => candidate.name.startsWith('iPhone'));
|
|
260
|
+
if (!simulator)
|
|
261
|
+
throw new Error(device ? `No simulator of iOS is ${device} (xcrun simctl list devices)` : 'No simulator of iOS is available (the platform of iOS of Xcode)');
|
|
262
|
+
if (simulator.state !== 'Booted') {
|
|
263
|
+
step(`The simulator ${simulator.name} booted`);
|
|
264
|
+
await must(`The simulator ${simulator.name} cannot be booted`, 'xcrun', ['simctl', 'bootstatus', simulator.udid, '-b']);
|
|
265
|
+
}
|
|
266
|
+
return simulator.udid;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Show a simulator of iOS in the app of Xcode showing them: Simulator, Device Hub since Xcode 27. Not shown without it:
|
|
270
|
+
* the application runs all the same
|
|
271
|
+
* @param udid
|
|
272
|
+
*/
|
|
273
|
+
async function showSimulator(udid) {
|
|
274
|
+
const developer = (await run('xcode-select', ['-p'])).stdout.trim();
|
|
275
|
+
if (!developer)
|
|
276
|
+
return;
|
|
277
|
+
const simulator = path.join(developer, 'Applications', 'Simulator.app');
|
|
278
|
+
const hub = path.join(developer, '..', 'Applications', 'DeviceHub.app');
|
|
279
|
+
if (existsSync(simulator))
|
|
280
|
+
await run('open', [simulator, '--args', '-CurrentDeviceUDID', udid]);
|
|
281
|
+
else if (existsSync(hub))
|
|
282
|
+
await run('open', [hub]);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Whether the pods of the project of iOS are those of its Podfile and of the libnode exported: installed after the last
|
|
286
|
+
* change of the Podfile and the last export of the libnode (whose framework the pod of the native module vendors)
|
|
287
|
+
* @param ios
|
|
288
|
+
* @param node the export of the libnode of iOS
|
|
289
|
+
*/
|
|
290
|
+
function podsInstalled(ios, node) {
|
|
291
|
+
const lock = path.join(ios, 'Podfile.lock');
|
|
292
|
+
const manifest = path.join(ios, 'Pods', 'Manifest.lock');
|
|
293
|
+
if (!existsSync(path.join(ios, 'App.xcworkspace')) || !existsSync(lock) || !existsSync(manifest) || !existsSync(path.join(ios, 'Podfile')))
|
|
294
|
+
return false;
|
|
295
|
+
const installed = statSync(manifest).mtimeMs;
|
|
296
|
+
return readFileSync(lock, 'utf8') === readFileSync(manifest, 'utf8') && installed >= statSync(path.join(ios, 'Podfile')).mtimeMs && (!existsSync(node) || installed >= statSync(node).mtimeMs);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Build the debug application of iOS for a simulator (the pods installed first when needed, xcodebuild), install it and
|
|
300
|
+
* start it; a simulator shares localhost with the computer, so Metro is reachable
|
|
301
|
+
* @param project
|
|
302
|
+
* @param udid the simulator, booted
|
|
303
|
+
* @return the id of the application
|
|
304
|
+
*/
|
|
305
|
+
async function runIos(project, udid) {
|
|
306
|
+
const ios = path.join(project.root, 'ios');
|
|
307
|
+
if (!podsInstalled(ios, nodeOf(project, 'ios')))
|
|
308
|
+
await shown('pod', ['install'], ios);
|
|
309
|
+
const derived = path.join(ios, 'build');
|
|
310
|
+
await shown('xcrun', ['xcodebuild', '-workspace', 'App.xcworkspace', '-scheme', 'App', '-configuration', 'Debug', '-destination', `id=${udid}`, '-derivedDataPath', derived, 'build'], ios);
|
|
311
|
+
const application = path.join(derived, 'Build', 'Products', 'Debug-iphonesimulator', 'App.app');
|
|
312
|
+
const id = (await must('The id of the application cannot be read', 'plutil', ['-extract', 'CFBundleIdentifier', 'raw', path.join(application, 'Info.plist')])).trim();
|
|
313
|
+
await must('The application cannot be installed', 'xcrun', ['simctl', 'install', udid, application]);
|
|
314
|
+
await showSimulator(udid);
|
|
315
|
+
await run('xcrun', ['simctl', 'terminate', udid, id]);
|
|
316
|
+
await must('The application cannot be started', 'xcrun', ['simctl', 'launch', udid, id]);
|
|
317
|
+
return id;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Put the Node.js project into the application installed in a simulator of iOS (the directory of the application on
|
|
321
|
+
* the Mac), with the frameworks of its native addons, and start the application again
|
|
322
|
+
* @param directory
|
|
323
|
+
* @param appId
|
|
324
|
+
* @param udid
|
|
325
|
+
* @param frameworks the frameworks of the native addons for the simulators
|
|
326
|
+
*/
|
|
327
|
+
async function deployIos(directory, appId, udid, frameworks) {
|
|
328
|
+
const container = (await must(`The application ${appId} is not installed on the simulator ${udid} (npm run ios)`, 'xcrun', ['simctl', 'get_app_container', udid, appId, 'app'])).trim();
|
|
329
|
+
await run('xcrun', ['simctl', 'terminate', udid, appId]);
|
|
330
|
+
const project = path.join(container, 'nodejs');
|
|
331
|
+
await rm(project, { recursive: true, force: true });
|
|
332
|
+
await mkdir(project, { recursive: true });
|
|
333
|
+
for (const entry of await readdir(directory))
|
|
334
|
+
await cp(path.join(directory, entry), path.join(project, entry), { recursive: true });
|
|
335
|
+
//The frameworks of its native addons (signed ad hoc, which the simulators take) in place of the ones of the application
|
|
336
|
+
for (const entry of existsSync(frameworks) ? await readdir(frameworks) : []) {
|
|
337
|
+
if (!entry.endsWith('.framework'))
|
|
338
|
+
continue;
|
|
339
|
+
const target = path.join(container, 'Frameworks', entry);
|
|
340
|
+
await rm(target, { recursive: true, force: true });
|
|
341
|
+
await cp(path.join(frameworks, entry), target, { recursive: true });
|
|
342
|
+
}
|
|
343
|
+
await must('The application cannot be started', 'xcrun', ['simctl', 'launch', udid, appId]);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* The tools of DevEco Studio building and installing the applications of HarmonyOS: ohpm and hvigor (run by the Node.js
|
|
347
|
+
* of DevEco Studio), its SDK and hdc. DEVECO_STUDIO_HOME gives the directory of DevEco Studio (its Contents on macOS) or
|
|
348
|
+
* of its command line tools, DEVECO_SDK_HOME its SDK
|
|
349
|
+
*/
|
|
350
|
+
function devEcoTools() {
|
|
351
|
+
const home = process.env.DEVECO_STUDIO_HOME ?? (process.platform === 'darwin' ? '/Applications/DevEco-Studio.app/Contents' : path.join(os.homedir(), 'command-line-tools'));
|
|
352
|
+
const sdk = process.env.DEVECO_SDK_HOME ?? path.join(home, 'sdk');
|
|
353
|
+
const hvigor = path.join(home, 'tools', 'hvigor', 'bin', 'hvigorw.js');
|
|
354
|
+
const node = path.join(home, 'tools', 'node', 'bin', process.platform === 'win32' ? 'node.exe' : 'node');
|
|
355
|
+
if (!existsSync(hvigor))
|
|
356
|
+
throw new Error(`DevEco Studio builds the application of HarmonyOS: install it, or give its directory (DEVECO_STUDIO_HOME; ${hvigor} missing)`);
|
|
357
|
+
return {
|
|
358
|
+
ohpm: path.join(home, 'tools', 'ohpm', 'bin', process.platform === 'win32' ? 'ohpm.bat' : 'ohpm'),
|
|
359
|
+
hvigor: hvigor,
|
|
360
|
+
node: existsSync(node) ? node : process.execPath,
|
|
361
|
+
sdk: sdk,
|
|
362
|
+
hdc: path.join(sdk, 'default', 'openharmony', 'toolchains', process.platform === 'win32' ? 'hdc.exe' : 'hdc')
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Run a command of hdc, which must succeed: hdc exits with 0 when it fails too, telling the failure in its output (a
|
|
367
|
+
* line [Fail]…, or error: … from the device)
|
|
368
|
+
* @param what
|
|
369
|
+
* @param hdc
|
|
370
|
+
* @param args
|
|
371
|
+
*/
|
|
372
|
+
async function mustHdc(what, hdc, args) {
|
|
373
|
+
const output = await must(what, hdc, args);
|
|
374
|
+
if (/^\[Fail\]|error:/im.test(output))
|
|
375
|
+
throw new Error(`${what}: ${output.trim()}`);
|
|
376
|
+
return output;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The emulator or the device of HarmonyOS the command runs on: the one given, else the one connected (hdc list targets)
|
|
380
|
+
* @param hdc
|
|
381
|
+
* @param device
|
|
382
|
+
*/
|
|
383
|
+
async function harmonyTarget(hdc, device) {
|
|
384
|
+
if (device)
|
|
385
|
+
return device;
|
|
386
|
+
const target = (await must('The devices of HarmonyOS cannot be listed', hdc, ['list', 'targets'])).split('\n').map((line) => line.trim()).find((line) => !!line && line !== '[Empty]');
|
|
387
|
+
if (!target)
|
|
388
|
+
throw new Error('No emulator or device of HarmonyOS is connected (start one in the Device Manager of DevEco Studio)');
|
|
389
|
+
return target;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The files of the application of HarmonyOS, as it sees them (hdc file send -b writes there into a debug build), where
|
|
393
|
+
* the native module runs the copy of the Node.js project written while developing (nodejs-dev: the resources of the HAP
|
|
394
|
+
* cannot be written) while its .development says on. The files cannot be removed from the computer, only written: the
|
|
395
|
+
* copy is turned off instead
|
|
396
|
+
*/
|
|
397
|
+
const HARMONY_FILES = '/data/storage/el2/base/files';
|
|
398
|
+
/**
|
|
399
|
+
* Write the state of the copy of the Node.js project written while developing on HarmonyOS (on: deployed, off: the
|
|
400
|
+
* project of the HAP runs), along with the files of a project to deploy
|
|
401
|
+
* @param hdc
|
|
402
|
+
* @param target
|
|
403
|
+
* @param bundle
|
|
404
|
+
* @param state
|
|
405
|
+
* @param project the Node.js project deployed with the state on
|
|
406
|
+
* @returns the output of hdc
|
|
407
|
+
*/
|
|
408
|
+
async function writeHarmonyDevelopment(hdc, target, bundle, state, project) {
|
|
409
|
+
const work = await mkdtemp(path.join(os.tmpdir(), 'lakutata-harmony-'));
|
|
410
|
+
try {
|
|
411
|
+
//Sent into the files as nodejs-dev, merged into the copy there (hdc copies a directory into the one of its name)
|
|
412
|
+
const development = path.join(work, 'nodejs-dev');
|
|
413
|
+
if (project)
|
|
414
|
+
await cp(project, development, { recursive: true });
|
|
415
|
+
else
|
|
416
|
+
await mkdir(development);
|
|
417
|
+
await writeFile(path.join(development, '.development'), `${state}\n`);
|
|
418
|
+
return project
|
|
419
|
+
? await run(hdc, ['-t', target, 'file', 'send', '-b', bundle, development, HARMONY_FILES])
|
|
420
|
+
: await run(hdc, ['-t', target, 'file', 'send', '-b', bundle, path.join(development, '.development'), `${HARMONY_FILES}/nodejs-dev/.development`]);
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
await rm(work, { recursive: true, force: true });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Write the state of the copy of the Node.js project written while developing on HarmonyOS (writeHarmonyDevelopment),
|
|
428
|
+
* with the application stopped. Once the device has started, the files of an application are there after it has run:
|
|
429
|
+
* before, hdc refuses its name (E005101), and the application is started once, then stopped, and the files written
|
|
430
|
+
* again
|
|
431
|
+
* @param hdc
|
|
432
|
+
* @param target
|
|
433
|
+
* @param bundle
|
|
434
|
+
* @param state
|
|
435
|
+
* @param project the Node.js project deployed with the state on
|
|
436
|
+
* @returns the output of hdc
|
|
437
|
+
*/
|
|
438
|
+
async function sendHarmonyDevelopment(hdc, target, bundle, state, project) {
|
|
439
|
+
const refused = (sent) => /E005101|Invalid bundle name/i.test(sent.stdout);
|
|
440
|
+
await run(hdc, ['-t', target, 'shell', 'aa', 'force-stop', bundle]);
|
|
441
|
+
let sent = await writeHarmonyDevelopment(hdc, target, bundle, state, project);
|
|
442
|
+
if (refused(sent)) {
|
|
443
|
+
await restartHarmony(hdc, target, bundle);
|
|
444
|
+
for (let attempt = 0; attempt < 10 && refused(sent); attempt++) {
|
|
445
|
+
await new Promise((resolve) => setTimeout(resolve, attempt ? 500 : 0));
|
|
446
|
+
await run(hdc, ['-t', target, 'shell', 'aa', 'force-stop', bundle]);
|
|
447
|
+
sent = await writeHarmonyDevelopment(hdc, target, bundle, state, project);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return sent;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Start the application installed on an emulator or a device of HarmonyOS again
|
|
454
|
+
* @param hdc
|
|
455
|
+
* @param target
|
|
456
|
+
* @param bundle
|
|
457
|
+
*/
|
|
458
|
+
async function restartHarmony(hdc, target, bundle) {
|
|
459
|
+
await run(hdc, ['-t', target, 'shell', 'aa', 'force-stop', bundle]);
|
|
460
|
+
const started = await must('The application cannot be started', hdc, ['-t', target, 'shell', 'aa', 'start', '-a', 'EntryAbility', '-b', bundle]);
|
|
461
|
+
if (/error/i.test(started))
|
|
462
|
+
throw new Error(`The application cannot be started: ${started.trim()}`);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Build the debug HAP of HarmonyOS (the user interface bundled by RNOH, hvigor), install it, make Metro reachable from
|
|
466
|
+
* the device (hdc rport) and start the application: the Node.js project of its resources replaces a copy deployed before
|
|
467
|
+
* @param project
|
|
468
|
+
* @param device
|
|
469
|
+
* @return the bundle of the application and the device
|
|
470
|
+
*/
|
|
471
|
+
async function runHarmony(project, device) {
|
|
472
|
+
const tools = devEcoTools();
|
|
473
|
+
const target = await harmonyTarget(tools.hdc, device);
|
|
474
|
+
const bundle = appIdOf(project, 'harmony');
|
|
475
|
+
//The user interface in the HAP, which loads it when Metro does not run (RNOH: a bundle of JavaScript)
|
|
476
|
+
const reactNative = path.join(project.root, 'node_modules', '.bin', process.platform === 'win32' ? 'react-native.cmd' : 'react-native');
|
|
477
|
+
await shown(reactNative, ['bundle-harmony', '--dev', 'false', '--bundle-output', path.join('harmony', 'entry', 'src', 'main', 'resources', 'rawfile', 'bundle.harmony.js')], project.root);
|
|
478
|
+
const harmony = path.join(project.root, 'harmony');
|
|
479
|
+
const env = { ...process.env, DEVECO_SDK_HOME: tools.sdk, PATH: `${path.dirname(tools.node)}${path.delimiter}${process.env.PATH ?? ''}` };
|
|
480
|
+
//The libraries of React Native ported to HarmonyOS (RNOH autolinking): their HARs added to oh-package.json5 before
|
|
481
|
+
//ohpm installs them, as hvigor adds them only once ohpm ran
|
|
482
|
+
await shown(reactNative, ['link-harmony'], project.root);
|
|
483
|
+
//The packages of the HAP (RNOH and the libraries, from node_modules) in harmony/oh_modules, which its native build reads
|
|
484
|
+
await shown(tools.ohpm, ['install', '--all'], harmony, env);
|
|
485
|
+
await shown(tools.node, [tools.hvigor, '--mode', 'module', '-p', 'product=default', '-p', 'module=entry@default', 'assembleHap', '--no-daemon'], harmony, env);
|
|
486
|
+
const outputs = path.join(harmony, 'entry', 'build', 'default', 'outputs', 'default');
|
|
487
|
+
const haps = existsSync(outputs) ? readdirSync(outputs).filter((name) => name.endsWith('.hap')) : [];
|
|
488
|
+
const hap = haps.find((name) => name.includes('-signed')) ?? haps[0];
|
|
489
|
+
if (!hap)
|
|
490
|
+
throw new Error(`The build made no HAP (${path.relative(project.root, outputs)})`);
|
|
491
|
+
await mustHdc('The application cannot be installed', tools.hdc, ['-t', target, 'install', '-r', path.join(outputs, hap)]);
|
|
492
|
+
//The forward of a run before removed first: the server of hdc keeps it, setting it up again on an emulator started
|
|
493
|
+
//again, and rport fails on the port it takes (none to remove: hdc tells it, which is no failure)
|
|
494
|
+
await run(tools.hdc, ['-t', target, 'fport', 'rm', `tcp:${METRO_PORT}`, `tcp:${METRO_PORT}`]);
|
|
495
|
+
await mustHdc('Metro cannot be made reachable from the device (hdc rport)', tools.hdc, ['-t', target, 'rport', `tcp:${METRO_PORT}`, `tcp:${METRO_PORT}`]);
|
|
496
|
+
//A copy deployed before turned off (none deployed: nothing to write); after the device started again too, or the
|
|
497
|
+
//copy would run instead of the project installed
|
|
498
|
+
await sendHarmonyDevelopment(tools.hdc, target, bundle, 'off');
|
|
499
|
+
await restartHarmony(tools.hdc, target, bundle);
|
|
500
|
+
return { bundle: bundle, target: target };
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Put the Node.js project into the debug application installed on an emulator or a device of HarmonyOS (files/nodejs-dev
|
|
504
|
+
* of the application, turned on: the native module runs it instead of the one of its resources), and start the
|
|
505
|
+
* application again
|
|
506
|
+
* @param directory
|
|
507
|
+
* @param bundle
|
|
508
|
+
* @param device
|
|
509
|
+
*/
|
|
510
|
+
async function deployHarmony(directory, bundle, device) {
|
|
511
|
+
const tools = devEcoTools();
|
|
512
|
+
const target = await harmonyTarget(tools.hdc, device);
|
|
513
|
+
const sent = await sendHarmonyDevelopment(tools.hdc, target, bundle, 'on', directory);
|
|
514
|
+
if (sent.code !== 0 || /fail|error/i.test(sent.stdout))
|
|
515
|
+
throw new Error(`The Node.js project cannot be written into the application ${bundle} (a debug build, hdc file send -b): ${(sent.stdout || sent.stderr).trim()}`);
|
|
516
|
+
await restartHarmony(tools.hdc, target, bundle);
|
|
517
|
+
return target;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* The commands of the mobile applications of React Native laid out as the mobile-react template: their libnode, their
|
|
521
|
+
* Node.js project, the application run on an emulator or a simulator, the project deployed into the application
|
|
522
|
+
* installed during the development
|
|
523
|
+
*/
|
|
524
|
+
export class MobileController extends Controller {
|
|
525
|
+
async node(input) {
|
|
526
|
+
const project = mobileProject(process.cwd());
|
|
527
|
+
const abis = input.abis ? input.abis.split(',').map((abi) => abi.trim()).filter(Boolean) : undefined;
|
|
528
|
+
const out = await putNode(project, input.platform, abis);
|
|
529
|
+
done(`The libnode of ${input.platform}: ${path.relative(process.cwd(), out)}`);
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
async stage(input) {
|
|
533
|
+
const project = mobileProject(process.cwd());
|
|
534
|
+
const stage = await this.stageProject(project, input.platform, { minify: input.minify, abis: input.abis ? input.abis.split(',').map((abi) => abi.trim()).filter(Boolean) : undefined });
|
|
535
|
+
done(`The Node.js project for ${stage.platform} (Node.js ${stage.node}): ${path.relative(process.cwd(), stage.directory)}`);
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
async run(input) {
|
|
539
|
+
const project = mobileProject(process.cwd());
|
|
540
|
+
if (input.platform === 'android') {
|
|
541
|
+
const { appId, serial } = await runAndroid(project, input.device);
|
|
542
|
+
done(`${appId} runs on ${serial}`);
|
|
543
|
+
}
|
|
544
|
+
else if (input.platform === 'ios') {
|
|
545
|
+
const udid = await iosSimulator(input.device);
|
|
546
|
+
done(`${await runIos(project, udid)} runs on the simulator ${udid}`);
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const { bundle, target } = await runHarmony(project, input.device);
|
|
550
|
+
done(`${bundle} runs on ${target}`);
|
|
551
|
+
}
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
async deploy(input) {
|
|
555
|
+
const project = mobileProject(process.cwd());
|
|
556
|
+
await this.deployOnce(project, input);
|
|
557
|
+
if (!input.watch)
|
|
558
|
+
return undefined;
|
|
559
|
+
//The files of the application, without its dependencies and its compiled files: built and deployed again once
|
|
560
|
+
//they stop changing
|
|
561
|
+
const manifest = JSON.parse(readFileSync(path.join(project.app, 'package.json'), 'utf8'));
|
|
562
|
+
const ignored = ['node_modules', path.dirname(manifest.main ?? 'build/App.js')].map((directory) => directory.split(path.sep).join('/'));
|
|
563
|
+
const watched = (file) => {
|
|
564
|
+
if (!file)
|
|
565
|
+
return false;
|
|
566
|
+
const relative = file.split(path.sep).join('/');
|
|
567
|
+
return !relative.split('/').some((part) => part.startsWith('.')) && !ignored.some((directory) => relative === directory || relative.startsWith(`${directory}/`));
|
|
568
|
+
};
|
|
569
|
+
step('Watching the files of the application: built and deployed again when they change (Ctrl+C to stop)');
|
|
570
|
+
await new Promise((resolve) => {
|
|
571
|
+
let timer;
|
|
572
|
+
let deploying = Promise.resolve();
|
|
573
|
+
const watcher = watch(project.app, { recursive: true }, (_event, file) => {
|
|
574
|
+
if (!watched(file))
|
|
575
|
+
return;
|
|
576
|
+
clearTimeout(timer);
|
|
577
|
+
timer = setTimeout(() => {
|
|
578
|
+
deploying = deploying.then(async () => {
|
|
579
|
+
if (manifest.scripts?.build) {
|
|
580
|
+
step('npm run build');
|
|
581
|
+
const built = await runCommand('npm', ['run', 'build'], project.app);
|
|
582
|
+
if (built.code !== 0)
|
|
583
|
+
return warn(`The build failed:\n${built.output.trim().split('\n').slice(-15).join('\n')}`);
|
|
584
|
+
}
|
|
585
|
+
await this.deployOnce(project, input);
|
|
586
|
+
}).catch((error) => warn(error.message));
|
|
587
|
+
}, 300);
|
|
588
|
+
});
|
|
589
|
+
const stop = () => {
|
|
590
|
+
clearTimeout(timer);
|
|
591
|
+
watcher.close();
|
|
592
|
+
resolve();
|
|
593
|
+
};
|
|
594
|
+
process.once('SIGINT', stop);
|
|
595
|
+
process.once('SIGTERM', stop);
|
|
596
|
+
});
|
|
597
|
+
return undefined;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Make the Node.js project of the application for a system, where its native project takes it
|
|
601
|
+
* @param project
|
|
602
|
+
* @param platform
|
|
603
|
+
* @param options
|
|
604
|
+
* @protected
|
|
605
|
+
*/
|
|
606
|
+
async stageProject(project, platform, options) {
|
|
607
|
+
const { stageMobileApp } = await loadBuilder(project.app);
|
|
608
|
+
//The libnode of the system downloaded first when the project has none yet
|
|
609
|
+
if (!existsSync(path.join(nodeOf(project, platform), 'include', 'node', 'node_api.h')))
|
|
610
|
+
await putNode(project, platform);
|
|
611
|
+
return stageMobileApp({
|
|
612
|
+
platform: platform,
|
|
613
|
+
project: project.app,
|
|
614
|
+
outdir: stageOf(project, platform),
|
|
615
|
+
node: nodeOf(project, platform),
|
|
616
|
+
frameworks: platform === 'ios' ? path.join(project.root, 'ios', 'NodeAddons') : undefined,
|
|
617
|
+
libraries: platform === 'harmony' ? harmonyLibraries(project) : undefined,
|
|
618
|
+
minify: options.minify,
|
|
619
|
+
abis: options.abis,
|
|
620
|
+
sdks: options.sdks,
|
|
621
|
+
progress: step
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Make the Node.js project, put it into the application and start the application again
|
|
626
|
+
* @param project
|
|
627
|
+
* @param input
|
|
628
|
+
* @protected
|
|
629
|
+
*/
|
|
630
|
+
async deployOnce(project, input) {
|
|
631
|
+
if (input.platform === 'android') {
|
|
632
|
+
const serial = await androidDevice(input.device);
|
|
633
|
+
//The native addons built for the architecture of the device only
|
|
634
|
+
const stage = await this.stageProject(project, 'android', { minify: false, abis: [await androidAbi(serial)] });
|
|
635
|
+
await deployAndroid(stage.directory, appIdOf(project, 'android'), serial);
|
|
636
|
+
done(`Deployed to ${appIdOf(project, 'android')} (${serial}) and started`);
|
|
637
|
+
}
|
|
638
|
+
else if (input.platform === 'ios') {
|
|
639
|
+
const udid = await iosSimulator(input.device);
|
|
640
|
+
const stage = await this.stageProject(project, 'ios', { minify: false, sdks: ['iphonesimulator'] });
|
|
641
|
+
await deployIos(stage.directory, appIdOf(project, 'ios'), udid, path.join(project.root, 'ios', 'NodeAddons', 'iphonesimulator'));
|
|
642
|
+
done(`Deployed to ${appIdOf(project, 'ios')} (${udid}) and started`);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
const stage = await this.stageProject(project, 'harmony', { minify: false });
|
|
646
|
+
//The libraries of the native addons are in the HAP: installed again with it
|
|
647
|
+
if (stage.addons?.libraries.length)
|
|
648
|
+
warn(`The native addons of HarmonyOS are libraries of the HAP: npm run harmony installs them (${stage.addons.libraries.join(', ')})`);
|
|
649
|
+
const target = await deployHarmony(stage.directory, appIdOf(project, 'harmony'), input.device);
|
|
650
|
+
done(`Deployed to ${appIdOf(project, 'harmony')} (${target}) and started`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
__decorate([
|
|
655
|
+
CLIAction('mobile node <platform>', MobileNodeDTO, { description: 'Download the libnode of a system (android, ios, harmony) from the releases of lakutata/libnode (LAKUTATA_LIBNODE_MIRROR: a mirror of them), once: kept in the cache of lakutata, put into packages/ipc/native/node/out/<platform>' }),
|
|
656
|
+
__metadata("design:type", Function),
|
|
657
|
+
__metadata("design:paramtypes", [MobileNodeDTO]),
|
|
658
|
+
__metadata("design:returntype", Promise)
|
|
659
|
+
], MobileController.prototype, "node", null);
|
|
660
|
+
__decorate([
|
|
661
|
+
CLIAction('mobile stage <platform>', MobileStageDTO, { description: 'Make the Node.js project of the mobile application (packages/app) for a system (android, ios, harmony), where its native project takes it, with its native addons built against the libnode of the system' }),
|
|
662
|
+
__metadata("design:type", Function),
|
|
663
|
+
__metadata("design:paramtypes", [MobileStageDTO]),
|
|
664
|
+
__metadata("design:returntype", Promise)
|
|
665
|
+
], MobileController.prototype, "stage", null);
|
|
666
|
+
__decorate([
|
|
667
|
+
CLIAction('mobile run <platform>', MobileRunDTO, { description: 'Build the debug application of a system (android, ios, harmony) from its native project, install it on an emulator, a device or a simulator and start it, Metro (npm start) giving its user interface' }),
|
|
668
|
+
__metadata("design:type", Function),
|
|
669
|
+
__metadata("design:paramtypes", [MobileRunDTO]),
|
|
670
|
+
__metadata("design:returntype", Promise)
|
|
671
|
+
], MobileController.prototype, "run", null);
|
|
672
|
+
__decorate([
|
|
673
|
+
CLIAction('mobile deploy <platform>', MobileDeployDTO, { description: 'Make the Node.js project of the mobile application, put it into the debug application installed on an emulator, a device or a simulator of a system (android, ios, harmony), and start the application again (the development, without installing it again)' }),
|
|
674
|
+
__metadata("design:type", Function),
|
|
675
|
+
__metadata("design:paramtypes", [MobileDeployDTO]),
|
|
676
|
+
__metadata("design:returntype", Promise)
|
|
677
|
+
], MobileController.prototype, "deploy", null);
|