@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/naslc.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import require$$0$1, { EventEmitter } from 'events';
|
|
4
4
|
import require$$1 from 'child_process';
|
|
5
5
|
import * as sysPath from 'path';
|
|
6
|
-
import sysPath__default from 'path';
|
|
7
|
-
import require$$0$2 from 'fs';
|
|
6
|
+
import sysPath__default, { join } from 'path';
|
|
7
|
+
import require$$0$2, { promises } from 'fs';
|
|
8
8
|
import require$$4 from 'process';
|
|
9
9
|
import require$$0$5 from 'os';
|
|
10
10
|
import require$$1$1 from 'tty';
|
|
@@ -15,7 +15,7 @@ import require$$5 from 'assert';
|
|
|
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
|
|
|
@@ -8304,10 +8304,42 @@ function loadConfig(configDir) {
|
|
|
8304
8304
|
const content = libExports.readFileSync(configPath, 'utf-8');
|
|
8305
8305
|
const config = JSON.parse(content);
|
|
8306
8306
|
// 验证必需字段
|
|
8307
|
-
if (!config.
|
|
8308
|
-
defaultLogger.error('
|
|
8307
|
+
if (!config.srcDir || !config.outDir) {
|
|
8308
|
+
defaultLogger.error('配置文件格式不正确,缺少必需的 srcDir 或 outDir 字段');
|
|
8309
8309
|
return defaultLogger.exit(1);
|
|
8310
8310
|
}
|
|
8311
|
+
// 环境变量优先级高于配置文件
|
|
8312
|
+
// ideVersion: 环境变量优先,如果都没有则报错
|
|
8313
|
+
if (process.env.NASL_IDE_VERSION)
|
|
8314
|
+
config.ideVersion = process.env.NASL_IDE_VERSION;
|
|
8315
|
+
if (!config.ideVersion) {
|
|
8316
|
+
defaultLogger.error('缺少配置 ideVersion,请在配置文件中添加,或在环境变量中配置 NASL_IDE_VERSION');
|
|
8317
|
+
return defaultLogger.exit(1);
|
|
8318
|
+
}
|
|
8319
|
+
// serverBaseURL: 环境变量优先,如果都没有则报错
|
|
8320
|
+
if (process.env.NASL_SERVER_BASE_URL)
|
|
8321
|
+
config.serverBaseURL = process.env.NASL_SERVER_BASE_URL;
|
|
8322
|
+
if (!config.serverBaseURL) {
|
|
8323
|
+
defaultLogger.error('缺少配置 serverBaseURL,请在配置文件中添加,或在环境变量中配置 NASL_SERVER_BASE_URL');
|
|
8324
|
+
return defaultLogger.exit(1);
|
|
8325
|
+
}
|
|
8326
|
+
// 环境变量优先
|
|
8327
|
+
if (process.env.USE_LCAP_OPENAPI !== undefined)
|
|
8328
|
+
config.useOPENAPI = process.env.USE_LCAP_OPENAPI === 'true';
|
|
8329
|
+
if (process.env.LCAP_OPENAPI_AK)
|
|
8330
|
+
config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
|
|
8331
|
+
if (process.env.LCAP_OPENAPI_SK)
|
|
8332
|
+
config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
|
|
8333
|
+
// 如果启用了 OpenAPI,验证必需的 AK 和 SK
|
|
8334
|
+
if (config.useOPENAPI) {
|
|
8335
|
+
if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
|
|
8336
|
+
defaultLogger.error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
8337
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
8338
|
+
- 或将 useOPENAPI 配置为 false
|
|
8339
|
+
`);
|
|
8340
|
+
return defaultLogger.exit(1);
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8311
8343
|
return config;
|
|
8312
8344
|
}
|
|
8313
8345
|
catch (error) {
|
|
@@ -8353,6 +8385,23 @@ function writeFileWithLog(filePath, content, logger) {
|
|
|
8353
8385
|
throw error;
|
|
8354
8386
|
}
|
|
8355
8387
|
}
|
|
8388
|
+
/**
|
|
8389
|
+
* 判断文件路径是否为已知的 NASL 文件类型
|
|
8390
|
+
* 支持的类型:
|
|
8391
|
+
* - 枚举 (enums)
|
|
8392
|
+
* - 实体 (entities)
|
|
8393
|
+
* - 数据结构 (structures)
|
|
8394
|
+
* - 逻辑 (logics)
|
|
8395
|
+
* - 页面 (views)
|
|
8396
|
+
* - 前端/服务端全局变量 (variables)
|
|
8397
|
+
* - 依赖库 (extensions)
|
|
8398
|
+
*/
|
|
8399
|
+
function isKnownFileType(filePath) {
|
|
8400
|
+
return (/\.(enums|entities|structures|logics|views)\.\w+\.(ts|tsx)$/.test(filePath) || // 枚举、实体、数据结构、逻辑、页面
|
|
8401
|
+
/^app\..*\.variables\.ts$/.test(filePath) || // 前端/服务端全局变量 (以app开头,以variables.ts结尾)
|
|
8402
|
+
/^extensions\.\w+\.(ts|tsx)$/.test(filePath) // 依赖库
|
|
8403
|
+
);
|
|
8404
|
+
}
|
|
8356
8405
|
|
|
8357
8406
|
/**
|
|
8358
8407
|
* Create a bound version of a function with a specified `this` context
|
|
@@ -28826,18 +28875,99 @@ function v4(options, buf, offset) {
|
|
|
28826
28875
|
return unsafeStringify(rnds);
|
|
28827
28876
|
}
|
|
28828
28877
|
|
|
28878
|
+
function generateHash(params) {
|
|
28879
|
+
const hash = createHash('sha256');
|
|
28880
|
+
hash.update(JSON.stringify(params));
|
|
28881
|
+
return hash.digest('hex');
|
|
28882
|
+
}
|
|
28883
|
+
async function getCachePathWithTimestamp(hash) {
|
|
28884
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
28885
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
28886
|
+
return join(cacheDir, `${hash}_${Date.now()}.json`);
|
|
28887
|
+
}
|
|
28829
28888
|
/**
|
|
28830
|
-
*
|
|
28889
|
+
* 查找指定 hash 的有效缓存文件
|
|
28890
|
+
* @param hash 缓存标识
|
|
28891
|
+
* @param ttl 缓存有效期(毫秒)
|
|
28892
|
+
* @returns 缓存文件路径和缓存内容,如果没有有效缓存则返回 null
|
|
28831
28893
|
*/
|
|
28832
|
-
function
|
|
28833
|
-
|
|
28894
|
+
async function findValidCache(hash, ttl) {
|
|
28895
|
+
const cacheDir = join(process.cwd(), '.cache');
|
|
28896
|
+
try {
|
|
28897
|
+
await promises.mkdir(cacheDir, { recursive: true });
|
|
28898
|
+
const files = await promises.readdir(cacheDir);
|
|
28899
|
+
const now = Date.now();
|
|
28900
|
+
// 查找所有匹配的缓存文件
|
|
28901
|
+
const matchingFiles = files
|
|
28902
|
+
.filter((file) => file.startsWith(`${hash}_`) && file.endsWith('.json'))
|
|
28903
|
+
.map((file) => {
|
|
28904
|
+
const match = file.match(/_(\d+)\.json$/);
|
|
28905
|
+
if (!match)
|
|
28906
|
+
return null;
|
|
28907
|
+
const timestamp = parseInt(match[1], 10);
|
|
28908
|
+
return {
|
|
28909
|
+
path: join(cacheDir, file),
|
|
28910
|
+
timestamp,
|
|
28911
|
+
isExpired: now - timestamp > ttl,
|
|
28912
|
+
};
|
|
28913
|
+
})
|
|
28914
|
+
.filter((item) => item !== null);
|
|
28915
|
+
// 删除过期的缓存文件
|
|
28916
|
+
const expiredFiles = matchingFiles.filter((item) => item.isExpired);
|
|
28917
|
+
await Promise.all(expiredFiles.map((item) => promises.unlink(item.path).catch(() => { })));
|
|
28918
|
+
// 查找有效的缓存文件(按时间戳降序排序,获取最新的)
|
|
28919
|
+
const validFiles = matchingFiles.filter((item) => !item.isExpired).sort((a, b) => b.timestamp - a.timestamp);
|
|
28920
|
+
if (validFiles.length > 0) {
|
|
28921
|
+
const cacheFile = validFiles[0];
|
|
28922
|
+
const cacheContent = await promises.readFile(cacheFile.path, 'utf-8');
|
|
28923
|
+
const cachedResult = JSON.parse(cacheContent);
|
|
28924
|
+
return { path: cacheFile.path, data: cachedResult };
|
|
28925
|
+
}
|
|
28926
|
+
return null;
|
|
28927
|
+
}
|
|
28928
|
+
catch (error) {
|
|
28929
|
+
return null;
|
|
28930
|
+
}
|
|
28834
28931
|
}
|
|
28835
28932
|
/**
|
|
28836
|
-
*
|
|
28933
|
+
* 高阶函数,为异步函数添加带时间戳的缓存功能
|
|
28934
|
+
* @param fn 需要添加缓存的函数
|
|
28935
|
+
* @param ttl 缓存有效期(毫秒),默认 1 小时
|
|
28936
|
+
* @param getCacheKey 可选的自定义缓存 key 生成函数,如果不提供则使用整个 params
|
|
28937
|
+
* @returns 带缓存的函数版本
|
|
28837
28938
|
*/
|
|
28838
|
-
function
|
|
28839
|
-
|
|
28939
|
+
function cachableWithTTL(fn, ttl = 3600 * 1000, // 默认 1 小时
|
|
28940
|
+
getCacheKey) {
|
|
28941
|
+
return async (params, extraParams) => {
|
|
28942
|
+
const functionName = fn.name;
|
|
28943
|
+
const logPrefix = functionName ? `[${functionName}]` : '[cachableWithTTL]';
|
|
28944
|
+
// 检查是否启用缓存
|
|
28945
|
+
if (process.env.NO_AI_CACHE) {
|
|
28946
|
+
return fn(params, extraParams);
|
|
28947
|
+
}
|
|
28948
|
+
// 使用自定义的 getCacheKey 或默认使用整个 params
|
|
28949
|
+
const cacheKey = getCacheKey ? getCacheKey(params) : params;
|
|
28950
|
+
const hash = generateHash(cacheKey);
|
|
28951
|
+
// 查找有效的缓存
|
|
28952
|
+
const validCache = await findValidCache(hash, ttl);
|
|
28953
|
+
if (validCache) {
|
|
28954
|
+
console.log(`${logPrefix} ✓ 使用缓存`);
|
|
28955
|
+
return validCache.data;
|
|
28956
|
+
}
|
|
28957
|
+
// 调用原函数
|
|
28958
|
+
const result = await fn(params, extraParams);
|
|
28959
|
+
// 保存结果到缓存(带时间戳)
|
|
28960
|
+
try {
|
|
28961
|
+
const cachePath = await getCachePathWithTimestamp(hash);
|
|
28962
|
+
await promises.writeFile(cachePath, JSON.stringify(result, null, 2), 'utf-8');
|
|
28963
|
+
}
|
|
28964
|
+
catch (error) {
|
|
28965
|
+
// 缓存写入失败,但不影响返回结果
|
|
28966
|
+
}
|
|
28967
|
+
return result;
|
|
28968
|
+
};
|
|
28840
28969
|
}
|
|
28970
|
+
|
|
28841
28971
|
/**
|
|
28842
28972
|
* 生成客户端签名信息
|
|
28843
28973
|
* @param appKey AppKey
|
|
@@ -28847,8 +28977,8 @@ function generateSignature(plainText) {
|
|
|
28847
28977
|
function generateClientSignature(appKey, secretKey) {
|
|
28848
28978
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
28849
28979
|
const nonce = v4();
|
|
28850
|
-
const plainText =
|
|
28851
|
-
const signature =
|
|
28980
|
+
const plainText = `${appKey}&${nonce}&${timestamp}&${secretKey}`;
|
|
28981
|
+
const signature = crypto$1.createHash('md5').update(plainText).digest('hex');
|
|
28852
28982
|
return {
|
|
28853
28983
|
appKey,
|
|
28854
28984
|
timestamp,
|
|
@@ -28873,12 +29003,12 @@ function generateBaseHeaders(ak, sk) {
|
|
|
28873
29003
|
};
|
|
28874
29004
|
}
|
|
28875
29005
|
/**
|
|
28876
|
-
* 获取租户的 signInfo
|
|
29006
|
+
* 获取租户的 signInfo(内部实现)
|
|
28877
29007
|
* @param options 服务器选项
|
|
28878
29008
|
* @param baseHeaders 基础认证头
|
|
28879
29009
|
* @returns x-signInfo 值
|
|
28880
29010
|
*/
|
|
28881
|
-
async function
|
|
29011
|
+
async function _fetchSignInfo({ options, baseHeaders }) {
|
|
28882
29012
|
const tenantName = options.tenantName || 'defaulttenant';
|
|
28883
29013
|
const userName = tenantName === 'defaulttenant' ? 'admin' : `${tenantName}-admin`;
|
|
28884
29014
|
const data = {
|
|
@@ -28900,6 +29030,14 @@ async function fetchSignInfo(options, baseHeaders) {
|
|
|
28900
29030
|
return null;
|
|
28901
29031
|
}
|
|
28902
29032
|
}
|
|
29033
|
+
/**
|
|
29034
|
+
* 获取租户的 signInfo(带缓存,1 小时过期)
|
|
29035
|
+
* @param options 服务器选项
|
|
29036
|
+
* @param baseHeaders 基础认证头
|
|
29037
|
+
* @returns x-signInfo 值
|
|
29038
|
+
*/
|
|
29039
|
+
const fetchSignInfo = cachableWithTTL(_fetchSignInfo, 3600 * 1000, // 1 小时
|
|
29040
|
+
(params) => params.options);
|
|
28903
29041
|
/**
|
|
28904
29042
|
* 生成完整的认证头(包含 x-signInfo)
|
|
28905
29043
|
* @param options 服务器选项
|
|
@@ -28909,19 +29047,18 @@ async function generateCompleteHeaders(options) {
|
|
|
28909
29047
|
const headers = {
|
|
28910
29048
|
'Content-Type': 'application/json',
|
|
28911
29049
|
};
|
|
28912
|
-
const OPENAPI_AK = options.OPENAPI_AK || process.env.LCAP_OPENAPI_AK;
|
|
28913
|
-
const OPENAPI_SK = options.OPENAPI_SK || process.env.LCAP_OPENAPI_SK;
|
|
28914
29050
|
// 如果没有提供 ak 和 sk,返回基础 headers
|
|
28915
|
-
if (!OPENAPI_AK || !OPENAPI_SK) {
|
|
28916
|
-
throw new Error(`配置了 useOPENAPI
|
|
28917
|
-
-
|
|
28918
|
-
-
|
|
29051
|
+
if (!options.OPENAPI_AK || !options.OPENAPI_SK) {
|
|
29052
|
+
throw new Error(`配置了 useOPENAPI,但缺少配置 OPENAPI_AK 和 OPENAPI_SK:
|
|
29053
|
+
- 在 nasl.config.json 中配置 OPENAPI_AK 和 OPENAPI_SK,或者在环境变量中配置 LCAP_OPENAPI_AK 和 LCAP_OPENAPI_SK
|
|
29054
|
+
- 或将 useOPENAPI 配置为 false
|
|
29055
|
+
`);
|
|
28919
29056
|
}
|
|
28920
29057
|
// 生成基础认证头
|
|
28921
|
-
const baseHeaders = generateBaseHeaders(OPENAPI_AK, OPENAPI_SK);
|
|
29058
|
+
const baseHeaders = generateBaseHeaders(options.OPENAPI_AK, options.OPENAPI_SK);
|
|
28922
29059
|
Object.assign(headers, baseHeaders);
|
|
28923
29060
|
// 获取 signInfo
|
|
28924
|
-
const signInfo = await fetchSignInfo(options, headers);
|
|
29061
|
+
const signInfo = await fetchSignInfo({ options, baseHeaders: headers });
|
|
28925
29062
|
if (signInfo) {
|
|
28926
29063
|
headers['x-signInfo'] = signInfo;
|
|
28927
29064
|
}
|
|
@@ -28936,10 +29073,9 @@ async function generateCompleteHeaders(options) {
|
|
|
28936
29073
|
async function createAxios(options) {
|
|
28937
29074
|
// 如果需要鉴权,拼接 /openapi/v3/nasl;否则使用原始 URL
|
|
28938
29075
|
const serverBaseURL = new URL(options.serverBaseURL).origin;
|
|
28939
|
-
const
|
|
28940
|
-
const baseURL = useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
29076
|
+
const baseURL = options.useOPENAPI ? `${serverBaseURL}/openapi/v3/nasl` : `${serverBaseURL}/api/v1/nasl`;
|
|
28941
29077
|
// 如果需要鉴权,生成完整的认证头;否则只使用基础 headers
|
|
28942
|
-
const headers = useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
29078
|
+
const headers = options.useOPENAPI ? await generateCompleteHeaders(options) : { 'Content-Type': 'application/json' };
|
|
28943
29079
|
console.log('本次服务调用方为:', baseURL);
|
|
28944
29080
|
const instance = axios.create({
|
|
28945
29081
|
baseURL,
|
|
@@ -29026,6 +29162,56 @@ function createSorter() {
|
|
|
29026
29162
|
};
|
|
29027
29163
|
}
|
|
29028
29164
|
const sorter = createSorter();
|
|
29165
|
+
/**
|
|
29166
|
+
* 验证 variables 文件
|
|
29167
|
+
*/
|
|
29168
|
+
function validateVariablesFile(file, errors) {
|
|
29169
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(declare\s+)?(const|let)\s+(\w+)/gm));
|
|
29170
|
+
if (matchArr.length === 0) {
|
|
29171
|
+
errors.push(`${file.path} (variables文件) 必须包含至少一个 export const 或 export let 声明`);
|
|
29172
|
+
}
|
|
29173
|
+
for (const match of matchArr) {
|
|
29174
|
+
const [, isExport] = match;
|
|
29175
|
+
if (!isExport)
|
|
29176
|
+
errors.push(`${file.path} 所有声明必须使用 export,错误代码:${match[0]}`);
|
|
29177
|
+
}
|
|
29178
|
+
}
|
|
29179
|
+
/**
|
|
29180
|
+
* 验证 extensions 文件
|
|
29181
|
+
*/
|
|
29182
|
+
function validateExtensionsFile(file, errors) {
|
|
29183
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)(namespace)\s+(\w+)/gm));
|
|
29184
|
+
if (matchArr.length === 0) {
|
|
29185
|
+
errors.push(`${file.path} (extensions文件) 必须包含至少一个 export namespace 声明`);
|
|
29186
|
+
}
|
|
29187
|
+
for (const match of matchArr) {
|
|
29188
|
+
const [, isExport] = match;
|
|
29189
|
+
if (!isExport)
|
|
29190
|
+
errors.push(`${file.path} 所有 namespace 必须使用 export,错误代码:${match[0]}`);
|
|
29191
|
+
}
|
|
29192
|
+
}
|
|
29193
|
+
/**
|
|
29194
|
+
* 验证普通文件(枚举、实体、数据结构、逻辑、页面)
|
|
29195
|
+
*/
|
|
29196
|
+
function validateNormalFile(file, nameFromPath, namespace, errors) {
|
|
29197
|
+
const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
|
|
29198
|
+
if (matchArr.length === 0)
|
|
29199
|
+
errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
|
|
29200
|
+
else if (matchArr.length > 1)
|
|
29201
|
+
errors.push(`${file.path} 只能有一个函数或类,错误代码:${matchArr.map((match) => match[0]).join('\n')}
|
|
29202
|
+
你可以将该文件中所有函数合并,或者将其他函数拆到多个文件中。`);
|
|
29203
|
+
for (const match of matchArr) {
|
|
29204
|
+
const [, isExport, isDeclare, type, name] = match;
|
|
29205
|
+
if (!isExport)
|
|
29206
|
+
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
29207
|
+
if (name !== nameFromPath)
|
|
29208
|
+
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
29209
|
+
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
29210
|
+
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
29211
|
+
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
29212
|
+
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
29213
|
+
}
|
|
29214
|
+
}
|
|
29029
29215
|
function composeToString(files) {
|
|
29030
29216
|
files.sort((a, b) => sorter(a.path, b.path));
|
|
29031
29217
|
const errors = [];
|
|
@@ -29033,28 +29219,26 @@ function composeToString(files) {
|
|
|
29033
29219
|
const arr = file.path.split('.');
|
|
29034
29220
|
const ext = arr.pop();
|
|
29035
29221
|
const nameFromPath = arr.pop();
|
|
29036
|
-
|
|
29222
|
+
// 判断是否是特殊文件类型(variables 或 extensions)
|
|
29223
|
+
const isVariablesFile = nameFromPath === 'variables';
|
|
29224
|
+
const isExtensionsFile = arr[0] === 'extensions';
|
|
29225
|
+
const isSpecialFile = isVariablesFile || isExtensionsFile;
|
|
29226
|
+
// 特殊文件的 namespace 包含文件名,普通文件不包含
|
|
29227
|
+
const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
|
|
29037
29228
|
if (['ts', 'tsx'].includes(ext)) {
|
|
29038
|
-
|
|
29039
|
-
|
|
29040
|
-
|
|
29041
|
-
else if (
|
|
29042
|
-
|
|
29043
|
-
|
|
29044
|
-
|
|
29045
|
-
|
|
29046
|
-
if (!isExport)
|
|
29047
|
-
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
29048
|
-
if (name !== nameFromPath)
|
|
29049
|
-
errors.push(`${file.path} 的函数或类名必须与文件名一致,错误代码:${match[0]}`);
|
|
29050
|
-
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
29051
|
-
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
29052
|
-
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
29053
|
-
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
29229
|
+
if (isVariablesFile) {
|
|
29230
|
+
validateVariablesFile(file, errors);
|
|
29231
|
+
}
|
|
29232
|
+
else if (isExtensionsFile) {
|
|
29233
|
+
validateExtensionsFile(file, errors);
|
|
29234
|
+
}
|
|
29235
|
+
else {
|
|
29236
|
+
validateNormalFile(file, nameFromPath, namespace, errors);
|
|
29054
29237
|
}
|
|
29055
29238
|
}
|
|
29056
29239
|
return `namespace ${namespace} {\n${file.content}\n}\n`;
|
|
29057
|
-
})
|
|
29240
|
+
})
|
|
29241
|
+
.join('\n');
|
|
29058
29242
|
if (errors.length > 0) {
|
|
29059
29243
|
throw new Error(errors.join('\n============\n'));
|
|
29060
29244
|
}
|
|
@@ -37615,11 +37799,13 @@ async function scanEntryFiles(projectRoot, patterns, logger) {
|
|
|
37615
37799
|
*/
|
|
37616
37800
|
function extractDeps(content) {
|
|
37617
37801
|
const deps = new Set();
|
|
37618
|
-
//
|
|
37802
|
+
// 预处理:移除注释和字符串字面量,避免它们影响依赖分析
|
|
37619
37803
|
let processedContent = content
|
|
37620
37804
|
.replace(/\/\/.*$/gm, '') // 移除单行注释 // ...
|
|
37621
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
37622
|
-
|
|
37805
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释 /* ... */
|
|
37806
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, '') // 移除单引号字符串 'xxx'
|
|
37807
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, ''); // 移除双引号字符串 "xxx"
|
|
37808
|
+
const allMatches = processedContent.matchAll(/(app|extensions)\.\w+\.[\w.]+/g); // 起码要2个点,支持 app.* 和 extensions.*
|
|
37623
37809
|
for (const match of allMatches) {
|
|
37624
37810
|
let dep = match[0];
|
|
37625
37811
|
if (/Entity$|Entity\./.test(dep)) {
|
|
@@ -37628,6 +37814,14 @@ function extractDeps(content) {
|
|
|
37628
37814
|
else if (/\.enums\.(\w+)\.(\w+)/.test(dep)) {
|
|
37629
37815
|
dep = dep.replace(/\.enums\.(\w+).+$/, '.enums.$1');
|
|
37630
37816
|
}
|
|
37817
|
+
else if (/\.variables\.(\w+)/.test(dep)) {
|
|
37818
|
+
// 处理 variables 依赖:app.backend.variables.xxx -> app.backend.variables
|
|
37819
|
+
dep = dep.replace(/\.variables\..+$/, '.variables');
|
|
37820
|
+
}
|
|
37821
|
+
else if (/^extensions\.\w+\.\w+/.test(dep)) {
|
|
37822
|
+
// 处理 extensions 依赖:extensions.lcap_auth.xxx -> extensions.lcap_auth
|
|
37823
|
+
dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
|
|
37824
|
+
}
|
|
37631
37825
|
else if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
|
|
37632
37826
|
continue; // 应该是写法有问题,跳过让后面的 checker 做检查
|
|
37633
37827
|
}
|
|
@@ -37749,7 +37943,12 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
37749
37943
|
while (filesToProcess.length > 0) {
|
|
37750
37944
|
const pathRelativeToSrc = filesToProcess.shift();
|
|
37751
37945
|
if (processedFileMap.has(pathRelativeToSrc))
|
|
37752
|
-
continue; // 跳过已处理的文件
|
|
37946
|
+
continue; // 跳过已处理的文件
|
|
37947
|
+
// 检查文件类型是否支持
|
|
37948
|
+
if (!isKnownFileType(pathRelativeToSrc)) {
|
|
37949
|
+
logger.warn(`跳过不支持的文件类型: ${pathRelativeToSrc}`);
|
|
37950
|
+
continue;
|
|
37951
|
+
}
|
|
37753
37952
|
try {
|
|
37754
37953
|
const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
|
|
37755
37954
|
result.push(fileInfo);
|
|
@@ -37827,8 +38026,18 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
37827
38026
|
}
|
|
37828
38027
|
logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
|
|
37829
38028
|
}
|
|
38029
|
+
// 统一过滤掉不支持的文件类型
|
|
38030
|
+
const filteredFiles = [];
|
|
38031
|
+
collectedFiles.forEach((file) => {
|
|
38032
|
+
if (isKnownFileType(file.path)) {
|
|
38033
|
+
filteredFiles.push(file);
|
|
38034
|
+
}
|
|
38035
|
+
else {
|
|
38036
|
+
logger.warn(`跳过不支持的文件类型: ${file.path}`);
|
|
38037
|
+
}
|
|
38038
|
+
});
|
|
37830
38039
|
return {
|
|
37831
|
-
collectedFiles,
|
|
38040
|
+
collectedFiles: filteredFiles,
|
|
37832
38041
|
config,
|
|
37833
38042
|
projectRoot,
|
|
37834
38043
|
srcDir,
|
|
@@ -37877,7 +38086,7 @@ async function tryCompile(entry, options) {
|
|
|
37877
38086
|
}
|
|
37878
38087
|
}
|
|
37879
38088
|
|
|
37880
|
-
var version = "0.1.
|
|
38089
|
+
var version = "0.1.19";
|
|
37881
38090
|
var pkg = {
|
|
37882
38091
|
version: version};
|
|
37883
38092
|
|