@nasl/cli 0.1.6 → 0.1.7

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