@lakutata/cli 3.1.5 → 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 CHANGED
@@ -22,6 +22,7 @@ both the features and the confirmation).
22
22
  |---|---|
23
23
  | `--name <name>` | the name of the application (required) |
24
24
  | `--id <id>` | the ID of the application (a package name), by default its name in lower case |
25
+ | `--bundle-id <id>` | the id of the application on the mobile systems (reverse domain name: `com.acme.shop`), for the mobile templates: `com.example.<name>` by default, which the stores refuse |
25
26
  | `--template <template>` | the template (`default` when it is not given): an official template (listed by `lakutata templates`), `npm:<package>[@<version>]`, `github:<owner>/<repo>[#<ref>]` or a directory |
26
27
  | `--path <directory>` | the directory in which the project directory is created, by default the current one |
27
28
  | `--init` | create the project in the `--path` directory itself, which must exist and may hold files: the files it already has are kept (a warning lists those that the template also has, to merge by hand), and only the files the tool writes are filled with the information of the project |
@@ -580,6 +581,90 @@ NW.js, with its resources and its native addons made to load in it, then run wit
580
581
  | `--release <version>` | the release of NW.js run (a version, `latest`, `win7`), the one of `lakutata.nwjs` by default |
581
582
  | `--flavor <sdk\|normal>` | `sdk` (the default: with the DevTools) or `normal` (the one the installers hold) |
582
583
 
584
+ ## Mobile applications
585
+
586
+ A mobile application of React Native laid out as the `mobile-react` template (Android, iOS, HarmonyOS) runs the
587
+ application of `packages/app` in a libnode, the library of Node.js that its native module (`packages/ipc/native`)
588
+ embeds; its native projects are at the root of the project, its user interface in `packages/ui`. These commands, run in
589
+ any directory of the project, download that libnode, make the Node.js project of the application, run the application,
590
+ and deploy the project during the development.
591
+
592
+ ### `lakutata mobile node <platform>`
593
+
594
+ Download the libnode of a system (`android`, `ios` or `harmony`) from the releases of
595
+ [lakutata/libnode](https://github.com/lakutata/libnode) and put it into `packages/ipc/native/node/out/<platform>`, which
596
+ the native projects build against; for HarmonyOS, `libnode.so` is put into the libraries of the HAP too
597
+ (`harmony/entry/libs/arm64-v8a`). Its archives (`libnode_<platform>_<arch>.tar.gz`, one per architecture) are checked
598
+ with the `SHA256SUMS` of the release and kept in the cache of lakutata (`~/.cache/lakutata/mobile`): they are downloaded
599
+ once. `lakutata mobile stage` downloads the libnode of a system when the project has none yet.
600
+
601
+ - `LAKUTATA_LIBNODE_MIRROR` gives a mirror of the releases, its files laid out as on GitHub
602
+ (`<mirror>/v<version>/libnode_android_arm64.tar.gz`, `<mirror>/v<version>/SHA256SUMS`).
603
+ - `LAKUTATA_MOBILE_NODE_DIR` gives a libnode of your own instead, built with the repository of lakutata/libnode: a
604
+ directory per system (`android`, `ios`, `harmony`), as its builds export them.
605
+
606
+ | Option | |
607
+ |---|---|
608
+ | `--abis <list>` | the architectures of Android, separated by commas (`arm64-v8a,armeabi-v7a,x86_64`), `arm64-v8a,x86_64` by default |
609
+
610
+ ### `lakutata mobile stage <platform>`
611
+
612
+ Make the Node.js project of the application where the native project of the system takes it:
613
+ `android/app/src/main/assets/nodejs` (copied into the files of the application at its first launch), `ios/nodejs` (put
614
+ into the bundle of the application by its build) or `harmony/entry/src/main/resources/resfile/nodejs` (run from the
615
+ resources of the HAP). The project holds the bundle of the application for the version of Node.js of the libnode, its
616
+ resources, its `package.json` and its entry, which puts away `fetch()` where there is no WebAssembly (HarmonyOS).
617
+
618
+ The native addons of the dependencies are built for the system from the sources of their packages (`binding.gyp`),
619
+ against the headers and the libraries of the libnode. For Android, each architecture of the libnode gets a native tree
620
+ of its own, built by node-gyp with the NDK (the latest one of the Android SDK, or `ANDROID_NDK_HOME`); a package that
621
+ ships prebuilt addons of Android (`prebuilds/android-<arch>`) keeps them. For iOS, on macOS, each addon becomes a
622
+ framework for the devices and one for the simulators, built with Xcode into `ios/NodeAddons`, which the build of the
623
+ application embeds. An addon is built with the settings its package gives to macOS (`OS=="mac"` in its `binding.gyp` and
624
+ in the gyp files of node-addon-api), while the SDK, the architecture and the deployment target are those of iOS. For
625
+ HarmonyOS, each addon becomes a library of the HAP (`libaddon-<package>-<file>.so` in `harmony/entry/libs/arm64-v8a`),
626
+ built with the native SDK of DevEco Studio (or `OHOS_NDK_HOME`). The builds are kept in the cache of lakutata.
627
+
628
+ | Option | |
629
+ |---|---|
630
+ | `--abis <list>` | the architectures of Android the addons are built for, those of the libnode by default |
631
+ | `--no-minify` | keep the bundle readable |
632
+
633
+ ### `lakutata mobile run <platform>`
634
+
635
+ Build the debug application of the system and run it, its user interface loaded from Metro (`npm start`). The Node.js
636
+ project of the application installed replaces a copy deployed before.
637
+
638
+ - Android: on the emulator or the device running (or given), built by Gradle for its architecture, installed, Metro made
639
+ reachable from it (`adb reverse`), and started. adb is found in the Android SDK (`ANDROID_HOME`), else in the `PATH`.
640
+ - iOS: on a simulator (the one given, else the one running, else the newest iPhone, booted when needed), its pods
641
+ installed first when the `Podfile` changed, built with `xcodebuild`, installed and started with `xcrun simctl`, and
642
+ shown in Simulator or in Device Hub.
643
+ - HarmonyOS: on the emulator or the device connected (or given), its user interface bundled into the HAP
644
+ (`react-native bundle-harmony`), the libraries of React Native ported to HarmonyOS linked (`react-native
645
+ link-harmony`, which adds their packages to `harmony/oh-package.json5`), its ohpm dependencies installed
646
+ (`ohpm install --all`), built by the hvigor of DevEco Studio (`DEVECO_STUDIO_HOME`, its SDK `DEVECO_SDK_HOME`), installed with hdc, Metro made reachable from it
647
+ (`hdc rport`), and started.
648
+
649
+ | Option | |
650
+ |---|---|
651
+ | `--device <id>` | the emulator or the device of Android (its serial, `adb devices`) or of HarmonyOS (`hdc list targets`), the simulator of iOS (its UDID or its name, `xcrun simctl list devices`) |
652
+
653
+ ### `lakutata mobile deploy <platform>`
654
+
655
+ Make the project, put it into the debug application installed on the device, and start the application again: the
656
+ Node.js side changes without installing the application again. On Android it writes into the files of the application
657
+ (`adb shell run-as`), marked so that the application keeps it until it is installed again; on iOS into the application
658
+ in the simulator, with the frameworks of the addons; on HarmonyOS into the files of the application (`hdc file send`),
659
+ which the native module runs instead of the project of the resources. The native addons are built for the device only
660
+ (its architecture of Android, the simulators of iOS); those of HarmonyOS are libraries of the HAP, which
661
+ `lakutata mobile run harmony` installs.
662
+
663
+ | Option | |
664
+ |---|---|
665
+ | `--device <id>` | as for `lakutata mobile run` |
666
+ | `--watch` | watch the files of the application (not its dependencies or its compiled files): when they change, run its `build` script and deploy it again, until Ctrl+C |
667
+
583
668
  ## Migrations
584
669
 
585
670
  ### `lakutata migration <action>`
@@ -610,8 +695,8 @@ its entrypoints. `create` only writes a file.
610
695
 
611
696
  The migrations are compiled with the application and listed in the options of their database (`migrations: [...]`;
612
697
  they run when it connects with `migrationsRun: true`). The commands fail (exit code 1) when the application has no
613
- database (except `generate`, which then generates nothing), when the given database is not one of the application,
614
- and when a migration fails.
698
+ database (except `generate`, which then generates nothing and exits with 0, as it does when the schema has no changes),
699
+ when the given database is not one of the application, and when a migration fails.
615
700
 
616
701
  ## Languages
617
702
 
package/README.zh-CN.md CHANGED
@@ -20,6 +20,7 @@ lakutata create --name my-app
20
20
  |---|---|
21
21
  | `--name <name>` | 应用的名称(必填) |
22
22
  | `--id <id>` | 应用的 ID(一个包名),默认是名称的小写形式 |
23
+ | `--bundle-id <id>` | 应用在手机系统上的标识(反向域名:`com.acme.shop`),用于手机模板:默认是 `com.example.<名称>`,应用商店不接受 |
23
24
  | `--template <template>` | 模板(不指定时为 `default`):官方模板(`lakutata templates` 会列出)、`npm:<package>[@<version>]`、`github:<owner>/<repo>[#<ref>]`,或者一个目录 |
24
25
  | `--path <directory>` | 在哪个目录中创建项目目录,默认是当前目录 |
25
26
  | `--init` | 直接在 `--path` 目录中创建项目。这个目录必须已经存在,里面可以有文件:已有的文件会保留(模板中也有的那些会在警告中列出,需要手动合并),只有新写入的文件会填入项目的信息 |
@@ -343,6 +344,53 @@ NW.js 应用(`nwjs-react` 和 `nwjs-vue` 模板)运行在它 `package.json`
343
344
  | `--release <version>` | 运行的 NW.js 版本(某个版本、`latest`、`win7`),默认是 `lakutata.nwjs` 中的版本 |
344
345
  | `--flavor <sdk\|normal>` | `sdk`(默认:带 DevTools)或 `normal`(安装包中的版本) |
345
346
 
347
+ ## 手机应用
348
+
349
+ 按 `mobile-react` 模板布局的 React Native 手机应用(Android、iOS、鸿蒙)在一个 libnode 中运行 `packages/app` 里的应用,libnode 是应用的原生模块(`packages/ipc/native`)内嵌的 Node.js 库;各系统的原生工程在项目根目录,界面在 `packages/ui` 中。下面的命令可以在项目的任何目录中执行,用来下载 libnode、生成应用的 Node.js 项目、运行应用,并在开发时部署这个项目。
350
+
351
+ ### `lakutata mobile node <platform>`
352
+
353
+ 从 [lakutata/libnode](https://github.com/lakutata/libnode) 的 Release 下载一个系统(`android`、`ios` 或 `harmony`)的 libnode,放到 `packages/ipc/native/node/out/<platform>`,原生工程对它编译;鸿蒙的 `libnode.so` 还会放进 HAP 的库目录(`harmony/entry/libs/arm64-v8a`)。它的压缩包(`libnode_<platform>_<arch>.tar.gz`,每种架构一个)会用 Release 的 `SHA256SUMS` 校验,保存在 lakutata 的缓存目录中(`~/.cache/lakutata/mobile`),所以只下载一次。项目里还没有某个系统的 libnode 时,`lakutata mobile stage` 会先下载它。
354
+
355
+ - `LAKUTATA_LIBNODE_MIRROR` 指定 Release 的镜像,文件的布局和 GitHub 上一样(`<镜像>/v<版本>/libnode_android_arm64.tar.gz`、`<镜像>/v<版本>/SHA256SUMS`)。
356
+ - `LAKUTATA_MOBILE_NODE_DIR` 指定自己编译的 libnode(用 lakutata/libnode 仓库编译),每个系统一个目录(`android`、`ios`、`harmony`),和它导出的结构一样。
357
+
358
+ | 选项 | |
359
+ |---|---|
360
+ | `--abis <列表>` | Android 的架构,用逗号分隔(`arm64-v8a,armeabi-v7a,x86_64`),默认是 `arm64-v8a,x86_64` |
361
+
362
+ ### `lakutata mobile stage <platform>`
363
+
364
+ 把应用的 Node.js 项目生成到系统的原生工程读取它的位置:`android/app/src/main/assets/nodejs`(应用第一次启动时复制到应用的文件目录)、`ios/nodejs`(构建应用时放进应用包)或 `harmony/entry/src/main/resources/resfile/nodejs`(直接从 HAP 的资源中运行)。项目包含面向 libnode 中 Node.js 版本的应用单文件包、资源、`package.json` 和入口;在没有 WebAssembly 的地方(鸿蒙),入口会收起 `fetch()`。
365
+
366
+ 依赖里的原生插件会从各自包的源码(`binding.gyp`)为目标系统编译,用的是 libnode 的头文件和库。Android 上 libnode 的每种架构各有一棵原生树,用 node-gyp 和 NDK 编译(Android SDK 里最新的 NDK,或 `ANDROID_NDK_HOME`);自带 Android 预编译插件(`prebuilds/android-<arch>`)的包直接用它们。iOS 上(在 macOS 中)每个插件分别为真机和模拟器编成一个 framework,用 Xcode 编译到 `ios/NodeAddons` 中,构建应用时嵌进应用。编译时用的是插件的包写给 macOS 的设置(它的 `binding.gyp` 以及 node-addon-api 的 gyp 文件里 `OS=="mac"` 的部分),SDK、架构和部署版本则是 iOS 的。鸿蒙上每个插件编成 HAP 里的一个库(`harmony/entry/libs/arm64-v8a` 中的 `libaddon-<包>-<文件>.so`),用 DevEco Studio 的原生 SDK 编译(或 `OHOS_NDK_HOME`)。编译结果保存在 lakutata 的缓存目录中。
367
+
368
+ | 选项 | |
369
+ |---|---|
370
+ | `--abis <列表>` | 为哪些 Android 架构编译插件,默认是 libnode 的那些架构 |
371
+ | `--no-minify` | 不压缩单文件包 |
372
+
373
+ ### `lakutata mobile run <platform>`
374
+
375
+ 构建并运行系统的调试版应用,界面从 Metro(`npm start`)加载。已安装应用里的 Node.js 项目会替换之前部署进去的副本。
376
+
377
+ - Android:在正在运行(或指定)的模拟器或设备上,用 Gradle 按它的架构构建、安装,让它能访问 Metro(`adb reverse`),然后启动。adb 从 Android SDK(`ANDROID_HOME`)中查找,找不到时用 `PATH` 中的。
378
+ - iOS:在模拟器上(指定的那个,没有指定时用正在运行的那个,都没有时用最新的 iPhone,需要时先启动它),`Podfile` 有变化时先安装 pods,再用 `xcodebuild` 构建,用 `xcrun simctl` 安装、启动,并在 Simulator 或 Device Hub 中显示。
379
+ - 鸿蒙:在已连接(或指定)的模拟器或设备上,先把界面打包进 HAP(`react-native bundle-harmony`),链接移植到鸿蒙的 React Native 库(`react-native link-harmony`,它把这些库的包加进 `harmony/oh-package.json5`),用 ohpm 安装依赖(`ohpm install --all`),再用 DevEco Studio 的 hvigor 构建(`DEVECO_STUDIO_HOME`,SDK 为 `DEVECO_SDK_HOME`),用 hdc 安装,让它能访问 Metro(`hdc rport`),然后启动。
380
+
381
+ | 选项 | |
382
+ |---|---|
383
+ | `--device <id>` | Android 的模拟器或设备(序列号,见 `adb devices`)、鸿蒙的模拟器或设备(见 `hdc list targets`)、iOS 的模拟器(UDID 或名称,见 `xcrun simctl list devices`) |
384
+
385
+ ### `lakutata mobile deploy <platform>`
386
+
387
+ 生成项目,放进设备上已安装的调试版应用,再重新启动应用:Node.js 一侧的改动不用重新安装应用。在 Android 上写进应用的文件目录(`adb shell run-as`),并加上标记,让应用在重新安装之前一直使用它;在 iOS 上写进模拟器里的应用,连同插件的 framework;在鸿蒙上写进应用的文件目录(`hdc file send`),原生模块会优先运行它,而不是资源里的项目。原生插件只为当前设备编译(Android 只编设备的那种架构,iOS 只编模拟器版);鸿蒙的插件是 HAP 里的库,由 `lakutata mobile run harmony` 安装。
388
+
389
+ | 选项 | |
390
+ |---|---|
391
+ | `--device <id>` | 同 `lakutata mobile run` |
392
+ | `--watch` | 监视应用的文件(不包括依赖和编译产物):有变化就执行它的 `build` 脚本并重新部署,直到按 Ctrl+C |
393
+
346
394
  ## 迁移
347
395
 
348
396
  ### `lakutata migration <action>`
@@ -366,7 +414,7 @@ NW.js 应用(`nwjs-react` 和 `nwjs-vue` 模板)运行在它 `package.json`
366
414
  | `--transaction <mode>` | 执行或撤销迁移时使用的事务:`all`(所有迁移共用一个)、`each` 或 `none`;默认取数据库的 `migrationsTransactionMode`,没有时为 `all`(`MIGRATION_TRANSACTION`) |
367
415
  | `--fake` | 只把迁移标记为已执行或已撤销,不真正执行(`MIGRATION_FAKE`) |
368
416
 
369
- 迁移随应用一起编译,并列在所属数据库的选项中(`migrations: [...]`;设置了 `migrationsRun: true` 时,数据库连接时就会执行它们)。应用没有数据库时(`generate` 除外,这时它什么都不生成)、指定的数据库不属于应用时,以及迁移失败时,命令都会失败(退出码 1)。
417
+ 迁移随应用一起编译,并列在所属数据库的选项中(`migrations: [...]`;设置了 `migrationsRun: true` 时,数据库连接时就会执行它们)。应用没有数据库时(`generate` 除外,这时它什么都不生成,以退出码 0 结束,表结构没有变化时也一样)、指定的数据库不属于应用时,以及迁移失败时,命令都会失败(退出码 1)。
370
418
 
371
419
  ## 多语言
372
420
 
@@ -9,13 +9,14 @@ import { MigrationController } from './controllers/MigrationController.js';
9
9
  import { NativeController } from './controllers/NativeController.js';
10
10
  import { FFIController } from './controllers/FFIController.js';
11
11
  import { NwjsController } from './controllers/NwjsController.js';
12
+ import { MobileController } from './controllers/MobileController.js';
12
13
  import { I18nController } from './controllers/I18nController.js';
13
14
  import { LANGUAGE_VARIABLE, LanguageMiddleware } from './lib/Language.js';
14
15
  import { toolPackage } from './lib/Versions.js';
15
16
  /**
16
17
  * The controllers of the commands of the tool
17
18
  */
18
- export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, FFIController, NwjsController, I18nController];
19
+ export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, FFIController, NwjsController, MobileController, I18nController];
19
20
  /**
20
21
  * The messages of the tool in the languages besides English (its texts in the code): its help, its prompts
21
22
  */
@@ -25,7 +26,8 @@ export const CONTROLLERS = [CommandLineController, WorkspaceController, PackCont
25
26
  export const COMMAND_GROUPS = {
26
27
  package: 'Package the compiled application: into one JavaScript file, an executable or its installers',
27
28
  ffi: 'Generate the classes of the native libraries of @lakutata/ffi from their C headers',
28
- nwjs: 'Give the application its version of NW.js, run it in NW.js, list the releases of NW.js'
29
+ nwjs: 'Give the application its version of NW.js, run it in NW.js, list the releases of NW.js',
30
+ mobile: 'Download the libnode of the mobile application of React Native (Android, iOS, HarmonyOS), make its Node.js project, run the application on an emulator or a simulator, deploy the project into the application installed during the development'
29
31
  };
30
32
  /**
31
33
  * The messages of a language, read from its file of the locales (an import of JSON needs the import attributes, which
@@ -22,6 +22,15 @@ import { detectPackageManager } from '../lib/Processes.js';
22
22
  import { done, styled, table } from '../lib/Output.js';
23
23
  import { frameworkVersion, latestVersion, projectFrameworkVersion, toolPackage } from '../lib/Versions.js';
24
24
  import { localize } from '../lib/Language.js';
25
+ /**
26
+ * The id of an application on the mobile systems by default: com.example.<its name>, in lower case, of letters and
27
+ * digits only (a segment of an applicationId of Android starts with a letter)
28
+ * @param name
29
+ */
30
+ function defaultBundleId(name) {
31
+ const segment = name.toLowerCase().replace(/[^a-z0-9]/g, '') || 'app';
32
+ return `com.example.${/^[a-z]/.test(segment) ? segment : `a${segment}`}`;
33
+ }
25
34
  /**
26
35
  * Whether the tool runs in a terminal, where it asks the questions
27
36
  */
@@ -75,6 +84,7 @@ export class CommandLineController extends Controller {
75
84
  const plan = {
76
85
  name: input.name,
77
86
  id: input.id ?? input.name.toLowerCase(),
87
+ bundleId: input.bundleId ?? defaultBundleId(input.name),
78
88
  description: input.description,
79
89
  author: input.author,
80
90
  license: input.license,
@@ -89,6 +99,7 @@ export class CommandLineController extends Controller {
89
99
  process.stdout.write(table([
90
100
  [say('plan.name', 'Name'), plan.name],
91
101
  [say('plan.id', 'ID'), plan.id],
102
+ ...(await this.creator.uses(prepared, '{$APP_BUNDLE_ID}') ? [[say('plan.bundleId', 'Bundle ID'), plan.bundleId]] : []),
92
103
  [say('plan.description', 'Description'), plan.description || '-'],
93
104
  [say('plan.author', 'Author'), plan.author || '-'],
94
105
  [say('plan.license', 'License'), plan.license],
@@ -42,10 +42,10 @@ export class MigrationController extends Controller {
42
42
  done(`Migrations generated: ${result.files.map(relative).join(', ')}`);
43
43
  return undefined;
44
44
  }
45
- //The application tells why (no change of the schema, the failure)
45
+ //The application tells why: the failure, no change of the schemas, no database
46
46
  if (result.code !== 0)
47
47
  throw new Error(`No migration generated (the application exited with the code ${result.code})`);
48
- done('No migration generated: the application has no Database component or provider');
48
+ done('No migration generated');
49
49
  return undefined;
50
50
  }
51
51
  default: {