copy-file-util 1.3.1 → 1.3.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
@@ -20,13 +20,17 @@ $ npm install --save-dev copy-file-util
20
20
  ```
21
21
 
22
22
  ## B) Usage
23
- ### 1. npm package.json scripts
24
- Run `copy-file` from the `"scripts"` section of your **package.json** file.
25
-
23
+ ### 1. Synopsis
24
+ ```
25
+ copy-file [SOURCE] [TARGET]
26
+ ```
26
27
  Parameters:
27
28
  * The **first** parameter is the *source* file.
28
29
  * The **second** parameter is the *target* file or folder (use the `--folder` flag).
29
30
 
31
+ ### 2. npm package.json scripts
32
+ Run `copy-file` from the `"scripts"` section of your **package.json** file.
33
+
30
34
  Example **package.json** scripts:
31
35
  ```json
32
36
  "scripts": {
@@ -35,7 +39,7 @@ Example **package.json** scripts:
35
39
  },
36
40
  ```
37
41
 
38
- ### 2. Command-line npx
42
+ ### 3. Command-line npx
39
43
  Example terminal commands:
40
44
  ```shell
41
45
  $ npm install --save-dev copy-file-util
@@ -43,7 +47,7 @@ $ copy-file src/web/api.html docs/api-manual.html
43
47
  ```
44
48
  You can also install **copy-file-util** globally (`--global`) and then run it anywhere directly from the terminal.
45
49
 
46
- ### 3. CLI flags
50
+ ### 4. CLI flags
47
51
  Command-line flags:
48
52
  | Flag | Description | Values |
49
53
  | ---------------- | ------------------------------------------------ | ---------- |
@@ -55,7 +59,7 @@ Command-line flags:
55
59
  | `--platform-eol` | Save target file with OS dependent line endings. | N/A |
56
60
  | `--quiet` | Suppress informational messages. | N/A |
57
61
 
58
- Examples:
62
+ ### 5. Examples
59
63
  - `copy-file app.js app.mjs --quiet`<br>
60
64
  Displays no output.
61
65
 
@@ -75,9 +79,10 @@ Examples:
75
79
  - `copy-file default-config.json settings/config.json --no-overwrite`<br>
76
80
  Performs a safe copy that aborts if the **settings/config.json** file already exists.
77
81
 
78
- _**Note:** Single quotes in commands are normalized so they work cross-platform and avoid the errors often encountered on Microsoft Windows._
82
+ > [!NOTE]
83
+ > _Single quotes in commands are normalized so they work cross-platform and avoid the errors often encountered on Microsoft Windows._
79
84
 
80
- ### 4. Template variables
85
+ ### 6. Template variables
81
86
  The *target* parameter can contain template variables, like `{{package.version}}` and `{{package.name}}`, which will be replaced with values with values from your project's **package.json** file.
82
87
 
83
88
  Example:
@@ -92,7 +97,7 @@ Example:
92
97
  import { copyFile } from 'copy-file-util';
93
98
 
94
99
  const result = copyFile.cp('src/web/api.html' { targetFile: 'docs/api-manual.html' });
95
- console.log('Execution time:', result.duration, 'ms');
100
+ console.info('Execution time:', result.duration, 'ms');
96
101
  ```
97
102
 
98
103
  See the **TypeScript Declarations** at the top of [copy-file.ts](src/copy-file.ts) for documentation.
package/bin/cli.js CHANGED
@@ -4,58 +4,6 @@
4
4
  // MIT License //
5
5
  ////////////////////
6
6
 
7
- // Usage in package.json:
8
- // "scripts": {
9
- // "pub-license": "copy-file src/LICENSE doc/license.txt"
10
- // },
11
- //
12
- // Usage from command line:
13
- // $ npm install --save-dev copy-file-util
14
- // $ copy-file src/LICENSE doc/license.txt
15
- //
16
- // Contributors to this project:
17
- // $ cd copy-file-util
18
- // $ npm install
19
- // $ npm test
20
- // $ node bin/cli.js --cd=spec/fixtures source/mock.html --folder target/to-folder
21
- // $ node bin/cli.js --cd=spec/fixtures source/mock.html target/{{package.type}}/{{package.name}}-v{{package.version}}.html
22
-
23
- // Imports
24
- import { cliArgvUtil } from 'cli-argv-util';
25
7
  import { copyFile } from '../dist/copy-file.js';
26
- import { dna } from 'dna-engine';
27
- import fs from 'fs';
28
-
29
- // Parameters and flags
30
- const validFlags = ['cd', 'folder', 'move', 'no-overwrite', 'note', 'platform-eol', 'quiet'];
31
- const cli = cliArgvUtil.parse(validFlags);
32
- const source = cli.params[0];
33
- const target = cli.params[1];
34
-
35
- // Utilities
36
- const readPackage = () => JSON.parse(fs.readFileSync('package.json', 'utf-8'));
37
- const getPackageField = (match) => //example: '{{package.version}}' --> '3.1.4'
38
- dna.util.value({ package: readPackage() }, match.replace(/[{}]/g, '')) ?? 'MISSING-FIELD-ERROR';
39
8
 
40
- // Copy File
41
- const error =
42
- cli.invalidFlag ? cli.invalidFlagMsg :
43
- cli.paramCount > 2 ? 'Extraneous parameter: ' + cli.params[2] :
44
- !source ? 'Missing source file.' :
45
- !target && cli.flagOn.folder ? 'Missing target folder.' :
46
- !target ? 'Missing target file.' :
47
- null;
48
- if (error)
49
- throw new Error('[copy-file-util] ' + error);
50
- const targetKey = cli.flagOn.folder ? 'targetFolder' : 'targetFile';
51
- const templateVariables = /{{[^{}]*}}/g; //example match: "{{package.version}}"
52
- const options = {
53
- cd: cli.flagMap.cd ?? null,
54
- move: cli.flagOn.move,
55
- overwrite: !cli.flagOn.noOverwrite,
56
- platformEol: cli.flagOn.platformEol,
57
- [targetKey]: target.replace(templateVariables, getPackageField),
58
- };
59
- const result = copyFile.cp(source, options);
60
- if (!cli.flagOn.quiet)
61
- copyFile.reporter(result);
9
+ copyFile.cli();
@@ -1,10 +1,10 @@
1
- //! copy-file-util v1.3.1 ~~ https://github.com/center-key/copy-file-util ~~ MIT License
1
+ //! copy-file-util v1.3.3 ~~ https://github.com/center-key/copy-file-util ~~ MIT License
2
2
 
3
3
  export type Settings = {
4
- cd: string;
5
- targetFile: string;
6
- targetFolder: string;
7
- fileExtension: string;
4
+ cd: string | null;
5
+ targetFile: string | null;
6
+ targetFolder: string | null;
7
+ fileExtension: string | null;
8
8
  move: boolean;
9
9
  overwrite: boolean;
10
10
  platformEol: boolean;
@@ -17,6 +17,8 @@ export type Result = {
17
17
  skipped: boolean;
18
18
  };
19
19
  declare const copyFile: {
20
+ assert(ok: unknown, message: string | null): void;
21
+ cli(): void;
20
22
  cp(sourceFile: string, options?: Partial<Settings>): Result;
21
23
  reporter(result: Result): Result;
22
24
  };
package/dist/copy-file.js CHANGED
@@ -1,5 +1,7 @@
1
- //! copy-file-util v1.3.1 ~~ https://github.com/center-key/copy-file-util ~~ MIT License
1
+ //! copy-file-util v1.3.3 ~~ https://github.com/center-key/copy-file-util ~~ MIT License
2
2
 
3
+ import { cliArgvUtil } from 'cli-argv-util';
4
+ import { dna } from 'dna-engine';
3
5
  import { EOL } from 'node:os';
4
6
  import chalk from 'chalk';
5
7
  import fs from 'fs';
@@ -7,6 +9,42 @@ import log from 'fancy-log';
7
9
  import path from 'path';
8
10
  import slash from 'slash';
9
11
  const copyFile = {
12
+ assert(ok, message) {
13
+ if (!ok)
14
+ throw new Error(`[copy-file-util] ${message}`);
15
+ },
16
+ cli() {
17
+ const validFlags = ['cd', 'folder', 'move', 'no-overwrite', 'note', 'platform-eol', 'quiet'];
18
+ const cli = cliArgvUtil.parse(validFlags);
19
+ const source = cli.params[0];
20
+ const target = cli.params[1];
21
+ const getPkgField = (substring) => {
22
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
23
+ const value = dna.util.value({ package: pkg }, substring.replace(/[{}]/g, ''));
24
+ return value ?? 'MISSING-FIELD-ERROR';
25
+ };
26
+ const error = cli.invalidFlag ? cli.invalidFlagMsg :
27
+ cli.paramCount > 2 ? 'Extraneous parameter: ' + cli.params[2] :
28
+ !source ? 'Missing source file.' :
29
+ !target && cli.flagOn.folder ? 'Missing target folder.' :
30
+ !target ? 'Missing target file.' :
31
+ null;
32
+ copyFile.assert(!error, error);
33
+ const templateVariables = /{{[^{}]*}}/g;
34
+ const targetValue = target.replace(templateVariables, getPkgField);
35
+ const options = {
36
+ cd: cli.flagMap.cd ?? null,
37
+ targetFile: !cli.flagOn.folder ? targetValue : null,
38
+ targetFolder: cli.flagOn.folder ? targetValue : null,
39
+ fileExtension: null,
40
+ move: !!cli.flagOn.move,
41
+ overwrite: !cli.flagOn.noOverwrite,
42
+ platformEol: !!cli.flagOn.platformEol,
43
+ };
44
+ const result = copyFile.cp(source, options);
45
+ if (!cli.flagOn.quiet)
46
+ copyFile.reporter(result);
47
+ },
10
48
  cp(sourceFile, options) {
11
49
  const defaults = {
12
50
  cd: null,
@@ -20,40 +58,40 @@ const copyFile = {
20
58
  const settings = { ...defaults, ...options };
21
59
  const startTime = Date.now();
22
60
  const missingTarget = !settings.targetFile && !settings.targetFolder;
23
- const ambiguousTarget = !!settings.targetFile && !!settings.targetFolder;
24
- const normalize = (folder) => !folder ? '' : slash(path.normalize(folder)).replace(/\/$/, '');
25
- const startFolder = settings.cd ? normalize(settings.cd) + '/' : '';
26
- const source = sourceFile ? normalize(startFolder + sourceFile) : '';
61
+ const doubleTarget = !!settings.targetFile && !!settings.targetFolder;
62
+ const cleanUp = (folder) => slash(path.normalize(folder)).replace(/\/$/, '');
63
+ const cleanPath = (folder) => !folder ? '' : cleanUp(folder);
64
+ const startFolder = settings.cd ? cleanPath(settings.cd) + '/' : '';
65
+ const source = sourceFile ? cleanPath(startFolder + sourceFile) : '';
27
66
  const sourceExists = source && fs.existsSync(source);
28
67
  const sourceIsFile = sourceExists && fs.statSync(source).isFile();
29
68
  const sourceFilename = sourceIsFile ? path.basename(source) : null;
30
69
  const targetPath = settings.targetFile ? path.dirname(settings.targetFile) : settings.targetFolder;
31
- const targetFolder = targetPath ? normalize(startFolder + targetPath) : null;
70
+ const targetFolder = targetPath ? cleanPath(startFolder + targetPath) : null;
32
71
  const targetFile = settings.targetFile ?? `${settings.targetFolder}/${sourceFilename}`;
33
- const target = normalize(startFolder + targetFile);
72
+ const target = cleanPath(startFolder + targetFile);
34
73
  const targetExists = !missingTarget && fs.existsSync(target);
35
74
  const skip = targetExists && !settings.overwrite;
36
75
  if (targetFolder)
37
76
  fs.mkdirSync(targetFolder, { recursive: true });
38
77
  const badTargetFolder = !targetFolder || !fs.existsSync(targetFolder);
39
- const errorMessage = settings.fileExtension ? 'Option "fileExtension" not yet implemented.' :
78
+ const error = settings.fileExtension ? 'Option "fileExtension" not yet implemented.' :
40
79
  !sourceFile ? 'Must specify the source file.' :
41
80
  !sourceExists ? 'Source file does not exist: ' + source :
42
81
  !sourceIsFile ? 'Source is not a file: ' + source :
43
82
  missingTarget ? 'Must specify a target file or folder.' :
44
- ambiguousTarget ? 'Target cannot be both a file and a folder.' :
83
+ doubleTarget ? 'Target cannot be both a file and a folder.' :
45
84
  badTargetFolder ? 'Target folder cannot be written to: ' + String(targetFolder) :
46
85
  null;
47
- if (errorMessage)
48
- throw new Error('[copy-file-util] ' + errorMessage);
86
+ copyFile.assert(!error, error);
49
87
  const createTarget = () => {
50
88
  if (settings.move)
51
89
  fs.renameSync(source, target);
52
90
  else
53
91
  fs.copyFileSync(source, target);
54
- const osEol = (text) => text.replace(/\r?\n/g, EOL);
92
+ const platformEol = (text) => text.replace(/\r?\n/g, EOL);
55
93
  if (settings.platformEol)
56
- fs.writeFileSync(target, osEol(fs.readFileSync(target, 'utf-8')));
94
+ fs.writeFileSync(target, platformEol(fs.readFileSync(target, 'utf-8')));
57
95
  };
58
96
  if (!skip)
59
97
  createTarget();
@@ -67,12 +105,10 @@ const copyFile = {
67
105
  },
68
106
  reporter(result) {
69
107
  const name = chalk.gray('copy-file');
70
- const origin = chalk.blue.bold(result.origin);
71
- const dest = chalk.magenta(result.dest);
72
- const arrow = chalk.gray.bold('→');
108
+ const ancestor = cliArgvUtil.calcAncestor(result.origin, result.dest);
73
109
  const status = result.skipped ? ', skip -- target exists' : result.moved ? ', move' : '';
74
110
  const info = chalk.white(`(${result.duration}ms${status})`);
75
- log(name, origin, arrow, dest, info);
111
+ log(name, ancestor.message, info);
76
112
  return result;
77
113
  },
78
114
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copy-file-util",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Copy or rename a file with optional package version number (CLI tool designed for use in npm package.json scripts)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "runScriptsConfig": {
44
44
  "clean": [
45
- "rimraf build dist spec/fixtures/target"
45
+ "rimraf build dist spec/target"
46
46
  ],
47
47
  "lint": [
48
48
  "jshint . --exclude-path .gitignore",
@@ -55,27 +55,27 @@
55
55
  },
56
56
  "scripts": {
57
57
  "pretest": "run-scripts clean lint build",
58
- "test": "mocha spec/*.spec.js"
58
+ "test": "mocha spec"
59
59
  },
60
60
  "dependencies": {
61
- "chalk": "~5.4",
62
- "cli-argv-util": "~1.3",
63
- "dna-engine": "~3.2",
61
+ "chalk": "~5.6",
62
+ "cli-argv-util": "~1.4",
63
+ "dna-engine": "~3.3",
64
64
  "fancy-log": "~2.0",
65
65
  "slash": "~5.1"
66
66
  },
67
67
  "devDependencies": {
68
- "@eslint/js": "~9.30",
68
+ "@eslint/js": "~9.39",
69
69
  "@types/fancy-log": "~2.0",
70
- "@types/node": "~24.0",
71
- "add-dist-header": "~1.4",
70
+ "@types/node": "~25.0",
71
+ "add-dist-header": "~1.6",
72
72
  "assert-deep-strict-equal": "~1.2",
73
- "eslint": "~9.30",
73
+ "eslint": "~9.39",
74
74
  "jshint": "~2.13",
75
75
  "mocha": "~11.7",
76
- "rimraf": "~6.0",
76
+ "rimraf": "~6.1",
77
77
  "run-scripts-util": "~1.3",
78
- "typescript": "~5.8",
79
- "typescript-eslint": "~8.36"
78
+ "typescript": "~5.9",
79
+ "typescript-eslint": "~8.50"
80
80
  }
81
81
  }