mediasnacks 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -11
- package/TODO.md +3 -0
- package/package.json +1 -1
- package/src/avif.js +7 -9
- package/src/dropdups.js +4 -6
- package/src/hev1tohvc1.js +2 -2
- package/src/moov2front.js +2 -2
- package/src/qdir.js +3 -2
- package/src/resize.js +8 -10
- package/src/seqcheck.js +0 -1
- package/src/sqcrop.js +6 -8
- package/src/utils/fs-utils.js +1 -1
- package/src/utils/parseOptions.js +39 -0
- package/src/utils/parseOptions.test.js +59 -0
- package/src/vconcat.sh +1 -1
- package/tests/avif.test.js +22 -0
- package/tests/fixtures/lenna.avif +0 -0
- package/tests/fixtures/lenna.png +0 -0
- package/src/utils/args-with-globs.js +0 -61
- package/src/utils/args-with-globs.test.js +0 -153
package/README.md
CHANGED
|
@@ -38,26 +38,23 @@ Commands:
|
|
|
38
38
|
|
|
39
39
|
### Glob Patterns and Literal Filenames
|
|
40
40
|
|
|
41
|
-
Most commands accept glob patterns (like `*.png` or `file[234].png`) to match multiple
|
|
41
|
+
Most commands accept glob patterns (like `*.png` or `file[234].png`) to match multiple
|
|
42
|
+
files. By default, these patterns are expanded by Node.js to match existing files.
|
|
42
43
|
|
|
43
|
-
To treat arguments as literal filenames instead of
|
|
44
|
+
To treat arguments as literal filenames instead of
|
|
45
|
+
glob patterns, use the `--` (double dash) separator:
|
|
44
46
|
|
|
45
47
|
```shell
|
|
46
|
-
#
|
|
48
|
+
# Expands to: file2.png, file3.png, file4.png
|
|
47
49
|
npx mediasnacks avif file[234].png
|
|
48
50
|
|
|
49
|
-
#
|
|
51
|
+
# Literal filename: "file[234].png"
|
|
50
52
|
npx mediasnacks avif -- file[234].png
|
|
51
53
|
|
|
52
|
-
#
|
|
54
|
+
# Mixed: expand first pattern, treat second as literal
|
|
53
55
|
npx mediasnacks avif file2.png -- file[234].png
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
This follows the standard POSIX convention where `--` stops option/pattern processing. Arguments after `--` are treated exactly as written, which is useful when:
|
|
57
|
-
- Filenames contain special glob characters like `[`, `]`, `*`, or `?`
|
|
58
|
-
- You want to pass a pattern to be processed later
|
|
59
|
-
- Working with filenames that haven't been created yet
|
|
60
|
-
|
|
61
58
|
<br/>
|
|
62
59
|
|
|
63
60
|
### Converting Images to AVIF
|
|
@@ -98,7 +95,7 @@ Rearranges .mov and .mp4 metadata to the start of the file for fast-start stream
|
|
|
98
95
|
```shell
|
|
99
96
|
npx mediasnacks moov2front <videos>
|
|
100
97
|
```
|
|
101
|
-
|
|
98
|
+
What is Fast Start?
|
|
102
99
|
- https://wiki.avblocks.com/avblocks-for-cpp/muxer-parameters/mp4
|
|
103
100
|
- https://trac.ffmpeg.org/wiki/HowToCheckIfFaststartIsEnabledForPlayback
|
|
104
101
|
|
package/TODO.md
CHANGED
package/package.json
CHANGED
package/src/avif.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { join } from 'node:path'
|
|
3
|
+
import { join, basename } from 'node:path'
|
|
4
4
|
|
|
5
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
5
6
|
import { replaceExt, lstat } from './utils/fs-utils.js'
|
|
6
|
-
import { parseArgsWithGlobs } from './utils/args-with-globs.js'
|
|
7
7
|
import { ffmpeg, assertUserHasFFmpeg } from './utils/ffmpeg.js'
|
|
8
8
|
|
|
9
9
|
|
|
@@ -17,12 +17,10 @@ Converts images to AVIF.
|
|
|
17
17
|
async function main() {
|
|
18
18
|
await assertUserHasFFmpeg()
|
|
19
19
|
|
|
20
|
-
const { values, files } = await
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
help: { short: 'h', type: 'boolean', default: false },
|
|
25
|
-
}
|
|
20
|
+
const { values, files } = await parseOptions({
|
|
21
|
+
'output-dir': { type: 'string', default: '' },
|
|
22
|
+
overwrite: { short: 'y', type: 'boolean', default: false },
|
|
23
|
+
help: { short: 'h', type: 'boolean', default: false },
|
|
26
24
|
})
|
|
27
25
|
|
|
28
26
|
if (values.help) {
|
|
@@ -37,7 +35,7 @@ async function main() {
|
|
|
37
35
|
for (const file of files)
|
|
38
36
|
await toAvif({
|
|
39
37
|
file,
|
|
40
|
-
outFile: join(values['output-dir'], replaceExt(file, 'avif')),
|
|
38
|
+
outFile: join(values['output-dir'], replaceExt(basename(file), 'avif')),
|
|
41
39
|
overwrite: values.overwrite
|
|
42
40
|
})
|
|
43
41
|
}
|
package/src/dropdups.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { resolve, parse, format } from 'node:path'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
6
6
|
import { ffmpeg, assertUserHasFFmpeg, run } from './utils/ffmpeg.js'
|
|
7
7
|
|
|
8
8
|
|
|
@@ -34,11 +34,9 @@ Options:
|
|
|
34
34
|
async function main() {
|
|
35
35
|
await assertUserHasFFmpeg()
|
|
36
36
|
|
|
37
|
-
const { values, files } = await
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
help: { short: 'h', type: 'boolean', default: false },
|
|
41
|
-
}
|
|
37
|
+
const { values, files } = await parseOptions({
|
|
38
|
+
'bad-frame-number': { short: 'n', type: 'string', default: '' },
|
|
39
|
+
help: { short: 'h', type: 'boolean', default: false },
|
|
42
40
|
})
|
|
43
41
|
|
|
44
42
|
if (values.help) {
|
package/src/hev1tohvc1.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
3
4
|
import { uniqueFilenameFor, overwrite } from './utils/fs-utils.js'
|
|
4
|
-
import { parseArgsWithGlobs } from './utils/args-with-globs.js'
|
|
5
5
|
import { videoAttrs, ffmpeg, assertUserHasFFmpeg } from './utils/ffmpeg.js'
|
|
6
6
|
|
|
7
7
|
|
|
@@ -17,7 +17,7 @@ by changing the container’s sample entry code from HEV1 to HVC1.
|
|
|
17
17
|
async function main() {
|
|
18
18
|
await assertUserHasFFmpeg()
|
|
19
19
|
|
|
20
|
-
const { files } = await
|
|
20
|
+
const { files } = await parseOptions()
|
|
21
21
|
|
|
22
22
|
if (!files.length)
|
|
23
23
|
throw new Error(USAGE)
|
package/src/moov2front.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { ffmpeg, assertUserHasFFmpeg } from './utils/ffmpeg.js'
|
|
4
4
|
import { uniqueFilenameFor, overwrite } from './utils/fs-utils.js'
|
|
5
|
-
import {
|
|
5
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
const USAGE = `
|
|
@@ -16,7 +16,7 @@ Files are overwritten.
|
|
|
16
16
|
async function main() {
|
|
17
17
|
await assertUserHasFFmpeg()
|
|
18
18
|
|
|
19
|
-
const { files } = await
|
|
19
|
+
const { files } = await parseOptions()
|
|
20
20
|
|
|
21
21
|
if (!files.length)
|
|
22
22
|
throw new Error(USAGE)
|
package/src/qdir.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { join } from 'node:path'
|
|
3
4
|
import { spawn } from 'node:child_process'
|
|
4
5
|
import { parseArgs } from 'node:util'
|
|
5
|
-
import {
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
6
7
|
import { readdir, writeFile, unlink, rename } from 'node:fs/promises'
|
|
8
|
+
|
|
7
9
|
import { isFile } from './utils/fs-utils.js'
|
|
8
|
-
import { fileURLToPath } from 'node:url'
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
const USAGE = `
|
package/src/resize.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
import { rename } from 'node:fs/promises'
|
|
5
5
|
|
|
6
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
6
7
|
import { isFile, uniqueFilenameFor } from './utils/fs-utils.js'
|
|
7
|
-
import { parseArgsWithGlobs } from './utils/args-with-globs.js'
|
|
8
8
|
import { ffmpeg, videoAttrs, assertUserHasFFmpeg } from './utils/ffmpeg.js'
|
|
9
9
|
|
|
10
10
|
|
|
@@ -30,14 +30,12 @@ Details:
|
|
|
30
30
|
async function main() {
|
|
31
31
|
await assertUserHasFFmpeg()
|
|
32
32
|
|
|
33
|
-
const { values, files } = await
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
help: { short: 'h', type: 'boolean', default: false },
|
|
40
|
-
}
|
|
33
|
+
const { values, files } = await parseOptions({
|
|
34
|
+
width: { type: 'string', default: '-2' },
|
|
35
|
+
height: { type: 'string', default: '-2' },
|
|
36
|
+
'output-dir': { type: 'string', default: '' },
|
|
37
|
+
overwrite: { short: 'y', type: 'boolean', default: false },
|
|
38
|
+
help: { short: 'h', type: 'boolean', default: false },
|
|
41
39
|
})
|
|
42
40
|
|
|
43
41
|
if (values.help) {
|
|
@@ -59,7 +57,7 @@ async function main() {
|
|
|
59
57
|
for (const file of files)
|
|
60
58
|
await resize({
|
|
61
59
|
file,
|
|
62
|
-
outFile: join(values['output-dir'], file),
|
|
60
|
+
outFile: join(values['output-dir'], file), // TODO basename ?
|
|
63
61
|
overwrite: values.overwrite,
|
|
64
62
|
width,
|
|
65
63
|
height,
|
package/src/seqcheck.js
CHANGED
package/src/sqcrop.js
CHANGED
|
@@ -4,8 +4,8 @@ import { join } from 'node:path'
|
|
|
4
4
|
|
|
5
5
|
import { rename } from 'node:fs/promises'
|
|
6
6
|
import { ffmpeg, assertUserHasFFmpeg } from './utils/ffmpeg.js'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { lstat, uniqueFilenameFor } from './utils/fs-utils.js'
|
|
8
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
const USAGE = `
|
|
@@ -18,12 +18,10 @@ Square crops images
|
|
|
18
18
|
async function main() {
|
|
19
19
|
await assertUserHasFFmpeg()
|
|
20
20
|
|
|
21
|
-
const { values, files } = await
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
help: { short: 'h', type: 'boolean', default: false },
|
|
26
|
-
}
|
|
21
|
+
const { values, files } = await parseOptions({
|
|
22
|
+
'output-dir': { type: 'string', default: '' },
|
|
23
|
+
overwrite: { short: 'y', type: 'boolean', default: false },
|
|
24
|
+
help: { short: 'h', type: 'boolean', default: false },
|
|
27
25
|
})
|
|
28
26
|
|
|
29
27
|
if (values.help) {
|
package/src/utils/fs-utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { lstatSync } from 'node:fs'
|
|
1
2
|
import { randomUUID } from 'node:crypto'
|
|
2
3
|
import { unlink, rename } from 'node:fs/promises'
|
|
3
4
|
import { dirname, extname, join } from 'node:path'
|
|
4
|
-
import { lstatSync } from 'node:fs'
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export const lstat = f => lstatSync(f, { throwIfNoEntry: false })
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { promisify, parseArgs } from 'node:util'
|
|
2
|
+
import { glob as _glob } from 'node:fs'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const glob = promisify(_glob)
|
|
6
|
+
|
|
7
|
+
export async function parseOptions(options = {}, config = {}) {
|
|
8
|
+
const { values, positionals, tokens } = parseArgs({
|
|
9
|
+
allowPositionals: true,
|
|
10
|
+
options,
|
|
11
|
+
...config,
|
|
12
|
+
tokens: true
|
|
13
|
+
})
|
|
14
|
+
return {
|
|
15
|
+
values,
|
|
16
|
+
files: await resolveGlobs(positionals, tokens)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function resolveGlobs(arr, tokens = []) {
|
|
21
|
+
const terminatorIndex = tokens.find(t => t.kind === 'option-terminator')?.index ?? -1
|
|
22
|
+
const set = new Set()
|
|
23
|
+
|
|
24
|
+
const globable = terminatorIndex === -1
|
|
25
|
+
? arr
|
|
26
|
+
: arr.slice(0, terminatorIndex)
|
|
27
|
+
|
|
28
|
+
for (const g of globable)
|
|
29
|
+
for (const file of await glob(g))
|
|
30
|
+
set.add(file)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if (terminatorIndex !== -1)
|
|
34
|
+
for (const literal of arr.slice(terminatorIndex))
|
|
35
|
+
set.add(literal)
|
|
36
|
+
|
|
37
|
+
return Array.from(set)
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { equal, deepEqual } from 'node:assert/strict'
|
|
4
|
+
import { mkdtemp, writeFile, rm } from 'node:fs/promises'
|
|
5
|
+
import { test, describe, before, after } from 'node:test'
|
|
6
|
+
|
|
7
|
+
import { parseOptions } from './parseOptions.js'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
describe('parseArgsWithGlobs', () => {
|
|
11
|
+
let testDir
|
|
12
|
+
let inTmpDir = f => join(testDir, f)
|
|
13
|
+
const testFiles = ['file1.png', 'file2.png', 'file3.png']
|
|
14
|
+
|
|
15
|
+
before(async () => {
|
|
16
|
+
testDir = await mkdtemp(join(tmpdir(), 'parse-args-'))
|
|
17
|
+
for (const file of testFiles)
|
|
18
|
+
await writeFile(inTmpDir(file), '')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
after(() => rm(testDir))
|
|
22
|
+
|
|
23
|
+
test('parses args and globs files', async () => {
|
|
24
|
+
const { values, files } = await parseOptions({
|
|
25
|
+
'output-dir': { type: 'string' }
|
|
26
|
+
}, {
|
|
27
|
+
args: ['--output-dir', '/tmp', inTmpDir('file[12].png')],
|
|
28
|
+
})
|
|
29
|
+
equal(values['output-dir'], '/tmp')
|
|
30
|
+
deepEqual(files, [
|
|
31
|
+
inTmpDir('file1.png'),
|
|
32
|
+
inTmpDir('file2.png')
|
|
33
|
+
])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('respects verbatim tokens', async () => {
|
|
37
|
+
const literal0 = 'literal-file[98].png'
|
|
38
|
+
const literal1 = 'literal-file[99].png'
|
|
39
|
+
const { files } = await parseOptions({}, {
|
|
40
|
+
args: [inTmpDir('file[12].png'), '--', literal0, literal1]
|
|
41
|
+
})
|
|
42
|
+
deepEqual(files, [
|
|
43
|
+
inTmpDir('file1.png'),
|
|
44
|
+
inTmpDir('file2.png'),
|
|
45
|
+
literal0,
|
|
46
|
+
literal1,
|
|
47
|
+
])
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('empty files array when no positionals', async () => {
|
|
51
|
+
const { files, values } = await parseOptions({
|
|
52
|
+
foo: { type: 'boolean' }
|
|
53
|
+
}, {
|
|
54
|
+
args: ['--foo'],
|
|
55
|
+
})
|
|
56
|
+
equal(values.foo, true)
|
|
57
|
+
deepEqual(files, [])
|
|
58
|
+
})
|
|
59
|
+
})
|
package/src/vconcat.sh
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { equal } from 'node:assert/strict'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { execSync } from 'node:child_process'
|
|
6
|
+
import { createHash } from 'node:crypto'
|
|
7
|
+
import { mkdtempSync, readFileSync } from 'node:fs'
|
|
8
|
+
|
|
9
|
+
function sha1(filePath) {
|
|
10
|
+
return createHash('sha1').update(readFileSync(filePath)).digest('hex')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('PNG to AVIF', () => {
|
|
14
|
+
const tmp = mkdtempSync(join(tmpdir(), 'avif-test-'))
|
|
15
|
+
|
|
16
|
+
execSync(`src/cli.js avif --output-dir ${tmp} tests/fixtures/lenna.png`, {
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
stdio: 'inherit'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
equal(sha1(join(tmp, 'lenna.avif')), sha1('tests/fixtures/lenna.avif'))
|
|
22
|
+
})
|
|
Binary file
|
|
Binary file
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { promisify } from 'node:util'
|
|
2
|
-
import { parseArgs } from 'node:util'
|
|
3
|
-
import { glob as _glob } from 'node:fs'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const glob = promisify(_glob)
|
|
7
|
-
|
|
8
|
-
export async function parseArgsWithGlobs(config) {
|
|
9
|
-
const { values, positionals, tokens } = parseArgs({
|
|
10
|
-
allowPositionals: true,
|
|
11
|
-
...config,
|
|
12
|
-
tokens: true
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
const files = await globAll(positionals, tokens)
|
|
16
|
-
|
|
17
|
-
return { values, positionals, tokens, files }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function globAll(arr, tokens) {
|
|
21
|
-
const terminatorIndex = findTerminatorIndex(tokens)
|
|
22
|
-
const set = new Set()
|
|
23
|
-
|
|
24
|
-
if (terminatorIndex >= 0) {
|
|
25
|
-
// Glob arguments before the terminator
|
|
26
|
-
const beforeTerminator = arr.slice(0, terminatorIndex)
|
|
27
|
-
const afterTerminator = arr.slice(terminatorIndex)
|
|
28
|
-
|
|
29
|
-
for (const g of beforeTerminator)
|
|
30
|
-
for (const file of await glob(g))
|
|
31
|
-
set.add(file)
|
|
32
|
-
|
|
33
|
-
// Add arguments after terminator as literal strings
|
|
34
|
-
for (const literal of afterTerminator)
|
|
35
|
-
set.add(literal)
|
|
36
|
-
} else {
|
|
37
|
-
// No terminator, glob everything (current behavior)
|
|
38
|
-
for (const g of arr)
|
|
39
|
-
for (const file of await glob(g))
|
|
40
|
-
set.add(file)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return Array.from(set)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function findTerminatorIndex(tokens) {
|
|
47
|
-
if (!tokens) return -1
|
|
48
|
-
for (const token of tokens) {
|
|
49
|
-
if (token.kind === 'option-terminator') {
|
|
50
|
-
// Find the position in the positionals array
|
|
51
|
-
// The terminator itself is not in positionals, so we count preceding positionals
|
|
52
|
-
let positionalCount = 0
|
|
53
|
-
for (const t of tokens) {
|
|
54
|
-
if (t === token) break
|
|
55
|
-
if (t.kind === 'positional') positionalCount++
|
|
56
|
-
}
|
|
57
|
-
return positionalCount
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return -1
|
|
61
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { equal, deepEqual } from 'node:assert/strict'
|
|
2
|
-
import test, { describe, before, after } from 'node:test'
|
|
3
|
-
import { mkdtemp, writeFile, rm } from 'node:fs/promises'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
import { tmpdir } from 'node:os'
|
|
6
|
-
import { globAll, parseArgsWithGlobs } from './args-with-globs.js'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
describe('globAll with token terminator', () => {
|
|
10
|
-
let testDir
|
|
11
|
-
const testFiles = ['test-file2.png', 'test-file3.png', 'test-file4.png', 'other-file.png']
|
|
12
|
-
|
|
13
|
-
before(async () => {
|
|
14
|
-
testDir = await mkdtemp(join(tmpdir(), 'glob-test-'))
|
|
15
|
-
for (const file of testFiles) {
|
|
16
|
-
await writeFile(join(testDir, file), '')
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
after(async () => {
|
|
21
|
-
await rm(testDir, { recursive: true, force: true })
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('globs patterns when no terminator present', async () => {
|
|
25
|
-
const pattern = join(testDir, 'test-file[234].png')
|
|
26
|
-
const result = await globAll([pattern])
|
|
27
|
-
const expected = testFiles.slice(0, 3).map(f => join(testDir, f)).sort()
|
|
28
|
-
deepEqual(result.sort(), expected)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('globs patterns before terminator and keeps literals after', async () => {
|
|
32
|
-
const pattern = join(testDir, 'test-file[234].png')
|
|
33
|
-
const literal = 'my-literal-file[234].png'
|
|
34
|
-
|
|
35
|
-
// Simulate tokens from parseArgs with terminator
|
|
36
|
-
const tokens = [
|
|
37
|
-
{ kind: 'positional', index: 0, value: pattern },
|
|
38
|
-
{ kind: 'option-terminator', index: 1 },
|
|
39
|
-
{ kind: 'positional', index: 1, value: literal }
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
const result = await globAll([pattern, literal], tokens)
|
|
43
|
-
const expected = [
|
|
44
|
-
...testFiles.slice(0, 3).map(f => join(testDir, f)),
|
|
45
|
-
literal
|
|
46
|
-
].sort()
|
|
47
|
-
deepEqual(result.sort(), expected)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('keeps all arguments literal when terminator is first', async () => {
|
|
51
|
-
const literal1 = 'literal-file[234].png'
|
|
52
|
-
const literal2 = 'another-file[567].png'
|
|
53
|
-
|
|
54
|
-
// Simulate tokens where terminator comes before all positionals
|
|
55
|
-
const tokens = [
|
|
56
|
-
{ kind: 'option-terminator', index: 0 },
|
|
57
|
-
{ kind: 'positional', index: 0, value: literal1 },
|
|
58
|
-
{ kind: 'positional', index: 1, value: literal2 }
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
const result = await globAll([literal1, literal2], tokens)
|
|
62
|
-
deepEqual(result.sort(), [literal1, literal2].sort())
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('handles no terminator with tokens (backwards compat)', async () => {
|
|
66
|
-
const pattern = join(testDir, 'test-file[234].png')
|
|
67
|
-
|
|
68
|
-
// Tokens without terminator
|
|
69
|
-
const tokens = [
|
|
70
|
-
{ kind: 'positional', index: 0, value: pattern }
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
const result = await globAll([pattern], tokens)
|
|
74
|
-
const expected = testFiles.slice(0, 3).map(f => join(testDir, f)).sort()
|
|
75
|
-
deepEqual(result.sort(), expected)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test('handles undefined tokens (backwards compat)', async () => {
|
|
79
|
-
const pattern = join(testDir, 'test-file[234].png')
|
|
80
|
-
const result = await globAll([pattern], undefined)
|
|
81
|
-
const expected = testFiles.slice(0, 3).map(f => join(testDir, f)).sort()
|
|
82
|
-
deepEqual(result.sort(), expected)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('handles empty arrays', async () => {
|
|
86
|
-
const result = await globAll([])
|
|
87
|
-
deepEqual(result, [])
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
test('deduplicates results', async () => {
|
|
91
|
-
const pattern = join(testDir, 'test-file2.png')
|
|
92
|
-
const result = await globAll([pattern, pattern])
|
|
93
|
-
deepEqual(result, [join(testDir, 'test-file2.png')])
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe('parseArgsWithGlobs', () => {
|
|
98
|
-
let testDir
|
|
99
|
-
const testFiles = ['file1.png', 'file2.png', 'file3.png']
|
|
100
|
-
|
|
101
|
-
before(async () => {
|
|
102
|
-
testDir = await mkdtemp(join(tmpdir(), 'parse-args-test-'))
|
|
103
|
-
for (const file of testFiles) {
|
|
104
|
-
await writeFile(join(testDir, file), '')
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
after(async () => {
|
|
109
|
-
await rm(testDir, { recursive: true, force: true })
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('parses args and globs files in one call', async () => {
|
|
113
|
-
const pattern = join(testDir, 'file[12].png')
|
|
114
|
-
const { values, files } = await parseArgsWithGlobs({
|
|
115
|
-
args: ['--output-dir', '/tmp', pattern],
|
|
116
|
-
options: {
|
|
117
|
-
'output-dir': { type: 'string' }
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
equal(values['output-dir'], '/tmp')
|
|
122
|
-
deepEqual(files.sort(), [
|
|
123
|
-
join(testDir, 'file1.png'),
|
|
124
|
-
join(testDir, 'file2.png')
|
|
125
|
-
].sort())
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
test('respects verbatim token in parseArgsWithGlobs', async () => {
|
|
129
|
-
const pattern = join(testDir, 'file[12].png')
|
|
130
|
-
const literal = 'literal-file[99].png'
|
|
131
|
-
const { files } = await parseArgsWithGlobs({
|
|
132
|
-
args: [pattern, '--', literal]
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
deepEqual(files.sort(), [
|
|
136
|
-
join(testDir, 'file1.png'),
|
|
137
|
-
join(testDir, 'file2.png'),
|
|
138
|
-
literal
|
|
139
|
-
].sort())
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('returns empty files array when no positionals', async () => {
|
|
143
|
-
const { files, values } = await parseArgsWithGlobs({
|
|
144
|
-
args: ['--flag'],
|
|
145
|
-
options: {
|
|
146
|
-
flag: { type: 'boolean' }
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
equal(values.flag, true)
|
|
151
|
-
deepEqual(files, [])
|
|
152
|
-
})
|
|
153
|
-
})
|