globlin 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/LICENSE +76 -0
- package/README.md +276 -0
- package/index.d.ts +399 -0
- package/index.js +325 -0
- package/js/index.d.ts +475 -0
- package/js/index.d.ts.map +1 -0
- package/js/index.js +924 -0
- package/js/stream.d.ts +41 -0
- package/js/stream.d.ts.map +1 -0
- package/js/stream.js +94 -0
- package/js/types.d.ts +141 -0
- package/js/types.d.ts.map +1 -0
- package/js/types.js +8 -0
- package/package.json +189 -0
package/js/index.js
ADDED
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* globlin - A high-performance glob pattern matcher
|
|
4
|
+
*
|
|
5
|
+
* This module provides a 100% compatible drop-in replacement for glob v13,
|
|
6
|
+
* implemented in Rust for 20-30x faster performance.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.Glob = exports.GloblinPath = exports.Minipass = exports.minimatch = exports.Minimatch = exports.Path = exports.PathScurry = void 0;
|
|
43
|
+
exports.convertToFullPathObjects = convertToFullPathObjects;
|
|
44
|
+
exports.globSync = globSync;
|
|
45
|
+
exports.glob = glob;
|
|
46
|
+
exports.globStream = globStream;
|
|
47
|
+
exports.globStreamSync = globStreamSync;
|
|
48
|
+
exports.globIterate = globIterate;
|
|
49
|
+
exports.globIterateSync = globIterateSync;
|
|
50
|
+
exports.hasMagic = hasMagic;
|
|
51
|
+
exports.escape = escape;
|
|
52
|
+
exports.unescape = unescape;
|
|
53
|
+
exports.analyzePattern = analyzePattern;
|
|
54
|
+
exports.analyzePatterns = analyzePatterns;
|
|
55
|
+
/// <reference types="node" />
|
|
56
|
+
const nativeBindings = require('../index.js');
|
|
57
|
+
const { globSync: nativeGlobSync, glob: nativeGlob, globSyncWithFileTypes: nativeGlobSyncWithFileTypes, globWithFileTypes: nativeGlobWithFileTypes, globStream: _nativeGlobStream, globStreamWithFileTypes: _nativeGlobStreamWithFileTypes, escape: nativeEscape, unescape: nativeUnescape, hasMagic: nativeHasMagic, analyzePattern: nativeAnalyzePattern, analyzePatterns: nativeAnalyzePatterns, } = nativeBindings;
|
|
58
|
+
// Note: _nativeGlobStream and _nativeGlobStreamWithFileTypes are currently unused
|
|
59
|
+
// because the streaming implementation uses setImmediate + sync collect instead.
|
|
60
|
+
// They are available for future optimization when proper async callback handling is implemented.
|
|
61
|
+
void _nativeGlobStream;
|
|
62
|
+
void _nativeGlobStreamWithFileTypes;
|
|
63
|
+
// Re-export path-scurry for API compatibility
|
|
64
|
+
const path_scurry_1 = require("path-scurry");
|
|
65
|
+
Object.defineProperty(exports, "PathScurry", { enumerable: true, get: function () { return path_scurry_1.PathScurry; } });
|
|
66
|
+
Object.defineProperty(exports, "Path", { enumerable: true, get: function () { return path_scurry_1.Path; } });
|
|
67
|
+
// Re-export minimatch for API compatibility
|
|
68
|
+
var minimatch_1 = require("minimatch");
|
|
69
|
+
Object.defineProperty(exports, "Minimatch", { enumerable: true, get: function () { return minimatch_1.Minimatch; } });
|
|
70
|
+
Object.defineProperty(exports, "minimatch", { enumerable: true, get: function () { return minimatch_1.minimatch; } });
|
|
71
|
+
// Re-export Minipass for stream API compatibility
|
|
72
|
+
const minipass_1 = require("minipass");
|
|
73
|
+
Object.defineProperty(exports, "Minipass", { enumerable: true, get: function () { return minipass_1.Minipass; } });
|
|
74
|
+
/**
|
|
75
|
+
* Type guard to check if an object is a Glob instance (for cache reuse)
|
|
76
|
+
*/
|
|
77
|
+
function isGlobInstance(obj) {
|
|
78
|
+
return obj instanceof Glob;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Helper to check if ignore option is an IgnorePattern object.
|
|
82
|
+
* Returns true only if the object has at least one of the methods.
|
|
83
|
+
* An empty object is NOT considered an IgnorePattern.
|
|
84
|
+
*/
|
|
85
|
+
function isIgnorePattern(ignore) {
|
|
86
|
+
if (typeof ignore !== 'object' || ignore === null || Array.isArray(ignore)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const maybePattern = ignore;
|
|
90
|
+
const hasIgnored = typeof maybePattern.ignored === 'function';
|
|
91
|
+
const hasChildrenIgnored = typeof maybePattern.childrenIgnored === 'function';
|
|
92
|
+
// Must have at least one of the methods to be considered an IgnorePattern
|
|
93
|
+
return hasIgnored || hasChildrenIgnored;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Convert JS GlobOptions to native options, handling JS-only features
|
|
97
|
+
*/
|
|
98
|
+
function toNativeOptions(options) {
|
|
99
|
+
if (!options) {
|
|
100
|
+
return { cwd: process.cwd() };
|
|
101
|
+
}
|
|
102
|
+
// Create native options, excluding JS-only fields (signal)
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
104
|
+
const { signal: _signal, ...rest } = options;
|
|
105
|
+
// Determine what to pass as ignore to native code:
|
|
106
|
+
// - If it's a custom IgnorePattern object with methods, don't pass it (handled in JS)
|
|
107
|
+
// - If it's an empty object (no methods), also don't pass it (not a valid ignore)
|
|
108
|
+
// - If it's a string or string[], pass it through
|
|
109
|
+
// - If it's undefined/null, don't pass it
|
|
110
|
+
let nativeIgnore;
|
|
111
|
+
const ignoreOpt = rest.ignore;
|
|
112
|
+
if (ignoreOpt === undefined || ignoreOpt === null) {
|
|
113
|
+
nativeIgnore = undefined;
|
|
114
|
+
}
|
|
115
|
+
else if (typeof ignoreOpt === 'string') {
|
|
116
|
+
nativeIgnore = ignoreOpt;
|
|
117
|
+
}
|
|
118
|
+
else if (Array.isArray(ignoreOpt)) {
|
|
119
|
+
nativeIgnore = ignoreOpt;
|
|
120
|
+
}
|
|
121
|
+
else if (isIgnorePattern(ignoreOpt)) {
|
|
122
|
+
// It's a custom object with methods - handled in JS, not passed to native
|
|
123
|
+
nativeIgnore = undefined;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// It's some other object (like empty {}) - treat as no ignore
|
|
127
|
+
nativeIgnore = undefined;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
// Use process.cwd() if cwd is undefined, null, or empty string
|
|
131
|
+
cwd: rest.cwd || process.cwd(),
|
|
132
|
+
...rest,
|
|
133
|
+
ignore: nativeIgnore,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Apply custom ignore filters to results.
|
|
138
|
+
* This handles IgnorePattern objects with ignored() and/or childrenIgnored() methods.
|
|
139
|
+
*/
|
|
140
|
+
function applyCustomIgnoreFilter(results, ignorePattern, cwd) {
|
|
141
|
+
if (!ignorePattern.ignored && !ignorePattern.childrenIgnored) {
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
const scurry = new path_scurry_1.PathScurry(cwd);
|
|
145
|
+
// Build a set of ignored directories (for childrenIgnored)
|
|
146
|
+
// We need to check all parent directories that might be childrenIgnored
|
|
147
|
+
const ignoredDirPrefixes = new Set();
|
|
148
|
+
// If we have childrenIgnored, we need to pre-check all unique parent directories
|
|
149
|
+
if (ignorePattern.childrenIgnored) {
|
|
150
|
+
const allParentDirs = new Set();
|
|
151
|
+
// Collect all unique parent directories from the results
|
|
152
|
+
// Normalize to forward slashes for cross-platform compatibility
|
|
153
|
+
for (const relPath of results) {
|
|
154
|
+
const normalized = relPath.replace(/\\/g, '/');
|
|
155
|
+
const parts = normalized.split('/');
|
|
156
|
+
let current = '';
|
|
157
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
158
|
+
current = current ? current + '/' + parts[i] : parts[i];
|
|
159
|
+
allParentDirs.add(current);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Sort by depth (shallow first) and check childrenIgnored on each
|
|
163
|
+
const sortedDirs = [...allParentDirs].sort((a, b) => {
|
|
164
|
+
const depthA = a.split('/').length;
|
|
165
|
+
const depthB = b.split('/').length;
|
|
166
|
+
return depthA - depthB;
|
|
167
|
+
});
|
|
168
|
+
for (const dir of sortedDirs) {
|
|
169
|
+
// Skip if already under an ignored prefix
|
|
170
|
+
let underIgnored = false;
|
|
171
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
172
|
+
if (dir === prefix || dir.startsWith(prefix + '/')) {
|
|
173
|
+
underIgnored = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (underIgnored)
|
|
178
|
+
continue;
|
|
179
|
+
// Check if this directory's children should be ignored
|
|
180
|
+
const pathObj = scurry.cwd.resolve(dir);
|
|
181
|
+
if (ignorePattern.childrenIgnored(pathObj)) {
|
|
182
|
+
ignoredDirPrefixes.add(dir);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Now filter the results
|
|
187
|
+
return results.filter(relPath => {
|
|
188
|
+
// Normalize path for comparison (cross-platform)
|
|
189
|
+
const normalizedPath = relPath.replace(/\\/g, '/');
|
|
190
|
+
// Check if path is under an ignored directory (childrenIgnored)
|
|
191
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
192
|
+
// Child paths should be filtered out
|
|
193
|
+
if (normalizedPath.startsWith(prefix + '/')) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Check if this specific path should be ignored
|
|
198
|
+
if (ignorePattern.ignored) {
|
|
199
|
+
const pathObj = scurry.cwd.resolve(relPath);
|
|
200
|
+
if (ignorePattern.ignored(pathObj)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Apply custom ignore filters to Path[] results.
|
|
209
|
+
* This handles IgnorePattern objects with ignored() and/or childrenIgnored() methods.
|
|
210
|
+
*/
|
|
211
|
+
function applyCustomIgnoreFilterForPaths(pathObjs, ignorePattern) {
|
|
212
|
+
if (!ignorePattern.ignored && !ignorePattern.childrenIgnored) {
|
|
213
|
+
return pathObjs;
|
|
214
|
+
}
|
|
215
|
+
// Build a set of ignored directories (for childrenIgnored)
|
|
216
|
+
// Use normalized (forward slash) paths for cross-platform comparison
|
|
217
|
+
const ignoredDirPrefixes = new Set();
|
|
218
|
+
// If we have childrenIgnored, pre-check all unique parent directories
|
|
219
|
+
if (ignorePattern.childrenIgnored) {
|
|
220
|
+
const allParentDirs = new Map();
|
|
221
|
+
// Collect all unique parent directories from the results
|
|
222
|
+
for (const pathObj of pathObjs) {
|
|
223
|
+
let current = pathObj.parent;
|
|
224
|
+
while (current && current.relative() !== '.') {
|
|
225
|
+
// Normalize path for cross-platform compatibility
|
|
226
|
+
const relPath = current.relative().replace(/\\/g, '/');
|
|
227
|
+
if (!allParentDirs.has(relPath)) {
|
|
228
|
+
allParentDirs.set(relPath, current);
|
|
229
|
+
}
|
|
230
|
+
current = current.parent;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Sort by depth (shallow first) and check childrenIgnored on each
|
|
234
|
+
const sortedDirs = [...allParentDirs.entries()].sort((a, b) => {
|
|
235
|
+
const depthA = a[0].split('/').length;
|
|
236
|
+
const depthB = b[0].split('/').length;
|
|
237
|
+
return depthA - depthB;
|
|
238
|
+
});
|
|
239
|
+
for (const [dir, pathObj] of sortedDirs) {
|
|
240
|
+
// Skip if already under an ignored prefix
|
|
241
|
+
let underIgnored = false;
|
|
242
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
243
|
+
if (dir === prefix || dir.startsWith(prefix + '/')) {
|
|
244
|
+
underIgnored = true;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (underIgnored)
|
|
249
|
+
continue;
|
|
250
|
+
// Check if this directory's children should be ignored
|
|
251
|
+
if (ignorePattern.childrenIgnored(pathObj)) {
|
|
252
|
+
ignoredDirPrefixes.add(dir);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Now filter the results
|
|
257
|
+
return pathObjs.filter(pathObj => {
|
|
258
|
+
// Normalize path for cross-platform comparison
|
|
259
|
+
const relPath = pathObj.relative().replace(/\\/g, '/');
|
|
260
|
+
// Check if path is under an ignored directory (childrenIgnored)
|
|
261
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
262
|
+
if (relPath.startsWith(prefix + '/')) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Check if this specific path should be ignored
|
|
267
|
+
if (ignorePattern.ignored && ignorePattern.ignored(pathObj)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Apply custom ignore filters to GloblinPath[] results.
|
|
275
|
+
* This handles IgnorePattern objects with ignored() and/or childrenIgnored() methods.
|
|
276
|
+
*
|
|
277
|
+
* Note: The IgnorePattern interface expects Path objects, so we convert GloblinPath
|
|
278
|
+
* to Path via toPath() when calling the user's ignore methods. This adds some overhead
|
|
279
|
+
* but is necessary for API compatibility.
|
|
280
|
+
*/
|
|
281
|
+
function applyCustomIgnoreFilterForGloblinPaths(pathObjs, ignorePattern) {
|
|
282
|
+
if (!ignorePattern.ignored && !ignorePattern.childrenIgnored) {
|
|
283
|
+
return pathObjs;
|
|
284
|
+
}
|
|
285
|
+
// Build a set of ignored directories (for childrenIgnored)
|
|
286
|
+
// Use normalized (forward slash) paths for cross-platform comparison
|
|
287
|
+
const ignoredDirPrefixes = new Set();
|
|
288
|
+
// If we have childrenIgnored, pre-check all unique parent directories
|
|
289
|
+
if (ignorePattern.childrenIgnored) {
|
|
290
|
+
const allParentDirs = new Map();
|
|
291
|
+
// Collect all unique parent directories from the results
|
|
292
|
+
for (const pathObj of pathObjs) {
|
|
293
|
+
// GloblinPath.parent returns a GloblinPath or undefined
|
|
294
|
+
let current = pathObj.parent;
|
|
295
|
+
while (current && current.relative() !== '.') {
|
|
296
|
+
// Normalize path for cross-platform compatibility
|
|
297
|
+
const relPath = current.relative().replace(/\\/g, '/');
|
|
298
|
+
if (!allParentDirs.has(relPath)) {
|
|
299
|
+
allParentDirs.set(relPath, current);
|
|
300
|
+
}
|
|
301
|
+
current = current.parent;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Sort by depth (shallow first) and check childrenIgnored on each
|
|
305
|
+
const sortedDirs = [...allParentDirs.entries()].sort((a, b) => {
|
|
306
|
+
const depthA = a[0].split('/').length;
|
|
307
|
+
const depthB = b[0].split('/').length;
|
|
308
|
+
return depthA - depthB;
|
|
309
|
+
});
|
|
310
|
+
for (const [dir, globlinPath] of sortedDirs) {
|
|
311
|
+
// Skip if already under an ignored prefix
|
|
312
|
+
let underIgnored = false;
|
|
313
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
314
|
+
if (dir === prefix || dir.startsWith(prefix + '/')) {
|
|
315
|
+
underIgnored = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (underIgnored)
|
|
320
|
+
continue;
|
|
321
|
+
// Check if this directory's children should be ignored
|
|
322
|
+
// Must convert to Path for the IgnorePattern interface
|
|
323
|
+
if (ignorePattern.childrenIgnored(globlinPath.toPath())) {
|
|
324
|
+
ignoredDirPrefixes.add(dir);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Now filter the results
|
|
329
|
+
return pathObjs.filter(pathObj => {
|
|
330
|
+
// Normalize path for cross-platform comparison
|
|
331
|
+
const relPath = pathObj.relative().replace(/\\/g, '/');
|
|
332
|
+
// Check if path is under an ignored directory (childrenIgnored)
|
|
333
|
+
for (const prefix of ignoredDirPrefixes) {
|
|
334
|
+
if (relPath.startsWith(prefix + '/')) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Check if this specific path should be ignored
|
|
339
|
+
// Must convert to Path for the IgnorePattern interface
|
|
340
|
+
if (ignorePattern.ignored && ignorePattern.ignored(pathObj.toPath())) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
return true;
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
const nodePath = __importStar(require("path"));
|
|
347
|
+
/**
|
|
348
|
+
* GloblinPath - A lazy Path-like wrapper that provides fast access to common properties
|
|
349
|
+
* while deferring expensive PathScurry resolution until needed.
|
|
350
|
+
*
|
|
351
|
+
* This class implements the most commonly used Path interface methods using cached
|
|
352
|
+
* values from Rust, avoiding the expensive `scurry.cwd.resolve()` call for each result.
|
|
353
|
+
* When advanced features are needed, call `toPath()` to get the full PathScurry Path.
|
|
354
|
+
*
|
|
355
|
+
* **Performance Characteristics:**
|
|
356
|
+
* - Creation: ~0.5µs (vs ~16µs for PathScurry Path)
|
|
357
|
+
* - isFile()/isDirectory()/isSymbolicLink(): ~0.06µs (uses cached values)
|
|
358
|
+
* - fullpath()/relative(): ~0.1µs (string operations)
|
|
359
|
+
* - toPath(): ~16µs (creates full PathScurry Path on demand)
|
|
360
|
+
*
|
|
361
|
+
* This optimization provides ~85% speedup for withFileTypes compared to eagerly
|
|
362
|
+
* creating PathScurry Path objects for every result.
|
|
363
|
+
*/
|
|
364
|
+
class GloblinPath {
|
|
365
|
+
/** The relative path string */
|
|
366
|
+
path;
|
|
367
|
+
/** The absolute working directory */
|
|
368
|
+
_cwd;
|
|
369
|
+
/** Cached file type information from Rust */
|
|
370
|
+
_isDir;
|
|
371
|
+
_isFileVal;
|
|
372
|
+
_isSymlinkVal;
|
|
373
|
+
/** Whether stat option was enabled (if false, isFile/isDirectory return false like PathScurry) */
|
|
374
|
+
_stat;
|
|
375
|
+
/** Lazily resolved PathScurry Path (only created if toPath() is called) */
|
|
376
|
+
_pathScurryPath;
|
|
377
|
+
_pathScurry;
|
|
378
|
+
/** Cached fullpath string */
|
|
379
|
+
_fullpath;
|
|
380
|
+
/** The name (basename) of this path entry */
|
|
381
|
+
name;
|
|
382
|
+
constructor(relativePath, cwd, isDirectory, isFile, isSymlink, stat = false) {
|
|
383
|
+
this.path = relativePath;
|
|
384
|
+
this._cwd = cwd;
|
|
385
|
+
this._isDir = isDirectory;
|
|
386
|
+
this._isFileVal = isFile;
|
|
387
|
+
this._isSymlinkVal = isSymlink;
|
|
388
|
+
this._stat = stat;
|
|
389
|
+
this.name = nodePath.basename(relativePath) || relativePath;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Returns true if this is a regular file.
|
|
393
|
+
* Without stat: true, returns false (unknown type) to match PathScurry behavior.
|
|
394
|
+
* With stat: true, uses cached value from Rust - no filesystem access needed.
|
|
395
|
+
*/
|
|
396
|
+
isFile() {
|
|
397
|
+
return this._stat ? this._isFileVal : false;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Returns true if this is a directory.
|
|
401
|
+
* Without stat: true, returns false (unknown type) to match PathScurry behavior.
|
|
402
|
+
* With stat: true, uses cached value from Rust - no filesystem access needed.
|
|
403
|
+
*/
|
|
404
|
+
isDirectory() {
|
|
405
|
+
return this._stat ? this._isDir : false;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Returns true if this is a symbolic link.
|
|
409
|
+
* Uses cached value from Rust - no filesystem access needed.
|
|
410
|
+
*/
|
|
411
|
+
isSymbolicLink() {
|
|
412
|
+
return this._isSymlinkVal;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Returns the full absolute path.
|
|
416
|
+
* This is a fast string operation (no filesystem access).
|
|
417
|
+
*/
|
|
418
|
+
fullpath() {
|
|
419
|
+
if (!this._fullpath) {
|
|
420
|
+
this._fullpath = nodePath.join(this._cwd, this.path);
|
|
421
|
+
}
|
|
422
|
+
return this._fullpath;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Returns the path relative to the cwd.
|
|
426
|
+
* This is a fast string operation (no filesystem access).
|
|
427
|
+
*/
|
|
428
|
+
relative() {
|
|
429
|
+
return this.path;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Returns the full absolute path as a string.
|
|
433
|
+
* Alias for fullpath() for compatibility.
|
|
434
|
+
*/
|
|
435
|
+
fullpathPosix() {
|
|
436
|
+
// Use forward slashes for posix compatibility
|
|
437
|
+
return this.fullpath().replace(/\\/g, '/');
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Returns the relative path as a string.
|
|
441
|
+
* Alias for relative() for compatibility.
|
|
442
|
+
*/
|
|
443
|
+
relativePosix() {
|
|
444
|
+
return this.path.replace(/\\/g, '/');
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Convert to a full PathScurry Path object.
|
|
448
|
+
* This is an expensive operation (~16µs) that should only be called
|
|
449
|
+
* when you need advanced Path features not provided by GloblinPath.
|
|
450
|
+
*
|
|
451
|
+
* The PathScurry Path is cached, so subsequent calls are fast.
|
|
452
|
+
*
|
|
453
|
+
* @returns A full PathScurry Path object
|
|
454
|
+
*/
|
|
455
|
+
toPath() {
|
|
456
|
+
if (!this._pathScurryPath) {
|
|
457
|
+
if (!this._pathScurry) {
|
|
458
|
+
this._pathScurry = new path_scurry_1.PathScurry(this._cwd);
|
|
459
|
+
}
|
|
460
|
+
this._pathScurryPath = this._pathScurry.cwd.resolve(this.path);
|
|
461
|
+
}
|
|
462
|
+
return this._pathScurryPath;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Get the parent directory as a GloblinPath.
|
|
466
|
+
* Note: This creates a new GloblinPath for the parent.
|
|
467
|
+
* For advanced parent traversal, use toPath().parent.
|
|
468
|
+
*/
|
|
469
|
+
get parent() {
|
|
470
|
+
const parentPath = nodePath.dirname(this.path);
|
|
471
|
+
if (parentPath === '.' || parentPath === this.path) {
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
// Parent is always a directory
|
|
475
|
+
return new GloblinPath(parentPath, this._cwd, true, false, false, this._stat);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* String representation (returns the relative path).
|
|
479
|
+
*/
|
|
480
|
+
toString() {
|
|
481
|
+
return this.path;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
exports.GloblinPath = GloblinPath;
|
|
485
|
+
/**
|
|
486
|
+
* Convert native PathData to GloblinPath objects.
|
|
487
|
+
* This is much faster than creating PathScurry Path objects for each result.
|
|
488
|
+
*
|
|
489
|
+
* @param data - Native path data from Rust
|
|
490
|
+
* @param cwd - Current working directory
|
|
491
|
+
* @param stat - If true, isFile/isDirectory return actual values; otherwise return false (like PathScurry)
|
|
492
|
+
* @returns Array of GloblinPath objects
|
|
493
|
+
*/
|
|
494
|
+
function convertToPathObjects(data, cwd, stat = false) {
|
|
495
|
+
return data.map(d => new GloblinPath(d.path, cwd, d.isDirectory, d.isFile, d.isSymlink, stat));
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* LEGACY: Convert native PathData to full PathScurry Path objects.
|
|
499
|
+
* This is the slow path (~16µs per result) that was used before optimization.
|
|
500
|
+
* Kept for compatibility and when full PathScurry features are needed.
|
|
501
|
+
*
|
|
502
|
+
* @deprecated Use convertToPathObjects() instead for better performance.
|
|
503
|
+
*/
|
|
504
|
+
function convertToFullPathObjects(data, cwd, performLstat = false) {
|
|
505
|
+
const scurry = new path_scurry_1.PathScurry(cwd);
|
|
506
|
+
return data.map(d => {
|
|
507
|
+
const p = scurry.cwd.resolve(d.path);
|
|
508
|
+
// If performLstat is true, call lstatSync to populate type information
|
|
509
|
+
// Otherwise, the Path object is returned without type info (saves syscalls)
|
|
510
|
+
if (performLstat) {
|
|
511
|
+
p.lstatSync();
|
|
512
|
+
}
|
|
513
|
+
return p;
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function globSync(pattern, options) {
|
|
517
|
+
// Check if signal is already aborted before starting
|
|
518
|
+
if (options?.signal?.aborted) {
|
|
519
|
+
throw options.signal.reason ?? new Error('The operation was aborted');
|
|
520
|
+
}
|
|
521
|
+
const opts = toNativeOptions(options);
|
|
522
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
523
|
+
const hasCustomIgnore = options?.ignore && isIgnorePattern(options.ignore);
|
|
524
|
+
// Handle withFileTypes option
|
|
525
|
+
if (options?.withFileTypes) {
|
|
526
|
+
const data = nativeGlobSyncWithFileTypes(pattern, opts);
|
|
527
|
+
// Convert to GloblinPath objects (fast - uses cached type info from Rust)
|
|
528
|
+
let pathObjs = convertToPathObjects(data, cwd, options.stat);
|
|
529
|
+
// Apply custom ignore filter if present
|
|
530
|
+
if (hasCustomIgnore) {
|
|
531
|
+
pathObjs = applyCustomIgnoreFilterForGloblinPaths(pathObjs, options.ignore);
|
|
532
|
+
}
|
|
533
|
+
return pathObjs;
|
|
534
|
+
}
|
|
535
|
+
// Pass patterns directly to native implementation (supports both string and array)
|
|
536
|
+
let results = nativeGlobSync(pattern, opts);
|
|
537
|
+
// Apply custom ignore filter if present
|
|
538
|
+
if (hasCustomIgnore) {
|
|
539
|
+
results = applyCustomIgnoreFilter(results, options.ignore, cwd);
|
|
540
|
+
}
|
|
541
|
+
return results;
|
|
542
|
+
}
|
|
543
|
+
async function glob(pattern, options) {
|
|
544
|
+
// Check if signal is already aborted before starting
|
|
545
|
+
if (options?.signal?.aborted) {
|
|
546
|
+
throw options.signal.reason ?? new Error('The operation was aborted');
|
|
547
|
+
}
|
|
548
|
+
const opts = toNativeOptions(options);
|
|
549
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
550
|
+
const hasCustomIgnore = options?.ignore && isIgnorePattern(options.ignore);
|
|
551
|
+
// Handle withFileTypes option
|
|
552
|
+
if (options?.withFileTypes) {
|
|
553
|
+
const promise = nativeGlobWithFileTypes(pattern, opts);
|
|
554
|
+
// Helper to apply custom ignore filter on GloblinPath[] results
|
|
555
|
+
const applyPathIgnoreFilter = (pathObjs) => {
|
|
556
|
+
if (!hasCustomIgnore)
|
|
557
|
+
return pathObjs;
|
|
558
|
+
return applyCustomIgnoreFilterForGloblinPaths(pathObjs, options.ignore);
|
|
559
|
+
};
|
|
560
|
+
// If we have a signal, set up abort handling
|
|
561
|
+
if (options?.signal) {
|
|
562
|
+
return new Promise((resolve, reject) => {
|
|
563
|
+
const onAbort = () => {
|
|
564
|
+
reject(options.signal.reason ?? new Error('The operation was aborted'));
|
|
565
|
+
};
|
|
566
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
567
|
+
promise
|
|
568
|
+
.then(data => {
|
|
569
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
570
|
+
if (options.signal.aborted) {
|
|
571
|
+
reject(options.signal.reason ?? new Error('The operation was aborted'));
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
let pathObjs = convertToPathObjects(data, cwd, options.stat);
|
|
575
|
+
pathObjs = applyPathIgnoreFilter(pathObjs);
|
|
576
|
+
resolve(pathObjs);
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
.catch(err => {
|
|
580
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
581
|
+
reject(err);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
const data = await promise;
|
|
586
|
+
let pathObjs = convertToPathObjects(data, cwd, options.stat);
|
|
587
|
+
pathObjs = applyPathIgnoreFilter(pathObjs);
|
|
588
|
+
return pathObjs;
|
|
589
|
+
}
|
|
590
|
+
// Start the async operation
|
|
591
|
+
const promise = nativeGlob(pattern, opts);
|
|
592
|
+
// If we have a signal, set up abort handling
|
|
593
|
+
if (options?.signal) {
|
|
594
|
+
return new Promise((resolve, reject) => {
|
|
595
|
+
// Handle abort during execution
|
|
596
|
+
const onAbort = () => {
|
|
597
|
+
reject(options.signal.reason ?? new Error('The operation was aborted'));
|
|
598
|
+
};
|
|
599
|
+
// If aborted while waiting, reject
|
|
600
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
601
|
+
promise
|
|
602
|
+
.then(results => {
|
|
603
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
604
|
+
// Check if aborted after completion
|
|
605
|
+
if (options.signal.aborted) {
|
|
606
|
+
reject(options.signal.reason ?? new Error('The operation was aborted'));
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
// Apply custom ignore filter if present
|
|
610
|
+
const finalResults = hasCustomIgnore
|
|
611
|
+
? applyCustomIgnoreFilter(results, options.ignore, cwd)
|
|
612
|
+
: results;
|
|
613
|
+
resolve(finalResults);
|
|
614
|
+
}
|
|
615
|
+
})
|
|
616
|
+
.catch(err => {
|
|
617
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
618
|
+
reject(err);
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
let results = await promise;
|
|
623
|
+
// Apply custom ignore filter if present
|
|
624
|
+
if (hasCustomIgnore) {
|
|
625
|
+
results = applyCustomIgnoreFilter(results, options.ignore, cwd);
|
|
626
|
+
}
|
|
627
|
+
return results;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Streaming glob pattern matching
|
|
631
|
+
*
|
|
632
|
+
* Uses native streaming to reduce peak memory usage for large result sets.
|
|
633
|
+
* Results are streamed directly from Rust as they are found, rather than
|
|
634
|
+
* collecting all results before sending to JavaScript.
|
|
635
|
+
*
|
|
636
|
+
* @param pattern - Glob pattern or array of patterns
|
|
637
|
+
* @param options - Glob options
|
|
638
|
+
* @returns Minipass stream of matching file paths
|
|
639
|
+
*/
|
|
640
|
+
function globStream(pattern, options) {
|
|
641
|
+
const stream = new minipass_1.Minipass({ objectMode: true });
|
|
642
|
+
// Check if signal is already aborted before starting
|
|
643
|
+
if (options?.signal?.aborted) {
|
|
644
|
+
setImmediate(() => {
|
|
645
|
+
stream.emit('error', options.signal.reason ?? new Error('The operation was aborted'));
|
|
646
|
+
});
|
|
647
|
+
return stream;
|
|
648
|
+
}
|
|
649
|
+
// Set up abort handling if signal provided
|
|
650
|
+
let aborted = false;
|
|
651
|
+
if (options?.signal) {
|
|
652
|
+
const onAbort = () => {
|
|
653
|
+
aborted = true;
|
|
654
|
+
stream.emit('error', options.signal.reason ?? new Error('The operation was aborted'));
|
|
655
|
+
};
|
|
656
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
657
|
+
// Clean up on stream end or error
|
|
658
|
+
stream.on('end', () => options.signal.removeEventListener('abort', onAbort));
|
|
659
|
+
stream.on('error', () => options.signal.removeEventListener('abort', onAbort));
|
|
660
|
+
}
|
|
661
|
+
// Use setImmediate to ensure async behavior (stream doesn't end synchronously)
|
|
662
|
+
// This matches glob v13's behavior where the stream is async
|
|
663
|
+
setImmediate(() => {
|
|
664
|
+
if (aborted)
|
|
665
|
+
return;
|
|
666
|
+
try {
|
|
667
|
+
// For now, use sync approach and stream the results
|
|
668
|
+
// TODO: Implement true streaming from Rust with proper async callback handling
|
|
669
|
+
const opts = { ...options, withFileTypes: false };
|
|
670
|
+
const results = globSync(pattern, opts);
|
|
671
|
+
for (const result of results) {
|
|
672
|
+
if (aborted)
|
|
673
|
+
return;
|
|
674
|
+
stream.write(result);
|
|
675
|
+
}
|
|
676
|
+
stream.end();
|
|
677
|
+
}
|
|
678
|
+
catch (err) {
|
|
679
|
+
stream.emit('error', err);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
return stream;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Synchronous streaming glob pattern matching
|
|
686
|
+
*
|
|
687
|
+
* @param pattern - Glob pattern or array of patterns
|
|
688
|
+
* @param options - Glob options
|
|
689
|
+
* @returns Minipass stream of matching file paths
|
|
690
|
+
*/
|
|
691
|
+
function globStreamSync(pattern, options) {
|
|
692
|
+
const stream = new minipass_1.Minipass({ objectMode: true });
|
|
693
|
+
// Check if signal is already aborted before starting
|
|
694
|
+
if (options?.signal?.aborted) {
|
|
695
|
+
// For sync stream, we need to emit error asynchronously to match expected behavior
|
|
696
|
+
setImmediate(() => {
|
|
697
|
+
stream.emit('error', options.signal.reason ?? new Error('The operation was aborted'));
|
|
698
|
+
});
|
|
699
|
+
return stream;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
// For streaming, always return strings (ignore withFileTypes)
|
|
703
|
+
const opts = { ...options, withFileTypes: false };
|
|
704
|
+
const results = globSync(pattern, opts);
|
|
705
|
+
for (const result of results) {
|
|
706
|
+
// Check for abort between writes (for sync stream with existing results)
|
|
707
|
+
if (options?.signal?.aborted) {
|
|
708
|
+
stream.emit('error', options.signal.reason ?? new Error('The operation was aborted'));
|
|
709
|
+
return stream;
|
|
710
|
+
}
|
|
711
|
+
stream.write(result);
|
|
712
|
+
}
|
|
713
|
+
stream.end();
|
|
714
|
+
}
|
|
715
|
+
catch (err) {
|
|
716
|
+
stream.emit('error', err);
|
|
717
|
+
}
|
|
718
|
+
return stream;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Async iterator for glob results
|
|
722
|
+
*
|
|
723
|
+
* @param pattern - Glob pattern or array of patterns
|
|
724
|
+
* @param options - Glob options
|
|
725
|
+
* @yields Matching file paths
|
|
726
|
+
*/
|
|
727
|
+
async function* globIterate(pattern, options) {
|
|
728
|
+
// For iteration, always return strings (ignore withFileTypes)
|
|
729
|
+
const opts = { ...options, withFileTypes: false };
|
|
730
|
+
const results = await glob(pattern, opts);
|
|
731
|
+
for (const result of results) {
|
|
732
|
+
yield result;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Sync iterator for glob results
|
|
737
|
+
*
|
|
738
|
+
* @param pattern - Glob pattern or array of patterns
|
|
739
|
+
* @param options - Glob options
|
|
740
|
+
* @yields Matching file paths
|
|
741
|
+
*/
|
|
742
|
+
function* globIterateSync(pattern, options) {
|
|
743
|
+
// For iteration, always return strings (ignore withFileTypes)
|
|
744
|
+
const opts = { ...options, withFileTypes: false };
|
|
745
|
+
const results = globSync(pattern, opts);
|
|
746
|
+
for (const result of results) {
|
|
747
|
+
yield result;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Check if a pattern contains any magic glob characters.
|
|
752
|
+
* Takes into account escaped characters - escaped magic chars don't count.
|
|
753
|
+
*
|
|
754
|
+
* @param pattern - Glob pattern or array of patterns
|
|
755
|
+
* @param options - Glob options
|
|
756
|
+
* @returns True if the pattern has magic (unescaped) glob characters
|
|
757
|
+
*/
|
|
758
|
+
function hasMagic(pattern, options) {
|
|
759
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
760
|
+
const noext = options?.noext ?? false;
|
|
761
|
+
const windowsPathsNoEscape = options?.windowsPathsNoEscape ?? false;
|
|
762
|
+
for (const p of patterns) {
|
|
763
|
+
if (nativeHasMagic(p, noext, windowsPathsNoEscape)) {
|
|
764
|
+
return true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Escape magic glob characters in a pattern.
|
|
771
|
+
* After escaping, the pattern will match the literal string.
|
|
772
|
+
*
|
|
773
|
+
* @param pattern - Pattern to escape
|
|
774
|
+
* @param options - Glob options (windowsPathsNoEscape affects escape style)
|
|
775
|
+
* @returns Escaped pattern
|
|
776
|
+
*/
|
|
777
|
+
function escape(pattern, options) {
|
|
778
|
+
const windowsPathsNoEscape = options?.windowsPathsNoEscape ?? false;
|
|
779
|
+
return nativeEscape(pattern, windowsPathsNoEscape);
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Unescape magic glob characters in a pattern.
|
|
783
|
+
* This reverses the effect of `escape()`.
|
|
784
|
+
*
|
|
785
|
+
* @param pattern - Pattern to unescape
|
|
786
|
+
* @param options - Glob options (windowsPathsNoEscape affects unescape style)
|
|
787
|
+
* @returns Unescaped pattern
|
|
788
|
+
*/
|
|
789
|
+
function unescape(pattern, options) {
|
|
790
|
+
const windowsPathsNoEscape = options?.windowsPathsNoEscape ?? false;
|
|
791
|
+
return nativeUnescape(pattern, windowsPathsNoEscape);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Analyze a pattern for potential issues and return warnings.
|
|
795
|
+
* This is useful for providing helpful feedback about common mistakes.
|
|
796
|
+
*
|
|
797
|
+
* @param pattern - The glob pattern to analyze
|
|
798
|
+
* @param options - Options affecting analysis (windowsPathsNoEscape, platform)
|
|
799
|
+
* @returns Array of warnings (empty if no issues detected)
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```ts
|
|
803
|
+
* import { analyzePattern } from 'globlin'
|
|
804
|
+
*
|
|
805
|
+
* // Check for trailing spaces
|
|
806
|
+
* const warnings = analyzePattern('*.txt ')
|
|
807
|
+
* // [{ warningType: 'trailing_spaces', suggestion: '*.txt', ... }]
|
|
808
|
+
* ```
|
|
809
|
+
*
|
|
810
|
+
* @remarks
|
|
811
|
+
* This function detects common mistakes such as:
|
|
812
|
+
* - Escaped wildcards at the start of patterns
|
|
813
|
+
* - Double-escaped characters
|
|
814
|
+
* - Backslash path separators on Windows without windowsPathsNoEscape
|
|
815
|
+
* - Performance issues (multiple globstars, redundant patterns)
|
|
816
|
+
* - Trailing spaces in patterns
|
|
817
|
+
* - Empty patterns
|
|
818
|
+
* - Null bytes in patterns
|
|
819
|
+
*/
|
|
820
|
+
function analyzePattern(pattern, options) {
|
|
821
|
+
const windowsPathsNoEscape = options?.windowsPathsNoEscape ?? false;
|
|
822
|
+
const platform = options?.platform;
|
|
823
|
+
return nativeAnalyzePattern(pattern, windowsPathsNoEscape, platform);
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Analyze multiple patterns for potential issues and return all warnings.
|
|
827
|
+
*
|
|
828
|
+
* @param patterns - Array of glob patterns to analyze
|
|
829
|
+
* @param options - Options affecting analysis (windowsPathsNoEscape, platform)
|
|
830
|
+
* @returns Array of warnings for all patterns (empty if no issues detected)
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```ts
|
|
834
|
+
* import { analyzePatterns } from 'globlin'
|
|
835
|
+
*
|
|
836
|
+
* const warnings = analyzePatterns(['*.js', '*.txt '])
|
|
837
|
+
* // Returns warnings for escaped wildcard and performance issue
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
function analyzePatterns(patterns, options) {
|
|
841
|
+
const windowsPathsNoEscape = options?.windowsPathsNoEscape ?? false;
|
|
842
|
+
const platform = options?.platform;
|
|
843
|
+
return nativeAnalyzePatterns(patterns, windowsPathsNoEscape, platform);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Glob class for reusable glob operations
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```ts
|
|
850
|
+
* // Basic usage
|
|
851
|
+
* const g = new Glob('*.js', { cwd: '/path' })
|
|
852
|
+
* const files = g.walkSync()
|
|
853
|
+
*
|
|
854
|
+
* // Cache reuse - pass a Glob instance as options to reuse settings
|
|
855
|
+
* const g2 = new Glob('*.ts', g) // Reuses cwd, dot, nocase, etc. from g
|
|
856
|
+
* ```
|
|
857
|
+
*/
|
|
858
|
+
class Glob {
|
|
859
|
+
pattern;
|
|
860
|
+
options;
|
|
861
|
+
/**
|
|
862
|
+
* Create a new Glob instance.
|
|
863
|
+
*
|
|
864
|
+
* @param pattern - Glob pattern or array of patterns
|
|
865
|
+
* @param options - Glob options or a previous Glob instance to reuse settings
|
|
866
|
+
*
|
|
867
|
+
* When a Glob instance is passed as options, its settings (cwd, dot, nocase, etc.)
|
|
868
|
+
* are copied to the new instance. This allows for efficient reuse of settings
|
|
869
|
+
* when running multiple glob operations with similar configurations.
|
|
870
|
+
*/
|
|
871
|
+
constructor(pattern, options = {}) {
|
|
872
|
+
// If options is a Glob instance, extract its options (cache reuse pattern)
|
|
873
|
+
// This matches glob v13 behavior where you can pass a Glob as options
|
|
874
|
+
let resolvedOptions;
|
|
875
|
+
if (isGlobInstance(options)) {
|
|
876
|
+
// Copy options from the existing Glob instance
|
|
877
|
+
resolvedOptions = { ...options.options };
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
resolvedOptions = options;
|
|
881
|
+
}
|
|
882
|
+
// Validate options on construction (same as glob v13)
|
|
883
|
+
if (resolvedOptions.withFileTypes && resolvedOptions.absolute !== undefined) {
|
|
884
|
+
throw new TypeError('cannot set absolute and withFileTypes:true');
|
|
885
|
+
}
|
|
886
|
+
if (resolvedOptions.matchBase && resolvedOptions.noglobstar) {
|
|
887
|
+
throw new TypeError('base matching requires globstar');
|
|
888
|
+
}
|
|
889
|
+
this.pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
890
|
+
this.options = resolvedOptions;
|
|
891
|
+
}
|
|
892
|
+
walk() {
|
|
893
|
+
// For the Glob class, always return strings (ignore withFileTypes)
|
|
894
|
+
const opts = { ...this.options, withFileTypes: false };
|
|
895
|
+
return glob(this.pattern, opts);
|
|
896
|
+
}
|
|
897
|
+
walkSync() {
|
|
898
|
+
// For the Glob class, always return strings (ignore withFileTypes)
|
|
899
|
+
const opts = { ...this.options, withFileTypes: false };
|
|
900
|
+
return globSync(this.pattern, opts);
|
|
901
|
+
}
|
|
902
|
+
stream() {
|
|
903
|
+
return globStream(this.pattern, this.options);
|
|
904
|
+
}
|
|
905
|
+
streamSync() {
|
|
906
|
+
return globStreamSync(this.pattern, this.options);
|
|
907
|
+
}
|
|
908
|
+
iterate() {
|
|
909
|
+
return globIterate(this.pattern, this.options);
|
|
910
|
+
}
|
|
911
|
+
iterateSync() {
|
|
912
|
+
return globIterateSync(this.pattern, this.options);
|
|
913
|
+
}
|
|
914
|
+
// Make the class iterable
|
|
915
|
+
[Symbol.asyncIterator]() {
|
|
916
|
+
return this.iterate();
|
|
917
|
+
}
|
|
918
|
+
[Symbol.iterator]() {
|
|
919
|
+
return this.iterateSync();
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
exports.Glob = Glob;
|
|
923
|
+
// Default export
|
|
924
|
+
exports.default = glob;
|