stdin-glob 1.2.0 → 1.4.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 +40 -9
- package/dist/index.js +58 -21
- package/dist/utils/binary.d.ts +6 -0
- package/dist/utils/binary.js +20 -0
- package/dist/utils/gitignore.d.ts +10 -0
- package/dist/utils/gitignore.js +66 -0
- package/dist/utils/pipes.d.ts +9 -0
- package/dist/utils/pipes.js +28 -0
- package/package.json +7 -2
- package/.github/workflows/npm-publish.yml +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/src/index.ts +0 -164
- package/tsconfig.json +0 -19
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ This pipes all relevant TypeScript/TSX files directly into my clipboard, ready t
|
|
|
30
30
|
- **Auto-copy output** directly to clipboard with `--copy` flag
|
|
31
31
|
- Support for absolute or relative paths
|
|
32
32
|
- Option to show only file paths without content
|
|
33
|
+
- **Intelligent handling of binary files** - shows metadata instead of attempting to display unreadable content
|
|
34
|
+
- **Automatic .gitignore filtering** - respects your project's ignore rules by default
|
|
33
35
|
- Written in TypeScript
|
|
34
36
|
|
|
35
37
|
## Installation
|
|
@@ -46,15 +48,16 @@ stdin-glob [options] [patterns...]
|
|
|
46
48
|
|
|
47
49
|
### Options
|
|
48
50
|
|
|
49
|
-
| Option | Description
|
|
50
|
-
| --------------------- |
|
|
51
|
-
| `--no-content` | Do not show file contents, only list matching paths
|
|
52
|
-
| `--absolute` | Show absolute paths for entries
|
|
53
|
-
| `-c, --copy` | Copy the output to clipboard instead of printing to console
|
|
54
|
-
| `-m, --max-lines <n>` | Show only the first N lines of each file (shows full file if omitted)
|
|
55
|
-
| `-n, --line-numbers` | Display line numbers next to each line, like in IDE sidebars
|
|
56
|
-
|
|
|
57
|
-
| `-
|
|
51
|
+
| Option | Description |
|
|
52
|
+
| --------------------- | --------------------------------------------------------------------------- |
|
|
53
|
+
| `--no-content` | Do not show file contents, only list matching paths |
|
|
54
|
+
| `--absolute` | Show absolute paths for entries |
|
|
55
|
+
| `-c, --copy` | Copy the output to clipboard instead of printing to console |
|
|
56
|
+
| `-m, --max-lines <n>` | Show only the first N lines of each file (shows full file if omitted) |
|
|
57
|
+
| `-n, --line-numbers` | Display line numbers next to each line, like in IDE sidebars |
|
|
58
|
+
| `--no-gitignore` | Disable .gitignore filtering (include files that would normally be ignored) |
|
|
59
|
+
| `-V, --version` | Output the version number |
|
|
60
|
+
| `-h, --help` | Display help information |
|
|
58
61
|
|
|
59
62
|
### Arguments
|
|
60
63
|
|
|
@@ -221,6 +224,17 @@ Output:
|
|
|
221
224
|
/home/pedrito/project/src/types/index.ts
|
|
222
225
|
```
|
|
223
226
|
|
|
227
|
+
### Binary file handling
|
|
228
|
+
|
|
229
|
+
When encountering binary files (like images, compiled binaries, etc.), the tool safely displays metadata instead of attempting to show unreadable content:
|
|
230
|
+
|
|
231
|
+
````
|
|
232
|
+
```png
|
|
233
|
+
// assets/logo.png
|
|
234
|
+
// [BINARY FILE] - Size: 0.024 MB
|
|
235
|
+
```
|
|
236
|
+
````
|
|
237
|
+
|
|
224
238
|
### Integration with other commands
|
|
225
239
|
|
|
226
240
|
Use with grep to search for specific content:
|
|
@@ -239,3 +253,20 @@ stdin-glob "src/**/*.ts" --no-content | pbcopy
|
|
|
239
253
|
stdin-glob "src/**/*.ts" --content
|
|
240
254
|
stdin-glob "src/**/*.ts" --copy
|
|
241
255
|
```
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
### .gitignore Support
|
|
259
|
+
|
|
260
|
+
By default, `stdin-glob` automatically respects your project's `.gitignore` rules. This means files and directories listed in `.gitignore` won't appear in the output. This is especially useful when you want to avoid including build artifacts, dependencies, or environment files in your context.
|
|
261
|
+
|
|
262
|
+
The gitignore pattern matching implementation is based on the official [gitignore pattern format documentation](https://git-scm.com/docs/gitignore#_pattern_format), ensuring compatibility with how git itself handles ignore rules.
|
|
263
|
+
|
|
264
|
+
#### Including ignored files
|
|
265
|
+
|
|
266
|
+
If you need to include files that would normally be ignored, use the `--no-gitignore` flag:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
stdin-glob "dist/**/*.js" --no-gitignore
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
This disables all `.gitignore` filtering and includes every file matching your patterns.
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,10 @@ const promises_1 = require("fs/promises");
|
|
|
9
9
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const clipboardy_1 = __importDefault(require("clipboardy"));
|
|
12
|
+
const ignore_1 = __importDefault(require("ignore"));
|
|
13
|
+
const binary_1 = require("./utils/binary");
|
|
14
|
+
const pipes_1 = require("./utils/pipes");
|
|
15
|
+
const gitignore_1 = require("./utils/gitignore");
|
|
12
16
|
const program = new commander_1.Command();
|
|
13
17
|
program
|
|
14
18
|
.name('stdin-glob')
|
|
@@ -23,25 +27,35 @@ program
|
|
|
23
27
|
return parseInt(value);
|
|
24
28
|
})
|
|
25
29
|
.option('-n, --line-numbers', 'Show line numbers next to each line, like in IDE sidebars', false)
|
|
30
|
+
.option('--no-gitignore', 'Disable .gitignore filtering (include files that would normally be ignored)')
|
|
26
31
|
.argument('[patterns...]', 'Glob patterns to match files')
|
|
27
32
|
.action(async (patterns, options) => {
|
|
28
33
|
if (patterns.length === 0) {
|
|
29
34
|
console.error('Error: No patterns provided.');
|
|
30
35
|
process.exit(1);
|
|
31
36
|
}
|
|
32
|
-
//
|
|
33
|
-
|
|
37
|
+
// Expand glob
|
|
38
|
+
let files = await (0, fast_glob_1.default)(patterns, {
|
|
34
39
|
onlyFiles: true,
|
|
35
|
-
|
|
40
|
+
dot: true,
|
|
41
|
+
absolute: false,
|
|
36
42
|
});
|
|
37
43
|
if (files.length === 0) {
|
|
38
44
|
console.error('No files matched the given patterns.');
|
|
39
45
|
process.exit(1);
|
|
40
46
|
}
|
|
47
|
+
// Apply gitignore filtering if not disabled
|
|
48
|
+
if (options.gitignore) {
|
|
49
|
+
files = await filterByGitignore(files);
|
|
50
|
+
if (files.length === 0) {
|
|
51
|
+
console.error('No files remained after applying .gitignore rules.');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
41
55
|
let output = '';
|
|
42
56
|
for (const file of files) {
|
|
43
57
|
if (options.content) {
|
|
44
|
-
const fileOutput = await getFileContent(file, options.maxLines ?? undefined, options.lineNumbers ?? false);
|
|
58
|
+
const fileOutput = await getFileContent(options.absolute ? path_1.default.join(process.cwd(), file) : file, options.maxLines ?? undefined, options.lineNumbers ?? false);
|
|
45
59
|
output += fileOutput;
|
|
46
60
|
}
|
|
47
61
|
else {
|
|
@@ -63,6 +77,29 @@ program
|
|
|
63
77
|
}
|
|
64
78
|
});
|
|
65
79
|
program.parse(process.argv);
|
|
80
|
+
/**
|
|
81
|
+
* Filter files by .gitignore rules recursively
|
|
82
|
+
*/
|
|
83
|
+
const filterByGitignore = async (files) => {
|
|
84
|
+
try {
|
|
85
|
+
// Find all .gitignore files in the project
|
|
86
|
+
const gitignoreFiles = await (0, gitignore_1.findGitignoreFiles)();
|
|
87
|
+
if (gitignoreFiles.length === 0) {
|
|
88
|
+
return files;
|
|
89
|
+
}
|
|
90
|
+
// Create ignore instance with all rules
|
|
91
|
+
const ig = (0, ignore_1.default)().add(await (0, gitignore_1.loadGitignoreRules)(gitignoreFiles));
|
|
92
|
+
return files.filter((file) => {
|
|
93
|
+
// Get relative path from the root where gitignore rules apply
|
|
94
|
+
const relativePath = path_1.default.relative(process.cwd(), file);
|
|
95
|
+
return !ig.ignores(relativePath);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.warn('Warning: Error processing .gitignore files, proceeding without filtering:', error);
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
66
103
|
/**
|
|
67
104
|
* Find the maximum number of consecutive backticks in a string
|
|
68
105
|
*/
|
|
@@ -72,21 +109,6 @@ const findMaxConsecutiveBackticks = (str) => {
|
|
|
72
109
|
return 0;
|
|
73
110
|
return Math.max(...matches.map((m) => m.length));
|
|
74
111
|
};
|
|
75
|
-
/**
|
|
76
|
-
* Add line numbers to content
|
|
77
|
-
*/
|
|
78
|
-
const addLineNumbers = (content, startLine = 1) => {
|
|
79
|
-
const lines = content.split('\n');
|
|
80
|
-
// calculate width of bar
|
|
81
|
-
const paddingWidth = (startLine + lines.length - 1).toString().length;
|
|
82
|
-
return lines
|
|
83
|
-
.map((line, index) => {
|
|
84
|
-
const lineNumber = startLine + index;
|
|
85
|
-
const paddedNumber = lineNumber.toString().padStart(paddingWidth, ' ');
|
|
86
|
-
return `${paddedNumber} | ${line}`;
|
|
87
|
-
})
|
|
88
|
-
.join('\n');
|
|
89
|
-
};
|
|
90
112
|
/**
|
|
91
113
|
* Get file content with markdown format
|
|
92
114
|
* @param filePath - The path to the file
|
|
@@ -96,13 +118,28 @@ const addLineNumbers = (content, startLine = 1) => {
|
|
|
96
118
|
*/
|
|
97
119
|
const getFileContent = async (filePath, maxLines, showLineNumbers) => {
|
|
98
120
|
try {
|
|
99
|
-
const
|
|
121
|
+
const fileBuffer = await (0, promises_1.readFile)(filePath);
|
|
122
|
+
if ((0, binary_1.isBinaryFile)(fileBuffer)) {
|
|
123
|
+
// is binary!! not show content
|
|
124
|
+
const stats = await (0, promises_1.stat)(filePath);
|
|
125
|
+
const fileSize = stats.size;
|
|
126
|
+
const extension = path_1.default.extname(filePath).replace('.', '');
|
|
127
|
+
const sizeFormatted = (0, pipes_1.formatSizeInMB)(fileSize);
|
|
128
|
+
return ('```' +
|
|
129
|
+
extension +
|
|
130
|
+
'\n' +
|
|
131
|
+
`// ${filePath}\n` +
|
|
132
|
+
`// [BINARY FILE] - Size: ${sizeFormatted}\n` +
|
|
133
|
+
'```\n\n');
|
|
134
|
+
}
|
|
135
|
+
// is text file!! proceed with normal processing
|
|
136
|
+
const content = fileBuffer.toString('utf-8');
|
|
100
137
|
const lines = content.split('\n');
|
|
101
138
|
// maxLines if exists
|
|
102
139
|
const linesToShow = maxLines ? lines.slice(0, maxLines) : lines;
|
|
103
140
|
let contentToShow = linesToShow.join('\n');
|
|
104
141
|
if (showLineNumbers)
|
|
105
|
-
contentToShow = addLineNumbers(linesToShow.join('\n'), 1);
|
|
142
|
+
contentToShow = (0, pipes_1.addLineNumbers)(linesToShow.join('\n'), 1);
|
|
106
143
|
const extension = path_1.default.extname(filePath).replace('.', '');
|
|
107
144
|
const maxBackticks = findMaxConsecutiveBackticks(content);
|
|
108
145
|
const wrapper = '`'.repeat(Math.max(3, maxBackticks + 1));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isBinaryFile = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Check if a file is binary
|
|
6
|
+
* @see https://gist.github.com/magnetikonline/7a21ec5f5bcdbf7adb92f9d617e6198f
|
|
7
|
+
*/
|
|
8
|
+
const isBinaryFile = (buffer) => {
|
|
9
|
+
// check for null bytes or control characters that indicate binary
|
|
10
|
+
for (let i = 0; i < Math.min(buffer.length, 512); i++) {
|
|
11
|
+
const byte = buffer[i];
|
|
12
|
+
// Null byte or control character
|
|
13
|
+
if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
};
|
|
19
|
+
exports.isBinaryFile = isBinaryFile;
|
|
20
|
+
//# sourceMappingURL=binary.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find all .gitignore files in the project
|
|
3
|
+
*/
|
|
4
|
+
export declare const findGitignoreFiles: () => Promise<string[]>;
|
|
5
|
+
/**
|
|
6
|
+
* Load and combine rules from recursive/multiple .gitignore files
|
|
7
|
+
* @see https://git-scm.com/docs/gitignore#_pattern_format
|
|
8
|
+
*/
|
|
9
|
+
export declare const loadGitignoreRules: (gitignoreFiles: string[]) => Promise<string[]>;
|
|
10
|
+
//# sourceMappingURL=gitignore.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadGitignoreRules = exports.findGitignoreFiles = void 0;
|
|
7
|
+
const promises_1 = require("fs/promises");
|
|
8
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
/**
|
|
11
|
+
* Find all .gitignore files in the project
|
|
12
|
+
*/
|
|
13
|
+
const findGitignoreFiles = async () => {
|
|
14
|
+
try {
|
|
15
|
+
const files = await (0, fast_glob_1.default)('**/.gitignore', {
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
ignore: ['**/node_modules/**'],
|
|
18
|
+
});
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.warn('Warning: Error finding .gitignore files:', error);
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
exports.findGitignoreFiles = findGitignoreFiles;
|
|
27
|
+
/**
|
|
28
|
+
* Parse a .gitignore file and return its rules
|
|
29
|
+
*/
|
|
30
|
+
const parseGitignore = (content) => {
|
|
31
|
+
return content
|
|
32
|
+
.split('\n')
|
|
33
|
+
.map((line) => line.trim())
|
|
34
|
+
.filter((line) => line && // Not empty
|
|
35
|
+
!line.startsWith('#'));
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Load and combine rules from recursive/multiple .gitignore files
|
|
39
|
+
* @see https://git-scm.com/docs/gitignore#_pattern_format
|
|
40
|
+
*/
|
|
41
|
+
const loadGitignoreRules = async (gitignoreFiles) => {
|
|
42
|
+
const allRules = [];
|
|
43
|
+
for (const file of gitignoreFiles) {
|
|
44
|
+
try {
|
|
45
|
+
const content = await (0, promises_1.readFile)(file, 'utf-8');
|
|
46
|
+
const rules = parseGitignore(content);
|
|
47
|
+
// directory of this .gitignore
|
|
48
|
+
const gitignoreDir = path_1.default.dirname(file);
|
|
49
|
+
// to be relative to the gitignore location
|
|
50
|
+
const dirRelativeRules = rules.map((rule) => {
|
|
51
|
+
//handle negations
|
|
52
|
+
if (rule.startsWith('!')) {
|
|
53
|
+
return `!${path_1.default.join(gitignoreDir, rule.slice(1))}`;
|
|
54
|
+
}
|
|
55
|
+
return path_1.default.join(gitignoreDir, rule);
|
|
56
|
+
});
|
|
57
|
+
allRules.push(...dirRelativeRules);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn(`Warning: Error reading .gitignore file ${file}:`, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return allRules;
|
|
64
|
+
};
|
|
65
|
+
exports.loadGitignoreRules = loadGitignoreRules;
|
|
66
|
+
//# sourceMappingURL=gitignore.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add line numbers to content
|
|
3
|
+
*/
|
|
4
|
+
export declare const addLineNumbers: (content: string, startLine?: number) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Format file size in MB with 3 decimal places
|
|
7
|
+
*/
|
|
8
|
+
export declare const formatSizeInMB: (bytes: number) => string;
|
|
9
|
+
//# sourceMappingURL=pipes.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatSizeInMB = exports.addLineNumbers = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Add line numbers to content
|
|
6
|
+
*/
|
|
7
|
+
const addLineNumbers = (content, startLine = 1) => {
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
// calculate width of bar
|
|
10
|
+
const paddingWidth = (startLine + lines.length - 1).toString().length;
|
|
11
|
+
return lines
|
|
12
|
+
.map((line, index) => {
|
|
13
|
+
const lineNumber = startLine + index;
|
|
14
|
+
const paddedNumber = lineNumber.toString().padStart(paddingWidth, ' ');
|
|
15
|
+
return `${paddedNumber} | ${line}`;
|
|
16
|
+
})
|
|
17
|
+
.join('\n');
|
|
18
|
+
};
|
|
19
|
+
exports.addLineNumbers = addLineNumbers;
|
|
20
|
+
/**
|
|
21
|
+
* Format file size in MB with 3 decimal places
|
|
22
|
+
*/
|
|
23
|
+
const formatSizeInMB = (bytes) => {
|
|
24
|
+
const mb = bytes / (1024 * 1024);
|
|
25
|
+
return mb.toFixed(3) + ' MB';
|
|
26
|
+
};
|
|
27
|
+
exports.formatSizeInMB = formatSizeInMB;
|
|
28
|
+
//# sourceMappingURL=pipes.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stdin-glob",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Expand glob patterns and output file contents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"prepare": "npm run build",
|
|
13
13
|
"prepublishOnly": "npm run build"
|
|
14
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/**/*.js",
|
|
17
|
+
"dist/**/*.d.ts"
|
|
18
|
+
],
|
|
15
19
|
"keywords": [
|
|
16
20
|
"glob",
|
|
17
21
|
"stdin",
|
|
@@ -32,7 +36,8 @@
|
|
|
32
36
|
"dependencies": {
|
|
33
37
|
"clipboardy": "^5.3.1",
|
|
34
38
|
"commander": "^11.1.0",
|
|
35
|
-
"fast-glob": "^3.3.3"
|
|
39
|
+
"fast-glob": "^3.3.3",
|
|
40
|
+
"ignore": "^7.0.5"
|
|
36
41
|
},
|
|
37
42
|
"devDependencies": {
|
|
38
43
|
"@types/node": "^25.3.3",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: NPM Package
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
tags:
|
|
9
|
-
- 'v*'
|
|
10
|
-
|
|
11
|
-
permissions:
|
|
12
|
-
id-token: write # Required for OIDC
|
|
13
|
-
contents: read
|
|
14
|
-
|
|
15
|
-
jobs:
|
|
16
|
-
publish:
|
|
17
|
-
runs-on: ubuntu-latest
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v4
|
|
20
|
-
- uses: actions/setup-node@v4
|
|
21
|
-
with:
|
|
22
|
-
node-version: '24'
|
|
23
|
-
registry-url: 'https://registry.npmjs.org'
|
|
24
|
-
|
|
25
|
-
- run: npm install
|
|
26
|
-
- run: npm publish
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/src/index.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { version } from '../package.json';
|
|
3
|
-
import { readFile } from 'fs/promises';
|
|
4
|
-
import glob from 'fast-glob';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import clipboard from 'clipboardy';
|
|
7
|
-
|
|
8
|
-
interface Options {
|
|
9
|
-
content?: boolean;
|
|
10
|
-
absolute?: boolean;
|
|
11
|
-
copy?: boolean;
|
|
12
|
-
maxLines?: number;
|
|
13
|
-
lineNumbers?: boolean; // Nueva opción
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const program = new Command();
|
|
17
|
-
|
|
18
|
-
program
|
|
19
|
-
.name('stdin-glob')
|
|
20
|
-
.description('Expand glob patterns and output file contents and paths')
|
|
21
|
-
.version(version)
|
|
22
|
-
.option('--no-content', 'Do not show file contents, only list matching paths')
|
|
23
|
-
.option('--absolute', 'Show the absolute path for entries', false)
|
|
24
|
-
.option(
|
|
25
|
-
'-c, --copy',
|
|
26
|
-
'Copy the output to clipboard instead of printing to console',
|
|
27
|
-
false,
|
|
28
|
-
)
|
|
29
|
-
.option(
|
|
30
|
-
'-m, --max-lines <int>',
|
|
31
|
-
'Show a limited number of lines in the file. If you not provide a number of lines it will show the full file content.',
|
|
32
|
-
(value) => {
|
|
33
|
-
if (isNaN(parseInt(value))) throw new Error('Lines must be a number');
|
|
34
|
-
return parseInt(value);
|
|
35
|
-
},
|
|
36
|
-
)
|
|
37
|
-
.option(
|
|
38
|
-
'-n, --line-numbers',
|
|
39
|
-
'Show line numbers next to each line, like in IDE sidebars',
|
|
40
|
-
false,
|
|
41
|
-
)
|
|
42
|
-
.argument('[patterns...]', 'Glob patterns to match files')
|
|
43
|
-
.action(async (patterns: string[], options: Options) => {
|
|
44
|
-
if (patterns.length === 0) {
|
|
45
|
-
console.error('Error: No patterns provided.');
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
//expand glob
|
|
50
|
-
const files = await glob(patterns, {
|
|
51
|
-
onlyFiles: true,
|
|
52
|
-
absolute: options.absolute ?? false,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
if (files.length === 0) {
|
|
56
|
-
console.error('No files matched the given patterns.');
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let output = '';
|
|
61
|
-
|
|
62
|
-
for (const file of files) {
|
|
63
|
-
if (options.content) {
|
|
64
|
-
const fileOutput = await getFileContent(
|
|
65
|
-
file,
|
|
66
|
-
options.maxLines ?? undefined,
|
|
67
|
-
options.lineNumbers ?? false,
|
|
68
|
-
);
|
|
69
|
-
output += fileOutput;
|
|
70
|
-
} else {
|
|
71
|
-
output += file + '\n';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (options.copy) {
|
|
76
|
-
try {
|
|
77
|
-
await clipboard.write(output.trim());
|
|
78
|
-
console.log('-> Output copied to clipboard successfully!');
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error('-X Error copying to clipboard:', error);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
console.log(output.trim());
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
program.parse(process.argv);
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Find the maximum number of consecutive backticks in a string
|
|
92
|
-
*/
|
|
93
|
-
const findMaxConsecutiveBackticks = (str: string): number => {
|
|
94
|
-
const matches = str.match(/`+/g);
|
|
95
|
-
if (!matches) return 0;
|
|
96
|
-
return Math.max(...matches.map((m) => m.length));
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Add line numbers to content
|
|
101
|
-
*/
|
|
102
|
-
const addLineNumbers = (content: string, startLine: number = 1): string => {
|
|
103
|
-
const lines = content.split('\n');
|
|
104
|
-
|
|
105
|
-
// calculate width of bar
|
|
106
|
-
const paddingWidth = (startLine + lines.length - 1).toString().length;
|
|
107
|
-
|
|
108
|
-
return lines
|
|
109
|
-
.map((line, index) => {
|
|
110
|
-
const lineNumber = startLine + index;
|
|
111
|
-
const paddedNumber = lineNumber.toString().padStart(paddingWidth, ' ');
|
|
112
|
-
return `${paddedNumber} | ${line}`;
|
|
113
|
-
})
|
|
114
|
-
.join('\n');
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get file content with markdown format
|
|
119
|
-
* @param filePath - The path to the file
|
|
120
|
-
* @param maxLines - The number of lines to show. If you not provide a number of lines it will show the full file content.
|
|
121
|
-
* @param showLineNumbers - Whether to show line numbers
|
|
122
|
-
* @returns The file content with markdown format
|
|
123
|
-
*/
|
|
124
|
-
const getFileContent = async (
|
|
125
|
-
filePath: string,
|
|
126
|
-
maxLines?: number,
|
|
127
|
-
showLineNumbers?: boolean,
|
|
128
|
-
): Promise<string> => {
|
|
129
|
-
try {
|
|
130
|
-
const content = await readFile(filePath, 'utf-8');
|
|
131
|
-
const lines = content.split('\n');
|
|
132
|
-
|
|
133
|
-
// maxLines if exists
|
|
134
|
-
const linesToShow = maxLines ? lines.slice(0, maxLines) : lines;
|
|
135
|
-
let contentToShow = linesToShow.join('\n');
|
|
136
|
-
|
|
137
|
-
if (showLineNumbers)
|
|
138
|
-
contentToShow = addLineNumbers(linesToShow.join('\n'), 1);
|
|
139
|
-
|
|
140
|
-
const extension = path.extname(filePath).replace('.', '');
|
|
141
|
-
const maxBackticks = findMaxConsecutiveBackticks(content);
|
|
142
|
-
const wrapper = '`'.repeat(Math.max(3, maxBackticks + 1));
|
|
143
|
-
|
|
144
|
-
const truncation =
|
|
145
|
-
maxLines && lines.length > maxLines
|
|
146
|
-
? `\n// ... (${lines.length - maxLines} more lines truncated)`
|
|
147
|
-
: '';
|
|
148
|
-
|
|
149
|
-
return (
|
|
150
|
-
wrapper +
|
|
151
|
-
extension +
|
|
152
|
-
'\n' +
|
|
153
|
-
`// ${filePath}\n\n` +
|
|
154
|
-
contentToShow +
|
|
155
|
-
truncation +
|
|
156
|
-
'\n' +
|
|
157
|
-
wrapper +
|
|
158
|
-
'\n\n'
|
|
159
|
-
);
|
|
160
|
-
} catch (e) {
|
|
161
|
-
console.error(`Error reading file ${filePath}:`, e);
|
|
162
|
-
return '';
|
|
163
|
-
}
|
|
164
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"lib": ["ES2020"],
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"declaration": true,
|
|
13
|
-
"declarationMap": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"resolveJsonModule": true
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist"]
|
|
19
|
-
}
|