@lakutata/cli 3.1.1 → 3.1.3
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 +35 -3
- package/README.zh-CN.md +22 -2
- package/dist/esm/Config.js +4 -2
- package/dist/esm/controllers/FFIController.js +54 -0
- package/dist/esm/dtos/FFIGenerateDTO.js +53 -0
- package/dist/esm/lib/FFI.js +21 -0
- package/dist/esm/lib/ProjectCreator.js +2 -2
- package/dist/esm/lib/Versions.js +10 -0
- package/dist/esm/locales/zh-CN.json +20 -0
- package/dist/esm/locales/zh-TW.json +20 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -57,7 +57,8 @@ the command fails.
|
|
|
57
57
|
A template package keeps its files in its `template/` directory. npm packages cannot hold some files under their real
|
|
58
58
|
names, so `_gitignore`, `_npmrc`… are renamed `.gitignore`, `.npmrc`… when the project is created. The text files are
|
|
59
59
|
filled with the information of the project: `{$APP_NAME}`, `{$APP_ID}`, `{$APP_DESC}`, `{$APP_AUTHOR}`,
|
|
60
|
-
`{$APP_LICENSE}` and `{$LAKUTATA_VERSION}` (the version of the framework
|
|
60
|
+
`{$APP_LICENSE}` and `{$LAKUTATA_VERSION}` (the minor version of the framework, `3.1.0` for 3.1.2: a patch releases
|
|
61
|
+
only the packages it changes, so `^{$LAKUTATA_VERSION}` finds every package of lakutata).
|
|
61
62
|
|
|
62
63
|
A template can have optional features, which `lakutata create --with <features>` enables (or which the user chooses in
|
|
63
64
|
a terminal):
|
|
@@ -213,6 +214,34 @@ Packages are still published with `npm publish -w <package>` (the packages `laku
|
|
|
213
214
|
`lerna.json` with `"version": "independent"` cannot be bumped), write changelogs from the commits or cache builds:
|
|
214
215
|
workspaces that need these keep lerna (or nx, turborepo) alongside it.
|
|
215
216
|
|
|
217
|
+
## Native libraries
|
|
218
|
+
|
|
219
|
+
### `lakutata ffi generate <header>`
|
|
220
|
+
|
|
221
|
+
Generate the class of a native library of `@lakutata/ffi` from its C header: its declarations, a property by function
|
|
222
|
+
with its TypeScript type, an interface by struct and union, its integer macros as constants. The header is run through
|
|
223
|
+
the preprocessor of the C compiler (`cc -E`; `cl /P` on Windows, in the Developer Command Prompt of Visual Studio); the
|
|
224
|
+
declarations of the library are kept (the ones of the header, and of the headers of its directory unless it is a
|
|
225
|
+
directory of the system), with the types of the system they use. The functions are then checked against the library:
|
|
226
|
+
the ones it does not export, or that cannot be called (a `va_list` parameter), are left out. The command lists what
|
|
227
|
+
it left out and why. The tool loads `@lakutata/ffi` from the project: install it there.
|
|
228
|
+
|
|
229
|
+
| Option | |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `--library <name or file>` | the library the functions are checked against: a name, as `@Library` takes it, or a file; the name of the header by default (`sqlite3.h`: `sqlite3`; zlib needs `--library z`). When it cannot be loaded, the class is generated without the check, with a warning |
|
|
232
|
+
| `--class-name <name>` | the name of the class, the name of the header in PascalCase by default |
|
|
233
|
+
| `--out <file>` | the file written, `<class>.ts` in the current directory by default |
|
|
234
|
+
| `--force` | replace the file when it exists |
|
|
235
|
+
| `--include <directories...>` | the directories of the headers it includes (`-I`) |
|
|
236
|
+
| `--define <macros...>` | the macros defined before it: `NAME` or `NAME=value` (`-D`) |
|
|
237
|
+
| `--own <directories...>` | more directories whose headers are the ones of the library |
|
|
238
|
+
| `--compiler <compiler>` | the C compiler whose preprocessor reads the header: `cc`, `gcc`, `clang`, `cl` (the one of `CC` by default, else `cl` on Windows and `cc` elsewhere) |
|
|
239
|
+
| `--no-check` | do not check the functions against the library |
|
|
240
|
+
|
|
241
|
+
The class is generated for the platform the command runs on. A header does not say which pointers are output
|
|
242
|
+
parameters, nor which functions block: mark them in the file generated (`_Out_`, `{async: true}`). See the guide to
|
|
243
|
+
native libraries ([FFI](https://lakutata.com/guide/FFI), section 9).
|
|
244
|
+
|
|
216
245
|
## Bundles and executables
|
|
217
246
|
|
|
218
247
|
These commands pack the application in the current directory, which must be compiled first (by `tsc`, which emits the
|
|
@@ -250,8 +279,11 @@ declarations, source maps, TypeScript sources and documentation of the packages
|
|
|
250
279
|
sources and objects of the addons (better-sqlite3 holds the source of SQLite) and what built them: the files of node-gyp
|
|
251
280
|
and of CMake in `build/`, the `target` directory of cargo, the static and import libraries. The addons of the
|
|
252
281
|
application itself (`require('./x.node')`, `createRequire(import.meta.url)('./x.node')`) go into the tree too. The
|
|
253
|
-
packages of the framework, and those built on it, are always bundled with the application, whatever they depend on
|
|
254
|
-
that there is one copy of the framework, its classes and its container
|
|
282
|
+
packages of the framework, and those built on it, are always bundled with the application, whatever they depend on or
|
|
283
|
+
hold, so that there is one copy of the framework, its classes and its container. Their native dependencies go into the
|
|
284
|
+
tree, and so do their own addons when they are loaded by a `require` of the file, by a path from their module
|
|
285
|
+
(`path.join(__dirname, 'prebuilds', ...)`) or from their own directory (`node-gyp-build`); an addon loaded in another
|
|
286
|
+
way is named in the warnings.
|
|
255
287
|
Optional dependencies that are not installed (the drivers of stores the application does not use: `peerDependenciesMeta`
|
|
256
288
|
optional, `optionalDependencies`) are left out: loading one throws at run time, as it would without the bundle.
|
|
257
289
|
|
package/README.zh-CN.md
CHANGED
|
@@ -42,7 +42,7 @@ lakutata create --name my-service --template service --with i18n --yes
|
|
|
42
42
|
|
|
43
43
|
官方模板取与工具主版本号相同的最新正式版。下载过的模板会缓存起来:连不上仓库时,使用缓存中这个主版本的最新版本;缓存中也没有时,命令失败。
|
|
44
44
|
|
|
45
|
-
模板包把文件放在它的 `template/` 目录中。有些文件无法以真实的文件名放进 npm 包,所以 `_gitignore`、`_npmrc` 等文件会在创建项目时改名为 `.gitignore`、`.npmrc` 等。文本文件中会填入项目的信息:`{$APP_NAME}`、`{$APP_ID}`、`{$APP_DESC}`、`{$APP_AUTHOR}`、`{$APP_LICENSE}` 和 `{$LAKUTATA_VERSION}
|
|
45
|
+
模板包把文件放在它的 `template/` 目录中。有些文件无法以真实的文件名放进 npm 包,所以 `_gitignore`、`_npmrc` 等文件会在创建项目时改名为 `.gitignore`、`.npmrc` 等。文本文件中会填入项目的信息:`{$APP_NAME}`、`{$APP_ID}`、`{$APP_DESC}`、`{$APP_AUTHOR}`、`{$APP_LICENSE}` 和 `{$LAKUTATA_VERSION}`(框架的次版本,比如框架是 3.1.2 时为 `3.1.0`:补丁版只发布改动过的包,写成 `^{$LAKUTATA_VERSION}` 才能找到 lakutata 的每个包)。
|
|
46
46
|
|
|
47
47
|
模板可以有可选功能,由 `lakutata create --with <features>` 启用(或者由用户在终端里选择):
|
|
48
48
|
|
|
@@ -130,6 +130,26 @@ lakutata create --name my-service --template service --with i18n --yes
|
|
|
130
130
|
|
|
131
131
|
包仍然用 `npm publish -w <package>` 发布(`lakutata add` 创建的包是私有的:请先删掉 `"private": true`)。工具只读写 `lerna.json` 中的 `version`:包来自根目录 `package.json` 的 `workspaces`,或者 `pnpm-workspace.yaml`。它不支持给各个包分别设置版本号(`"version": "independent"` 的 `lerna.json` 无法递增版本),不根据提交生成变更日志,也不缓存构建结果:需要这些功能的工作区可以同时保留 lerna(或 nx、turborepo)。
|
|
132
132
|
|
|
133
|
+
## 原生库
|
|
134
|
+
|
|
135
|
+
### `lakutata ffi generate <header>`
|
|
136
|
+
|
|
137
|
+
根据 C 头文件生成 `@lakutata/ffi` 原生库的类:包括它的声明、每个函数一个带 TypeScript 类型的属性、每个结构体和联合一个接口,整数宏作为常量。头文件先经过 C 编译器的预处理器(`cc -E`;在 Windows 上是 `cl /P`,请在 Visual Studio 的开发者命令提示符里运行);保留库自己的声明(头文件里的,以及它所在目录里其他头文件的,这个目录是系统目录时除外),以及这些声明用到的系统类型。然后用库检查这些函数:库里没有导出的函数和不能调用的函数(参数是 `va_list`)会被去掉。命令会列出去掉了哪些声明,以及原因。工具从项目里加载 `@lakutata/ffi`:请把它装在项目里。
|
|
138
|
+
|
|
139
|
+
| 选项 | |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `--library <name or file>` | 用来检查函数的库:`@Library` 接受的库名,或一个文件;默认和头文件同名(`sqlite3.h` 对应 `sqlite3`;zlib 要写 `--library z`)。库加载不了时,照样生成类,只是不做检查,并给出警告 |
|
|
142
|
+
| `--class-name <name>` | 类的名称,默认是头文件名的 PascalCase 形式 |
|
|
143
|
+
| `--out <file>` | 要写入的文件,默认是当前目录下的 `<类名>.ts` |
|
|
144
|
+
| `--force` | 文件已存在时覆盖它 |
|
|
145
|
+
| `--include <directories...>` | 头文件引用的其他头文件所在的目录(`-I`) |
|
|
146
|
+
| `--define <macros...>` | 在头文件之前定义的宏:`NAME` 或 `NAME=value`(`-D`) |
|
|
147
|
+
| `--own <directories...>` | 其他属于这个库的头文件的目录 |
|
|
148
|
+
| `--compiler <compiler>` | 用来预处理头文件的 C 编译器:`cc`、`gcc`、`clang`、`cl`(默认取 `CC` 环境变量,没有时在 Windows 上是 `cl`,其他系统上是 `cc`) |
|
|
149
|
+
| `--no-check` | 不用库检查函数 |
|
|
150
|
+
|
|
151
|
+
类是针对运行命令的平台生成的。头文件看不出哪些指针是输出参数,也看不出哪些函数会阻塞:请在生成的文件里自己标出来(`_Out_`、`{async: true}`)。见原生库的指南([FFI](https://lakutata.com/zh/guide/FFI) 第 9 节)。
|
|
152
|
+
|
|
133
153
|
## 单文件包和可执行文件
|
|
134
154
|
|
|
135
155
|
这些命令打包当前目录中的应用,应用必须先编译好(用 `tsc`,它会生成装饰器的元数据)。实际的工作由 `@lakutata/builder` 完成:优先用项目中安装的那份(`npm install --save-dev @lakutata/builder`,需要打包应用的模板都是这样做的),否则用随工具一起安装的那份(全局安装工具时不会带上它,因为它只是工具的可选 peer 依赖)。脚本也可以调用它的函数(`bundleProject`、`buildExecutable`)来打包应用。
|
|
@@ -147,7 +167,7 @@ lakutata create --name my-service --template service --with i18n --yes
|
|
|
147
167
|
|
|
148
168
|
单文件包是 CommonJS 模块,因为 Node.js 的可执行文件要运行它们。应用的 ES 模块会被转换,所以不能使用顶层 `await`:请在异步函数里 await。
|
|
149
169
|
|
|
150
|
-
原生插件(`.node` 文件)无法打进单文件包。打包时,工具会找出含有插件的包,以及通过计算出的路径加载插件的包(例如 sharp),把它们连同运行时加载的内容(它们 require 的 JavaScript,以及插件所链接的共享库所在的包,例如 libvips)排除在单文件包之外,按 `node_modules` 的结构放在 `dist/<name>.native/` 中,这个目录随单文件包一起发布。其中是运行时加载的文件:插件和共享库所在包的全部文件;其他包则是它们加载的 JavaScript(逐个跟踪得出)和其他文件(它们读取的数据);以及通过无法跟踪的路径加载自身文件的包(`require(path.join(__dirname, name))`)的全部文件。这些包的类型声明、source map、TypeScript 源码和文档会被排除,插件的 C 和 C++ 源码和目标文件(better-sqlite3 带有 SQLite 的源码),以及构建它们时产生的文件也会被排除:`build/` 中 node-gyp 和 CMake 的文件、cargo 的 `target` 目录、静态库和导入库。应用自己的插件(`require('./x.node')`、`createRequire(import.meta.url)('./x.node')
|
|
170
|
+
原生插件(`.node` 文件)无法打进单文件包。打包时,工具会找出含有插件的包,以及通过计算出的路径加载插件的包(例如 sharp),把它们连同运行时加载的内容(它们 require 的 JavaScript,以及插件所链接的共享库所在的包,例如 libvips)排除在单文件包之外,按 `node_modules` 的结构放在 `dist/<name>.native/` 中,这个目录随单文件包一起发布。其中是运行时加载的文件:插件和共享库所在包的全部文件;其他包则是它们加载的 JavaScript(逐个跟踪得出)和其他文件(它们读取的数据);以及通过无法跟踪的路径加载自身文件的包(`require(path.join(__dirname, name))`)的全部文件。这些包的类型声明、source map、TypeScript 源码和文档会被排除,插件的 C 和 C++ 源码和目标文件(better-sqlite3 带有 SQLite 的源码),以及构建它们时产生的文件也会被排除:`build/` 中 node-gyp 和 CMake 的文件、cargo 的 `target` 目录、静态库和导入库。应用自己的插件(`require('./x.node')`、`createRequire(import.meta.url)('./x.node')`)也放进这个目录。框架的包以及基于框架的包,不论依赖什么、自己是否带有插件,总是和应用一起打进单文件包,这样框架、它的类和它的容器都只有一份。它们的原生依赖放进这个目录;它们自己的插件也放进这个目录,前提是插件通过直接 `require` 插件文件、从模块位置构造的路径(`path.join(__dirname, 'prebuilds', ...)`)或包自身的目录(`node-gyp-build`)加载,用其他方式加载的插件会列在警告中。没有安装的可选依赖(应用没有用到的存储驱动:`peerDependenciesMeta` 中标为 optional 的依赖,以及 `optionalDependencies`)会被排除:运行时加载它会抛出异常,和不打包时一样。
|
|
151
171
|
|
|
152
172
|
打包器不会跟踪应用按路径读取的文件:它单独运行的脚本(worker:`new Worker()`、传入文件的 worker 池;子进程:`fork()`;按路径加载的模块:`require(path.join(__dirname, 'x.js'))`),以及它的数据(`fs.readFileSync(path.join(__dirname, 'defaults.json'))`、存放 schema 的目录)。而且单文件包并不在原来各个模块所在的位置:在 `build/config/` 中的模块里,`path.resolve(__dirname, '../../schema')` 是项目的 `schema/` 目录,但打包到 `dist/` 之后就不是了。工具会在应用的模块和它所用包的模块中,找出所有由模块位置构造出的文件路径,不论写在哪里,并改写这些表达式,让它们指向 `.native` 目录中的文件(`dist/<name>.native/.lakutata/files/`,目录结构与项目中相同,从项目外部链接进来的包放在 `node_modules/` 下):
|
|
153
173
|
|
package/dist/esm/Config.js
CHANGED
|
@@ -8,13 +8,14 @@ import { WorkspaceController } from './controllers/WorkspaceController.js';
|
|
|
8
8
|
import { PackController } from './controllers/PackController.js';
|
|
9
9
|
import { MigrationController } from './controllers/MigrationController.js';
|
|
10
10
|
import { NativeController } from './controllers/NativeController.js';
|
|
11
|
+
import { FFIController } from './controllers/FFIController.js';
|
|
11
12
|
import { I18nController } from './controllers/I18nController.js';
|
|
12
13
|
import { LANGUAGE_VARIABLE, LanguageMiddleware } from './lib/Language.js';
|
|
13
14
|
import { toolPackage } from './lib/Versions.js';
|
|
14
15
|
/**
|
|
15
16
|
* The controllers of the commands of the tool
|
|
16
17
|
*/
|
|
17
|
-
export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, I18nController];
|
|
18
|
+
export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, FFIController, I18nController];
|
|
18
19
|
/**
|
|
19
20
|
* The messages of the tool in the languages besides English (its texts in the code): its help, its prompts
|
|
20
21
|
*/
|
|
@@ -22,7 +23,8 @@ export const CONTROLLERS = [CommandLineController, WorkspaceController, PackCont
|
|
|
22
23
|
* The groups of commands: lakutata package --help lists the commands of the group package
|
|
23
24
|
*/
|
|
24
25
|
export const COMMAND_GROUPS = {
|
|
25
|
-
package: 'Package the compiled application: into one JavaScript file, an executable or its installers'
|
|
26
|
+
package: 'Package the compiled application: into one JavaScript file, an executable or its installers',
|
|
27
|
+
ffi: 'Generate the classes of the native libraries of @lakutata/ffi from their C headers'
|
|
26
28
|
};
|
|
27
29
|
export const MESSAGES = { 'zh-CN': zhCN, 'zh-TW': zhTW };
|
|
28
30
|
/**
|
|
@@ -0,0 +1,54 @@
|
|
|
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 { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { Controller } from 'lakutata/com/entrypoint';
|
|
13
|
+
import { CLIAction } from 'lakutata/decorator/ctrl';
|
|
14
|
+
import { FFIGenerateDTO } from '../dtos/FFIGenerateDTO.js';
|
|
15
|
+
import { loadGenerator } from '../lib/FFI.js';
|
|
16
|
+
import { done, warn } from '../lib/Output.js';
|
|
17
|
+
/**
|
|
18
|
+
* The commands of the native libraries (@lakutata/ffi)
|
|
19
|
+
*/
|
|
20
|
+
export class FFIController extends Controller {
|
|
21
|
+
async generate(input) {
|
|
22
|
+
const generator = await loadGenerator(process.cwd());
|
|
23
|
+
const { className, code, report } = await generator.generateNativeLibrary({
|
|
24
|
+
header: input.header,
|
|
25
|
+
library: input.library,
|
|
26
|
+
className: input.className,
|
|
27
|
+
include: input.include,
|
|
28
|
+
define: input.define,
|
|
29
|
+
own: input.own,
|
|
30
|
+
compiler: input.compiler,
|
|
31
|
+
check: input.check
|
|
32
|
+
});
|
|
33
|
+
const file = path.resolve(input.out ?? `${className}.ts`);
|
|
34
|
+
if (existsSync(file) && !input.force)
|
|
35
|
+
throw new Error(`${path.relative(process.cwd(), file)} exists: give --force to replace it`);
|
|
36
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
37
|
+
writeFileSync(file, code);
|
|
38
|
+
const count = (number, noun) => `${number} ${noun}${number === 1 ? '' : 's'}`;
|
|
39
|
+
done(`Generated ${path.relative(process.cwd(), file)} (${className}): ${count(report.functions, 'function')}, ${count(report.variables, 'variable')}, ${count(report.interfaces, 'interface')}, ${count(report.constants, 'constant')}${report.library ? `, checked against ${report.library}` : ''}`);
|
|
40
|
+
if (!report.library)
|
|
41
|
+
warn(input.check ? `The functions were not checked against the library: ${report.unchecked}; give it with --library` : 'The functions were not checked against the library (--no-check)');
|
|
42
|
+
if (report.skipped.length)
|
|
43
|
+
warn(`Left out ${count(report.skipped.length, 'declaration')} of the library:`);
|
|
44
|
+
for (const skipped of report.skipped)
|
|
45
|
+
warn(` ${skipped.name}${skipped.file ? ` (${path.relative(process.cwd(), skipped.file)}:${skipped.line})` : ''}: ${skipped.reason}`);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
__decorate([
|
|
50
|
+
CLIAction('ffi generate <header>', FFIGenerateDTO, { description: 'Generate the class of a native library from its C header: a property by function, an interface by struct, the constants, with their TypeScript types' }),
|
|
51
|
+
__metadata("design:type", Function),
|
|
52
|
+
__metadata("design:paramtypes", [FFIGenerateDTO]),
|
|
53
|
+
__metadata("design:returntype", Promise)
|
|
54
|
+
], FFIController.prototype, "generate", null);
|
|
@@ -0,0 +1,53 @@
|
|
|
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 { DTO } from 'lakutata';
|
|
11
|
+
import { Expect } from 'lakutata/decorator/dto';
|
|
12
|
+
export class FFIGenerateDTO extends DTO {
|
|
13
|
+
}
|
|
14
|
+
__decorate([
|
|
15
|
+
Expect(DTO.String().required().description('The C header of the library')),
|
|
16
|
+
__metadata("design:type", String)
|
|
17
|
+
], FFIGenerateDTO.prototype, "header", void 0);
|
|
18
|
+
__decorate([
|
|
19
|
+
Expect(DTO.String().optional().description('The library its functions are checked against: a name, as @Library takes it, or a file (the name of the header by default)')),
|
|
20
|
+
__metadata("design:type", String)
|
|
21
|
+
], FFIGenerateDTO.prototype, "library", void 0);
|
|
22
|
+
__decorate([
|
|
23
|
+
Expect(DTO.String().optional().description('The name of the class (the name of the header in PascalCase by default)')),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], FFIGenerateDTO.prototype, "className", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
Expect(DTO.String().optional().description('The TypeScript file written (<class>.ts in the current directory by default)')),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], FFIGenerateDTO.prototype, "out", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
Expect(DTO.Array(DTO.String()).optional().description('The directories of the headers it includes (-I)')),
|
|
32
|
+
__metadata("design:type", Array)
|
|
33
|
+
], FFIGenerateDTO.prototype, "include", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
Expect(DTO.Array(DTO.String()).optional().description('The macros defined before it: NAME or NAME=value (-D)')),
|
|
36
|
+
__metadata("design:type", Array)
|
|
37
|
+
], FFIGenerateDTO.prototype, "define", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
Expect(DTO.Array(DTO.String()).optional().description('More directories whose headers are the ones of the library (the header already is, and so are the headers of its directory unless that is a directory of the system)')),
|
|
40
|
+
__metadata("design:type", Array)
|
|
41
|
+
], FFIGenerateDTO.prototype, "own", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
Expect(DTO.String().optional().description('The C compiler whose preprocessor reads the header: cc, gcc, clang, cl (the one of CC by default, else cl on Windows and cc elsewhere)')),
|
|
44
|
+
__metadata("design:type", String)
|
|
45
|
+
], FFIGenerateDTO.prototype, "compiler", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
Expect(DTO.Boolean().optional().default(true).description('Check the functions and the variables against the library, leaving out the ones it cannot give')),
|
|
48
|
+
__metadata("design:type", Boolean)
|
|
49
|
+
], FFIGenerateDTO.prototype, "check", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
Expect(DTO.Boolean().optional().default(false).description('Replace the file when it exists')),
|
|
52
|
+
__metadata("design:type", Boolean)
|
|
53
|
+
], FFIGenerateDTO.prototype, "force", void 0);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* The generator of a project: the one of the @lakutata/ffi installed in the project (its node_modules, the ones of its
|
|
6
|
+
* workspace), else the one installed with the tool
|
|
7
|
+
* @param project
|
|
8
|
+
*/
|
|
9
|
+
export async function loadGenerator(project) {
|
|
10
|
+
for (const base of [path.join(project, 'package.json'), import.meta.url]) {
|
|
11
|
+
let entry;
|
|
12
|
+
try {
|
|
13
|
+
entry = createRequire(base).resolve('@lakutata/ffi/generator');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
return await import(pathToFileURL(entry).href);
|
|
19
|
+
}
|
|
20
|
+
throw new Error('@lakutata/ffi is not installed: install it in the project (npm install @lakutata/ffi)');
|
|
21
|
+
}
|
|
@@ -17,7 +17,7 @@ import { TemplateCatalog } from './TemplateCatalog.js';
|
|
|
17
17
|
import { runCommand } from './Processes.js';
|
|
18
18
|
import { done, step, warn } from './Output.js';
|
|
19
19
|
import { applyFeatureBlocks, applyFeatureFiles, projectPath } from './TemplateFeatures.js';
|
|
20
|
-
import {
|
|
20
|
+
import { templateVersion } from './Versions.js';
|
|
21
21
|
/**
|
|
22
22
|
* The directories of a template which are not copied
|
|
23
23
|
*/
|
|
@@ -154,7 +154,7 @@ let ProjectCreator = class ProjectCreator extends Component {
|
|
|
154
154
|
$APP_DESC: plan.description,
|
|
155
155
|
$APP_AUTHOR: plan.author,
|
|
156
156
|
$APP_LICENSE: plan.license,
|
|
157
|
-
$LAKUTATA_VERSION:
|
|
157
|
+
$LAKUTATA_VERSION: templateVersion()
|
|
158
158
|
}, new Set(plan.features));
|
|
159
159
|
done('Project information filled');
|
|
160
160
|
if (plan.packageManager !== 'none') {
|
package/dist/esm/lib/Versions.js
CHANGED
|
@@ -15,6 +15,16 @@ export function toolPackage() {
|
|
|
15
15
|
export function frameworkVersion() {
|
|
16
16
|
return require('lakutata/package.json').version;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The version of the packages of lakutata written in the projects created, after a ^: the minor version of the
|
|
20
|
+
* framework (3.1.0 for 3.1.2), since a patch releases only the packages it changes; a prerelease as it is, since all
|
|
21
|
+
* the packages are released with it
|
|
22
|
+
*/
|
|
23
|
+
export function templateVersion() {
|
|
24
|
+
const version = frameworkVersion();
|
|
25
|
+
const release = /^(\d+)\.(\d+)\.\d+$/.exec(version);
|
|
26
|
+
return release ? `${release[1]}.${release[2]}.0` : version;
|
|
27
|
+
}
|
|
18
28
|
/**
|
|
19
29
|
* The latest version of a package on the registry, undefined when it cannot be fetched
|
|
20
30
|
* @param name
|
|
@@ -132,6 +132,26 @@
|
|
|
132
132
|
"target": "要修改的构建目标(默认是唯一的那个)"
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
|
+
"ffi": {
|
|
136
|
+
"description": "根据 C 头文件生成 @lakutata/ffi 原生库的类",
|
|
137
|
+
"generate": {
|
|
138
|
+
"description": "根据 C 头文件生成原生库的类:每个函数一个属性,每个结构体一个接口,加上常量,都带有 TypeScript 类型",
|
|
139
|
+
"arguments": {
|
|
140
|
+
"header": "库的 C 头文件"
|
|
141
|
+
},
|
|
142
|
+
"options": {
|
|
143
|
+
"library": "用来检查函数的库:@Library 接受的库名,或一个文件(默认按头文件的名字)",
|
|
144
|
+
"className": "类的名称(默认是头文件名的 PascalCase 形式)",
|
|
145
|
+
"out": "要写入的 TypeScript 文件(默认是当前目录下的 <类名>.ts)",
|
|
146
|
+
"include": "头文件引用的其他头文件所在的目录(-I)",
|
|
147
|
+
"define": "在头文件之前定义的宏:NAME 或 NAME=value(-D)",
|
|
148
|
+
"own": "其他属于这个库的头文件的目录(头文件本身算库的;它所在的目录不是系统目录时,目录里的头文件也算)",
|
|
149
|
+
"compiler": "用来预处理头文件的 C 编译器:cc、gcc、clang、cl(默认取 CC 环境变量,没有时在 Windows 上是 cl,其他系统上是 cc)",
|
|
150
|
+
"check": "用库检查函数和变量,去掉库里没有的",
|
|
151
|
+
"force": "文件已存在时覆盖它"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
135
155
|
"i18n": {
|
|
136
156
|
"description": "检查各语言的翻译、声明键名的类型,或添加一种语言",
|
|
137
157
|
"arguments": {
|
|
@@ -132,6 +132,26 @@
|
|
|
132
132
|
"target": "要修改的建置目標(預設是唯一的那個)"
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
|
+
"ffi": {
|
|
136
|
+
"description": "根據 C 標頭檔產生 @lakutata/ffi 原生程式庫的類別",
|
|
137
|
+
"generate": {
|
|
138
|
+
"description": "根據 C 標頭檔產生原生程式庫的類別:每個函式一個屬性,每個結構一個介面,加上常數,都帶有 TypeScript 型別",
|
|
139
|
+
"arguments": {
|
|
140
|
+
"header": "程式庫的 C 標頭檔"
|
|
141
|
+
},
|
|
142
|
+
"options": {
|
|
143
|
+
"library": "用來檢查函式的程式庫:@Library 接受的程式庫名稱,或一個檔案(預設依標頭檔的名稱)",
|
|
144
|
+
"className": "類別的名稱(預設是標頭檔名的 PascalCase 形式)",
|
|
145
|
+
"out": "要寫入的 TypeScript 檔案(預設是目前目錄下的 <類別名稱>.ts)",
|
|
146
|
+
"include": "標頭檔引入的其他標頭檔所在的目錄(-I)",
|
|
147
|
+
"define": "在標頭檔之前定義的巨集:NAME 或 NAME=value(-D)",
|
|
148
|
+
"own": "其他屬於這個程式庫的標頭檔的目錄(標頭檔本身算程式庫的;它所在的目錄不是系統目錄時,目錄裡的標頭檔也算)",
|
|
149
|
+
"compiler": "用來前置處理標頭檔的 C 編譯器:cc、gcc、clang、cl(預設取 CC 環境變數,沒有時在 Windows 上是 cl,其他系統上是 cc)",
|
|
150
|
+
"check": "用程式庫檢查函式和變數,去掉程式庫裡沒有的",
|
|
151
|
+
"force": "檔案已存在時覆寫它"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
135
155
|
"i18n": {
|
|
136
156
|
"description": "檢查各語言的翻譯、宣告鍵名的型別,或新增一種語言",
|
|
137
157
|
"arguments": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakutata/cli",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "The command line tool of the lakutata framework: create the projects from the templates, run and version the packages of the workspaces, bundle the applications and make their executables (with @lakutata/builder).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lakutata",
|
|
@@ -39,18 +39,23 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@inquirer/prompts": "^8.7.2",
|
|
41
41
|
"commander": "^15.0.0",
|
|
42
|
-
"lakutata": "3.1.
|
|
42
|
+
"lakutata": "3.1.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@lakutata/builder": "3.1.
|
|
45
|
+
"@lakutata/builder": "~3.1.3",
|
|
46
|
+
"@lakutata/ffi": "~3.1.2"
|
|
46
47
|
},
|
|
47
48
|
"peerDependenciesMeta": {
|
|
48
49
|
"@lakutata/builder": {
|
|
49
50
|
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"@lakutata/ffi": {
|
|
53
|
+
"optional": true
|
|
50
54
|
}
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
|
-
"@lakutata/builder": "3.1.
|
|
57
|
+
"@lakutata/builder": "~3.1.3",
|
|
58
|
+
"@lakutata/ffi": "~3.1.2"
|
|
54
59
|
},
|
|
55
60
|
"engines": {
|
|
56
61
|
"node": "^22.13.0 || >=24.11.0"
|