@airiot/cli 1.0.4 → 1.0.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/bin/iot-scripts.js +51 -2
- package/config/eslint.config.js +13 -1
- package/config/paths.js +2 -0
- package/package.json +2 -1
- package/scripts/bugfix.js +1 -1
- package/scripts/build.js +27 -1
- package/scripts/eslint.js +7 -2
- package/scripts/i18n-scanner.js +0 -4
- package/scripts/install.js +2 -1
- package/scripts/pack.js +2 -1
- package/vite-plugin/index.js +2 -2
- package/vite-plugin/plugin.js +83 -18
package/bin/iot-scripts.js
CHANGED
|
@@ -59,10 +59,59 @@ switch (script) {
|
|
|
59
59
|
}
|
|
60
60
|
case 'deploy': {
|
|
61
61
|
await runScript('build');
|
|
62
|
-
await runScript('
|
|
62
|
+
await runScript('install');
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
|
+
case 'help':
|
|
65
66
|
default:
|
|
66
|
-
|
|
67
|
+
// 使用 chalk 打印美观的使用说明
|
|
68
|
+
const chalk = (await import('chalk')).default;
|
|
69
|
+
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(chalk.bold.cyan('Airiot CLI — 使用说明\n'));
|
|
72
|
+
console.log(`${chalk.green('用法:')} ${chalk.white.bold('air <command> [options]')}`);
|
|
73
|
+
console.log(`${chalk.green('说明:')} 在命令名前传入 Node 参数(例如调试参数),在命令名后传入脚本参数。\n`);
|
|
74
|
+
|
|
75
|
+
const pad = (s, w = 12) => s + ' '.repeat(Math.max(0, w - s.length));
|
|
76
|
+
|
|
77
|
+
const commands = [
|
|
78
|
+
['build', '构建项目,生成可部署包'],
|
|
79
|
+
['start', '本地启动开发服务器(开发模式)'],
|
|
80
|
+
['preview', '预览构建后的产物(静态预览)'],
|
|
81
|
+
['test', '运行测试用例'],
|
|
82
|
+
['upload', '(不推荐)使用老的接口将构建产物上传到目标平台/服务器'],
|
|
83
|
+
['install', '将构建产物安装到目标平台/服务器)'],
|
|
84
|
+
['pack', '打包为发布包'],
|
|
85
|
+
['deploy', '先构建再上传(内部等价于 build -> install'],
|
|
86
|
+
['i18n-scanner', '扫描并提取国际化文本'],
|
|
87
|
+
['i18n-translate', '对提取的文本进行翻译处理'],
|
|
88
|
+
['eslint', '运行 ESLint 检查代码'],
|
|
89
|
+
['bugfix', '使用AI修复Bug'],
|
|
90
|
+
['help', '显示此帮助信息']
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
console.log(chalk.yellow('可用命令:'));
|
|
94
|
+
for (const [cmd, desc] of commands) {
|
|
95
|
+
console.log(' ' + chalk.cyan(pad(cmd)) + ' ' + chalk.dim(desc));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log();
|
|
99
|
+
console.log(chalk.yellow('示例:'));
|
|
100
|
+
console.log(' ' + chalk.cyan('air build') + ' ' + chalk.gray('# 生产构建'));
|
|
101
|
+
console.log(' ' + chalk.cyan('air start') + ' ' + chalk.gray('# 本地开发并热重载'));
|
|
102
|
+
console.log(' ' + chalk.cyan('air deploy') + ' ' + chalk.gray('# 构建并上传'));
|
|
103
|
+
console.log();
|
|
104
|
+
console.log(chalk.yellow('Node 参数:'));
|
|
105
|
+
console.log(' ' + chalk.white('如果需要传递 Node 参数(例如 --fix),请将它们放在命令前面:'));
|
|
106
|
+
console.log(' ' + chalk.cyan('air --fix eslint') + ' ' + chalk.gray('# 启用调试'));
|
|
107
|
+
console.log();
|
|
108
|
+
|
|
109
|
+
if (script) {
|
|
110
|
+
console.log(
|
|
111
|
+
chalk.red('未知的脚本') + ' ' +
|
|
112
|
+
chalk.bold(`"${script}"`) + '。' +
|
|
113
|
+
' ' + chalk.gray('运行') + ' ' + chalk.cyan('air help') + ' ' + chalk.gray('以查看可用命令。')
|
|
114
|
+
);
|
|
115
|
+
}
|
|
67
116
|
break;
|
|
68
117
|
}
|
package/config/eslint.config.js
CHANGED
|
@@ -6,6 +6,17 @@ import reactPlugin from 'eslint-plugin-react';
|
|
|
6
6
|
import importPlugin from 'eslint-plugin-import';
|
|
7
7
|
import paths from './paths.js';
|
|
8
8
|
|
|
9
|
+
let appEsLintConfig = [];
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const module = await import(new URL(`file://${paths.appPath}/eslint.config.js`));
|
|
13
|
+
appEsLintConfig = Array.isArray(module.default) ? module.default : [ module.default ];
|
|
14
|
+
} catch (err) {
|
|
15
|
+
if (err.code !== 'ERR_MODULE_NOT_FOUND') {
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
export default defineConfig([
|
|
10
21
|
// 基础 JS 配置
|
|
11
22
|
{
|
|
@@ -88,5 +99,6 @@ export default defineConfig([
|
|
|
88
99
|
{
|
|
89
100
|
ignores: ['**/node_modules/**', '**/dist/**'],
|
|
90
101
|
rules: {}
|
|
91
|
-
}
|
|
102
|
+
},
|
|
103
|
+
...appEsLintConfig
|
|
92
104
|
]);
|
package/config/paths.js
CHANGED
|
@@ -56,6 +56,7 @@ export default {
|
|
|
56
56
|
dotenv: resolveApp('.env'),
|
|
57
57
|
appPath: resolveApp('.'),
|
|
58
58
|
buildPath: resolveApp('dist'),
|
|
59
|
+
buildEsPath: resolveApp('es'),
|
|
59
60
|
appHtml: resolveDevtool('dist/index.html'),
|
|
60
61
|
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
|
61
62
|
appFrontJs: resolveModule(resolveApp, 'src/front'),
|
|
@@ -73,6 +74,7 @@ export default {
|
|
|
73
74
|
proxySetup: resolveApp('src/setupProxy.js'),
|
|
74
75
|
appNodeModules,
|
|
75
76
|
clientBuildPath: path.resolve(appNodeModules, '@gtiot/iot-client/dist'),
|
|
77
|
+
clientBuildEsPath: path.resolve(appNodeModules, '@gtiot/iot-client/es'),
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
export { moduleFileExtensions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airiot/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "AIRIOT平台前端包管理工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {},
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"license": "ISC",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@eslint/js": "^9.39.0",
|
|
29
|
+
"@npmcli/arborist": "^9.1.6",
|
|
29
30
|
"@vitejs/plugin-legacy": "^7.2.1",
|
|
30
31
|
"@vitejs/plugin-react": "^5.1.0",
|
|
31
32
|
"archiver": "^7.0.1",
|
package/scripts/bugfix.js
CHANGED
|
@@ -9,7 +9,7 @@ const execP = promisify(exec);
|
|
|
9
9
|
|
|
10
10
|
try {
|
|
11
11
|
// 检查 cline 是否可用
|
|
12
|
-
await execP('cline
|
|
12
|
+
await execP('cline version', { maxBuffer: 1024 * 1024 });
|
|
13
13
|
} catch (err) {
|
|
14
14
|
console.error(chalk.red('未检测到 cline CLI,脚本需要 cline 才能运行。'));
|
|
15
15
|
console.log('请按下面任一方式安装并重试:');
|
package/scripts/build.js
CHANGED
|
@@ -29,18 +29,44 @@ import { visualizer } from 'rollup-plugin-visualizer';
|
|
|
29
29
|
if (process.env.ANALYZE === 'true' || analyze) {
|
|
30
30
|
plugins.push(
|
|
31
31
|
visualizer({
|
|
32
|
-
filename:
|
|
32
|
+
filename: '.build_stats.html',
|
|
33
33
|
title: 'Build Analysis',
|
|
34
34
|
open: true,
|
|
35
35
|
})
|
|
36
36
|
)
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
let outDir = 'es'
|
|
40
|
+
for (let i = 0; i < args.length; i++) {
|
|
41
|
+
const a = args[i];
|
|
42
|
+
if (a === '-o' || a === '--out' || a === '--outDir') {
|
|
43
|
+
const v = args[i + 1];
|
|
44
|
+
if (v && !v.startsWith('-')) {
|
|
45
|
+
outDir = v;
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
if (a.startsWith('-o=')) {
|
|
51
|
+
outDir = a.slice(3);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (a.startsWith('--out=')) {
|
|
55
|
+
outDir = a.split('=')[1];
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
if (a.startsWith('--outDir=')) {
|
|
59
|
+
outDir = a.split('=')[1];
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
38
63
|
|
|
39
64
|
await build({
|
|
40
65
|
configFile: false,
|
|
41
66
|
root: paths.appPath,
|
|
42
67
|
base: '',
|
|
43
68
|
build: {
|
|
69
|
+
outDir,
|
|
44
70
|
assetsDir: '',
|
|
45
71
|
assetsInlineLimit: 0,
|
|
46
72
|
cssCodeSplit: true,
|
package/scripts/eslint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ESLint } from 'eslint';
|
|
2
|
-
|
|
2
|
+
import chalk from 'chalk';
|
|
3
3
|
import overrideConfig from '../config/eslint.config.js';
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
@@ -31,5 +31,10 @@ const eslint = new ESLint({
|
|
|
31
31
|
}
|
|
32
32
|
const formatter = await eslint.loadFormatter('stylish');
|
|
33
33
|
const resultText = formatter.format(results);
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
if(resultText) {
|
|
36
|
+
console.log(resultText);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(chalk.green('✓' + ' eslint 检测没有发现任何问题。'))
|
|
39
|
+
}
|
|
35
40
|
})();
|
package/scripts/i18n-scanner.js
CHANGED
|
@@ -6,10 +6,6 @@ import paths from '../config/paths.js';
|
|
|
6
6
|
|
|
7
7
|
const input = [
|
|
8
8
|
'src/**/*.{js,jsx}',
|
|
9
|
-
// Use ! to filter out files or directories
|
|
10
|
-
'!src/**/*.spec.{js,jsx}',
|
|
11
|
-
'!src/i18n/**',
|
|
12
|
-
'!**/node_modules/**',
|
|
13
9
|
].map(p => path.resolve(paths.appPath, p));
|
|
14
10
|
|
|
15
11
|
const options = {
|
package/scripts/install.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import pacote from "pacote";
|
|
4
|
+
import Arborist from '@npmcli/arborist';
|
|
4
5
|
import os from "os";
|
|
5
6
|
import fs from "fs";
|
|
6
7
|
import fetch from "node-fetch";
|
|
@@ -80,7 +81,7 @@ inquirer
|
|
|
80
81
|
}
|
|
81
82
|
const token = `Bearer ${json.accessToken}`;
|
|
82
83
|
|
|
83
|
-
const data = await pacote.tarball(paths.appPath);
|
|
84
|
+
const data = await pacote.tarball("file:" + paths.appPath, { Arborist });
|
|
84
85
|
|
|
85
86
|
const form = new FormData();
|
|
86
87
|
|
package/scripts/pack.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pacote from 'pacote';
|
|
2
2
|
import paths from "../config/paths.js";
|
|
3
|
+
import Arborist from '@npmcli/arborist';
|
|
3
4
|
|
|
4
|
-
pacote.tarball.file(paths.appPath, 'package.tgz').then(data => {
|
|
5
|
+
pacote.tarball.file("file:" + paths.appPath, 'package.tgz', { Arborist }).then(data => {
|
|
5
6
|
console.log('生成包文件成功')
|
|
6
7
|
})
|
package/vite-plugin/index.js
CHANGED
|
@@ -37,11 +37,11 @@ const getPlugins = async (mode) => {
|
|
|
37
37
|
}),
|
|
38
38
|
jsInJsxPlugin(),
|
|
39
39
|
svgInline(),
|
|
40
|
-
airiotPlugin(mode),
|
|
40
|
+
mode != 'build' && airiotPlugin(mode),
|
|
41
41
|
lazyImportPlugin(),
|
|
42
42
|
airiotHtmlPlugin(),
|
|
43
43
|
buildinfoPlugin()
|
|
44
|
-
]
|
|
44
|
+
].filter(Boolean)
|
|
45
45
|
|
|
46
46
|
try {
|
|
47
47
|
const appPlugin = await import(new URL(`file://${paths.appVitePlugin}`));
|
package/vite-plugin/plugin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import paths from '../config/paths.js'
|
|
2
2
|
import { promises as fsPromises } from 'fs';
|
|
3
|
+
import fetch from 'node-fetch';
|
|
3
4
|
import { getBaseUrl, getHost, developConfig } from '../config/envs.js';
|
|
4
5
|
|
|
5
6
|
const jsonData = await fsPromises.readFile(paths.appPackageJson, 'utf-8');
|
|
@@ -8,7 +9,7 @@ const htmlTpl = await fsPromises.readFile(paths.appHtml, 'utf-8');
|
|
|
8
9
|
const appPackage = JSON.parse(jsonData);
|
|
9
10
|
|
|
10
11
|
const findModule = (scripts, packageName) => {
|
|
11
|
-
return scripts.find(s => s.indexOf(packageName) >= 0)
|
|
12
|
+
return scripts.find(s => s.src && s.src.indexOf(packageName) >= 0)
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const host = getHost()
|
|
@@ -24,9 +25,9 @@ let scripts = (developConfig && typeof developConfig.scripts == 'function')
|
|
|
24
25
|
let frontScripts = (developConfig && typeof developConfig.scripts == 'function')
|
|
25
26
|
&& developConfig.scripts(basePath, 'front') || []
|
|
26
27
|
|
|
27
|
-
let scriptsAppend = appPackage.name == '@airiot/core'
|
|
28
28
|
let dllScript = basePath + '/@airiot/dll/dist/iot.min.js',
|
|
29
29
|
dllDevScript = basePath + '/@airiot/dll/dist/iot.js';
|
|
30
|
+
|
|
30
31
|
const filterDll = s => {
|
|
31
32
|
if(s.indexOf("@airiot/dll") >= 0) {
|
|
32
33
|
dllScript = s
|
|
@@ -36,31 +37,95 @@ const filterDll = s => {
|
|
|
36
37
|
return true
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
scripts = scripts.filter(filterDll);
|
|
40
|
-
frontScripts = frontScripts.filter(filterDll);
|
|
40
|
+
scripts = scripts.filter(filterDll).map(s => typeof s === 'string' ? { src: s } : s);
|
|
41
|
+
frontScripts = frontScripts.filter(filterDll).map(s => typeof s === 'string' ? { src: s } : s);
|
|
42
|
+
|
|
43
|
+
const packageNames = [ ...defaultModules, ...(appPackage.iotDependencies||[]) ];
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
const packageScripts = await Promise.all(packageNames.map(async packageName => {
|
|
43
46
|
if(packageName != appPackage.name && !findModule(scripts, packageName)) {
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
const packagePath = basePath + '/' + packageName
|
|
48
|
+
try {
|
|
49
|
+
let pkgUrl = packagePath.replace(/\/+$/,'') + '/package.json'
|
|
50
|
+
pkgUrl = (pkgUrl.startsWith("http://") || pkgUrl.startsWith("https://")) ? pkgUrl : (host + pkgUrl)
|
|
51
|
+
const res = await fetch(pkgUrl)
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
return [ packagePath + '/dist/index.js', packagePath + '/dist/front.js' ]
|
|
54
|
+
}
|
|
55
|
+
const pkg = await res.json()
|
|
56
|
+
|
|
57
|
+
const unwrap = entry => {
|
|
58
|
+
if (!entry) return null
|
|
59
|
+
if (typeof entry === 'string') return entry
|
|
60
|
+
if (typeof entry === 'object') {
|
|
61
|
+
// Prefer standard fields or first available value
|
|
62
|
+
return unwrap(entry.import || entry.module || entry.default || Object.values(entry)[0])
|
|
63
|
+
}
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parseQuery = qs => {
|
|
68
|
+
const obj = {}
|
|
69
|
+
for (const [k, v] of new URLSearchParams(qs)) {
|
|
70
|
+
if (obj[k] === undefined) obj[k] = v
|
|
71
|
+
else if (Array.isArray(obj[k])) obj[k].push(v)
|
|
72
|
+
else obj[k] = [obj[k], v]
|
|
73
|
+
}
|
|
74
|
+
return obj
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const join = (base, rel) => {
|
|
78
|
+
if (!rel) return null
|
|
79
|
+
// allow "./foo.js" or "foo.js" or "/foo.js"
|
|
80
|
+
let url = base.replace(/\/+$/,'') + '/' + rel.replace(/^\.?\/+/,'')
|
|
81
|
+
const qIndex = url.indexOf('?')
|
|
82
|
+
if (qIndex >= 0) {
|
|
83
|
+
const hashIndex = url.indexOf('#', qIndex)
|
|
84
|
+
const qs = url.slice(qIndex + 1, hashIndex >= 0 ? hashIndex : undefined)
|
|
85
|
+
const cleanUrl = hashIndex >= 0 ? url.slice(0, hashIndex) : url.slice(0, qIndex)
|
|
86
|
+
return { src: cleanUrl, ...parseQuery(qs) }
|
|
87
|
+
}
|
|
88
|
+
return { src: url }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let adminRel = null, frontRel = null
|
|
92
|
+
|
|
93
|
+
if (pkg.exports && pkg.exports.airiot) {
|
|
94
|
+
const a = pkg.exports.airiot
|
|
95
|
+
adminRel = unwrap(a.admin || a.index )
|
|
96
|
+
frontRel = unwrap(a.front)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!adminRel && !frontRel) {
|
|
100
|
+
adminRel = pkg.main || null
|
|
101
|
+
frontRel = pkg.iotFront || null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const adminScript = adminRel ? join(packagePath, adminRel) : null
|
|
105
|
+
const frontScript = frontRel ? join(packagePath, frontRel) : null
|
|
106
|
+
|
|
107
|
+
return [ adminScript, frontScript ]
|
|
108
|
+
} catch (e) {
|
|
109
|
+
return [ packagePath + '/dist/index.js', packagePath + '/dist/front.js' ]
|
|
110
|
+
}
|
|
46
111
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
112
|
+
return null
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
packageScripts.forEach(scriptsArr => {
|
|
116
|
+
if(scriptsArr) {
|
|
117
|
+
const [adminScript, frontScript] = scriptsArr
|
|
118
|
+
adminScript && scripts.push(typeof adminScript === "string" ? { src: adminScript } : adminScript)
|
|
119
|
+
frontScript && frontScripts.push(typeof frontScript === "string" ? { src: frontScript } : frontScript)
|
|
120
|
+
}
|
|
121
|
+
});
|
|
57
122
|
|
|
58
123
|
const getHtmlScripts = (html, isAdmin, mode) => {
|
|
59
124
|
const appendScripts = isAdmin ? scripts : frontScripts
|
|
60
125
|
return [
|
|
61
126
|
{ tag: 'script', attrs: { }, children: 'window._r = s => s; window._t1 = s => s', injectTo: 'body-prepend' },
|
|
62
127
|
{ tag: 'script', attrs: { src: mode == 'dev' ? dllDevScript : dllScript }, injectTo: 'body' },
|
|
63
|
-
...appendScripts.map(script => ({ tag: 'script', attrs: {
|
|
128
|
+
...appendScripts.map(script => ({ tag: 'script', attrs: { ...script }, injectTo: 'body' })),
|
|
64
129
|
{ tag: 'script', attrs: { type: 'module' }, children: '__app__.start()', injectTo: 'body' },
|
|
65
130
|
]
|
|
66
131
|
}
|