Skip to content

Commit c7ed58d

Browse files
authored
feat: rename multiple files while copying using glob patterns (#19)
* feat: rename multiple files while copying using glob patterns
1 parent 02253ee commit c7ed58d

5 files changed

Lines changed: 489 additions & 98 deletions

File tree

README.md

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ npm install native-copyfiles -D
3434
-E, --error throw error if nothing is copied [boolean]
3535
-V, --verbose print more information to console [boolean]
3636
-F, --follow follow symbolic links [boolean]
37-
-v, --version Show version number [boolean]
38-
-h, --help Show help [boolean]
37+
-v, --version show version number [boolean]
38+
-h, --help show help [boolean]
3939
```
4040
4141
> [!NOTE]
@@ -122,6 +122,66 @@ copyfiles input/original.txt output/renamed.txt
122122
123123
If the destination path is a directory, the file will be copied into that directory as usual. If the destination path is a filename, the file will be copied and renamed.
124124
125+
---
126+
127+
### Rename Multiple Files During Copy
128+
129+
#### 1. Rename Using Glob Patterns
130+
131+
You can use a wildcard (`*`) in the destination to rename files dynamically. For example, to copy all `.css` files and change their extension to `.scss`:
132+
133+
```bash
134+
copyfiles "input/**/*.css" "output/*.scss"
135+
```
136+
137+
This will copy:
138+
139+
- `input/foo.css``output/foo.scss`
140+
- `input/bar/baz.css``output/bar/baz.scss`
141+
142+
The `*` in the destination is replaced with the base filename from the source.
143+
You can combine this with `--flat` or `--up` to control the output structure.
144+
145+
#### 2. Rename Using a Callback (JavaScript API)
146+
147+
For advanced renaming, you can use the `rename` callback option in the API.
148+
This function receives the source and destination path and should return the new destination path.
149+
150+
**Example: Change extension to `.scss` using a callback**
151+
152+
```js
153+
import { copyfiles } from 'native-copyfiles';
154+
155+
copyfiles(['input/**/*.css', 'output'], {
156+
flat: true,
157+
rename: (src, dest) => dest.replace(/\.css$/, '.scss')
158+
}, (err) => {
159+
// All files like input/foo.css → output/foo.scss
160+
});
161+
```
162+
163+
**Example: Prefix all filenames with `renamed-` but keep the extension**
164+
165+
```js
166+
copyfiles(['input/**/*.css', 'output'], {
167+
up: 1,
168+
rename: (src, dest) => dest.replace(/([^/\\]+)\.css$/, 'renamed-$1.css')
169+
}, (err) => {
170+
// input/foo.css → output/renamed-foo.css
171+
// input/bar/baz.css → output/bar/renamed-baz.css
172+
});
173+
```
174+
175+
The `rename` callback gives you full control over the output filename and path.
176+
177+
> **Tip:**
178+
> You can use either the glob pattern approach or the `rename` callback, or even combine them for advanced scenarios!
179+
180+
> [!NOTE]
181+
> If you use both a destination glob pattern (e.g. `output/*.ext`) and a `rename` callback, the glob pattern is applied first and then the `rename` callback is executed last on the computed destination path. This allows you to combine both features for advanced renaming scenarios.
182+
183+
---
184+
125185
### JavaScript API
126186
127187
```js
@@ -136,11 +196,12 @@ and finally the third and last argument is a callback function which is executed
136196
137197
```js
138198
{
139-
verbose: bool, // enable debug messages
140-
up: number, // -u value
141-
exclude: string, // exclude pattern
142-
all: bool, // include dot files
143-
follow: bool, // Follow symlinked directories when expanding ** patterns
144-
error: bool // raise errors if no files copied
199+
verbose: bool, // enable debug messages
200+
up: number, // -u value
201+
exclude: string, // exclude pattern
202+
all: bool, // include dot files
203+
follow: bool, // follow symlinked directories when expanding ** patterns
204+
error: bool // raise errors if no files copied
205+
rename: (src, dest) => string; // callback to transform the destination filename(s)
145206
}
146-
```
207+
```

src/__tests__/cli.spec.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { existsSync, readdir, rmSync, writeFileSync } from 'node:fs';
1+
import { existsSync, readdir, readdirSync, rmSync, writeFileSync } from 'node:fs';
22
import { afterAll, afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
33

44
import { createDir } from '../index';
@@ -62,17 +62,17 @@ describe('copyfiles', () => {
6262
setTimeout(check, 50);
6363
return;
6464
}
65-
readdir('output2/input2', (err, files) => {
66-
try {
67-
expect(err).toBeNull();
65+
try {
66+
setTimeout(() => {
67+
const files = readdirSync('output2/input2');
6868
expect(files).toEqual(['a.txt', 'b.txt']);
6969
exitSpy.mockRestore();
7070
done();
71-
} catch (e) {
72-
exitSpy.mockRestore();
73-
done(e);
74-
}
75-
});
71+
}, 50);
72+
} catch (e) {
73+
exitSpy.mockRestore();
74+
done(e);
75+
}
7676
};
7777
check();
7878
})

0 commit comments

Comments
 (0)