remove-glob 1.1.1 → 1.2.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.
package/README.md CHANGED
@@ -2,19 +2,27 @@
2
2
  [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
3
3
  [![Vitest](https://img.shields.io/badge/tested%20with-vitest-fcc72b.svg?logo=vitest)](https://vitest.dev/)
4
4
  [![codecov](https://codecov.io/gh/ghiscoding/remove-glob/branch/main/graph/badge.svg)](https://codecov.io/gh/ghiscoding/remove-glob)
5
- [![npm](https://img.shields.io/npm/v/remove-glob.svg)](https://www.npmjs.com/package/remove-glob)
6
- [![npm](https://img.shields.io/npm/dy/remove-glob)](https://www.npmjs.com/package/remove-glob)
7
- [![npm bundle size](https://img.shields.io/bundlephobia/minzip/remove-glob?color=success&label=gzip)](https://bundlephobia.com/result?p=remove-glob)
8
5
  <a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/remove-glob.svg" alt="Node" /></a>
9
6
 
10
- ## remove-glob
7
+ [![npm](https://img.shields.io/npm/dy/remove-glob)](https://www.npmjs.com/package/remove-glob)
8
+ [![npm](https://img.shields.io/npm/v/remove-glob.svg)](https://www.npmjs.com/package/remove-glob)
9
+ [![npm bundle size](https://img.shields.io/badge/API%20only%20(gzip)-1.05kB-1183c4)](https://bundlejs.com/?q=remove-glob)
10
+ [![npm bundle size](https://img.shields.io/badge/API+CLI%20(gzip)-4.08kB-1183c4)](https://bundlejs.com/?q=remove-glob%2Cremove-glob%2Fdist%2Fcli.js)
11
+
12
+ ## Remove-Glob
11
13
 
12
14
  A tiny cross-platform utility to remove items or directories recursively, it also accepts an optional glob pattern. There's also a CLI for easy, cross-platform usage using [cli-nano](https://www.npmjs.com/package/cli-nano) which is the only external dependency.
13
15
 
14
16
  Inspired by [rimraf](https://www.npmjs.com/package/rimraf) and [premove](https://www.npmjs.com/package/premove) but also supports glob pattern to remove multiple files or directories.
15
17
 
16
- > [!NOTE]
17
- > This project now requires Node.JS >= 22.17.0 so that we can use the native `fs.glob`, however if you can't update your Node.JS just yet, then just stick with `remove-glob: ^0.4.10` since that is the only change in v1.0.0
18
+ ### Major Changes
19
+ #### version 0.x
20
+ - works with Node.JS 20.x by using `tinyglobby` for glob matching
21
+
22
+ #### version 1.0
23
+ - drop `tinyglobby` and use `fs.glob` native code (requires Node.JS >=22.17)
24
+
25
+ ---
18
26
 
19
27
  ### Install
20
28
  ```sh
@@ -29,29 +37,46 @@ A `remove` binary is available, it takes an optional path argument (zero or mult
29
37
  > The `paths` and `glob` arguments are both optionals, but you **must** provide at least 1 of them.
30
38
  > However, please note that providing both of them simultaneously is not supported and will throw an error (choose the option that is best suited to your use case).
31
39
 
32
- > [!NOTE]
33
- > When using the `--glob` option, dotfiles and dot-directories (e.g. `.env`, `.gitignore`, `.config/`) are included by default. If you want to exclude them, you can adjust your glob pattern (e.g. use `**/[!.]*.js` or add an `!**/.*` pattern).
40
+ When using the `--glob` option, dotfiles and dot-directories (e.g. `.env`, `.gitignore`, `.config/`) are included by default. If you want to exclude them, you can adjust your glob pattern (e.g. use `**/[!.]*.js` or add an `!**/.*` pattern).
34
41
 
35
42
  ```
36
43
  Usage:
37
- remove [paths..] [options] Remove all items recursively
44
+ remove [paths..] [options]Remove all items recursively
38
45
 
39
46
  Arguments:
40
- paths directory or file paths to remove [string..]
47
+ paths Directory or file paths to remove [string..]
41
48
 
42
49
  Options:
43
- --cwd Directory to resolve from (default ".") [string]
44
- -d, --dryRun Show which files/dirs would be deleted but without actually removing them [boolean]
45
- -g, --glob Glob pattern(s) to find which files/dirs to remove [array]
46
- -s, --stat Show the stats of the items being removed [boolean]
47
- -V, --verbose If true, it will log each file or directory being removed [boolean]
48
- -e, --exclude Pattern or glob to exclude (may be passed multiple times) [string|string[]]
49
- -h, --help Show help [boolean]
50
- -v, --version Show version number [boolean]
50
+ --cwd Directory to resolve from (default ".") [string]
51
+ -d, --dry-run Show which files/dirs would be deleted but without actually removing them [boolean]
52
+ -g, --glob Glob pattern(s) to find which files/dirs to remove [array]
53
+ -a, --all Include dotfiles (files starting with a dot) when matching glob patterns [boolean]
54
+ -s, --stat Show the stats of the items being removed [boolean]
55
+ -V, --verbose If true, it will log each file or directory being removed [boolean]
56
+ -e, --exclude Glob pattern(s) to exclude from deletion (overrides the default patterns) [array]
57
+ -h, --help Show help [boolean]
58
+ -v, --version Show version number [boolean]
51
59
  ```
52
60
 
53
- > [!NOTE]
54
- > When `exclude` glob pattern(s) are provided, it will override the default exclude of: [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`]
61
+ When `exclude` glob pattern(s) are provided, it will override the default exclude [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`].
62
+
63
+ The `--all`/`-a` option includes dotfiles (files starting with a dot) in glob matches. By default, dotfiles are excluded unless explicitly matched or this option is used.
64
+
65
+ #### Negation Patterns
66
+ You can use negation patterns (starting with `!`) in glob arrays to exclude files from removal:
67
+
68
+ ```sh
69
+ $ npx remove --glob "src/**/*.js" --glob "!src/**/*.test.js"
70
+ # This will remove all .js files except those ending with .test.js
71
+ ```
72
+
73
+ #### Brace Expansion
74
+ Brace expansion is supported in glob patterns:
75
+
76
+ ```sh
77
+ $ npx remove --glob "src/*.{js,ts}"
78
+ # This will match both .js and .ts files in src/
79
+ ```
55
80
 
56
81
  Remove files or directories. Note: on Windows globs must be **double quoted**, everybody else can quote however they please.
57
82
 
@@ -69,16 +94,15 @@ $ remove foo bar
69
94
  $ remove --glob \"dist/**/*.{js,map}\"
70
95
  ```
71
96
 
72
- > [!NOTE]
73
- > When using the `--glob` option, it will skip `.git/` and `node_modules/` directories by default (using the default `--exclude` patterns).
74
- > If you want to allow deletion of these directories, you can override the default by providing your own `--exclude` option (including an empty array to disable exclusion):
75
- >
76
- > ```sh
77
- > # Remove everything, including .git and node_modules
78
- > $ npx remove --glob "**/*" --exclude ""
79
- > # Or exclude only .git, but allow node_modules to be deleted
80
- > $ npx remove --glob "**/*" --exclude "**/.git/**" --exclude "**/.git"
81
- > ```
97
+ When using the `--glob` option, it will skip `.git/` and `node_modules/` directories by default (internally it just set that as the default `exclude`). If you want to allow deletion of these directories, you can override the default by providing your own `--exclude` option (including an empty array to disable exclusion), for example:
98
+
99
+ ```sh
100
+ # Remove everything, including .git and node_modules
101
+ $ npx remove --glob "**/*" --exclude ""
102
+
103
+ # Or exclude only .git, but allow node_modules to be deleted
104
+ $ npx remove --glob "**/*" --exclude "**/.git/**" --exclude "**/.git"
105
+ ```
82
106
 
83
107
  ### Usage
84
108
 
@@ -98,7 +122,7 @@ const dir = resolve('./foo/bar');
98
122
  await removeSync({ paths: ['hello.txt'], cwd: dir });
99
123
  ```
100
124
 
101
- ### JavaScript API
125
+ ## JavaScript API
102
126
 
103
127
  ```js
104
128
  import { removeSync } from 'remove-glob';
@@ -110,19 +134,28 @@ The first argument is an object holding any of the options shown below. The last
110
134
 
111
135
  ```js
112
136
  {
113
- cwd: string; // directory to resolve your `filepath` from, defaults to `process.cwd()`
114
- dryRun: boolean; // show what would be copied, without actually copying anything
115
- paths: string | string[]; // filepath(s) to remove – may be a file or a directory.
116
- glob: string; // glob pattern to find which files/directories to remove
117
- exclude: string[]; // glob pattern(s) to exclude from deletion
118
- stats: boolean; // show some statistics after execution (time + file count)
119
- verbose: boolean; // print more information to console when executing the removal
137
+ cwd: string; // directory to resolve your `filepath` from, defaults to `process.cwd()`
138
+ dryRun: boolean; // show what would be removed, without actually removing anything
139
+ paths: string | string[]; // filepath(s) to remove – may be a file or a directory.
140
+ glob: string | string[]; // glob pattern(s) to find which files/directories to remove
141
+ exclude: string | string[]; // glob pattern(s) to exclude from deletion
142
+ all: boolean; // include dotfiles (files starting with a dot) in glob matches
143
+ stat: boolean; // show some statistics after execution (time + file count)
144
+ verbose: boolean; // print more information to console when executing the removal
120
145
  }
121
146
  ```
122
147
 
148
+
123
149
  > [!NOTE]
124
- > The first argument is required and it **must** include either a `paths` or a `glob`, but it cannot include both options simultaneously.
125
- > Also when `exclude` glob pattern(s) are provided, it will override the default exclude of: [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`]
150
+ > The first argument is required and it **must** include either a `paths` or a `glob`, but it cannot include both options simultaneously. A
151
+
152
+ When an `exclude` glob pattern is provided, it will override the default exclusion [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`].
153
+
154
+ #### Advanced glob usage
155
+
156
+ - **Negation**: Use `!pattern` in a glob array to exclude files from removal.
157
+ - **Brace expansion**: Use `{a,b}` to match multiple alternatives in a single pattern.
158
+ - **Dotfiles**: Use `--all`/`-a` to include dotfiles in glob matches.
126
159
 
127
160
  ### Used by
128
161
 
package/dist/cli.js CHANGED
@@ -25,11 +25,11 @@ try {
25
25
  name: 'remove',
26
26
  describe: 'Remove all items recursively',
27
27
  examples: [
28
- { cmd: '$0 foo bar', describe: 'Remove "foo" and "bar" folders' },
29
- { cmd: '$0 --glob="dist/**/*.js"', describe: 'Remove all files from from "dist" folder with ".js" extension' },
28
+ { cmd: '$0 foo bar', describe: 'Remove "foo" and "bar" folders' },
29
+ { cmd: '$0 --glob="dist/**/*.js"', describe: 'Remove all files from from "dist" folder with ".js" extension' },
30
30
  {
31
31
  cmd: '$0 --glob="dist/**/*.js" --glob="packages/*/tsconfig.tsbuildinfo"',
32
- describe: 'Remove all files from from "dist" folder with ".js" extension and "tsconfig.tsbuildinfo" file from every "packages" folders',
32
+ describe: 'Remove all files from from "dist" folder with ".js" extension and "tsconfig.tsbuildinfo" file from every "packages" folders',
33
33
  },
34
34
  ],
35
35
  positionals: [
@@ -58,6 +58,12 @@ try {
58
58
  type: 'array',
59
59
  describe: 'Glob pattern(s) to find which files/dirs to remove',
60
60
  },
61
+ all: {
62
+ alias: 'a',
63
+ type: 'boolean',
64
+ default: false,
65
+ describe: 'Include dotfiles (files starting with a dot) when matching glob patterns',
66
+ },
61
67
  stat: {
62
68
  alias: 's',
63
69
  default: false,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,aAAkB,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,qBAgFlF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,aAAkB,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,qBAoElF"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { existsSync, globSync, rmSync, statSync, unlinkSync } from 'node:fs';
1
+ import { existsSync, rmSync, statSync, unlinkSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
- import { throwOrCallback } from './utils.js';
3
+ import { throwOrCallback, getMatchedFiles } from './utils.js';
4
4
  /**
5
5
  * Remove the files or directories, the item(s) can be provided via positional arguments or via a `--glob` pattern.
6
6
  * @param {RemoveOptions} options - CLI options
@@ -31,18 +31,7 @@ export function removeSync(opts = {}, callback) {
31
31
  // paths is always an array now
32
32
  const requiresCwdChange = !!(paths.length && opts.cwd);
33
33
  if (!paths.length && opts.glob) {
34
- // Use fs.globSync to match files. Dotfiles and dot-directories are always included.
35
- const defaultExclude = ['**/.git/**', '**/.git', '**/node_modules/**', '**/node_modules'];
36
- const globOptions = { cwd: opts.cwd, withFileTypes: false };
37
- globOptions.exclude = Array.isArray(opts.exclude) ? opts.exclude : defaultExclude;
38
- const globResult = globSync(opts.glob, globOptions);
39
- // We reassign 'paths' here to hold the final list of files matched by glob,
40
- // after filtering for strings and resolving to absolute paths if needed.
41
- paths =
42
- Array.isArray(globResult) && globResult.length > 0 ? globResult.filter((v) => typeof v === 'string') : [];
43
- if (opts.cwd) {
44
- paths = paths.map(p => resolve(opts.cwd, p));
45
- }
34
+ paths = getMatchedFiles(opts.glob, { cwd: opts.cwd, exclude: opts.exclude, all: opts.all });
46
35
  }
47
36
  if (opts.stat || opts.verbose) {
48
37
  console.time('Duration');
@@ -56,7 +45,7 @@ export function removeSync(opts = {}, callback) {
56
45
  }
57
46
  if (existsSync(path)) {
58
47
  const isDir = statSync(path).isDirectory();
59
- const pathLog = `${isDir ? 'directory' : 'file'}: ${path}`;
48
+ const pathLog = `${isDir ? 'directory recursively' : 'file'}: ${path}`;
60
49
  if (opts.dryRun) {
61
50
  console.log(`would remove ${pathLog}`);
62
51
  }
@@ -26,5 +26,9 @@ export interface RemoveOptions {
26
26
  * To disable exclusion and allow deleting everything, pass an empty array ([]).
27
27
  */
28
28
  exclude?: string | string[];
29
+ /**
30
+ * If true, include dotfiles (files starting with a dot) when matching glob patterns.
31
+ */
32
+ all?: boolean;
29
33
  }
30
34
  //# sourceMappingURL=interfaces.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAE/B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B,0CAA0C;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC7B"}
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAE/B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B,0CAA0C;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf"}
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Helper to get all matched files from glob patterns, supporting dotfile logic via opts.all
3
+ */
4
+ export declare function getMatchedFiles(glob: string | string[], opts: {
5
+ cwd?: string;
6
+ exclude?: string | string[];
7
+ all?: boolean;
8
+ }): string[];
1
9
  /** Helper to throw or callback with error */
2
10
  export declare function throwOrCallback(err?: Error, cb?: (e?: Error) => void): void;
3
11
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,QAMpE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,EAAE,CA0DrI;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,QAMpE"}
package/dist/utils.js CHANGED
@@ -1,3 +1,65 @@
1
+ import { globSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * Helper to get all matched files from glob patterns, supporting dotfile logic via opts.all
5
+ */
6
+ export function getMatchedFiles(glob, opts) {
7
+ const defaultExclude = ['**/.git/**', '**/.git', '**/node_modules/**', '**/node_modules'];
8
+ const globOptions = { cwd: opts.cwd, withFileTypes: false, exclude: defaultExclude };
9
+ if (Array.isArray(opts.exclude)) {
10
+ globOptions.exclude = opts.exclude;
11
+ }
12
+ else if (typeof opts.exclude === 'string') {
13
+ globOptions.exclude = [opts.exclude];
14
+ }
15
+ // Separate positive and negated patterns
16
+ const patterns = Array.isArray(glob) ? glob : [glob];
17
+ const positivePatterns = [];
18
+ const negatedPatterns = [];
19
+ for (const pat of patterns) {
20
+ if (typeof pat === 'string' && pat.startsWith('!')) {
21
+ negatedPatterns.push(pat.slice(1));
22
+ }
23
+ else {
24
+ positivePatterns.push(pat);
25
+ // Dotfile logic for positive patterns
26
+ if (opts.all && typeof pat === 'string' && pat.includes('*') && !pat.startsWith('.')) {
27
+ const dotPattern = pat.replace(/(\*)/g, '.*');
28
+ if (dotPattern !== pat) {
29
+ positivePatterns.push(dotPattern);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ // Collect all files matching positive patterns
35
+ const matchedSet = new Set();
36
+ for (const pattern of positivePatterns) {
37
+ const globResult = globSync(pattern, globOptions);
38
+ if (Array.isArray(globResult) && globResult.length > 0) {
39
+ for (const v of globResult) {
40
+ if (typeof v === 'string') {
41
+ matchedSet.add(v);
42
+ }
43
+ }
44
+ }
45
+ }
46
+ // Remove files matching any negated pattern
47
+ for (const pattern of negatedPatterns) {
48
+ const globResult = globSync(pattern, globOptions);
49
+ if (Array.isArray(globResult) && globResult.length > 0) {
50
+ for (const v of globResult) {
51
+ if (typeof v === 'string') {
52
+ matchedSet.delete(v);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ let paths = Array.from(matchedSet);
58
+ if (opts.cwd) {
59
+ paths = paths.map(p => resolve(opts.cwd, p));
60
+ }
61
+ return paths;
62
+ }
1
63
  /** Helper to throw or callback with error */
2
64
  export function throwOrCallback(err, cb) {
3
65
  if (typeof cb === 'function') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remove-glob",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "A tiny utility to remove items or directories recursively, also supports glob",
5
5
  "bin": {
6
6
  "remove": "dist/cli.js"
@@ -35,7 +35,7 @@
35
35
  "url": "https://github.com/ghiscoding/remove-glob/issues"
36
36
  },
37
37
  "dependencies": {
38
- "cli-nano": "^1.2.2"
38
+ "cli-nano": "^1.4.0"
39
39
  },
40
40
  "engines": {
41
41
  "node": "^22.17.0 || >=24.0.0"
package/src/cli.ts CHANGED
@@ -29,12 +29,12 @@ try {
29
29
  name: 'remove',
30
30
  describe: 'Remove all items recursively',
31
31
  examples: [
32
- { cmd: '$0 foo bar', describe: 'Remove "foo" and "bar" folders' },
33
- { cmd: '$0 --glob="dist/**/*.js"', describe: 'Remove all files from from "dist" folder with ".js" extension' },
32
+ { cmd: '$0 foo bar', describe: 'Remove "foo" and "bar" folders' },
33
+ { cmd: '$0 --glob="dist/**/*.js"', describe: 'Remove all files from from "dist" folder with ".js" extension' },
34
34
  {
35
35
  cmd: '$0 --glob="dist/**/*.js" --glob="packages/*/tsconfig.tsbuildinfo"',
36
36
  describe:
37
- 'Remove all files from from "dist" folder with ".js" extension and "tsconfig.tsbuildinfo" file from every "packages" folders',
37
+ 'Remove all files from from "dist" folder with ".js" extension and "tsconfig.tsbuildinfo" file from every "packages" folders',
38
38
  },
39
39
  ],
40
40
  positionals: [
@@ -63,6 +63,12 @@ try {
63
63
  type: 'array',
64
64
  describe: 'Glob pattern(s) to find which files/dirs to remove',
65
65
  },
66
+ all: {
67
+ alias: 'a',
68
+ type: 'boolean',
69
+ default: false,
70
+ describe: 'Include dotfiles (files starting with a dot) when matching glob patterns',
71
+ },
66
72
  stat: {
67
73
  alias: 's',
68
74
  default: false,
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
- import type { GlobOptions } from 'node:fs';
2
- import { existsSync, globSync, rmSync, statSync, unlinkSync } from 'node:fs';
1
+ import { existsSync, rmSync, statSync, unlinkSync } from 'node:fs';
3
2
  import { resolve } from 'node:path';
4
3
  import type { RemoveOptions } from './interfaces.js';
5
- import { throwOrCallback } from './utils.js';
4
+ import { throwOrCallback, getMatchedFiles } from './utils.js';
6
5
 
7
6
  /**
8
7
  * Remove the files or directories, the item(s) can be provided via positional arguments or via a `--glob` pattern.
@@ -35,19 +34,7 @@ export function removeSync(opts: RemoveOptions = {}, callback?: (e?: Error) => v
35
34
  // paths is always an array now
36
35
  const requiresCwdChange = !!(paths.length && opts.cwd);
37
36
  if (!paths.length && opts.glob) {
38
- // Use fs.globSync to match files. Dotfiles and dot-directories are always included.
39
- const defaultExclude = ['**/.git/**', '**/.git', '**/node_modules/**', '**/node_modules'];
40
- const globOptions: GlobOptions = { cwd: opts.cwd, withFileTypes: false };
41
- globOptions.exclude = Array.isArray(opts.exclude) ? opts.exclude : defaultExclude;
42
-
43
- const globResult = globSync(opts.glob, globOptions);
44
- // We reassign 'paths' here to hold the final list of files matched by glob,
45
- // after filtering for strings and resolving to absolute paths if needed.
46
- paths =
47
- Array.isArray(globResult) && globResult.length > 0 ? (globResult as unknown[]).filter((v): v is string => typeof v === 'string') : [];
48
- if (opts.cwd) {
49
- paths = paths.map(p => resolve(opts.cwd as string, p));
50
- }
37
+ paths = getMatchedFiles(opts.glob, { cwd: opts.cwd, exclude: opts.exclude, all: opts.all });
51
38
  }
52
39
 
53
40
  if (opts.stat || opts.verbose) {
@@ -64,7 +51,7 @@ export function removeSync(opts: RemoveOptions = {}, callback?: (e?: Error) => v
64
51
 
65
52
  if (existsSync(path)) {
66
53
  const isDir = statSync(path).isDirectory();
67
- const pathLog = `${isDir ? 'directory' : 'file'}: ${path}`;
54
+ const pathLog = `${isDir ? 'directory recursively' : 'file'}: ${path}`;
68
55
 
69
56
  if (opts.dryRun) {
70
57
  console.log(`would remove ${pathLog}`);
package/src/interfaces.ts CHANGED
@@ -33,4 +33,9 @@ export interface RemoveOptions {
33
33
  * To disable exclusion and allow deleting everything, pass an empty array ([]).
34
34
  */
35
35
  exclude?: string | string[];
36
+
37
+ /**
38
+ * If true, include dotfiles (files starting with a dot) when matching glob patterns.
39
+ */
40
+ all?: boolean;
36
41
  }
package/src/utils.ts CHANGED
@@ -1,3 +1,70 @@
1
+ import type { GlobOptions } from 'node:fs';
2
+ import { globSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+
5
+ /**
6
+ * Helper to get all matched files from glob patterns, supporting dotfile logic via opts.all
7
+ */
8
+ export function getMatchedFiles(glob: string | string[], opts: { cwd?: string; exclude?: string | string[]; all?: boolean }): string[] {
9
+ const defaultExclude = ['**/.git/**', '**/.git', '**/node_modules/**', '**/node_modules'];
10
+ const globOptions: GlobOptions = { cwd: opts.cwd, withFileTypes: false, exclude: defaultExclude };
11
+ if (Array.isArray(opts.exclude)) {
12
+ globOptions.exclude = opts.exclude;
13
+ } else if (typeof opts.exclude === 'string') {
14
+ globOptions.exclude = [opts.exclude];
15
+ }
16
+
17
+ // Separate positive and negated patterns
18
+ const patterns = Array.isArray(glob) ? glob : [glob];
19
+ const positivePatterns: string[] = [];
20
+ const negatedPatterns: string[] = [];
21
+ for (const pat of patterns) {
22
+ if (typeof pat === 'string' && pat.startsWith('!')) {
23
+ negatedPatterns.push(pat.slice(1));
24
+ } else {
25
+ positivePatterns.push(pat);
26
+ // Dotfile logic for positive patterns
27
+ if (opts.all && typeof pat === 'string' && pat.includes('*') && !pat.startsWith('.')) {
28
+ const dotPattern = pat.replace(/(\*)/g, '.*');
29
+ if (dotPattern !== pat) {
30
+ positivePatterns.push(dotPattern);
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ // Collect all files matching positive patterns
37
+ const matchedSet = new Set<string>();
38
+ for (const pattern of positivePatterns) {
39
+ const globResult = globSync(pattern, globOptions);
40
+ if (Array.isArray(globResult) && globResult.length > 0) {
41
+ for (const v of globResult) {
42
+ if (typeof v === 'string') {
43
+ matchedSet.add(v);
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ // Remove files matching any negated pattern
50
+ for (const pattern of negatedPatterns) {
51
+ const globResult = globSync(pattern, globOptions);
52
+ if (Array.isArray(globResult) && globResult.length > 0) {
53
+ for (const v of globResult) {
54
+ if (typeof v === 'string') {
55
+ matchedSet.delete(v);
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ let paths = Array.from(matchedSet);
62
+ if (opts.cwd) {
63
+ paths = paths.map(p => resolve(opts.cwd as string, p));
64
+ }
65
+ return paths;
66
+ }
67
+
1
68
  /** Helper to throw or callback with error */
2
69
  export function throwOrCallback(err?: Error, cb?: (e?: Error) => void) {
3
70
  if (typeof cb === 'function') {