preetier-staged 0.0.1 → 0.0.2
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/.git-hooks/pre-commit +3 -1
- package/CHANGELOG.md +2 -0
- package/README.md +29 -1
- package/jest.config.js +38 -0
- package/package.json +12 -4
- package/src/index.js +36 -27
- package/tests/index.test.js +233 -0
package/.git-hooks/pre-commit
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.0.2](https://github.com/iamandersonp/preetier-staged/compare/v0.0.1...v0.0.2) (2026-04-01)
|
|
6
|
+
|
|
5
7
|
### 0.0.1 (2026-04-01)
|
|
6
8
|
|
|
7
9
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
An utitlty to auto format stagged files using prettier
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
To use as a dev dependency
|
|
8
8
|
|
|
@@ -39,3 +39,31 @@ if [ -n "$STAGED_FILES" ]; then
|
|
|
39
39
|
echo "$STAGED_FILES" | xargs git add
|
|
40
40
|
fi
|
|
41
41
|
```
|
|
42
|
+
|
|
43
|
+
## Testing
|
|
44
|
+
|
|
45
|
+
This project includes comprehensive unit tests with Jest. The tests cover all the main functionality and edge cases.
|
|
46
|
+
|
|
47
|
+
### Available test commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Run all tests
|
|
51
|
+
npm test
|
|
52
|
+
|
|
53
|
+
# Run tests in watch mode (automatically re-run on file changes)
|
|
54
|
+
npm run test:watch
|
|
55
|
+
|
|
56
|
+
# Run tests with coverage report
|
|
57
|
+
npm run test:coverage
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Test coverage
|
|
61
|
+
|
|
62
|
+
Current test coverage: **>95%** including:
|
|
63
|
+
|
|
64
|
+
- ✅ Successful file formatting scenarios
|
|
65
|
+
- ✅ No files to format scenarios
|
|
66
|
+
- ✅ Error handling (Prettier not found, syntax errors, general errors)
|
|
67
|
+
- ✅ Edge cases (files with spaces, whitespace trimming, all supported extensions)
|
|
68
|
+
|
|
69
|
+
The tests use mocks to simulate Git commands and Prettier execution without running actual commands, making tests fast and reliable.
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// Configuración Jest para Node.js CLI tool
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
|
|
5
|
+
// Patrones de archivos de test
|
|
6
|
+
testMatch: ['**/tests/**/*.test.js', '**/tests/**/*.spec.js', '**/__tests__/**/*.js'],
|
|
7
|
+
|
|
8
|
+
// Configuración de coverage
|
|
9
|
+
collectCoverageFrom: ['src/**/*.js', '!**/node_modules/**', '!**/tests/**'],
|
|
10
|
+
|
|
11
|
+
// Threshold mínimo de coverage
|
|
12
|
+
coverageThreshold: {
|
|
13
|
+
global: {
|
|
14
|
+
branches: 85,
|
|
15
|
+
functions: 85,
|
|
16
|
+
lines: 85,
|
|
17
|
+
statements: 85
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Formato de reportes de coverage
|
|
22
|
+
coverageReporters: ['text', 'html', 'lcov'],
|
|
23
|
+
|
|
24
|
+
// Directorio para reportes de coverage
|
|
25
|
+
coverageDirectory: 'coverage',
|
|
26
|
+
|
|
27
|
+
// Limpiar mocks automáticamente después de cada test
|
|
28
|
+
clearMocks: true,
|
|
29
|
+
|
|
30
|
+
// Restaurar mocks automáticamente después de cada test
|
|
31
|
+
restoreMocks: true,
|
|
32
|
+
|
|
33
|
+
// Configuración adicional para mejor output
|
|
34
|
+
verbose: true,
|
|
35
|
+
|
|
36
|
+
// Configuración de timeout para tests
|
|
37
|
+
testTimeout: 10000
|
|
38
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "preetier-staged",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"prettier-staged": "src/index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"test:watch": "jest --watch",
|
|
11
|
+
"test:coverage": "jest --coverage",
|
|
10
12
|
"prettier-staged": "src/index.js",
|
|
11
13
|
"release": "standard-version",
|
|
12
14
|
"setup:git-hooks": "git config core.hooksPath .git-hooks || true && chmod +x ./.git-hooks/*",
|
|
@@ -15,13 +17,19 @@
|
|
|
15
17
|
"release:major": "standard-version --release-as major",
|
|
16
18
|
"prepare": "npm run setup:git-hooks"
|
|
17
19
|
},
|
|
18
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"prettier",
|
|
22
|
+
"staged",
|
|
23
|
+
"git",
|
|
24
|
+
"hooks"
|
|
25
|
+
],
|
|
19
26
|
"author": "",
|
|
20
|
-
"license": "
|
|
27
|
+
"license": "GPL-3.0-only",
|
|
21
28
|
"description": "",
|
|
22
29
|
"devDependencies": {
|
|
23
30
|
"@commitlint/cli": "^20.5.0",
|
|
24
31
|
"@commitlint/config-conventional": "^20.5.0",
|
|
32
|
+
"jest": "^30.3.0",
|
|
25
33
|
"prettier": "^3.8.1",
|
|
26
34
|
"standard-version": "^9.5.0"
|
|
27
35
|
}
|
package/src/index.js
CHANGED
|
@@ -4,32 +4,41 @@ const { execSync } = require('node:child_process');
|
|
|
4
4
|
|
|
5
5
|
const extensions = /\.(html|ts|scss|css|json)$/;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
7
|
+
function runPrettierStaged() {
|
|
8
|
+
try {
|
|
9
|
+
const output = execSync('git diff --name-only --cached --diff-filter=ACM', {
|
|
10
|
+
encoding: 'utf-8'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const files = output
|
|
14
|
+
.split('\n')
|
|
15
|
+
.map((file) => file.trim())
|
|
16
|
+
.filter((file) => extensions.test(file));
|
|
17
|
+
|
|
18
|
+
if (files.length > 0) {
|
|
19
|
+
console.log('🧼 Formatting staged files with Prettier:');
|
|
20
|
+
execSync('npx prettier --write ' + files.join(' '), { stdio: 'inherit' });
|
|
21
|
+
|
|
22
|
+
// Re-staging
|
|
23
|
+
execSync(`git add ${files.join(' ')}`);
|
|
24
|
+
} else {
|
|
25
|
+
console.log('✅ No staged files matching for formatting.');
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.status === 127) {
|
|
29
|
+
console.error("❌ Error: Prettier not found. Make sure it's installed:", error.message);
|
|
30
|
+
} else if (error.status === 2) {
|
|
31
|
+
console.error('❌ Error: Prettier found syntax errors:', error.message);
|
|
32
|
+
} else {
|
|
33
|
+
console.error('❌ Error running Prettier:', error.message);
|
|
34
|
+
}
|
|
35
|
+
process.exit(1);
|
|
33
36
|
}
|
|
34
|
-
process.exit(1);
|
|
35
37
|
}
|
|
38
|
+
|
|
39
|
+
// Only run if this file is executed directly (not required as a module)
|
|
40
|
+
if (require.main === module) {
|
|
41
|
+
runPrettierStaged();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { runPrettierStaged };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const { execSync } = require('node:child_process');
|
|
2
|
+
|
|
3
|
+
// Mock completo del módulo child_process
|
|
4
|
+
jest.mock('node:child_process', () => ({
|
|
5
|
+
execSync: jest.fn()
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
const { runPrettierStaged } = require('../src/index.js');
|
|
9
|
+
|
|
10
|
+
describe('prettier-staged CLI', () => {
|
|
11
|
+
let consoleSpy, errorSpy, exitSpy;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Reset all mocks
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
|
|
17
|
+
// Setup console spies
|
|
18
|
+
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
19
|
+
errorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
20
|
+
exitSpy = jest.spyOn(process, 'exit').mockImplementation();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
// Restore all spies
|
|
25
|
+
consoleSpy.mockRestore();
|
|
26
|
+
errorSpy.mockRestore();
|
|
27
|
+
exitSpy.mockRestore();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Successful formatting', () => {
|
|
31
|
+
test('should format staged files with valid extensions', () => {
|
|
32
|
+
// Mock git diff to return files with valid extensions
|
|
33
|
+
execSync
|
|
34
|
+
.mockReturnValueOnce('src/app.ts\nstyles/main.scss\nconfig.json\n')
|
|
35
|
+
.mockReturnValueOnce(undefined) // prettier command
|
|
36
|
+
.mockReturnValueOnce(undefined); // git add command
|
|
37
|
+
|
|
38
|
+
// Execute the function
|
|
39
|
+
runPrettierStaged();
|
|
40
|
+
|
|
41
|
+
// Verify git diff was called
|
|
42
|
+
expect(execSync).toHaveBeenNthCalledWith(
|
|
43
|
+
1,
|
|
44
|
+
'git diff --name-only --cached --diff-filter=ACM',
|
|
45
|
+
{ encoding: 'utf-8' }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Verify prettier was called with correct files
|
|
49
|
+
expect(execSync).toHaveBeenNthCalledWith(
|
|
50
|
+
2,
|
|
51
|
+
'npx prettier --write src/app.ts styles/main.scss config.json',
|
|
52
|
+
{ stdio: 'inherit' }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Verify git add was called with formatted files
|
|
56
|
+
expect(execSync).toHaveBeenNthCalledWith(
|
|
57
|
+
3,
|
|
58
|
+
'git add src/app.ts styles/main.scss config.json'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Verify success message
|
|
62
|
+
expect(consoleSpy).toHaveBeenCalledWith('🧼 Formatting staged files with Prettier:');
|
|
63
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should handle single file with valid extension', () => {
|
|
67
|
+
execSync
|
|
68
|
+
.mockReturnValueOnce('index.html\n')
|
|
69
|
+
.mockReturnValueOnce(undefined)
|
|
70
|
+
.mockReturnValueOnce(undefined);
|
|
71
|
+
|
|
72
|
+
runPrettierStaged();
|
|
73
|
+
|
|
74
|
+
expect(execSync).toHaveBeenNthCalledWith(2, 'npx prettier --write index.html', {
|
|
75
|
+
stdio: 'inherit'
|
|
76
|
+
});
|
|
77
|
+
expect(consoleSpy).toHaveBeenCalledWith('🧼 Formatting staged files with Prettier:');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('No files to format', () => {
|
|
82
|
+
test('should show message when no staged files', () => {
|
|
83
|
+
execSync.mockReturnValueOnce('\n'); // Empty git diff output
|
|
84
|
+
|
|
85
|
+
runPrettierStaged();
|
|
86
|
+
|
|
87
|
+
expect(execSync).toHaveBeenCalledTimes(1);
|
|
88
|
+
expect(consoleSpy).toHaveBeenCalledWith('✅ No staged files matching for formatting.');
|
|
89
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('should show message when staged files have invalid extensions', () => {
|
|
93
|
+
execSync.mockReturnValueOnce('README.md\nscript.py\nimage.png\n');
|
|
94
|
+
|
|
95
|
+
runPrettierStaged();
|
|
96
|
+
|
|
97
|
+
expect(execSync).toHaveBeenCalledTimes(1);
|
|
98
|
+
expect(consoleSpy).toHaveBeenCalledWith('✅ No staged files matching for formatting.');
|
|
99
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should filter mixed files and format only valid ones', () => {
|
|
103
|
+
execSync
|
|
104
|
+
.mockReturnValueOnce('README.md\nsrc/app.ts\nimage.png\nstyle.css\n')
|
|
105
|
+
.mockReturnValueOnce(undefined)
|
|
106
|
+
.mockReturnValueOnce(undefined);
|
|
107
|
+
|
|
108
|
+
runPrettierStaged();
|
|
109
|
+
|
|
110
|
+
expect(execSync).toHaveBeenNthCalledWith(2, 'npx prettier --write src/app.ts style.css', {
|
|
111
|
+
stdio: 'inherit'
|
|
112
|
+
});
|
|
113
|
+
expect(consoleSpy).toHaveBeenCalledWith('🧼 Formatting staged files with Prettier:');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Error handling', () => {
|
|
118
|
+
test('should handle Prettier not found error (status 127)', () => {
|
|
119
|
+
execSync.mockReturnValueOnce('src/app.ts\n');
|
|
120
|
+
|
|
121
|
+
const error = new Error('prettier: command not found');
|
|
122
|
+
error.status = 127;
|
|
123
|
+
execSync.mockImplementationOnce(() => {
|
|
124
|
+
throw error;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
runPrettierStaged();
|
|
128
|
+
|
|
129
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
130
|
+
"❌ Error: Prettier not found. Make sure it's installed:",
|
|
131
|
+
error.message
|
|
132
|
+
);
|
|
133
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('should handle Prettier syntax errors (status 2)', () => {
|
|
137
|
+
execSync.mockReturnValueOnce('src/broken.ts\n');
|
|
138
|
+
|
|
139
|
+
const error = new Error('Syntax error in file');
|
|
140
|
+
error.status = 2;
|
|
141
|
+
execSync.mockImplementationOnce(() => {
|
|
142
|
+
throw error;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
runPrettierStaged();
|
|
146
|
+
|
|
147
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
148
|
+
'❌ Error: Prettier found syntax errors:',
|
|
149
|
+
error.message
|
|
150
|
+
);
|
|
151
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('should handle general Prettier errors', () => {
|
|
155
|
+
execSync.mockReturnValueOnce('src/app.ts\n');
|
|
156
|
+
|
|
157
|
+
const error = new Error('General prettier error');
|
|
158
|
+
error.status = 1;
|
|
159
|
+
execSync.mockImplementationOnce(() => {
|
|
160
|
+
throw error;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
runPrettierStaged();
|
|
164
|
+
|
|
165
|
+
expect(errorSpy).toHaveBeenCalledWith('❌ Error running Prettier:', error.message);
|
|
166
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('should handle git command errors', () => {
|
|
170
|
+
const error = new Error('Git command failed');
|
|
171
|
+
error.status = 128;
|
|
172
|
+
execSync.mockImplementationOnce(() => {
|
|
173
|
+
throw error;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
runPrettierStaged();
|
|
177
|
+
|
|
178
|
+
expect(errorSpy).toHaveBeenCalledWith('❌ Error running Prettier:', error.message);
|
|
179
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('Edge cases', () => {
|
|
184
|
+
test('should handle files with spaces in names', () => {
|
|
185
|
+
execSync
|
|
186
|
+
.mockReturnValueOnce('src/my file.ts\nother file.css\n')
|
|
187
|
+
.mockReturnValueOnce(undefined)
|
|
188
|
+
.mockReturnValueOnce(undefined);
|
|
189
|
+
|
|
190
|
+
runPrettierStaged();
|
|
191
|
+
|
|
192
|
+
expect(execSync).toHaveBeenNthCalledWith(
|
|
193
|
+
2,
|
|
194
|
+
'npx prettier --write src/my file.ts other file.css',
|
|
195
|
+
{ stdio: 'inherit' }
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('should trim whitespace from file names', () => {
|
|
200
|
+
execSync
|
|
201
|
+
.mockReturnValueOnce(' src/app.ts \n style.css \n\n')
|
|
202
|
+
.mockReturnValueOnce(undefined)
|
|
203
|
+
.mockReturnValueOnce(undefined);
|
|
204
|
+
|
|
205
|
+
runPrettierStaged();
|
|
206
|
+
|
|
207
|
+
expect(execSync).toHaveBeenNthCalledWith(2, 'npx prettier --write src/app.ts style.css', {
|
|
208
|
+
stdio: 'inherit'
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('should handle all supported file extensions', () => {
|
|
213
|
+
const validFiles = [
|
|
214
|
+
'template.html',
|
|
215
|
+
'component.ts',
|
|
216
|
+
'styles.scss',
|
|
217
|
+
'reset.css',
|
|
218
|
+
'package.json'
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
execSync
|
|
222
|
+
.mockReturnValueOnce(validFiles.join('\n') + '\n')
|
|
223
|
+
.mockReturnValueOnce(undefined)
|
|
224
|
+
.mockReturnValueOnce(undefined);
|
|
225
|
+
|
|
226
|
+
runPrettierStaged();
|
|
227
|
+
|
|
228
|
+
expect(execSync).toHaveBeenNthCalledWith(2, `npx prettier --write ${validFiles.join(' ')}`, {
|
|
229
|
+
stdio: 'inherit'
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|