testem 3.19.0 → 3.20.0

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.
@@ -1,10 +1,9 @@
1
1
 
2
2
 
3
- const log = require('npmlog');
3
+ const log = require('../log');
4
4
  const BrowserTapConsumer = require('../browser_tap_consumer');
5
- const Bluebird = require('bluebird');
6
-
7
5
  const LogEntry = require('../utils/log-entry');
6
+ const { asCallback } = require('../utils/promises');
8
7
  const toResult = require('./to-result');
9
8
 
10
9
  module.exports = class BrowserTestRunner {
@@ -33,7 +32,7 @@ module.exports = class BrowserTestRunner {
33
32
  this.finished = false;
34
33
  this.pending = true;
35
34
 
36
- return new Bluebird.Promise(resolve => {
35
+ return new Promise(resolve => {
37
36
  this.onFinish = resolve;
38
37
 
39
38
  if (this.socket) {
@@ -41,26 +40,28 @@ module.exports = class BrowserTestRunner {
41
40
  } else {
42
41
  this.launcher.start().then(browserProcess => {
43
42
  this.process = browserProcess;
44
- this.process.on('processExit', this.onProcessExit.bind(this));
43
+ if (!this.launcher.settings.ignoreProcessExit) {
44
+ this.process.on('processExit', this.onProcessExit.bind(this));
45
+ }
45
46
  this.process.on('processError', this.onProcessError.bind(this));
46
47
  this.setupStartTimer();
47
48
  }).catch(err => {
48
49
  this.onProcessError(err);
49
50
  });
50
51
  }
51
- }).asCallback(onFinish);
52
+ }).then(...asCallback(onFinish));
52
53
  }
53
54
 
54
55
  stop(cb) {
55
56
  if (this.socket) {
56
57
  this.socket.emit('stop-run');
57
58
  }
58
- return Bluebird.resolve().asCallback(cb);
59
+ return Promise.resolve().then(...asCallback(cb));
59
60
  }
60
61
 
61
62
  exit() {
62
63
  if (!this.process) {
63
- return Bluebird.resolve();
64
+ return Promise.resolve();
64
65
  }
65
66
 
66
67
  // mark that somebody (generally app.js) has requested that the
@@ -119,6 +120,12 @@ module.exports = class BrowserTestRunner {
119
120
  socket.on('test-metadata', this.onTestMetadata.bind(this));
120
121
  socket.on('top-level-error', this.onGlobalError.bind(this));
121
122
 
123
+ socket.on('tap-debug', function(data) {
124
+ if (process.env.TESTEM_VERBOSE) {
125
+ log.verbose('tap-debug', data && data.msg);
126
+ }
127
+ });
128
+
122
129
  socket.on('browser-console', function(/* ...args */) {
123
130
  let args = Array.prototype.slice.call(arguments);
124
131
  let type = args.shift();
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
- const Bluebird = require('bluebird');
4
- const log = require('npmlog');
3
+ const { fromCallback, disposer } = require('../utils/promises');
4
+ const log = require('../log');
5
5
 
6
6
  const template = require('../utils/strutils').template;
7
7
  const ProcessCtl = require('../process-ctl');
@@ -11,13 +11,13 @@ module.exports = class HookRunner {
11
11
  this.config = config;
12
12
  }
13
13
 
14
- run(hook, data, callback) {
14
+ run(hook, data) {
15
15
  let hookCfg = this.config.get(hook);
16
16
  if (!hookCfg) {
17
- return Bluebird.resolve();
17
+ return Promise.resolve();
18
18
  }
19
19
  if (typeof hookCfg === 'function') {
20
- return Bluebird.fromCallback(callback => {
20
+ return fromCallback(callback => {
21
21
  hookCfg(this.config, data, callback);
22
22
  });
23
23
  }
@@ -55,9 +55,8 @@ module.exports = class HookRunner {
55
55
  } else if (exe) {
56
56
  args = this.varsub(args || []);
57
57
  hookProcessPromise = this.processCtl.spawn(exe, args, options);
58
- command = exe + ' ' + args.join(' ');
59
58
  } else {
60
- return Bluebird.reject(new Error('No command or exe/args specified for hook ' + hook));
59
+ return Promise.reject(new Error('No command or exe/args specified for hook ' + hook));
61
60
  }
62
61
 
63
62
  waitForTextTimeout = waitForTextTimeout || 10000;
@@ -65,7 +64,7 @@ module.exports = class HookRunner {
65
64
 
66
65
  let exited = false;
67
66
  let finished;
68
- let p = new Bluebird.Promise((resolve, reject) => {
67
+ let p = new Promise((resolve, reject) => {
69
68
  finished = (err, stdout, stderr) => {
70
69
  if (exited) {
71
70
  return;
@@ -109,7 +108,7 @@ module.exports = class HookRunner {
109
108
  }
110
109
  });
111
110
 
112
- return p.asCallback(callback);
111
+ return p;
113
112
  }
114
113
 
115
114
  varsubParams() {
@@ -135,17 +134,19 @@ module.exports = class HookRunner {
135
134
  return this.hookProcess.kill();
136
135
  }
137
136
 
138
- return Bluebird.resolve();
137
+ return Promise.resolve();
139
138
  }
140
139
 
141
140
  static with(config, hook, data) {
142
141
  let hookRunner = new HookRunner(config);
143
142
 
144
- return hookRunner.run(hook, data).catch(err => {
143
+ let runPromise = hookRunner.run(hook, data).catch(err => {
145
144
  log.error(hook + ' error', err);
146
145
 
147
146
  throw err;
148
- }).disposer(() => hookRunner.stop());
147
+ });
148
+
149
+ return disposer(runPromise, () => hookRunner.stop());
149
150
  }
150
151
  };
151
152
 
@@ -1,7 +1,6 @@
1
1
 
2
- var Bluebird = require('bluebird');
3
-
4
2
  var toResult = require('./to-result');
3
+ const { asCallback } = require('../utils/promises');
5
4
 
6
5
  class ProcessTestRunner {
7
6
  constructor(launcher, reporter) {
@@ -15,7 +14,7 @@ class ProcessTestRunner {
15
14
  this.onStart();
16
15
  this.finished = false;
17
16
 
18
- return new Bluebird.Promise((resolve, reject) => {
17
+ return new Promise((resolve, reject) => {
19
18
  this.onFinish = resolve;
20
19
  this.launcher.start().then(testProcess => {
21
20
  this.process = testProcess;
@@ -23,12 +22,12 @@ class ProcessTestRunner {
23
22
  this.process.once('processError', this.onProcessError.bind(this));
24
23
  return Promise.resolve(testProcess);
25
24
  }).catch(reject);
26
- }).asCallback(onFinish);
25
+ }).then(...asCallback(onFinish));
27
26
  }
28
27
 
29
28
  exit() {
30
29
  if (!this.process) {
31
- return Bluebird.resolve();
30
+ return Promise.resolve();
32
31
  }
33
32
 
34
33
  return this.process.kill();
@@ -1,10 +1,9 @@
1
1
 
2
2
 
3
3
  var TapConsumer = require('../tap_consumer');
4
- var log = require('npmlog');
5
- var Bluebird = require('bluebird');
6
-
4
+ var log = require('../log');
7
5
  var toResult = require('./to-result');
6
+ const { asCallback } = require('../utils/promises');
8
7
 
9
8
  class TapProcessTestRunner {
10
9
  constructor(launcher, reporter) {
@@ -23,19 +22,19 @@ class TapProcessTestRunner {
23
22
  this.tapConsumer.on('test-result', this.onTestResult.bind(this));
24
23
  this.tapConsumer.on('all-test-results', this.onAllTestResults.bind(this));
25
24
 
26
- return new Bluebird.Promise((resolve, reject) => {
25
+ return new Promise((resolve, reject) => {
27
26
  this.onFinish = resolve;
28
27
  this.launcher.start().then(tapProcess => {
29
28
  this.process = tapProcess;
30
29
  this.process.once('processError', this.onProcessError.bind(this));
31
30
  this.process.process.stdout.pipe(this.tapConsumer.stream);
32
31
  }).catch(reject);
33
- }).asCallback(onFinish);
32
+ }).then(...asCallback(onFinish));
34
33
  }
35
34
 
36
35
  exit() {
37
36
  if (!this.process) {
38
- return Bluebird.resolve();
37
+ return Promise.resolve();
39
38
  }
40
39
 
41
40
  return this.process.kill();
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- const log = require('npmlog');
3
+ const log = require('../log');
4
4
 
5
5
  module.exports = function toResult(launcherId, err, code, runnerProcess, config, testContext) {
6
6
  let logs = [];
@@ -14,15 +14,13 @@ var socketIO = require('socket.io');
14
14
  var compression = require('compression');
15
15
  var fs = require('fs');
16
16
  var path = require('path');
17
- var log = require('npmlog');
17
+ var log = require('../log');
18
18
  var EventEmitter = require('events').EventEmitter;
19
19
  var Mustache = require('consolidate').mustache;
20
20
  var http = require('http');
21
21
  var https = require('https');
22
22
  var httpProxy = require('http-proxy');
23
- var Bluebird = require('bluebird');
24
-
25
- var readFileAsync = Bluebird.promisify(fs.readFile);
23
+ const { fromCallback, each } = require('../utils/promises');
26
24
 
27
25
  class Server extends EventEmitter {
28
26
  constructor(config) {
@@ -37,7 +35,7 @@ class Server extends EventEmitter {
37
35
  this.nextSocketId = 0;
38
36
  }
39
37
 
40
- start(callback) {
38
+ start() {
41
39
  this.createExpress();
42
40
 
43
41
  // Start the server!
@@ -50,7 +48,7 @@ class Server extends EventEmitter {
50
48
  });
51
49
  });
52
50
 
53
- return new Bluebird.Promise((resolve, reject) => {
51
+ return new Promise((resolve, reject) => {
54
52
  this.server.on('listening', () => {
55
53
  this.config.set('port', this.server.address().port);
56
54
  resolve();
@@ -63,23 +61,23 @@ class Server extends EventEmitter {
63
61
  });
64
62
 
65
63
  this.server.listen(this.config.get('port'));
66
- }).asCallback(callback);
64
+ });
67
65
  }
68
66
 
69
- stop(callback) {
67
+ stop() {
70
68
  if (this.server && !this.stopped) {
71
69
  this.stopped = true;
72
70
 
73
- return Bluebird.fromCallback(closeCallback => {
71
+ return fromCallback(closeCallback => {
74
72
  this.server.close(closeCallback);
75
73
 
76
74
  // Destroy all open sockets
77
75
  for (var socketId in this.sockets) {
78
76
  this.sockets[socketId].destroy();
79
77
  }
80
- }).asCallback(callback);
78
+ });
81
79
  } else {
82
- return Bluebird.resolve().asCallback(callback);
80
+ return Promise.resolve();
83
81
  }
84
82
  }
85
83
 
@@ -105,8 +103,10 @@ class Server extends EventEmitter {
105
103
  })
106
104
  );
107
105
 
106
+ // In Express 5 (path-to-regexp v8), wildcard params like /*file are captured
107
+ // as an array of path segments, so we rejoin them with '/' to reconstruct the path.
108
108
  var serveStaticFile = (req, res) => {
109
- this.serveStaticFile(req.params[0], req, res);
109
+ this.serveStaticFile('/' + req.params.file.join('/'), req, res);
110
110
  };
111
111
 
112
112
  if (this.config.get('key') || this.config.get('pfx')) {
@@ -144,16 +144,31 @@ class Server extends EventEmitter {
144
144
  res.redirect(`/${String(Math.floor(Math.random() * 10000))}`);
145
145
  });
146
146
 
147
- app.get(/\/(-?[0-9]+)$/, (req, res) => {
148
- this.serveHomePage(req, res);
149
- });
150
-
151
147
  app.get('/testem.js', (req, res) => {
152
148
  this.serveTestemClientJs(req, res);
153
149
  });
154
150
 
155
- app.all(/^\/(?:-?[0-9]+)(\/.+)$/, serveStaticFile);
156
- app.all(/^(.+)$/, serveStaticFile);
151
+ // Express 5 dropped regex-in-param syntax (e.g. /:id(-?\d+)), so we match
152
+ // any single-segment :id and validate numerically in the handler.
153
+ app.get('/:id', (req, res, next) => {
154
+ if (/^-?\d+$/.test(req.params.id)) {
155
+ this.serveHomePage(req, res);
156
+ } else {
157
+ next();
158
+ }
159
+ });
160
+
161
+ // Serve static files prefixed with a numeric browser id (e.g. /1234/web/test.js).
162
+ // The numeric guard prevents /:id from greedily swallowing non-numeric paths.
163
+ app.all('/:id/*file', (req, res, next) => {
164
+ if (/^-?\d+$/.test(req.params.id)) {
165
+ serveStaticFile(req, res);
166
+ } else {
167
+ next();
168
+ }
169
+ });
170
+ // Catch-all: serve any other path as a static file.
171
+ app.all('/*file', serveStaticFile);
157
172
 
158
173
  app.use((err, req, res, next) => {
159
174
  if (err) {
@@ -231,7 +246,13 @@ class Server extends EventEmitter {
231
246
  });
232
247
  return;
233
248
  }
234
- app.all(`${url}*`, (req, res, next) => {
249
+ // Express 5 no longer accepts bare wildcard segments (e.g. /api*). We
250
+ // replace each * in the user-supplied proxy URL with a named param, then
251
+ // append an optional catch-all for trailing path segments.
252
+ let wIdx = 0;
253
+ const proxyPath =
254
+ url.replace(/\*/g, () => `:_proxyW${wIdx++}`).replace(/\/$/, '') + '{/*_proxyRest}';
255
+ app.all(proxyPath, (req, res, next) => {
235
256
  var opts = proxies[url];
236
257
  if (this.shouldProxy(req, opts)) {
237
258
  if (opts.host) {
@@ -281,11 +302,13 @@ class Server extends EventEmitter {
281
302
  var url = base + test_page;
282
303
  res.redirect(url);
283
304
  } else {
284
- config.getServeFiles((err, files) => {
285
- config.getFooterScripts((err, footerScripts) => {
286
- this.renderRunnerPage(err, files, footerScripts, res);
305
+ Promise.all([config.getServeFiles(), config.getFooterScripts()])
306
+ .then(([files, footerScripts]) => {
307
+ this.renderRunnerPage(null, files, footerScripts, res);
308
+ })
309
+ .catch(err => {
310
+ this.renderRunnerPage(err, [], [], res);
287
311
  });
288
- });
289
312
  }
290
313
  }
291
314
 
@@ -314,11 +337,11 @@ class Server extends EventEmitter {
314
337
  'mocha_adapter.js',
315
338
  'testem_client.js'
316
339
  ];
317
- Bluebird.each(files, file => {
340
+ each(files, file => {
318
341
  if (file.indexOf(path.sep) === -1) {
319
342
  file = `${__dirname}/../../public/testem/${file}`;
320
343
  }
321
- return readFileAsync(file).then(data => {
344
+ return fs.promises.readFile(file).then(data => {
322
345
  res.write(`\n//============== ${path.basename(file)} ==================\n\n`);
323
346
  res.write(data);
324
347
  }).catch(err => {
@@ -376,20 +399,21 @@ class Server extends EventEmitter {
376
399
  this.emit('file-requested', filePath);
377
400
  });
378
401
  } else {
379
- fs.stat(filePath, (err, stat) => {
380
- this.emit('file-requested', filePath);
381
- if (err) {
382
- return res.sendFile(filePath);
383
- }
384
- if (stat.isDirectory()) {
385
- fs.readdir(filePath, (err, files) => {
386
- var dirListingPage = `${__dirname}/../../views/directorylisting.mustache`;
387
- res.render(dirListingPage, { files: files });
388
- });
389
- } else {
402
+ fs.promises.stat(filePath)
403
+ .then(stat => {
404
+ this.emit('file-requested', filePath);
405
+ if (stat.isDirectory()) {
406
+ return fs.promises.readdir(filePath).then(files => {
407
+ var dirListingPage = `${__dirname}/../../views/directorylisting.mustache`;
408
+ res.render(dirListingPage, { files: files });
409
+ });
410
+ }
390
411
  res.sendFile(filePath);
391
- }
392
- });
412
+ })
413
+ .catch(() => {
414
+ this.emit('file-requested', filePath);
415
+ res.sendFile(filePath);
416
+ });
393
417
  }
394
418
  }
395
419
 
@@ -3,40 +3,92 @@
3
3
  const yaml = require('js-yaml');
4
4
  const extend = Object.assign;
5
5
  const EventEmitter = require('events').EventEmitter;
6
- const TapParser = require('tap-parser');
7
- const log = require('npmlog');
6
+ const { Parser: TapParser } = require('tap-parser');
7
+ const log = require('./log');
8
8
 
9
9
  module.exports = class TapConsumer extends EventEmitter {
10
10
  constructor() {
11
11
  super();
12
12
 
13
+ this.namePrefix = '';
14
+ this.prefixStack = [];
15
+ this.childParsers = [];
16
+
13
17
  this.stream = new TapParser();
14
- this.stream.on('assert', this.onTapAssert.bind(this));
15
- this.stream.on('extra', this.onTapExtra.bind(this));
18
+ this.attachParser(this.stream, true);
19
+ }
16
20
 
17
- this.stream.on('complete', this.onTapEnd.bind(this));
18
- this.stream.on('bailout', this.onTapError.bind(this));
21
+ attachParser(parser, isRoot) {
22
+ parser.on('assert', this.onTapAssert.bind(this));
23
+ parser.on('extra', this.onTapExtra.bind(this));
24
+ parser.on('child', this.onChildParser.bind(this));
25
+ parser.on('complete', this.onParserComplete.bind(this, parser));
26
+ if (isRoot) {
27
+ parser.on('bailout', this.onTapError.bind(this));
28
+ }
19
29
  }
20
30
 
21
- onTapAssert(data) {
22
- log.info(data);
23
- if (data.skip) {
31
+ onChildParser(childParser) {
32
+ this.prefixStack.push(this.namePrefix);
33
+ const parentPrefix = this.namePrefix;
34
+ const segment = (childParser.name && childParser.name.trim()) || '';
35
+ if (parentPrefix && segment) {
36
+ this.namePrefix = `${parentPrefix} > ${segment}`;
37
+ } else if (segment) {
38
+ this.namePrefix = segment;
39
+ } else if (parentPrefix) {
40
+ this.namePrefix = parentPrefix;
41
+ } else {
42
+ this.namePrefix = '';
43
+ }
44
+
45
+ this.childParsers.push(childParser);
46
+ this.attachParser(childParser, false);
47
+ }
48
+
49
+ onParserComplete(parser) {
50
+ if (parser === this.stream) {
51
+ this.onTapEnd();
24
52
  return;
25
53
  }
54
+ this.namePrefix = this.prefixStack.pop();
55
+ const idx = this.childParsers.indexOf(parser);
56
+ if (idx !== -1) {
57
+ this.childParsers.splice(idx, 1);
58
+ }
59
+ }
60
+
61
+ onTapAssert(data) {
62
+ log.verbose(JSON.stringify(data, null, 2));
26
63
 
27
64
  if (data.id === undefined) {
28
65
  return;
29
66
  }
30
67
 
68
+ const baseName = data.name ? data.name.trim() : '';
69
+ const qualifiedName = this.namePrefix
70
+ ? `${this.namePrefix} > ${baseName}`
71
+ : baseName;
72
+
31
73
  let test = {
32
74
  passed: 0,
33
75
  failed: 0,
34
76
  total: 1,
35
77
  id: data.id,
36
- name: data.name ? data.name.trim() : '',
78
+ name: qualifiedName,
37
79
  items: []
38
80
  };
39
81
 
82
+ if (data.skip) {
83
+ test.skipped = true;
84
+ this.emit('test-result', test);
85
+ return;
86
+ }
87
+
88
+ if (data.todo) {
89
+ test.todo = true;
90
+ }
91
+
40
92
  if (!data.ok) {
41
93
  let stack;
42
94
  if (data.diag) {
@@ -71,6 +123,16 @@ module.exports = class TapConsumer extends EventEmitter {
71
123
  }
72
124
  }
73
125
 
126
+ detachAllListeners() {
127
+ this.stream.removeAllListeners();
128
+ for (const p of this.childParsers) {
129
+ p.removeAllListeners();
130
+ }
131
+ this.childParsers = [];
132
+ this.prefixStack = [];
133
+ this.namePrefix = '';
134
+ }
135
+
74
136
  onTapError(reason) {
75
137
  let test = {
76
138
  failed: 1,
@@ -81,13 +143,13 @@ module.exports = class TapConsumer extends EventEmitter {
81
143
  }
82
144
  };
83
145
 
84
- this.stream.removeAllListeners();
146
+ this.detachAllListeners();
85
147
  this.emit('test-result', test);
86
148
  this.emit('all-test-results');
87
149
  }
88
150
 
89
151
  onTapEnd() {
90
- this.stream.removeAllListeners();
152
+ this.detachAllListeners();
91
153
  this.emit('all-test-results');
92
154
  }
93
155
  };
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
  const capitalize = require('./capitalize');
4
- const log = require('npmlog');
4
+ const log = require('../log');
5
5
  const _ = require('lodash');
6
6
 
7
7
  function Validation() {
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared esbuild config for bundling Node.js-dependent test code to run in a
3
+ * browser via testem.
4
+ *
5
+ * `esbuild-plugins-node-modules-polyfill` stores the `process`
6
+ * polyfill as a scoped variable inside the IIFE it emits, but CJS module
7
+ * factories (wrapped in `__commonJS`) reference `process`, `__dirname`, and
8
+ * `setImmediate` as free globals — causing ReferenceErrors at runtime.
9
+ *
10
+ * These two options fix that:
11
+ * - `banner.js`: sets `globalThis.process`, `globalThis.setImmediate`, and
12
+ * `globalThis.Buffer` before the bundle executes, so CJS factories find
13
+ * them as globals.
14
+ * - `define`: replaces `__dirname` / `__filename` literals, which esbuild
15
+ * does not inject when targeting the browser platform.
16
+ */
17
+
18
+ var banner = '(function(){' +
19
+ 'if(typeof process==="undefined"){' +
20
+ 'var p={env:{},browser:true,platform:"browser",' +
21
+ 'nextTick:function(fn){Promise.resolve().then(fn);},' +
22
+ 'on:function(){},once:function(){},off:function(){},' +
23
+ 'emit:function(){},removeListener:function(){},' +
24
+ 'removeAllListeners:function(){},addListener:function(){},' +
25
+ 'binding:function(){throw new Error("process.binding not supported");},' +
26
+ 'cwd:function(){return "/";},' +
27
+ 'exit:function(){}' +
28
+ '};' +
29
+ 'if(typeof globalThis!=="undefined")globalThis.process=p;' +
30
+ 'else if(typeof window!=="undefined")window.process=p;' +
31
+ '}' +
32
+ 'if(typeof setImmediate==="undefined"){' +
33
+ 'var si=function(fn){Promise.resolve().then(fn);};' +
34
+ 'if(typeof globalThis!=="undefined")globalThis.setImmediate=si;' +
35
+ 'else if(typeof window!=="undefined")window.setImmediate=si;' +
36
+ '}' +
37
+ '})();';
38
+
39
+ module.exports = {
40
+ banner: { js: banner },
41
+ bufferInject: require.resolve('./esbuild-buffer-inject.js'),
42
+ define: {
43
+ __dirname: '""',
44
+ __filename: '""'
45
+ }
46
+ };
@@ -0,0 +1,6 @@
1
+ // esbuild inject shim: exposes Buffer as a global for browser bundles.
2
+ // The esbuild-plugins-node-modules-polyfill plugin resolves `require('buffer')`
3
+ // to a browser polyfill, but does not assign it to globalThis automatically.
4
+ import { Buffer } from 'buffer';
5
+ if (typeof globalThis !== 'undefined') { globalThis.Buffer = Buffer; }
6
+ else if (typeof window !== 'undefined') { window.Buffer = Buffer; }
@@ -0,0 +1,19 @@
1
+ const { glob } = require('glob');
2
+
3
+ /**
4
+ * Expand one resolved pattern to matching file paths using the project glob backend.
5
+ *
6
+ * Callers pass POSIX-style paths (see `convertToPosix`); this keeps options aligned
7
+ * with `glob` today and allows swapping the implementation later without touching Config.
8
+ *
9
+ * @param {string} resolvedPatternPosix - Single pattern (already resolved to absolute or cwd-relative form, forward slashes).
10
+ * @param {string[]} [ignorePatternsPosix] - Ignore globs in POSIX form; empty entries are dropped.
11
+ * @returns {Promise<string[]>} Matching paths, sorted lexicographically (same ordering as before extraction).
12
+ */
13
+ async function expandGlobPattern(resolvedPatternPosix, ignorePatternsPosix) {
14
+ const ignore = (ignorePatternsPosix || []).filter(Boolean);
15
+ const files = await glob(resolvedPatternPosix, { ignore });
16
+ return files.slice().sort();
17
+ }
18
+
19
+ module.exports = expandGlobPattern;