remove-glob 1.1.0 → 1.2.0

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
@@ -29,25 +29,46 @@ A `remove` binary is available, it takes an optional path argument (zero or mult
29
29
  > The `paths` and `glob` arguments are both optionals, but you **must** provide at least 1 of them.
30
30
  > 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
31
 
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).
32
+ 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
33
 
35
34
  ```
36
35
  Usage:
37
- remove [paths..] [options] Remove all items recursively
36
+ remove [paths..] [options]Remove all items recursively
38
37
 
39
38
  Arguments:
40
- paths directory or file paths to remove [string..]
39
+ paths Directory or file paths to remove [string..]
41
40
 
42
41
  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
- --exclude Glob pattern(s) to exclude from deletion (overrides the default: ["**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"]) [array]
49
- -h, --help Show help [boolean]
50
- -v, --version Show version number [boolean]
42
+ --cwd Directory to resolve from (default ".") [string]
43
+ -d, --dry-run Show which files/dirs would be deleted but without actually removing them [boolean]
44
+ -g, --glob Glob pattern(s) to find which files/dirs to remove [array]
45
+ -a, --all Include dotfiles (files starting with a dot) when matching glob patterns [boolean]
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 Glob pattern(s) to exclude from deletion (overrides the default patterns) [array]
49
+ -h, --help Show help [boolean]
50
+ -v, --version Show version number [boolean]
51
+ ```
52
+
53
+ When `exclude` glob pattern(s) are provided, it will override the default exclude of: [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`].
54
+
55
+
56
+ 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.
57
+
58
+ #### Negation Patterns
59
+ You can use negation patterns (starting with `!`) in glob arrays to exclude files from removal:
60
+
61
+ ```sh
62
+ $ npx remove --glob "src/**/*.js" --glob "!src/**/*.test.js"
63
+ # This will remove all .js files except those ending with .test.js
64
+ ```
65
+
66
+ #### Brace Expansion
67
+ Brace expansion is supported in glob patterns:
68
+
69
+ ```sh
70
+ $ npx remove --glob "src/*.{js,ts}"
71
+ # This will match both .js and .ts files in src/
51
72
  ```
52
73
 
53
74
  Remove files or directories. Note: on Windows globs must be **double quoted**, everybody else can quote however they please.
@@ -66,16 +87,14 @@ $ remove foo bar
66
87
  $ remove --glob \"dist/**/*.{js,map}\"
67
88
  ```
68
89
 
69
- > [!NOTE]
70
- > When using the `--glob` option, it will skip `.git/` and `node_modules/` directories by default (using the default `--exclude` patterns).
71
- > 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):
72
- >
73
- > ```sh
74
- > # Remove everything, including .git and node_modules
75
- > $ npx remove --glob "**/*" --exclude ""
76
- > # Or exclude only .git, but allow node_modules to be deleted
77
- > $ npx remove --glob "**/*" --exclude "**/.git/**" --exclude "**/.git"
78
- > ```
90
+ When using the `--glob` option, it will skip `.git/` and `node_modules/` directories by default (using the default `--exclude` patterns). 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):
91
+
92
+ ```sh
93
+ # Remove everything, including .git and node_modules
94
+ $ npx remove --glob "**/*" --exclude ""
95
+ # Or exclude only .git, but allow node_modules to be deleted
96
+ $ npx remove --glob "**/*" --exclude "**/.git/**" --exclude "**/.git"
97
+ ```
79
98
 
80
99
  ### Usage
81
100
 
@@ -108,17 +127,27 @@ The first argument is an object holding any of the options shown below. The last
108
127
  ```js
109
128
  {
110
129
  cwd: string; // directory to resolve your `filepath` from, defaults to `process.cwd()`
111
- dryRun: boolean; // show what would be copied, without actually copying anything
130
+ dryRun: boolean; // show what would be removed, without actually removing anything
112
131
  paths: string | string[]; // filepath(s) to remove – may be a file or a directory.
113
- glob: string; // glob pattern to find which files/directories to remove
114
- exclude: string[]; // glob pattern(s) to exclude from deletion (default: ["**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"])
115
- stats: boolean; // show some statistics after execution (time + file count)
132
+ glob: string | string[]; // glob pattern(s) to find which files/directories to remove
133
+ exclude: string | string[]; // glob pattern(s) to exclude from deletion
134
+ all: boolean; // include dotfiles (files starting with a dot) in glob matches
135
+ stat: boolean; // show some statistics after execution (time + file count)
116
136
  verbose: boolean; // print more information to console when executing the removal
117
137
  }
118
138
  ```
119
139
 
140
+
120
141
  > [!NOTE]
121
- > The first argument is required and it **must** include either a `paths` or a `glob`, but it cannot include both options simultaneously.
142
+ > The first argument is required and it **must** include either a `paths` or a `glob`, but it cannot include both options simultaneously. A
143
+
144
+ When an `exclude` glob pattern is provided, it will override the default exclusion of: [`"**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"`].
145
+
146
+ #### Advanced glob usage
147
+
148
+ - **Negation**: Use `!pattern` in a glob array to exclude files from removal.
149
+ - **Brace expansion**: Use `{a,b}` to match multiple alternatives in a single pattern.
150
+ - **Dotfiles**: Use `--all`/`-a` to include dotfiles in glob matches.
122
151
 
123
152
  ### Used by
124
153
 
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,
@@ -71,8 +77,9 @@ try {
71
77
  describe: 'If true, it will log each file or directory being removed',
72
78
  },
73
79
  exclude: {
80
+ alias: 'e',
74
81
  type: 'array',
75
- describe: 'Glob pattern(s) to exclude from deletion (overrides the default: ["**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"])',
82
+ describe: 'Glob pattern(s) to exclude from deletion (overrides the default patterns)',
76
83
  },
77
84
  },
78
85
  version: readPackage().version,
@@ -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.0",
3
+ "version": "1.2.0",
4
4
  "description": "A tiny utility to remove items or directories recursively, also supports glob",
5
5
  "bin": {
6
6
  "remove": "dist/cli.js"
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,
@@ -76,9 +82,9 @@ try {
76
82
  describe: 'If true, it will log each file or directory being removed',
77
83
  },
78
84
  exclude: {
85
+ alias: 'e',
79
86
  type: 'array',
80
- describe:
81
- 'Glob pattern(s) to exclude from deletion (overrides the default: ["**/.git/**", "**/.git", "**/node_modules/**", "**/node_modules"])',
87
+ describe: 'Glob pattern(s) to exclude from deletion (overrides the default patterns)',
82
88
  },
83
89
  },
84
90
  version: readPackage().version,
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') {