From f320ff244d78784dc90b27139b68cb53c8738803 Mon Sep 17 00:00:00 2001 From: dpilafian Date: Sat, 16 Mar 2024 18:26:21 -0700 Subject: [PATCH] Add --no-overwrite flag to preserve target file #1 --- README.md | 18 +++++++++++------- bin/cli.js | 3 ++- copy-file.ts | 19 ++++++++++++++----- spec/fixtures/target/skip/mock1.html | 11 +++++++++++ spec/fixtures/target/skip/mock2.html | 11 +++++++++++ spec/mocha.spec.js | 12 ++++++++++++ 6 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 spec/fixtures/target/skip/mock1.html create mode 100644 spec/fixtures/target/skip/mock2.html diff --git a/README.md b/README.md index 0c4e37d..26fec3f 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,14 @@ You can also install **copy-file-util** globally (`--global`) and then run it an ### 3. CLI flags Command-line flags: -| Flag | Description | Values | -| ---------- | ---------------------------------------------- | ---------- | -| `--cd` | Change working directory before starting copy. | **string** | -| `--folder` | Indicates the target is a folder. | N/A | -| `--move` | Delete the source file after copying it. | N/A | -| `--note` | Place to add a comment only for humans. | **string** | -| `--quiet` | Suppress informational messages. | N/A | +| Flag | Description | Values | +| ---------------- | ---------------------------------------------- | ---------- | +| `--cd` | Change working directory before starting copy. | **string** | +| `--folder` | Indicates the target is a folder. | N/A | +| `--move` | Delete the source file after copying it. | N/A | +| `--note` | Place to add a comment only for humans. | **string** | +| `--quiet` | Suppress informational messages. | N/A | +| `--no-overwrite` | Abort if target file already exists. | N/A | Examples: - `copy-file app.js app.mjs --quiet`
@@ -66,6 +67,9 @@ Examples: - `copy-file app.js --move --folder dist`
Like the `mv` Unix command. + - `copy-file default-config.json settings/config.json --no-overwrite`
+ Performs a safe copy that aborts if the **settings/config.json** file already exists. + _**Note:** Single quotes in commands are normalized so they work cross-platform and avoid the errors often encountered on Microsoft Windows._ ### 4. Template variables diff --git a/bin/cli.js b/bin/cli.js index ff8b28a..4dc47ee 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -27,7 +27,7 @@ import { dna } from 'dna-engine'; import fs from 'fs'; // Parameters and flags -const validFlags = ['cd', 'folder', 'move', 'note', 'quiet']; +const validFlags = ['cd', 'folder', 'move', 'no-overwrite', 'note', 'quiet']; const cli = cliArgvUtil.parse(validFlags); const source = cli.params[0]; const target = cli.params[1]; @@ -51,6 +51,7 @@ const targetKey = cli.flagOn.folder ? 'targetFolder' : 'targetFile'; const options = { cd: cli.flagMap.cd ?? null, move: cli.flagOn.move, + overwrite: !cli.flagOn.noOverwrite, [targetKey]: target.replace(/{{[^{}]*}}/g, getPackageField), }; const result = copyFile.cp(source, options); diff --git a/copy-file.ts b/copy-file.ts index 24a0f5e..b11a12e 100644 --- a/copy-file.ts +++ b/copy-file.ts @@ -14,12 +14,14 @@ export type Settings = { targetFolder: string, //destination folder for file copy operation fileExtension: string, //new file extension for the target file move: boolean, //delete the source file after copying it + overwrite: boolean, //clobber target file if it exists }; export type Result = { origin: string, //path of origination file dest: string, //path of destination file duration: number, //execution time in milliseconds moved: boolean, //original file was deleted + skipped: boolean, //target file exists and was not overwritten }; const copyFile = { @@ -31,9 +33,10 @@ const copyFile = { targetFolder: null, fileExtension: null, move: false, + overwrite: true, }; - const settings = { ...defaults, ...options }; - const startTime = Date.now(); + const settings = { ...defaults, ...options }; + const startTime = Date.now(); const missingTarget = !settings.targetFile && !settings.targetFolder; const ambiguousTarget = !!settings.targetFile && !!settings.targetFolder; const normalize = (folder: string | null) => @@ -47,6 +50,8 @@ const copyFile = { const targetFolder = targetPath ? normalize(startFolder + targetPath) : null; const targetFile = settings.targetFile ?? settings.targetFolder + '/' + sourceFilename; const target = normalize(startFolder + targetFile); + const targetExists = !missingTarget && fs.existsSync(target); + const skip = targetExists && !settings.overwrite; if (targetFolder) fs.mkdirSync(targetFolder, { recursive: true }); const badTargetFolder = !targetFolder || !fs.existsSync(targetFolder); @@ -61,24 +66,28 @@ const copyFile = { null; if (errorMessage) throw Error('[copy-file-util] ' + errorMessage); - if (settings.move) + if (!skip && settings.move) fs.renameSync(source, target); - else + else if (!skip) fs.copyFileSync(source, target); return { origin: source, dest: target, moved: settings.move, + skipped: skip, duration: Date.now() - startTime, }; }, reporter(result: Result): Result { + // Example output: + // [10:52:42] copy-file build/app.js → dist/app.js (1ms, moved) const name = chalk.gray('copy-file'); const origin = chalk.blue.bold(result.origin); const dest = chalk.magenta(result.dest); const arrow = chalk.gray.bold('→'); - const info = chalk.white(`(${result.duration}ms${result.moved ? ', move' : ''})`); + const status = result.skipped ? ', skip -- target exists' : result.moved ? ', move' : ''; + const info = chalk.white(`(${result.duration}ms${status})`); log(name, origin, arrow, dest, info); return result; }, diff --git a/spec/fixtures/target/skip/mock1.html b/spec/fixtures/target/skip/mock1.html new file mode 100644 index 0000000..22dd1f0 --- /dev/null +++ b/spec/fixtures/target/skip/mock1.html @@ -0,0 +1,11 @@ + + + + + 📂📂📂 copy-file-util 📂📂📂 + + +

📂📂📂 copy-file-util 📂📂📂

+

Copy or rename a file with optional package version number (CLI tool designed for use in npm package.json scripts)

+ + diff --git a/spec/fixtures/target/skip/mock2.html b/spec/fixtures/target/skip/mock2.html new file mode 100644 index 0000000..22dd1f0 --- /dev/null +++ b/spec/fixtures/target/skip/mock2.html @@ -0,0 +1,11 @@ + + + + + 📂📂📂 copy-file-util 📂📂📂 + + +

📂📂📂 copy-file-util 📂📂📂

+

Copy or rename a file with optional package version number (CLI tool designed for use in npm package.json scripts)

+ + diff --git a/spec/mocha.spec.js b/spec/mocha.spec.js index e9e5cd2..a3371b9 100644 --- a/spec/mocha.spec.js +++ b/spec/mocha.spec.js @@ -128,4 +128,16 @@ describe('Executing the CLI', () => { assertDeepStrictEqual(actual, expected); }); + it('with the --no-overwrite flag prevents the target file from being clobbered', () => { + run('copy-file spec/fixtures/source/mock.html spec/fixtures/target/skip/mock1.html --no-overwrite --quiet'); + run('copy-file spec/fixtures/source/mock.html spec/fixtures/target/skip/mock2.html --no-overwrite --quiet'); + run('copy-file spec/fixtures/target/skip/mock1.html spec/fixtures/target/skip/mock2.html --move --no-overwrite'); + const actual = cliArgvUtil.readFolder('spec/fixtures/target/skip'); + const expected = [ + 'mock1.html', + 'mock2.html', + ]; + assertDeepStrictEqual(actual, expected); + }); + });