@nasl/cli 0.1.17 → 0.1.19
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 +115 -11
- package/dist/bin/nasl.mjs +267 -57
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +259 -50
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +266 -56
- package/dist/index.mjs.map +1 -1
- package/out/apis/cachable.d.ts +16 -0
- package/out/apis/cachable.d.ts.map +1 -0
- package/out/apis/cachable.js +142 -0
- package/out/apis/cachable.js.map +1 -0
- package/out/apis/createAxios.d.ts.map +1 -1
- package/out/apis/createAxios.js +2 -3
- package/out/apis/createAxios.js.map +1 -1
- package/out/apis/openapi.d.ts +5 -2
- package/out/apis/openapi.d.ts.map +1 -1
- package/out/apis/openapi.js +21 -25
- package/out/apis/openapi.js.map +1 -1
- package/out/commands/createAppInIde.d.ts.map +1 -1
- package/out/commands/createAppInIde.js +4 -0
- package/out/commands/createAppInIde.js.map +1 -1
- package/out/commands/transform.d.ts.map +1 -1
- package/out/commands/transform.js +1 -4
- package/out/commands/transform.js.map +1 -1
- package/out/services/compose.d.ts +1 -1
- package/out/services/compose.d.ts.map +1 -1
- package/out/services/compose.js +66 -18
- package/out/services/compose.js.map +1 -1
- package/out/services/resolve.d.ts.map +1 -1
- package/out/services/resolve.js +30 -5
- package/out/services/resolve.js.map +1 -1
- package/out/utils/config.d.ts.map +1 -1
- package/out/utils/config.js +34 -2
- package/out/utils/config.js.map +1 -1
- package/out/utils/file.d.ts +12 -0
- package/out/utils/file.d.ts.map +1 -1
- package/out/utils/file.js +18 -0
- package/out/utils/file.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/nasl.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import require$$0$1, { EventEmitter } from 'events';
|
|
4
4
|
import require$$1, { spawn, spawnSync } from 'child_process';
|
|
5
5
|
import * as sysPath from 'path';
|
|
6
|
-
import sysPath__default from 'path';
|
|
7
|
-
import require$$0$2, { unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
6
|
+
import sysPath__default, { join } from 'path';
|
|
7
|
+
import require$$0$2, { promises, unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
8
8
|
import require$$4 from 'process';
|
|
9
9
|
import require$$0$3 from 'constants';
|
|
10
10
|
import stream$4, { Readable } from 'stream';
|
|
@@ -15,14 +15,14 @@ import require$$1$1 from 'tty';
|
|
|
15
15
|
import require$$3 from 'http';
|
|
16
16
|
import require$$4$1 from 'https';
|
|
17
17
|
import require$$0$6 from 'url';
|
|
18
|
-
import crypto$1 from 'crypto';
|
|
18
|
+
import crypto$1, { createHash } from 'crypto';
|
|
19
19
|
import http2 from 'http2';
|
|
20
20
|
import zlib from 'zlib';
|
|
21
21
|
import * as readline from 'readline';
|
|
22
22
|
import { realpath as realpath$1, stat as stat$2, lstat as lstat$1, open, readdir as readdir$1 } from 'fs/promises';
|
|
23
23
|
import { lstat, stat as stat$1, readdir, realpath } from 'node:fs/promises';
|
|
24
24
|
import { Readable as Readable$1 } from 'node:stream';
|
|
25
|
-
import { resolve, join, relative, sep } from 'node:path';
|
|
25
|
+
import { resolve, join as join$1, relative, sep } from 'node:path';
|
|
26
26
|
|
|
27
27
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
28
28
|
|
|
@@ -8338,10 +8338,42 @@ function loadConfig(configDir) {
|
|
|
8338
8338
|
const content = libExports.readFileSync(configPath, 'utf-8');
|
|
8339
8339
|
const config = JSON.parse(content);
|
|
8340
8340
|
// 验证必需字段
|
|
8341
|
-
if (!config.
|
|
8342
|
-
defaultLogger.error('
|
|
8341
|
+
if (!config.srcDir || !config.outDir) {
|
|
8342
|
+
defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
|
|
8343
8343
|
return defaultLogger.exit(1);
|
|
8344
8344
|
}
|
|
8345
|
+
// 环境变量优先级高于配置文件
|
|
8346
|
+
// ideVersion: 环境变量优先,如果都没有则报错
|
|
8347
|
+
if (process.env.NASL_IDE_VERSION)
|
|
8348
|
+
config.ideVersion = process.env.NASL_IDE_VERSION;
|
|
8349
|
+
if (!config.ideVersion) {
|
|
8350
|
+
defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
|
|
8351
|
+
return defaultLogger.exit(1);
|
|
8352
|
+
}
|
|
8353
|
+
// serverBaseURL: 环境变量优先,如果都没有则报错
|
|
8354
|
+
if (process.env.NASL_SERVER_BASE_URL)
|
|
8355
|
+
config.serverBaseURL = process.env.NASL_SERVER_BASE_URL;
|
|
8356
|
+
if (!config.serverBaseURL) {
|
|
8357
|
+
defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
|
|
8358
|
+
return defaultLogger.exit(1);
|
|
8359
|
+
}
|
|
8360
|
+
// 环境变量优先
|
|
8361
|
+
if (process.env.USE_LCAP_OPENAPI !== undefined)
|
|
8362
|
+
config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
|
|
8363
|
+
if (process.env.LCAP_OPENAPI_AK)
|
|
8364
|
+
config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
|
|
8365
|
+
if (process.env.LCAP_OPENAPI_SK)
|
|
8366
|
+
config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
|
|
8367
|
+
// 如果启用了 OpenAPI,验证必需的 AK 和 SK
|
|
8368
|
+
if (config.useOPENAPI) {
|
|
8369
|
+
if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
|
|
8370
|
+
defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
8371
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
8372
|
+
- 或将 useOPENAPI 配置为 false
|
|
8373
|
+
`);
|
|
8374
|
+
return defaultLogger.exit(1);
|
|
8375
|
+
}
|
|
8376
|
+
}
|
|
8345
8377
|
return config;
|
|
8346
8378
|
}
|
|
8347
8379
|
catch (error) {
|
|
@@ -8387,6 +8419,23 @@ function writeFileWithLog(filePath, content, logger) {
|
|
|
8387
8419
|
throw error;
|
|
8388
8420
|
}
|
|
8389
8421
|
}
|
|
8422
|
+
/**
|
|
8423
|
+
* 判断文件路径是否为已知的 NASL 文件类型
|
|
8424
|
+
* 支持的类型:
|
|
8425
|
+
* - 枚举 (enums)
|
|
8426
|
+
* - 实体 (entities)
|
|
8427
|
+
* - 数据结构 (structures)
|
|
8428
|
+
* - 逻辑 (logics)
|
|
8429
|
+
* - 页面 (views)
|
|
8430
|
+
* - 前端/服务端全局变量 (variables)
|
|
8431
|
+
* - 依赖库 (extensions)
|
|
8432
|
+
*/
|
|
8433
|
+
function isKnownFileType(filePath) {
|
|
8434
|
+
return (/\.(enums|entities|structures|logics|views)\.\w+\.(ts|tsx)$/.test(filePath) || // 枚举、实体、数据结构、逻辑、页面
|
|
8435
|
+
/^app\..*\.variables\.ts$/.test(filePath) || // 前端/服务端全局变量 (以app开头,以variables.ts结尾)
|
|
8436
|
+
/^extensions\.\w+\.(ts|tsx)$/.test(filePath) // 依赖库
|
|
8437
|
+
);
|
|
8438
|
+
}
|
|
8390
8439
|
|
|
8391
8440
|
/**
|
|
8392
8441
|
* 初始化命令 - 创建配置文件
|
|
@@ -28895,18 +28944,99 @@ function v4(options, buf, offset) {
|
|
|
28895
28944
|
return unsafeStringify(rnds);
|
|
28896
28945
|
}
|
|
28897
28946
|
|
|
28947
|
+
function generateHash(params) {
|
|
28948
|
+
const hash = createHash('sha256');
|
|
28949
|
+
hash.update(JSON.stringify(params));
|
|
28950
|
+
return hash.digest('hex');
|
|
28951
|
+
}
|
|
28952
|
+
async function getCachePathWithTimestamp(hash) {
|
|
28953
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
28954
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
28955
|
+
return join(cacheDir, `${hash}_${Date.now()}.json`);
|
|
28956
|
+
}
|
|
28898
28957
|
/**
|
|
28899
|
-
*
|
|
28958
|
+
* 查找指定 hash 的有效缓存文件
|
|
28959
|
+
* @param hash 缓存标识
|
|
28960
|
+
* @param ttl 缓存有效期(毫秒)
|
|
28961
|
+
* @returns 缓存文件路径和缓存内容,如果没有有效缓存则返回 null
|
|
28900
28962
|
*/
|
|
28901
|
-
function
|
|
28902
|
-
|
|
28963
|
+
async function findValidCache(hash, ttl) {
|
|
28964
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
28965
|
+
try {
|
|
28966
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
28967
|
+
const files = await promises.readdir(cacheDir);
|
|
28968
|
+
const now = Date.now();
|
|
28969
|
+
// 查找所有匹配的缓存文件
|
|
28970
|
+
const matchingFiles = files
|
|
28971
|
+
.filter((file) => file.startsWith(`${hash}_`) && file.endsWith('.json'))
|
|
28972
|
+
.map((file) => {
|
|
28973
|
+
const match = file.match(/_(\d+)\.json$/);
|
|
28974
|
+
if (!match)
|
|
28975
|
+
return null;
|
|
28976
|
+
const timestamp = parseInt(match[1], 10);
|
|
28977
|
+
return {
|
|
28978
|
+
path: join(cacheDir, file),
|
|
28979
|
+
timestamp,
|
|
28980
|
+
isExpired: now - timestamp > ttl,
|
|
28981
|
+
};
|
|
28982
|
+
})
|
|
28983
|
+
.filter((item) => item !== null);
|
|
28984
|
+
// 删除过期的缓存文件
|
|
28985
|
+
const expiredFiles = matchingFiles.filter((item) => item.isExpired);
|
|
28986
|
+
await Promise.all(expiredFiles.map((item) => promises.unlink(item.path).catch(() => { })));
|
|
28987
|
+
// 查找有效的缓存文件(按时间戳降序排序,获取最新的)
|
|
28988
|
+
const validFiles = matchingFiles.filter((item) => !item.isExpired).sort((a, b) => b.timestamp - a.timestamp);
|
|
28989
|
+
if (validFiles.length > 0) {
|
|
28990
|
+
const cacheFile = validFiles[0];
|
|
28991
|
+
const cacheContent = await promises.readFile(cacheFile.path, 'utf-8');
|
|
28992
|
+
const cachedResult = JSON.parse(cacheContent);
|
|
28993
|
+
return { path: cacheFile.path, data: cachedResult };
|
|
28994
|
+
}
|
|
28995
|
+
return null;
|
|
28996
|
+
}
|
|
28997
|
+
catch (error) {
|
|
28998
|
+
return null;
|
|
28999
|
+
}
|
|
28903
29000
|
}
|
|
28904
29001
|
/**
|
|
28905
|
-
*
|
|
29002
|
+
* 高阶函数,为异步函数添加带时间戳的缓存功能
|
|
29003
|
+
* @param fn 需要添加缓存的函数
|
|
29004
|
+
* @param ttl 缓存有效期(毫秒),默认 1 小时
|
|
29005
|
+
* @param getCacheKey 可选的自定义缓存 key 生成函数,如果不提供则使用整个 params
|
|
29006
|
+
* @returns 带缓存的函数版本
|
|
28906
29007
|
*/
|
|
28907
|
-
function
|
|
28908
|
-
|
|
29008
|
+
function cachableWithTTL(fn, ttl = 3600 * 1000, // 默认 1 小时
|
|
29009
|
+
getCacheKey) {
|
|
29010
|
+
return async (params, extraParams) => {
|
|
29011
|
+
const functionName = fn.name;
|
|
29012
|
+
const logPrefix = functionName ? `[${functionName}]` : '[cachableWithTTL]';
|
|
29013
|
+
// 检查是否启用缓存
|
|
29014
|
+
if (process.env.NO_AI_CACHE) {
|
|
29015
|
+
return fn(params, extraParams);
|
|
29016
|
+
}
|
|
29017
|
+
// 使用自定义的 getCacheKey 或默认使用整个 params
|
|
29018
|
+
const cacheKey = getCacheKey ? getCacheKey(params) : params;
|
|
29019
|
+
const hash = generateHash(cacheKey);
|
|
29020
|
+
// 查找有效的缓存
|
|
29021
|
+
const validCache = await findValidCache(hash, ttl);
|
|
29022
|
+
if (validCache) {
|
|
29023
|
+
console.log(`${logPrefix} ✓ 使用缓存`);
|
|
29024
|
+
return validCache.data;
|
|
29025
|
+
}
|
|
29026
|
+
// 调用原函数
|
|
29027
|
+
const result = await fn(params, extraParams);
|
|
29028
|
+
// 保存结果到缓存(带时间戳)
|
|
29029
|
+
try {
|
|
29030
|
+
const cachePath = await getCachePathWithTimestamp(hash);
|
|
29031
|
+
await promises.writeFile(cachePath, JSON.stringify(result, null, 2), 'utf-8');
|
|
29032
|
+
}
|
|
29033
|
+
catch (error) {
|
|
29034
|
+
// 缓存写入失败,但不影响返回结果
|
|
29035
|
+
}
|
|
29036
|
+
return result;
|
|
29037
|
+
};
|
|
28909
29038
|
}
|
|
29039
|
+
|
|
28910
29040
|
/**
|
|
28911
29041
|
* 生成客户端签名信息
|
|
28912
29042
|
* @param appKey AppKey
|
|
@@ -28916,8 +29046,8 @@ function generateSignature(plainText) {
|
|
|
28916
29046
|
function generateClientSignature(appKey, secretKey) {
|
|
28917
29047
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
28918
29048
|
const nonce = v4();
|
|
28919
|
-
const plainText =
|
|
28920
|
-
const signature =
|
|
29049
|
+
const plainText = `${appKey}&${nonce}&${timestamp}&${secretKey}`;
|
|
29050
|
+
const signature = crypto$1.createHash('md5').update(plainText).digest('hex');
|
|
28921
29051
|
return {
|
|
28922
29052
|
appKey,
|
|
28923
29053
|
timestamp,
|
|
@@ -28942,12 +29072,12 @@ function generateBaseHeaders(ak, sk) {
|
|
|
28942
29072
|
};
|
|
28943
29073
|
}
|
|
28944
29074
|
/**
|
|
28945
|
-
* 获取租户的 signInfo
|
|
29075
|
+
* 获取租户的 signInfo(内部实现)
|
|
28946
29076
|
* @param options 服务器选项
|
|
28947
29077
|
* @param baseHeaders 基础认证头
|
|
28948
29078
|
* @returns x-signInfo 值
|
|
28949
29079
|
*/
|
|
28950
|
-
async function
|
|
29080
|
+
async function _fetchSignInfo({ options, baseHeaders }) {
|
|
28951
29081
|
const tenantName = options.tenantName || 'defaulttenant';
|
|
28952
29082
|
const userName = tenantName === 'defaulttenant' ? 'admin' : `${tenantName}-admin`;
|
|
28953
29083
|
const data = {
|
|
@@ -28969,6 +29099,14 @@ async function fetchSignInfo(options, baseHeaders) {
|
|
|
28969
29099
|
return null;
|
|
28970
29100
|
}
|
|
28971
29101
|
}
|
|
29102
|
+
/**
|
|
29103
|
+
* 获取租户的 signInfo(带缓存,1 小时过期)
|
|
29104
|
+
* @param options 服务器选项
|
|
29105
|
+
* @param baseHeaders 基础认证头
|
|
29106
|
+
* @returns x-signInfo 值
|
|
29107
|
+
*/
|
|
29108
|
+
const fetchSignInfo = cachableWithTTL(_fetchSignInfo, 3600 * 1000, // 1 小时
|
|
29109
|
+
(params) => params.options);
|
|
28972
29110
|
/**
|
|
28973
29111
|
* 生成完整的认证头(包含 x-signInfo)
|
|
28974
29112
|
* @param options 服务器选项
|
|
@@ -28978,19 +29116,18 @@ async function generateCompleteHeaders(options) {
|
|
|
28978
29116
|
const headers = {
|
|
28979
29117
|
'Content-Type': 'application/json',
|
|
28980
29118
|
};
|
|
28981
|
-
const OPENAPI_AK = options.OPENAPI_AK || process.env.LCAP_OPENAPI_AK;
|
|
28982
|
-
const OPENAPI_SK = options.OPENAPI_SK || process.env.LCAP_OPENAPI_SK;
|
|
28983
29119
|
// 如果没有提供 ak 和 sk,返回基础 headers
|
|
28984
|
-
if (!OPENAPI_AK || !OPENAPI_SK) {
|
|
28985
|
-
throw new Error(`配置了 useOPENAPI
|
|
28986
|
-
-
|
|
28987
|
-
-
|
|
29120
|
+
if (!options.OPENAPI_AK || !options.OPENAPI_SK) {
|
|
29121
|
+
throw new Error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
29122
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
29123
|
+
- 或将 useOPENAPI 配置为 false
|
|
29124
|
+
`);
|
|
28988
29125
|
}
|
|
28989
29126
|
// 生成基础认证头
|
|
28990
|
-
const baseHeaders = generateBaseHeaders(OPENAPI_AK, OPENAPI_SK);
|
|
29127
|
+
const baseHeaders = generateBaseHeaders(options.OPENAPI_AK, options.OPENAPI_SK);
|
|
28991
29128
|
Object.assign(headers, baseHeaders);
|
|
28992
29129
|
// 获取 signInfo
|
|
28993
|
-
const signInfo = await fetchSignInfo(options, headers);
|
|
29130
|
+
const signInfo = await fetchSignInfo({ options, baseHeaders: headers });
|
|
28994
29131
|
if (signInfo) {
|
|
28995
29132
|
headers['x-signInfo'] = signInfo;
|
|
28996
29133
|
}
|
|
@@ -29005,10 +29142,9 @@ async function generateCompleteHeaders(options) {
|
|
|
29005
29142
|
async function createAxios(options) {
|
|
29006
29143
|
// 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
|
|
29007
29144
|
const serverBaseURL = new URL(options.serverBaseURL).origin;
|
|
29008
|
-
const
|
|
29009
|
-
const baseURL = useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
29145
|
+
const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
29010
29146
|
// 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
|
|
29011
|
-
const headers = useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
29147
|
+
const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
29012
29148
|
console.log('本次服务调用方为:', baseURL);
|
|
29013
29149
|
const instance = axios.create({
|
|
29014
29150
|
baseURL,
|
|
@@ -29096,7 +29232,7 @@ async function checkApi(fullNaturalTS, options) {
|
|
|
29096
29232
|
async function createAppSyncApi(fullNaturalTS, options) {
|
|
29097
29233
|
const url = new URL(options.serverBaseURL);
|
|
29098
29234
|
const origin = url.origin + '/app-ai-creator';
|
|
29099
|
-
const axios = await createAxios({ serverBaseURL: origin});
|
|
29235
|
+
const axios = await createAxios({ serverBaseURL: origin, ideVersion: options.ideVersion });
|
|
29100
29236
|
try {
|
|
29101
29237
|
const res = await axios.post('/api/createAppSync', {
|
|
29102
29238
|
fullNaturalTS,
|
|
@@ -29153,6 +29289,56 @@ function createSorter() {
|
|
|
29153
29289
|
};
|
|
29154
29290
|
}
|
|
29155
29291
|
const sorter = createSorter();
|
|
29292
|
+
/**
|
|
29293
|
+
* 验证 variables 文件
|
|
29294
|
+
*/
|
|
29295
|
+
function validateVariablesFile(file, errors) {
|
|
29296
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(declare\s+)?(const|let)\s+(\w+)/gm));
|
|
29297
|
+
if (matchArr.length === 0) {
|
|
29298
|
+
errors.push(`${file.path} (variables文件) 必须包含至少一个 export const 或 export let 声明`);
|
|
29299
|
+
}
|
|
29300
|
+
for (const match of matchArr) {
|
|
29301
|
+
const [, isExport] = match;
|
|
29302
|
+
if (!isExport)
|
|
29303
|
+
errors.push(`${file.path} 所有声明必须使用 export,错误代码:${match[0]}`);
|
|
29304
|
+
}
|
|
29305
|
+
}
|
|
29306
|
+
/**
|
|
29307
|
+
* 验证 extensions 文件
|
|
29308
|
+
*/
|
|
29309
|
+
function validateExtensionsFile(file, errors) {
|
|
29310
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(namespace)\s+(\w+)/gm));
|
|
29311
|
+
if (matchArr.length === 0) {
|
|
29312
|
+
errors.push(`${file.path} (extensions文件) 必须包含至少一个 export namespace 声明`);
|
|
29313
|
+
}
|
|
29314
|
+
for (const match of matchArr) {
|
|
29315
|
+
const [, isExport] = match;
|
|
29316
|
+
if (!isExport)
|
|
29317
|
+
errors.push(`${file.path} 所有 namespace 必须使用 export,错误代码:${match[0]}`);
|
|
29318
|
+
}
|
|
29319
|
+
}
|
|
29320
|
+
/**
|
|
29321
|
+
* 验证普通文件(枚举、实体、数据结构、逻辑、页面)
|
|
29322
|
+
*/
|
|
29323
|
+
function validateNormalFile(file, nameFromPath, namespace, errors) {
|
|
29324
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
|
|
29325
|
+
if (matchArr.length === 0)
|
|
29326
|
+
errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
|
|
29327
|
+
else if (matchArr.length > 1)
|
|
29328
|
+
errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
|
|
29329
|
+
你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
|
|
29330
|
+
for (const match of matchArr) {
|
|
29331
|
+
const [, isExport, isDeclare, type, name] = match;
|
|
29332
|
+
if (!isExport)
|
|
29333
|
+
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
29334
|
+
if (name !== nameFromPath)
|
|
29335
|
+
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
29336
|
+
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
29337
|
+
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
29338
|
+
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
29339
|
+
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
29340
|
+
}
|
|
29341
|
+
}
|
|
29156
29342
|
function composeToString(files) {
|
|
29157
29343
|
files.sort((a, b) => sorter(a.path, b.path));
|
|
29158
29344
|
const errors = [];
|
|
@@ -29160,28 +29346,26 @@ function composeToString(files) {
|
|
|
29160
29346
|
const arr = file.path.split('.');
|
|
29161
29347
|
const ext = arr.pop();
|
|
29162
29348
|
const nameFromPath = arr.pop();
|
|
29163
|
-
|
|
29349
|
+
// 判断是否是特殊文件类型(variables 或 extensions)
|
|
29350
|
+
const isVariablesFile = nameFromPath === 'variables';
|
|
29351
|
+
const isExtensionsFile = arr[0] === 'extensions';
|
|
29352
|
+
const isSpecialFile = isVariablesFile || isExtensionsFile;
|
|
29353
|
+
// 特殊文件的 namespace 包含文件名,普通文件不包含
|
|
29354
|
+
const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
|
|
29164
29355
|
if (['ts', 'tsx'].includes(ext)) {
|
|
29165
|
-
|
|
29166
|
-
|
|
29167
|
-
|
|
29168
|
-
else if (
|
|
29169
|
-
|
|
29170
|
-
|
|
29171
|
-
|
|
29172
|
-
|
|
29173
|
-
if (!isExport)
|
|
29174
|
-
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
29175
|
-
if (name !== nameFromPath)
|
|
29176
|
-
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
29177
|
-
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
29178
|
-
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
29179
|
-
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
29180
|
-
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
29356
|
+
if (isVariablesFile) {
|
|
29357
|
+
validateVariablesFile(file, errors);
|
|
29358
|
+
}
|
|
29359
|
+
else if (isExtensionsFile) {
|
|
29360
|
+
validateExtensionsFile(file, errors);
|
|
29361
|
+
}
|
|
29362
|
+
else {
|
|
29363
|
+
validateNormalFile(file, nameFromPath, namespace, errors);
|
|
29181
29364
|
}
|
|
29182
29365
|
}
|
|
29183
29366
|
return `namespace ${namespace} {\n${file.content}\n}\n`;
|
|
29184
|
-
})
|
|
29367
|
+
})
|
|
29368
|
+
.join('\n');
|
|
29185
29369
|
if (errors.length > 0) {
|
|
29186
29370
|
throw new Error(errors.join('\n============\n'));
|
|
29187
29371
|
}
|
|
@@ -37742,11 +37926,13 @@ async function scanEntryFiles(projectRoot, patterns, logger) {
|
|
|
37742
37926
|
*/
|
|
37743
37927
|
function extractDeps(content) {
|
|
37744
37928
|
const deps = new Set();
|
|
37745
|
-
//
|
|
37929
|
+
// 预处理:移除注释和字符串字面量,避免它们影响依赖分析
|
|
37746
37930
|
let processedContent = content
|
|
37747
37931
|
.replace(/\/\/.*$/gm, '') // 移除单行注释 // ...
|
|
37748
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
37749
|
-
|
|
37932
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释 /* ... */
|
|
37933
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, '') // 移除单引号字符串 'xxx'
|
|
37934
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, ''); // 移除双引号字符串 "xxx"
|
|
37935
|
+
const allMatches = processedContent.matchAll(/(app|extensions)\.\w+\.[\w.]+/g); // 起码要2个点,支持 app.* 和 extensions.*
|
|
37750
37936
|
for (const match of allMatches) {
|
|
37751
37937
|
let dep = match[0];
|
|
37752
37938
|
if (/Entity$|Entity\./.test(dep)) {
|
|
@@ -37755,6 +37941,14 @@ function extractDeps(content) {
|
|
|
37755
37941
|
else if (/\.enums\.(\w+)\.(\w+)/.test(dep)) {
|
|
37756
37942
|
dep = dep.replace(/\.enums\.(\w+).+$/, '.enums.$1');
|
|
37757
37943
|
}
|
|
37944
|
+
else if (/\.variables\.(\w+)/.test(dep)) {
|
|
37945
|
+
// 处理 variables 依赖:app.backend.variables.xxx -> app.backend.variables
|
|
37946
|
+
dep = dep.replace(/\.variables\..+$/, '.variables');
|
|
37947
|
+
}
|
|
37948
|
+
else if (/^extensions\.\w+\.\w+/.test(dep)) {
|
|
37949
|
+
// 处理 extensions 依赖:extensions.lcap_auth.xxx -> extensions.lcap_auth
|
|
37950
|
+
dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
|
|
37951
|
+
}
|
|
37758
37952
|
else if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
|
|
37759
37953
|
continue; // 应该是写法有问题,跳过让后面的 checker 做检查
|
|
37760
37954
|
}
|
|
@@ -37876,7 +38070,12 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
37876
38070
|
while (filesToProcess.length > 0) {
|
|
37877
38071
|
const pathRelativeToSrc = filesToProcess.shift();
|
|
37878
38072
|
if (processedFileMap.has(pathRelativeToSrc))
|
|
37879
|
-
continue; // 跳过已处理的文件
|
|
38073
|
+
continue; // 跳过已处理的文件
|
|
38074
|
+
// 检查文件类型是否支持
|
|
38075
|
+
if (!isKnownFileType(pathRelativeToSrc)) {
|
|
38076
|
+
logger.warn(`跳过不支持的文件类型: ${pathRelativeToSrc}`);
|
|
38077
|
+
continue;
|
|
38078
|
+
}
|
|
37880
38079
|
try {
|
|
37881
38080
|
const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
|
|
37882
38081
|
result.push(fileInfo);
|
|
@@ -37956,8 +38155,18 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
37956
38155
|
}
|
|
37957
38156
|
logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
|
|
37958
38157
|
}
|
|
38158
|
+
// 统一过滤掉不支持的文件类型
|
|
38159
|
+
const filteredFiles = [];
|
|
38160
|
+
collectedFiles.forEach((file) => {
|
|
38161
|
+
if (isKnownFileType(file.path)) {
|
|
38162
|
+
filteredFiles.push(file);
|
|
38163
|
+
}
|
|
38164
|
+
else {
|
|
38165
|
+
logger.warn(`跳过不支持的文件类型: ${file.path}`);
|
|
38166
|
+
}
|
|
38167
|
+
});
|
|
37959
38168
|
return {
|
|
37960
|
-
collectedFiles,
|
|
38169
|
+
collectedFiles: filteredFiles,
|
|
37961
38170
|
config,
|
|
37962
38171
|
projectRoot,
|
|
37963
38172
|
srcDir,
|
|
@@ -38305,6 +38514,10 @@ async function createAppInIde(entry, options) {
|
|
|
38305
38514
|
logger.info('开始创建应用在 IDE 中...');
|
|
38306
38515
|
// 收集需要处理的文件
|
|
38307
38516
|
const { collectedFiles, config } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38517
|
+
if (config.useOPENAPI) {
|
|
38518
|
+
logger.error('create-app-in-ide 暂不支持 useOPENAPI 模式');
|
|
38519
|
+
logger.exit(1);
|
|
38520
|
+
}
|
|
38308
38521
|
// 生成 fullNaturalTS
|
|
38309
38522
|
logger.newLine();
|
|
38310
38523
|
logger.info('正在生成 NaturalTS 代码...');
|
|
@@ -38424,10 +38637,7 @@ const transformFns = {
|
|
|
38424
38637
|
jsonContent = libExports.readFileSync(entry, 'utf-8');
|
|
38425
38638
|
logger.info(`读取到 JSON 文件: ${entry}`);
|
|
38426
38639
|
}
|
|
38427
|
-
const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {},
|
|
38428
|
-
serverBaseURL: config.serverBaseURL,
|
|
38429
|
-
ideVersion: config.ideVersion,
|
|
38430
|
-
});
|
|
38640
|
+
const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {}, config);
|
|
38431
38641
|
await Promise.all(files.map(async (file) => {
|
|
38432
38642
|
const outputPath = sysPath.join(projectRoot, config.srcDir, file.path);
|
|
38433
38643
|
await libExports.writeFile(outputPath, file.content);
|
|
@@ -38456,7 +38666,7 @@ async function transform(transformType, entry, options) {
|
|
|
38456
38666
|
await transformFn(entry, options);
|
|
38457
38667
|
}
|
|
38458
38668
|
|
|
38459
|
-
var version = "0.1.
|
|
38669
|
+
var version = "0.1.19";
|
|
38460
38670
|
var pkg = {
|
|
38461
38671
|
version: version};
|
|
38462
38672
|
|
|
@@ -38760,7 +38970,7 @@ class ReaddirpStream extends Readable$1 {
|
|
|
38760
38970
|
let entry;
|
|
38761
38971
|
const basename = this._isDirent ? dirent.name : dirent;
|
|
38762
38972
|
try {
|
|
38763
|
-
const fullPath = resolve(join(path, basename));
|
|
38973
|
+
const fullPath = resolve(join$1(path, basename));
|
|
38764
38974
|
entry = { path: relative(this._root, fullPath), fullPath, basename };
|
|
38765
38975
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
38766
38976
|
}
|