stdin-glob 1.2.0 → 1.3.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
@@ -30,6 +30,7 @@ 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
33
34
  - Written in TypeScript
34
35
 
35
36
  ## Installation
@@ -221,6 +222,17 @@ Output:
221
222
  /home/pedrito/project/src/types/index.ts
222
223
  ```
223
224
 
225
+ ### Binary file handling
226
+
227
+ When encountering binary files (like images, compiled binaries, etc.), the tool safely displays metadata instead of attempting to show unreadable content:
228
+
229
+ ````
230
+ ```png
231
+ // assets/logo.png
232
+ // [BINARY FILE] - Size: 0.024 MB
233
+ ```
234
+ ````
235
+
224
236
  ### Integration with other commands
225
237
 
226
238
  Use with grep to search for specific content:
package/dist/index.js CHANGED
@@ -9,6 +9,8 @@ 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 binary_1 = require("./utils/binary");
13
+ const pipes_1 = require("./utils/pipes");
12
14
  const program = new commander_1.Command();
13
15
  program
14
16
  .name('stdin-glob')
@@ -72,21 +74,6 @@ const findMaxConsecutiveBackticks = (str) => {
72
74
  return 0;
73
75
  return Math.max(...matches.map((m) => m.length));
74
76
  };
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
77
  /**
91
78
  * Get file content with markdown format
92
79
  * @param filePath - The path to the file
@@ -96,13 +83,28 @@ const addLineNumbers = (content, startLine = 1) => {
96
83
  */
97
84
  const getFileContent = async (filePath, maxLines, showLineNumbers) => {
98
85
  try {
99
- const content = await (0, promises_1.readFile)(filePath, 'utf-8');
86
+ const fileBuffer = await (0, promises_1.readFile)(filePath);
87
+ if ((0, binary_1.isBinaryFile)(fileBuffer)) {
88
+ // is binary!! not show content
89
+ const stats = await (0, promises_1.stat)(filePath);
90
+ const fileSize = stats.size;
91
+ const extension = path_1.default.extname(filePath).replace('.', '');
92
+ const sizeFormatted = (0, pipes_1.formatSizeInMB)(fileSize);
93
+ return ('```' +
94
+ extension +
95
+ '\n' +
96
+ `// ${filePath}\n` +
97
+ `// [BINARY FILE] - Size: ${sizeFormatted}\n` +
98
+ '```\n\n');
99
+ }
100
+ // is text file!! proceed with normal processing
101
+ const content = fileBuffer.toString('utf-8');
100
102
  const lines = content.split('\n');
101
103
  // maxLines if exists
102
104
  const linesToShow = maxLines ? lines.slice(0, maxLines) : lines;
103
105
  let contentToShow = linesToShow.join('\n');
104
106
  if (showLineNumbers)
105
- contentToShow = addLineNumbers(linesToShow.join('\n'), 1);
107
+ contentToShow = (0, pipes_1.addLineNumbers)(linesToShow.join('\n'), 1);
106
108
  const extension = path_1.default.extname(filePath).replace('.', '');
107
109
  const maxBackticks = findMaxConsecutiveBackticks(content);
108
110
  const wrapper = '`'.repeat(Math.max(3, maxBackticks + 1));
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check if a file is binary
3
+ * @see https://gist.github.com/magnetikonline/7a21ec5f5bcdbf7adb92f9d617e6198f
4
+ */
5
+ export declare const isBinaryFile: (buffer: Buffer) => boolean;
6
+ //# sourceMappingURL=binary.d.ts.map
@@ -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,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.2.0",
3
+ "version": "1.3.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",
@@ -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
@@ -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
- }