@vanillaes/esmtk 0.14.0 → 0.15.1

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.
@@ -106,7 +106,55 @@
106
106
  ],
107
107
  "program": "${workspaceFolder}/bin/esmtk.js",
108
108
  "cwd": "${workspaceFolder}",
109
- "args": ["cp", "./test/cp/test1.txt", "./test/cp2"],
109
+ "args": ["cp", "./test/cp/test1.txt", "./test/cp2/test1.txt"],
110
+ "console": "integratedTerminal",
111
+ },
112
+ {
113
+ "name": "CopyGlobToFolder",
114
+ "type": "node",
115
+ "request": "launch",
116
+ "skipFiles": [
117
+ "<node_internals>/**"
118
+ ],
119
+ "program": "${workspaceFolder}/bin/esmtk.js",
120
+ "cwd": "${workspaceFolder}",
121
+ "args": ["cp", "./test/cp/*.js", "./test/cp2/"],
122
+ "console": "integratedTerminal",
123
+ },
124
+ {
125
+ "name": "CopyFileToFile - Error source doesn't exist",
126
+ "type": "node",
127
+ "request": "launch",
128
+ "skipFiles": [
129
+ "<node_internals>/**"
130
+ ],
131
+ "program": "${workspaceFolder}/bin/esmtk.js",
132
+ "cwd": "${workspaceFolder}",
133
+ "args": ["cp", "./test/cp/testx.txt", "./test/cp2/"],
134
+ "console": "integratedTerminal",
135
+ },
136
+ {
137
+ "name": "CopyFileToFile - Error source is a directory",
138
+ "type": "node",
139
+ "request": "launch",
140
+ "skipFiles": [
141
+ "<node_internals>/**"
142
+ ],
143
+ "program": "${workspaceFolder}/bin/esmtk.js",
144
+ "cwd": "${workspaceFolder}",
145
+ "args": ["cp", "./test/cp/", "./test/cp2/test1.txt"],
146
+ "console": "integratedTerminal",
147
+ },
148
+ {
149
+ "name": "CopyFileToFile - Error target directory doesn't exist",
150
+ "type": "node",
151
+ "request": "launch",
152
+ "skipFiles": [
153
+ "<node_internals>/**"
154
+ ],
155
+ "program": "${workspaceFolder}/bin/esmtk.js",
156
+ "cwd": "${workspaceFolder}",
157
+ "args": ["cp", "./test/cp/test1.txt", "./test/cpx/"],
110
158
  "console": "integratedTerminal",
111
159
  },
112
160
  {
@@ -121,6 +169,66 @@
121
169
  "args": ["cp", "-r", "./test/cp/", "./test/cp2"],
122
170
  "console": "integratedTerminal",
123
171
  },
172
+ {
173
+ "name": "CopyMultipleFiles",
174
+ "type": "node",
175
+ "request": "launch",
176
+ "skipFiles": [
177
+ "<node_internals>/**"
178
+ ],
179
+ "program": "${workspaceFolder}/bin/esmtk.js",
180
+ "cwd": "${workspaceFolder}",
181
+ "args": ["cp", "./test/cp/test1.txt", "./test/cp/test1.js", "./test/cp/test2.txt", "./test/cp/test2.js", "./test/cp2/"],
182
+ "console": "integratedTerminal",
183
+ },
184
+ {
185
+ "name": "CopyMultipleFiles - Error file doesn't exist",
186
+ "type": "node",
187
+ "request": "launch",
188
+ "skipFiles": [
189
+ "<node_internals>/**"
190
+ ],
191
+ "program": "${workspaceFolder}/bin/esmtk.js",
192
+ "cwd": "${workspaceFolder}",
193
+ "args": ["cp", "./test/cp/test1.ts", "./test/cp/test1.js", "./test/cp/test2.txt", "./test/cp/test2.js", "./test/cp2/"],
194
+ "console": "integratedTerminal",
195
+ },
196
+ {
197
+ "name": "CopyMultipleGlobs",
198
+ "type": "node",
199
+ "request": "launch",
200
+ "skipFiles": [
201
+ "<node_internals>/**"
202
+ ],
203
+ "program": "${workspaceFolder}/bin/esmtk.js",
204
+ "cwd": "${workspaceFolder}",
205
+ "args": ["cp", "./test/cp/*.txt", "./test/cp/*.js", "./test/cp2/"],
206
+ "console": "integratedTerminal",
207
+ },
208
+ {
209
+ "name": "CopyMultipleGlobs - Error glob not found",
210
+ "type": "node",
211
+ "request": "launch",
212
+ "skipFiles": [
213
+ "<node_internals>/**"
214
+ ],
215
+ "program": "${workspaceFolder}/bin/esmtk.js",
216
+ "cwd": "${workspaceFolder}",
217
+ "args": ["cp", "./test/cp/*.ts", "./test/cp/*.js", "./test/cp2/"],
218
+ "console": "integratedTerminal",
219
+ },
220
+ {
221
+ "name": "CopyMultipleMixed",
222
+ "type": "node",
223
+ "request": "launch",
224
+ "skipFiles": [
225
+ "<node_internals>/**"
226
+ ],
227
+ "program": "${workspaceFolder}/bin/esmtk.js",
228
+ "cwd": "${workspaceFolder}",
229
+ "args": ["cp", "./test/cp/test1.txt", "./test/cp/test2.txt", "./test/cp/*.js", "./test/cp2/"],
230
+ "console": "integratedTerminal",
231
+ },
124
232
  {
125
233
  "name": "RemoveFile",
126
234
  "type": "node",
@@ -1,16 +1,41 @@
1
- import { copyAsync, copyRecursiveAsync } from '../../src/cp.js'
1
+ import { copyAsync, copyMultipleAsync, copyRecursiveAsync } from '../../src/cp.js'
2
+ import { expandSource } from '../../src/index.js'
2
3
 
3
4
  /**
4
5
  * POSIX cp Implemented in Node
5
- * @param {string} path path of file(s)/directorie(s) to copy
6
- * @param {any} options cp options
6
+ *
7
+ * @param {string[]} paths Variadic of source/destination paths
8
+ * @param {any} options 'cp' options
7
9
  */
8
- export async function cp (source, target, options) {
9
- if (!options?.recursive) {
10
- await copyAsync(source, target, options?.force)
10
+ export async function cp (paths, options) {
11
+ if (paths.length < 2) {
12
+ console.error('cp: Not enough arguments')
11
13
  }
12
14
 
13
- if (options?.recursive) {
14
- await copyRecursiveAsync(source, target, options?.force)
15
+ if (paths.length === 2) {
16
+ const source = paths[0]
17
+ const target = paths[1]
18
+
19
+ if (!options?.recursive && !source.includes('*')) {
20
+ await copyAsync(source, target, options?.force)
21
+ }
22
+
23
+ if (options?.recursive) {
24
+ await copyRecursiveAsync(source, target, options?.force)
25
+ }
26
+
27
+ if (source.includes('*')) {
28
+ const sources = await expandSource(source)
29
+ await copyMultipleAsync(sources, target)
30
+ }
31
+ }
32
+
33
+ if (paths.length >= 2) {
34
+ let sources = paths.slice(0, -1)
35
+ sources = await await Promise.all(sources.map(source => expandSource(source)))
36
+ sources = sources.flat()
37
+ const target = paths.slice(-1)[0]
38
+
39
+ await copyMultipleAsync(sources, target, options?.force)
15
40
  }
16
41
  }
package/bin/esmtk.js CHANGED
@@ -51,13 +51,20 @@ program.command('minify <input> <output>')
51
51
  minify(input, output, options)
52
52
  })
53
53
 
54
- program.command('cp <source> <target>')
55
- .usage('[-rf] source target')
56
- .description('Copy files from the source to the target')
57
- .option('-f, --force', 'Do not prompt before overwriting', false)
54
+ program.command('cp')
55
+ .argument('[paths...]')
56
+ .usage(`[-r] source target
57
+
58
+ Examples:
59
+ $ cp SOURCE DEST
60
+ $ cp SOURCE... DIRECTORY
61
+ $ cp SOURCEGLOB... DIRECTORY
62
+ $ cp -r SOURCEDIR DIRECTORY
63
+ `)
64
+ .description('Copy files and directories')
58
65
  .option('-r, --recursive', 'Copy directories recursively', false)
59
- .action((source, target, options) => {
60
- cp(source, target, options)
66
+ .action((paths, options) => {
67
+ cp(paths, options)
61
68
  })
62
69
 
63
70
  program.command('rm <path>')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vanillaes/esmtk",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "ES Module Toolkit",
5
5
  "keywords": [
6
6
  "esm",
@@ -37,6 +37,9 @@
37
37
  "preversion": "npm run lint && npm run types",
38
38
  "postversion": "git push --follow-tags"
39
39
  },
40
+ "engines": {
41
+ "node": ">=22"
42
+ },
40
43
  "dependencies": {
41
44
  "commander": "^14.0.3",
42
45
  "standard": "^17.1.2"
package/src/cp.d.ts CHANGED
@@ -1,12 +1,22 @@
1
1
  /**
2
2
  * Copy a single file asynchronously
3
+ *
3
4
  * @param {string} source The source file
4
5
  * @param {string} target The target file
5
6
  * @param {boolean} force If the file already exists, overwrite it (default false)
6
7
  */
7
8
  export function copyAsync(source: string, target: string, force?: boolean): Promise<void>;
9
+ /**
10
+ * Copy a multiple files/globs asynchronously
11
+ *
12
+ * @param {string[]} sources The source files/globs
13
+ * @param {string} target The target file
14
+ * @param {boolean} force If the file already exists, overwrite it (default false)
15
+ */
16
+ export function copyMultipleAsync(sources: string[], target: string, force?: boolean): Promise<void>;
8
17
  /**
9
18
  * Recursively copy a directory asynchronously
19
+ *
10
20
  * @param {string} source The source directory
11
21
  * @param {string} target The target directory
12
22
  * @param {boolean} force If the file already exists, overwrite it (default false)
package/src/cp.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { fileExists } from './index.js'
2
- import { basename, dirname } from 'node:path'
2
+ import { basename, sep } from 'node:path'
3
3
  import { cp, stat } from 'node:fs/promises'
4
4
 
5
5
  /**
6
6
  * Copy a single file asynchronously
7
+ *
7
8
  * @param {string} source The source file
8
9
  * @param {string} target The target file
9
10
  * @param {boolean} force If the file already exists, overwrite it (default false)
@@ -11,45 +12,78 @@ import { cp, stat } from 'node:fs/promises'
11
12
  export async function copyAsync (source, target, force = false) {
12
13
  const sExists = await fileExists(source)
13
14
  if (!sExists) {
14
- console.error(`cp: source ${source} does not exist`)
15
+ console.error(`cp: ${source} No such file or directory`)
15
16
  process.exit(1)
16
17
  }
17
18
  const sStats = await stat(source)
18
19
  if (sStats.isSymbolicLink()) {
19
- console.error(`cp: source ${source} is a sybolic link`)
20
+ console.error(`cp: ${source} is a sybolic link (not copied)`)
20
21
  process.exit(1)
21
22
  }
22
-
23
- const tDir = dirname(target)
24
- const tDirExists = await fileExists(tDir)
25
- if (!tDirExists) {
26
- console.error(`cp: target directory ${tDir} does not exist`)
23
+ if (!sStats.isFile()) {
24
+ console.error(`cp: ${source} is a directory (not copied)`)
27
25
  process.exit(1)
28
26
  }
29
27
 
30
28
  const tExists = await fileExists(target)
31
- if (tExists) {
29
+ const tIsDir = target.endsWith(sep)
30
+ // copy file-to-directory
31
+ if (tIsDir) {
32
+ if (!tExists) {
33
+ console.error(`cp: ${target} No such file or directory`)
34
+ process.exit(1)
35
+ }
32
36
  const tStats = await stat(target)
33
37
  if (tStats.isSymbolicLink()) {
34
- console.error(`cp: target ${target} is a sybolic link`)
38
+ console.error(`cp: ${target} is a sybolic link (not copied)`)
35
39
  process.exit(1)
36
40
  }
37
- if (tStats.isDirectory()) {
38
- const sourceFile = basename(source)
39
- target = target.endsWith('/') ? target.slice(0, -1) : target
40
- target = `${target}/${sourceFile}`
41
- }
41
+
42
+ // append source file name to target directory
43
+ const sourceFile = basename(source)
44
+ target = target.endsWith('/') ? target.slice(0, -1) : target
45
+ target = `${target}/${sourceFile}`
42
46
  }
43
47
 
44
48
  try {
45
- await cp(source, target, { force })
49
+ await cp(source, target, { force: true })
46
50
  } catch (err) {
47
51
  console.error(`cp: error ${err.message}`)
48
52
  }
49
53
  }
50
54
 
55
+ /**
56
+ * Copy a multiple files/globs asynchronously
57
+ *
58
+ * @param {string[]} sources The source files/globs
59
+ * @param {string} target The target file
60
+ * @param {boolean} force If the file already exists, overwrite it (default false)
61
+ */
62
+ export async function copyMultipleAsync (sources, target, force = false) {
63
+ const tExists = await fileExists(target)
64
+ if (!tExists) {
65
+ console.error(`cp: ${target} No such file or directory`)
66
+ process.exit(1)
67
+ }
68
+ const tStats = await stat(target)
69
+ if (tStats.isSymbolicLink()) {
70
+ console.error(`cp: ${target} is a sybolic link (not copied)`)
71
+ process.exit(1)
72
+ }
73
+
74
+ try {
75
+ target = target.endsWith('/') ? target.slice(0, -1) : target
76
+ for (const source of sources) {
77
+ await cp(source, `${target}/${basename(source)}`, { force: true })
78
+ }
79
+ } catch (err) {
80
+ console.error(`cp": error ${err.message}`)
81
+ }
82
+ }
83
+
51
84
  /**
52
85
  * Recursively copy a directory asynchronously
86
+ *
53
87
  * @param {string} source The source directory
54
88
  * @param {string} target The target directory
55
89
  * @param {boolean} force If the file already exists, overwrite it (default false)
@@ -57,32 +91,32 @@ export async function copyAsync (source, target, force = false) {
57
91
  export async function copyRecursiveAsync (source, target, force = false) {
58
92
  const sExists = await fileExists(source)
59
93
  if (!sExists) {
60
- console.error(`cp: source ${source} does not exist`)
94
+ console.error(`cp: ${source} No such file or directory`)
61
95
  process.exit(1)
62
96
  }
63
97
  const sStats = await stat(source)
64
98
  if (sStats.isSymbolicLink()) {
65
- console.error(`cp: source ${source} is a sybolic link`)
99
+ console.error(`cp: ${source} is a sybolic link (not copied)`)
66
100
  process.exit(1)
67
101
  }
68
102
 
69
103
  const tExists = await fileExists(target)
70
104
  if (!tExists) {
71
- console.error(`cp: target directory ${target} does not exist`)
105
+ console.error(`cp: ${target} No such file or directory`)
72
106
  process.exit(1)
73
107
  }
74
108
  const tStats = await stat(target)
75
109
  if (tStats.isSymbolicLink()) {
76
- console.error(`cp: target ${target} is a sybolic link`)
110
+ console.error(`cp: ${target} is a sybolic link (not copied)`)
77
111
  process.exit(1)
78
112
  }
79
113
  if (!tStats.isDirectory()) {
80
- console.error(`cp: target ${target} is not a directory`)
114
+ console.error(`cp: target ${target} Not a directory`)
81
115
  process.exit(1)
82
116
  }
83
117
 
84
118
  try {
85
- await cp(source, target, { force, recursive: true })
119
+ await cp(source, target, { force: true, recursive: true })
86
120
  } catch (err) {
87
121
  console.error(`cp": error ${err.message}`)
88
122
  }
package/src/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { copyAsync, copyRecursiveAsync } from "./cp.js";
2
2
  export { removeAsync, removeRecursiveAsync } from "./rm.js";
3
- export { fileExists, installed, which } from "./util.js";
3
+ export { expandSource, fileExists, installed, match, which } from "./util.js";
package/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { copyAsync, copyRecursiveAsync } from './cp.js'
2
2
  export { removeAsync, removeRecursiveAsync } from './rm.js'
3
- export { fileExists, installed, which } from './util.js'
3
+ export { expandSource, fileExists, installed, match, which } from './util.js'
package/src/util.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Expand source file/glob into a list of paths
3
+ *
4
+ * @param {*} source the source file/glob
5
+ * @returns {Promise<string[]>} an array of paths
6
+ */
7
+ export function expandSource(source: any): Promise<string[]>;
1
8
  /**
2
9
  * Check if a file/folder exists
3
10
  * @param {string} path the path to the file/folder
@@ -10,6 +17,12 @@ export function fileExists(path: string): Promise<boolean>;
10
17
  * @returns {Promise<boolean>} true if the package is installed, false otherwise
11
18
  */
12
19
  export function installed(pkg: string): Promise<boolean>;
20
+ /**
21
+ * Description
22
+ * @param {string} pattern glob pattern(s) to match
23
+ * @returns {Promise<string[]>} an array of paths
24
+ */
25
+ export function match(pattern: string): Promise<string[]>;
13
26
  /**
14
27
  * Check to see if an application is installed globally
15
28
  * @param {string} program the name of the application
package/src/util.js CHANGED
@@ -1,9 +1,34 @@
1
1
  import { exec } from 'child_process'
2
- import { access, constants } from 'node:fs/promises'
2
+ import { access, constants, glob } from 'node:fs/promises'
3
3
  import { promisify } from 'node:util'
4
4
 
5
5
  const execAsync = promisify(exec)
6
6
 
7
+ /**
8
+ * Expand source file/glob into a list of paths
9
+ *
10
+ * @param {*} source the source file/glob
11
+ * @returns {Promise<string[]>} an array of paths
12
+ */
13
+ export async function expandSource (source) {
14
+ const isGlob = source.includes('*')
15
+ if (isGlob) {
16
+ const paths = await match(source)
17
+ if (paths.length === 0) {
18
+ console.error(`cp: ${paths} no matches found`)
19
+ process.exit(1)
20
+ }
21
+ return paths
22
+ } else {
23
+ const exists = await fileExists(source)
24
+ if (!exists) {
25
+ console.error(`cp: ${source} No such file or directory`)
26
+ process.exit(1)
27
+ }
28
+ return [source]
29
+ }
30
+ }
31
+
7
32
  /**
8
33
  * Check if a file/folder exists
9
34
  * @param {string} path the path to the file/folder
@@ -32,6 +57,15 @@ export async function installed (pkg) {
32
57
  }
33
58
  }
34
59
 
60
+ /**
61
+ * Description
62
+ * @param {string} pattern glob pattern(s) to match
63
+ * @returns {Promise<string[]>} an array of paths
64
+ */
65
+ export async function match (pattern) {
66
+ return await Array.fromAsync(glob(pattern))
67
+ }
68
+
35
69
  /**
36
70
  * Check to see if an application is installed globally
37
71
  * @param {string} program the name of the application