endef 2.0.1 → 2.0.3

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `endef` 是一个纯 Node.js 的文件旁路转换工具。
4
4
 
5
- 它适合这样的场景:某些常见源码或文本后缀的文件在系统读取、复制、粘贴、编辑器打开时出现乱码或异常,但通过 Node.js 脚本读取仍然正常。`endef` 会先把目标文件内容写到同目录的 `.endef` 副本里,再删除原文件,并把 `.endef` 副本重命名回原文件名。
5
+ 它适合这样的场景:某些常见源码或文本后缀的文件在系统读取、复制、粘贴、编辑器打开时出现乱码或异常,但通过 Node.js 脚本读取仍然正常。`endef` 会先把目标文件内容写到同目录的 `.endef` 副本里,再通过临时 `.tar.gz` 恢复包解压回原文件名。
6
6
 
7
7
  示例:
8
8
 
@@ -19,8 +19,7 @@ README.md -> README.md.endef -> README.md
19
19
  - 纯 Node.js 实现,不依赖 Bash、find、xargs、grep 等 shell 工具
20
20
  - 默认旁路后缀为 `.endef`
21
21
  - `en` 默认转换所有匹配后缀的文件
22
- - `en --marker` 可以只转换命中 marker 的文件,marker 支持数组配置,也支持 CLI 手动指定多个
23
- - `de` 会删除原文件,并把 `.endef` 临时副本重命名回原文件名
22
+ - `de` 会把 `.endef` 临时副本做成恢复包,解压回原文件名,再删除 `.endef`
24
23
  - `pack` 会把恢复后的目录打包成 `.tar.gz`
25
24
  - 不引入生产依赖
26
25
 
@@ -42,31 +41,7 @@ npm install -g endef
42
41
  endef en /path/to/project
43
42
  ```
44
43
 
45
- 只转换命中默认 marker 的文件:
46
-
47
- ```bash
48
- endef en /path/to/project --marker
49
- ```
50
-
51
- 手动指定 marker:
52
-
53
- ```bash
54
- endef en /path/to/project --marker E-SafeNet
55
- ```
56
-
57
- 指定多个 marker:
58
-
59
- ```bash
60
- endef en /path/to/project --marker E-SafeNet --marker BadText
61
- ```
62
-
63
- 也可以用英文逗号分隔:
64
-
65
- ```bash
66
- endef en /path/to/project --marker E-SafeNet,BadText
67
- ```
68
-
69
- 删除原文件,并把 `.endef` 副本重命名回原文件名:
44
+ 通过临时恢复包解压回原文件名,并删除 `.endef`:
70
45
 
71
46
  ```bash
72
47
  endef de /path/to/project
@@ -107,11 +82,11 @@ endef all [directory] [options]
107
82
 
108
83
  `en` 会扫描目标目录,把匹配后缀的文件读取出来,并写成同目录的 `.endef` 副本。
109
84
 
110
- `de` 会扫描目标目录中的 `.endef` 文件,删除对应的原文件,然后把 `.endef` 副本重命名回原文件名。这个命令会直接修改文件,请在执行前确认目录和备份策略。
85
+ `de` 会扫描目标目录中的 `.endef` 文件,生成一个临时恢复包,把 `.endef` 内容映射回原文件名并解压,然后删除 `.endef` 副本。这个命令会直接修改文件,请在执行前确认目录和备份策略。
111
86
 
112
87
  `pack` 会按当前配置扫描当前目录,把没有被 `excludeDirs` 排除的文件打包成 `.tar.gz`。如果输出文件已存在,会直接覆盖。
113
88
 
114
- `all` 会按顺序执行 `en`、`de`、`pack`,适合确认配置后一次性完成旁路转换、重命名恢复和打包。
89
+ `all` 会先执行 `en`,然后生成一次最终恢复包;这个包既会被解压回目录,也会作为最终 `.tar.gz` 保留下来,避免 `de` 和 `pack` 重复打包。
115
90
 
116
91
  ## 参数
117
92
 
@@ -121,7 +96,6 @@ endef all [directory] [options]
121
96
  --ext <list> 目标文件后缀,多个值用英文逗号分隔
122
97
  --exclude <list> 排除目录名,多个值用英文逗号分隔
123
98
  --concurrency <n> 并发文件操作数量
124
- --marker [text] 只为命中任意 marker 的文件生成副本
125
99
  --out <file> 压缩包输出名称
126
100
  ```
127
101
 
@@ -129,10 +103,9 @@ endef all [directory] [options]
129
103
 
130
104
  ```bash
131
105
  endef en . --ext .js,.ts,.vue,.md --exclude node_modules,.git,dist
132
- endef en . --marker E-SafeNet --marker BadText
133
106
  endef de .
134
107
  endef pack recovered
135
- endef all . --marker --out recovered
108
+ endef all . --out recovered
136
109
  ```
137
110
 
138
111
  ## 配置文件
@@ -150,7 +123,6 @@ endef all . --marker --out recovered
150
123
  ```json
151
124
  {
152
125
  "suffix": ".endef",
153
- "markers": ["E-SafeNet"],
154
126
  "extensions": [
155
127
  ".js",
156
128
  ".jsx",
@@ -186,34 +158,6 @@ endef all . --marker --out recovered
186
158
  }
187
159
  ```
188
160
 
189
- ## marker 模式
190
-
191
- 默认情况下:
192
-
193
- ```bash
194
- endef en .
195
- ```
196
-
197
- 会为所有匹配后缀的文件生成 `.endef` 副本。
198
-
199
- 启用 marker 模式后:
200
-
201
- ```bash
202
- endef en . --marker
203
- ```
204
-
205
- 只会为命中任意 marker 的文件生成 `.endef` 副本。`--marker` 不带值时使用配置中的 `markers`,默认是:
206
-
207
- ```json
208
- ["E-SafeNet"]
209
- ```
210
-
211
- 如果 CLI 指定了 marker,则使用 CLI 的值:
212
-
213
- ```bash
214
- endef en . --marker E-SafeNet --marker BadText
215
- ```
216
-
217
161
  ## 恢复策略
218
162
 
219
163
  恢复时,`endef` 会把:
@@ -222,13 +166,13 @@ endef en . --marker E-SafeNet --marker BadText
222
166
  file.ext.endef
223
167
  ```
224
168
 
225
- 删除原文件后重命名为:
169
+ 通过临时恢复包解压为:
226
170
 
227
171
  ```text
228
172
  file.ext
229
173
  ```
230
174
 
231
- 如果 `file.ext` 已经存在,会先被删除,然后由 `.endef` 副本重命名得到新的 `file.ext`。这个行为符合工具的核心假设:原文件可能已经被异常内容污染,而 `.endef` 是通过旁路读取保存出来的可恢复内容。
175
+ `de` 会先生成临时 `.tar.gz` 恢复包,包内路径使用原文件名;随后解压这个恢复包覆盖原文件,解压成功后删除 `file.ext.endef` 和临时恢复包。这个行为符合工具的核心假设:原文件可能已经被异常内容污染,而 `.endef` 是通过旁路读取保存出来的可恢复内容。
232
176
 
233
177
  ## 打包策略
234
178
 
@@ -286,10 +230,10 @@ endef de .
286
230
  endef pack
287
231
  ```
288
232
 
289
- 同样支持 marker、排除目录和输出名称:
233
+ 同样支持排除目录和输出名称:
290
234
 
291
235
  ```bash
292
- endef all . --marker E-SafeNet,BadText --exclude node_modules,.git,dist --out recovered
236
+ endef all . --exclude node_modules,.git,dist --out recovered
293
237
  ```
294
238
 
295
239
  ## 安全建议
@@ -297,7 +241,6 @@ endef all . --marker E-SafeNet,BadText --exclude node_modules,.git,dist --out re
297
241
  - 操作前尽量手动备份目录或磁盘
298
242
  - 优先在干净系统、离线环境或挂载出来的磁盘上运行
299
243
  - 先在小目录中测试 `endef en` 和 `endef de`
300
- - 不确定 marker 是否完整时,优先使用默认的全量 `endef en`
301
244
  - 不要直接运行恢复出来的未知脚本或可执行文件
302
245
  - 恢复后建议做杀毒、哈希校验和人工抽查
303
246
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "endef",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Recover text and source files through safe .endef sidecar copies.",
5
5
  "bin": {
6
6
  "endef": "bin/main.js"
package/src/config.js CHANGED
@@ -4,7 +4,6 @@ const path = require('path')
4
4
 
5
5
  const defaultConfig = {
6
6
  suffix: '.endef',
7
- markers: ['E-SafeNet'],
8
7
  extensions: [
9
8
  '.js',
10
9
  '.jsx',
@@ -73,35 +72,13 @@ function stripBom(value) {
73
72
  }
74
73
 
75
74
  function mergeConfig(base, next) {
76
- const markers = normalizeMarkers(next.markers)
77
-
78
75
  return {
79
76
  ...base,
80
77
  ...next,
81
78
  extensions: Array.isArray(next.extensions) ? next.extensions : base.extensions,
82
79
  excludeDirs: Array.isArray(next.excludeDirs) ? next.excludeDirs : base.excludeDirs,
83
- excludeFiles: Array.isArray(next.excludeFiles) ? next.excludeFiles : base.excludeFiles,
84
- markers: markers.length > 0 ? markers : base.markers
80
+ excludeFiles: Array.isArray(next.excludeFiles) ? next.excludeFiles : base.excludeFiles
85
81
  }
86
82
  }
87
83
 
88
- function normalizeMarkers(value) {
89
- if (Array.isArray(value)) {
90
- return value.map(item => String(item).trim()).filter(Boolean)
91
- }
92
-
93
- return []
94
- }
95
-
96
- function splitMarkers(value) {
97
- if (!value) {
98
- return []
99
- }
100
-
101
- return String(value)
102
- .split(',')
103
- .map(item => item.trim())
104
- .filter(Boolean)
105
- }
106
-
107
- module.exports = { defaultConfig, loadConfig, normalizeMarkers, splitMarkers }
84
+ module.exports = { defaultConfig, loadConfig }
package/src/de.js CHANGED
@@ -1,69 +1,47 @@
1
1
  const fs = require('fs/promises')
2
2
  const path = require('path')
3
3
  const { defaultConfig } = require('./config')
4
- const { createLimiter, walk } = require('./utils')
4
+ const {
5
+ collectRecoveryEntries,
6
+ createPackConfig,
7
+ extractTarGz,
8
+ removeSidecars,
9
+ writeTarGz
10
+ } = require('./pack')
5
11
 
6
12
  async function restoreFiles(directory, options = {}) {
7
- const config = {
13
+ const config = createPackConfig(directory, {
8
14
  ...defaultConfig,
9
- ...options,
10
- directory: path.resolve(directory || process.cwd())
11
- }
12
- const stats = createStats()
13
- const limit = createLimiter(config.concurrency)
14
- const tasks = []
15
-
16
- await walk(config.directory, config, async filePath => {
17
- if (!filePath.endsWith(config.suffix)) {
18
- return
19
- }
20
-
21
- tasks.push(
22
- limit(async () => {
23
- await restoreOne(filePath, config, stats)
24
- }).catch(error => {
25
- stats.failed += 1
26
- console.error(`Failed: ${filePath}`)
27
- console.error(error && error.message ? error.message : error)
28
- })
29
- )
15
+ ...options
16
+ })
17
+ const archivePath = path.join(config.directory, `.endef-restore-${Date.now()}.tar.gz`)
18
+ const entries = await collectRecoveryEntries(config, {
19
+ onlySidecars: true,
20
+ outputPath: archivePath
30
21
  })
31
22
 
32
- await Promise.all(tasks)
33
- console.log(`Done. restored=${stats.restored}, removed=${stats.removed}, renamed=${stats.renamed}, failed=${stats.failed}`)
34
- return stats
35
- }
36
-
37
- async function restoreOne(filePath, config, stats) {
38
- const targetPath = filePath.slice(0, -config.suffix.length)
39
- const targetExists = await exists(targetPath)
40
-
41
- await fs.rm(targetPath, { force: true })
42
- await fs.rename(filePath, targetPath)
43
-
44
- stats.restored += 1
45
- if (targetExists) {
46
- stats.removed += 1
23
+ if (entries.length === 0) {
24
+ console.log('Done. restored=0, deleted=0, failed=0')
25
+ return {
26
+ restored: 0,
27
+ deleted: 0,
28
+ failed: 0
29
+ }
47
30
  }
48
- stats.renamed += 1
49
- console.log(`Restored: ${filePath} -> ${targetPath}`)
50
- }
51
31
 
52
- async function exists(filePath) {
53
32
  try {
54
- await fs.access(filePath)
55
- return true
56
- } catch (error) {
57
- return false
58
- }
59
- }
60
-
61
- function createStats() {
62
- return {
63
- restored: 0,
64
- removed: 0,
65
- renamed: 0,
66
- failed: 0
33
+ await writeTarGz(config.directory, archivePath, entries)
34
+ const restored = await extractTarGz(archivePath, config.directory)
35
+ const deleted = await removeSidecars(config.directory, config)
36
+
37
+ console.log(`Done. restored=${restored}, deleted=${deleted}, failed=0`)
38
+ return {
39
+ restored,
40
+ deleted,
41
+ failed: 0
42
+ }
43
+ } finally {
44
+ await fs.rm(archivePath, { force: true })
67
45
  }
68
46
  }
69
47
 
package/src/en.js CHANGED
@@ -27,14 +27,9 @@ async function processFiles(directory, options = {}) {
27
27
  tasks.push(
28
28
  limit(async () => {
29
29
  const outputPath = `${filePath}${config.suffix}`
30
- const data = await fs.readFile(filePath, 'utf8')
30
+ const data = await fs.readFile(filePath)
31
31
 
32
- if (config.markerFilter && !matchesAnyMarker(data, config.markers)) {
33
- stats.skipped += 1
34
- return
35
- }
36
-
37
- await fs.writeFile(outputPath, data, 'utf8')
32
+ await fs.writeFile(outputPath, data)
38
33
  console.log(`Written: ${outputPath}`)
39
34
  stats.written += 1
40
35
  }).catch(error => {
@@ -58,12 +53,4 @@ function createStats() {
58
53
  }
59
54
  }
60
55
 
61
- function matchesAnyMarker(data, markers) {
62
- if (!Array.isArray(markers) || markers.length === 0) {
63
- return false
64
- }
65
-
66
- return markers.some(marker => data.includes(marker))
67
- }
68
-
69
56
  module.exports = processFiles
package/src/pack.js CHANGED
@@ -2,36 +2,93 @@ const fs = require('fs')
2
2
  const fsp = require('fs/promises')
3
3
  const path = require('path')
4
4
  const zlib = require('zlib')
5
+ const { promisify } = require('util')
5
6
  const { once } = require('events')
6
7
  const { pipeline } = require('stream/promises')
7
8
  const { defaultConfig } = require('./config')
8
9
  const { walk } = require('./utils')
9
10
 
11
+ const gunzip = promisify(zlib.gunzip)
12
+
10
13
  async function packFiles(directory, options = {}) {
11
- const config = {
14
+ const config = createPackConfig(directory, options)
15
+ const outputPath = resolveOutputPath(config.directory, config.out)
16
+ const entries = await collectPackEntries(config, outputPath)
17
+
18
+ await fsp.rm(outputPath, { force: true })
19
+ await writeTarGz(config.directory, outputPath, entries)
20
+ console.log(`Packed: ${outputPath}`)
21
+ console.log(`Done. files=${entries.length}`)
22
+
23
+ return {
24
+ outputPath,
25
+ files: entries.length
26
+ }
27
+ }
28
+
29
+ function createPackConfig(directory, options) {
30
+ return {
12
31
  ...defaultConfig,
13
32
  ...options,
14
33
  directory: path.resolve(directory || process.cwd())
15
34
  }
16
- const outputPath = resolveOutputPath(config.directory, config.out)
17
- const files = []
35
+ }
36
+
37
+ async function collectPackEntries(config, outputPath) {
38
+ const entries = []
18
39
 
19
40
  await walk(config.directory, config, async filePath => {
20
- if (path.resolve(filePath) === outputPath || filePath.endsWith(config.suffix)) {
41
+ if (shouldSkipPackFile(filePath, config, outputPath)) {
21
42
  return
22
43
  }
23
44
 
24
- files.push(filePath)
45
+ entries.push(createEntry(config.directory, filePath, filePath))
25
46
  })
26
47
 
27
- await fsp.rm(outputPath, { force: true })
28
- await writeTarGz(config.directory, outputPath, files)
29
- console.log(`Packed: ${outputPath}`)
30
- console.log(`Done. files=${files.length}`)
48
+ return entries
49
+ }
50
+
51
+ async function collectRecoveryEntries(config, options = {}) {
52
+ const entries = []
53
+ const sidecarTargets = new Set()
54
+
55
+ await walk(config.directory, config, async filePath => {
56
+ if (filePath.endsWith(config.suffix)) {
57
+ sidecarTargets.add(path.resolve(filePath.slice(0, -config.suffix.length)))
58
+ }
59
+ })
60
+
61
+ await walk(config.directory, config, async filePath => {
62
+ const resolvedPath = path.resolve(filePath)
63
+
64
+ if (options.outputPath && resolvedPath === path.resolve(options.outputPath)) {
65
+ return
66
+ }
67
+
68
+ if (filePath.endsWith(config.suffix)) {
69
+ const targetPath = filePath.slice(0, -config.suffix.length)
70
+ entries.push(createEntry(config.directory, filePath, targetPath))
71
+ return
72
+ }
73
+
74
+ if (options.onlySidecars || sidecarTargets.has(resolvedPath)) {
75
+ return
76
+ }
77
+
78
+ entries.push(createEntry(config.directory, filePath, filePath))
79
+ })
80
+
81
+ return entries
82
+ }
83
+
84
+ function shouldSkipPackFile(filePath, config, outputPath) {
85
+ return path.resolve(filePath) === outputPath || filePath.endsWith(config.suffix)
86
+ }
31
87
 
88
+ function createEntry(rootDirectory, sourcePath, archiveTargetPath) {
32
89
  return {
33
- outputPath,
34
- files: files.length
90
+ sourcePath,
91
+ archivePath: toArchivePath(path.relative(rootDirectory, archiveTargetPath))
35
92
  }
36
93
  }
37
94
 
@@ -51,30 +108,77 @@ function normalizeOutputName(out) {
51
108
  return `${out}.tar.gz`
52
109
  }
53
110
 
54
- async function writeTarGz(rootDirectory, outputPath, files) {
111
+ async function writeTarGz(rootDirectory, outputPath, entries) {
55
112
  await fsp.mkdir(path.dirname(outputPath), { recursive: true })
56
113
 
57
114
  const gzip = zlib.createGzip()
58
115
  const output = fs.createWriteStream(outputPath)
59
- const writer = writeTarEntries(rootDirectory, files, gzip)
116
+ const writer = writeTarEntries(entries, gzip)
60
117
 
61
118
  await Promise.all([writer, pipeline(gzip, output)])
62
119
  }
63
120
 
64
- async function writeTarEntries(rootDirectory, files, stream) {
65
- for (const filePath of files) {
66
- const stat = await fsp.stat(filePath)
67
- const archivePath = toArchivePath(path.relative(rootDirectory, filePath))
68
-
69
- await writeToStream(stream, createHeader(archivePath, stat))
121
+ async function writeTarEntries(entries, stream) {
122
+ for (const entry of entries) {
123
+ const stat = await fsp.stat(entry.sourcePath)
70
124
 
71
- await writeFileToStream(filePath, stream)
125
+ await writeToStream(stream, createHeader(entry.archivePath, stat))
126
+ await writeFileToStream(entry.sourcePath, stream)
72
127
  await writeToStream(stream, Buffer.alloc(paddingSize(stat.size)))
73
128
  }
74
129
 
75
130
  stream.end(Buffer.alloc(1024))
76
131
  }
77
132
 
133
+ async function extractTarGz(archivePath, directory) {
134
+ const archive = await fsp.readFile(archivePath)
135
+ const data = await gunzip(archive)
136
+ let offset = 0
137
+ let extracted = 0
138
+
139
+ while (offset + 512 <= data.length) {
140
+ const header = data.subarray(offset, offset + 512)
141
+
142
+ if (header.every(value => value === 0)) {
143
+ break
144
+ }
145
+
146
+ const entry = readHeader(header)
147
+ offset += 512
148
+
149
+ const content = data.subarray(offset, offset + entry.size)
150
+ const outputPath = path.resolve(directory, entry.name)
151
+
152
+ if (!outputPath.startsWith(path.resolve(directory) + path.sep)) {
153
+ throw new Error(`Unsafe archive path: ${entry.name}`)
154
+ }
155
+
156
+ await fsp.mkdir(path.dirname(outputPath), { recursive: true })
157
+ await fsp.rm(outputPath, { force: true })
158
+ await fsp.writeFile(outputPath, content, { flag: 'wx' })
159
+
160
+ offset += entry.size + paddingSize(entry.size)
161
+ extracted += 1
162
+ }
163
+
164
+ return extracted
165
+ }
166
+
167
+ async function removeSidecars(directory, config) {
168
+ let deleted = 0
169
+
170
+ await walk(directory, config, async filePath => {
171
+ if (!filePath.endsWith(config.suffix)) {
172
+ return
173
+ }
174
+
175
+ await fsp.rm(filePath, { force: true })
176
+ deleted += 1
177
+ })
178
+
179
+ return deleted
180
+ }
181
+
78
182
  async function writeFileToStream(filePath, stream) {
79
183
  for await (const chunk of fs.createReadStream(filePath)) {
80
184
  await writeToStream(stream, chunk)
@@ -113,6 +217,20 @@ function createHeader(filePath, stat) {
113
217
  return header
114
218
  }
115
219
 
220
+ function readHeader(header) {
221
+ const name = readString(header, 0, 100)
222
+ const prefix = readString(header, 345, 155)
223
+
224
+ return {
225
+ name: prefix ? `${prefix}/${name}` : name,
226
+ size: parseInt(readString(header, 124, 12).trim() || '0', 8)
227
+ }
228
+ }
229
+
230
+ function readString(buffer, offset, length) {
231
+ return buffer.subarray(offset, offset + length).toString('utf8').replace(/\0.*$/, '')
232
+ }
233
+
116
234
  function splitTarPath(filePath) {
117
235
  if (Buffer.byteLength(filePath) <= 100) {
118
236
  return {
@@ -156,3 +274,9 @@ function paddingSize(size) {
156
274
  }
157
275
 
158
276
  module.exports = packFiles
277
+ module.exports.createPackConfig = createPackConfig
278
+ module.exports.collectRecoveryEntries = collectRecoveryEntries
279
+ module.exports.extractTarGz = extractTarGz
280
+ module.exports.removeSidecars = removeSidecars
281
+ module.exports.resolveOutputPath = resolveOutputPath
282
+ module.exports.writeTarGz = writeTarGz
package/src/run.js CHANGED
@@ -1,7 +1,15 @@
1
1
  const enF = require('./en')
2
2
  const deF = require('./de')
3
3
  const packF = require('./pack')
4
- const { loadConfig, splitMarkers } = require('./config')
4
+ const { loadConfig } = require('./config')
5
+ const {
6
+ collectRecoveryEntries,
7
+ createPackConfig,
8
+ extractTarGz,
9
+ removeSidecars,
10
+ resolveOutputPath,
11
+ writeTarGz
12
+ } = require('./pack')
5
13
 
6
14
  function printHelp() {
7
15
  console.log(`Usage:
@@ -16,7 +24,6 @@ Options:
16
24
  --ext <list> Comma separated target extensions
17
25
  --exclude <list> Comma separated excluded directory names
18
26
  --concurrency <n> Concurrent file operation limit
19
- --marker [text] Only create sidecar files when any marker is found
20
27
  --out <file> Archive output name
21
28
  `)
22
29
  }
@@ -24,8 +31,6 @@ Options:
24
31
  function parseArgs(command, args) {
25
32
  const result = {
26
33
  directory: process.cwd(),
27
- markerFilter: undefined,
28
- markers: [],
29
34
  positionals: []
30
35
  }
31
36
 
@@ -98,23 +103,6 @@ function parseArgs(command, args) {
98
103
  continue
99
104
  }
100
105
 
101
- if (arg.startsWith('--marker=')) {
102
- result.markerFilter = true
103
- result.markers.push(...splitMarkers(arg.slice('--marker='.length)))
104
- continue
105
- }
106
-
107
- if (arg === '--marker') {
108
- result.markerFilter = true
109
-
110
- if (args[index + 1] && !args[index + 1].startsWith('-')) {
111
- result.markers.push(...splitMarkers(args[index + 1]))
112
- index += 1
113
- }
114
-
115
- continue
116
- }
117
-
118
106
  if (!arg.startsWith('-')) {
119
107
  result.positionals.push(arg)
120
108
  }
@@ -160,8 +148,7 @@ async function run(args) {
160
148
  const options = {
161
149
  ...config,
162
150
  ...cliOptions,
163
- directory: cliOptions.directory || config.directory || process.cwd(),
164
- markers: cliOptions.markers && cliOptions.markers.length > 0 ? cliOptions.markers : config.markers
151
+ directory: cliOptions.directory || config.directory || process.cwd()
165
152
  }
166
153
 
167
154
  if (command === 'en') {
@@ -181,14 +168,28 @@ async function run(args) {
181
168
 
182
169
  if (command === 'all') {
183
170
  await enF(options.directory, options)
184
- await deF(options.directory, options)
185
- await packF(options.directory, options)
171
+ await runAll(options.directory, options)
186
172
  return
187
173
  }
188
174
 
189
175
  printHelp()
190
176
  }
191
177
 
178
+ async function runAll(directory, options) {
179
+ const config = createPackConfig(directory, options)
180
+ const outputPath = resolveOutputPath(config.directory, config.out)
181
+ const entries = await collectRecoveryEntries(config, {
182
+ outputPath
183
+ })
184
+
185
+ await writeTarGz(config.directory, outputPath, entries)
186
+ const restored = await extractTarGz(outputPath, config.directory)
187
+ const deleted = await removeSidecars(config.directory, config)
188
+
189
+ console.log(`Packed: ${outputPath}`)
190
+ console.log(`Done. files=${entries.length}, restored=${restored}, deleted=${deleted}`)
191
+ }
192
+
192
193
  function omitUndefined(value) {
193
194
  return Object.keys(value).reduce((result, key) => {
194
195
  if (value[key] !== undefined) {