@nasl/cli 0.2.3 → 0.3.1
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 +14 -0
- package/dist/bin/nasl.mjs +714 -133
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +315 -62
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +11851 -11261
- package/dist/index.mjs.map +1 -1
- package/out/apis/compileApi.d.ts +4 -0
- package/out/apis/compileApi.d.ts.map +1 -1
- package/out/apis/compileApi.js +30 -2
- package/out/apis/compileApi.js.map +1 -1
- package/out/apis/dependencyApi.d.ts +29 -0
- package/out/apis/dependencyApi.d.ts.map +1 -0
- package/out/apis/dependencyApi.js +17 -0
- package/out/apis/dependencyApi.js.map +1 -0
- package/out/apis/index.d.ts +1 -0
- package/out/apis/index.d.ts.map +1 -1
- package/out/apis/index.js +1 -0
- package/out/apis/index.js.map +1 -1
- package/out/apis/openapi.d.ts +12 -0
- package/out/apis/openapi.d.ts.map +1 -1
- package/out/apis/openapi.js +15 -0
- package/out/apis/openapi.js.map +1 -1
- package/out/apis/transformApi.d.ts.map +1 -1
- package/out/apis/transformApi.js +2 -0
- package/out/apis/transformApi.js.map +1 -1
- package/out/bin/nasl-doc.d.ts +3 -0
- package/out/bin/nasl-doc.d.ts.map +1 -0
- package/out/bin/nasl-doc.js +36 -0
- package/out/bin/nasl-doc.js.map +1 -0
- package/out/bin/nasl.js +40 -4
- package/out/bin/nasl.js.map +1 -1
- package/out/commands/check.d.ts.map +1 -1
- package/out/commands/check.js +2 -1
- package/out/commands/check.js.map +1 -1
- package/out/commands/compile.d.ts.map +1 -1
- package/out/commands/compile.js +3 -2
- package/out/commands/compile.js.map +1 -1
- package/out/commands/dep.d.ts +4 -2
- package/out/commands/dep.d.ts.map +1 -1
- package/out/commands/dep.js +44 -2
- package/out/commands/dep.js.map +1 -1
- package/out/commands/docCheck.d.ts +18 -0
- package/out/commands/docCheck.d.ts.map +1 -0
- package/out/commands/docCheck.js +172 -0
- package/out/commands/docCheck.js.map +1 -0
- package/out/commands/index.d.ts +2 -0
- package/out/commands/index.d.ts.map +1 -1
- package/out/commands/index.js +2 -0
- package/out/commands/index.js.map +1 -1
- package/out/commands/install.d.ts +9 -0
- package/out/commands/install.d.ts.map +1 -0
- package/out/commands/install.js +123 -0
- package/out/commands/install.js.map +1 -0
- package/out/commands/transform.d.ts +1 -1
- package/out/commands/transform.d.ts.map +1 -1
- package/out/commands/transform.js +31 -2
- package/out/commands/transform.js.map +1 -1
- package/out/constants/nasl-file-types.d.ts.map +1 -1
- package/out/constants/nasl-file-types.js +21 -0
- package/out/constants/nasl-file-types.js.map +1 -1
- package/out/services/compose.d.ts +12 -1
- package/out/services/compose.d.ts.map +1 -1
- package/out/services/compose.js +25 -5
- package/out/services/compose.js.map +1 -1
- package/out/services/resolve.d.ts +18 -0
- package/out/services/resolve.d.ts.map +1 -1
- package/out/services/resolve.js +208 -9
- package/out/services/resolve.js.map +1 -1
- package/out/types/config.d.ts +2 -0
- package/out/types/config.d.ts.map +1 -1
- package/out/types/config.js +1 -0
- package/out/types/config.js.map +1 -1
- package/out/utils/config.d.ts.map +1 -1
- package/out/utils/config.js +2 -0
- package/out/utils/config.js.map +1 -1
- package/package.json +5 -2
package/dist/bin/nasl.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
2
|
import require$$0$1, { EventEmitter } from 'events';
|
|
4
3
|
import require$$1, { spawn, spawnSync } from 'child_process';
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
4
|
+
import * as path$1 from 'path';
|
|
5
|
+
import path__default, { join } from 'path';
|
|
7
6
|
import require$$0$2, { promises, unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
8
7
|
import require$$4 from 'process';
|
|
9
8
|
import require$$0$3 from 'constants';
|
|
@@ -1171,7 +1170,7 @@ function requireCommand () {
|
|
|
1171
1170
|
hasRequiredCommand = 1;
|
|
1172
1171
|
const EventEmitter = require$$0$1.EventEmitter;
|
|
1173
1172
|
const childProcess = require$$1;
|
|
1174
|
-
const path =
|
|
1173
|
+
const path = path__default;
|
|
1175
1174
|
const fs = require$$0$2;
|
|
1176
1175
|
const process = require$$4;
|
|
1177
1176
|
|
|
@@ -4581,7 +4580,7 @@ var hasRequiredUtils$5;
|
|
|
4581
4580
|
function requireUtils$5 () {
|
|
4582
4581
|
if (hasRequiredUtils$5) return utils$7;
|
|
4583
4582
|
hasRequiredUtils$5 = 1;
|
|
4584
|
-
const path =
|
|
4583
|
+
const path = path__default;
|
|
4585
4584
|
|
|
4586
4585
|
// https://github.com/nodejs/node/issues/8987
|
|
4587
4586
|
// https://github.com/libuv/libuv/pull/1088
|
|
@@ -4727,7 +4726,7 @@ function requireStat () {
|
|
|
4727
4726
|
hasRequiredStat = 1;
|
|
4728
4727
|
|
|
4729
4728
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
4730
|
-
const path =
|
|
4729
|
+
const path = path__default;
|
|
4731
4730
|
const u = requireUniversalify().fromPromise;
|
|
4732
4731
|
|
|
4733
4732
|
function getStats (src, dest, opts) {
|
|
@@ -4931,7 +4930,7 @@ function requireCopy$1 () {
|
|
|
4931
4930
|
hasRequiredCopy$1 = 1;
|
|
4932
4931
|
|
|
4933
4932
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
4934
|
-
const path =
|
|
4933
|
+
const path = path__default;
|
|
4935
4934
|
const { mkdirs } = /*@__PURE__*/ requireMkdirs();
|
|
4936
4935
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
4937
4936
|
const { utimesMillis } = /*@__PURE__*/ requireUtimes();
|
|
@@ -5114,7 +5113,7 @@ function requireCopySync () {
|
|
|
5114
5113
|
hasRequiredCopySync = 1;
|
|
5115
5114
|
|
|
5116
5115
|
const fs = requireGracefulFs();
|
|
5117
|
-
const path =
|
|
5116
|
+
const path = path__default;
|
|
5118
5117
|
const mkdirsSync = /*@__PURE__*/ requireMkdirs().mkdirsSync;
|
|
5119
5118
|
const utimesMillisSync = /*@__PURE__*/ requireUtimes().utimesMillisSync;
|
|
5120
5119
|
const stat = /*@__PURE__*/ requireStat();
|
|
@@ -5334,7 +5333,7 @@ function requireEmpty () {
|
|
|
5334
5333
|
|
|
5335
5334
|
const u = requireUniversalify().fromPromise;
|
|
5336
5335
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5337
|
-
const path =
|
|
5336
|
+
const path = path__default;
|
|
5338
5337
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5339
5338
|
const remove = /*@__PURE__*/ requireRemove();
|
|
5340
5339
|
|
|
@@ -5380,7 +5379,7 @@ function requireFile () {
|
|
|
5380
5379
|
hasRequiredFile = 1;
|
|
5381
5380
|
|
|
5382
5381
|
const u = requireUniversalify().fromPromise;
|
|
5383
|
-
const path =
|
|
5382
|
+
const path = path__default;
|
|
5384
5383
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5385
5384
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5386
5385
|
|
|
@@ -5454,7 +5453,7 @@ function requireLink () {
|
|
|
5454
5453
|
hasRequiredLink = 1;
|
|
5455
5454
|
|
|
5456
5455
|
const u = requireUniversalify().fromPromise;
|
|
5457
|
-
const path =
|
|
5456
|
+
const path = path__default;
|
|
5458
5457
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5459
5458
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5460
5459
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
@@ -5525,7 +5524,7 @@ function requireSymlinkPaths () {
|
|
|
5525
5524
|
if (hasRequiredSymlinkPaths) return symlinkPaths_1;
|
|
5526
5525
|
hasRequiredSymlinkPaths = 1;
|
|
5527
5526
|
|
|
5528
|
-
const path =
|
|
5527
|
+
const path = path__default;
|
|
5529
5528
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5530
5529
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
5531
5530
|
|
|
@@ -5677,7 +5676,7 @@ function requireSymlink () {
|
|
|
5677
5676
|
hasRequiredSymlink = 1;
|
|
5678
5677
|
|
|
5679
5678
|
const u = requireUniversalify().fromPromise;
|
|
5680
|
-
const path =
|
|
5679
|
+
const path = path__default;
|
|
5681
5680
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5682
5681
|
|
|
5683
5682
|
const { mkdirs, mkdirsSync } = /*@__PURE__*/ requireMkdirs();
|
|
@@ -5923,7 +5922,7 @@ function requireOutputFile () {
|
|
|
5923
5922
|
|
|
5924
5923
|
const u = requireUniversalify().fromPromise;
|
|
5925
5924
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5926
|
-
const path =
|
|
5925
|
+
const path = path__default;
|
|
5927
5926
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5928
5927
|
const pathExists = /*@__PURE__*/ requirePathExists().pathExists;
|
|
5929
5928
|
|
|
@@ -6025,7 +6024,7 @@ function requireMove$1 () {
|
|
|
6025
6024
|
hasRequiredMove$1 = 1;
|
|
6026
6025
|
|
|
6027
6026
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
6028
|
-
const path =
|
|
6027
|
+
const path = path__default;
|
|
6029
6028
|
const { copy } = /*@__PURE__*/ requireCopy();
|
|
6030
6029
|
const { remove } = /*@__PURE__*/ requireRemove();
|
|
6031
6030
|
const { mkdirp } = /*@__PURE__*/ requireMkdirs();
|
|
@@ -6092,7 +6091,7 @@ function requireMoveSync () {
|
|
|
6092
6091
|
hasRequiredMoveSync = 1;
|
|
6093
6092
|
|
|
6094
6093
|
const fs = requireGracefulFs();
|
|
6095
|
-
const path =
|
|
6094
|
+
const path = path__default;
|
|
6096
6095
|
const copySync = /*@__PURE__*/ requireCopy().copySync;
|
|
6097
6096
|
const removeSync = /*@__PURE__*/ requireRemove().removeSync;
|
|
6098
6097
|
const mkdirpSync = /*@__PURE__*/ requireMkdirs().mkdirpSync;
|
|
@@ -6198,6 +6197,7 @@ const DEFAULT_CONFIG = {
|
|
|
6198
6197
|
ideVersion: '4.4',
|
|
6199
6198
|
srcDir: 'src',
|
|
6200
6199
|
outDir: 'out',
|
|
6200
|
+
tenantName: 'defaulttenant',
|
|
6201
6201
|
};
|
|
6202
6202
|
/**
|
|
6203
6203
|
* 配置文件名称
|
|
@@ -8192,15 +8192,15 @@ var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
|
|
|
8192
8192
|
|
|
8193
8193
|
function fastLogToFile(entry, suffix, content) {
|
|
8194
8194
|
if (process.env.NASL_CLI_LOG_DIR) {
|
|
8195
|
-
const logDir =
|
|
8195
|
+
const logDir = path$1.resolve(process.env.NASL_CLI_LOG_DIR);
|
|
8196
8196
|
libExports.ensureDirSync(logDir);
|
|
8197
8197
|
const timeInDir = (process.env.NASL_CLI_LOG_DIR.match(/\d{6,8}_(\d{6})-/)?.[1] || '00');
|
|
8198
8198
|
const now = dayjs();
|
|
8199
8199
|
const hourInDir = parseInt(timeInDir.slice(0, 2), 10);
|
|
8200
8200
|
// 跨天则累加 24 小时
|
|
8201
8201
|
const hourStr = (now.hour() >= hourInDir ? now.hour() : Math.ceil(hourInDir / 24) * 24 + now.hour()).toString().padStart(2, '0');
|
|
8202
|
-
const entryName =
|
|
8203
|
-
const filePath =
|
|
8202
|
+
const entryName = path$1.basename(Array.isArray(entry) ? entry[0] || '*' : entry).replace(/[\[\]\(\)\{\}\+\-\*\?\|\^\$\#\:\,\\\/\s]+/g, '_');
|
|
8203
|
+
const filePath = path$1.resolve(logDir, `${hourStr}${now.format('mmss_SSS')}-${entryName}-${suffix}`);
|
|
8204
8204
|
libExports.outputFileSync(filePath, content);
|
|
8205
8205
|
}
|
|
8206
8206
|
}
|
|
@@ -8231,16 +8231,16 @@ class BaseLogger {
|
|
|
8231
8231
|
}
|
|
8232
8232
|
debugToFile(topic, ...args) {
|
|
8233
8233
|
if (process.env.DEBUG_TO_FILE) {
|
|
8234
|
-
const baseDir =
|
|
8234
|
+
const baseDir = path$1.join(process.cwd(), '.nasl');
|
|
8235
8235
|
const currentHour = dayjs().format('YYYYMMDD_HH');
|
|
8236
8236
|
const prevHour = dayjs().subtract(1, 'hour').format('YYYYMMDD_HH');
|
|
8237
8237
|
let subdir = `${topic}_${currentHour}`;
|
|
8238
8238
|
const prevSubdir = `${topic}_${prevHour}`;
|
|
8239
8239
|
// 判断如果有上一个小时的 subdir,则自己汇聚到上一个小时的 subdir 中
|
|
8240
|
-
if (libExports.existsSync(
|
|
8240
|
+
if (libExports.existsSync(path$1.join(baseDir, prevSubdir)))
|
|
8241
8241
|
subdir = prevSubdir;
|
|
8242
8242
|
const fileName = `${topic}_${dayjs().format('YYYYMMDD_HHmmss')}.log`;
|
|
8243
|
-
const filePath =
|
|
8243
|
+
const filePath = path$1.join(baseDir, subdir, fileName);
|
|
8244
8244
|
libExports.outputFileSync(filePath, args.join(' '));
|
|
8245
8245
|
}
|
|
8246
8246
|
}
|
|
@@ -8312,11 +8312,11 @@ function findConfigDir(startDir = process.cwd()) {
|
|
|
8312
8312
|
let currentDir = startDir;
|
|
8313
8313
|
// eslint-disable-next-line no-constant-condition
|
|
8314
8314
|
while (true) {
|
|
8315
|
-
const configPath =
|
|
8315
|
+
const configPath = path$1.join(currentDir, CONFIG_FILE_NAME);
|
|
8316
8316
|
if (libExports.existsSync(configPath)) {
|
|
8317
8317
|
return currentDir;
|
|
8318
8318
|
}
|
|
8319
|
-
const parentDir =
|
|
8319
|
+
const parentDir = path$1.dirname(currentDir);
|
|
8320
8320
|
// 已经到达根目录
|
|
8321
8321
|
if (parentDir === currentDir) {
|
|
8322
8322
|
return null;
|
|
@@ -8333,7 +8333,7 @@ function loadConfig(configDir) {
|
|
|
8333
8333
|
defaultLogger.error(`未找到配置文件 ${CONFIG_FILE_NAME},请先运行 nasl-init 初始化配置`);
|
|
8334
8334
|
return defaultLogger.exit(1);
|
|
8335
8335
|
}
|
|
8336
|
-
const configPath =
|
|
8336
|
+
const configPath = path$1.join(dir, CONFIG_FILE_NAME);
|
|
8337
8337
|
try {
|
|
8338
8338
|
const content = libExports.readFileSync(configPath, 'utf-8');
|
|
8339
8339
|
const config = JSON.parse(content);
|
|
@@ -8364,6 +8364,8 @@ function loadConfig(configDir) {
|
|
|
8364
8364
|
config.OPENAPI_AK = process.env.LCAP_OPENAPI_AK;
|
|
8365
8365
|
if (process.env.LCAP_OPENAPI_SK)
|
|
8366
8366
|
config.OPENAPI_SK = process.env.LCAP_OPENAPI_SK;
|
|
8367
|
+
if (process.env.LCAP_TENANT_NAME)
|
|
8368
|
+
config.tenantName = process.env.LCAP_TENANT_NAME;
|
|
8367
8369
|
// 如果启用了 OpenAPI,验证必需的 AK 和 SK
|
|
8368
8370
|
if (config.useOPENAPI) {
|
|
8369
8371
|
if (!config.OPENAPI_AK || !config.OPENAPI_SK) {
|
|
@@ -8410,7 +8412,7 @@ function readFileWithLog(filePath, logger) {
|
|
|
8410
8412
|
*/
|
|
8411
8413
|
function writeFileWithLog(filePath, content, logger) {
|
|
8412
8414
|
try {
|
|
8413
|
-
const dir =
|
|
8415
|
+
const dir = path$1.dirname(filePath);
|
|
8414
8416
|
libExports.ensureDirSync(dir);
|
|
8415
8417
|
libExports.writeFileSync(filePath, content, 'utf-8');
|
|
8416
8418
|
}
|
|
@@ -8425,7 +8427,7 @@ function writeFileWithLog(filePath, content, logger) {
|
|
|
8425
8427
|
*/
|
|
8426
8428
|
function init() {
|
|
8427
8429
|
const cwd = process.cwd();
|
|
8428
|
-
const configPath =
|
|
8430
|
+
const configPath = path$1.join(cwd, CONFIG_FILE_NAME);
|
|
8429
8431
|
// 检查配置文件是否已存在
|
|
8430
8432
|
if (libExports.existsSync(configPath)) {
|
|
8431
8433
|
defaultLogger.warn(`配置文件已存在: ${configPath}`);
|
|
@@ -8439,7 +8441,7 @@ function init() {
|
|
|
8439
8441
|
defaultLogger.info('\n配置内容:');
|
|
8440
8442
|
defaultLogger.info(JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
8441
8443
|
// 创建源代码目录
|
|
8442
|
-
const srcDirPath =
|
|
8444
|
+
const srcDirPath = path$1.join(cwd, DEFAULT_CONFIG.srcDir);
|
|
8443
8445
|
libExports.ensureDirSync(srcDirPath);
|
|
8444
8446
|
defaultLogger.success(`源代码目录已创建: ${srcDirPath}`);
|
|
8445
8447
|
defaultLogger.info('\n提示:');
|
|
@@ -20431,7 +20433,7 @@ function requireMimeTypes () {
|
|
|
20431
20433
|
*/
|
|
20432
20434
|
|
|
20433
20435
|
var db = requireMimeDb();
|
|
20434
|
-
var extname =
|
|
20436
|
+
var extname = path__default.extname;
|
|
20435
20437
|
|
|
20436
20438
|
/**
|
|
20437
20439
|
* Module variables.
|
|
@@ -22172,7 +22174,7 @@ function requireForm_data () {
|
|
|
22172
22174
|
|
|
22173
22175
|
var CombinedStream = requireCombined_stream();
|
|
22174
22176
|
var util = require$$0$4;
|
|
22175
|
-
var path =
|
|
22177
|
+
var path = path__default;
|
|
22176
22178
|
var http = require$$3;
|
|
22177
22179
|
var https = require$$4$1;
|
|
22178
22180
|
var parseUrl = require$$0$6.parse;
|
|
@@ -29116,6 +29118,20 @@ async function generateCompleteHeaders(options) {
|
|
|
29116
29118
|
}
|
|
29117
29119
|
return headers;
|
|
29118
29120
|
}
|
|
29121
|
+
/**
|
|
29122
|
+
* 构建请求 headers
|
|
29123
|
+
* 如果 ServerOptions.useOPENAPI 为 true, 意味着接口内部是走的集群,反之则需要走 openapi 调用(本地或者server的场景,透传options相关配置)
|
|
29124
|
+
*/
|
|
29125
|
+
function buildNaslHeaders(options) {
|
|
29126
|
+
return {
|
|
29127
|
+
'Content-Type': 'text/plain',
|
|
29128
|
+
'X-Server-Base-URL': options.serverBaseURL,
|
|
29129
|
+
...(options.OPENAPI_AK && { 'X-OPENAPI-AK': options.OPENAPI_AK }),
|
|
29130
|
+
...(options.OPENAPI_SK && { 'X-OPENAPI-SK': options.OPENAPI_SK }),
|
|
29131
|
+
...(options.tenantName && { 'X-Tenant-Name': options.tenantName }),
|
|
29132
|
+
...(options.useOPENAPI !== undefined && { 'X-Use-OPENAPI': String(!options.useOPENAPI) }),
|
|
29133
|
+
};
|
|
29134
|
+
}
|
|
29119
29135
|
|
|
29120
29136
|
/**
|
|
29121
29137
|
* 创建 Axios 实例
|
|
@@ -29167,7 +29183,7 @@ async function compileApi(fullNaturalTS, options) {
|
|
|
29167
29183
|
// 示例实现:
|
|
29168
29184
|
const axios = await createAxios(options);
|
|
29169
29185
|
const res = await axios.post(`/compile/tsx?ideVersion=${options.ideVersion}&needAnnotation=true`, fullNaturalTS, {
|
|
29170
|
-
headers:
|
|
29186
|
+
headers: buildNaslHeaders(options),
|
|
29171
29187
|
});
|
|
29172
29188
|
const data = res.data;
|
|
29173
29189
|
const { bundle } = data.result;
|
|
@@ -29199,7 +29215,7 @@ window.backendApp = app;
|
|
|
29199
29215
|
async function checkApi(fullNaturalTS, options) {
|
|
29200
29216
|
const axios = await createAxios(options);
|
|
29201
29217
|
const res = await axios.post(`/check/tsx?ideVersion=${options.ideVersion}`, fullNaturalTS, {
|
|
29202
|
-
headers:
|
|
29218
|
+
headers: buildNaslHeaders(options),
|
|
29203
29219
|
});
|
|
29204
29220
|
const result = res.data.result;
|
|
29205
29221
|
const errors = (result.errors || []);
|
|
@@ -29248,6 +29264,17 @@ async function createAppSyncApi(fullNaturalTS, options) {
|
|
|
29248
29264
|
}
|
|
29249
29265
|
}
|
|
29250
29266
|
|
|
29267
|
+
async function installDependenciesApi(dependenciesJSON, options) {
|
|
29268
|
+
const axios = await createAxios(options);
|
|
29269
|
+
const res = await axios.post(`/load/dependencies?ideVersion=${options.ideVersion}`, dependenciesJSON, {
|
|
29270
|
+
headers: buildNaslHeaders(options),
|
|
29271
|
+
});
|
|
29272
|
+
const data = res.data;
|
|
29273
|
+
if (data.code !== 200)
|
|
29274
|
+
throw new Error(data.message);
|
|
29275
|
+
return data.result;
|
|
29276
|
+
}
|
|
29277
|
+
|
|
29251
29278
|
function createSorter() {
|
|
29252
29279
|
const priorityOrder = ['app.dataSources', 'app.enums', 'app.structures', 'app.logics', 'app.frontendTypes'];
|
|
29253
29280
|
// 获取优先级索引
|
|
@@ -29304,7 +29331,7 @@ function validateExtensionsFile(file, errors) {
|
|
|
29304
29331
|
/**
|
|
29305
29332
|
* 验证普通文件(枚举、实体、数据结构、逻辑、页面)
|
|
29306
29333
|
*/
|
|
29307
|
-
function validateNormalFile(file, nameFromPath, namespace, errors) {
|
|
29334
|
+
function validateNormalFile(file, nameFromPath, namespace, errors, options) {
|
|
29308
29335
|
const matchArr = Array.from(file.content.matchAll(/^(export\s+)?(declare\s+)?(function|class|interface)\s+(\w+)/gm));
|
|
29309
29336
|
if (matchArr.length === 0)
|
|
29310
29337
|
errors.push(`${file.path} 必须有一个函数或类,错误代码:${file.content}`);
|
|
@@ -29316,14 +29343,14 @@ function validateNormalFile(file, nameFromPath, namespace, errors) {
|
|
|
29316
29343
|
if (!isExport)
|
|
29317
29344
|
errors.push(`${file.path} 必须使用 export,错误代码:${match[0]}`);
|
|
29318
29345
|
if (name !== nameFromPath)
|
|
29319
|
-
errors.push(`${file.path}
|
|
29346
|
+
errors.push(`${file.path} 的函数或类名必须与${options?.isDocCheck ? ' path 属性' : '文件名'}一致,错误代码:${match[0]}`);
|
|
29320
29347
|
if (/\.(entities|enums|structures)/.test(namespace) && type !== 'class')
|
|
29321
29348
|
errors.push(`${file.path} 实体、数据结构和枚举只能使用 class 定义,错误代码:${match[0]}`);
|
|
29322
29349
|
if (/\.(logics|views)/.test(namespace) && type !== 'function')
|
|
29323
29350
|
errors.push(`${file.path} 逻辑和页面只能使用 function 定义,错误代码:${match[0]}`);
|
|
29324
29351
|
}
|
|
29325
29352
|
}
|
|
29326
|
-
function composeToString(files) {
|
|
29353
|
+
function composeToString(files, options) {
|
|
29327
29354
|
files.sort((a, b) => sorter(a.path, b.path));
|
|
29328
29355
|
const errors = [];
|
|
29329
29356
|
let currentLine = 1;
|
|
@@ -29338,7 +29365,7 @@ function composeToString(files) {
|
|
|
29338
29365
|
const isExtensionsFile = arr[0] === 'extensions';
|
|
29339
29366
|
const isThemeCss = ext === 'css' && nameFromPath === 'theme';
|
|
29340
29367
|
const isSpecialFile = isVariablesFile || isExtensionsFile || isThemeCss;
|
|
29341
|
-
// 特殊文件的 namespace 包含文件名,普通文件不包含
|
|
29368
|
+
// 特殊文件的 namespace ${包含文件名,普通文件不包含
|
|
29342
29369
|
const namespace = isSpecialFile ? [...arr, nameFromPath].join('.') : arr.join('.');
|
|
29343
29370
|
let content = file.content;
|
|
29344
29371
|
if (['ts', 'tsx'].includes(ext)) {
|
|
@@ -29349,7 +29376,7 @@ function composeToString(files) {
|
|
|
29349
29376
|
validateExtensionsFile(file, errors);
|
|
29350
29377
|
}
|
|
29351
29378
|
else {
|
|
29352
|
-
validateNormalFile(file, nameFromPath, namespace, errors);
|
|
29379
|
+
validateNormalFile(file, nameFromPath, namespace, errors, options);
|
|
29353
29380
|
}
|
|
29354
29381
|
}
|
|
29355
29382
|
else if (isThemeCss) {
|
|
@@ -29370,6 +29397,25 @@ function composeToString(files) {
|
|
|
29370
29397
|
}
|
|
29371
29398
|
return result;
|
|
29372
29399
|
}
|
|
29400
|
+
/**
|
|
29401
|
+
* 将依赖信息拼接到 fullNaturalTS 顶部
|
|
29402
|
+
* @param fullNaturalTS 组合后的 NASL 代码
|
|
29403
|
+
* @param dependencies mergedDependencies 对象或 JSON 字符串
|
|
29404
|
+
* @returns 拼接后的完整内容
|
|
29405
|
+
*/
|
|
29406
|
+
function prependDependencies(fullNaturalTS, dependencies) {
|
|
29407
|
+
if (!dependencies)
|
|
29408
|
+
return fullNaturalTS;
|
|
29409
|
+
const jsonStr = JSON.stringify(dependencies, null, 2);
|
|
29410
|
+
const header = `/*
|
|
29411
|
+
-----------app.dependencies.json7xdhjutehm.start
|
|
29412
|
+
${jsonStr}
|
|
29413
|
+
-----------app.dependencies.json7xdhjutehm.end
|
|
29414
|
+
*/
|
|
29415
|
+
|
|
29416
|
+
`;
|
|
29417
|
+
return header + fullNaturalTS;
|
|
29418
|
+
}
|
|
29373
29419
|
|
|
29374
29420
|
var globby$1 = {exports: {}};
|
|
29375
29421
|
|
|
@@ -29627,7 +29673,7 @@ function requirePath () {
|
|
|
29627
29673
|
Object.defineProperty(path, "__esModule", { value: true });
|
|
29628
29674
|
path.convertPosixPathToPattern = path.convertWindowsPathToPattern = path.convertPathToPattern = path.escapePosixPath = path.escapeWindowsPath = path.escape = path.removeLeadingDotSegment = path.makeAbsolute = path.unixify = void 0;
|
|
29629
29675
|
const os = require$$0$5;
|
|
29630
|
-
const path$1 =
|
|
29676
|
+
const path$1 = path__default;
|
|
29631
29677
|
const IS_WINDOWS_PLATFORM = os.platform() === 'win32';
|
|
29632
29678
|
const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\
|
|
29633
29679
|
/**
|
|
@@ -29892,7 +29938,7 @@ function requireGlobParent () {
|
|
|
29892
29938
|
hasRequiredGlobParent = 1;
|
|
29893
29939
|
|
|
29894
29940
|
var isGlob = requireIsGlob();
|
|
29895
|
-
var pathPosixDirname =
|
|
29941
|
+
var pathPosixDirname = path__default.posix.dirname;
|
|
29896
29942
|
var isWin32 = require$$0$5.platform() === 'win32';
|
|
29897
29943
|
|
|
29898
29944
|
var slash = '/';
|
|
@@ -31464,7 +31510,7 @@ function requireConstants$1 () {
|
|
|
31464
31510
|
if (hasRequiredConstants$1) return constants$1;
|
|
31465
31511
|
hasRequiredConstants$1 = 1;
|
|
31466
31512
|
|
|
31467
|
-
const path =
|
|
31513
|
+
const path = path__default;
|
|
31468
31514
|
const WIN_SLASH = '\\\\/';
|
|
31469
31515
|
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
31470
31516
|
|
|
@@ -31651,7 +31697,7 @@ function requireUtils$2 () {
|
|
|
31651
31697
|
hasRequiredUtils$2 = 1;
|
|
31652
31698
|
(function (exports$1) {
|
|
31653
31699
|
|
|
31654
|
-
const path =
|
|
31700
|
+
const path = path__default;
|
|
31655
31701
|
const win32 = process.platform === 'win32';
|
|
31656
31702
|
const {
|
|
31657
31703
|
REGEX_BACKSLASH,
|
|
@@ -33222,7 +33268,7 @@ function requirePicomatch$1 () {
|
|
|
33222
33268
|
if (hasRequiredPicomatch$1) return picomatch_1;
|
|
33223
33269
|
hasRequiredPicomatch$1 = 1;
|
|
33224
33270
|
|
|
33225
|
-
const path =
|
|
33271
|
+
const path = path__default;
|
|
33226
33272
|
const scan = requireScan();
|
|
33227
33273
|
const parse = requireParse();
|
|
33228
33274
|
const utils = requireUtils$2();
|
|
@@ -34065,7 +34111,7 @@ function requirePattern () {
|
|
|
34065
34111
|
hasRequiredPattern = 1;
|
|
34066
34112
|
Object.defineProperty(pattern, "__esModule", { value: true });
|
|
34067
34113
|
pattern.isAbsolute = pattern.partitionAbsoluteAndRelative = pattern.removeDuplicateSlashes = pattern.matchAny = pattern.convertPatternsToRe = pattern.makeRe = pattern.getPatternParts = pattern.expandBraceExpansion = pattern.expandPatternsWithBraceExpansion = pattern.isAffectDepthOfReadingPattern = pattern.endsWithSlashGlobStar = pattern.hasGlobStar = pattern.getBaseDirectory = pattern.isPatternRelatedToParentDirectory = pattern.getPatternsOutsideCurrentDirectory = pattern.getPatternsInsideCurrentDirectory = pattern.getPositivePatterns = pattern.getNegativePatterns = pattern.isPositivePattern = pattern.isNegativePattern = pattern.convertToNegativePattern = pattern.convertToPositivePattern = pattern.isDynamicPattern = pattern.isStaticPattern = void 0;
|
|
34068
|
-
const path =
|
|
34114
|
+
const path = path__default;
|
|
34069
34115
|
const globParent = requireGlobParent();
|
|
34070
34116
|
const micromatch = requireMicromatch();
|
|
34071
34117
|
const GLOBSTAR = '**';
|
|
@@ -35019,7 +35065,7 @@ function requireSettings$2 () {
|
|
|
35019
35065
|
if (hasRequiredSettings$2) return settings$2;
|
|
35020
35066
|
hasRequiredSettings$2 = 1;
|
|
35021
35067
|
Object.defineProperty(settings$2, "__esModule", { value: true });
|
|
35022
|
-
const path =
|
|
35068
|
+
const path = path__default;
|
|
35023
35069
|
const fsStat = requireOut$3();
|
|
35024
35070
|
const fs = requireFs();
|
|
35025
35071
|
class Settings {
|
|
@@ -35781,7 +35827,7 @@ function requireSettings$1 () {
|
|
|
35781
35827
|
if (hasRequiredSettings$1) return settings$1;
|
|
35782
35828
|
hasRequiredSettings$1 = 1;
|
|
35783
35829
|
Object.defineProperty(settings$1, "__esModule", { value: true });
|
|
35784
|
-
const path =
|
|
35830
|
+
const path = path__default;
|
|
35785
35831
|
const fsScandir = requireOut$2();
|
|
35786
35832
|
class Settings {
|
|
35787
35833
|
constructor(_options = {}) {
|
|
@@ -35857,7 +35903,7 @@ function requireReader () {
|
|
|
35857
35903
|
if (hasRequiredReader) return reader;
|
|
35858
35904
|
hasRequiredReader = 1;
|
|
35859
35905
|
Object.defineProperty(reader, "__esModule", { value: true });
|
|
35860
|
-
const path =
|
|
35906
|
+
const path = path__default;
|
|
35861
35907
|
const fsStat = requireOut$3();
|
|
35862
35908
|
const utils = requireUtils$1();
|
|
35863
35909
|
class Reader {
|
|
@@ -36330,7 +36376,7 @@ function requireProvider () {
|
|
|
36330
36376
|
if (hasRequiredProvider) return provider;
|
|
36331
36377
|
hasRequiredProvider = 1;
|
|
36332
36378
|
Object.defineProperty(provider, "__esModule", { value: true });
|
|
36333
|
-
const path =
|
|
36379
|
+
const path = path__default;
|
|
36334
36380
|
const deep_1 = requireDeep();
|
|
36335
36381
|
const entry_1 = requireEntry$1();
|
|
36336
36382
|
const error_1 = requireError();
|
|
@@ -36770,7 +36816,7 @@ var hasRequiredDirGlob;
|
|
|
36770
36816
|
function requireDirGlob () {
|
|
36771
36817
|
if (hasRequiredDirGlob) return dirGlob.exports;
|
|
36772
36818
|
hasRequiredDirGlob = 1;
|
|
36773
|
-
const path =
|
|
36819
|
+
const path = path__default;
|
|
36774
36820
|
const pathType = requirePathType();
|
|
36775
36821
|
|
|
36776
36822
|
const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0];
|
|
@@ -37520,7 +37566,7 @@ function requireGitignore () {
|
|
|
37520
37566
|
hasRequiredGitignore = 1;
|
|
37521
37567
|
const {promisify} = require$$0$4;
|
|
37522
37568
|
const fs = require$$0$2;
|
|
37523
|
-
const path =
|
|
37569
|
+
const path = path__default;
|
|
37524
37570
|
const fastGlob = requireOut();
|
|
37525
37571
|
const gitIgnore = requireIgnore();
|
|
37526
37572
|
const slash = requireSlash();
|
|
@@ -37952,6 +37998,27 @@ const NASL_FILE_TYPES = [
|
|
|
37952
37998
|
codeRefPattern: 'extensions\\.\\w+(?:\\.\\w+)*',
|
|
37953
37999
|
extension: 'ts',
|
|
37954
38000
|
},
|
|
38001
|
+
{
|
|
38002
|
+
name: 'apis',
|
|
38003
|
+
description: 'API',
|
|
38004
|
+
fileNamePattern: 'apis\\.\\w+\\.ts',
|
|
38005
|
+
codeRefPattern: 'apis\\.\\w+(?:\\.\\w+)*',
|
|
38006
|
+
extension: 'ts',
|
|
38007
|
+
},
|
|
38008
|
+
{
|
|
38009
|
+
name: 'connectors',
|
|
38010
|
+
description: '连接器',
|
|
38011
|
+
fileNamePattern: 'connectors\\.\\w+\\.ts',
|
|
38012
|
+
codeRefPattern: 'connectors\\.\\w+(?:\\.\\w+)*',
|
|
38013
|
+
extension: 'ts',
|
|
38014
|
+
},
|
|
38015
|
+
{
|
|
38016
|
+
name: 'uilibs',
|
|
38017
|
+
description: 'UI 库',
|
|
38018
|
+
fileNamePattern: 'uilibs\\.\\w+\\.ts',
|
|
38019
|
+
codeRefPattern: 'uilibs\\.\\w+(?:\\.\\w+)*',
|
|
38020
|
+
extension: 'ts',
|
|
38021
|
+
},
|
|
37955
38022
|
];
|
|
37956
38023
|
/**
|
|
37957
38024
|
* 获取用于验证文件名的正则表达式数组
|
|
@@ -37984,10 +38051,15 @@ const codeRefPatterns = getCodeRefPatterns();
|
|
|
37984
38051
|
*/
|
|
37985
38052
|
function isKnownFileType(filePath) {
|
|
37986
38053
|
// 提取文件名
|
|
37987
|
-
const fileName =
|
|
38054
|
+
const fileName = path$1.basename(filePath);
|
|
37988
38055
|
return fileNamePatterns.some((pattern) => pattern.test(fileName));
|
|
37989
38056
|
}
|
|
37990
38057
|
|
|
38058
|
+
/** 这些前缀的依赖不再向下递归查找 */
|
|
38059
|
+
const EXTERNAL_DEP_PREFIXES = ['extensions.', 'connectors.', 'apis.', 'uilibs.'];
|
|
38060
|
+
function shouldSkipDependencyTraversal(depPath) {
|
|
38061
|
+
return EXTERNAL_DEP_PREFIXES.some((prefix) => depPath.startsWith(prefix));
|
|
38062
|
+
}
|
|
37991
38063
|
/**
|
|
37992
38064
|
* 扫描目录下的所有 NASL 文件
|
|
37993
38065
|
*/
|
|
@@ -38053,6 +38125,15 @@ function extractDeps(content) {
|
|
|
38053
38125
|
if (/^extensions\.\w+\./.test(dep)) {
|
|
38054
38126
|
dep = dep.replace(/^(extensions\.\w+)\..+$/, '$1');
|
|
38055
38127
|
}
|
|
38128
|
+
if (/^apis\.\w+\./.test(dep)) {
|
|
38129
|
+
dep = dep.replace(/^(apis\.\w+)\..+$/, '$1');
|
|
38130
|
+
}
|
|
38131
|
+
if (/^connectors\.\w+\./.test(dep)) {
|
|
38132
|
+
dep = dep.replace(/^(connectors\.\w+)\..+$/, '$1');
|
|
38133
|
+
}
|
|
38134
|
+
if (/^uilibs\.\w+\./.test(dep)) {
|
|
38135
|
+
dep = dep.replace(/^(uilibs\.\w+)\..+$/, '$1');
|
|
38136
|
+
}
|
|
38056
38137
|
// 跳过不合法的实体引用(实体后还有字段的情况)
|
|
38057
38138
|
if (/app\.dataSources\.\w+\.entities\.\w+\.\w+$/.test(dep)) {
|
|
38058
38139
|
continue;
|
|
@@ -38064,6 +38145,152 @@ function extractDeps(content) {
|
|
|
38064
38145
|
}
|
|
38065
38146
|
return Array.from(deps);
|
|
38066
38147
|
}
|
|
38148
|
+
/**
|
|
38149
|
+
* 从文件内容中提取外部依赖引用(extensions/connectors/apis/uilibs)
|
|
38150
|
+
* 用于构建 externalDependencies
|
|
38151
|
+
*/
|
|
38152
|
+
function extractExternalRefs(content) {
|
|
38153
|
+
const refs = {
|
|
38154
|
+
extensions: new Set(),
|
|
38155
|
+
connectors: new Set(),
|
|
38156
|
+
uilibs: new Set(),
|
|
38157
|
+
apis: new Map(),
|
|
38158
|
+
};
|
|
38159
|
+
const processedContent = content
|
|
38160
|
+
.replace(/\/\/.*$/gm, '')
|
|
38161
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
38162
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, '')
|
|
38163
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, '');
|
|
38164
|
+
// extensions.xxx, connectors.xxx, uilibs.xxx
|
|
38165
|
+
for (const m of processedContent.matchAll(/\b(extensions|connectors|uilibs)\.(\w+)/g)) {
|
|
38166
|
+
const [, kind, name] = m;
|
|
38167
|
+
if (kind === 'extensions')
|
|
38168
|
+
refs.extensions.add(name);
|
|
38169
|
+
else if (kind === 'connectors')
|
|
38170
|
+
refs.connectors.add(name);
|
|
38171
|
+
else
|
|
38172
|
+
refs.uilibs.add(name);
|
|
38173
|
+
}
|
|
38174
|
+
// apis.xxx.interfaces.yyy
|
|
38175
|
+
for (const m of processedContent.matchAll(/\bapis\.(\w+)\.interfaces\.(\w+)/g)) {
|
|
38176
|
+
const [, apiName, ifaceName] = m;
|
|
38177
|
+
if (!refs.apis.has(apiName))
|
|
38178
|
+
refs.apis.set(apiName, new Set());
|
|
38179
|
+
refs.apis.get(apiName).add(ifaceName);
|
|
38180
|
+
}
|
|
38181
|
+
return refs;
|
|
38182
|
+
}
|
|
38183
|
+
function mergeExternalRefs(into, from) {
|
|
38184
|
+
from.extensions.forEach((x) => into.extensions.add(x));
|
|
38185
|
+
from.connectors.forEach((x) => into.connectors.add(x));
|
|
38186
|
+
from.uilibs.forEach((x) => into.uilibs.add(x));
|
|
38187
|
+
from.apis.forEach((interfaces, apiName) => {
|
|
38188
|
+
if (!into.apis.has(apiName))
|
|
38189
|
+
into.apis.set(apiName, new Set());
|
|
38190
|
+
interfaces.forEach((i) => into.apis.get(apiName).add(i));
|
|
38191
|
+
});
|
|
38192
|
+
}
|
|
38193
|
+
/**
|
|
38194
|
+
* 加载 app.dependencies.json(不存在则返回空对象)
|
|
38195
|
+
*/
|
|
38196
|
+
function loadAppDependencies(srcDir) {
|
|
38197
|
+
const p = path$1.join(srcDir, 'app.dependencies.json');
|
|
38198
|
+
if (!libExports.existsSync(p))
|
|
38199
|
+
return {};
|
|
38200
|
+
try {
|
|
38201
|
+
return libExports.readJsonSync(p);
|
|
38202
|
+
}
|
|
38203
|
+
catch {
|
|
38204
|
+
return {};
|
|
38205
|
+
}
|
|
38206
|
+
}
|
|
38207
|
+
/**
|
|
38208
|
+
* 加载并合并 app.dependencies.json 与 potential.dependencies.json
|
|
38209
|
+
* 用于版本号查找:先查 app,再查 potential
|
|
38210
|
+
*/
|
|
38211
|
+
function loadMergedDependencies(srcDir) {
|
|
38212
|
+
const load = (file) => {
|
|
38213
|
+
const p = path$1.join(srcDir, file);
|
|
38214
|
+
if (!libExports.existsSync(p))
|
|
38215
|
+
return null;
|
|
38216
|
+
try {
|
|
38217
|
+
return libExports.readJsonSync(p);
|
|
38218
|
+
}
|
|
38219
|
+
catch {
|
|
38220
|
+
return null;
|
|
38221
|
+
}
|
|
38222
|
+
};
|
|
38223
|
+
const app = load('app.dependencies.json') ?? {};
|
|
38224
|
+
const potential = load('potential.dependencies.json') ?? {};
|
|
38225
|
+
const merged = {};
|
|
38226
|
+
for (const key of ['extensions', 'uilibs', 'connectors', 'apis']) {
|
|
38227
|
+
merged[key] = { ...(potential[key] ?? {}), ...(app[key] ?? {}) };
|
|
38228
|
+
}
|
|
38229
|
+
return merged;
|
|
38230
|
+
}
|
|
38231
|
+
/**
|
|
38232
|
+
* 将 externalDependencies 补充到 app.dependencies.json(只增不减)
|
|
38233
|
+
*/
|
|
38234
|
+
function mergeDependenciesIntoApp(existing, incoming) {
|
|
38235
|
+
const result = {};
|
|
38236
|
+
for (const key of ['extensions', 'uilibs', 'connectors']) {
|
|
38237
|
+
const existingRecord = existing[key] ?? {};
|
|
38238
|
+
const incomingRecord = incoming[key] ?? {};
|
|
38239
|
+
result[key] = { ...incomingRecord, ...existingRecord };
|
|
38240
|
+
}
|
|
38241
|
+
result.apis = {};
|
|
38242
|
+
if (existing.apis || incoming.apis) {
|
|
38243
|
+
const allApiNames = new Set([...Object.keys(existing.apis ?? {}), ...Object.keys(incoming.apis ?? {})]);
|
|
38244
|
+
for (const apiName of allApiNames) {
|
|
38245
|
+
const existingInterfaces = existing.apis?.[apiName] ?? [];
|
|
38246
|
+
const incomingInterfaces = incoming.apis?.[apiName] ?? [];
|
|
38247
|
+
result.apis[apiName] = [...new Set([...existingInterfaces, ...incomingInterfaces])].sort();
|
|
38248
|
+
}
|
|
38249
|
+
}
|
|
38250
|
+
return result;
|
|
38251
|
+
}
|
|
38252
|
+
/**
|
|
38253
|
+
* 根据收集的外部引用和版本配置,构建 DependenciesJSON
|
|
38254
|
+
* extensions/connectors/uilibs 需要版本号,apis 只需 interfaces 数组
|
|
38255
|
+
*/
|
|
38256
|
+
function buildExternalDependencies(refs, mergedDeps, logger) {
|
|
38257
|
+
const result = {};
|
|
38258
|
+
const errors = [];
|
|
38259
|
+
for (const name of refs.extensions) {
|
|
38260
|
+
const ver = mergedDeps.extensions?.[name];
|
|
38261
|
+
if (!ver) {
|
|
38262
|
+
errors.push(`extensions.${name} 未在 app.dependencies.json 或 potential.dependencies.json 中找到版本号`);
|
|
38263
|
+
}
|
|
38264
|
+
else {
|
|
38265
|
+
(result.extensions ?? (result.extensions = {}))[name] = ver;
|
|
38266
|
+
}
|
|
38267
|
+
}
|
|
38268
|
+
for (const name of refs.connectors) {
|
|
38269
|
+
const ver = mergedDeps.connectors?.[name];
|
|
38270
|
+
if (!ver) {
|
|
38271
|
+
errors.push(`connectors.${name} 未在 app.dependencies.json 或 potential.dependencies.json 中找到版本号`);
|
|
38272
|
+
}
|
|
38273
|
+
else {
|
|
38274
|
+
(result.connectors ?? (result.connectors = {}))[name] = ver;
|
|
38275
|
+
}
|
|
38276
|
+
}
|
|
38277
|
+
for (const name of refs.uilibs) {
|
|
38278
|
+
const ver = mergedDeps.uilibs?.[name];
|
|
38279
|
+
if (!ver) {
|
|
38280
|
+
errors.push(`uilibs.${name} 未在 app.dependencies.json 或 potential.dependencies.json 中找到版本号`);
|
|
38281
|
+
}
|
|
38282
|
+
else {
|
|
38283
|
+
(result.uilibs ?? (result.uilibs = {}))[name] = ver;
|
|
38284
|
+
}
|
|
38285
|
+
}
|
|
38286
|
+
// result.uilibs = mergedDeps.uilibs; // 临时先都加上去
|
|
38287
|
+
refs.apis.forEach((interfaces, apiName) => {
|
|
38288
|
+
(result.apis ?? (result.apis = {}))[apiName] = Array.from(interfaces).sort();
|
|
38289
|
+
});
|
|
38290
|
+
if (errors.length > 0)
|
|
38291
|
+
throw new Error(errors.join('\n'));
|
|
38292
|
+
return result;
|
|
38293
|
+
}
|
|
38067
38294
|
/**
|
|
38068
38295
|
* 提取页面文件的签名,移除函数体
|
|
38069
38296
|
* @param content 页面文件内容
|
|
@@ -38086,7 +38313,7 @@ function replaceViewAsSignature(content, hasSubViews = false) {
|
|
|
38086
38313
|
* @returns 文件信息和新发现的依赖文件列表
|
|
38087
38314
|
*/
|
|
38088
38315
|
function processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose) {
|
|
38089
|
-
const absoluteFilePath =
|
|
38316
|
+
const absoluteFilePath = path$1.join(srcDir, pathRelativeToSrc);
|
|
38090
38317
|
const fileInfo = { path: pathRelativeToSrc, content: '' };
|
|
38091
38318
|
try {
|
|
38092
38319
|
fileInfo.content = readFileWithLog(absoluteFilePath, logger);
|
|
@@ -38116,7 +38343,9 @@ function processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFil
|
|
|
38116
38343
|
}
|
|
38117
38344
|
processedFileMap.set(pathRelativeToSrc, fileInfo);
|
|
38118
38345
|
// 提取依赖
|
|
38119
|
-
|
|
38346
|
+
// extensions/connectors/apis/uilibs 作为叶子节点,不再向下递归查找依赖
|
|
38347
|
+
const isExternalDep = shouldSkipDependencyTraversal(pathRelativeToSrc);
|
|
38348
|
+
const deps = isExternalDep ? [] : extractDeps(fileInfo.content);
|
|
38120
38349
|
// 查找依赖文件
|
|
38121
38350
|
if (isView) {
|
|
38122
38351
|
const pathArr = pathRelativeToSrc.split('.');
|
|
@@ -38131,24 +38360,30 @@ function processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFil
|
|
|
38131
38360
|
} while (true);
|
|
38132
38361
|
}
|
|
38133
38362
|
if (verbose || process.env.DEBUG) {
|
|
38134
|
-
console.log(`${isEntryFile ? chalk.magenta('[Entry]') : chalk.green('[ Dep ]')} ${pathRelativeToSrc}${deps.map((dep) => `\n├── ${dep}`).join('')}`);
|
|
38363
|
+
console.log(`${isEntryFile ? chalk.magenta('[Entry]') : isExternalDep ? chalk.blue('[ External Dep ]') : chalk.green('[ Dep ]')} ${pathRelativeToSrc}${deps.map((dep) => `\n├── ${dep}`).join('')}`);
|
|
38135
38364
|
}
|
|
38136
38365
|
return {
|
|
38137
38366
|
fileInfo,
|
|
38138
38367
|
newDeps: deps.filter((dep) => !processedFileMap.has(dep)),
|
|
38368
|
+
isExternalDep,
|
|
38139
38369
|
};
|
|
38140
38370
|
}
|
|
38141
38371
|
/**
|
|
38142
38372
|
* 收集入口文件及其依赖
|
|
38143
38373
|
* @param patterns 入口文件的 glob 模式数组
|
|
38144
38374
|
* @param srcDir 源代码目录绝对路径
|
|
38145
|
-
* @
|
|
38146
|
-
* @returns 文件信息列表
|
|
38375
|
+
* @returns 文件信息列表和外部依赖引用
|
|
38147
38376
|
*/
|
|
38148
38377
|
async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
38149
38378
|
const processedFileMap = new Map(); // 已处理的文件(相对路径)
|
|
38150
38379
|
const filesToProcess = []; // 待处理的文件队列
|
|
38151
38380
|
const result = [];
|
|
38381
|
+
const externalRefs = {
|
|
38382
|
+
extensions: new Set(),
|
|
38383
|
+
connectors: new Set(),
|
|
38384
|
+
uilibs: new Set(),
|
|
38385
|
+
apis: new Map(),
|
|
38386
|
+
};
|
|
38152
38387
|
// 1. 使用 glob 匹配入口文件
|
|
38153
38388
|
logger.newLine();
|
|
38154
38389
|
logger.info(`匹配入口文件: ${patterns.join(', ')}`);
|
|
@@ -38158,15 +38393,15 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
38158
38393
|
throw new Error('未找到匹配的入口文件');
|
|
38159
38394
|
}
|
|
38160
38395
|
logger.info(`找到 ${matchedFiles.length} 个入口文件`);
|
|
38161
|
-
const absoluteSrcDir =
|
|
38396
|
+
const absoluteSrcDir = path$1.resolve(srcDir);
|
|
38162
38397
|
const matchedFileSet = new Set();
|
|
38163
38398
|
matchedFiles.forEach((pathRelativeToRoot) => {
|
|
38164
38399
|
// 统一使用相对于 src 的路径
|
|
38165
|
-
const absoluteFilePath =
|
|
38400
|
+
const absoluteFilePath = path$1.resolve(projectRoot, pathRelativeToRoot);
|
|
38166
38401
|
// 判断 file 是否超出 srcDir 目录(支持相对路径和绝对路径)
|
|
38167
38402
|
if (!absoluteFilePath.startsWith(absoluteSrcDir))
|
|
38168
38403
|
throw new Error(`入口文件 ${absoluteFilePath} 超出了源代码目录 ${srcDir}`);
|
|
38169
|
-
const pathRelativeToSrc =
|
|
38404
|
+
const pathRelativeToSrc = path$1.relative(srcDir, absoluteFilePath);
|
|
38170
38405
|
filesToProcess.push(pathRelativeToSrc);
|
|
38171
38406
|
matchedFileSet.add(pathRelativeToSrc);
|
|
38172
38407
|
});
|
|
@@ -38184,8 +38419,9 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
38184
38419
|
continue;
|
|
38185
38420
|
}
|
|
38186
38421
|
try {
|
|
38187
|
-
const { fileInfo, newDeps } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
|
|
38188
|
-
result.push(fileInfo);
|
|
38422
|
+
const { fileInfo, newDeps, isExternalDep } = processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose);
|
|
38423
|
+
!isExternalDep && result.push(fileInfo);
|
|
38424
|
+
mergeExternalRefs(externalRefs, extractExternalRefs(fileInfo.content));
|
|
38189
38425
|
filesToProcess.push(...newDeps);
|
|
38190
38426
|
}
|
|
38191
38427
|
catch (error) {
|
|
@@ -38207,7 +38443,7 @@ ${depNotFoundList.map((dep) => `- ${dep}\n`).join('')}
|
|
|
38207
38443
|
}
|
|
38208
38444
|
if (errorMessage)
|
|
38209
38445
|
throw new Error(errorMessage);
|
|
38210
|
-
return result;
|
|
38446
|
+
return { files: result, externalRefs };
|
|
38211
38447
|
}
|
|
38212
38448
|
/**
|
|
38213
38449
|
* 解析需要处理的文件(包含配置加载和目录设置)
|
|
@@ -38215,12 +38451,13 @@ ${depNotFoundList.map((dep) => `- ${dep}\n`).join('')}
|
|
|
38215
38451
|
* @returns 文件信息、配置和目录信息
|
|
38216
38452
|
*/
|
|
38217
38453
|
async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
38454
|
+
depMode = true; // !!ensure depMode is true
|
|
38218
38455
|
// 加载配置
|
|
38219
38456
|
const config = loadConfig();
|
|
38220
38457
|
const projectRoot = getProjectRoot();
|
|
38221
38458
|
logger.info(`项目根目录: ${projectRoot}`);
|
|
38222
38459
|
logger.info(`源代码目录: ${config.srcDir}`);
|
|
38223
|
-
const srcDir =
|
|
38460
|
+
const srcDir = path$1.join(projectRoot, config.srcDir);
|
|
38224
38461
|
// 收集需要处理的文件
|
|
38225
38462
|
let collectedFiles = [];
|
|
38226
38463
|
if (Array.isArray(entry) && !entry.length) {
|
|
@@ -38229,10 +38466,12 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
38229
38466
|
config,
|
|
38230
38467
|
projectRoot,
|
|
38231
38468
|
srcDir,
|
|
38469
|
+
externalDependencies: undefined,
|
|
38232
38470
|
};
|
|
38233
38471
|
}
|
|
38472
|
+
let externalRefs = null;
|
|
38234
38473
|
if (!entry && depMode)
|
|
38235
|
-
entry = 'src
|
|
38474
|
+
entry = 'src/**/app.*.{ts,tsx,css}';
|
|
38236
38475
|
if (entry) {
|
|
38237
38476
|
// 检查入口路径是否在源代码目录外面
|
|
38238
38477
|
// if (entry.startsWith('..')) throw new Error('入口路径不能在源代码目录外面');
|
|
@@ -38240,7 +38479,9 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
38240
38479
|
// 具体路径也转换为 glob 模式统一处理
|
|
38241
38480
|
// 使用依赖分析收集文件
|
|
38242
38481
|
try {
|
|
38243
|
-
|
|
38482
|
+
const { files, externalRefs: refs } = await collectDeps(Array.isArray(entry) ? entry : [entry], projectRoot, srcDir, logger, depMode || verbose);
|
|
38483
|
+
collectedFiles = files;
|
|
38484
|
+
externalRefs = refs;
|
|
38244
38485
|
}
|
|
38245
38486
|
catch (error) {
|
|
38246
38487
|
logger.error(`依赖分析失败:\n${error.message}`);
|
|
@@ -38254,13 +38495,21 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
38254
38495
|
filePaths.sort(sorter);
|
|
38255
38496
|
collectedFiles = filePaths.map((filePath) => ({
|
|
38256
38497
|
path: filePath,
|
|
38257
|
-
content: readFileWithLog(
|
|
38498
|
+
content: readFileWithLog(path$1.join(srcDir, filePath), logger),
|
|
38258
38499
|
}));
|
|
38259
38500
|
if (collectedFiles.length === 0) {
|
|
38260
38501
|
logger.warn('未找到 NASL 文件');
|
|
38261
38502
|
logger.exit(1);
|
|
38262
38503
|
}
|
|
38263
38504
|
logger.info(`找到 ${collectedFiles.length} 个 NASL 文件`);
|
|
38505
|
+
// 全量扫描时也从文件中提取外部依赖引用
|
|
38506
|
+
externalRefs = {
|
|
38507
|
+
extensions: new Set(),
|
|
38508
|
+
connectors: new Set(),
|
|
38509
|
+
uilibs: new Set(),
|
|
38510
|
+
apis: new Map(),
|
|
38511
|
+
};
|
|
38512
|
+
collectedFiles.forEach((f) => mergeExternalRefs(externalRefs, extractExternalRefs(f.content)));
|
|
38264
38513
|
}
|
|
38265
38514
|
// 统一过滤掉不支持的文件类型
|
|
38266
38515
|
const filteredFiles = [];
|
|
@@ -38272,11 +38521,24 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
38272
38521
|
logger.warn(`跳过不支持的文件类型: ${file.path}`);
|
|
38273
38522
|
}
|
|
38274
38523
|
});
|
|
38524
|
+
// 从分析中构建 externalDependencies(版本号从 app.dependencies.json / potential.dependencies.json 获取)
|
|
38525
|
+
let externalDependencies;
|
|
38526
|
+
let mergedDependencies;
|
|
38527
|
+
if (externalRefs && (externalRefs.extensions.size > 0 || externalRefs.connectors.size > 0 || externalRefs.uilibs.size > 0 || externalRefs.apis.size > 0)) {
|
|
38528
|
+
const mergedDeps = loadMergedDependencies(srcDir);
|
|
38529
|
+
externalDependencies = buildExternalDependencies(externalRefs, mergedDeps);
|
|
38530
|
+
const appDeps = loadAppDependencies(srcDir);
|
|
38531
|
+
mergedDependencies = mergeDependenciesIntoApp(appDeps, externalDependencies);
|
|
38532
|
+
logger.debug('已从分析构建 externalDependencies', externalDependencies);
|
|
38533
|
+
logger.debug('已从分析构建 mergedDependencies', mergedDependencies);
|
|
38534
|
+
}
|
|
38275
38535
|
return {
|
|
38276
38536
|
collectedFiles: filteredFiles,
|
|
38277
38537
|
config,
|
|
38278
38538
|
projectRoot,
|
|
38279
38539
|
srcDir,
|
|
38540
|
+
externalDependencies,
|
|
38541
|
+
mergedDependencies,
|
|
38280
38542
|
};
|
|
38281
38543
|
}
|
|
38282
38544
|
|
|
@@ -38287,19 +38549,20 @@ async function compile(entry, options) {
|
|
|
38287
38549
|
const logger = options?.logger || defaultLogger;
|
|
38288
38550
|
logger.info('开始编译 NASL 代码...');
|
|
38289
38551
|
// 收集需要编译的文件
|
|
38290
|
-
const { collectedFiles, config, projectRoot } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38552
|
+
const { collectedFiles, config, projectRoot, mergedDependencies } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38291
38553
|
logger.info(`输出目录: ${config.outDir}`);
|
|
38292
|
-
const outDir =
|
|
38554
|
+
const outDir = path$1.join(projectRoot, config.outDir);
|
|
38293
38555
|
// 调用编译 API
|
|
38294
38556
|
logger.newLine();
|
|
38295
38557
|
logger.info('正在调用编译服务...');
|
|
38296
38558
|
try {
|
|
38297
|
-
|
|
38559
|
+
let fullNaturalTS = composeToString(collectedFiles);
|
|
38560
|
+
fullNaturalTS = prependDependencies(fullNaturalTS, mergedDependencies);
|
|
38298
38561
|
const outputFiles = await compileApi(fullNaturalTS, config);
|
|
38299
38562
|
logger.success('编译成功!');
|
|
38300
38563
|
// 写入输出文件
|
|
38301
38564
|
for (const file of outputFiles) {
|
|
38302
|
-
const outputPath =
|
|
38565
|
+
const outputPath = path$1.join(outDir, file.path);
|
|
38303
38566
|
writeFileWithLog(outputPath, file.content, logger);
|
|
38304
38567
|
}
|
|
38305
38568
|
logger.info(`输出 ${outputFiles.length} 个文件`);
|
|
@@ -38386,7 +38649,7 @@ async function check(entry, options) {
|
|
|
38386
38649
|
const logger = options?.logger || defaultLogger;
|
|
38387
38650
|
logger.info('开始检查 NASL 代码...');
|
|
38388
38651
|
// 收集需要检查的文件
|
|
38389
|
-
const { collectedFiles, config } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38652
|
+
const { collectedFiles, config, mergedDependencies } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38390
38653
|
// 调用检查 API
|
|
38391
38654
|
logger.newLine();
|
|
38392
38655
|
logger.info('正在调用检查服务,对语法和语义进行检查...');
|
|
@@ -38394,6 +38657,7 @@ async function check(entry, options) {
|
|
|
38394
38657
|
let result = '';
|
|
38395
38658
|
try {
|
|
38396
38659
|
fullNaturalTS = composeToString(collectedFiles);
|
|
38660
|
+
fullNaturalTS = prependDependencies(fullNaturalTS, mergedDependencies);
|
|
38397
38661
|
}
|
|
38398
38662
|
catch (error) {
|
|
38399
38663
|
result = error.message.trim();
|
|
@@ -38422,12 +38686,19 @@ async function check(entry, options) {
|
|
|
38422
38686
|
}
|
|
38423
38687
|
|
|
38424
38688
|
/**
|
|
38425
|
-
*
|
|
38689
|
+
* 依赖分析命令 - 从入口文件开始进行依赖分析
|
|
38426
38690
|
*/
|
|
38427
38691
|
async function dep(entry, options) {
|
|
38428
|
-
const logger = defaultLogger;
|
|
38692
|
+
const logger = options?.logger || defaultLogger;
|
|
38429
38693
|
logger.info('开始进行依赖分析...');
|
|
38430
|
-
|
|
38694
|
+
const result = await resolveNASLFiles(entry, logger, true, options?.verbose ?? true);
|
|
38695
|
+
if (options?.save && result.mergedDependencies) {
|
|
38696
|
+
const srcDir = path$1.join(result.projectRoot, result.config.srcDir);
|
|
38697
|
+
const appDepsPath = path$1.join(srcDir, 'app.dependencies.json');
|
|
38698
|
+
await libExports.writeJson(appDepsPath, result.mergedDependencies, { spaces: 2 });
|
|
38699
|
+
logger.success(`已将 externalDependencies 补充到 ${appDepsPath}`);
|
|
38700
|
+
}
|
|
38701
|
+
return result;
|
|
38431
38702
|
}
|
|
38432
38703
|
|
|
38433
38704
|
/**
|
|
@@ -38437,15 +38708,15 @@ async function dev(entry, options) {
|
|
|
38437
38708
|
const logger = options?.logger || new ConsoleLoggerWithoutExit();
|
|
38438
38709
|
// 获取项目根目录和配置
|
|
38439
38710
|
const projectRoot = process.cwd();
|
|
38440
|
-
const configPath =
|
|
38711
|
+
const configPath = path$1.join(projectRoot, 'nasl.config.json');
|
|
38441
38712
|
if (!libExports.existsSync(configPath)) {
|
|
38442
38713
|
logger.error('未找到 nasl.config.json 配置文件,请先初始化项目');
|
|
38443
38714
|
process.exit(1);
|
|
38444
38715
|
}
|
|
38445
38716
|
const configContent = libExports.readFileSync(configPath, 'utf-8');
|
|
38446
38717
|
const config = JSON.parse(configContent);
|
|
38447
|
-
const srcDir =
|
|
38448
|
-
const outDir =
|
|
38718
|
+
const srcDir = path$1.join(projectRoot, config.srcDir);
|
|
38719
|
+
const outDir = path$1.join(projectRoot, config.outDir);
|
|
38449
38720
|
// 检查是否存在 src 目录
|
|
38450
38721
|
const hasSrcDir = libExports.existsSync(srcDir) && libExports.statSync(srcDir).isDirectory();
|
|
38451
38722
|
let lastCompileSuccess = false;
|
|
@@ -38495,7 +38766,7 @@ async function dev(entry, options) {
|
|
|
38495
38766
|
// 过滤非目标文件类型
|
|
38496
38767
|
if (!shouldHandleFile(filePath))
|
|
38497
38768
|
return;
|
|
38498
|
-
logger.info(`检测到文件变化: ${
|
|
38769
|
+
logger.info(`检测到文件变化: ${path$1.relative(projectRoot, filePath)}`);
|
|
38499
38770
|
if (isCompiling) {
|
|
38500
38771
|
pendingCompile = true;
|
|
38501
38772
|
return;
|
|
@@ -38612,7 +38883,7 @@ async function dev(entry, options) {
|
|
|
38612
38883
|
async function startWebpackDevServer(outDir, options, logger) {
|
|
38613
38884
|
logger?.newLine();
|
|
38614
38885
|
logger?.info('正在启动开发服务...');
|
|
38615
|
-
const configRelativePath =
|
|
38886
|
+
const configRelativePath = path$1.relative(outDir, path$1.resolve(__dirname, '../../build/webpack.config.js'));
|
|
38616
38887
|
// 构建 webpack-cli 参数
|
|
38617
38888
|
let webpackArgs = `${require.resolve('.bin/webpack-cli')} serve --config ${configRelativePath}`;
|
|
38618
38889
|
if (options?.port)
|
|
@@ -38656,7 +38927,7 @@ async function build(entry, options) {
|
|
|
38656
38927
|
const { outDir } = await compile(entry);
|
|
38657
38928
|
logger.newLine();
|
|
38658
38929
|
logger.info('正在构建项目...');
|
|
38659
|
-
const configRelativePath =
|
|
38930
|
+
const configRelativePath = path$1.relative(outDir, path$1.resolve(__dirname, '../../build/webpack.config.js'));
|
|
38660
38931
|
const mode = options?.mode || 'production';
|
|
38661
38932
|
// 构建 webpack-cli 参数
|
|
38662
38933
|
let webpackArgs = `${require.resolve('.bin/webpack-cli')} build --config ${configRelativePath} --mode ${mode}`;
|
|
@@ -38667,8 +38938,8 @@ async function build(entry, options) {
|
|
|
38667
38938
|
* 获取 specs 下第1个文件夹的名字
|
|
38668
38939
|
*/
|
|
38669
38940
|
function getFirstSpecFolderName(projectRoot) {
|
|
38670
|
-
const dirname =
|
|
38671
|
-
const specsDir =
|
|
38941
|
+
const dirname = path$1.basename(projectRoot);
|
|
38942
|
+
const specsDir = path$1.join(projectRoot, 'specs');
|
|
38672
38943
|
if (!libExports.existsSync(specsDir))
|
|
38673
38944
|
return dirname;
|
|
38674
38945
|
const entries = libExports.readdirSync(specsDir, { withFileTypes: true });
|
|
@@ -38759,9 +39030,175 @@ async function transformJson2FilesApi(json, options) {
|
|
|
38759
39030
|
headers: { 'Content-Type': 'application/json' },
|
|
38760
39031
|
});
|
|
38761
39032
|
const data = res.data;
|
|
39033
|
+
if (data.code !== 200)
|
|
39034
|
+
throw new Error(data.message);
|
|
38762
39035
|
return data.result;
|
|
38763
39036
|
}
|
|
38764
39037
|
|
|
39038
|
+
// src/codeBlock.ts
|
|
39039
|
+
function extractCodeBlocks(markdown) {
|
|
39040
|
+
const codeBlocks = [];
|
|
39041
|
+
const lines = markdown.split("\n");
|
|
39042
|
+
let inCodeBlock = false;
|
|
39043
|
+
let codeBlockStart = 0;
|
|
39044
|
+
let currLang = "";
|
|
39045
|
+
let currLangExtra = "";
|
|
39046
|
+
let currCode = [];
|
|
39047
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39048
|
+
const line = lines[i];
|
|
39049
|
+
if (line.startsWith("```")) {
|
|
39050
|
+
if (!inCodeBlock) {
|
|
39051
|
+
inCodeBlock = true;
|
|
39052
|
+
codeBlockStart = i + 1;
|
|
39053
|
+
const langPart = line.slice(3).trim();
|
|
39054
|
+
const spaceIndex = langPart.indexOf(" ");
|
|
39055
|
+
let potentialLang = "";
|
|
39056
|
+
let potentialExtra = "";
|
|
39057
|
+
if (spaceIndex > 0) {
|
|
39058
|
+
potentialLang = langPart.slice(0, spaceIndex);
|
|
39059
|
+
potentialExtra = langPart.slice(spaceIndex + 1).trim();
|
|
39060
|
+
} else {
|
|
39061
|
+
potentialLang = langPart;
|
|
39062
|
+
potentialExtra = "";
|
|
39063
|
+
}
|
|
39064
|
+
const isLikelyLanguage = potentialLang && !potentialLang.includes("=") && !potentialLang.startsWith("/") && !potentialLang.startsWith(".") && potentialLang !== "";
|
|
39065
|
+
if (isLikelyLanguage) {
|
|
39066
|
+
currLang = potentialLang;
|
|
39067
|
+
currLangExtra = potentialExtra;
|
|
39068
|
+
} else {
|
|
39069
|
+
currLang = "text";
|
|
39070
|
+
currLangExtra = langPart;
|
|
39071
|
+
}
|
|
39072
|
+
currCode = [];
|
|
39073
|
+
} else {
|
|
39074
|
+
inCodeBlock = false;
|
|
39075
|
+
codeBlocks.push({
|
|
39076
|
+
lang: currLang,
|
|
39077
|
+
langExtra: currLangExtra || void 0,
|
|
39078
|
+
code: currCode.join("\n"),
|
|
39079
|
+
startLine: codeBlockStart,
|
|
39080
|
+
endLine: i
|
|
39081
|
+
});
|
|
39082
|
+
currLang = "";
|
|
39083
|
+
currLangExtra = "";
|
|
39084
|
+
currCode = [];
|
|
39085
|
+
}
|
|
39086
|
+
} else if (inCodeBlock) {
|
|
39087
|
+
currCode.push(line);
|
|
39088
|
+
}
|
|
39089
|
+
}
|
|
39090
|
+
return codeBlocks;
|
|
39091
|
+
}
|
|
39092
|
+
|
|
39093
|
+
const DOC_PATTERNS = [
|
|
39094
|
+
/^enums\.md$/,
|
|
39095
|
+
/^entity-.+\.md$/,
|
|
39096
|
+
/^structure-.+\.md$/,
|
|
39097
|
+
/^logic-.+\.md$/,
|
|
39098
|
+
/^view-.+\.md$/,
|
|
39099
|
+
];
|
|
39100
|
+
// 文档类型前缀 → NASL 文件类型名
|
|
39101
|
+
const DOC_TO_NASL_TYPE = {
|
|
39102
|
+
enums: 'enums',
|
|
39103
|
+
entity: 'entities',
|
|
39104
|
+
structure: 'structures',
|
|
39105
|
+
logic: 'logics',
|
|
39106
|
+
view: 'views',
|
|
39107
|
+
};
|
|
39108
|
+
/**
|
|
39109
|
+
* 从 tsx FileInfo 中解析出所有祖先 view,生成空壳 stub
|
|
39110
|
+
* 例如 path=a.views.dashboard.views.knowledgeAccess.views.accessBrowse.tsx
|
|
39111
|
+
* 会生成 dashboard 和 knowledgeAccess 的 stub
|
|
39112
|
+
*/
|
|
39113
|
+
function buildViewStubs(files) {
|
|
39114
|
+
const existingPaths = new Set(files.map((f) => f.path));
|
|
39115
|
+
const stubs = [];
|
|
39116
|
+
for (const file of files) {
|
|
39117
|
+
if (!file.path.endsWith('.tsx'))
|
|
39118
|
+
continue;
|
|
39119
|
+
const parts = file.path.split('.').slice(0, -2);
|
|
39120
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
39121
|
+
if (parts[i] === 'views') {
|
|
39122
|
+
const viewName = parts[i + 1];
|
|
39123
|
+
if (!viewName)
|
|
39124
|
+
continue;
|
|
39125
|
+
const stubPath = [...parts.slice(0, i + 2), 'tsx'].join('.');
|
|
39126
|
+
if (!existingPaths.has(stubPath)) {
|
|
39127
|
+
existingPaths.add(stubPath);
|
|
39128
|
+
stubs.push({
|
|
39129
|
+
path: stubPath,
|
|
39130
|
+
content: `$View({\n title: "",\n auth: false,\n isIndex: false,\n});\nexport function ${viewName}() {\n return ( <ElRouterView /> );\n}`,
|
|
39131
|
+
});
|
|
39132
|
+
}
|
|
39133
|
+
}
|
|
39134
|
+
}
|
|
39135
|
+
}
|
|
39136
|
+
return stubs;
|
|
39137
|
+
}
|
|
39138
|
+
/**
|
|
39139
|
+
* 从 plan markdown 文件中提取并组装 fullNaturalTS,返回字符串或错误信息
|
|
39140
|
+
*/
|
|
39141
|
+
function extractFullNaturalTS(filePath, logger = defaultLogger) {
|
|
39142
|
+
// 1. 验证路径模式(只检查文件名)
|
|
39143
|
+
const basename = path$1.basename(filePath);
|
|
39144
|
+
if (!DOC_PATTERNS.some((p) => p.test(basename))) {
|
|
39145
|
+
logger.error(`路径不匹配任何支持的文档类型:${basename}\n` +
|
|
39146
|
+
`支持的类型:enums.md, entity-*.md, structure-*.md, logic-*.md, view-*.md`);
|
|
39147
|
+
return { success: false };
|
|
39148
|
+
}
|
|
39149
|
+
// 2. 读取文件
|
|
39150
|
+
if (!libExports.existsSync(filePath)) {
|
|
39151
|
+
logger.error(`文件不存在:${filePath}`);
|
|
39152
|
+
return { success: false };
|
|
39153
|
+
}
|
|
39154
|
+
const content = libExports.readFileSync(filePath, 'utf-8');
|
|
39155
|
+
// 3. 提取 naturalts 代码块,转换为 FileInfo[]
|
|
39156
|
+
const blocks = extractCodeBlocks(content).filter((b) => b.lang === 'naturalts');
|
|
39157
|
+
if (blocks.length === 0) {
|
|
39158
|
+
// logger.warn('未发现 naturalts 代码块');
|
|
39159
|
+
return { fullNaturalTS: '' };
|
|
39160
|
+
}
|
|
39161
|
+
const isViewDoc = /^view-.+\.md$/.test(basename);
|
|
39162
|
+
const expectedExt = isViewDoc ? '.tsx' : '.ts';
|
|
39163
|
+
const docTypePrefix = basename.split(/[-.]/)[0] ?? '';
|
|
39164
|
+
const naslTypeName = DOC_TO_NASL_TYPE[docTypePrefix];
|
|
39165
|
+
const expectedPattern = NASL_FILE_TYPES.find((t) => t.name === naslTypeName)?.fileNamePattern;
|
|
39166
|
+
const errors = [];
|
|
39167
|
+
const files = blocks.map((block) => {
|
|
39168
|
+
const match = block.langExtra?.match(/path="([^"]+)"/);
|
|
39169
|
+
if (!match) {
|
|
39170
|
+
errors.push(`代码块缺少 path 属性(第 ${block.startLine} 行)`);
|
|
39171
|
+
return null;
|
|
39172
|
+
}
|
|
39173
|
+
const blockPath = match[1];
|
|
39174
|
+
if (!blockPath.endsWith(expectedExt)) {
|
|
39175
|
+
errors.push(`代码块 path 扩展名不正确,应为 ${expectedExt}(第 ${block.startLine} 行):${blockPath}`);
|
|
39176
|
+
return null;
|
|
39177
|
+
}
|
|
39178
|
+
if (!isKnownFileType(blockPath)) {
|
|
39179
|
+
const hint = expectedPattern ? `\n正确格式应匹配:${expectedPattern}` : '';
|
|
39180
|
+
errors.push(`代码块 path 不是合法的 NASL 文件类型(第 ${block.startLine} 行):${blockPath}${hint}`);
|
|
39181
|
+
return null;
|
|
39182
|
+
}
|
|
39183
|
+
const fileInfo = { path: blockPath, content: block.code };
|
|
39184
|
+
return fileInfo;
|
|
39185
|
+
}).filter((f) => f !== null);
|
|
39186
|
+
if (errors.length > 0) {
|
|
39187
|
+
logger.error(`在 ${filePath} 检测到以下错误:\n` + errors.join('\n'));
|
|
39188
|
+
return { success: false };
|
|
39189
|
+
}
|
|
39190
|
+
// 4. 补充父级 view stub(tsx 文件需要祖先页面定义)
|
|
39191
|
+
const stubs = buildViewStubs(files);
|
|
39192
|
+
// 5. 复用 composeToString 组装(包含排序和 namespace 构建)
|
|
39193
|
+
try {
|
|
39194
|
+
return { fullNaturalTS: composeToString([...files, ...stubs], { isDocCheck: true }) };
|
|
39195
|
+
}
|
|
39196
|
+
catch (err) {
|
|
39197
|
+
logger.error(`在 ${filePath} 检测到以下错误:\n` + err.message);
|
|
39198
|
+
return { success: false };
|
|
39199
|
+
}
|
|
39200
|
+
}
|
|
39201
|
+
|
|
38765
39202
|
const transformFns = {
|
|
38766
39203
|
/**
|
|
38767
39204
|
* 将 src 中的文件组合成 fullNaturalTS
|
|
@@ -38769,7 +39206,7 @@ const transformFns = {
|
|
|
38769
39206
|
async files2full(entry, options) {
|
|
38770
39207
|
const logger = options?.logger || defaultLogger;
|
|
38771
39208
|
// 收集需要处理的文件
|
|
38772
|
-
const { collectedFiles, projectRoot } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
39209
|
+
const { collectedFiles, projectRoot, mergedDependencies } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
38773
39210
|
if (collectedFiles.length === 0) {
|
|
38774
39211
|
logger.error('未找到需要转换的文件');
|
|
38775
39212
|
logger.exit(1);
|
|
@@ -38778,15 +39215,43 @@ const transformFns = {
|
|
|
38778
39215
|
// 组合成 fullNaturalTS
|
|
38779
39216
|
logger.newLine();
|
|
38780
39217
|
logger.info('正在组合文件...');
|
|
38781
|
-
|
|
39218
|
+
let fullNaturalTS = composeToString(collectedFiles);
|
|
39219
|
+
fullNaturalTS = prependDependencies(fullNaturalTS, mergedDependencies);
|
|
38782
39220
|
// 确定输出路径
|
|
38783
39221
|
const outputPath = options?.output
|
|
38784
|
-
?
|
|
38785
|
-
:
|
|
39222
|
+
? path$1.resolve(projectRoot, options.output)
|
|
39223
|
+
: path$1.join(projectRoot, './full-natural.ts');
|
|
38786
39224
|
// 写入文件
|
|
38787
39225
|
writeFileWithLog(outputPath, fullNaturalTS, logger);
|
|
38788
39226
|
logger.success(`文件已输出到: ${outputPath}`);
|
|
38789
39227
|
},
|
|
39228
|
+
/**
|
|
39229
|
+
* 将 plan markdown 文档中的 naturalts 代码块组合成 fullNaturalTS 文件
|
|
39230
|
+
*/
|
|
39231
|
+
async doc2full(entry, options) {
|
|
39232
|
+
const logger = options?.logger || defaultLogger;
|
|
39233
|
+
if (!entry) {
|
|
39234
|
+
logger.error('请指定 plan 文档路径');
|
|
39235
|
+
logger.exit(1);
|
|
39236
|
+
return;
|
|
39237
|
+
}
|
|
39238
|
+
const extracted = extractFullNaturalTS(entry, logger);
|
|
39239
|
+
if ('success' in extracted) {
|
|
39240
|
+
logger.exit(1);
|
|
39241
|
+
return;
|
|
39242
|
+
}
|
|
39243
|
+
if (!extracted.fullNaturalTS) {
|
|
39244
|
+
logger.warn('未发现 naturalts 代码块,跳过输出');
|
|
39245
|
+
return;
|
|
39246
|
+
}
|
|
39247
|
+
// 确定输出路径
|
|
39248
|
+
const projectRoot = getProjectRoot();
|
|
39249
|
+
const outputPath = options?.output
|
|
39250
|
+
? path$1.resolve(projectRoot, options.output)
|
|
39251
|
+
: path$1.join(projectRoot, './full-natural.ts');
|
|
39252
|
+
writeFileWithLog(outputPath, extracted.fullNaturalTS, logger);
|
|
39253
|
+
logger.success(`文件已输出到: ${outputPath}`);
|
|
39254
|
+
},
|
|
38790
39255
|
/**
|
|
38791
39256
|
* 将 JSON 文件转换成 src 的 files
|
|
38792
39257
|
* TODO: 待实现
|
|
@@ -38812,7 +39277,7 @@ const transformFns = {
|
|
|
38812
39277
|
}
|
|
38813
39278
|
const files = await transformJson2FilesApi(jsonContent ? JSON.parse(jsonContent) : {}, config);
|
|
38814
39279
|
await Promise.all(files.map(async (file) => {
|
|
38815
|
-
const outputPath =
|
|
39280
|
+
const outputPath = path$1.join(projectRoot, config.srcDir, file.path);
|
|
38816
39281
|
await libExports.writeFile(outputPath, file.content);
|
|
38817
39282
|
}));
|
|
38818
39283
|
logger.success(`JSON 文件已转换为 ${files.length} 个文件,输出到 ${config.srcDir}`);
|
|
@@ -38839,7 +39304,87 @@ async function transform(transformType, entry, options) {
|
|
|
38839
39304
|
await transformFn(entry, options);
|
|
38840
39305
|
}
|
|
38841
39306
|
|
|
38842
|
-
|
|
39307
|
+
async function installAuto(options) {
|
|
39308
|
+
const logger = options?.logger || defaultLogger;
|
|
39309
|
+
const config = loadConfig();
|
|
39310
|
+
const projectRoot = getProjectRoot();
|
|
39311
|
+
const srcDir = path$1.join(projectRoot, config.srcDir);
|
|
39312
|
+
const appDependenciesJsonPath = path$1.join(srcDir, 'app.dependencies.json');
|
|
39313
|
+
const appDependenciesJson = libExports.readJsonSync(appDependenciesJsonPath);
|
|
39314
|
+
logger.info(`开始按照 ${appDependenciesJsonPath} 安装依赖`);
|
|
39315
|
+
return installByJSON(appDependenciesJson, options);
|
|
39316
|
+
}
|
|
39317
|
+
async function installByKind(kind, deps, options) {
|
|
39318
|
+
const logger = options?.logger || defaultLogger;
|
|
39319
|
+
if (!deps || deps.length === 0) {
|
|
39320
|
+
logger.error(`请至少指定一个要安装的 ${kind},例如:nasl install ${kind} aaa@0.1.0`);
|
|
39321
|
+
return logger.exit(1);
|
|
39322
|
+
}
|
|
39323
|
+
const dependencyMap = {};
|
|
39324
|
+
for (const dep of deps) {
|
|
39325
|
+
if (kind === 'api') {
|
|
39326
|
+
const [service, interfaces] = dep.split(':');
|
|
39327
|
+
if (!service || !interfaces) {
|
|
39328
|
+
logger.error(`无效的依赖格式:${dep},请使用 service:api1,api2 格式,例如:some_service:api1,api2`);
|
|
39329
|
+
return logger.exit(1);
|
|
39330
|
+
}
|
|
39331
|
+
dependencyMap[service] = interfaces.split(',');
|
|
39332
|
+
}
|
|
39333
|
+
else {
|
|
39334
|
+
const [name, version] = dep.split('@');
|
|
39335
|
+
if (!name || !version) {
|
|
39336
|
+
logger.error(`无效的依赖格式:${dep},请使用 name@version 格式,例如:aaa@0.1.0`);
|
|
39337
|
+
return logger.exit(1);
|
|
39338
|
+
}
|
|
39339
|
+
dependencyMap[name] = version;
|
|
39340
|
+
}
|
|
39341
|
+
}
|
|
39342
|
+
return installByJSON({ [kind + 's']: dependencyMap }, options);
|
|
39343
|
+
}
|
|
39344
|
+
async function installByJSON(json, options) {
|
|
39345
|
+
const logger = options?.logger || defaultLogger;
|
|
39346
|
+
if (!Object.keys(json).length) {
|
|
39347
|
+
logger.error('没有需要安装的依赖');
|
|
39348
|
+
return;
|
|
39349
|
+
}
|
|
39350
|
+
const config = loadConfig();
|
|
39351
|
+
const projectRoot = getProjectRoot();
|
|
39352
|
+
const srcDir = path$1.join(projectRoot, config.srcDir);
|
|
39353
|
+
const result = await installDependenciesApi(json, config);
|
|
39354
|
+
if (!result.files || result.files.length <= 1)
|
|
39355
|
+
throw new Error(`安装依赖失败,没有生成任何文件:\n${JSON.stringify(result)}`);
|
|
39356
|
+
if (result.errors.length > 0) {
|
|
39357
|
+
logger.error(`某些依赖安装失败:`);
|
|
39358
|
+
for (const error of result.errors)
|
|
39359
|
+
logger.error(error);
|
|
39360
|
+
}
|
|
39361
|
+
// const appDepsFile = result.files.find((f) => f.path === 'app.dependencies.json');
|
|
39362
|
+
const otherFiles = result.files.filter((f) => f.path !== 'app.dependencies.json');
|
|
39363
|
+
const jsonToSave = JSON.parse(JSON.stringify(json));
|
|
39364
|
+
for (const err of result.errors) {
|
|
39365
|
+
const { name, category } = err;
|
|
39366
|
+
const record = jsonToSave[category];
|
|
39367
|
+
if (record && typeof record === 'object') {
|
|
39368
|
+
delete record[name];
|
|
39369
|
+
if (Object.keys(record).length === 0)
|
|
39370
|
+
delete jsonToSave[category];
|
|
39371
|
+
}
|
|
39372
|
+
}
|
|
39373
|
+
if (options?.savePotential) {
|
|
39374
|
+
const potentialPath = path$1.join(srcDir, 'potential.dependencies.json');
|
|
39375
|
+
await libExports.writeJson(potentialPath, jsonToSave, { spaces: 2 });
|
|
39376
|
+
logger.success(`依赖信息已写入: ${path$1.relative(projectRoot, potentialPath)}`);
|
|
39377
|
+
}
|
|
39378
|
+
await Promise.all(otherFiles.map(async (file) => {
|
|
39379
|
+
const filePath = path$1.join(srcDir, file.path);
|
|
39380
|
+
await libExports.outputFile(filePath, file.content, 'utf-8');
|
|
39381
|
+
logger.success(`依赖文件已写入: ${path$1.relative(projectRoot, filePath)}`);
|
|
39382
|
+
}));
|
|
39383
|
+
const writtenCount = otherFiles.length;
|
|
39384
|
+
logger.success(`成功写入 ${writtenCount} 个依赖文件到 ${config.srcDir} 目录`);
|
|
39385
|
+
}
|
|
39386
|
+
|
|
39387
|
+
var version = "0.3.1";
|
|
38843
39388
|
var pkg = {
|
|
38844
39389
|
version: version};
|
|
38845
39390
|
|
|
@@ -38899,15 +39444,51 @@ program
|
|
|
38899
39444
|
program
|
|
38900
39445
|
.command('dep [entry]')
|
|
38901
39446
|
.description('从入口文件开始进行依赖分析')
|
|
38902
|
-
.
|
|
39447
|
+
.option('--save', '将计算出的 externalDependencies 补充到 src/app.dependencies.json')
|
|
39448
|
+
.action(async (entry, options) => {
|
|
38903
39449
|
try {
|
|
38904
|
-
await dep(entry);
|
|
39450
|
+
await dep(entry, options);
|
|
38905
39451
|
}
|
|
38906
39452
|
catch (error) {
|
|
38907
39453
|
defaultLogger.error(`查找依赖过程发生错误:${error.message}`);
|
|
38908
39454
|
process.exit(1);
|
|
38909
39455
|
}
|
|
38910
39456
|
});
|
|
39457
|
+
program
|
|
39458
|
+
.command('install [kind|by-json] [deps...]')
|
|
39459
|
+
.option('--save-potential', '将 app.dependencies.json 写入 potential.dependencies.json')
|
|
39460
|
+
.description(`安装一个或多个 uilib, extension, api 或 connector 依赖,例如:
|
|
39461
|
+
- nasl install uilib some_component@0.1.0 some_component@0.2.0
|
|
39462
|
+
- nasl install extension some_encryption@0.1.0 some_interceptor@0.2.0
|
|
39463
|
+
- nasl install api some_service:api1,api2 some_service:api3,api4
|
|
39464
|
+
- nasl install connector some_connector@0.1.0 some_connector@0.2.0
|
|
39465
|
+
- nasl install by-json '{"uilibs": {"some_component": "0.1.0"}, "extensions": {"some_encryption": "0.1.0", "some_interceptor": "0.2.0"}}'
|
|
39466
|
+
`)
|
|
39467
|
+
.action(async (kind, deps, options) => {
|
|
39468
|
+
try {
|
|
39469
|
+
if (kind === 'by-json') {
|
|
39470
|
+
if (deps.length < 1)
|
|
39471
|
+
throw new Error('请提供一个 JSON 字符串');
|
|
39472
|
+
else if (deps.length > 1)
|
|
39473
|
+
throw new Error('参数过多,JSON 字符串只能有一个');
|
|
39474
|
+
const jsonStr = deps[0];
|
|
39475
|
+
await installByJSON(JSON.parse(jsonStr), options);
|
|
39476
|
+
}
|
|
39477
|
+
else if (kind) {
|
|
39478
|
+
if (!['uilib', 'extension', 'api', 'connector'].includes(kind)) {
|
|
39479
|
+
throw new Error(`无效的依赖类型: ${kind}。\n支持的类型有: uilib, extension, api, connector`);
|
|
39480
|
+
}
|
|
39481
|
+
await installByKind(kind, deps, options);
|
|
39482
|
+
}
|
|
39483
|
+
else {
|
|
39484
|
+
await installAuto(options);
|
|
39485
|
+
}
|
|
39486
|
+
}
|
|
39487
|
+
catch (error) {
|
|
39488
|
+
defaultLogger.error(`安装过程发生错误:${error.message}`);
|
|
39489
|
+
process.exit(1);
|
|
39490
|
+
}
|
|
39491
|
+
});
|
|
38911
39492
|
program
|
|
38912
39493
|
.command('dev [entry]')
|
|
38913
39494
|
.description('启动开发服务')
|
|
@@ -38965,12 +39546,12 @@ program
|
|
|
38965
39546
|
});
|
|
38966
39547
|
program
|
|
38967
39548
|
.command('transform <transformType> [entry]')
|
|
38968
|
-
.description('转换文件格式\n transformType: files2full (将 src 文件组合成 fullNaturalTS), json2files (将 JSON 转换为文件), files2json (将 src 文件转换为 JSON)')
|
|
39549
|
+
.description('转换文件格式\n transformType: files2full (将 src 文件组合成 fullNaturalTS), json2files (将 JSON 转换为文件), files2json (将 src 文件转换为 JSON), doc2full (将 plan markdown 文档组合成 fullNaturalTS)')
|
|
38969
39550
|
.option('-o, --output <outputPath>', '指定输出路径')
|
|
38970
39551
|
.option('-v, --verbose', '显示详细信息,如依赖分析树')
|
|
38971
39552
|
.action(async (transformType, entry, options) => {
|
|
38972
39553
|
try {
|
|
38973
|
-
const validTypes = ['files2full', 'json2files', 'files2json'];
|
|
39554
|
+
const validTypes = ['files2full', 'json2files', 'files2json', 'doc2full'];
|
|
38974
39555
|
if (!validTypes.includes(transformType)) {
|
|
38975
39556
|
defaultLogger.error(`无效的转换类型: ${transformType}。支持的类型: ${validTypes.join(', ')}`);
|
|
38976
39557
|
process.exit(1);
|
|
@@ -39291,7 +39872,7 @@ const binaryExtensions = new Set([
|
|
|
39291
39872
|
'xmind', 'xpi', 'xpm', 'xwd', 'xz',
|
|
39292
39873
|
'z', 'zip', 'zipx',
|
|
39293
39874
|
]);
|
|
39294
|
-
const isBinaryPath = (filePath) => binaryExtensions.has(
|
|
39875
|
+
const isBinaryPath = (filePath) => binaryExtensions.has(path$1.extname(filePath).slice(1).toLowerCase());
|
|
39295
39876
|
// TODO: emit errors properly. Example: EMFILE on Macos.
|
|
39296
39877
|
const foreach = (val, fn) => {
|
|
39297
39878
|
if (val instanceof Set) {
|
|
@@ -39344,7 +39925,7 @@ function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
|
|
|
39344
39925
|
// emit based on events occurring for files from a directory's watcher in
|
|
39345
39926
|
// case the file's watcher misses it (and rely on throttling to de-dupe)
|
|
39346
39927
|
if (evPath && path !== evPath) {
|
|
39347
|
-
fsWatchBroadcast(
|
|
39928
|
+
fsWatchBroadcast(path$1.resolve(path, evPath), KEY_LISTENERS, path$1.join(path, evPath));
|
|
39348
39929
|
}
|
|
39349
39930
|
};
|
|
39350
39931
|
try {
|
|
@@ -39527,11 +40108,11 @@ class NodeFsHandler {
|
|
|
39527
40108
|
*/
|
|
39528
40109
|
_watchWithNodeFs(path, listener) {
|
|
39529
40110
|
const opts = this.fsw.options;
|
|
39530
|
-
const directory =
|
|
39531
|
-
const basename =
|
|
40111
|
+
const directory = path$1.dirname(path);
|
|
40112
|
+
const basename = path$1.basename(path);
|
|
39532
40113
|
const parent = this.fsw._getWatchedDir(directory);
|
|
39533
40114
|
parent.add(basename);
|
|
39534
|
-
const absolutePath =
|
|
40115
|
+
const absolutePath = path$1.resolve(path);
|
|
39535
40116
|
const options = {
|
|
39536
40117
|
persistent: opts.persistent,
|
|
39537
40118
|
};
|
|
@@ -39563,8 +40144,8 @@ class NodeFsHandler {
|
|
|
39563
40144
|
if (this.fsw.closed) {
|
|
39564
40145
|
return;
|
|
39565
40146
|
}
|
|
39566
|
-
const dirname =
|
|
39567
|
-
const basename =
|
|
40147
|
+
const dirname = path$1.dirname(file);
|
|
40148
|
+
const basename = path$1.basename(file);
|
|
39568
40149
|
const parent = this.fsw._getWatchedDir(dirname);
|
|
39569
40150
|
// stats is always present
|
|
39570
40151
|
let prevStats = stats;
|
|
@@ -39671,7 +40252,7 @@ class NodeFsHandler {
|
|
|
39671
40252
|
}
|
|
39672
40253
|
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
|
39673
40254
|
// Normalize the directory name on Windows
|
|
39674
|
-
directory =
|
|
40255
|
+
directory = path$1.join(directory, '');
|
|
39675
40256
|
throttler = this.fsw._throttle('readdir', directory, 1000);
|
|
39676
40257
|
if (!throttler)
|
|
39677
40258
|
return;
|
|
@@ -39690,7 +40271,7 @@ class NodeFsHandler {
|
|
|
39690
40271
|
return;
|
|
39691
40272
|
}
|
|
39692
40273
|
const item = entry.path;
|
|
39693
|
-
let path =
|
|
40274
|
+
let path = path$1.join(directory, item);
|
|
39694
40275
|
current.add(item);
|
|
39695
40276
|
if (entry.stats.isSymbolicLink() &&
|
|
39696
40277
|
(await this._handleSymlink(entry, directory, path, item))) {
|
|
@@ -39706,7 +40287,7 @@ class NodeFsHandler {
|
|
|
39706
40287
|
if (item === target || (!target && !previous.has(item))) {
|
|
39707
40288
|
this.fsw._incrReadyCount();
|
|
39708
40289
|
// ensure relativeness of path is preserved in case of watcher reuse
|
|
39709
|
-
path =
|
|
40290
|
+
path = path$1.join(dir, path$1.relative(dir, path));
|
|
39710
40291
|
this._addToNodeFs(path, initialAdd, wh, depth + 1);
|
|
39711
40292
|
}
|
|
39712
40293
|
})
|
|
@@ -39751,13 +40332,13 @@ class NodeFsHandler {
|
|
|
39751
40332
|
* @returns closer for the watcher instance.
|
|
39752
40333
|
*/
|
|
39753
40334
|
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
|
|
39754
|
-
const parentDir = this.fsw._getWatchedDir(
|
|
39755
|
-
const tracked = parentDir.has(
|
|
40335
|
+
const parentDir = this.fsw._getWatchedDir(path$1.dirname(dir));
|
|
40336
|
+
const tracked = parentDir.has(path$1.basename(dir));
|
|
39756
40337
|
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
39757
40338
|
this.fsw._emit(EV.ADD_DIR, dir, stats);
|
|
39758
40339
|
}
|
|
39759
40340
|
// ensure dir is tracked (harmless if redundant)
|
|
39760
|
-
parentDir.add(
|
|
40341
|
+
parentDir.add(path$1.basename(dir));
|
|
39761
40342
|
this.fsw._getWatchedDir(dir);
|
|
39762
40343
|
let throttler;
|
|
39763
40344
|
let closer;
|
|
@@ -39809,7 +40390,7 @@ class NodeFsHandler {
|
|
|
39809
40390
|
const follow = this.fsw.options.followSymlinks;
|
|
39810
40391
|
let closer;
|
|
39811
40392
|
if (stats.isDirectory()) {
|
|
39812
|
-
const absPath =
|
|
40393
|
+
const absPath = path$1.resolve(path);
|
|
39813
40394
|
const targetPath = follow ? await realpath$1(path) : path;
|
|
39814
40395
|
if (this.fsw.closed)
|
|
39815
40396
|
return;
|
|
@@ -39825,7 +40406,7 @@ class NodeFsHandler {
|
|
|
39825
40406
|
const targetPath = follow ? await realpath$1(path) : path;
|
|
39826
40407
|
if (this.fsw.closed)
|
|
39827
40408
|
return;
|
|
39828
|
-
const parent =
|
|
40409
|
+
const parent = path$1.dirname(wh.watchPath);
|
|
39829
40410
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
39830
40411
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
39831
40412
|
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
|
|
@@ -39833,7 +40414,7 @@ class NodeFsHandler {
|
|
|
39833
40414
|
return;
|
|
39834
40415
|
// preserve this symlink's target path
|
|
39835
40416
|
if (targetPath !== undefined) {
|
|
39836
|
-
this.fsw._symlinkPaths.set(
|
|
40417
|
+
this.fsw._symlinkPaths.set(path$1.resolve(path), targetPath);
|
|
39837
40418
|
}
|
|
39838
40419
|
}
|
|
39839
40420
|
else {
|
|
@@ -39879,11 +40460,11 @@ function createPattern(matcher) {
|
|
|
39879
40460
|
if (matcher.path === string)
|
|
39880
40461
|
return true;
|
|
39881
40462
|
if (matcher.recursive) {
|
|
39882
|
-
const relative =
|
|
40463
|
+
const relative = path$1.relative(matcher.path, string);
|
|
39883
40464
|
if (!relative) {
|
|
39884
40465
|
return false;
|
|
39885
40466
|
}
|
|
39886
|
-
return !relative.startsWith('..') && !
|
|
40467
|
+
return !relative.startsWith('..') && !path$1.isAbsolute(relative);
|
|
39887
40468
|
}
|
|
39888
40469
|
return false;
|
|
39889
40470
|
};
|
|
@@ -39893,7 +40474,7 @@ function createPattern(matcher) {
|
|
|
39893
40474
|
function normalizePath(path) {
|
|
39894
40475
|
if (typeof path !== 'string')
|
|
39895
40476
|
throw new Error('string expected');
|
|
39896
|
-
path =
|
|
40477
|
+
path = path$1.normalize(path);
|
|
39897
40478
|
path = path.replace(/\\/g, '/');
|
|
39898
40479
|
let prepend = false;
|
|
39899
40480
|
if (path.startsWith('//'))
|
|
@@ -39953,21 +40534,21 @@ const toUnix = (string) => {
|
|
|
39953
40534
|
};
|
|
39954
40535
|
// Our version of upath.normalize
|
|
39955
40536
|
// TODO: this is not equal to path-normalize module - investigate why
|
|
39956
|
-
const normalizePathToUnix = (path) => toUnix(
|
|
40537
|
+
const normalizePathToUnix = (path) => toUnix(path$1.normalize(toUnix(path)));
|
|
39957
40538
|
// TODO: refactor
|
|
39958
40539
|
const normalizeIgnored = (cwd = '') => (path) => {
|
|
39959
40540
|
if (typeof path === 'string') {
|
|
39960
|
-
return normalizePathToUnix(
|
|
40541
|
+
return normalizePathToUnix(path$1.isAbsolute(path) ? path : path$1.join(cwd, path));
|
|
39961
40542
|
}
|
|
39962
40543
|
else {
|
|
39963
40544
|
return path;
|
|
39964
40545
|
}
|
|
39965
40546
|
};
|
|
39966
40547
|
const getAbsolutePath = (path, cwd) => {
|
|
39967
|
-
if (
|
|
40548
|
+
if (path$1.isAbsolute(path)) {
|
|
39968
40549
|
return path;
|
|
39969
40550
|
}
|
|
39970
|
-
return
|
|
40551
|
+
return path$1.join(cwd, path);
|
|
39971
40552
|
};
|
|
39972
40553
|
const EMPTY_SET = Object.freeze(new Set());
|
|
39973
40554
|
/**
|
|
@@ -39999,7 +40580,7 @@ class DirEntry {
|
|
|
39999
40580
|
}
|
|
40000
40581
|
catch (err) {
|
|
40001
40582
|
if (this._removeWatcher) {
|
|
40002
|
-
this._removeWatcher(
|
|
40583
|
+
this._removeWatcher(path$1.dirname(dir), path$1.basename(dir));
|
|
40003
40584
|
}
|
|
40004
40585
|
}
|
|
40005
40586
|
}
|
|
@@ -40031,7 +40612,7 @@ class WatchHelper {
|
|
|
40031
40612
|
const watchPath = path;
|
|
40032
40613
|
this.path = path = path.replace(REPLACER_RE, '');
|
|
40033
40614
|
this.watchPath = watchPath;
|
|
40034
|
-
this.fullWatchPath =
|
|
40615
|
+
this.fullWatchPath = path$1.resolve(watchPath);
|
|
40035
40616
|
this.dirParts = [];
|
|
40036
40617
|
this.dirParts.forEach((parts) => {
|
|
40037
40618
|
if (parts.length > 1)
|
|
@@ -40041,7 +40622,7 @@ class WatchHelper {
|
|
|
40041
40622
|
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
|
40042
40623
|
}
|
|
40043
40624
|
entryPath(entry) {
|
|
40044
|
-
return
|
|
40625
|
+
return path$1.join(this.watchPath, path$1.relative(this.watchPath, entry.fullPath));
|
|
40045
40626
|
}
|
|
40046
40627
|
filterPath(entry) {
|
|
40047
40628
|
const { stats } = entry;
|
|
@@ -40197,7 +40778,7 @@ class FSWatcher extends EventEmitter {
|
|
|
40197
40778
|
return;
|
|
40198
40779
|
results.forEach((item) => {
|
|
40199
40780
|
if (item)
|
|
40200
|
-
this.add(
|
|
40781
|
+
this.add(path$1.dirname(item), path$1.basename(_origAdd || item));
|
|
40201
40782
|
});
|
|
40202
40783
|
});
|
|
40203
40784
|
return this;
|
|
@@ -40212,10 +40793,10 @@ class FSWatcher extends EventEmitter {
|
|
|
40212
40793
|
const { cwd } = this.options;
|
|
40213
40794
|
paths.forEach((path) => {
|
|
40214
40795
|
// convert to absolute path unless relative path already matches
|
|
40215
|
-
if (!
|
|
40796
|
+
if (!path$1.isAbsolute(path) && !this._closers.has(path)) {
|
|
40216
40797
|
if (cwd)
|
|
40217
|
-
path =
|
|
40218
|
-
path =
|
|
40798
|
+
path = path$1.join(cwd, path);
|
|
40799
|
+
path = path$1.resolve(path);
|
|
40219
40800
|
}
|
|
40220
40801
|
this._closePath(path);
|
|
40221
40802
|
this._addIgnoredPath(path);
|
|
@@ -40269,7 +40850,7 @@ class FSWatcher extends EventEmitter {
|
|
|
40269
40850
|
getWatched() {
|
|
40270
40851
|
const watchList = {};
|
|
40271
40852
|
this._watched.forEach((entry, dir) => {
|
|
40272
|
-
const key = this.options.cwd ?
|
|
40853
|
+
const key = this.options.cwd ? path$1.relative(this.options.cwd, dir) : dir;
|
|
40273
40854
|
const index = key || ONE_DOT;
|
|
40274
40855
|
watchList[index] = entry.getChildren().sort();
|
|
40275
40856
|
});
|
|
@@ -40295,9 +40876,9 @@ class FSWatcher extends EventEmitter {
|
|
|
40295
40876
|
return;
|
|
40296
40877
|
const opts = this.options;
|
|
40297
40878
|
if (isWindows)
|
|
40298
|
-
path =
|
|
40879
|
+
path = path$1.normalize(path);
|
|
40299
40880
|
if (opts.cwd)
|
|
40300
|
-
path =
|
|
40881
|
+
path = path$1.relative(opts.cwd, path);
|
|
40301
40882
|
const args = [path];
|
|
40302
40883
|
if (stats != null)
|
|
40303
40884
|
args.push(stats);
|
|
@@ -40353,7 +40934,7 @@ class FSWatcher extends EventEmitter {
|
|
|
40353
40934
|
if (opts.alwaysStat &&
|
|
40354
40935
|
stats === undefined &&
|
|
40355
40936
|
(event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
40356
|
-
const fullPath = opts.cwd ?
|
|
40937
|
+
const fullPath = opts.cwd ? path$1.join(opts.cwd, path) : path;
|
|
40357
40938
|
let stats;
|
|
40358
40939
|
try {
|
|
40359
40940
|
stats = await stat$2(fullPath);
|
|
@@ -40436,8 +41017,8 @@ class FSWatcher extends EventEmitter {
|
|
|
40436
41017
|
const pollInterval = awf.pollInterval;
|
|
40437
41018
|
let timeoutHandler;
|
|
40438
41019
|
let fullPath = path;
|
|
40439
|
-
if (this.options.cwd && !
|
|
40440
|
-
fullPath =
|
|
41020
|
+
if (this.options.cwd && !path$1.isAbsolute(path)) {
|
|
41021
|
+
fullPath = path$1.join(this.options.cwd, path);
|
|
40441
41022
|
}
|
|
40442
41023
|
const now = new Date();
|
|
40443
41024
|
const writes = this._pendingWrites;
|
|
@@ -40508,7 +41089,7 @@ class FSWatcher extends EventEmitter {
|
|
|
40508
41089
|
* @param directory path of the directory
|
|
40509
41090
|
*/
|
|
40510
41091
|
_getWatchedDir(directory) {
|
|
40511
|
-
const dir =
|
|
41092
|
+
const dir = path$1.resolve(directory);
|
|
40512
41093
|
if (!this._watched.has(dir))
|
|
40513
41094
|
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
|
40514
41095
|
return this._watched.get(dir);
|
|
@@ -40534,8 +41115,8 @@ class FSWatcher extends EventEmitter {
|
|
|
40534
41115
|
// if what is being deleted is a directory, get that directory's paths
|
|
40535
41116
|
// for recursive deleting and cleaning of watched object
|
|
40536
41117
|
// if it is not a directory, nestedDirectoryChildren will be empty array
|
|
40537
|
-
const path =
|
|
40538
|
-
const fullPath =
|
|
41118
|
+
const path = path$1.join(directory, item);
|
|
41119
|
+
const fullPath = path$1.resolve(path);
|
|
40539
41120
|
isDirectory =
|
|
40540
41121
|
isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
|
|
40541
41122
|
// prevent duplicate handling in case of arriving here nearly simultaneously
|
|
@@ -40567,7 +41148,7 @@ class FSWatcher extends EventEmitter {
|
|
|
40567
41148
|
// If we wait for this file to be fully written, cancel the wait.
|
|
40568
41149
|
let relPath = path;
|
|
40569
41150
|
if (this.options.cwd)
|
|
40570
|
-
relPath =
|
|
41151
|
+
relPath = path$1.relative(this.options.cwd, path);
|
|
40571
41152
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
40572
41153
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
40573
41154
|
if (event === EVENTS.ADD)
|
|
@@ -40588,8 +41169,8 @@ class FSWatcher extends EventEmitter {
|
|
|
40588
41169
|
*/
|
|
40589
41170
|
_closePath(path) {
|
|
40590
41171
|
this._closeFile(path);
|
|
40591
|
-
const dir =
|
|
40592
|
-
this._getWatchedDir(dir).remove(
|
|
41172
|
+
const dir = path$1.dirname(path);
|
|
41173
|
+
this._getWatchedDir(dir).remove(path$1.basename(path));
|
|
40593
41174
|
}
|
|
40594
41175
|
/**
|
|
40595
41176
|
* Closes only file-specific watchers
|