@nasl/cli 0.1.6 → 0.1.8
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 +12 -0
- package/build/index.js +8 -0
- package/dist/bin/nasl.mjs +1930 -70
- package/dist/bin/nasl.mjs.map +1 -1
- package/dist/bin/naslc.mjs +47 -47
- package/dist/bin/naslc.mjs.map +1 -1
- package/dist/index.mjs +1929 -69
- package/dist/index.mjs.map +1 -1
- package/out/apis/createAxios.d.ts +1 -1
- package/out/apis/createAxios.d.ts.map +1 -1
- package/out/apis/createAxios.js +2 -2
- package/out/apis/createAxios.js.map +1 -1
- package/out/commands/compile.js +1 -1
- package/out/commands/compile.js.map +1 -1
- package/out/commands/dev.d.ts.map +1 -1
- package/out/commands/dev.js +191 -6
- package/out/commands/dev.js.map +1 -1
- package/out/utils/logger.d.ts +3 -0
- package/out/utils/logger.d.ts.map +1 -1
- package/out/utils/logger.js +9 -1
- package/out/utils/logger.js.map +1 -1
- package/package.json +3 -2
package/dist/bin/nasl.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
#!/usr/bin/env node
|
|
3
3
|
import require$$0$1, { EventEmitter } from 'events';
|
|
4
|
-
import require$$1, { spawnSync } from 'child_process';
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
7
|
-
import require$$0$2 from 'fs';
|
|
4
|
+
import require$$1, { spawn, spawnSync } from 'child_process';
|
|
5
|
+
import * as sysPath from 'path';
|
|
6
|
+
import sysPath__default from 'path';
|
|
7
|
+
import require$$0$2, { unwatchFile, watchFile, watch as watch$1, stat as stat$3 } from 'fs';
|
|
8
8
|
import require$$4 from 'process';
|
|
9
9
|
import require$$0$3 from 'constants';
|
|
10
10
|
import stream$4, { Readable } from 'stream';
|
|
11
11
|
import require$$0$4 from 'util';
|
|
12
12
|
import require$$5 from 'assert';
|
|
13
|
-
import require$$0$5 from 'os';
|
|
13
|
+
import require$$0$5, { type as type$1 } from 'os';
|
|
14
14
|
import require$$1$1 from 'tty';
|
|
15
15
|
import require$$3 from 'http';
|
|
16
16
|
import require$$4$1 from 'https';
|
|
@@ -18,6 +18,10 @@ import require$$0$6 from 'url';
|
|
|
18
18
|
import require$$8 from 'crypto';
|
|
19
19
|
import http2 from 'http2';
|
|
20
20
|
import zlib from 'zlib';
|
|
21
|
+
import { realpath as realpath$1, stat as stat$2, lstat as lstat$1, open, readdir as readdir$1 } from 'fs/promises';
|
|
22
|
+
import { lstat, stat as stat$1, readdir, realpath } from 'node:fs/promises';
|
|
23
|
+
import { Readable as Readable$1 } from 'node:stream';
|
|
24
|
+
import { resolve, join, relative, sep } from 'node:path';
|
|
21
25
|
|
|
22
26
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
23
27
|
|
|
@@ -1166,7 +1170,7 @@ function requireCommand () {
|
|
|
1166
1170
|
hasRequiredCommand = 1;
|
|
1167
1171
|
const EventEmitter = require$$0$1.EventEmitter;
|
|
1168
1172
|
const childProcess = require$$1;
|
|
1169
|
-
const path =
|
|
1173
|
+
const path = sysPath__default;
|
|
1170
1174
|
const fs = require$$0$2;
|
|
1171
1175
|
const process = require$$4;
|
|
1172
1176
|
|
|
@@ -4576,7 +4580,7 @@ var hasRequiredUtils$5;
|
|
|
4576
4580
|
function requireUtils$5 () {
|
|
4577
4581
|
if (hasRequiredUtils$5) return utils$7;
|
|
4578
4582
|
hasRequiredUtils$5 = 1;
|
|
4579
|
-
const path =
|
|
4583
|
+
const path = sysPath__default;
|
|
4580
4584
|
|
|
4581
4585
|
// https://github.com/nodejs/node/issues/8987
|
|
4582
4586
|
// https://github.com/libuv/libuv/pull/1088
|
|
@@ -4722,7 +4726,7 @@ function requireStat () {
|
|
|
4722
4726
|
hasRequiredStat = 1;
|
|
4723
4727
|
|
|
4724
4728
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
4725
|
-
const path =
|
|
4729
|
+
const path = sysPath__default;
|
|
4726
4730
|
const u = requireUniversalify().fromPromise;
|
|
4727
4731
|
|
|
4728
4732
|
function getStats (src, dest, opts) {
|
|
@@ -4926,7 +4930,7 @@ function requireCopy$1 () {
|
|
|
4926
4930
|
hasRequiredCopy$1 = 1;
|
|
4927
4931
|
|
|
4928
4932
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
4929
|
-
const path =
|
|
4933
|
+
const path = sysPath__default;
|
|
4930
4934
|
const { mkdirs } = /*@__PURE__*/ requireMkdirs();
|
|
4931
4935
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
4932
4936
|
const { utimesMillis } = /*@__PURE__*/ requireUtimes();
|
|
@@ -5109,7 +5113,7 @@ function requireCopySync () {
|
|
|
5109
5113
|
hasRequiredCopySync = 1;
|
|
5110
5114
|
|
|
5111
5115
|
const fs = requireGracefulFs();
|
|
5112
|
-
const path =
|
|
5116
|
+
const path = sysPath__default;
|
|
5113
5117
|
const mkdirsSync = /*@__PURE__*/ requireMkdirs().mkdirsSync;
|
|
5114
5118
|
const utimesMillisSync = /*@__PURE__*/ requireUtimes().utimesMillisSync;
|
|
5115
5119
|
const stat = /*@__PURE__*/ requireStat();
|
|
@@ -5329,7 +5333,7 @@ function requireEmpty () {
|
|
|
5329
5333
|
|
|
5330
5334
|
const u = requireUniversalify().fromPromise;
|
|
5331
5335
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5332
|
-
const path =
|
|
5336
|
+
const path = sysPath__default;
|
|
5333
5337
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5334
5338
|
const remove = /*@__PURE__*/ requireRemove();
|
|
5335
5339
|
|
|
@@ -5375,7 +5379,7 @@ function requireFile () {
|
|
|
5375
5379
|
hasRequiredFile = 1;
|
|
5376
5380
|
|
|
5377
5381
|
const u = requireUniversalify().fromPromise;
|
|
5378
|
-
const path =
|
|
5382
|
+
const path = sysPath__default;
|
|
5379
5383
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5380
5384
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5381
5385
|
|
|
@@ -5449,7 +5453,7 @@ function requireLink () {
|
|
|
5449
5453
|
hasRequiredLink = 1;
|
|
5450
5454
|
|
|
5451
5455
|
const u = requireUniversalify().fromPromise;
|
|
5452
|
-
const path =
|
|
5456
|
+
const path = sysPath__default;
|
|
5453
5457
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5454
5458
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5455
5459
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
@@ -5520,7 +5524,7 @@ function requireSymlinkPaths () {
|
|
|
5520
5524
|
if (hasRequiredSymlinkPaths) return symlinkPaths_1;
|
|
5521
5525
|
hasRequiredSymlinkPaths = 1;
|
|
5522
5526
|
|
|
5523
|
-
const path =
|
|
5527
|
+
const path = sysPath__default;
|
|
5524
5528
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5525
5529
|
const { pathExists } = /*@__PURE__*/ requirePathExists();
|
|
5526
5530
|
|
|
@@ -5672,7 +5676,7 @@ function requireSymlink () {
|
|
|
5672
5676
|
hasRequiredSymlink = 1;
|
|
5673
5677
|
|
|
5674
5678
|
const u = requireUniversalify().fromPromise;
|
|
5675
|
-
const path =
|
|
5679
|
+
const path = sysPath__default;
|
|
5676
5680
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5677
5681
|
|
|
5678
5682
|
const { mkdirs, mkdirsSync } = /*@__PURE__*/ requireMkdirs();
|
|
@@ -5918,7 +5922,7 @@ function requireOutputFile () {
|
|
|
5918
5922
|
|
|
5919
5923
|
const u = requireUniversalify().fromPromise;
|
|
5920
5924
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
5921
|
-
const path =
|
|
5925
|
+
const path = sysPath__default;
|
|
5922
5926
|
const mkdir = /*@__PURE__*/ requireMkdirs();
|
|
5923
5927
|
const pathExists = /*@__PURE__*/ requirePathExists().pathExists;
|
|
5924
5928
|
|
|
@@ -6020,7 +6024,7 @@ function requireMove$1 () {
|
|
|
6020
6024
|
hasRequiredMove$1 = 1;
|
|
6021
6025
|
|
|
6022
6026
|
const fs = /*@__PURE__*/ requireFs$4();
|
|
6023
|
-
const path =
|
|
6027
|
+
const path = sysPath__default;
|
|
6024
6028
|
const { copy } = /*@__PURE__*/ requireCopy();
|
|
6025
6029
|
const { remove } = /*@__PURE__*/ requireRemove();
|
|
6026
6030
|
const { mkdirp } = /*@__PURE__*/ requireMkdirs();
|
|
@@ -6087,7 +6091,7 @@ function requireMoveSync () {
|
|
|
6087
6091
|
hasRequiredMoveSync = 1;
|
|
6088
6092
|
|
|
6089
6093
|
const fs = requireGracefulFs();
|
|
6090
|
-
const path =
|
|
6094
|
+
const path = sysPath__default;
|
|
6091
6095
|
const copySync = /*@__PURE__*/ requireCopy().copySync;
|
|
6092
6096
|
const removeSync = /*@__PURE__*/ requireRemove().removeSync;
|
|
6093
6097
|
const mkdirpSync = /*@__PURE__*/ requireMkdirs().mkdirpSync;
|
|
@@ -8187,15 +8191,15 @@ var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
|
|
|
8187
8191
|
|
|
8188
8192
|
function fastLogToFile(entry, suffix, content) {
|
|
8189
8193
|
if (process.env.NASL_CLI_LOG_DIR) {
|
|
8190
|
-
const logDir =
|
|
8194
|
+
const logDir = sysPath.resolve(process.env.NASL_CLI_LOG_DIR);
|
|
8191
8195
|
libExports.ensureDirSync(logDir);
|
|
8192
8196
|
const timeInDir = (process.env.NASL_CLI_LOG_DIR.match(/\d{6,8}_(\d{6})-/)?.[1] || '00');
|
|
8193
8197
|
const now = dayjs();
|
|
8194
8198
|
const hourInDir = parseInt(timeInDir.slice(0, 2), 10);
|
|
8195
8199
|
// 跨天则累加 24 小时
|
|
8196
8200
|
const hourStr = (now.hour() >= hourInDir ? now.hour() : Math.ceil(hourInDir / 24) * 24 + now.hour()).toString().padStart(2, '0');
|
|
8197
|
-
const entryName =
|
|
8198
|
-
const filePath =
|
|
8201
|
+
const entryName = sysPath.basename(Array.isArray(entry) ? entry[0] || '*' : entry).replace(/[\[\]\(\)\{\}\*\+\-\?\|\^\$\,\/\#\:\\\s]+/g, '_');
|
|
8202
|
+
const filePath = sysPath.resolve(logDir, `${hourStr}${now.format('mmss_SSS')}-${entryName}-${suffix}`);
|
|
8199
8203
|
libExports.outputFileSync(filePath, content);
|
|
8200
8204
|
}
|
|
8201
8205
|
}
|
|
@@ -8226,16 +8230,16 @@ class BaseLogger {
|
|
|
8226
8230
|
}
|
|
8227
8231
|
debugToFile(topic, ...args) {
|
|
8228
8232
|
if (process.env.DEBUG_TO_FILE) {
|
|
8229
|
-
const baseDir =
|
|
8233
|
+
const baseDir = sysPath.join(process.cwd(), '.nasl');
|
|
8230
8234
|
const currentHour = dayjs().format('YYYYMMDD_HH');
|
|
8231
8235
|
const prevHour = dayjs().subtract(1, 'hour').format('YYYYMMDD_HH');
|
|
8232
8236
|
let subdir = `${topic}_${currentHour}`;
|
|
8233
8237
|
const prevSubdir = `${topic}_${prevHour}`;
|
|
8234
8238
|
// 判断如果有上一个小时的 subdir,则自己汇聚到上一个小时的 subdir 中
|
|
8235
|
-
if (libExports.existsSync(
|
|
8239
|
+
if (libExports.existsSync(sysPath.join(baseDir, prevSubdir)))
|
|
8236
8240
|
subdir = prevSubdir;
|
|
8237
8241
|
const fileName = `${topic}_${dayjs().format('YYYYMMDD_HHmmss')}.log`;
|
|
8238
|
-
const filePath =
|
|
8242
|
+
const filePath = sysPath.join(baseDir, subdir, fileName);
|
|
8239
8243
|
libExports.outputFileSync(filePath, args.join(' '));
|
|
8240
8244
|
}
|
|
8241
8245
|
}
|
|
@@ -8283,6 +8287,13 @@ class ConsoleLogger extends BaseLogger {
|
|
|
8283
8287
|
process.exit(code);
|
|
8284
8288
|
}
|
|
8285
8289
|
}
|
|
8290
|
+
class ConsoleLoggerWithoutExit extends ConsoleLogger {
|
|
8291
|
+
exit(code) {
|
|
8292
|
+
if (code)
|
|
8293
|
+
throw new Error(`Error with code: ${code}`);
|
|
8294
|
+
return undefined;
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8286
8297
|
const defaultLogger = new ConsoleLogger();
|
|
8287
8298
|
|
|
8288
8299
|
/**
|
|
@@ -8293,11 +8304,11 @@ function findConfigDir(startDir = process.cwd()) {
|
|
|
8293
8304
|
let currentDir = startDir;
|
|
8294
8305
|
// eslint-disable-next-line no-constant-condition
|
|
8295
8306
|
while (true) {
|
|
8296
|
-
const configPath =
|
|
8307
|
+
const configPath = sysPath.join(currentDir, CONFIG_FILE_NAME);
|
|
8297
8308
|
if (libExports.existsSync(configPath)) {
|
|
8298
8309
|
return currentDir;
|
|
8299
8310
|
}
|
|
8300
|
-
const parentDir =
|
|
8311
|
+
const parentDir = sysPath.dirname(currentDir);
|
|
8301
8312
|
// 已经到达根目录
|
|
8302
8313
|
if (parentDir === currentDir) {
|
|
8303
8314
|
return null;
|
|
@@ -8314,7 +8325,7 @@ function loadConfig(configDir) {
|
|
|
8314
8325
|
defaultLogger.error(`未找到配置文件 ${CONFIG_FILE_NAME},请先运行 nasl-init 初始化配置`);
|
|
8315
8326
|
return defaultLogger.exit(1);
|
|
8316
8327
|
}
|
|
8317
|
-
const configPath =
|
|
8328
|
+
const configPath = sysPath.join(dir, CONFIG_FILE_NAME);
|
|
8318
8329
|
try {
|
|
8319
8330
|
const content = libExports.readFileSync(configPath, 'utf-8');
|
|
8320
8331
|
const config = JSON.parse(content);
|
|
@@ -8359,7 +8370,7 @@ function readFileWithLog(filePath, logger) {
|
|
|
8359
8370
|
*/
|
|
8360
8371
|
function writeFileWithLog(filePath, content, logger) {
|
|
8361
8372
|
try {
|
|
8362
|
-
const dir =
|
|
8373
|
+
const dir = sysPath.dirname(filePath);
|
|
8363
8374
|
libExports.ensureDirSync(dir);
|
|
8364
8375
|
libExports.writeFileSync(filePath, content, 'utf-8');
|
|
8365
8376
|
}
|
|
@@ -8374,7 +8385,7 @@ function writeFileWithLog(filePath, content, logger) {
|
|
|
8374
8385
|
*/
|
|
8375
8386
|
function init() {
|
|
8376
8387
|
const cwd = process.cwd();
|
|
8377
|
-
const configPath =
|
|
8388
|
+
const configPath = sysPath.join(cwd, CONFIG_FILE_NAME);
|
|
8378
8389
|
// 检查配置文件是否已存在
|
|
8379
8390
|
if (libExports.existsSync(configPath)) {
|
|
8380
8391
|
defaultLogger.warn(`配置文件已存在: ${configPath}`);
|
|
@@ -8388,7 +8399,7 @@ function init() {
|
|
|
8388
8399
|
defaultLogger.info('\n配置内容:');
|
|
8389
8400
|
defaultLogger.info(JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
8390
8401
|
// 创建源代码目录
|
|
8391
|
-
const srcDirPath =
|
|
8402
|
+
const srcDirPath = sysPath.join(cwd, DEFAULT_CONFIG.srcDir);
|
|
8392
8403
|
libExports.ensureDirSync(srcDirPath);
|
|
8393
8404
|
defaultLogger.success(`源代码目录已创建: ${srcDirPath}`);
|
|
8394
8405
|
defaultLogger.info('\n提示:');
|
|
@@ -20380,7 +20391,7 @@ function requireMimeTypes () {
|
|
|
20380
20391
|
*/
|
|
20381
20392
|
|
|
20382
20393
|
var db = requireMimeDb();
|
|
20383
|
-
var extname =
|
|
20394
|
+
var extname = sysPath__default.extname;
|
|
20384
20395
|
|
|
20385
20396
|
/**
|
|
20386
20397
|
* Module variables.
|
|
@@ -22121,7 +22132,7 @@ function requireForm_data () {
|
|
|
22121
22132
|
|
|
22122
22133
|
var CombinedStream = requireCombined_stream();
|
|
22123
22134
|
var util = require$$0$4;
|
|
22124
|
-
var path =
|
|
22135
|
+
var path = sysPath__default;
|
|
22125
22136
|
var http = require$$3;
|
|
22126
22137
|
var https = require$$4$1;
|
|
22127
22138
|
var parseUrl = require$$0$6.parse;
|
|
@@ -26888,7 +26899,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
|
|
26888
26899
|
} else {
|
|
26889
26900
|
options.hostname = parsed.hostname.startsWith("[") ? parsed.hostname.slice(1, -1) : parsed.hostname;
|
|
26890
26901
|
options.port = parsed.port;
|
|
26891
|
-
setProxy(options, config.proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path);
|
|
26902
|
+
setProxy(options, config.proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : parsed.protocol === 'https:' ? '443' : '') + options.path);
|
|
26892
26903
|
}
|
|
26893
26904
|
|
|
26894
26905
|
let transport;
|
|
@@ -29204,7 +29215,7 @@ function requirePath () {
|
|
|
29204
29215
|
Object.defineProperty(path, "__esModule", { value: true });
|
|
29205
29216
|
path.convertPosixPathToPattern = path.convertWindowsPathToPattern = path.convertPathToPattern = path.escapePosixPath = path.escapeWindowsPath = path.escape = path.removeLeadingDotSegment = path.makeAbsolute = path.unixify = void 0;
|
|
29206
29217
|
const os = require$$0$5;
|
|
29207
|
-
const path$1 =
|
|
29218
|
+
const path$1 = sysPath__default;
|
|
29208
29219
|
const IS_WINDOWS_PLATFORM = os.platform() === 'win32';
|
|
29209
29220
|
const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\
|
|
29210
29221
|
/**
|
|
@@ -29469,7 +29480,7 @@ function requireGlobParent () {
|
|
|
29469
29480
|
hasRequiredGlobParent = 1;
|
|
29470
29481
|
|
|
29471
29482
|
var isGlob = requireIsGlob();
|
|
29472
|
-
var pathPosixDirname =
|
|
29483
|
+
var pathPosixDirname = sysPath__default.posix.dirname;
|
|
29473
29484
|
var isWin32 = require$$0$5.platform() === 'win32';
|
|
29474
29485
|
|
|
29475
29486
|
var slash = '/';
|
|
@@ -31041,7 +31052,7 @@ function requireConstants$1 () {
|
|
|
31041
31052
|
if (hasRequiredConstants$1) return constants$1;
|
|
31042
31053
|
hasRequiredConstants$1 = 1;
|
|
31043
31054
|
|
|
31044
|
-
const path =
|
|
31055
|
+
const path = sysPath__default;
|
|
31045
31056
|
const WIN_SLASH = '\\\\/';
|
|
31046
31057
|
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
31047
31058
|
|
|
@@ -31228,7 +31239,7 @@ function requireUtils$2 () {
|
|
|
31228
31239
|
hasRequiredUtils$2 = 1;
|
|
31229
31240
|
(function (exports$1) {
|
|
31230
31241
|
|
|
31231
|
-
const path =
|
|
31242
|
+
const path = sysPath__default;
|
|
31232
31243
|
const win32 = process.platform === 'win32';
|
|
31233
31244
|
const {
|
|
31234
31245
|
REGEX_BACKSLASH,
|
|
@@ -32799,7 +32810,7 @@ function requirePicomatch$1 () {
|
|
|
32799
32810
|
if (hasRequiredPicomatch$1) return picomatch_1;
|
|
32800
32811
|
hasRequiredPicomatch$1 = 1;
|
|
32801
32812
|
|
|
32802
|
-
const path =
|
|
32813
|
+
const path = sysPath__default;
|
|
32803
32814
|
const scan = requireScan();
|
|
32804
32815
|
const parse = requireParse();
|
|
32805
32816
|
const utils = requireUtils$2();
|
|
@@ -33642,7 +33653,7 @@ function requirePattern () {
|
|
|
33642
33653
|
hasRequiredPattern = 1;
|
|
33643
33654
|
Object.defineProperty(pattern, "__esModule", { value: true });
|
|
33644
33655
|
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;
|
|
33645
|
-
const path =
|
|
33656
|
+
const path = sysPath__default;
|
|
33646
33657
|
const globParent = requireGlobParent();
|
|
33647
33658
|
const micromatch = requireMicromatch();
|
|
33648
33659
|
const GLOBSTAR = '**';
|
|
@@ -34596,7 +34607,7 @@ function requireSettings$2 () {
|
|
|
34596
34607
|
if (hasRequiredSettings$2) return settings$2;
|
|
34597
34608
|
hasRequiredSettings$2 = 1;
|
|
34598
34609
|
Object.defineProperty(settings$2, "__esModule", { value: true });
|
|
34599
|
-
const path =
|
|
34610
|
+
const path = sysPath__default;
|
|
34600
34611
|
const fsStat = requireOut$3();
|
|
34601
34612
|
const fs = requireFs();
|
|
34602
34613
|
class Settings {
|
|
@@ -35358,7 +35369,7 @@ function requireSettings$1 () {
|
|
|
35358
35369
|
if (hasRequiredSettings$1) return settings$1;
|
|
35359
35370
|
hasRequiredSettings$1 = 1;
|
|
35360
35371
|
Object.defineProperty(settings$1, "__esModule", { value: true });
|
|
35361
|
-
const path =
|
|
35372
|
+
const path = sysPath__default;
|
|
35362
35373
|
const fsScandir = requireOut$2();
|
|
35363
35374
|
class Settings {
|
|
35364
35375
|
constructor(_options = {}) {
|
|
@@ -35434,7 +35445,7 @@ function requireReader () {
|
|
|
35434
35445
|
if (hasRequiredReader) return reader;
|
|
35435
35446
|
hasRequiredReader = 1;
|
|
35436
35447
|
Object.defineProperty(reader, "__esModule", { value: true });
|
|
35437
|
-
const path =
|
|
35448
|
+
const path = sysPath__default;
|
|
35438
35449
|
const fsStat = requireOut$3();
|
|
35439
35450
|
const utils = requireUtils$1();
|
|
35440
35451
|
class Reader {
|
|
@@ -35907,7 +35918,7 @@ function requireProvider () {
|
|
|
35907
35918
|
if (hasRequiredProvider) return provider;
|
|
35908
35919
|
hasRequiredProvider = 1;
|
|
35909
35920
|
Object.defineProperty(provider, "__esModule", { value: true });
|
|
35910
|
-
const path =
|
|
35921
|
+
const path = sysPath__default;
|
|
35911
35922
|
const deep_1 = requireDeep();
|
|
35912
35923
|
const entry_1 = requireEntry$1();
|
|
35913
35924
|
const error_1 = requireError();
|
|
@@ -36347,7 +36358,7 @@ var hasRequiredDirGlob;
|
|
|
36347
36358
|
function requireDirGlob () {
|
|
36348
36359
|
if (hasRequiredDirGlob) return dirGlob.exports;
|
|
36349
36360
|
hasRequiredDirGlob = 1;
|
|
36350
|
-
const path =
|
|
36361
|
+
const path = sysPath__default;
|
|
36351
36362
|
const pathType = requirePathType();
|
|
36352
36363
|
|
|
36353
36364
|
const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0];
|
|
@@ -37097,7 +37108,7 @@ function requireGitignore () {
|
|
|
37097
37108
|
hasRequiredGitignore = 1;
|
|
37098
37109
|
const {promisify} = require$$0$4;
|
|
37099
37110
|
const fs = require$$0$2;
|
|
37100
|
-
const path =
|
|
37111
|
+
const path = sysPath__default;
|
|
37101
37112
|
const fastGlob = requireOut();
|
|
37102
37113
|
const gitIgnore = requireIgnore();
|
|
37103
37114
|
const slash = requireSlash();
|
|
@@ -37533,7 +37544,7 @@ function replaceViewAsSignature(content) {
|
|
|
37533
37544
|
* @returns 文件信息和新发现的依赖文件列表
|
|
37534
37545
|
*/
|
|
37535
37546
|
function processFileDeps(pathRelativeToSrc, srcDir, matchedFileSet, processedFileMap, depNotFoundList, logger, verbose) {
|
|
37536
|
-
const absoluteFilePath =
|
|
37547
|
+
const absoluteFilePath = sysPath.join(srcDir, pathRelativeToSrc);
|
|
37537
37548
|
const fileInfo = { path: pathRelativeToSrc, content: '' };
|
|
37538
37549
|
try {
|
|
37539
37550
|
fileInfo.content = readFileWithLog(absoluteFilePath, logger);
|
|
@@ -37597,15 +37608,15 @@ async function collectDeps(patterns, projectRoot, srcDir, logger, verbose) {
|
|
|
37597
37608
|
throw new Error('未找到匹配的入口文件');
|
|
37598
37609
|
}
|
|
37599
37610
|
logger.info(`找到 ${matchedFiles.length} 个入口文件`);
|
|
37600
|
-
const absoluteSrcDir =
|
|
37611
|
+
const absoluteSrcDir = sysPath.resolve(srcDir);
|
|
37601
37612
|
const matchedFileSet = new Set();
|
|
37602
37613
|
matchedFiles.forEach((pathRelativeToRoot) => {
|
|
37603
37614
|
// 统一使用相对于 src 的路径
|
|
37604
|
-
const absoluteFilePath =
|
|
37615
|
+
const absoluteFilePath = sysPath.resolve(projectRoot, pathRelativeToRoot);
|
|
37605
37616
|
// 判断 file 是否超出 srcDir 目录(支持相对路径和绝对路径)
|
|
37606
37617
|
if (!absoluteFilePath.startsWith(absoluteSrcDir))
|
|
37607
37618
|
throw new Error(`入口文件 ${absoluteFilePath} 超出了源代码目录 ${srcDir}`);
|
|
37608
|
-
const pathRelativeToSrc =
|
|
37619
|
+
const pathRelativeToSrc = sysPath.relative(srcDir, absoluteFilePath);
|
|
37609
37620
|
filesToProcess.push(pathRelativeToSrc);
|
|
37610
37621
|
matchedFileSet.add(pathRelativeToSrc);
|
|
37611
37622
|
});
|
|
@@ -37654,7 +37665,7 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
37654
37665
|
const projectRoot = getProjectRoot();
|
|
37655
37666
|
logger.info(`项目根目录: ${projectRoot}`);
|
|
37656
37667
|
logger.info(`源代码目录: ${config.srcDir}`);
|
|
37657
|
-
const srcDir =
|
|
37668
|
+
const srcDir = sysPath.join(projectRoot, config.srcDir);
|
|
37658
37669
|
// 收集需要处理的文件
|
|
37659
37670
|
let collectedFiles = [];
|
|
37660
37671
|
if (Array.isArray(entry) && !entry.length) {
|
|
@@ -37688,7 +37699,7 @@ async function resolveNASLFiles(entry, logger, depMode, verbose) {
|
|
|
37688
37699
|
filePaths.sort(sorter);
|
|
37689
37700
|
collectedFiles = filePaths.map((filePath) => ({
|
|
37690
37701
|
path: filePath,
|
|
37691
|
-
content: readFileWithLog(
|
|
37702
|
+
content: readFileWithLog(sysPath.join(srcDir, filePath), logger),
|
|
37692
37703
|
}));
|
|
37693
37704
|
if (collectedFiles.length === 0) {
|
|
37694
37705
|
logger.warn('未找到 NASL 文件');
|
|
@@ -37713,7 +37724,7 @@ async function compile(entry, options) {
|
|
|
37713
37724
|
// 收集需要编译的文件
|
|
37714
37725
|
const { collectedFiles, config, projectRoot } = await resolveNASLFiles(entry, logger, false, options?.verbose);
|
|
37715
37726
|
logger.info(`输出目录: ${config.outDir}`);
|
|
37716
|
-
const outDir =
|
|
37727
|
+
const outDir = sysPath.join(projectRoot, config.outDir);
|
|
37717
37728
|
// 调用编译 API
|
|
37718
37729
|
logger.newLine();
|
|
37719
37730
|
logger.info('正在调用编译服务...');
|
|
@@ -37726,7 +37737,7 @@ async function compile(entry, options) {
|
|
|
37726
37737
|
logger.success('编译成功!');
|
|
37727
37738
|
// 写入输出文件
|
|
37728
37739
|
for (const file of outputFiles) {
|
|
37729
|
-
const outputPath =
|
|
37740
|
+
const outputPath = sysPath.join(outDir, file.path);
|
|
37730
37741
|
writeFileWithLog(outputPath, file.content, logger);
|
|
37731
37742
|
}
|
|
37732
37743
|
logger.info(`输出 ${outputFiles.length} 个文件`);
|
|
@@ -37739,11 +37750,11 @@ async function compile(entry, options) {
|
|
|
37739
37750
|
return { config, outDir };
|
|
37740
37751
|
}
|
|
37741
37752
|
async function tryCompile(entry, options) {
|
|
37742
|
-
const logger = options?.logger || defaultLogger;
|
|
37743
37753
|
try {
|
|
37744
37754
|
await compile(entry, options);
|
|
37745
37755
|
}
|
|
37746
37756
|
catch (error) {
|
|
37757
|
+
const logger = options?.logger || defaultLogger;
|
|
37747
37758
|
logger.error(`编译过程发生错误:${error.message}`);
|
|
37748
37759
|
logger.exit(1);
|
|
37749
37760
|
}
|
|
@@ -37794,24 +37805,189 @@ async function dep(entry, options) {
|
|
|
37794
37805
|
return resolveNASLFiles(entry, logger, true, true);
|
|
37795
37806
|
}
|
|
37796
37807
|
|
|
37797
|
-
|
|
37798
|
-
|
|
37799
|
-
|
|
37800
|
-
|
|
37801
|
-
|
|
37808
|
+
/**
|
|
37809
|
+
* 编译命令 - 将 NASL 编译为 Vue 和 JS
|
|
37810
|
+
*/
|
|
37811
|
+
async function dev(entry, options) {
|
|
37812
|
+
const logger = options?.logger || new ConsoleLoggerWithoutExit();
|
|
37813
|
+
// 获取项目根目录和配置
|
|
37814
|
+
const projectRoot = process.cwd();
|
|
37815
|
+
const configPath = sysPath.join(projectRoot, 'nasl.config.json');
|
|
37816
|
+
if (!libExports.existsSync(configPath)) {
|
|
37817
|
+
logger.error('未找到 nasl.config.json 配置文件,请先初始化项目');
|
|
37802
37818
|
process.exit(1);
|
|
37803
37819
|
}
|
|
37820
|
+
const configContent = libExports.readFileSync(configPath, 'utf-8');
|
|
37821
|
+
const config = JSON.parse(configContent);
|
|
37822
|
+
const srcDir = sysPath.join(projectRoot, config.srcDir);
|
|
37823
|
+
const outDir = sysPath.join(projectRoot, config.outDir);
|
|
37824
|
+
// 检查是否存在 src 目录
|
|
37825
|
+
const hasSrcDir = libExports.existsSync(srcDir) && libExports.statSync(srcDir).isDirectory();
|
|
37826
|
+
let lastCompileSuccess = false;
|
|
37827
|
+
if (hasSrcDir) {
|
|
37828
|
+
// 有 src 目录,先进行一次初始编译
|
|
37829
|
+
logger.info('检测到 src 目录,开始初始编译...');
|
|
37830
|
+
try {
|
|
37831
|
+
await compile(entry);
|
|
37832
|
+
lastCompileSuccess = true;
|
|
37833
|
+
logger.success('初始编译成功!');
|
|
37834
|
+
}
|
|
37835
|
+
catch (error) {
|
|
37836
|
+
logger.error(`初始编译失败:${error.message}`);
|
|
37837
|
+
// 即使编译失败,也要尝试启动服务(可能 out 目录已有旧文件)
|
|
37838
|
+
lastCompileSuccess = false;
|
|
37839
|
+
}
|
|
37840
|
+
}
|
|
37841
|
+
else {
|
|
37842
|
+
logger.info('未检测到 src 目录,将监听 src 目录的创建');
|
|
37843
|
+
}
|
|
37844
|
+
// 确保 out 目录存在
|
|
37845
|
+
if (!libExports.existsSync(outDir)) {
|
|
37846
|
+
logger.info('out 目录不存在,创建空的 out 目录');
|
|
37847
|
+
libExports.ensureDirSync(outDir);
|
|
37848
|
+
}
|
|
37849
|
+
// 启动 webpack 开发服务器
|
|
37850
|
+
const webpackProcess = await startWebpackDevServer(outDir, options, logger);
|
|
37851
|
+
const { default: chokidar } = await Promise.resolve().then(function () { return index$1; });
|
|
37852
|
+
// 监听 src 目录下的文件变化(即使目录不存在也可以监听)
|
|
37853
|
+
logger.info(`正在监听 src 目录: ${srcDir}`);
|
|
37854
|
+
const watcher = chokidar.watch(srcDir, {
|
|
37855
|
+
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
|
|
37856
|
+
ignoreInitial: true,
|
|
37857
|
+
persistent: true,
|
|
37858
|
+
awaitWriteFinish: {
|
|
37859
|
+
stabilityThreshold: 100,
|
|
37860
|
+
pollInterval: 10,
|
|
37861
|
+
},
|
|
37862
|
+
});
|
|
37863
|
+
// 只监听 ts/tsx/nasl 文件的变化
|
|
37864
|
+
const shouldHandleFile = (filePath) => {
|
|
37865
|
+
return /\.(ts|tsx|nasl|css)$/.test(filePath);
|
|
37866
|
+
};
|
|
37867
|
+
let isCompiling = false;
|
|
37868
|
+
let pendingCompile = false;
|
|
37869
|
+
const handleFileChange = async (filePath) => {
|
|
37870
|
+
// 过滤非目标文件类型
|
|
37871
|
+
if (!shouldHandleFile(filePath))
|
|
37872
|
+
return;
|
|
37873
|
+
logger.info(`检测到文件变化: ${sysPath.relative(projectRoot, filePath)}`);
|
|
37874
|
+
if (isCompiling) {
|
|
37875
|
+
pendingCompile = true;
|
|
37876
|
+
return;
|
|
37877
|
+
}
|
|
37878
|
+
isCompiling = true;
|
|
37879
|
+
pendingCompile = false;
|
|
37880
|
+
try {
|
|
37881
|
+
logger.newLine();
|
|
37882
|
+
logger.info('开始重新编译...');
|
|
37883
|
+
await compile(entry);
|
|
37884
|
+
logger.success('重新编译成功!');
|
|
37885
|
+
lastCompileSuccess = true;
|
|
37886
|
+
}
|
|
37887
|
+
catch (error) {
|
|
37888
|
+
logger.error(`重新编译失败:${error.message}`);
|
|
37889
|
+
logger.info('继续使用上次成功的编译结果提供服务');
|
|
37890
|
+
// 不重启 webpack,继续使用旧的 out 目录
|
|
37891
|
+
}
|
|
37892
|
+
finally {
|
|
37893
|
+
isCompiling = false;
|
|
37894
|
+
if (pendingCompile) {
|
|
37895
|
+
// 如果在编译期间有新的文件变化,立即处理
|
|
37896
|
+
setTimeout(() => handleFileChange(filePath), 0);
|
|
37897
|
+
}
|
|
37898
|
+
}
|
|
37899
|
+
};
|
|
37900
|
+
watcher.on('change', handleFileChange);
|
|
37901
|
+
watcher.on('add', handleFileChange);
|
|
37902
|
+
watcher.on('unlink', handleFileChange);
|
|
37903
|
+
// 监听 src 目录本身的创建
|
|
37904
|
+
watcher.on('addDir', async (dirPath) => {
|
|
37905
|
+
// 当 src 目录本身被创建时触发编译
|
|
37906
|
+
if (dirPath === srcDir) {
|
|
37907
|
+
logger.info('检测到 src 目录被创建');
|
|
37908
|
+
if (isCompiling) {
|
|
37909
|
+
pendingCompile = true;
|
|
37910
|
+
return;
|
|
37911
|
+
}
|
|
37912
|
+
isCompiling = true;
|
|
37913
|
+
pendingCompile = false;
|
|
37914
|
+
try {
|
|
37915
|
+
logger.newLine();
|
|
37916
|
+
logger.info('开始编译...');
|
|
37917
|
+
await compile(entry);
|
|
37918
|
+
logger.success('编译成功!');
|
|
37919
|
+
lastCompileSuccess = true;
|
|
37920
|
+
}
|
|
37921
|
+
catch (error) {
|
|
37922
|
+
logger.error(`编译失败:${error.message}`);
|
|
37923
|
+
}
|
|
37924
|
+
finally {
|
|
37925
|
+
isCompiling = false;
|
|
37926
|
+
if (pendingCompile) {
|
|
37927
|
+
setTimeout(() => handleFileChange(''), 0);
|
|
37928
|
+
}
|
|
37929
|
+
}
|
|
37930
|
+
}
|
|
37931
|
+
});
|
|
37932
|
+
// 监听 out 目录的删除,以便在 out 目录被删除时重新编译
|
|
37933
|
+
const outWatcher = chokidar.watch(outDir, {
|
|
37934
|
+
ignoreInitial: true,
|
|
37935
|
+
persistent: true,
|
|
37936
|
+
depth: 0, // 只监听 out 目录本身,不监听子目录
|
|
37937
|
+
});
|
|
37938
|
+
outWatcher.on('unlinkDir', async (deletedPath) => {
|
|
37939
|
+
if (deletedPath === outDir) {
|
|
37940
|
+
logger.warn('检测到 out 目录被删除');
|
|
37941
|
+
// 检查是否存在 src 目录
|
|
37942
|
+
const hasSrc = libExports.existsSync(srcDir) && libExports.statSync(srcDir).isDirectory();
|
|
37943
|
+
if (hasSrc) {
|
|
37944
|
+
logger.info('检测到 src 目录存在,开始重新编译...');
|
|
37945
|
+
if (isCompiling) {
|
|
37946
|
+
pendingCompile = true;
|
|
37947
|
+
return;
|
|
37948
|
+
}
|
|
37949
|
+
isCompiling = true;
|
|
37950
|
+
pendingCompile = false;
|
|
37951
|
+
try {
|
|
37952
|
+
// 确保 out 目录存在
|
|
37953
|
+
libExports.ensureDirSync(outDir);
|
|
37954
|
+
await compile(entry);
|
|
37955
|
+
logger.success('重新编译成功!out 目录已恢复');
|
|
37956
|
+
lastCompileSuccess = true;
|
|
37957
|
+
}
|
|
37958
|
+
catch (error) {
|
|
37959
|
+
logger.error(`重新编译失败:${error.message}`);
|
|
37960
|
+
}
|
|
37961
|
+
finally {
|
|
37962
|
+
isCompiling = false;
|
|
37963
|
+
if (pendingCompile) {
|
|
37964
|
+
setTimeout(() => handleFileChange(''), 0);
|
|
37965
|
+
}
|
|
37966
|
+
}
|
|
37967
|
+
}
|
|
37968
|
+
else {
|
|
37969
|
+
logger.warn('未检测到 src 目录,无法重新编译');
|
|
37970
|
+
}
|
|
37971
|
+
}
|
|
37972
|
+
});
|
|
37973
|
+
// 处理进程退出
|
|
37974
|
+
const cleanup = () => {
|
|
37975
|
+
if (webpackProcess) {
|
|
37976
|
+
webpackProcess.kill();
|
|
37977
|
+
}
|
|
37978
|
+
watcher.close().catch(() => { });
|
|
37979
|
+
outWatcher.close().catch(() => { });
|
|
37980
|
+
};
|
|
37981
|
+
process.on('SIGINT', cleanup);
|
|
37982
|
+
process.on('SIGTERM', cleanup);
|
|
37804
37983
|
}
|
|
37805
|
-
|
|
37806
37984
|
/**
|
|
37807
|
-
*
|
|
37985
|
+
* 启动 webpack 开发服务器
|
|
37808
37986
|
*/
|
|
37809
|
-
async function
|
|
37810
|
-
|
|
37811
|
-
|
|
37812
|
-
|
|
37813
|
-
logger.info('正在启动开发服务...');
|
|
37814
|
-
const configRelativePath = path$1.relative(outDir, path$1.resolve(__dirname, '../../build/webpack.config.js'));
|
|
37987
|
+
async function startWebpackDevServer(outDir, options, logger) {
|
|
37988
|
+
logger?.newLine();
|
|
37989
|
+
logger?.info('正在启动开发服务...');
|
|
37990
|
+
const configRelativePath = sysPath.relative(outDir, sysPath.resolve(__dirname, '../../build/webpack.config.js'));
|
|
37815
37991
|
// 构建 webpack-cli 参数
|
|
37816
37992
|
let webpackArgs = `${require.resolve('.bin/webpack-cli')} serve --config ${configRelativePath}`;
|
|
37817
37993
|
if (options?.port)
|
|
@@ -37822,7 +37998,26 @@ async function dev(entry, options) {
|
|
|
37822
37998
|
webpackArgs += ' --open';
|
|
37823
37999
|
if (options?.hot)
|
|
37824
38000
|
webpackArgs += ' --hot';
|
|
37825
|
-
|
|
38001
|
+
// 使用 spawn 而不是 spawnSync,以便进程可以常驻
|
|
38002
|
+
const webpackProcess = spawn(webpackArgs, {
|
|
38003
|
+
shell: true,
|
|
38004
|
+
stdio: 'inherit',
|
|
38005
|
+
cwd: outDir,
|
|
38006
|
+
});
|
|
38007
|
+
webpackProcess.on('error', (error) => {
|
|
38008
|
+
logger?.error(`Webpack 开发服务器启动失败: ${error.message}`);
|
|
38009
|
+
process.exit(1);
|
|
38010
|
+
});
|
|
38011
|
+
return webpackProcess;
|
|
38012
|
+
}
|
|
38013
|
+
|
|
38014
|
+
function justExecCommandSync(command, cwd) {
|
|
38015
|
+
console.log(command);
|
|
38016
|
+
const result = spawnSync(command, { shell: true, stdio: 'inherit', cwd });
|
|
38017
|
+
if (result.status) {
|
|
38018
|
+
console.error(String(result.stderr || result.stdout));
|
|
38019
|
+
process.exit(1);
|
|
38020
|
+
}
|
|
37826
38021
|
}
|
|
37827
38022
|
|
|
37828
38023
|
/**
|
|
@@ -37833,14 +38028,14 @@ async function build(entry, options) {
|
|
|
37833
38028
|
const { outDir } = await compile(entry);
|
|
37834
38029
|
logger.newLine();
|
|
37835
38030
|
logger.info('正在构建项目...');
|
|
37836
|
-
const configRelativePath =
|
|
38031
|
+
const configRelativePath = sysPath.relative(outDir, sysPath.resolve(__dirname, '../../build/webpack.config.js'));
|
|
37837
38032
|
const mode = options?.mode || 'production';
|
|
37838
38033
|
// 构建 webpack-cli 参数
|
|
37839
38034
|
let webpackArgs = `${require.resolve('.bin/webpack-cli')} build --config ${configRelativePath} --mode ${mode}`;
|
|
37840
38035
|
await justExecCommandSync(webpackArgs, outDir);
|
|
37841
38036
|
}
|
|
37842
38037
|
|
|
37843
|
-
var version = "0.1.
|
|
38038
|
+
var version = "0.1.8";
|
|
37844
38039
|
var pkg = {
|
|
37845
38040
|
version: version};
|
|
37846
38041
|
|
|
@@ -37927,4 +38122,1669 @@ program.parse(process.argv);
|
|
|
37927
38122
|
if (!process.argv.slice(2).length) {
|
|
37928
38123
|
program.outputHelp();
|
|
37929
38124
|
}
|
|
38125
|
+
|
|
38126
|
+
const EntryTypes = {
|
|
38127
|
+
FILE_TYPE: 'files',
|
|
38128
|
+
DIR_TYPE: 'directories',
|
|
38129
|
+
FILE_DIR_TYPE: 'files_directories',
|
|
38130
|
+
EVERYTHING_TYPE: 'all',
|
|
38131
|
+
};
|
|
38132
|
+
const defaultOptions = {
|
|
38133
|
+
root: '.',
|
|
38134
|
+
fileFilter: (_entryInfo) => true,
|
|
38135
|
+
directoryFilter: (_entryInfo) => true,
|
|
38136
|
+
type: EntryTypes.FILE_TYPE,
|
|
38137
|
+
lstat: false,
|
|
38138
|
+
depth: 2147483648,
|
|
38139
|
+
alwaysStat: false,
|
|
38140
|
+
highWaterMark: 4096,
|
|
38141
|
+
};
|
|
38142
|
+
Object.freeze(defaultOptions);
|
|
38143
|
+
const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
|
|
38144
|
+
const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);
|
|
38145
|
+
const ALL_TYPES = [
|
|
38146
|
+
EntryTypes.DIR_TYPE,
|
|
38147
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
38148
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
38149
|
+
EntryTypes.FILE_TYPE,
|
|
38150
|
+
];
|
|
38151
|
+
const DIR_TYPES = new Set([
|
|
38152
|
+
EntryTypes.DIR_TYPE,
|
|
38153
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
38154
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
38155
|
+
]);
|
|
38156
|
+
const FILE_TYPES = new Set([
|
|
38157
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
38158
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
38159
|
+
EntryTypes.FILE_TYPE,
|
|
38160
|
+
]);
|
|
38161
|
+
const isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
|
|
38162
|
+
const wantBigintFsStats = process.platform === 'win32';
|
|
38163
|
+
const emptyFn = (_entryInfo) => true;
|
|
38164
|
+
const normalizeFilter = (filter) => {
|
|
38165
|
+
if (filter === undefined)
|
|
38166
|
+
return emptyFn;
|
|
38167
|
+
if (typeof filter === 'function')
|
|
38168
|
+
return filter;
|
|
38169
|
+
if (typeof filter === 'string') {
|
|
38170
|
+
const fl = filter.trim();
|
|
38171
|
+
return (entry) => entry.basename === fl;
|
|
38172
|
+
}
|
|
38173
|
+
if (Array.isArray(filter)) {
|
|
38174
|
+
const trItems = filter.map((item) => item.trim());
|
|
38175
|
+
return (entry) => trItems.some((f) => entry.basename === f);
|
|
38176
|
+
}
|
|
38177
|
+
return emptyFn;
|
|
38178
|
+
};
|
|
38179
|
+
/** Readable readdir stream, emitting new files as they're being listed. */
|
|
38180
|
+
class ReaddirpStream extends Readable$1 {
|
|
38181
|
+
constructor(options = {}) {
|
|
38182
|
+
super({
|
|
38183
|
+
objectMode: true,
|
|
38184
|
+
autoDestroy: true,
|
|
38185
|
+
highWaterMark: options.highWaterMark,
|
|
38186
|
+
});
|
|
38187
|
+
const opts = { ...defaultOptions, ...options };
|
|
38188
|
+
const { root, type } = opts;
|
|
38189
|
+
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
38190
|
+
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
38191
|
+
const statMethod = opts.lstat ? lstat : stat$1;
|
|
38192
|
+
// Use bigint stats if it's windows and stat() supports options (node 10+).
|
|
38193
|
+
if (wantBigintFsStats) {
|
|
38194
|
+
this._stat = (path) => statMethod(path, { bigint: true });
|
|
38195
|
+
}
|
|
38196
|
+
else {
|
|
38197
|
+
this._stat = statMethod;
|
|
38198
|
+
}
|
|
38199
|
+
this._maxDepth = opts.depth ?? defaultOptions.depth;
|
|
38200
|
+
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
38201
|
+
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
38202
|
+
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
38203
|
+
this._root = resolve(root);
|
|
38204
|
+
this._isDirent = !opts.alwaysStat;
|
|
38205
|
+
this._statsProp = this._isDirent ? 'dirent' : 'stats';
|
|
38206
|
+
this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };
|
|
38207
|
+
// Launch stream with one parent, the root dir.
|
|
38208
|
+
this.parents = [this._exploreDir(root, 1)];
|
|
38209
|
+
this.reading = false;
|
|
38210
|
+
this.parent = undefined;
|
|
38211
|
+
}
|
|
38212
|
+
async _read(batch) {
|
|
38213
|
+
if (this.reading)
|
|
38214
|
+
return;
|
|
38215
|
+
this.reading = true;
|
|
38216
|
+
try {
|
|
38217
|
+
while (!this.destroyed && batch > 0) {
|
|
38218
|
+
const par = this.parent;
|
|
38219
|
+
const fil = par && par.files;
|
|
38220
|
+
if (fil && fil.length > 0) {
|
|
38221
|
+
const { path, depth } = par;
|
|
38222
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
|
|
38223
|
+
const awaited = await Promise.all(slice);
|
|
38224
|
+
for (const entry of awaited) {
|
|
38225
|
+
if (!entry)
|
|
38226
|
+
continue;
|
|
38227
|
+
if (this.destroyed)
|
|
38228
|
+
return;
|
|
38229
|
+
const entryType = await this._getEntryType(entry);
|
|
38230
|
+
if (entryType === 'directory' && this._directoryFilter(entry)) {
|
|
38231
|
+
if (depth <= this._maxDepth) {
|
|
38232
|
+
this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
|
|
38233
|
+
}
|
|
38234
|
+
if (this._wantsDir) {
|
|
38235
|
+
this.push(entry);
|
|
38236
|
+
batch--;
|
|
38237
|
+
}
|
|
38238
|
+
}
|
|
38239
|
+
else if ((entryType === 'file' || this._includeAsFile(entry)) &&
|
|
38240
|
+
this._fileFilter(entry)) {
|
|
38241
|
+
if (this._wantsFile) {
|
|
38242
|
+
this.push(entry);
|
|
38243
|
+
batch--;
|
|
38244
|
+
}
|
|
38245
|
+
}
|
|
38246
|
+
}
|
|
38247
|
+
}
|
|
38248
|
+
else {
|
|
38249
|
+
const parent = this.parents.pop();
|
|
38250
|
+
if (!parent) {
|
|
38251
|
+
this.push(null);
|
|
38252
|
+
break;
|
|
38253
|
+
}
|
|
38254
|
+
this.parent = await parent;
|
|
38255
|
+
if (this.destroyed)
|
|
38256
|
+
return;
|
|
38257
|
+
}
|
|
38258
|
+
}
|
|
38259
|
+
}
|
|
38260
|
+
catch (error) {
|
|
38261
|
+
this.destroy(error);
|
|
38262
|
+
}
|
|
38263
|
+
finally {
|
|
38264
|
+
this.reading = false;
|
|
38265
|
+
}
|
|
38266
|
+
}
|
|
38267
|
+
async _exploreDir(path, depth) {
|
|
38268
|
+
let files;
|
|
38269
|
+
try {
|
|
38270
|
+
files = await readdir(path, this._rdOptions);
|
|
38271
|
+
}
|
|
38272
|
+
catch (error) {
|
|
38273
|
+
this._onError(error);
|
|
38274
|
+
}
|
|
38275
|
+
return { files, depth, path };
|
|
38276
|
+
}
|
|
38277
|
+
async _formatEntry(dirent, path) {
|
|
38278
|
+
let entry;
|
|
38279
|
+
const basename = this._isDirent ? dirent.name : dirent;
|
|
38280
|
+
try {
|
|
38281
|
+
const fullPath = resolve(join(path, basename));
|
|
38282
|
+
entry = { path: relative(this._root, fullPath), fullPath, basename };
|
|
38283
|
+
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
38284
|
+
}
|
|
38285
|
+
catch (err) {
|
|
38286
|
+
this._onError(err);
|
|
38287
|
+
return;
|
|
38288
|
+
}
|
|
38289
|
+
return entry;
|
|
38290
|
+
}
|
|
38291
|
+
_onError(err) {
|
|
38292
|
+
if (isNormalFlowError(err) && !this.destroyed) {
|
|
38293
|
+
this.emit('warn', err);
|
|
38294
|
+
}
|
|
38295
|
+
else {
|
|
38296
|
+
this.destroy(err);
|
|
38297
|
+
}
|
|
38298
|
+
}
|
|
38299
|
+
async _getEntryType(entry) {
|
|
38300
|
+
// entry may be undefined, because a warning or an error were emitted
|
|
38301
|
+
// and the statsProp is undefined
|
|
38302
|
+
if (!entry && this._statsProp in entry) {
|
|
38303
|
+
return '';
|
|
38304
|
+
}
|
|
38305
|
+
const stats = entry[this._statsProp];
|
|
38306
|
+
if (stats.isFile())
|
|
38307
|
+
return 'file';
|
|
38308
|
+
if (stats.isDirectory())
|
|
38309
|
+
return 'directory';
|
|
38310
|
+
if (stats && stats.isSymbolicLink()) {
|
|
38311
|
+
const full = entry.fullPath;
|
|
38312
|
+
try {
|
|
38313
|
+
const entryRealPath = await realpath(full);
|
|
38314
|
+
const entryRealPathStats = await lstat(entryRealPath);
|
|
38315
|
+
if (entryRealPathStats.isFile()) {
|
|
38316
|
+
return 'file';
|
|
38317
|
+
}
|
|
38318
|
+
if (entryRealPathStats.isDirectory()) {
|
|
38319
|
+
const len = entryRealPath.length;
|
|
38320
|
+
if (full.startsWith(entryRealPath) && full.substr(len, 1) === sep) {
|
|
38321
|
+
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
38322
|
+
// @ts-ignore
|
|
38323
|
+
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
38324
|
+
return this._onError(recursiveError);
|
|
38325
|
+
}
|
|
38326
|
+
return 'directory';
|
|
38327
|
+
}
|
|
38328
|
+
}
|
|
38329
|
+
catch (error) {
|
|
38330
|
+
this._onError(error);
|
|
38331
|
+
return '';
|
|
38332
|
+
}
|
|
38333
|
+
}
|
|
38334
|
+
}
|
|
38335
|
+
_includeAsFile(entry) {
|
|
38336
|
+
const stats = entry && entry[this._statsProp];
|
|
38337
|
+
return stats && this._wantsEverything && !stats.isDirectory();
|
|
38338
|
+
}
|
|
38339
|
+
}
|
|
38340
|
+
/**
|
|
38341
|
+
* Streaming version: Reads all files and directories in given root recursively.
|
|
38342
|
+
* Consumes ~constant small amount of RAM.
|
|
38343
|
+
* @param root Root directory
|
|
38344
|
+
* @param options Options to specify root (start directory), filters and recursion depth
|
|
38345
|
+
*/
|
|
38346
|
+
function readdirp(root, options = {}) {
|
|
38347
|
+
// @ts-ignore
|
|
38348
|
+
let type = options.entryType || options.type;
|
|
38349
|
+
if (type === 'both')
|
|
38350
|
+
type = EntryTypes.FILE_DIR_TYPE; // backwards-compatibility
|
|
38351
|
+
if (type)
|
|
38352
|
+
options.type = type;
|
|
38353
|
+
if (!root) {
|
|
38354
|
+
throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
|
|
38355
|
+
}
|
|
38356
|
+
else if (typeof root !== 'string') {
|
|
38357
|
+
throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');
|
|
38358
|
+
}
|
|
38359
|
+
else if (type && !ALL_TYPES.includes(type)) {
|
|
38360
|
+
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
|
|
38361
|
+
}
|
|
38362
|
+
options.root = root;
|
|
38363
|
+
return new ReaddirpStream(options);
|
|
38364
|
+
}
|
|
38365
|
+
|
|
38366
|
+
const STR_DATA = 'data';
|
|
38367
|
+
const STR_END = 'end';
|
|
38368
|
+
const STR_CLOSE = 'close';
|
|
38369
|
+
const EMPTY_FN = () => { };
|
|
38370
|
+
const pl = process.platform;
|
|
38371
|
+
const isWindows = pl === 'win32';
|
|
38372
|
+
const isMacos = pl === 'darwin';
|
|
38373
|
+
const isLinux = pl === 'linux';
|
|
38374
|
+
const isFreeBSD = pl === 'freebsd';
|
|
38375
|
+
const isIBMi = type$1() === 'OS400';
|
|
38376
|
+
const EVENTS = {
|
|
38377
|
+
ALL: 'all',
|
|
38378
|
+
READY: 'ready',
|
|
38379
|
+
ADD: 'add',
|
|
38380
|
+
CHANGE: 'change',
|
|
38381
|
+
ADD_DIR: 'addDir',
|
|
38382
|
+
UNLINK: 'unlink',
|
|
38383
|
+
UNLINK_DIR: 'unlinkDir',
|
|
38384
|
+
RAW: 'raw',
|
|
38385
|
+
ERROR: 'error',
|
|
38386
|
+
};
|
|
38387
|
+
const EV = EVENTS;
|
|
38388
|
+
const THROTTLE_MODE_WATCH = 'watch';
|
|
38389
|
+
const statMethods = { lstat: lstat$1, stat: stat$2 };
|
|
38390
|
+
const KEY_LISTENERS = 'listeners';
|
|
38391
|
+
const KEY_ERR = 'errHandlers';
|
|
38392
|
+
const KEY_RAW = 'rawEmitters';
|
|
38393
|
+
const HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
|
|
38394
|
+
// prettier-ignore
|
|
38395
|
+
const binaryExtensions = new Set([
|
|
38396
|
+
'3dm', '3ds', '3g2', '3gp', '7z', 'a', 'aac', 'adp', 'afdesign', 'afphoto', 'afpub', 'ai',
|
|
38397
|
+
'aif', 'aiff', 'alz', 'ape', 'apk', 'appimage', 'ar', 'arj', 'asf', 'au', 'avi',
|
|
38398
|
+
'bak', 'baml', 'bh', 'bin', 'bk', 'bmp', 'btif', 'bz2', 'bzip2',
|
|
38399
|
+
'cab', 'caf', 'cgm', 'class', 'cmx', 'cpio', 'cr2', 'cur', 'dat', 'dcm', 'deb', 'dex', 'djvu',
|
|
38400
|
+
'dll', 'dmg', 'dng', 'doc', 'docm', 'docx', 'dot', 'dotm', 'dra', 'DS_Store', 'dsk', 'dts',
|
|
38401
|
+
'dtshd', 'dvb', 'dwg', 'dxf',
|
|
38402
|
+
'ecelp4800', 'ecelp7470', 'ecelp9600', 'egg', 'eol', 'eot', 'epub', 'exe',
|
|
38403
|
+
'f4v', 'fbs', 'fh', 'fla', 'flac', 'flatpak', 'fli', 'flv', 'fpx', 'fst', 'fvt',
|
|
38404
|
+
'g3', 'gh', 'gif', 'graffle', 'gz', 'gzip',
|
|
38405
|
+
'h261', 'h263', 'h264', 'icns', 'ico', 'ief', 'img', 'ipa', 'iso',
|
|
38406
|
+
'jar', 'jpeg', 'jpg', 'jpgv', 'jpm', 'jxr', 'key', 'ktx',
|
|
38407
|
+
'lha', 'lib', 'lvp', 'lz', 'lzh', 'lzma', 'lzo',
|
|
38408
|
+
'm3u', 'm4a', 'm4v', 'mar', 'mdi', 'mht', 'mid', 'midi', 'mj2', 'mka', 'mkv', 'mmr', 'mng',
|
|
38409
|
+
'mobi', 'mov', 'movie', 'mp3',
|
|
38410
|
+
'mp4', 'mp4a', 'mpeg', 'mpg', 'mpga', 'mxu',
|
|
38411
|
+
'nef', 'npx', 'numbers', 'nupkg',
|
|
38412
|
+
'o', 'odp', 'ods', 'odt', 'oga', 'ogg', 'ogv', 'otf', 'ott',
|
|
38413
|
+
'pages', 'pbm', 'pcx', 'pdb', 'pdf', 'pea', 'pgm', 'pic', 'png', 'pnm', 'pot', 'potm',
|
|
38414
|
+
'potx', 'ppa', 'ppam',
|
|
38415
|
+
'ppm', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'psd', 'pya', 'pyc', 'pyo', 'pyv',
|
|
38416
|
+
'qt',
|
|
38417
|
+
'rar', 'ras', 'raw', 'resources', 'rgb', 'rip', 'rlc', 'rmf', 'rmvb', 'rpm', 'rtf', 'rz',
|
|
38418
|
+
's3m', 's7z', 'scpt', 'sgi', 'shar', 'snap', 'sil', 'sketch', 'slk', 'smv', 'snk', 'so',
|
|
38419
|
+
'stl', 'suo', 'sub', 'swf',
|
|
38420
|
+
'tar', 'tbz', 'tbz2', 'tga', 'tgz', 'thmx', 'tif', 'tiff', 'tlz', 'ttc', 'ttf', 'txz',
|
|
38421
|
+
'udf', 'uvh', 'uvi', 'uvm', 'uvp', 'uvs', 'uvu',
|
|
38422
|
+
'viv', 'vob',
|
|
38423
|
+
'war', 'wav', 'wax', 'wbmp', 'wdp', 'weba', 'webm', 'webp', 'whl', 'wim', 'wm', 'wma',
|
|
38424
|
+
'wmv', 'wmx', 'woff', 'woff2', 'wrm', 'wvx',
|
|
38425
|
+
'xbm', 'xif', 'xla', 'xlam', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'xm',
|
|
38426
|
+
'xmind', 'xpi', 'xpm', 'xwd', 'xz',
|
|
38427
|
+
'z', 'zip', 'zipx',
|
|
38428
|
+
]);
|
|
38429
|
+
const isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
|
|
38430
|
+
// TODO: emit errors properly. Example: EMFILE on Macos.
|
|
38431
|
+
const foreach = (val, fn) => {
|
|
38432
|
+
if (val instanceof Set) {
|
|
38433
|
+
val.forEach(fn);
|
|
38434
|
+
}
|
|
38435
|
+
else {
|
|
38436
|
+
fn(val);
|
|
38437
|
+
}
|
|
38438
|
+
};
|
|
38439
|
+
const addAndConvert = (main, prop, item) => {
|
|
38440
|
+
let container = main[prop];
|
|
38441
|
+
if (!(container instanceof Set)) {
|
|
38442
|
+
main[prop] = container = new Set([container]);
|
|
38443
|
+
}
|
|
38444
|
+
container.add(item);
|
|
38445
|
+
};
|
|
38446
|
+
const clearItem = (cont) => (key) => {
|
|
38447
|
+
const set = cont[key];
|
|
38448
|
+
if (set instanceof Set) {
|
|
38449
|
+
set.clear();
|
|
38450
|
+
}
|
|
38451
|
+
else {
|
|
38452
|
+
delete cont[key];
|
|
38453
|
+
}
|
|
38454
|
+
};
|
|
38455
|
+
const delFromSet = (main, prop, item) => {
|
|
38456
|
+
const container = main[prop];
|
|
38457
|
+
if (container instanceof Set) {
|
|
38458
|
+
container.delete(item);
|
|
38459
|
+
}
|
|
38460
|
+
else if (container === item) {
|
|
38461
|
+
delete main[prop];
|
|
38462
|
+
}
|
|
38463
|
+
};
|
|
38464
|
+
const isEmptySet = (val) => (val instanceof Set ? val.size === 0 : !val);
|
|
38465
|
+
const FsWatchInstances = new Map();
|
|
38466
|
+
/**
|
|
38467
|
+
* Instantiates the fs_watch interface
|
|
38468
|
+
* @param path to be watched
|
|
38469
|
+
* @param options to be passed to fs_watch
|
|
38470
|
+
* @param listener main event handler
|
|
38471
|
+
* @param errHandler emits info about errors
|
|
38472
|
+
* @param emitRaw emits raw event data
|
|
38473
|
+
* @returns {NativeFsWatcher}
|
|
38474
|
+
*/
|
|
38475
|
+
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
|
|
38476
|
+
const handleEvent = (rawEvent, evPath) => {
|
|
38477
|
+
listener(path);
|
|
38478
|
+
emitRaw(rawEvent, evPath, { watchedPath: path });
|
|
38479
|
+
// emit based on events occurring for files from a directory's watcher in
|
|
38480
|
+
// case the file's watcher misses it (and rely on throttling to de-dupe)
|
|
38481
|
+
if (evPath && path !== evPath) {
|
|
38482
|
+
fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
|
|
38483
|
+
}
|
|
38484
|
+
};
|
|
38485
|
+
try {
|
|
38486
|
+
return watch$1(path, {
|
|
38487
|
+
persistent: options.persistent,
|
|
38488
|
+
}, handleEvent);
|
|
38489
|
+
}
|
|
38490
|
+
catch (error) {
|
|
38491
|
+
errHandler(error);
|
|
38492
|
+
return undefined;
|
|
38493
|
+
}
|
|
38494
|
+
}
|
|
38495
|
+
/**
|
|
38496
|
+
* Helper for passing fs_watch event data to a collection of listeners
|
|
38497
|
+
* @param fullPath absolute path bound to fs_watch instance
|
|
38498
|
+
*/
|
|
38499
|
+
const fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
38500
|
+
const cont = FsWatchInstances.get(fullPath);
|
|
38501
|
+
if (!cont)
|
|
38502
|
+
return;
|
|
38503
|
+
foreach(cont[listenerType], (listener) => {
|
|
38504
|
+
listener(val1, val2, val3);
|
|
38505
|
+
});
|
|
38506
|
+
};
|
|
38507
|
+
/**
|
|
38508
|
+
* Instantiates the fs_watch interface or binds listeners
|
|
38509
|
+
* to an existing one covering the same file system entry
|
|
38510
|
+
* @param path
|
|
38511
|
+
* @param fullPath absolute path
|
|
38512
|
+
* @param options to be passed to fs_watch
|
|
38513
|
+
* @param handlers container for event listener functions
|
|
38514
|
+
*/
|
|
38515
|
+
const setFsWatchListener = (path, fullPath, options, handlers) => {
|
|
38516
|
+
const { listener, errHandler, rawEmitter } = handlers;
|
|
38517
|
+
let cont = FsWatchInstances.get(fullPath);
|
|
38518
|
+
let watcher;
|
|
38519
|
+
if (!options.persistent) {
|
|
38520
|
+
watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);
|
|
38521
|
+
if (!watcher)
|
|
38522
|
+
return;
|
|
38523
|
+
return watcher.close.bind(watcher);
|
|
38524
|
+
}
|
|
38525
|
+
if (cont) {
|
|
38526
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
38527
|
+
addAndConvert(cont, KEY_ERR, errHandler);
|
|
38528
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
38529
|
+
}
|
|
38530
|
+
else {
|
|
38531
|
+
watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, // no need to use broadcast here
|
|
38532
|
+
fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
|
|
38533
|
+
if (!watcher)
|
|
38534
|
+
return;
|
|
38535
|
+
watcher.on(EV.ERROR, async (error) => {
|
|
38536
|
+
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
|
|
38537
|
+
if (cont)
|
|
38538
|
+
cont.watcherUnusable = true; // documented since Node 10.4.1
|
|
38539
|
+
// Workaround for https://github.com/joyent/node/issues/4337
|
|
38540
|
+
if (isWindows && error.code === 'EPERM') {
|
|
38541
|
+
try {
|
|
38542
|
+
const fd = await open(path, 'r');
|
|
38543
|
+
await fd.close();
|
|
38544
|
+
broadcastErr(error);
|
|
38545
|
+
}
|
|
38546
|
+
catch (err) {
|
|
38547
|
+
// do nothing
|
|
38548
|
+
}
|
|
38549
|
+
}
|
|
38550
|
+
else {
|
|
38551
|
+
broadcastErr(error);
|
|
38552
|
+
}
|
|
38553
|
+
});
|
|
38554
|
+
cont = {
|
|
38555
|
+
listeners: listener,
|
|
38556
|
+
errHandlers: errHandler,
|
|
38557
|
+
rawEmitters: rawEmitter,
|
|
38558
|
+
watcher,
|
|
38559
|
+
};
|
|
38560
|
+
FsWatchInstances.set(fullPath, cont);
|
|
38561
|
+
}
|
|
38562
|
+
// const index = cont.listeners.indexOf(listener);
|
|
38563
|
+
// removes this instance's listeners and closes the underlying fs_watch
|
|
38564
|
+
// instance if there are no more listeners left
|
|
38565
|
+
return () => {
|
|
38566
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
38567
|
+
delFromSet(cont, KEY_ERR, errHandler);
|
|
38568
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
38569
|
+
if (isEmptySet(cont.listeners)) {
|
|
38570
|
+
// Check to protect against issue gh-730.
|
|
38571
|
+
// if (cont.watcherUnusable) {
|
|
38572
|
+
cont.watcher.close();
|
|
38573
|
+
// }
|
|
38574
|
+
FsWatchInstances.delete(fullPath);
|
|
38575
|
+
HANDLER_KEYS.forEach(clearItem(cont));
|
|
38576
|
+
// @ts-ignore
|
|
38577
|
+
cont.watcher = undefined;
|
|
38578
|
+
Object.freeze(cont);
|
|
38579
|
+
}
|
|
38580
|
+
};
|
|
38581
|
+
};
|
|
38582
|
+
// fs_watchFile helpers
|
|
38583
|
+
// object to hold per-process fs_watchFile instances
|
|
38584
|
+
// (may be shared across chokidar FSWatcher instances)
|
|
38585
|
+
const FsWatchFileInstances = new Map();
|
|
38586
|
+
/**
|
|
38587
|
+
* Instantiates the fs_watchFile interface or binds listeners
|
|
38588
|
+
* to an existing one covering the same file system entry
|
|
38589
|
+
* @param path to be watched
|
|
38590
|
+
* @param fullPath absolute path
|
|
38591
|
+
* @param options options to be passed to fs_watchFile
|
|
38592
|
+
* @param handlers container for event listener functions
|
|
38593
|
+
* @returns closer
|
|
38594
|
+
*/
|
|
38595
|
+
const setFsWatchFileListener = (path, fullPath, options, handlers) => {
|
|
38596
|
+
const { listener, rawEmitter } = handlers;
|
|
38597
|
+
let cont = FsWatchFileInstances.get(fullPath);
|
|
38598
|
+
// let listeners = new Set();
|
|
38599
|
+
// let rawEmitters = new Set();
|
|
38600
|
+
const copts = cont && cont.options;
|
|
38601
|
+
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
|
|
38602
|
+
// "Upgrade" the watcher to persistence or a quicker interval.
|
|
38603
|
+
// This creates some unlikely edge case issues if the user mixes
|
|
38604
|
+
// settings in a very weird way, but solving for those cases
|
|
38605
|
+
// doesn't seem worthwhile for the added complexity.
|
|
38606
|
+
// listeners = cont.listeners;
|
|
38607
|
+
// rawEmitters = cont.rawEmitters;
|
|
38608
|
+
unwatchFile(fullPath);
|
|
38609
|
+
cont = undefined;
|
|
38610
|
+
}
|
|
38611
|
+
if (cont) {
|
|
38612
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
38613
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
38614
|
+
}
|
|
38615
|
+
else {
|
|
38616
|
+
// TODO
|
|
38617
|
+
// listeners.add(listener);
|
|
38618
|
+
// rawEmitters.add(rawEmitter);
|
|
38619
|
+
cont = {
|
|
38620
|
+
listeners: listener,
|
|
38621
|
+
rawEmitters: rawEmitter,
|
|
38622
|
+
options,
|
|
38623
|
+
watcher: watchFile(fullPath, options, (curr, prev) => {
|
|
38624
|
+
foreach(cont.rawEmitters, (rawEmitter) => {
|
|
38625
|
+
rawEmitter(EV.CHANGE, fullPath, { curr, prev });
|
|
38626
|
+
});
|
|
38627
|
+
const currmtime = curr.mtimeMs;
|
|
38628
|
+
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
38629
|
+
foreach(cont.listeners, (listener) => listener(path, curr));
|
|
38630
|
+
}
|
|
38631
|
+
}),
|
|
38632
|
+
};
|
|
38633
|
+
FsWatchFileInstances.set(fullPath, cont);
|
|
38634
|
+
}
|
|
38635
|
+
// const index = cont.listeners.indexOf(listener);
|
|
38636
|
+
// Removes this instance's listeners and closes the underlying fs_watchFile
|
|
38637
|
+
// instance if there are no more listeners left.
|
|
38638
|
+
return () => {
|
|
38639
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
38640
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
38641
|
+
if (isEmptySet(cont.listeners)) {
|
|
38642
|
+
FsWatchFileInstances.delete(fullPath);
|
|
38643
|
+
unwatchFile(fullPath);
|
|
38644
|
+
cont.options = cont.watcher = undefined;
|
|
38645
|
+
Object.freeze(cont);
|
|
38646
|
+
}
|
|
38647
|
+
};
|
|
38648
|
+
};
|
|
38649
|
+
/**
|
|
38650
|
+
* @mixin
|
|
38651
|
+
*/
|
|
38652
|
+
class NodeFsHandler {
|
|
38653
|
+
constructor(fsW) {
|
|
38654
|
+
this.fsw = fsW;
|
|
38655
|
+
this._boundHandleError = (error) => fsW._handleError(error);
|
|
38656
|
+
}
|
|
38657
|
+
/**
|
|
38658
|
+
* Watch file for changes with fs_watchFile or fs_watch.
|
|
38659
|
+
* @param path to file or dir
|
|
38660
|
+
* @param listener on fs change
|
|
38661
|
+
* @returns closer for the watcher instance
|
|
38662
|
+
*/
|
|
38663
|
+
_watchWithNodeFs(path, listener) {
|
|
38664
|
+
const opts = this.fsw.options;
|
|
38665
|
+
const directory = sysPath.dirname(path);
|
|
38666
|
+
const basename = sysPath.basename(path);
|
|
38667
|
+
const parent = this.fsw._getWatchedDir(directory);
|
|
38668
|
+
parent.add(basename);
|
|
38669
|
+
const absolutePath = sysPath.resolve(path);
|
|
38670
|
+
const options = {
|
|
38671
|
+
persistent: opts.persistent,
|
|
38672
|
+
};
|
|
38673
|
+
if (!listener)
|
|
38674
|
+
listener = EMPTY_FN;
|
|
38675
|
+
let closer;
|
|
38676
|
+
if (opts.usePolling) {
|
|
38677
|
+
const enableBin = opts.interval !== opts.binaryInterval;
|
|
38678
|
+
options.interval = enableBin && isBinaryPath(basename) ? opts.binaryInterval : opts.interval;
|
|
38679
|
+
closer = setFsWatchFileListener(path, absolutePath, options, {
|
|
38680
|
+
listener,
|
|
38681
|
+
rawEmitter: this.fsw._emitRaw,
|
|
38682
|
+
});
|
|
38683
|
+
}
|
|
38684
|
+
else {
|
|
38685
|
+
closer = setFsWatchListener(path, absolutePath, options, {
|
|
38686
|
+
listener,
|
|
38687
|
+
errHandler: this._boundHandleError,
|
|
38688
|
+
rawEmitter: this.fsw._emitRaw,
|
|
38689
|
+
});
|
|
38690
|
+
}
|
|
38691
|
+
return closer;
|
|
38692
|
+
}
|
|
38693
|
+
/**
|
|
38694
|
+
* Watch a file and emit add event if warranted.
|
|
38695
|
+
* @returns closer for the watcher instance
|
|
38696
|
+
*/
|
|
38697
|
+
_handleFile(file, stats, initialAdd) {
|
|
38698
|
+
if (this.fsw.closed) {
|
|
38699
|
+
return;
|
|
38700
|
+
}
|
|
38701
|
+
const dirname = sysPath.dirname(file);
|
|
38702
|
+
const basename = sysPath.basename(file);
|
|
38703
|
+
const parent = this.fsw._getWatchedDir(dirname);
|
|
38704
|
+
// stats is always present
|
|
38705
|
+
let prevStats = stats;
|
|
38706
|
+
// if the file is already being watched, do nothing
|
|
38707
|
+
if (parent.has(basename))
|
|
38708
|
+
return;
|
|
38709
|
+
const listener = async (path, newStats) => {
|
|
38710
|
+
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
38711
|
+
return;
|
|
38712
|
+
if (!newStats || newStats.mtimeMs === 0) {
|
|
38713
|
+
try {
|
|
38714
|
+
const newStats = await stat$2(file);
|
|
38715
|
+
if (this.fsw.closed)
|
|
38716
|
+
return;
|
|
38717
|
+
// Check that change event was not fired because of changed only accessTime.
|
|
38718
|
+
const at = newStats.atimeMs;
|
|
38719
|
+
const mt = newStats.mtimeMs;
|
|
38720
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
38721
|
+
this.fsw._emit(EV.CHANGE, file, newStats);
|
|
38722
|
+
}
|
|
38723
|
+
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats.ino) {
|
|
38724
|
+
this.fsw._closeFile(path);
|
|
38725
|
+
prevStats = newStats;
|
|
38726
|
+
const closer = this._watchWithNodeFs(file, listener);
|
|
38727
|
+
if (closer)
|
|
38728
|
+
this.fsw._addPathCloser(path, closer);
|
|
38729
|
+
}
|
|
38730
|
+
else {
|
|
38731
|
+
prevStats = newStats;
|
|
38732
|
+
}
|
|
38733
|
+
}
|
|
38734
|
+
catch (error) {
|
|
38735
|
+
// Fix issues where mtime is null but file is still present
|
|
38736
|
+
this.fsw._remove(dirname, basename);
|
|
38737
|
+
}
|
|
38738
|
+
// add is about to be emitted if file not already tracked in parent
|
|
38739
|
+
}
|
|
38740
|
+
else if (parent.has(basename)) {
|
|
38741
|
+
// Check that change event was not fired because of changed only accessTime.
|
|
38742
|
+
const at = newStats.atimeMs;
|
|
38743
|
+
const mt = newStats.mtimeMs;
|
|
38744
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
38745
|
+
this.fsw._emit(EV.CHANGE, file, newStats);
|
|
38746
|
+
}
|
|
38747
|
+
prevStats = newStats;
|
|
38748
|
+
}
|
|
38749
|
+
};
|
|
38750
|
+
// kick off the watcher
|
|
38751
|
+
const closer = this._watchWithNodeFs(file, listener);
|
|
38752
|
+
// emit an add event if we're supposed to
|
|
38753
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
|
|
38754
|
+
if (!this.fsw._throttle(EV.ADD, file, 0))
|
|
38755
|
+
return;
|
|
38756
|
+
this.fsw._emit(EV.ADD, file, stats);
|
|
38757
|
+
}
|
|
38758
|
+
return closer;
|
|
38759
|
+
}
|
|
38760
|
+
/**
|
|
38761
|
+
* Handle symlinks encountered while reading a dir.
|
|
38762
|
+
* @param entry returned by readdirp
|
|
38763
|
+
* @param directory path of dir being read
|
|
38764
|
+
* @param path of this item
|
|
38765
|
+
* @param item basename of this item
|
|
38766
|
+
* @returns true if no more processing is needed for this entry.
|
|
38767
|
+
*/
|
|
38768
|
+
async _handleSymlink(entry, directory, path, item) {
|
|
38769
|
+
if (this.fsw.closed) {
|
|
38770
|
+
return;
|
|
38771
|
+
}
|
|
38772
|
+
const full = entry.fullPath;
|
|
38773
|
+
const dir = this.fsw._getWatchedDir(directory);
|
|
38774
|
+
if (!this.fsw.options.followSymlinks) {
|
|
38775
|
+
// watch symlink directly (don't follow) and detect changes
|
|
38776
|
+
this.fsw._incrReadyCount();
|
|
38777
|
+
let linkPath;
|
|
38778
|
+
try {
|
|
38779
|
+
linkPath = await realpath$1(path);
|
|
38780
|
+
}
|
|
38781
|
+
catch (e) {
|
|
38782
|
+
this.fsw._emitReady();
|
|
38783
|
+
return true;
|
|
38784
|
+
}
|
|
38785
|
+
if (this.fsw.closed)
|
|
38786
|
+
return;
|
|
38787
|
+
if (dir.has(item)) {
|
|
38788
|
+
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
38789
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
38790
|
+
this.fsw._emit(EV.CHANGE, path, entry.stats);
|
|
38791
|
+
}
|
|
38792
|
+
}
|
|
38793
|
+
else {
|
|
38794
|
+
dir.add(item);
|
|
38795
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
38796
|
+
this.fsw._emit(EV.ADD, path, entry.stats);
|
|
38797
|
+
}
|
|
38798
|
+
this.fsw._emitReady();
|
|
38799
|
+
return true;
|
|
38800
|
+
}
|
|
38801
|
+
// don't follow the same symlink more than once
|
|
38802
|
+
if (this.fsw._symlinkPaths.has(full)) {
|
|
38803
|
+
return true;
|
|
38804
|
+
}
|
|
38805
|
+
this.fsw._symlinkPaths.set(full, true);
|
|
38806
|
+
}
|
|
38807
|
+
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
|
38808
|
+
// Normalize the directory name on Windows
|
|
38809
|
+
directory = sysPath.join(directory, '');
|
|
38810
|
+
throttler = this.fsw._throttle('readdir', directory, 1000);
|
|
38811
|
+
if (!throttler)
|
|
38812
|
+
return;
|
|
38813
|
+
const previous = this.fsw._getWatchedDir(wh.path);
|
|
38814
|
+
const current = new Set();
|
|
38815
|
+
let stream = this.fsw._readdirp(directory, {
|
|
38816
|
+
fileFilter: (entry) => wh.filterPath(entry),
|
|
38817
|
+
directoryFilter: (entry) => wh.filterDir(entry),
|
|
38818
|
+
});
|
|
38819
|
+
if (!stream)
|
|
38820
|
+
return;
|
|
38821
|
+
stream
|
|
38822
|
+
.on(STR_DATA, async (entry) => {
|
|
38823
|
+
if (this.fsw.closed) {
|
|
38824
|
+
stream = undefined;
|
|
38825
|
+
return;
|
|
38826
|
+
}
|
|
38827
|
+
const item = entry.path;
|
|
38828
|
+
let path = sysPath.join(directory, item);
|
|
38829
|
+
current.add(item);
|
|
38830
|
+
if (entry.stats.isSymbolicLink() &&
|
|
38831
|
+
(await this._handleSymlink(entry, directory, path, item))) {
|
|
38832
|
+
return;
|
|
38833
|
+
}
|
|
38834
|
+
if (this.fsw.closed) {
|
|
38835
|
+
stream = undefined;
|
|
38836
|
+
return;
|
|
38837
|
+
}
|
|
38838
|
+
// Files that present in current directory snapshot
|
|
38839
|
+
// but absent in previous are added to watch list and
|
|
38840
|
+
// emit `add` event.
|
|
38841
|
+
if (item === target || (!target && !previous.has(item))) {
|
|
38842
|
+
this.fsw._incrReadyCount();
|
|
38843
|
+
// ensure relativeness of path is preserved in case of watcher reuse
|
|
38844
|
+
path = sysPath.join(dir, sysPath.relative(dir, path));
|
|
38845
|
+
this._addToNodeFs(path, initialAdd, wh, depth + 1);
|
|
38846
|
+
}
|
|
38847
|
+
})
|
|
38848
|
+
.on(EV.ERROR, this._boundHandleError);
|
|
38849
|
+
return new Promise((resolve, reject) => {
|
|
38850
|
+
if (!stream)
|
|
38851
|
+
return reject();
|
|
38852
|
+
stream.once(STR_END, () => {
|
|
38853
|
+
if (this.fsw.closed) {
|
|
38854
|
+
stream = undefined;
|
|
38855
|
+
return;
|
|
38856
|
+
}
|
|
38857
|
+
const wasThrottled = throttler ? throttler.clear() : false;
|
|
38858
|
+
resolve(undefined);
|
|
38859
|
+
// Files that absent in current directory snapshot
|
|
38860
|
+
// but present in previous emit `remove` event
|
|
38861
|
+
// and are removed from @watched[directory].
|
|
38862
|
+
previous
|
|
38863
|
+
.getChildren()
|
|
38864
|
+
.filter((item) => {
|
|
38865
|
+
return item !== directory && !current.has(item);
|
|
38866
|
+
})
|
|
38867
|
+
.forEach((item) => {
|
|
38868
|
+
this.fsw._remove(directory, item);
|
|
38869
|
+
});
|
|
38870
|
+
stream = undefined;
|
|
38871
|
+
// one more time for any missed in case changes came in extremely quickly
|
|
38872
|
+
if (wasThrottled)
|
|
38873
|
+
this._handleRead(directory, false, wh, target, dir, depth, throttler);
|
|
38874
|
+
});
|
|
38875
|
+
});
|
|
38876
|
+
}
|
|
38877
|
+
/**
|
|
38878
|
+
* Read directory to add / remove files from `@watched` list and re-read it on change.
|
|
38879
|
+
* @param dir fs path
|
|
38880
|
+
* @param stats
|
|
38881
|
+
* @param initialAdd
|
|
38882
|
+
* @param depth relative to user-supplied path
|
|
38883
|
+
* @param target child path targeted for watch
|
|
38884
|
+
* @param wh Common watch helpers for this path
|
|
38885
|
+
* @param realpath
|
|
38886
|
+
* @returns closer for the watcher instance.
|
|
38887
|
+
*/
|
|
38888
|
+
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
|
|
38889
|
+
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
|
|
38890
|
+
const tracked = parentDir.has(sysPath.basename(dir));
|
|
38891
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
38892
|
+
this.fsw._emit(EV.ADD_DIR, dir, stats);
|
|
38893
|
+
}
|
|
38894
|
+
// ensure dir is tracked (harmless if redundant)
|
|
38895
|
+
parentDir.add(sysPath.basename(dir));
|
|
38896
|
+
this.fsw._getWatchedDir(dir);
|
|
38897
|
+
let throttler;
|
|
38898
|
+
let closer;
|
|
38899
|
+
const oDepth = this.fsw.options.depth;
|
|
38900
|
+
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
|
|
38901
|
+
if (!target) {
|
|
38902
|
+
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
|
|
38903
|
+
if (this.fsw.closed)
|
|
38904
|
+
return;
|
|
38905
|
+
}
|
|
38906
|
+
closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
|
|
38907
|
+
// if current directory is removed, do nothing
|
|
38908
|
+
if (stats && stats.mtimeMs === 0)
|
|
38909
|
+
return;
|
|
38910
|
+
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
|
|
38911
|
+
});
|
|
38912
|
+
}
|
|
38913
|
+
return closer;
|
|
38914
|
+
}
|
|
38915
|
+
/**
|
|
38916
|
+
* Handle added file, directory, or glob pattern.
|
|
38917
|
+
* Delegates call to _handleFile / _handleDir after checks.
|
|
38918
|
+
* @param path to file or ir
|
|
38919
|
+
* @param initialAdd was the file added at watch instantiation?
|
|
38920
|
+
* @param priorWh depth relative to user-supplied path
|
|
38921
|
+
* @param depth Child path actually targeted for watch
|
|
38922
|
+
* @param target Child path actually targeted for watch
|
|
38923
|
+
*/
|
|
38924
|
+
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
|
|
38925
|
+
const ready = this.fsw._emitReady;
|
|
38926
|
+
if (this.fsw._isIgnored(path) || this.fsw.closed) {
|
|
38927
|
+
ready();
|
|
38928
|
+
return false;
|
|
38929
|
+
}
|
|
38930
|
+
const wh = this.fsw._getWatchHelpers(path);
|
|
38931
|
+
if (priorWh) {
|
|
38932
|
+
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
38933
|
+
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
38934
|
+
}
|
|
38935
|
+
// evaluate what is at the path we're being asked to watch
|
|
38936
|
+
try {
|
|
38937
|
+
const stats = await statMethods[wh.statMethod](wh.watchPath);
|
|
38938
|
+
if (this.fsw.closed)
|
|
38939
|
+
return;
|
|
38940
|
+
if (this.fsw._isIgnored(wh.watchPath, stats)) {
|
|
38941
|
+
ready();
|
|
38942
|
+
return false;
|
|
38943
|
+
}
|
|
38944
|
+
const follow = this.fsw.options.followSymlinks;
|
|
38945
|
+
let closer;
|
|
38946
|
+
if (stats.isDirectory()) {
|
|
38947
|
+
const absPath = sysPath.resolve(path);
|
|
38948
|
+
const targetPath = follow ? await realpath$1(path) : path;
|
|
38949
|
+
if (this.fsw.closed)
|
|
38950
|
+
return;
|
|
38951
|
+
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
38952
|
+
if (this.fsw.closed)
|
|
38953
|
+
return;
|
|
38954
|
+
// preserve this symlink's target path
|
|
38955
|
+
if (absPath !== targetPath && targetPath !== undefined) {
|
|
38956
|
+
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
38957
|
+
}
|
|
38958
|
+
}
|
|
38959
|
+
else if (stats.isSymbolicLink()) {
|
|
38960
|
+
const targetPath = follow ? await realpath$1(path) : path;
|
|
38961
|
+
if (this.fsw.closed)
|
|
38962
|
+
return;
|
|
38963
|
+
const parent = sysPath.dirname(wh.watchPath);
|
|
38964
|
+
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
38965
|
+
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
38966
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
|
|
38967
|
+
if (this.fsw.closed)
|
|
38968
|
+
return;
|
|
38969
|
+
// preserve this symlink's target path
|
|
38970
|
+
if (targetPath !== undefined) {
|
|
38971
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
|
|
38972
|
+
}
|
|
38973
|
+
}
|
|
38974
|
+
else {
|
|
38975
|
+
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
38976
|
+
}
|
|
38977
|
+
ready();
|
|
38978
|
+
if (closer)
|
|
38979
|
+
this.fsw._addPathCloser(path, closer);
|
|
38980
|
+
return false;
|
|
38981
|
+
}
|
|
38982
|
+
catch (error) {
|
|
38983
|
+
if (this.fsw._handleError(error)) {
|
|
38984
|
+
ready();
|
|
38985
|
+
return path;
|
|
38986
|
+
}
|
|
38987
|
+
}
|
|
38988
|
+
}
|
|
38989
|
+
}
|
|
38990
|
+
|
|
38991
|
+
/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
|
|
38992
|
+
const SLASH = '/';
|
|
38993
|
+
const SLASH_SLASH = '//';
|
|
38994
|
+
const ONE_DOT = '.';
|
|
38995
|
+
const TWO_DOTS = '..';
|
|
38996
|
+
const STRING_TYPE = 'string';
|
|
38997
|
+
const BACK_SLASH_RE = /\\/g;
|
|
38998
|
+
const DOUBLE_SLASH_RE = /\/\//;
|
|
38999
|
+
const DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
|
|
39000
|
+
const REPLACER_RE = /^\.[/\\]/;
|
|
39001
|
+
function arrify(item) {
|
|
39002
|
+
return Array.isArray(item) ? item : [item];
|
|
39003
|
+
}
|
|
39004
|
+
const isMatcherObject = (matcher) => typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);
|
|
39005
|
+
function createPattern(matcher) {
|
|
39006
|
+
if (typeof matcher === 'function')
|
|
39007
|
+
return matcher;
|
|
39008
|
+
if (typeof matcher === 'string')
|
|
39009
|
+
return (string) => matcher === string;
|
|
39010
|
+
if (matcher instanceof RegExp)
|
|
39011
|
+
return (string) => matcher.test(string);
|
|
39012
|
+
if (typeof matcher === 'object' && matcher !== null) {
|
|
39013
|
+
return (string) => {
|
|
39014
|
+
if (matcher.path === string)
|
|
39015
|
+
return true;
|
|
39016
|
+
if (matcher.recursive) {
|
|
39017
|
+
const relative = sysPath.relative(matcher.path, string);
|
|
39018
|
+
if (!relative) {
|
|
39019
|
+
return false;
|
|
39020
|
+
}
|
|
39021
|
+
return !relative.startsWith('..') && !sysPath.isAbsolute(relative);
|
|
39022
|
+
}
|
|
39023
|
+
return false;
|
|
39024
|
+
};
|
|
39025
|
+
}
|
|
39026
|
+
return () => false;
|
|
39027
|
+
}
|
|
39028
|
+
function normalizePath(path) {
|
|
39029
|
+
if (typeof path !== 'string')
|
|
39030
|
+
throw new Error('string expected');
|
|
39031
|
+
path = sysPath.normalize(path);
|
|
39032
|
+
path = path.replace(/\\/g, '/');
|
|
39033
|
+
let prepend = false;
|
|
39034
|
+
if (path.startsWith('//'))
|
|
39035
|
+
prepend = true;
|
|
39036
|
+
const DOUBLE_SLASH_RE = /\/\//;
|
|
39037
|
+
while (path.match(DOUBLE_SLASH_RE))
|
|
39038
|
+
path = path.replace(DOUBLE_SLASH_RE, '/');
|
|
39039
|
+
if (prepend)
|
|
39040
|
+
path = '/' + path;
|
|
39041
|
+
return path;
|
|
39042
|
+
}
|
|
39043
|
+
function matchPatterns(patterns, testString, stats) {
|
|
39044
|
+
const path = normalizePath(testString);
|
|
39045
|
+
for (let index = 0; index < patterns.length; index++) {
|
|
39046
|
+
const pattern = patterns[index];
|
|
39047
|
+
if (pattern(path, stats)) {
|
|
39048
|
+
return true;
|
|
39049
|
+
}
|
|
39050
|
+
}
|
|
39051
|
+
return false;
|
|
39052
|
+
}
|
|
39053
|
+
function anymatch(matchers, testString) {
|
|
39054
|
+
if (matchers == null) {
|
|
39055
|
+
throw new TypeError('anymatch: specify first argument');
|
|
39056
|
+
}
|
|
39057
|
+
// Early cache for matchers.
|
|
39058
|
+
const matchersArray = arrify(matchers);
|
|
39059
|
+
const patterns = matchersArray.map((matcher) => createPattern(matcher));
|
|
39060
|
+
{
|
|
39061
|
+
return (testString, stats) => {
|
|
39062
|
+
return matchPatterns(patterns, testString, stats);
|
|
39063
|
+
};
|
|
39064
|
+
}
|
|
39065
|
+
}
|
|
39066
|
+
const unifyPaths = (paths_) => {
|
|
39067
|
+
const paths = arrify(paths_).flat();
|
|
39068
|
+
if (!paths.every((p) => typeof p === STRING_TYPE)) {
|
|
39069
|
+
throw new TypeError(`Non-string provided as watch path: ${paths}`);
|
|
39070
|
+
}
|
|
39071
|
+
return paths.map(normalizePathToUnix);
|
|
39072
|
+
};
|
|
39073
|
+
// If SLASH_SLASH occurs at the beginning of path, it is not replaced
|
|
39074
|
+
// because "//StoragePC/DrivePool/Movies" is a valid network path
|
|
39075
|
+
const toUnix = (string) => {
|
|
39076
|
+
let str = string.replace(BACK_SLASH_RE, SLASH);
|
|
39077
|
+
let prepend = false;
|
|
39078
|
+
if (str.startsWith(SLASH_SLASH)) {
|
|
39079
|
+
prepend = true;
|
|
39080
|
+
}
|
|
39081
|
+
while (str.match(DOUBLE_SLASH_RE)) {
|
|
39082
|
+
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
|
39083
|
+
}
|
|
39084
|
+
if (prepend) {
|
|
39085
|
+
str = SLASH + str;
|
|
39086
|
+
}
|
|
39087
|
+
return str;
|
|
39088
|
+
};
|
|
39089
|
+
// Our version of upath.normalize
|
|
39090
|
+
// TODO: this is not equal to path-normalize module - investigate why
|
|
39091
|
+
const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));
|
|
39092
|
+
// TODO: refactor
|
|
39093
|
+
const normalizeIgnored = (cwd = '') => (path) => {
|
|
39094
|
+
if (typeof path === 'string') {
|
|
39095
|
+
return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
|
|
39096
|
+
}
|
|
39097
|
+
else {
|
|
39098
|
+
return path;
|
|
39099
|
+
}
|
|
39100
|
+
};
|
|
39101
|
+
const getAbsolutePath = (path, cwd) => {
|
|
39102
|
+
if (sysPath.isAbsolute(path)) {
|
|
39103
|
+
return path;
|
|
39104
|
+
}
|
|
39105
|
+
return sysPath.join(cwd, path);
|
|
39106
|
+
};
|
|
39107
|
+
const EMPTY_SET = Object.freeze(new Set());
|
|
39108
|
+
/**
|
|
39109
|
+
* Directory entry.
|
|
39110
|
+
*/
|
|
39111
|
+
class DirEntry {
|
|
39112
|
+
constructor(dir, removeWatcher) {
|
|
39113
|
+
this.path = dir;
|
|
39114
|
+
this._removeWatcher = removeWatcher;
|
|
39115
|
+
this.items = new Set();
|
|
39116
|
+
}
|
|
39117
|
+
add(item) {
|
|
39118
|
+
const { items } = this;
|
|
39119
|
+
if (!items)
|
|
39120
|
+
return;
|
|
39121
|
+
if (item !== ONE_DOT && item !== TWO_DOTS)
|
|
39122
|
+
items.add(item);
|
|
39123
|
+
}
|
|
39124
|
+
async remove(item) {
|
|
39125
|
+
const { items } = this;
|
|
39126
|
+
if (!items)
|
|
39127
|
+
return;
|
|
39128
|
+
items.delete(item);
|
|
39129
|
+
if (items.size > 0)
|
|
39130
|
+
return;
|
|
39131
|
+
const dir = this.path;
|
|
39132
|
+
try {
|
|
39133
|
+
await readdir$1(dir);
|
|
39134
|
+
}
|
|
39135
|
+
catch (err) {
|
|
39136
|
+
if (this._removeWatcher) {
|
|
39137
|
+
this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
|
|
39138
|
+
}
|
|
39139
|
+
}
|
|
39140
|
+
}
|
|
39141
|
+
has(item) {
|
|
39142
|
+
const { items } = this;
|
|
39143
|
+
if (!items)
|
|
39144
|
+
return;
|
|
39145
|
+
return items.has(item);
|
|
39146
|
+
}
|
|
39147
|
+
getChildren() {
|
|
39148
|
+
const { items } = this;
|
|
39149
|
+
if (!items)
|
|
39150
|
+
return [];
|
|
39151
|
+
return [...items.values()];
|
|
39152
|
+
}
|
|
39153
|
+
dispose() {
|
|
39154
|
+
this.items.clear();
|
|
39155
|
+
this.path = '';
|
|
39156
|
+
this._removeWatcher = EMPTY_FN;
|
|
39157
|
+
this.items = EMPTY_SET;
|
|
39158
|
+
Object.freeze(this);
|
|
39159
|
+
}
|
|
39160
|
+
}
|
|
39161
|
+
const STAT_METHOD_F = 'stat';
|
|
39162
|
+
const STAT_METHOD_L = 'lstat';
|
|
39163
|
+
class WatchHelper {
|
|
39164
|
+
constructor(path, follow, fsw) {
|
|
39165
|
+
this.fsw = fsw;
|
|
39166
|
+
const watchPath = path;
|
|
39167
|
+
this.path = path = path.replace(REPLACER_RE, '');
|
|
39168
|
+
this.watchPath = watchPath;
|
|
39169
|
+
this.fullWatchPath = sysPath.resolve(watchPath);
|
|
39170
|
+
this.dirParts = [];
|
|
39171
|
+
this.dirParts.forEach((parts) => {
|
|
39172
|
+
if (parts.length > 1)
|
|
39173
|
+
parts.pop();
|
|
39174
|
+
});
|
|
39175
|
+
this.followSymlinks = follow;
|
|
39176
|
+
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
|
39177
|
+
}
|
|
39178
|
+
entryPath(entry) {
|
|
39179
|
+
return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));
|
|
39180
|
+
}
|
|
39181
|
+
filterPath(entry) {
|
|
39182
|
+
const { stats } = entry;
|
|
39183
|
+
if (stats && stats.isSymbolicLink())
|
|
39184
|
+
return this.filterDir(entry);
|
|
39185
|
+
const resolvedPath = this.entryPath(entry);
|
|
39186
|
+
// TODO: what if stats is undefined? remove !
|
|
39187
|
+
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
|
|
39188
|
+
}
|
|
39189
|
+
filterDir(entry) {
|
|
39190
|
+
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
|
|
39191
|
+
}
|
|
39192
|
+
}
|
|
39193
|
+
/**
|
|
39194
|
+
* Watches files & directories for changes. Emitted events:
|
|
39195
|
+
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
|
|
39196
|
+
*
|
|
39197
|
+
* new FSWatcher()
|
|
39198
|
+
* .add(directories)
|
|
39199
|
+
* .on('add', path => log('File', path, 'was added'))
|
|
39200
|
+
*/
|
|
39201
|
+
class FSWatcher extends EventEmitter {
|
|
39202
|
+
// Not indenting methods for history sake; for now.
|
|
39203
|
+
constructor(_opts = {}) {
|
|
39204
|
+
super();
|
|
39205
|
+
this.closed = false;
|
|
39206
|
+
this._closers = new Map();
|
|
39207
|
+
this._ignoredPaths = new Set();
|
|
39208
|
+
this._throttled = new Map();
|
|
39209
|
+
this._streams = new Set();
|
|
39210
|
+
this._symlinkPaths = new Map();
|
|
39211
|
+
this._watched = new Map();
|
|
39212
|
+
this._pendingWrites = new Map();
|
|
39213
|
+
this._pendingUnlinks = new Map();
|
|
39214
|
+
this._readyCount = 0;
|
|
39215
|
+
this._readyEmitted = false;
|
|
39216
|
+
const awf = _opts.awaitWriteFinish;
|
|
39217
|
+
const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };
|
|
39218
|
+
const opts = {
|
|
39219
|
+
// Defaults
|
|
39220
|
+
persistent: true,
|
|
39221
|
+
ignoreInitial: false,
|
|
39222
|
+
ignorePermissionErrors: false,
|
|
39223
|
+
interval: 100,
|
|
39224
|
+
binaryInterval: 300,
|
|
39225
|
+
followSymlinks: true,
|
|
39226
|
+
usePolling: false,
|
|
39227
|
+
// useAsync: false,
|
|
39228
|
+
atomic: true, // NOTE: overwritten later (depends on usePolling)
|
|
39229
|
+
..._opts,
|
|
39230
|
+
// Change format
|
|
39231
|
+
ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
|
|
39232
|
+
awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === 'object' ? { ...DEF_AWF, ...awf } : false,
|
|
39233
|
+
};
|
|
39234
|
+
// Always default to polling on IBM i because fs.watch() is not available on IBM i.
|
|
39235
|
+
if (isIBMi)
|
|
39236
|
+
opts.usePolling = true;
|
|
39237
|
+
// Editor atomic write normalization enabled by default with fs.watch
|
|
39238
|
+
if (opts.atomic === undefined)
|
|
39239
|
+
opts.atomic = !opts.usePolling;
|
|
39240
|
+
// opts.atomic = typeof _opts.atomic === 'number' ? _opts.atomic : 100;
|
|
39241
|
+
// Global override. Useful for developers, who need to force polling for all
|
|
39242
|
+
// instances of chokidar, regardless of usage / dependency depth
|
|
39243
|
+
const envPoll = process.env.CHOKIDAR_USEPOLLING;
|
|
39244
|
+
if (envPoll !== undefined) {
|
|
39245
|
+
const envLower = envPoll.toLowerCase();
|
|
39246
|
+
if (envLower === 'false' || envLower === '0')
|
|
39247
|
+
opts.usePolling = false;
|
|
39248
|
+
else if (envLower === 'true' || envLower === '1')
|
|
39249
|
+
opts.usePolling = true;
|
|
39250
|
+
else
|
|
39251
|
+
opts.usePolling = !!envLower;
|
|
39252
|
+
}
|
|
39253
|
+
const envInterval = process.env.CHOKIDAR_INTERVAL;
|
|
39254
|
+
if (envInterval)
|
|
39255
|
+
opts.interval = Number.parseInt(envInterval, 10);
|
|
39256
|
+
// This is done to emit ready only once, but each 'add' will increase that?
|
|
39257
|
+
let readyCalls = 0;
|
|
39258
|
+
this._emitReady = () => {
|
|
39259
|
+
readyCalls++;
|
|
39260
|
+
if (readyCalls >= this._readyCount) {
|
|
39261
|
+
this._emitReady = EMPTY_FN;
|
|
39262
|
+
this._readyEmitted = true;
|
|
39263
|
+
// use process.nextTick to allow time for listener to be bound
|
|
39264
|
+
process.nextTick(() => this.emit(EVENTS.READY));
|
|
39265
|
+
}
|
|
39266
|
+
};
|
|
39267
|
+
this._emitRaw = (...args) => this.emit(EVENTS.RAW, ...args);
|
|
39268
|
+
this._boundRemove = this._remove.bind(this);
|
|
39269
|
+
this.options = opts;
|
|
39270
|
+
this._nodeFsHandler = new NodeFsHandler(this);
|
|
39271
|
+
// You’re frozen when your heart’s not open.
|
|
39272
|
+
Object.freeze(opts);
|
|
39273
|
+
}
|
|
39274
|
+
_addIgnoredPath(matcher) {
|
|
39275
|
+
if (isMatcherObject(matcher)) {
|
|
39276
|
+
// return early if we already have a deeply equal matcher object
|
|
39277
|
+
for (const ignored of this._ignoredPaths) {
|
|
39278
|
+
if (isMatcherObject(ignored) &&
|
|
39279
|
+
ignored.path === matcher.path &&
|
|
39280
|
+
ignored.recursive === matcher.recursive) {
|
|
39281
|
+
return;
|
|
39282
|
+
}
|
|
39283
|
+
}
|
|
39284
|
+
}
|
|
39285
|
+
this._ignoredPaths.add(matcher);
|
|
39286
|
+
}
|
|
39287
|
+
_removeIgnoredPath(matcher) {
|
|
39288
|
+
this._ignoredPaths.delete(matcher);
|
|
39289
|
+
// now find any matcher objects with the matcher as path
|
|
39290
|
+
if (typeof matcher === 'string') {
|
|
39291
|
+
for (const ignored of this._ignoredPaths) {
|
|
39292
|
+
// TODO (43081j): make this more efficient.
|
|
39293
|
+
// probably just make a `this._ignoredDirectories` or some
|
|
39294
|
+
// such thing.
|
|
39295
|
+
if (isMatcherObject(ignored) && ignored.path === matcher) {
|
|
39296
|
+
this._ignoredPaths.delete(ignored);
|
|
39297
|
+
}
|
|
39298
|
+
}
|
|
39299
|
+
}
|
|
39300
|
+
}
|
|
39301
|
+
// Public methods
|
|
39302
|
+
/**
|
|
39303
|
+
* Adds paths to be watched on an existing FSWatcher instance.
|
|
39304
|
+
* @param paths_ file or file list. Other arguments are unused
|
|
39305
|
+
*/
|
|
39306
|
+
add(paths_, _origAdd, _internal) {
|
|
39307
|
+
const { cwd } = this.options;
|
|
39308
|
+
this.closed = false;
|
|
39309
|
+
this._closePromise = undefined;
|
|
39310
|
+
let paths = unifyPaths(paths_);
|
|
39311
|
+
if (cwd) {
|
|
39312
|
+
paths = paths.map((path) => {
|
|
39313
|
+
const absPath = getAbsolutePath(path, cwd);
|
|
39314
|
+
// Check `path` instead of `absPath` because the cwd portion can't be a glob
|
|
39315
|
+
return absPath;
|
|
39316
|
+
});
|
|
39317
|
+
}
|
|
39318
|
+
paths.forEach((path) => {
|
|
39319
|
+
this._removeIgnoredPath(path);
|
|
39320
|
+
});
|
|
39321
|
+
this._userIgnored = undefined;
|
|
39322
|
+
if (!this._readyCount)
|
|
39323
|
+
this._readyCount = 0;
|
|
39324
|
+
this._readyCount += paths.length;
|
|
39325
|
+
Promise.all(paths.map(async (path) => {
|
|
39326
|
+
const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, undefined, 0, _origAdd);
|
|
39327
|
+
if (res)
|
|
39328
|
+
this._emitReady();
|
|
39329
|
+
return res;
|
|
39330
|
+
})).then((results) => {
|
|
39331
|
+
if (this.closed)
|
|
39332
|
+
return;
|
|
39333
|
+
results.forEach((item) => {
|
|
39334
|
+
if (item)
|
|
39335
|
+
this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
|
|
39336
|
+
});
|
|
39337
|
+
});
|
|
39338
|
+
return this;
|
|
39339
|
+
}
|
|
39340
|
+
/**
|
|
39341
|
+
* Close watchers or start ignoring events from specified paths.
|
|
39342
|
+
*/
|
|
39343
|
+
unwatch(paths_) {
|
|
39344
|
+
if (this.closed)
|
|
39345
|
+
return this;
|
|
39346
|
+
const paths = unifyPaths(paths_);
|
|
39347
|
+
const { cwd } = this.options;
|
|
39348
|
+
paths.forEach((path) => {
|
|
39349
|
+
// convert to absolute path unless relative path already matches
|
|
39350
|
+
if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
|
|
39351
|
+
if (cwd)
|
|
39352
|
+
path = sysPath.join(cwd, path);
|
|
39353
|
+
path = sysPath.resolve(path);
|
|
39354
|
+
}
|
|
39355
|
+
this._closePath(path);
|
|
39356
|
+
this._addIgnoredPath(path);
|
|
39357
|
+
if (this._watched.has(path)) {
|
|
39358
|
+
this._addIgnoredPath({
|
|
39359
|
+
path,
|
|
39360
|
+
recursive: true,
|
|
39361
|
+
});
|
|
39362
|
+
}
|
|
39363
|
+
// reset the cached userIgnored anymatch fn
|
|
39364
|
+
// to make ignoredPaths changes effective
|
|
39365
|
+
this._userIgnored = undefined;
|
|
39366
|
+
});
|
|
39367
|
+
return this;
|
|
39368
|
+
}
|
|
39369
|
+
/**
|
|
39370
|
+
* Close watchers and remove all listeners from watched paths.
|
|
39371
|
+
*/
|
|
39372
|
+
close() {
|
|
39373
|
+
if (this._closePromise) {
|
|
39374
|
+
return this._closePromise;
|
|
39375
|
+
}
|
|
39376
|
+
this.closed = true;
|
|
39377
|
+
// Memory management.
|
|
39378
|
+
this.removeAllListeners();
|
|
39379
|
+
const closers = [];
|
|
39380
|
+
this._closers.forEach((closerList) => closerList.forEach((closer) => {
|
|
39381
|
+
const promise = closer();
|
|
39382
|
+
if (promise instanceof Promise)
|
|
39383
|
+
closers.push(promise);
|
|
39384
|
+
}));
|
|
39385
|
+
this._streams.forEach((stream) => stream.destroy());
|
|
39386
|
+
this._userIgnored = undefined;
|
|
39387
|
+
this._readyCount = 0;
|
|
39388
|
+
this._readyEmitted = false;
|
|
39389
|
+
this._watched.forEach((dirent) => dirent.dispose());
|
|
39390
|
+
this._closers.clear();
|
|
39391
|
+
this._watched.clear();
|
|
39392
|
+
this._streams.clear();
|
|
39393
|
+
this._symlinkPaths.clear();
|
|
39394
|
+
this._throttled.clear();
|
|
39395
|
+
this._closePromise = closers.length
|
|
39396
|
+
? Promise.all(closers).then(() => undefined)
|
|
39397
|
+
: Promise.resolve();
|
|
39398
|
+
return this._closePromise;
|
|
39399
|
+
}
|
|
39400
|
+
/**
|
|
39401
|
+
* Expose list of watched paths
|
|
39402
|
+
* @returns for chaining
|
|
39403
|
+
*/
|
|
39404
|
+
getWatched() {
|
|
39405
|
+
const watchList = {};
|
|
39406
|
+
this._watched.forEach((entry, dir) => {
|
|
39407
|
+
const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
|
|
39408
|
+
const index = key || ONE_DOT;
|
|
39409
|
+
watchList[index] = entry.getChildren().sort();
|
|
39410
|
+
});
|
|
39411
|
+
return watchList;
|
|
39412
|
+
}
|
|
39413
|
+
emitWithAll(event, args) {
|
|
39414
|
+
this.emit(event, ...args);
|
|
39415
|
+
if (event !== EVENTS.ERROR)
|
|
39416
|
+
this.emit(EVENTS.ALL, event, ...args);
|
|
39417
|
+
}
|
|
39418
|
+
// Common helpers
|
|
39419
|
+
// --------------
|
|
39420
|
+
/**
|
|
39421
|
+
* Normalize and emit events.
|
|
39422
|
+
* Calling _emit DOES NOT MEAN emit() would be called!
|
|
39423
|
+
* @param event Type of event
|
|
39424
|
+
* @param path File or directory path
|
|
39425
|
+
* @param stats arguments to be passed with event
|
|
39426
|
+
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
39427
|
+
*/
|
|
39428
|
+
async _emit(event, path, stats) {
|
|
39429
|
+
if (this.closed)
|
|
39430
|
+
return;
|
|
39431
|
+
const opts = this.options;
|
|
39432
|
+
if (isWindows)
|
|
39433
|
+
path = sysPath.normalize(path);
|
|
39434
|
+
if (opts.cwd)
|
|
39435
|
+
path = sysPath.relative(opts.cwd, path);
|
|
39436
|
+
const args = [path];
|
|
39437
|
+
if (stats != null)
|
|
39438
|
+
args.push(stats);
|
|
39439
|
+
const awf = opts.awaitWriteFinish;
|
|
39440
|
+
let pw;
|
|
39441
|
+
if (awf && (pw = this._pendingWrites.get(path))) {
|
|
39442
|
+
pw.lastChange = new Date();
|
|
39443
|
+
return this;
|
|
39444
|
+
}
|
|
39445
|
+
if (opts.atomic) {
|
|
39446
|
+
if (event === EVENTS.UNLINK) {
|
|
39447
|
+
this._pendingUnlinks.set(path, [event, ...args]);
|
|
39448
|
+
setTimeout(() => {
|
|
39449
|
+
this._pendingUnlinks.forEach((entry, path) => {
|
|
39450
|
+
this.emit(...entry);
|
|
39451
|
+
this.emit(EVENTS.ALL, ...entry);
|
|
39452
|
+
this._pendingUnlinks.delete(path);
|
|
39453
|
+
});
|
|
39454
|
+
}, typeof opts.atomic === 'number' ? opts.atomic : 100);
|
|
39455
|
+
return this;
|
|
39456
|
+
}
|
|
39457
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path)) {
|
|
39458
|
+
event = EVENTS.CHANGE;
|
|
39459
|
+
this._pendingUnlinks.delete(path);
|
|
39460
|
+
}
|
|
39461
|
+
}
|
|
39462
|
+
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
39463
|
+
const awfEmit = (err, stats) => {
|
|
39464
|
+
if (err) {
|
|
39465
|
+
event = EVENTS.ERROR;
|
|
39466
|
+
args[0] = err;
|
|
39467
|
+
this.emitWithAll(event, args);
|
|
39468
|
+
}
|
|
39469
|
+
else if (stats) {
|
|
39470
|
+
// if stats doesn't exist the file must have been deleted
|
|
39471
|
+
if (args.length > 1) {
|
|
39472
|
+
args[1] = stats;
|
|
39473
|
+
}
|
|
39474
|
+
else {
|
|
39475
|
+
args.push(stats);
|
|
39476
|
+
}
|
|
39477
|
+
this.emitWithAll(event, args);
|
|
39478
|
+
}
|
|
39479
|
+
};
|
|
39480
|
+
this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
|
|
39481
|
+
return this;
|
|
39482
|
+
}
|
|
39483
|
+
if (event === EVENTS.CHANGE) {
|
|
39484
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path, 50);
|
|
39485
|
+
if (isThrottled)
|
|
39486
|
+
return this;
|
|
39487
|
+
}
|
|
39488
|
+
if (opts.alwaysStat &&
|
|
39489
|
+
stats === undefined &&
|
|
39490
|
+
(event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
39491
|
+
const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
|
|
39492
|
+
let stats;
|
|
39493
|
+
try {
|
|
39494
|
+
stats = await stat$2(fullPath);
|
|
39495
|
+
}
|
|
39496
|
+
catch (err) {
|
|
39497
|
+
// do nothing
|
|
39498
|
+
}
|
|
39499
|
+
// Suppress event when fs_stat fails, to avoid sending undefined 'stat'
|
|
39500
|
+
if (!stats || this.closed)
|
|
39501
|
+
return;
|
|
39502
|
+
args.push(stats);
|
|
39503
|
+
}
|
|
39504
|
+
this.emitWithAll(event, args);
|
|
39505
|
+
return this;
|
|
39506
|
+
}
|
|
39507
|
+
/**
|
|
39508
|
+
* Common handler for errors
|
|
39509
|
+
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
39510
|
+
*/
|
|
39511
|
+
_handleError(error) {
|
|
39512
|
+
const code = error && error.code;
|
|
39513
|
+
if (error &&
|
|
39514
|
+
code !== 'ENOENT' &&
|
|
39515
|
+
code !== 'ENOTDIR' &&
|
|
39516
|
+
(!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))) {
|
|
39517
|
+
this.emit(EVENTS.ERROR, error);
|
|
39518
|
+
}
|
|
39519
|
+
return error || this.closed;
|
|
39520
|
+
}
|
|
39521
|
+
/**
|
|
39522
|
+
* Helper utility for throttling
|
|
39523
|
+
* @param actionType type being throttled
|
|
39524
|
+
* @param path being acted upon
|
|
39525
|
+
* @param timeout duration of time to suppress duplicate actions
|
|
39526
|
+
* @returns tracking object or false if action should be suppressed
|
|
39527
|
+
*/
|
|
39528
|
+
_throttle(actionType, path, timeout) {
|
|
39529
|
+
if (!this._throttled.has(actionType)) {
|
|
39530
|
+
this._throttled.set(actionType, new Map());
|
|
39531
|
+
}
|
|
39532
|
+
const action = this._throttled.get(actionType);
|
|
39533
|
+
if (!action)
|
|
39534
|
+
throw new Error('invalid throttle');
|
|
39535
|
+
const actionPath = action.get(path);
|
|
39536
|
+
if (actionPath) {
|
|
39537
|
+
actionPath.count++;
|
|
39538
|
+
return false;
|
|
39539
|
+
}
|
|
39540
|
+
// eslint-disable-next-line prefer-const
|
|
39541
|
+
let timeoutObject;
|
|
39542
|
+
const clear = () => {
|
|
39543
|
+
const item = action.get(path);
|
|
39544
|
+
const count = item ? item.count : 0;
|
|
39545
|
+
action.delete(path);
|
|
39546
|
+
clearTimeout(timeoutObject);
|
|
39547
|
+
if (item)
|
|
39548
|
+
clearTimeout(item.timeoutObject);
|
|
39549
|
+
return count;
|
|
39550
|
+
};
|
|
39551
|
+
timeoutObject = setTimeout(clear, timeout);
|
|
39552
|
+
const thr = { timeoutObject, clear, count: 0 };
|
|
39553
|
+
action.set(path, thr);
|
|
39554
|
+
return thr;
|
|
39555
|
+
}
|
|
39556
|
+
_incrReadyCount() {
|
|
39557
|
+
return this._readyCount++;
|
|
39558
|
+
}
|
|
39559
|
+
/**
|
|
39560
|
+
* Awaits write operation to finish.
|
|
39561
|
+
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
|
|
39562
|
+
* @param path being acted upon
|
|
39563
|
+
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
|
|
39564
|
+
* @param event
|
|
39565
|
+
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
39566
|
+
*/
|
|
39567
|
+
_awaitWriteFinish(path, threshold, event, awfEmit) {
|
|
39568
|
+
const awf = this.options.awaitWriteFinish;
|
|
39569
|
+
if (typeof awf !== 'object')
|
|
39570
|
+
return;
|
|
39571
|
+
const pollInterval = awf.pollInterval;
|
|
39572
|
+
let timeoutHandler;
|
|
39573
|
+
let fullPath = path;
|
|
39574
|
+
if (this.options.cwd && !sysPath.isAbsolute(path)) {
|
|
39575
|
+
fullPath = sysPath.join(this.options.cwd, path);
|
|
39576
|
+
}
|
|
39577
|
+
const now = new Date();
|
|
39578
|
+
const writes = this._pendingWrites;
|
|
39579
|
+
function awaitWriteFinishFn(prevStat) {
|
|
39580
|
+
stat$3(fullPath, (err, curStat) => {
|
|
39581
|
+
if (err || !writes.has(path)) {
|
|
39582
|
+
if (err && err.code !== 'ENOENT')
|
|
39583
|
+
awfEmit(err);
|
|
39584
|
+
return;
|
|
39585
|
+
}
|
|
39586
|
+
const now = Number(new Date());
|
|
39587
|
+
if (prevStat && curStat.size !== prevStat.size) {
|
|
39588
|
+
writes.get(path).lastChange = now;
|
|
39589
|
+
}
|
|
39590
|
+
const pw = writes.get(path);
|
|
39591
|
+
const df = now - pw.lastChange;
|
|
39592
|
+
if (df >= threshold) {
|
|
39593
|
+
writes.delete(path);
|
|
39594
|
+
awfEmit(undefined, curStat);
|
|
39595
|
+
}
|
|
39596
|
+
else {
|
|
39597
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
39598
|
+
}
|
|
39599
|
+
});
|
|
39600
|
+
}
|
|
39601
|
+
if (!writes.has(path)) {
|
|
39602
|
+
writes.set(path, {
|
|
39603
|
+
lastChange: now,
|
|
39604
|
+
cancelWait: () => {
|
|
39605
|
+
writes.delete(path);
|
|
39606
|
+
clearTimeout(timeoutHandler);
|
|
39607
|
+
return event;
|
|
39608
|
+
},
|
|
39609
|
+
});
|
|
39610
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
|
|
39611
|
+
}
|
|
39612
|
+
}
|
|
39613
|
+
/**
|
|
39614
|
+
* Determines whether user has asked to ignore this path.
|
|
39615
|
+
*/
|
|
39616
|
+
_isIgnored(path, stats) {
|
|
39617
|
+
if (this.options.atomic && DOT_RE.test(path))
|
|
39618
|
+
return true;
|
|
39619
|
+
if (!this._userIgnored) {
|
|
39620
|
+
const { cwd } = this.options;
|
|
39621
|
+
const ign = this.options.ignored;
|
|
39622
|
+
const ignored = (ign || []).map(normalizeIgnored(cwd));
|
|
39623
|
+
const ignoredPaths = [...this._ignoredPaths];
|
|
39624
|
+
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
39625
|
+
this._userIgnored = anymatch(list);
|
|
39626
|
+
}
|
|
39627
|
+
return this._userIgnored(path, stats);
|
|
39628
|
+
}
|
|
39629
|
+
_isntIgnored(path, stat) {
|
|
39630
|
+
return !this._isIgnored(path, stat);
|
|
39631
|
+
}
|
|
39632
|
+
/**
|
|
39633
|
+
* Provides a set of common helpers and properties relating to symlink handling.
|
|
39634
|
+
* @param path file or directory pattern being watched
|
|
39635
|
+
*/
|
|
39636
|
+
_getWatchHelpers(path) {
|
|
39637
|
+
return new WatchHelper(path, this.options.followSymlinks, this);
|
|
39638
|
+
}
|
|
39639
|
+
// Directory helpers
|
|
39640
|
+
// -----------------
|
|
39641
|
+
/**
|
|
39642
|
+
* Provides directory tracking objects
|
|
39643
|
+
* @param directory path of the directory
|
|
39644
|
+
*/
|
|
39645
|
+
_getWatchedDir(directory) {
|
|
39646
|
+
const dir = sysPath.resolve(directory);
|
|
39647
|
+
if (!this._watched.has(dir))
|
|
39648
|
+
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
|
39649
|
+
return this._watched.get(dir);
|
|
39650
|
+
}
|
|
39651
|
+
// File helpers
|
|
39652
|
+
// ------------
|
|
39653
|
+
/**
|
|
39654
|
+
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
|
|
39655
|
+
*/
|
|
39656
|
+
_hasReadPermissions(stats) {
|
|
39657
|
+
if (this.options.ignorePermissionErrors)
|
|
39658
|
+
return true;
|
|
39659
|
+
return Boolean(Number(stats.mode) & 0o400);
|
|
39660
|
+
}
|
|
39661
|
+
/**
|
|
39662
|
+
* Handles emitting unlink events for
|
|
39663
|
+
* files and directories, and via recursion, for
|
|
39664
|
+
* files and directories within directories that are unlinked
|
|
39665
|
+
* @param directory within which the following item is located
|
|
39666
|
+
* @param item base path of item/directory
|
|
39667
|
+
*/
|
|
39668
|
+
_remove(directory, item, isDirectory) {
|
|
39669
|
+
// if what is being deleted is a directory, get that directory's paths
|
|
39670
|
+
// for recursive deleting and cleaning of watched object
|
|
39671
|
+
// if it is not a directory, nestedDirectoryChildren will be empty array
|
|
39672
|
+
const path = sysPath.join(directory, item);
|
|
39673
|
+
const fullPath = sysPath.resolve(path);
|
|
39674
|
+
isDirectory =
|
|
39675
|
+
isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
|
|
39676
|
+
// prevent duplicate handling in case of arriving here nearly simultaneously
|
|
39677
|
+
// via multiple paths (such as _handleFile and _handleDir)
|
|
39678
|
+
if (!this._throttle('remove', path, 100))
|
|
39679
|
+
return;
|
|
39680
|
+
// if the only watched file is removed, watch for its return
|
|
39681
|
+
if (!isDirectory && this._watched.size === 1) {
|
|
39682
|
+
this.add(directory, item, true);
|
|
39683
|
+
}
|
|
39684
|
+
// This will create a new entry in the watched object in either case
|
|
39685
|
+
// so we got to do the directory check beforehand
|
|
39686
|
+
const wp = this._getWatchedDir(path);
|
|
39687
|
+
const nestedDirectoryChildren = wp.getChildren();
|
|
39688
|
+
// Recursively remove children directories / files.
|
|
39689
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));
|
|
39690
|
+
// Check if item was on the watched list and remove it
|
|
39691
|
+
const parent = this._getWatchedDir(directory);
|
|
39692
|
+
const wasTracked = parent.has(item);
|
|
39693
|
+
parent.remove(item);
|
|
39694
|
+
// Fixes issue #1042 -> Relative paths were detected and added as symlinks
|
|
39695
|
+
// (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
|
|
39696
|
+
// but never removed from the map in case the path was deleted.
|
|
39697
|
+
// This leads to an incorrect state if the path was recreated:
|
|
39698
|
+
// https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
|
|
39699
|
+
if (this._symlinkPaths.has(fullPath)) {
|
|
39700
|
+
this._symlinkPaths.delete(fullPath);
|
|
39701
|
+
}
|
|
39702
|
+
// If we wait for this file to be fully written, cancel the wait.
|
|
39703
|
+
let relPath = path;
|
|
39704
|
+
if (this.options.cwd)
|
|
39705
|
+
relPath = sysPath.relative(this.options.cwd, path);
|
|
39706
|
+
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
39707
|
+
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
39708
|
+
if (event === EVENTS.ADD)
|
|
39709
|
+
return;
|
|
39710
|
+
}
|
|
39711
|
+
// The Entry will either be a directory that just got removed
|
|
39712
|
+
// or a bogus entry to a file, in either case we have to remove it
|
|
39713
|
+
this._watched.delete(path);
|
|
39714
|
+
this._watched.delete(fullPath);
|
|
39715
|
+
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
39716
|
+
if (wasTracked && !this._isIgnored(path))
|
|
39717
|
+
this._emit(eventName, path);
|
|
39718
|
+
// Avoid conflicts if we later create another file with the same name
|
|
39719
|
+
this._closePath(path);
|
|
39720
|
+
}
|
|
39721
|
+
/**
|
|
39722
|
+
* Closes all watchers for a path
|
|
39723
|
+
*/
|
|
39724
|
+
_closePath(path) {
|
|
39725
|
+
this._closeFile(path);
|
|
39726
|
+
const dir = sysPath.dirname(path);
|
|
39727
|
+
this._getWatchedDir(dir).remove(sysPath.basename(path));
|
|
39728
|
+
}
|
|
39729
|
+
/**
|
|
39730
|
+
* Closes only file-specific watchers
|
|
39731
|
+
*/
|
|
39732
|
+
_closeFile(path) {
|
|
39733
|
+
const closers = this._closers.get(path);
|
|
39734
|
+
if (!closers)
|
|
39735
|
+
return;
|
|
39736
|
+
closers.forEach((closer) => closer());
|
|
39737
|
+
this._closers.delete(path);
|
|
39738
|
+
}
|
|
39739
|
+
_addPathCloser(path, closer) {
|
|
39740
|
+
if (!closer)
|
|
39741
|
+
return;
|
|
39742
|
+
let list = this._closers.get(path);
|
|
39743
|
+
if (!list) {
|
|
39744
|
+
list = [];
|
|
39745
|
+
this._closers.set(path, list);
|
|
39746
|
+
}
|
|
39747
|
+
list.push(closer);
|
|
39748
|
+
}
|
|
39749
|
+
_readdirp(root, opts) {
|
|
39750
|
+
if (this.closed)
|
|
39751
|
+
return;
|
|
39752
|
+
const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
|
|
39753
|
+
let stream = readdirp(root, options);
|
|
39754
|
+
this._streams.add(stream);
|
|
39755
|
+
stream.once(STR_CLOSE, () => {
|
|
39756
|
+
stream = undefined;
|
|
39757
|
+
});
|
|
39758
|
+
stream.once(STR_END, () => {
|
|
39759
|
+
if (stream) {
|
|
39760
|
+
this._streams.delete(stream);
|
|
39761
|
+
stream = undefined;
|
|
39762
|
+
}
|
|
39763
|
+
});
|
|
39764
|
+
return stream;
|
|
39765
|
+
}
|
|
39766
|
+
}
|
|
39767
|
+
/**
|
|
39768
|
+
* Instantiates watcher with paths to be tracked.
|
|
39769
|
+
* @param paths file / directory paths
|
|
39770
|
+
* @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others
|
|
39771
|
+
* @returns an instance of FSWatcher for chaining.
|
|
39772
|
+
* @example
|
|
39773
|
+
* const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });
|
|
39774
|
+
* watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })
|
|
39775
|
+
*/
|
|
39776
|
+
function watch(paths, options = {}) {
|
|
39777
|
+
const watcher = new FSWatcher(options);
|
|
39778
|
+
watcher.add(paths);
|
|
39779
|
+
return watcher;
|
|
39780
|
+
}
|
|
39781
|
+
var index = { watch, FSWatcher };
|
|
39782
|
+
|
|
39783
|
+
var index$1 = /*#__PURE__*/Object.freeze({
|
|
39784
|
+
__proto__: null,
|
|
39785
|
+
FSWatcher: FSWatcher,
|
|
39786
|
+
WatchHelper: WatchHelper,
|
|
39787
|
+
default: index,
|
|
39788
|
+
watch: watch
|
|
39789
|
+
});
|
|
37930
39790
|
//# sourceMappingURL=nasl.mjs.map
|