ts-repo-utils 2.2.2 → 2.3.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/dist/functions/assert-path-exists.d.mts.map +1 -1
- package/dist/functions/assert-path-exists.mjs +1 -2
- package/dist/functions/assert-path-exists.mjs.map +1 -1
- package/dist/functions/assert-repo-is-dirty.d.mts +6 -2
- package/dist/functions/assert-repo-is-dirty.d.mts.map +1 -1
- package/dist/functions/assert-repo-is-dirty.mjs +15 -9
- package/dist/functions/assert-repo-is-dirty.mjs.map +1 -1
- package/dist/functions/diff.d.mts +4 -0
- package/dist/functions/diff.d.mts.map +1 -1
- package/dist/functions/diff.mjs +16 -14
- package/dist/functions/diff.mjs.map +1 -1
- package/dist/functions/exec-async.mjs +2 -2
- package/dist/functions/exec-async.mjs.map +1 -1
- package/dist/functions/format.d.mts +12 -3
- package/dist/functions/format.d.mts.map +1 -1
- package/dist/functions/format.mjs +64 -33
- package/dist/functions/format.mjs.map +1 -1
- package/dist/functions/gen-index.d.mts +4 -1
- package/dist/functions/gen-index.d.mts.map +1 -1
- package/dist/functions/gen-index.mjs +5 -2
- package/dist/functions/gen-index.mjs.map +1 -1
- package/dist/functions/index.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/functions/assert-path-exists.mts +0 -1
- package/src/functions/assert-repo-is-dirty.mts +20 -8
- package/src/functions/diff.mts +24 -16
- package/src/functions/diff.test.mts +45 -32
- package/src/functions/exec-async.mts +2 -2
- package/src/functions/format.mts +76 -35
- package/src/functions/format.test.mts +28 -15
- package/src/functions/gen-index.mts +8 -2
|
@@ -6,8 +6,10 @@ import '../node-global.mjs';
|
|
|
6
6
|
* @returns True if the repo is dirty, false otherwise.
|
|
7
7
|
* @throws Error if git command fails.
|
|
8
8
|
*/
|
|
9
|
-
export const repoIsDirty = async (
|
|
10
|
-
|
|
9
|
+
export const repoIsDirty = async (
|
|
10
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
11
|
+
): Promise<boolean> => {
|
|
12
|
+
const status = await getGitStatus({ silent: options?.silent ?? false });
|
|
11
13
|
return status.isDirty;
|
|
12
14
|
};
|
|
13
15
|
|
|
@@ -15,9 +17,11 @@ export const repoIsDirty = async (): Promise<boolean> => {
|
|
|
15
17
|
* Checks if the repository is dirty and exits with code 1 if it is.
|
|
16
18
|
* Shows git status and diff output before exiting.
|
|
17
19
|
*/
|
|
18
|
-
export const
|
|
20
|
+
export const assertRepoIsClean = async (
|
|
21
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
22
|
+
): Promise<void> => {
|
|
19
23
|
try {
|
|
20
|
-
const status = await getGitStatus();
|
|
24
|
+
const status = await getGitStatus({ silent: options?.silent ?? false });
|
|
21
25
|
|
|
22
26
|
if (!status.isDirty) {
|
|
23
27
|
echo('Repo is clean\n');
|
|
@@ -29,12 +33,16 @@ export const assertRepoIsDirty = async (): Promise<void> => {
|
|
|
29
33
|
echo(status.stdout);
|
|
30
34
|
|
|
31
35
|
// Show files not tracked by git and unstaged changes
|
|
32
|
-
const addResult = await $('git add -N .'
|
|
36
|
+
const addResult = await $('git add -N .', {
|
|
37
|
+
silent: options?.silent ?? false,
|
|
38
|
+
});
|
|
33
39
|
if (Result.isErr(addResult)) {
|
|
34
40
|
echo('Warning: Failed to add untracked files for diff\n');
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
const diffResult = await $('git diff'
|
|
43
|
+
const diffResult = await $('git diff', {
|
|
44
|
+
silent: options?.silent ?? false,
|
|
45
|
+
});
|
|
38
46
|
if (Result.isErr(diffResult)) {
|
|
39
47
|
echo('Warning: Failed to show diff\n');
|
|
40
48
|
}
|
|
@@ -50,11 +58,15 @@ export const assertRepoIsDirty = async (): Promise<void> => {
|
|
|
50
58
|
* Gets the git status of the repository.
|
|
51
59
|
* @returns An object containing status information.
|
|
52
60
|
*/
|
|
53
|
-
const getGitStatus = async (
|
|
61
|
+
const getGitStatus = async (
|
|
62
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
63
|
+
): Promise<{
|
|
54
64
|
isDirty: boolean;
|
|
55
65
|
stdout: string;
|
|
56
66
|
}> => {
|
|
57
|
-
const res = await $('git status --porcelain'
|
|
67
|
+
const res = await $('git status --porcelain', {
|
|
68
|
+
silent: options?.silent ?? false,
|
|
69
|
+
});
|
|
58
70
|
|
|
59
71
|
if (Result.isErr(res)) {
|
|
60
72
|
throw new Error(`Failed to get git status: ${res.value.message}`);
|
package/src/functions/diff.mts
CHANGED
|
@@ -9,12 +9,23 @@ export const getUntrackedFiles = async (
|
|
|
9
9
|
options?: Readonly<{
|
|
10
10
|
/** @default true */
|
|
11
11
|
excludeDeleted?: boolean;
|
|
12
|
+
/** @default false */
|
|
13
|
+
silent?: boolean;
|
|
12
14
|
}>,
|
|
13
15
|
): Promise<
|
|
14
16
|
Result<readonly string[], ExecException | Readonly<{ message: string }>>
|
|
15
17
|
> => {
|
|
16
18
|
// Get changed files from git status
|
|
17
|
-
const result = await $(
|
|
19
|
+
const result = await $(
|
|
20
|
+
[
|
|
21
|
+
`git ls-files --others --exclude-standard`,
|
|
22
|
+
// Append '--deleted' to include deleted files only if excludeDeleted is explicitly false
|
|
23
|
+
(options?.excludeDeleted ?? true) ? '' : '--deleted',
|
|
24
|
+
]
|
|
25
|
+
.filter((s) => s !== '')
|
|
26
|
+
.join(' '),
|
|
27
|
+
{ silent: options?.silent ?? false },
|
|
28
|
+
);
|
|
18
29
|
|
|
19
30
|
if (Result.isErr(result)) {
|
|
20
31
|
return result;
|
|
@@ -25,20 +36,8 @@ export const getUntrackedFiles = async (
|
|
|
25
36
|
// Parse git status output
|
|
26
37
|
const files = stdout
|
|
27
38
|
.split('\n')
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
// Status format: "XY filename" where X and Y are status codes
|
|
31
|
-
const match = /^..\s+(.+)$/u.exec(line);
|
|
32
|
-
return match?.[1];
|
|
33
|
-
})
|
|
34
|
-
.filter(
|
|
35
|
-
(file): file is string =>
|
|
36
|
-
// Filter out deleted files (status starts with 'D')
|
|
37
|
-
file !== undefined &&
|
|
38
|
-
((options?.excludeDeleted ?? true)
|
|
39
|
-
? !stdout.includes(`D ${file}`)
|
|
40
|
-
: true),
|
|
41
|
-
);
|
|
39
|
+
.map((s) => s.trim())
|
|
40
|
+
.filter((s) => s !== '');
|
|
42
41
|
|
|
43
42
|
return Result.ok(files);
|
|
44
43
|
};
|
|
@@ -51,13 +50,22 @@ export const getDiffFrom = async (
|
|
|
51
50
|
options?: Readonly<{
|
|
52
51
|
/** @default true */
|
|
53
52
|
excludeDeleted?: boolean;
|
|
53
|
+
/** @default false */
|
|
54
|
+
silent?: boolean;
|
|
54
55
|
}>,
|
|
55
56
|
): Promise<
|
|
56
57
|
Result<readonly string[], ExecException | Readonly<{ message: string }>>
|
|
57
58
|
> => {
|
|
58
59
|
// Get files that differ from base branch/commit (excluding deleted files)
|
|
59
60
|
const result = await $(
|
|
60
|
-
|
|
61
|
+
[
|
|
62
|
+
`git diff --name-only`,
|
|
63
|
+
base,
|
|
64
|
+
(options?.excludeDeleted ?? true) ? '--diff-filter=d' : '',
|
|
65
|
+
]
|
|
66
|
+
.filter((s) => s !== '')
|
|
67
|
+
.join(' '),
|
|
68
|
+
{ silent: options?.silent ?? false },
|
|
61
69
|
);
|
|
62
70
|
|
|
63
71
|
if (Result.isErr(result)) {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { rm, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import { Result } from 'ts-data-forge';
|
|
4
2
|
import '../node-global.mjs';
|
|
5
|
-
import { getUntrackedFiles } from './diff.mjs';
|
|
3
|
+
import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
|
|
6
4
|
|
|
7
5
|
describe('diff', () => {
|
|
8
6
|
// Use project root for test files to ensure git tracking
|
|
@@ -13,7 +11,7 @@ describe('diff', () => {
|
|
|
13
11
|
for (const file of testFiles) {
|
|
14
12
|
try {
|
|
15
13
|
// eslint-disable-next-line no-await-in-loop
|
|
16
|
-
await rm(file, { force: true });
|
|
14
|
+
await fs.rm(file, { force: true });
|
|
17
15
|
} catch {
|
|
18
16
|
// Ignore cleanup errors
|
|
19
17
|
}
|
|
@@ -23,7 +21,7 @@ describe('diff', () => {
|
|
|
23
21
|
|
|
24
22
|
describe('getUntrackedFiles', () => {
|
|
25
23
|
test('should return empty array when no files are changed', async () => {
|
|
26
|
-
const result = await getUntrackedFiles();
|
|
24
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
27
25
|
|
|
28
26
|
expect(Result.isOk(result)).toBe(true);
|
|
29
27
|
if (Result.isOk(result)) {
|
|
@@ -34,84 +32,82 @@ describe('diff', () => {
|
|
|
34
32
|
test('should detect newly created files', async () => {
|
|
35
33
|
// Create a new file in project root
|
|
36
34
|
const testFileName = 'test-new-file.tmp';
|
|
37
|
-
const testFilePath = join(process.cwd(), testFileName);
|
|
35
|
+
const testFilePath = path.join(process.cwd(), testFileName);
|
|
38
36
|
testFiles.push(testFilePath);
|
|
39
37
|
|
|
40
|
-
await writeFile(testFilePath, 'test content');
|
|
38
|
+
await fs.writeFile(testFilePath, 'test content');
|
|
41
39
|
|
|
42
|
-
const result = await getUntrackedFiles();
|
|
40
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
43
41
|
|
|
44
42
|
expect(Result.isOk(result)).toBe(true);
|
|
45
43
|
if (Result.isOk(result)) {
|
|
46
44
|
const files = result.value;
|
|
47
|
-
expect(files.some((file) => file
|
|
45
|
+
expect(files.some((file) => file === testFileName)).toBe(true);
|
|
48
46
|
}
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
test('should detect modified existing files', async () => {
|
|
52
50
|
// Use an existing file in the project that we can modify safely
|
|
53
51
|
const testFileName = 'test-modify-file.tmp';
|
|
54
|
-
const testFilePath = join(process.cwd(), testFileName);
|
|
52
|
+
const testFilePath = path.join(process.cwd(), testFileName);
|
|
55
53
|
testFiles.push(testFilePath);
|
|
56
54
|
|
|
57
55
|
// Create and commit the file first
|
|
58
|
-
await writeFile(testFilePath, 'initial content');
|
|
56
|
+
await fs.writeFile(testFilePath, 'initial content');
|
|
59
57
|
|
|
60
58
|
// Add to git to track it
|
|
61
|
-
await $(`git add ${testFileName}
|
|
59
|
+
await $(`git add ${testFileName}`, { silent: true });
|
|
62
60
|
|
|
63
61
|
// Modify the file
|
|
64
|
-
await writeFile(testFilePath, 'modified content');
|
|
62
|
+
await fs.writeFile(testFilePath, 'modified content');
|
|
65
63
|
|
|
66
|
-
const result = await getUntrackedFiles();
|
|
64
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
67
65
|
|
|
68
66
|
expect(Result.isOk(result)).toBe(true);
|
|
69
67
|
if (Result.isOk(result)) {
|
|
70
68
|
const files = result.value;
|
|
71
|
-
expect(files.some((file) => file
|
|
69
|
+
expect(files.some((file) => file === testFileName)).toBe(false);
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
// Reset git state
|
|
75
|
-
await $(`git reset HEAD ${testFileName}
|
|
73
|
+
await $(`git reset HEAD ${testFileName}`, { silent: true });
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
test('should detect multiple types of changes', async () => {
|
|
79
77
|
// Create multiple test files
|
|
80
|
-
const newFile = join(process.cwd(), 'test-new-file.tmp');
|
|
81
|
-
const modifyFile = join(process.cwd(), 'test-modify-file.tmp');
|
|
78
|
+
const newFile = path.join(process.cwd(), 'test-new-file.tmp');
|
|
79
|
+
const modifyFile = path.join(process.cwd(), 'test-modify-file.tmp');
|
|
82
80
|
testFiles.push(newFile, modifyFile);
|
|
83
81
|
|
|
84
82
|
// Create new file
|
|
85
|
-
await writeFile(newFile, 'new file content');
|
|
83
|
+
await fs.writeFile(newFile, 'new file content');
|
|
86
84
|
|
|
87
85
|
// Create and track another file
|
|
88
|
-
await writeFile(modifyFile, 'initial content');
|
|
89
|
-
await $(`git add test-modify-file.tmp
|
|
86
|
+
await fs.writeFile(modifyFile, 'initial content');
|
|
87
|
+
await $(`git add test-modify-file.tmp`, { silent: true });
|
|
90
88
|
|
|
91
89
|
// Modify the tracked file
|
|
92
|
-
await writeFile(modifyFile, 'modified content');
|
|
90
|
+
await fs.writeFile(modifyFile, 'modified content');
|
|
93
91
|
|
|
94
|
-
const result = await getUntrackedFiles();
|
|
92
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
95
93
|
|
|
96
94
|
expect(Result.isOk(result)).toBe(true);
|
|
97
95
|
if (Result.isOk(result)) {
|
|
98
96
|
const files = result.value;
|
|
99
|
-
expect(files.some((file) => file
|
|
100
|
-
|
|
97
|
+
expect(files.some((file) => file === 'test-new-file.tmp')).toBe(true);
|
|
98
|
+
expect(files.some((file) => file === 'test-modify-file.tmp')).toBe(
|
|
99
|
+
false,
|
|
101
100
|
);
|
|
102
|
-
expect(
|
|
103
|
-
files.some((file) => file.includes('test-modify-file.tmp')),
|
|
104
|
-
).toBe(true);
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
// Reset git state
|
|
108
|
-
await $(`git reset HEAD test-modify-file.tmp
|
|
104
|
+
await $(`git reset HEAD test-modify-file.tmp`, { silent: true });
|
|
109
105
|
});
|
|
110
106
|
|
|
111
107
|
test('should exclude deleted files from results', async () => {
|
|
112
108
|
// This test is more complex as it requires simulating git state
|
|
113
109
|
// For now, we'll test that the function executes successfully
|
|
114
|
-
const result = await getUntrackedFiles();
|
|
110
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
115
111
|
|
|
116
112
|
expect(Result.isOk(result)).toBe(true);
|
|
117
113
|
if (Result.isOk(result)) {
|
|
@@ -127,14 +123,14 @@ describe('diff', () => {
|
|
|
127
123
|
test('should handle git command errors gracefully', async () => {
|
|
128
124
|
// This test would require mocking git command failure
|
|
129
125
|
// For now, we'll ensure the function returns a Result type
|
|
130
|
-
const result = await getUntrackedFiles();
|
|
126
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
131
127
|
|
|
132
128
|
// Should always return a Result, either Ok or Err
|
|
133
129
|
expect(Result.isOk(result) || Result.isErr(result)).toBe(true);
|
|
134
130
|
});
|
|
135
131
|
|
|
136
132
|
test('should parse git status output correctly', async () => {
|
|
137
|
-
const result = await getUntrackedFiles();
|
|
133
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
138
134
|
|
|
139
135
|
expect(Result.isOk(result)).toBe(true);
|
|
140
136
|
if (Result.isOk(result)) {
|
|
@@ -148,5 +144,22 @@ describe('diff', () => {
|
|
|
148
144
|
});
|
|
149
145
|
}
|
|
150
146
|
});
|
|
147
|
+
|
|
148
|
+
test('should work with silent option', async () => {
|
|
149
|
+
const result = await getUntrackedFiles({ silent: true });
|
|
150
|
+
|
|
151
|
+
expect(Result.isOk(result)).toBe(true);
|
|
152
|
+
if (Result.isOk(result)) {
|
|
153
|
+
expect(Array.isArray(result.value)).toBe(true);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('getDiffFrom', () => {
|
|
159
|
+
test('should work with silent option', async () => {
|
|
160
|
+
const result = await getDiffFrom('HEAD~1', { silent: true });
|
|
161
|
+
|
|
162
|
+
expect(Result.isOk(result) || Result.isErr(result)).toBe(true);
|
|
163
|
+
});
|
|
151
164
|
});
|
|
152
165
|
});
|
|
@@ -16,7 +16,7 @@ export const $ = (
|
|
|
16
16
|
const { silent = false, timeout = 30000 } = options;
|
|
17
17
|
|
|
18
18
|
if (!silent) {
|
|
19
|
-
|
|
19
|
+
echo(`$ ${cmd}`);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
return new Promise((resolve) => {
|
|
@@ -25,7 +25,7 @@ export const $ = (
|
|
|
25
25
|
exec(cmd, execOptions, (error, stdout, stderr) => {
|
|
26
26
|
if (!silent) {
|
|
27
27
|
if (stdout !== '') {
|
|
28
|
-
|
|
28
|
+
echo(stdout);
|
|
29
29
|
}
|
|
30
30
|
if (stderr !== '') {
|
|
31
31
|
console.error(stderr);
|
package/src/functions/format.mts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import glob from 'fast-glob';
|
|
2
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
1
|
import * as prettier from 'prettier';
|
|
4
|
-
import { Result } from 'ts-data-forge';
|
|
2
|
+
import { Arr, Result } from 'ts-data-forge';
|
|
5
3
|
import '../node-global.mjs';
|
|
6
4
|
import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
|
|
7
5
|
|
|
@@ -12,23 +10,30 @@ import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
|
|
|
12
10
|
*/
|
|
13
11
|
export const formatFilesList = async (
|
|
14
12
|
files: readonly string[],
|
|
13
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
15
14
|
): Promise<'ok' | 'err'> => {
|
|
15
|
+
const silent = options?.silent ?? false;
|
|
16
|
+
|
|
16
17
|
if (files.length === 0) {
|
|
17
|
-
|
|
18
|
+
if (!silent) {
|
|
19
|
+
echo('No files to format');
|
|
20
|
+
}
|
|
18
21
|
return 'ok';
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
if (!silent) {
|
|
25
|
+
echo(`Formatting ${files.length} files...`);
|
|
26
|
+
}
|
|
22
27
|
|
|
23
28
|
// Format each file
|
|
24
29
|
const results = await Promise.allSettled(
|
|
25
30
|
files.map(async (filePath) => {
|
|
26
31
|
try {
|
|
27
32
|
// Read file content
|
|
28
|
-
const content = await readFile(filePath, 'utf8');
|
|
33
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
29
34
|
|
|
30
35
|
// Resolve prettier config for this file
|
|
31
|
-
const
|
|
36
|
+
const prettierOptions = await prettier.resolveConfig(filePath);
|
|
32
37
|
|
|
33
38
|
// Check if file is ignored by prettier
|
|
34
39
|
const fileInfo = await prettier.getFileInfo(filePath, {
|
|
@@ -36,20 +41,24 @@ export const formatFilesList = async (
|
|
|
36
41
|
});
|
|
37
42
|
|
|
38
43
|
if (fileInfo.ignored) {
|
|
39
|
-
|
|
44
|
+
if (!silent) {
|
|
45
|
+
echo(`Skipping ignored file: ${filePath}`);
|
|
46
|
+
}
|
|
40
47
|
return;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
// Format the content
|
|
44
51
|
const formatted = await prettier.format(content, {
|
|
45
|
-
...
|
|
52
|
+
...prettierOptions,
|
|
46
53
|
filepath: filePath,
|
|
47
54
|
});
|
|
48
55
|
|
|
49
56
|
// Only write if content changed
|
|
50
57
|
if (formatted !== content) {
|
|
51
|
-
await writeFile(filePath, formatted, 'utf8');
|
|
52
|
-
|
|
58
|
+
await fs.writeFile(filePath, formatted, 'utf8');
|
|
59
|
+
if (!silent) {
|
|
60
|
+
echo(`Formatted: ${filePath}`);
|
|
61
|
+
}
|
|
53
62
|
}
|
|
54
63
|
} catch (error) {
|
|
55
64
|
console.error(`Error formatting ${filePath}:`, error);
|
|
@@ -68,7 +77,12 @@ export const formatFilesList = async (
|
|
|
68
77
|
* @param pathGlob - Glob pattern to match files
|
|
69
78
|
* @returns 'ok' if successful, 'err' if any errors occurred
|
|
70
79
|
*/
|
|
71
|
-
export const formatFiles = async (
|
|
80
|
+
export const formatFiles = async (
|
|
81
|
+
pathGlob: string,
|
|
82
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
83
|
+
): Promise<'ok' | 'err'> => {
|
|
84
|
+
const silent = options?.silent ?? false;
|
|
85
|
+
|
|
72
86
|
try {
|
|
73
87
|
// Find all files matching the glob
|
|
74
88
|
const files = await glob(pathGlob, {
|
|
@@ -78,11 +92,13 @@ export const formatFiles = async (pathGlob: string): Promise<'ok' | 'err'> => {
|
|
|
78
92
|
});
|
|
79
93
|
|
|
80
94
|
if (files.length === 0) {
|
|
81
|
-
|
|
95
|
+
if (!silent) {
|
|
96
|
+
echo('No files found matching pattern:', pathGlob);
|
|
97
|
+
}
|
|
82
98
|
return 'ok';
|
|
83
99
|
}
|
|
84
100
|
|
|
85
|
-
return await formatFilesList(files);
|
|
101
|
+
return await formatFilesList(files, { silent });
|
|
86
102
|
} catch (error) {
|
|
87
103
|
console.error('Error in formatFiles:', error);
|
|
88
104
|
return 'err';
|
|
@@ -91,11 +107,18 @@ export const formatFiles = async (pathGlob: string): Promise<'ok' | 'err'> => {
|
|
|
91
107
|
|
|
92
108
|
/**
|
|
93
109
|
* Format only files that have been changed (git status)
|
|
110
|
+
* @param options - Options for formatting
|
|
94
111
|
* @returns 'ok' if successful, 'err' if any errors occurred
|
|
95
112
|
*/
|
|
96
|
-
export const formatUntracked = async (
|
|
113
|
+
export const formatUntracked = async (
|
|
114
|
+
options?: Readonly<{ silent?: boolean }>,
|
|
115
|
+
): Promise<'ok' | 'err'> => {
|
|
116
|
+
const silent = options?.silent ?? false;
|
|
117
|
+
|
|
97
118
|
try {
|
|
98
|
-
const untrackedFilesResult = await getUntrackedFiles(
|
|
119
|
+
const untrackedFilesResult = await getUntrackedFiles({
|
|
120
|
+
silent,
|
|
121
|
+
});
|
|
99
122
|
|
|
100
123
|
if (Result.isErr(untrackedFilesResult)) {
|
|
101
124
|
console.error('Error getting changed files:', untrackedFilesResult.value);
|
|
@@ -105,20 +128,26 @@ export const formatUntracked = async (): Promise<'ok' | 'err'> => {
|
|
|
105
128
|
const files = untrackedFilesResult.value;
|
|
106
129
|
|
|
107
130
|
if (files.length === 0) {
|
|
108
|
-
|
|
131
|
+
if (!silent) {
|
|
132
|
+
echo('No changed files to format');
|
|
133
|
+
}
|
|
109
134
|
return 'ok';
|
|
110
135
|
}
|
|
111
136
|
|
|
112
|
-
|
|
137
|
+
if (!silent) {
|
|
138
|
+
echo('Formatting changed files:', files);
|
|
139
|
+
}
|
|
113
140
|
|
|
114
141
|
// Filter out non-existent files before formatting
|
|
115
142
|
const fileExistenceChecks = await Promise.allSettled(
|
|
116
143
|
files.map(async (filePath) => {
|
|
117
144
|
try {
|
|
118
|
-
await readFile(filePath, 'utf8');
|
|
145
|
+
await fs.readFile(filePath, 'utf8');
|
|
119
146
|
return filePath;
|
|
120
147
|
} catch {
|
|
121
|
-
|
|
148
|
+
if (!silent) {
|
|
149
|
+
echo(`Skipping non-existent file: ${filePath}`);
|
|
150
|
+
}
|
|
122
151
|
return undefined;
|
|
123
152
|
}
|
|
124
153
|
}),
|
|
@@ -131,7 +160,7 @@ export const formatUntracked = async (): Promise<'ok' | 'err'> => {
|
|
|
131
160
|
)
|
|
132
161
|
.map((result) => result.value);
|
|
133
162
|
|
|
134
|
-
return await formatFilesList(existingFiles);
|
|
163
|
+
return await formatFilesList(existingFiles, { silent });
|
|
135
164
|
} catch (error) {
|
|
136
165
|
console.error('Error in formatUntracked:', error);
|
|
137
166
|
return 'err';
|
|
@@ -143,15 +172,20 @@ export const formatUntracked = async (): Promise<'ok' | 'err'> => {
|
|
|
143
172
|
* @param base - Base branch name or commit hash to compare against (defaults to 'main')
|
|
144
173
|
* @param options - Options for formatting
|
|
145
174
|
* @param options.includeUntracked - Include untracked files in addition to diff files (default is true)
|
|
175
|
+
* @param options.silent - Silent mode to suppress command output (default is false)
|
|
146
176
|
* @returns 'ok' if successful, 'err' if any errors occurred
|
|
147
177
|
*/
|
|
148
178
|
export const formatDiffFrom = async (
|
|
149
179
|
base: string,
|
|
150
|
-
options?: Readonly<{ includeUntracked?: boolean }>,
|
|
180
|
+
options?: Readonly<{ includeUntracked?: boolean; silent?: boolean }>,
|
|
151
181
|
): Promise<'ok' | 'err'> => {
|
|
182
|
+
const silent = options?.silent ?? false;
|
|
183
|
+
|
|
152
184
|
try {
|
|
153
185
|
// Get files that differ from base branch/commit (excluding deleted files)
|
|
154
|
-
const diffFromBaseResult = await getDiffFrom(base
|
|
186
|
+
const diffFromBaseResult = await getDiffFrom(base, {
|
|
187
|
+
silent,
|
|
188
|
+
});
|
|
155
189
|
|
|
156
190
|
if (Result.isErr(diffFromBaseResult)) {
|
|
157
191
|
console.error('Error getting changed files:', diffFromBaseResult.value);
|
|
@@ -159,11 +193,13 @@ export const formatDiffFrom = async (
|
|
|
159
193
|
}
|
|
160
194
|
|
|
161
195
|
const diffFiles = diffFromBaseResult.value;
|
|
162
|
-
let
|
|
196
|
+
let mut_allFiles = diffFiles;
|
|
163
197
|
|
|
164
198
|
// If includeUntracked is true, also get untracked files
|
|
165
199
|
if (options?.includeUntracked ?? true) {
|
|
166
|
-
const untrackedFilesResult = await getUntrackedFiles(
|
|
200
|
+
const untrackedFilesResult = await getUntrackedFiles({
|
|
201
|
+
silent,
|
|
202
|
+
});
|
|
167
203
|
|
|
168
204
|
if (Result.isErr(untrackedFilesResult)) {
|
|
169
205
|
console.error(
|
|
@@ -176,23 +212,28 @@ export const formatDiffFrom = async (
|
|
|
176
212
|
const untrackedFiles = untrackedFilesResult.value;
|
|
177
213
|
|
|
178
214
|
// Combine and deduplicate files
|
|
179
|
-
|
|
180
|
-
allFiles = Array.from(uniqueFiles);
|
|
215
|
+
mut_allFiles = Arr.uniq([...diffFiles, ...untrackedFiles]);
|
|
181
216
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
217
|
+
if (!silent) {
|
|
218
|
+
echo(
|
|
219
|
+
`Formatting files that differ from ${base} and untracked files:`,
|
|
220
|
+
mut_allFiles,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
186
223
|
} else {
|
|
187
|
-
|
|
224
|
+
if (!silent) {
|
|
225
|
+
echo(`Formatting files that differ from ${base}:`, mut_allFiles);
|
|
226
|
+
}
|
|
188
227
|
}
|
|
189
228
|
|
|
190
|
-
if (
|
|
191
|
-
|
|
229
|
+
if (mut_allFiles.length === 0) {
|
|
230
|
+
if (!silent) {
|
|
231
|
+
echo(`No files to format`);
|
|
232
|
+
}
|
|
192
233
|
return 'ok';
|
|
193
234
|
}
|
|
194
235
|
|
|
195
|
-
return await formatFilesList(
|
|
236
|
+
return await formatFilesList(mut_allFiles, { silent });
|
|
196
237
|
} catch (error) {
|
|
197
238
|
console.error('Error in formatDiffFrom:', error);
|
|
198
239
|
return 'err';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
2
|
import { Result } from 'ts-data-forge';
|
|
3
|
+
import '../node-global.mjs';
|
|
5
4
|
import { getDiffFrom, getUntrackedFiles } from './diff.mjs';
|
|
6
5
|
import { formatDiffFrom, formatFiles, formatFilesList } from './format.mjs';
|
|
7
6
|
|
|
@@ -54,7 +53,7 @@ describe('formatFiles', () => {
|
|
|
54
53
|
await createTestFile('test.md', '# Test\n\nSome spaces');
|
|
55
54
|
|
|
56
55
|
// Format TypeScript files
|
|
57
|
-
const result = await formatFiles(`${testDir}/*.ts
|
|
56
|
+
const result = await formatFiles(`${testDir}/*.ts`, { silent: true });
|
|
58
57
|
expect(result).toBe('ok');
|
|
59
58
|
|
|
60
59
|
// Check that files were formatted
|
|
@@ -85,7 +84,9 @@ describe('formatFiles', () => {
|
|
|
85
84
|
});
|
|
86
85
|
|
|
87
86
|
test('should return ok when no files match pattern', async () => {
|
|
88
|
-
const result = await formatFiles('/non-existent-path/*.ts'
|
|
87
|
+
const result = await formatFiles('/non-existent-path/*.ts', {
|
|
88
|
+
silent: true,
|
|
89
|
+
});
|
|
89
90
|
expect(result).toBe('ok');
|
|
90
91
|
});
|
|
91
92
|
|
|
@@ -103,7 +104,9 @@ describe('formatFiles', () => {
|
|
|
103
104
|
);
|
|
104
105
|
|
|
105
106
|
// Format with recursive glob
|
|
106
|
-
const result = await formatFiles(`${testDir}/**/*.ts
|
|
107
|
+
const result = await formatFiles(`${testDir}/**/*.ts`, {
|
|
108
|
+
silent: true,
|
|
109
|
+
});
|
|
107
110
|
expect(result).toBe('ok');
|
|
108
111
|
|
|
109
112
|
// Check that nested file was formatted
|
|
@@ -159,7 +162,9 @@ describe('formatFilesList', () => {
|
|
|
159
162
|
);
|
|
160
163
|
|
|
161
164
|
// Format the files
|
|
162
|
-
const result = await formatFilesList([file1, file2]
|
|
165
|
+
const result = await formatFilesList([file1, file2], {
|
|
166
|
+
silent: true,
|
|
167
|
+
});
|
|
163
168
|
expect(result).toBe('ok');
|
|
164
169
|
|
|
165
170
|
// Check formatted content
|
|
@@ -184,7 +189,9 @@ describe('formatFilesList', () => {
|
|
|
184
189
|
});
|
|
185
190
|
|
|
186
191
|
test('should return ok for empty file list', async () => {
|
|
187
|
-
const result = await formatFilesList([]
|
|
192
|
+
const result = await formatFilesList([], {
|
|
193
|
+
silent: true,
|
|
194
|
+
});
|
|
188
195
|
expect(result).toBe('ok');
|
|
189
196
|
});
|
|
190
197
|
});
|
|
@@ -224,7 +231,7 @@ describe('formatDiffFrom', () => {
|
|
|
224
231
|
|
|
225
232
|
vi.mocked(getUntrackedFiles).mockResolvedValue(Result.ok([]));
|
|
226
233
|
|
|
227
|
-
const result = await formatDiffFrom('main');
|
|
234
|
+
const result = await formatDiffFrom('main', { silent: true });
|
|
228
235
|
expect(result).toBe('ok');
|
|
229
236
|
|
|
230
237
|
// Check file was formatted
|
|
@@ -236,7 +243,7 @@ describe('formatDiffFrom', () => {
|
|
|
236
243
|
`}\n`,
|
|
237
244
|
);
|
|
238
245
|
|
|
239
|
-
expect(getDiffFrom).toHaveBeenCalledWith('main');
|
|
246
|
+
expect(getDiffFrom).toHaveBeenCalledWith('main', { silent: true });
|
|
240
247
|
} finally {
|
|
241
248
|
await fs.rm(testDir, { recursive: true, force: true });
|
|
242
249
|
}
|
|
@@ -266,7 +273,10 @@ describe('formatDiffFrom', () => {
|
|
|
266
273
|
Result.ok([untrackedFile]),
|
|
267
274
|
);
|
|
268
275
|
|
|
269
|
-
const result = await formatDiffFrom('main', {
|
|
276
|
+
const result = await formatDiffFrom('main', {
|
|
277
|
+
includeUntracked: true,
|
|
278
|
+
silent: true,
|
|
279
|
+
});
|
|
270
280
|
expect(result).toBe('ok');
|
|
271
281
|
|
|
272
282
|
// Check both files were formatted
|
|
@@ -284,8 +294,8 @@ describe('formatDiffFrom', () => {
|
|
|
284
294
|
`}\n`,
|
|
285
295
|
);
|
|
286
296
|
|
|
287
|
-
expect(getDiffFrom).toHaveBeenCalledWith('main');
|
|
288
|
-
expect(getUntrackedFiles).
|
|
297
|
+
expect(getDiffFrom).toHaveBeenCalledWith('main', { silent: true });
|
|
298
|
+
expect(getUntrackedFiles).toHaveBeenCalledWith({ silent: true });
|
|
289
299
|
} finally {
|
|
290
300
|
await fs.rm(testDir, { recursive: true, force: true });
|
|
291
301
|
}
|
|
@@ -306,12 +316,15 @@ describe('formatDiffFrom', () => {
|
|
|
306
316
|
vi.mocked(getDiffFrom).mockResolvedValue(Result.ok([sharedFile]));
|
|
307
317
|
vi.mocked(getUntrackedFiles).mockResolvedValue(Result.ok([sharedFile]));
|
|
308
318
|
|
|
309
|
-
const result = await formatDiffFrom('main', {
|
|
319
|
+
const result = await formatDiffFrom('main', {
|
|
320
|
+
includeUntracked: true,
|
|
321
|
+
silent: true,
|
|
322
|
+
});
|
|
310
323
|
expect(result).toBe('ok');
|
|
311
324
|
|
|
312
325
|
// Verify both functions were called
|
|
313
|
-
expect(getDiffFrom).toHaveBeenCalledWith('main');
|
|
314
|
-
expect(getUntrackedFiles).
|
|
326
|
+
expect(getDiffFrom).toHaveBeenCalledWith('main', { silent: true });
|
|
327
|
+
expect(getUntrackedFiles).toHaveBeenCalledWith({ silent: true });
|
|
315
328
|
|
|
316
329
|
// Check that the file was formatted (content should change)
|
|
317
330
|
const finalContent = await readTestFile(sharedFile);
|