1#!/usr/bin/env node
2import { foregroundChild } from 'foreground-child';
3import { existsSync } from 'fs';
4import { readFile } from 'fs/promises';
5import { jack } from 'jackspeak';
6import { join } from 'path';
7import { fileURLToPath } from 'url';
8import { globStream } from './index.js';
9/* c8 ignore start */
10const { version } = JSON.parse(await readFile(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8').catch(() => readFile(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8')));
11/* c8 ignore stop */
12const j = jack({
13    usage: 'glob [options] [<pattern> [<pattern> ...]]',
14})
15    .description(`
16    Glob v${version}
17
18    Expand the positional glob expression arguments into any matching file
19    system paths found.
20  `)
21    .opt({
22    cmd: {
23        short: 'c',
24        hint: 'command',
25        description: `Run the command provided, passing the glob expression
26                    matches as arguments.`,
27    },
28})
29    .opt({
30    default: {
31        short: 'p',
32        hint: 'pattern',
33        description: `If no positional arguments are provided, glob will use
34                    this pattern`,
35    },
36})
37    .flag({
38    all: {
39        short: 'A',
40        description: `By default, the glob cli command will not expand any
41                    arguments that are an exact match to a file on disk.
42
43                    This prevents double-expanding, in case the shell expands
44                    an argument whose filename is a glob expression.
45
46                    For example, if 'app/*.ts' would match 'app/[id].ts', then
47                    on Windows powershell or cmd.exe, 'glob app/*.ts' will
48                    expand to 'app/[id].ts', as expected. However, in posix
49                    shells such as bash or zsh, the shell will first expand
50                    'app/*.ts' to a list of filenames. Then glob will look
51                    for a file matching 'app/[id].ts' (ie, 'app/i.ts' or
52                    'app/d.ts'), which is unexpected.
53
54                    Setting '--all' prevents this behavior, causing glob
55                    to treat ALL patterns as glob expressions to be expanded,
56                    even if they are an exact match to a file on disk.
57
58                    When setting this option, be sure to enquote arguments
59                    so that the shell will not expand them prior to passing
60                    them to the glob command process.
61      `,
62    },
63    absolute: {
64        short: 'a',
65        description: 'Expand to absolute paths',
66    },
67    'dot-relative': {
68        short: 'd',
69        description: `Prepend './' on relative matches`,
70    },
71    mark: {
72        short: 'm',
73        description: `Append a / on any directories matched`,
74    },
75    posix: {
76        short: 'x',
77        description: `Always resolve to posix style paths, using '/' as the
78                    directory separator, even on Windows. Drive letter
79                    absolute matches on Windows will be expanded to their
80                    full resolved UNC maths, eg instead of 'C:\\foo\\bar',
81                    it will expand to '//?/C:/foo/bar'.
82      `,
83    },
84    follow: {
85        short: 'f',
86        description: `Follow symlinked directories when expanding '**'`,
87    },
88    realpath: {
89        short: 'R',
90        description: `Call 'fs.realpath' on all of the results. In the case
91                    of an entry that cannot be resolved, the entry is
92                    omitted. This incurs a slight performance penalty, of
93                    course, because of the added system calls.`,
94    },
95    stat: {
96        short: 's',
97        description: `Call 'fs.lstat' on all entries, whether required or not
98                    to determine if it's a valid match.`,
99    },
100    'match-base': {
101        short: 'b',
102        description: `Perform a basename-only match if the pattern does not
103                    contain any slash characters. That is, '*.js' would be
104                    treated as equivalent to '**/*.js', matching js files
105                    in all directories.
106      `,
107    },
108    dot: {
109        description: `Allow patterns to match files/directories that start
110                    with '.', even if the pattern does not start with '.'
111      `,
112    },
113    nobrace: {
114        description: 'Do not expand {...} patterns',
115    },
116    nocase: {
117        description: `Perform a case-insensitive match. This defaults to
118                    'true' on macOS and Windows platforms, and false on
119                    all others.
120
121                    Note: 'nocase' should only be explicitly set when it is
122                    known that the filesystem's case sensitivity differs
123                    from the platform default. If set 'true' on
124                    case-insensitive file systems, then the walk may return
125                    more or less results than expected.
126      `,
127    },
128    nodir: {
129        description: `Do not match directories, only files.
130
131                    Note: to *only* match directories, append a '/' at the
132                    end of the pattern.
133      `,
134    },
135    noext: {
136        description: `Do not expand extglob patterns, such as '+(a|b)'`,
137    },
138    noglobstar: {
139        description: `Do not expand '**' against multiple path portions.
140                    Ie, treat it as a normal '*' instead.`,
141    },
142    'windows-path-no-escape': {
143        description: `Use '\\' as a path separator *only*, and *never* as an
144                    escape character. If set, all '\\' characters are
145                    replaced with '/' in the pattern.`,
146    },
147})
148    .num({
149    'max-depth': {
150        short: 'D',
151        description: `Maximum depth to traverse from the current
152                    working directory`,
153    },
154})
155    .opt({
156    cwd: {
157        short: 'C',
158        description: 'Current working directory to execute/match in',
159        default: process.cwd(),
160    },
161    root: {
162        short: 'r',
163        description: `A string path resolved against the 'cwd', which is
164                    used as the starting point for absolute patterns that
165                    start with '/' (but not drive letters or UNC paths
166                    on Windows).
167
168                    Note that this *doesn't* necessarily limit the walk to
169                    the 'root' directory, and doesn't affect the cwd
170                    starting point for non-absolute patterns. A pattern
171                    containing '..' will still be able to traverse out of
172                    the root directory, if it is not an actual root directory
173                    on the filesystem, and any non-absolute patterns will
174                    still be matched in the 'cwd'.
175
176                    To start absolute and non-absolute patterns in the same
177                    path, you can use '--root=' to set it to the empty
178                    string. However, be aware that on Windows systems, a
179                    pattern like 'x:/*' or '//host/share/*' will *always*
180                    start in the 'x:/' or '//host/share/' directory,
181                    regardless of the --root setting.
182      `,
183    },
184    platform: {
185        description: `Defaults to the value of 'process.platform' if
186                    available, or 'linux' if not. Setting --platform=win32
187                    on non-Windows systems may cause strange behavior!`,
188        validate: v => new Set([
189            'aix',
190            'android',
191            'darwin',
192            'freebsd',
193            'haiku',
194            'linux',
195            'openbsd',
196            'sunos',
197            'win32',
198            'cygwin',
199            'netbsd',
200        ]).has(v),
201    },
202})
203    .optList({
204    ignore: {
205        short: 'i',
206        description: `Glob patterns to ignore`,
207    },
208})
209    .flag({
210    debug: {
211        short: 'v',
212        description: `Output a huge amount of noisy debug information about
213                    patterns as they are parsed and used to match files.`,
214    },
215})
216    .flag({
217    help: {
218        short: 'h',
219        description: 'Show this usage information',
220    },
221});
222try {
223    const { positionals, values } = j.parse();
224    if (values.help) {
225        console.log(j.usage());
226        process.exit(0);
227    }
228    if (positionals.length === 0 && !values.default)
229        throw 'No patterns provided';
230    if (positionals.length === 0 && values.default)
231        positionals.push(values.default);
232    const patterns = values.all
233        ? positionals
234        : positionals.filter(p => !existsSync(p));
235    const matches = values.all
236        ? []
237        : positionals.filter(p => existsSync(p)).map(p => join(p));
238    const stream = globStream(patterns, {
239        absolute: values.absolute,
240        cwd: values.cwd,
241        dot: values.dot,
242        dotRelative: values['dot-relative'],
243        follow: values.follow,
244        ignore: values.ignore,
245        mark: values.mark,
246        matchBase: values['match-base'],
247        maxDepth: values['max-depth'],
248        nobrace: values.nobrace,
249        nocase: values.nocase,
250        nodir: values.nodir,
251        noext: values.noext,
252        noglobstar: values.noglobstar,
253        platform: values.platform,
254        realpath: values.realpath,
255        root: values.root,
256        stat: values.stat,
257        debug: values.debug,
258        posix: values.posix,
259    });
260    const cmd = values.cmd;
261    if (!cmd) {
262        matches.forEach(m => console.log(m));
263        stream.on('data', f => console.log(f));
264    }
265    else {
266        stream.on('data', f => matches.push(f));
267        stream.on('end', () => foregroundChild(cmd, matches, { shell: true }));
268    }
269}
270catch (e) {
271    console.error(j.usage());
272    console.error(e instanceof Error ? e.message : String(e));
273    process.exit(1);
274}
275//# sourceMappingURL=bin.mjs.map