endef 2.0.1 → 2.0.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 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` 副本里,再删除原文件,并用 `.endef` 副本内容新建原文件。
6
6
 
7
7
  示例:
8
8
 
@@ -20,7 +20,7 @@ README.md -> README.md.endef -> README.md
20
20
  - 默认旁路后缀为 `.endef`
21
21
  - `en` 默认转换所有匹配后缀的文件
22
22
  - `en --marker` 可以只转换命中 marker 的文件,marker 支持数组配置,也支持 CLI 手动指定多个
23
- - `de` 会删除原文件,并把 `.endef` 临时副本重命名回原文件名
23
+ - `de` 会删除原文件,用 `.endef` 临时副本内容新建原文件,再删除 `.endef`
24
24
  - `pack` 会把恢复后的目录打包成 `.tar.gz`
25
25
  - 不引入生产依赖
26
26
 
@@ -66,7 +66,7 @@ endef en /path/to/project --marker E-SafeNet --marker BadText
66
66
  endef en /path/to/project --marker E-SafeNet,BadText
67
67
  ```
68
68
 
69
- 删除原文件,并把 `.endef` 副本重命名回原文件名:
69
+ 删除原文件,用 `.endef` 副本内容新建原文件,再删除 `.endef`:
70
70
 
71
71
  ```bash
72
72
  endef de /path/to/project
@@ -107,11 +107,11 @@ endef all [directory] [options]
107
107
 
108
108
  `en` 会扫描目标目录,把匹配后缀的文件读取出来,并写成同目录的 `.endef` 副本。
109
109
 
110
- `de` 会扫描目标目录中的 `.endef` 文件,删除对应的原文件,然后把 `.endef` 副本重命名回原文件名。这个命令会直接修改文件,请在执行前确认目录和备份策略。
110
+ `de` 会扫描目标目录中的 `.endef` 文件,删除对应的原文件,用 `.endef` 副本内容新建原文件,然后删除 `.endef` 副本。这个命令会直接修改文件,请在执行前确认目录和备份策略。
111
111
 
112
112
  `pack` 会按当前配置扫描当前目录,把没有被 `excludeDirs` 排除的文件打包成 `.tar.gz`。如果输出文件已存在,会直接覆盖。
113
113
 
114
- `all` 会按顺序执行 `en`、`de`、`pack`,适合确认配置后一次性完成旁路转换、重命名恢复和打包。
114
+ `all` 会按顺序执行 `en`、`de`、`pack`,适合确认配置后一次性完成旁路转换、新建恢复和打包。
115
115
 
116
116
  ## 参数
117
117
 
@@ -222,13 +222,13 @@ endef en . --marker E-SafeNet --marker BadText
222
222
  file.ext.endef
223
223
  ```
224
224
 
225
- 删除原文件后重命名为:
225
+ 删除原文件后,用 `.endef` 内容新建:
226
226
 
227
227
  ```text
228
228
  file.ext
229
229
  ```
230
230
 
231
- 如果 `file.ext` 已经存在,会先被删除,然后由 `.endef` 副本重命名得到新的 `file.ext`。这个行为符合工具的核心假设:原文件可能已经被异常内容污染,而 `.endef` 是通过旁路读取保存出来的可恢复内容。
231
+ 如果 `file.ext` 已经存在,会先被删除,然后用 `.endef` 副本内容创建新的 `file.ext`。创建成功后,`file.ext.endef` 会被删除。这个行为符合工具的核心假设:原文件可能已经被异常内容污染,而 `.endef` 是通过旁路读取保存出来的可恢复内容。
232
232
 
233
233
  ## 打包策略
234
234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "endef",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Recover text and source files through safe .endef sidecar copies.",
5
5
  "bin": {
6
6
  "endef": "bin/main.js"
package/src/de.js CHANGED
@@ -30,22 +30,28 @@ async function restoreFiles(directory, options = {}) {
30
30
  })
31
31
 
32
32
  await Promise.all(tasks)
33
- console.log(`Done. restored=${stats.restored}, removed=${stats.removed}, renamed=${stats.renamed}, failed=${stats.failed}`)
33
+ console.log(
34
+ `Done. restored=${stats.restored}, removed=${stats.removed}, created=${stats.created}, deleted=${stats.deleted}, failed=${stats.failed}`
35
+ )
34
36
  return stats
35
37
  }
36
38
 
37
39
  async function restoreOne(filePath, config, stats) {
38
40
  const targetPath = filePath.slice(0, -config.suffix.length)
39
41
  const targetExists = await exists(targetPath)
42
+ const data = await fs.readFile(filePath)
40
43
 
41
44
  await fs.rm(targetPath, { force: true })
42
- await fs.rename(filePath, targetPath)
45
+ await waitForMissing(targetPath)
46
+ await fs.writeFile(targetPath, data, { flag: 'wx' })
47
+ await fs.rm(filePath, { force: true })
43
48
 
44
49
  stats.restored += 1
45
50
  if (targetExists) {
46
51
  stats.removed += 1
47
52
  }
48
- stats.renamed += 1
53
+ stats.created += 1
54
+ stats.deleted += 1
49
55
  console.log(`Restored: ${filePath} -> ${targetPath}`)
50
56
  }
51
57
 
@@ -58,11 +64,30 @@ async function exists(filePath) {
58
64
  }
59
65
  }
60
66
 
67
+ async function waitForMissing(filePath) {
68
+ for (let index = 0; index < 20; index += 1) {
69
+ if (!(await exists(filePath))) {
70
+ return
71
+ }
72
+
73
+ await delay(25)
74
+ }
75
+
76
+ throw new Error(`Target still exists after remove: ${filePath}`)
77
+ }
78
+
79
+ function delay(ms) {
80
+ return new Promise(resolve => {
81
+ setTimeout(resolve, ms)
82
+ })
83
+ }
84
+
61
85
  function createStats() {
62
86
  return {
63
87
  restored: 0,
64
88
  removed: 0,
65
- renamed: 0,
89
+ created: 0,
90
+ deleted: 0,
66
91
  failed: 0
67
92
  }
68
93
  }
package/src/en.js CHANGED
@@ -27,14 +27,14 @@ 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
32
  if (config.markerFilter && !matchesAnyMarker(data, config.markers)) {
33
33
  stats.skipped += 1
34
34
  return
35
35
  }
36
36
 
37
- await fs.writeFile(outputPath, data, 'utf8')
37
+ await fs.writeFile(outputPath, data)
38
38
  console.log(`Written: ${outputPath}`)
39
39
  stats.written += 1
40
40
  }).catch(error => {