Skip to content

Commit 3062616

Browse files
committed
DRY all commands, correct overload signature types
This moves the repetitive overload definitions into a single type created by a makeCommand method. This also means that all of the repetitive setup and validation can be moved to one place as well. Corrects a bug where TS thought that a `Promise<void>` would be returned, which actually it was an instance of Unpack (or whatever). The type calculus is rather baroque. The next major will replace the overloaded function signatures with separately named functions, so there'll be `createFile`, `createSync`, `createFileSync`, etc.
1 parent ce612d0 commit 3062616

20 files changed

+743
-540
lines changed

src/create.ts

Lines changed: 20 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,16 @@
1+
import { WriteStream, WriteStreamSync } from '@isaacs/fs-minipass'
2+
import { Minipass } from 'minipass'
3+
import path from 'node:path'
4+
import { list } from './list.js'
5+
import { makeCommand } from './make-command.js'
16
import {
2-
dealias,
3-
isFile,
4-
isSync,
5-
isSyncFile,
67
TarOptions,
78
TarOptionsFile,
89
TarOptionsSync,
910
TarOptionsSyncFile,
10-
TarOptionsWithAliases,
11-
TarOptionsWithAliasesFile,
12-
TarOptionsWithAliasesSync,
13-
TarOptionsWithAliasesSyncFile,
1411
} from './options.js'
15-
16-
import { WriteStream, WriteStreamSync } from '@isaacs/fs-minipass'
17-
import { Minipass } from 'minipass'
18-
import path from 'node:path'
19-
import { list } from './list.js'
2012
import { Pack, PackSync } from './pack.js'
2113

22-
export function create(
23-
opt: TarOptionsWithAliasesSyncFile,
24-
files?: string[],
25-
): void
26-
export function create(
27-
opt: TarOptionsWithAliasesSync,
28-
files?: string[],
29-
): void
30-
export function create(
31-
opt: TarOptionsWithAliasesFile,
32-
files?: string[],
33-
cb?: () => any,
34-
): Promise<void>
35-
export function create(
36-
opt: TarOptionsWithAliasesFile,
37-
cb: () => any,
38-
): Promise<void>
39-
export function create(
40-
opt: TarOptionsWithAliases,
41-
files?: string[],
42-
): Pack
43-
export function create(
44-
opt_: TarOptionsWithAliases,
45-
files?: string[] | (() => any),
46-
cb?: () => any,
47-
): void | Promise<void> | Pack {
48-
if (typeof files === 'function') {
49-
cb = files
50-
}
51-
52-
if (Array.isArray(opt_)) {
53-
;(files = opt_), (opt_ = {})
54-
}
55-
56-
if (!files || !Array.isArray(files) || !files.length) {
57-
throw new TypeError('no files or directories specified')
58-
}
59-
60-
files = Array.from(files)
61-
62-
const opt = dealias(opt_)
63-
64-
if (opt.sync && typeof cb === 'function') {
65-
throw new TypeError(
66-
'callback not supported for sync tar functions',
67-
)
68-
}
69-
70-
if (!opt.file && typeof cb === 'function') {
71-
throw new TypeError('callback only supported with file option')
72-
}
73-
74-
return (
75-
isSyncFile(opt) ? createFileSync(opt, files)
76-
: isFile(opt) ? createFile(opt, files, cb)
77-
: isSync(opt) ? createSync(opt, files)
78-
: create_(opt, files)
79-
)
80-
}
81-
8214
const createFileSync = (opt: TarOptionsSyncFile, files: string[]) => {
8315
const p = new PackSync(opt)
8416
const stream = new WriteStreamSync(opt.file, {
@@ -88,11 +20,7 @@ const createFileSync = (opt: TarOptionsSyncFile, files: string[]) => {
8820
addFilesSync(p, files)
8921
}
9022

91-
const createFile = (
92-
opt: TarOptionsFile,
93-
files: string[],
94-
cb?: () => any,
95-
) => {
23+
const createFile = (opt: TarOptionsFile, files: string[]) => {
9624
const p = new Pack(opt)
9725
const stream = new WriteStream(opt.file, {
9826
mode: opt.mode || 0o666,
@@ -107,7 +35,7 @@ const createFile = (
10735

10836
addFilesAsync(p, files)
10937

110-
return cb ? promise.then(cb, cb) : promise
38+
return promise
11139
}
11240

11341
const addFilesSync = (p: PackSync, files: string[]) => {
@@ -153,8 +81,20 @@ const createSync = (opt: TarOptionsSync, files: string[]) => {
15381
return p
15482
}
15583

156-
const create_ = (opt: TarOptions, files: string[]) => {
84+
const createAsync = (opt: TarOptions, files: string[]) => {
15785
const p = new Pack(opt)
15886
addFilesAsync(p, files)
15987
return p
16088
}
89+
90+
export const create = makeCommand(
91+
createFileSync,
92+
createFile,
93+
createSync,
94+
createAsync,
95+
(_opt, files) => {
96+
if (!files?.length) {
97+
throw new TypeError('no paths specified to add to archive')
98+
}
99+
},
100+
)

src/extract.ts

Lines changed: 14 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,13 @@
11
// tar -x
22
import * as fsm from '@isaacs/fs-minipass'
33
import fs from 'node:fs'
4-
import { dirname, parse } from 'node:path'
5-
import {
6-
dealias,
7-
isFile,
8-
isSync,
9-
isSyncFile,
10-
TarOptions,
11-
TarOptionsFile,
12-
TarOptionsSync,
13-
TarOptionsSyncFile,
14-
TarOptionsWithAliases,
15-
TarOptionsWithAliasesFile,
16-
TarOptionsWithAliasesSync,
17-
TarOptionsWithAliasesSyncFile,
18-
} from './options.js'
19-
import { stripTrailingSlashes } from './strip-trailing-slashes.js'
4+
import { filesFilter } from './list.js'
5+
import { makeCommand } from './make-command.js'
6+
import { TarOptionsFile, TarOptionsSyncFile } from './options.js'
207
import { Unpack, UnpackSync } from './unpack.js'
218

22-
export function extract(
23-
opt: TarOptionsWithAliasesSyncFile,
24-
files?: string[],
25-
): void
26-
export function extract(
27-
opt: TarOptionsWithAliasesSync,
28-
files?: string[],
29-
): void
30-
export function extract(
31-
opt: TarOptionsWithAliasesFile,
32-
files?: string[],
33-
cb?: () => any,
34-
): Promise<void>
35-
export function extract(
36-
opt: TarOptionsWithAliasesFile,
37-
cb: () => any,
38-
): Promise<void>
39-
export function extract(
40-
opt: TarOptionsWithAliases,
41-
files?: string[],
42-
): Unpack
43-
export function extract(
44-
opt_: TarOptionsWithAliases,
45-
files?: string[] | (() => any),
46-
cb?: () => any,
47-
): void | Promise<void> | Unpack {
48-
if (typeof opt_ === 'function') {
49-
;(cb = opt_), (files = undefined), (opt_ = {})
50-
} else if (Array.isArray(opt_)) {
51-
;(files = opt_), (opt_ = {})
52-
}
53-
54-
if (typeof files === 'function') {
55-
;(cb = files), (files = undefined)
56-
}
57-
58-
if (!files) {
59-
files = []
60-
} else {
61-
files = Array.from(files)
62-
}
63-
64-
const opt = dealias(opt_)
65-
66-
if (opt.sync && typeof cb === 'function') {
67-
throw new TypeError(
68-
'callback not supported for sync tar functions',
69-
)
70-
}
71-
72-
if (!opt.file && typeof cb === 'function') {
73-
throw new TypeError('callback only supported with file option')
74-
}
75-
76-
if (files.length) {
77-
filesFilter(opt, files)
78-
}
79-
80-
return (
81-
isSyncFile(opt) ? extractFileSync(opt)
82-
: isFile(opt) ? extractFile(opt, cb)
83-
: isSync(opt) ? extractSync(opt)
84-
: extract_(opt)
85-
)
86-
}
87-
88-
// construct a filter that limits the file entries listed
89-
// include child entries if a dir is included
90-
const filesFilter = (opt: TarOptions, files: string[]) => {
91-
const map = new Map(files.map(f => [stripTrailingSlashes(f), true]))
92-
const filter = opt.filter
93-
94-
const mapHas = (file: string, r: string = ''): boolean => {
95-
const root = r || parse(file).root || '.'
96-
let ret: boolean
97-
if (file === root) ret = false
98-
else {
99-
const m = map.get(file)
100-
if (m !== undefined) {
101-
ret = m
102-
} else {
103-
ret = mapHas(dirname(file), root)
104-
}
105-
}
106-
107-
map.set(file, ret)
108-
return ret
109-
}
110-
111-
opt.filter =
112-
filter ?
113-
(file, entry) =>
114-
filter(file, entry) && mapHas(stripTrailingSlashes(file))
115-
: file => mapHas(stripTrailingSlashes(file))
116-
}
117-
1189
const extractFileSync = (opt: TarOptionsSyncFile) => {
11910
const u = new UnpackSync(opt)
120-
12111
const file = opt.file
12212
const stat = fs.statSync(file)
12313
// This trades a zero-byte read() syscall for a stat
@@ -130,7 +20,7 @@ const extractFileSync = (opt: TarOptionsSyncFile) => {
13020
stream.pipe(u)
13121
}
13222

133-
const extractFile = (opt: TarOptionsFile, cb?: () => void) => {
23+
const extractFile = (opt: TarOptionsFile, _?: string[]) => {
13424
const u = new Unpack(opt)
13525
const readSize = opt.maxReadSize || 16 * 1024 * 1024
13626

@@ -154,9 +44,15 @@ const extractFile = (opt: TarOptionsFile, cb?: () => void) => {
15444
}
15545
})
15646
})
157-
return cb ? p.then(cb, cb) : p
47+
return p
15848
}
15949

160-
const extractSync = (opt: TarOptionsSync) => new UnpackSync(opt)
161-
162-
const extract_ = (opt: TarOptions) => new Unpack(opt)
50+
export const extract = makeCommand<Unpack, UnpackSync>(
51+
extractFileSync,
52+
extractFile,
53+
opt => new UnpackSync(opt),
54+
opt => new Unpack(opt),
55+
(opt, files) => {
56+
if (files?.length) filesFilter(opt, files)
57+
},
58+
)

src/index.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
export * from './create.js'
2-
export * from './replace.js'
3-
export * from './list.js'
4-
export * from './update.js'
5-
export * from './extract.js'
1+
export {
2+
type TarOptionsWithAliasesAsync,
3+
type TarOptionsWithAliasesAsyncFile,
4+
type TarOptionsWithAliasesAsyncNoFile,
5+
type TarOptionsWithAliasesSyncNoFile,
6+
type TarOptionsWithAliases,
7+
type TarOptionsWithAliasesFile,
8+
type TarOptionsWithAliasesSync,
9+
type TarOptionsWithAliasesSyncFile,
10+
} from './options.js'
611

12+
export * from './create.js'
713
export { create as c } from './create.js'
8-
export { replace as r } from './replace.js'
9-
export { list as t } from './list.js'
10-
export { update as u } from './update.js'
14+
export * from './extract.js'
1115
export { extract as x } from './extract.js'
12-
16+
export * from './header.js'
17+
export * from './list.js'
18+
export { list as t } from './list.js'
1319
// classes
1420
export * from './pack.js'
15-
export * from './unpack.js'
1621
export * from './parse.js'
17-
export * from './read-entry.js'
18-
export * from './write-entry.js'
19-
export * from './header.js'
2022
export * from './pax.js'
23+
export * from './read-entry.js'
24+
export * from './replace.js'
25+
export { replace as r } from './replace.js'
2126
export * as types from './types.js'
27+
export * from './unpack.js'
28+
export * from './update.js'
29+
export { update as u } from './update.js'
30+
export * from './write-entry.js'

0 commit comments

Comments
 (0)