1'use strict'
2
3// tar -c
4const hlo = require('./high-level-opt.js')
5
6const Pack = require('./pack.js')
7const fsm = require('fs-minipass')
8const t = require('./list.js')
9const path = require('path')
10
11module.exports = (opt_, files, cb) => {
12  if (typeof files === 'function') {
13    cb = files
14  }
15
16  if (Array.isArray(opt_)) {
17    files = opt_, opt_ = {}
18  }
19
20  if (!files || !Array.isArray(files) || !files.length) {
21    throw new TypeError('no files or directories specified')
22  }
23
24  files = Array.from(files)
25
26  const opt = hlo(opt_)
27
28  if (opt.sync && typeof cb === 'function') {
29    throw new TypeError('callback not supported for sync tar functions')
30  }
31
32  if (!opt.file && typeof cb === 'function') {
33    throw new TypeError('callback only supported with file option')
34  }
35
36  return opt.file && opt.sync ? createFileSync(opt, files)
37    : opt.file ? createFile(opt, files, cb)
38    : opt.sync ? createSync(opt, files)
39    : create(opt, files)
40}
41
42const createFileSync = (opt, files) => {
43  const p = new Pack.Sync(opt)
44  const stream = new fsm.WriteStreamSync(opt.file, {
45    mode: opt.mode || 0o666,
46  })
47  p.pipe(stream)
48  addFilesSync(p, files)
49}
50
51const createFile = (opt, files, cb) => {
52  const p = new Pack(opt)
53  const stream = new fsm.WriteStream(opt.file, {
54    mode: opt.mode || 0o666,
55  })
56  p.pipe(stream)
57
58  const promise = new Promise((res, rej) => {
59    stream.on('error', rej)
60    stream.on('close', res)
61    p.on('error', rej)
62  })
63
64  addFilesAsync(p, files)
65
66  return cb ? promise.then(cb, cb) : promise
67}
68
69const addFilesSync = (p, files) => {
70  files.forEach(file => {
71    if (file.charAt(0) === '@') {
72      t({
73        file: path.resolve(p.cwd, file.slice(1)),
74        sync: true,
75        noResume: true,
76        onentry: entry => p.add(entry),
77      })
78    } else {
79      p.add(file)
80    }
81  })
82  p.end()
83}
84
85const addFilesAsync = (p, files) => {
86  while (files.length) {
87    const file = files.shift()
88    if (file.charAt(0) === '@') {
89      return t({
90        file: path.resolve(p.cwd, file.slice(1)),
91        noResume: true,
92        onentry: entry => p.add(entry),
93      }).then(_ => addFilesAsync(p, files))
94    } else {
95      p.add(file)
96    }
97  }
98  p.end()
99}
100
101const createSync = (opt, files) => {
102  const p = new Pack.Sync(opt)
103  addFilesSync(p, files)
104  return p
105}
106
107const create = (opt, files) => {
108  const p = new Pack(opt)
109  addFilesAsync(p, files)
110  return p
111}
112