{"id":27621916,"url":"https://github.com/ghiscoding/native-copyfiles","last_synced_at":"2026-01-28T00:32:44.877Z","repository":{"id":275350093,"uuid":"925842892","full_name":"ghiscoding/native-copyfiles","owner":"ghiscoding","description":"a small JS and CLI util to copy files","archived":false,"fork":false,"pushed_at":"2025-04-15T21:24:59.000Z","size":133,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-23T08:09:50.734Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ghiscoding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-01T21:53:45.000Z","updated_at":"2025-04-15T21:25:03.000Z","dependencies_parsed_at":"2025-02-01T22:01:42.608Z","dependency_job_id":"8f0018fa-c1ca-4d56-865b-c832d4cf8e9c","html_url":"https://github.com/ghiscoding/native-copyfiles","commit_stats":null,"previous_names":["ghiscoding/native-copyfiles"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Fnative-copyfiles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Fnative-copyfiles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Fnative-copyfiles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Fnative-copyfiles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghiscoding","download_url":"https://codeload.github.com/ghiscoding/native-copyfiles/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250395287,"owners_count":21423400,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-23T08:09:57.261Z","updated_at":"2026-01-28T00:32:44.872Z","avatar_url":"https://github.com/ghiscoding.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![Vitest](https://img.shields.io/badge/tested%20with-vitest-fcc72b.svg?logo=vitest)](https://vitest.dev/)\n[![codecov](https://codecov.io/gh/ghiscoding/native-copyfiles/branch/main/graph/badge.svg)](https://codecov.io/gh/ghiscoding/native-copyfiles)\n\u003ca href=\"https://nodejs.org/en/about/previous-releases\"\u003e\u003cimg src=\"https://img.shields.io/node/v/native-copyfiles.svg\" alt=\"Node\" /\u003e\u003c/a\u003e\n\n[![npm](https://img.shields.io/npm/v/native-copyfiles.svg)](https://www.npmjs.com/package/native-copyfiles)\n[![npm](https://img.shields.io/npm/dy/native-copyfiles)](https://www.npmjs.com/package/native-copyfiles)\n[![npm bundle size](https://img.shields.io/badge/gzip-1.85kB-success)](https://bundlejs.com/?q=native-copyfiles)\n\n## native-copyfiles\n\nCopy files easily via JavaScript or the CLI and cross-platform usage using [cli-nano](https://www.npmjs.com/package/cli-nano) dependency for the CLI.\n\nThe library is very similar to the [copyfiles](https://www.npmjs.com/package/copyfiles) package, at least from the outside; however it is quite different internally. It uses native NodeJS as much as possible and so as a lot less dependencies (just 2 instead of 7), which makes this package a lot smaller compared to the original `copyfiles` project (1.8kB instead of 27.6kB gzip). The options are nearly the same (except for `--soft`, which is not implemented), there's also some new options that were added in this project (mainly the rename and dry-run features, see below).\n\n\u003e **Note**: there is 1 noticeable difference with `copyfiles` package, all the CLI options must be provided as suffix and after the source/target directories command (the original `copyfiles` project has them as prefix).\u003cbr\u003e\n\u003e This mean calling: `copyfiles source target [options]` instead of `copyfiles [options] source target`\n\u003e The JS API is also different since the destination is the 2nd function argument instead of the first argument.\n\n\u003e [!NOTE]\n\u003e This project now requires Node.JS \u003e= 22.17.0 so that we can use the native `fs.glob` and decrease the projet size. If you can't update your Node.JS just yet, then just stick with `native-copyfiles: ^1.3.7` until you can. The version 2.0 bumped Node requirement and changed the JS API arguments (see below).\n\n### Advanced Glob Pattern Support\n\n`native-copyfiles` supports advanced glob patterns, including:\n\n- **Brace expansion**: e.g. `src/*.{js,ts}`\n- **Negation**: e.g. `['src/**/*.js', '!src/**/*.test.js']`\n- **Extended wildcards**: e.g. `**/*.js`, `*bar?.js`\n- **Dotfiles**: Use `-a`/`--all` to include files starting with a dot\n\nThis makes it easier to match complex sets of files for copying, similar to Bash or advanced glob libraries.\n\n### Install\n\n```bash\nnpm install native-copyfiles -D\n```\n\n### Command Line\n\n```\n  Usage: copyfiles \u003cinFile..\u003e \u003coutDirectory\u003e [options]\n\nPositionals:\n  inFile              Source file(s)                                            [string|string[]]\n  outDirectory        Destination directory                                     [string]\n\n  Options:\n    -u, --up       slice a path off the bottom of the paths                     [number]\n    -a, --all      include files \u0026 directories begining with a dot (.)          [boolean]\n    -d, --dry-run  show what would be copied, without actually copying anything [boolean]\n    -f, --flat     flatten the output                                           [boolean]\n    -e, --exclude  pattern or glob to exclude (may be passed multiple times)    [string|string[]]\n    -E, --error    throw error if nothing is copied                             [boolean]\n    -V, --verbose  print more information to console                            [boolean]\n    -F, --follow   follow symbolic links                                        [boolean]\n    -s, --stat     show statistics after execution (time + files/folders count) [boolean]\n    -v, --version  show version number                                          [boolean]\n    -h, --help     show help                                                    [boolean]\n```\n\n\u003e [!NOTE]\n\u003e Options **must** be provided after the command directories as suffix (the original project references them as prefix)\n\nCopy some files, give it a bunch of arguments (which can include advanced globs), the last argument being the \"out\" directory (which will be created when necessary). Note: on Windows globs must be **double quoted**, everybody else can quote however they please.\n\n```bash\ncopyfiles foo foobar foo/bar/*.js out\n```\n\nyou now have a directory called `\"out\"`, with the files `\"foo\"` and `\"foobar\"` in it, it also has a directory named `\"foo\"` with a directory named\n`\"bar\"` in it that has all the files from `\"foo/bar\"` that match the glob.\n\n\n#### Examples of advanced glob usage\n\n**Brace expansion:**\n```bash\ncopyfiles \"src/*.{js,ts}\" out\n```\n\n**Negation:**\n```bash\ncopyfiles \"src/**/*.js\" out -e \"**/*.test.js\"\n```\n\n**Dotfiles:**\n```bash\ncopyfiles -a \".*.env\" out\n```\n\nIf all the files are in a folder that you don't want in the path out path, ex:\n\n```bash\ncopyfiles something/*.js out\n```\n\nwhich would put all the JS files in `\"out/something\"`, you can use the `--up` (or `-u`) option\n\n```bash\ncopyfiles something/*.js out -u 1\n```\n\nwhich would put all the JS files in `out`\n\nyou can also just do `-f` which will flatten all the output into one directory, so in the end we'll have files `\"./foo/a.txt\"` and `\"./foo/bar/b.txt\"`\n\n```bash\ncopyfiles ./foo/*.txt ./foo/bar/*.txt out -f\n```\n\nwill put `\"a.txt\"` and `\"b.txt\"` into out\n\nif your terminal doesn't support globstars then you can quote them\n\n```bash\ncopyfiles ./foo/**/*.txt out -f\n```\n\nhowever this does not work by default on a Mac, but the following does:\n\n```bash\ncopyfiles \"./foo/**/*.txt\" out -f\n```\n\nYou could quote globstars as a part of input:\n```bash\ncopyfiles some.json \"./some_folder/*.json\" \"./dist/\" \u0026\u0026 echo 'JSON files copied.'\n```\n\nYou can use the `-e` option to exclude some files from the pattern, and if we want to exclude all files ending in `\".test.js\"` you could do\n\n```bash\ncopyfiles \"**/*.test.js\" -f \"./foo/**/*.js\" out -e\n```\n\n\u003e [!NOTE]\n\u003e By default the `.git/` and `node_modules/` directories will be excluded (when using globs). If you provide your own `--exclude` option, it will override the defaults and only use your patterns.\n\nOther options include\n\n- `-a` or `--all` which includes files that start with a dot.\n- `-F` or `--follow` which follows symbolic links\n\n### Copy and Rename a Single File\n\nYou can copy and rename a single file by specifying the source file and the destination filename (not just a directory). For example, to copy `input/.env_publish` to `output/.env`:\n\n```bash\ncopyfiles input/.env_publish output/.env\n```\n\nThis will copy and rename the file in one step.\nYou can use this for any filename, not just files starting with a dot:\n\n```bash\ncopyfiles input/original.txt output/renamed.txt\n```\n\nIf 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.\n\n---\n\n### Rename Multiple Files During Copy\n\n#### 1. Rename Using Wildcard (`*`)\n\nYou can use a wildcard (`*`) in the destination to rename files dynamically. For example, to copy all `.css` files and change their extension to `.scss`:\n\n```bash\ncopyfiles \"input/**/*.css\" \"output/*.scss\"\n```\n\nThis will copy:\n\n- `input/foo.css` → `output/foo.scss`\n- `input/bar/baz.css` → `output/bar/baz.scss`\n\nThe `*` in the destination is replaced with the base filename from the source.\nYou can combine this with `--flat` or `--up` to control the output structure.\n\n#### 2. Rename Using a Callback (JavaScript API)\n\nFor advanced renaming, you can use the `rename` callback option in the API.\nThis function receives the source and destination path and should return the new destination path of each file being processed.\n\n**Example: Change extension to `.scss` using a callback**\n\n```js\nimport { copyfiles } from 'native-copyfiles';\n\ncopyfiles(['input/**/*.css', 'output'], {\n  flat: true,\n  rename: (src, dest) =\u003e dest.replace(/\\.css$/, '.scss')\n}, (err) =\u003e {\n  // All files like input/foo.css → output/foo.scss\n});\n```\n\n**Example: Prefix all filenames with `renamed-` but keep the extension**\n\n```js\ncopyfiles(['input/**/*.css', 'output'], {\n  up: 1,\n  rename: (src, dest) =\u003e dest.replace(/([^/\\\\]+)\\.css$/, 'renamed-$1.css')\n}, (err) =\u003e {\n  // input/foo.css → output/renamed-foo.css\n  // input/bar/baz.css → output/bar/renamed-baz.css\n});\n```\n\nThe `rename` callback gives you full control over the output filename and path.\n\n\u003e **Tip:**\n\u003e You can use either the wildcard approach or the `rename` callback, or even combine them for advanced scenarios!\n\n\u003e [!NOTE]\n\u003e If you use both a destination wildcard approach (e.g. `output/*.ext`) and a `rename` callback, the wildcard change 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.\n\n---\n\n### JavaScript API\n\n```js\nimport { copyfiles } from 'native-copyfiles';\n\ncopyfiles(sources, destination, opt, callback);\n```\n\n1. first argument is a string or an array of source paths\n2. second argument is the destination path\n3. third argument (`opt`) is the \"options\" argument\n4. and finally the last argument is a callback function that will be executed after the copy process\n\n```js\n{\n    verbose: boolean;     // print more information to console\n    up: number;           // slice a path off the bottom of the paths\n    exclude: string;      // exclude pattern\n    all: boolean;         // include dot files\n    dryRun: boolean;      // show what would be copied, without actually copying anything\n    follow: boolean;      // follow symlinked directories when expanding ** patterns\n    error: boolean;       // raise errors if no files copied\n    stat: boolean;        // show statistics after execution (time + files or folders count)\n    rename: (src, dest) =\u003e string;  // callback to transform the destination filename(s)\n}\n```\n\n\u003e [!WARNING]\n\u003e Version 2.0 changed the JS API and moved the destination as the 2nd argument (which is different compared to v1.0 which previously had its destination inside the 1st argument array as the last element which was super confusing).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghiscoding%2Fnative-copyfiles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghiscoding%2Fnative-copyfiles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghiscoding%2Fnative-copyfiles/lists"}