ts-repo-utils 1.0.0 → 1.1.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.
Files changed (47) hide show
  1. package/README.md +278 -0
  2. package/dist/functions/assert-ext.d.mts +22 -0
  3. package/dist/functions/assert-ext.d.mts.map +1 -0
  4. package/dist/functions/assert-ext.mjs +74 -0
  5. package/dist/functions/assert-ext.mjs.map +1 -0
  6. package/dist/functions/assert-path-exists.d.mts +14 -0
  7. package/dist/functions/assert-path-exists.d.mts.map +1 -0
  8. package/dist/functions/assert-path-exists.mjs +31 -0
  9. package/dist/functions/assert-path-exists.mjs.map +1 -0
  10. package/dist/functions/assert-repo-is-dirty.d.mts +13 -0
  11. package/dist/functions/assert-repo-is-dirty.d.mts.map +1 -0
  12. package/dist/functions/assert-repo-is-dirty.mjs +58 -0
  13. package/dist/functions/assert-repo-is-dirty.mjs.map +1 -0
  14. package/dist/functions/exec-async.d.mts +20 -0
  15. package/dist/functions/exec-async.d.mts.map +1 -0
  16. package/dist/functions/exec-async.mjs +36 -0
  17. package/dist/functions/exec-async.mjs.map +1 -0
  18. package/dist/functions/format.d.mts +18 -0
  19. package/dist/functions/format.d.mts.map +1 -0
  20. package/dist/functions/format.mjs +204 -0
  21. package/dist/functions/format.mjs.map +1 -0
  22. package/dist/functions/gen-index.d.mts +21 -0
  23. package/dist/functions/gen-index.d.mts.map +1 -0
  24. package/dist/functions/gen-index.mjs +161 -0
  25. package/dist/functions/gen-index.mjs.map +1 -0
  26. package/dist/functions/index.d.mts +7 -0
  27. package/dist/functions/index.d.mts.map +1 -0
  28. package/dist/functions/index.mjs +7 -0
  29. package/dist/functions/index.mjs.map +1 -0
  30. package/dist/globals.d.mts +1 -0
  31. package/dist/index.d.mts +2 -0
  32. package/dist/index.d.mts.map +1 -0
  33. package/dist/index.mjs +7 -0
  34. package/dist/index.mjs.map +1 -0
  35. package/dist/node-global.d.mts +14 -0
  36. package/dist/node-global.d.mts.map +1 -0
  37. package/dist/node-global.mjs +16 -0
  38. package/dist/node-global.mjs.map +1 -0
  39. package/dist/project-root-path.d.mts +2 -0
  40. package/dist/project-root-path.d.mts.map +1 -0
  41. package/dist/project-root-path.mjs +6 -0
  42. package/dist/project-root-path.mjs.map +1 -0
  43. package/dist/tsconfig.json +1 -0
  44. package/package.json +1 -1
  45. package/src/functions/assert-ext.mts +3 -2
  46. package/src/functions/assert-path-exists.mts +5 -4
  47. package/src/functions/assert-repo-is-dirty.mts +1 -1
package/README.md CHANGED
@@ -1,3 +1,281 @@
1
1
  # ts-repo-utils
2
2
 
3
3
  Utilities for TypeScript Repositories.
4
+
5
+ A comprehensive toolkit for managing TypeScript projects with strict ESM support, providing essential utilities for file validation, code formatting, git operations, and project automation.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install ts-repo-utils
11
+ ```
12
+
13
+ ## API Reference
14
+
15
+ ### Path and File System Utilities
16
+
17
+ #### `pathExists(filePath: string): Promise<boolean>`
18
+
19
+ Checks if a file or directory exists at the specified path.
20
+
21
+ ```typescript
22
+ import { pathExists } from 'ts-repo-utils';
23
+
24
+ const exists = await pathExists('./src/index.ts');
25
+ console.log(exists); // true or false
26
+ ```
27
+
28
+ #### `assertPathExists(filePath: string, description?: string): Promise<void>`
29
+
30
+ Validates that a path exists and exits with code 1 if it doesn't.
31
+
32
+ ```typescript
33
+ import { assertPathExists } from 'ts-repo-utils';
34
+
35
+ // If the file doesn't exist, this will exit the process with code 1
36
+ await assertPathExists('./src/index.ts', 'Entry point file');
37
+ ```
38
+
39
+ ### File Extension Validation
40
+
41
+ #### `assertExt(config: CheckExtConfig): Promise<void>`
42
+
43
+ Validates that all files in specified directories have the correct extensions. Exits with code 1 if any files have incorrect extensions.
44
+
45
+ ```typescript
46
+ import { assertExt } from 'ts-repo-utils';
47
+
48
+ await assertExt({
49
+ directories: [
50
+ {
51
+ path: './src',
52
+ extension: '.ts',
53
+ ignorePatterns: ['*.d.ts', '*.test.ts'],
54
+ },
55
+ {
56
+ path: './scripts',
57
+ extension: '.mjs',
58
+ },
59
+ ],
60
+ });
61
+ ```
62
+
63
+ **Configuration Type:**
64
+
65
+ ```typescript
66
+ type CheckExtConfig = DeepReadonly<{
67
+ directories: {
68
+ path: string; // Directory path to check
69
+ extension: string; // Expected file extension (including the dot)
70
+ ignorePatterns?: string[]; // Optional glob patterns to ignore
71
+ }[];
72
+ }>;
73
+ ```
74
+
75
+ ### Git Repository Utilities
76
+
77
+ #### `repoIsDirty(): Promise<boolean>`
78
+
79
+ Checks if the repository has uncommitted changes.
80
+
81
+ ```typescript
82
+ import { repoIsDirty } from 'ts-repo-utils';
83
+
84
+ const isDirty = await repoIsDirty();
85
+ if (isDirty) {
86
+ console.log('Repository has uncommitted changes');
87
+ }
88
+ ```
89
+
90
+ #### `assertRepoIsDirty(): Promise<void>`
91
+
92
+ Checks if repository is dirty and exits with code 1 if it is (shows changes and diff).
93
+
94
+ ```typescript
95
+ import { assertRepoIsDirty } from 'ts-repo-utils';
96
+
97
+ // Use in CI/build scripts to ensure clean state
98
+ await assertRepoIsDirty();
99
+ ```
100
+
101
+ ### Command Execution
102
+
103
+ #### `$(command: string, options?: ExecOptions): Promise<ExecResult>`
104
+
105
+ Executes a shell command asynchronously with timeout support and type-safe results.
106
+
107
+ ```typescript
108
+ import { $ } from 'ts-repo-utils';
109
+
110
+ const result = await $('npm test', { timeout: 60000 });
111
+
112
+ if (result.type === 'ok') {
113
+ console.log('Tests passed:', result.stdout);
114
+ } else {
115
+ console.error('Tests failed:', result.exception.message);
116
+ }
117
+ ```
118
+
119
+ **Types:**
120
+
121
+ ```typescript
122
+ type ExecOptions = Readonly<{
123
+ silent?: boolean; // Don't log command/output (default: false)
124
+ timeout?: number; // Timeout in milliseconds (default: 30000)
125
+ }>;
126
+
127
+ type ExecResult = Readonly<
128
+ | { type: 'ok'; stdout: string; stderr: string }
129
+ | { type: 'error'; exception: ExecException }
130
+ >;
131
+ ```
132
+
133
+ ### Code Formatting Utilities
134
+
135
+ #### `formatFiles(pathGlob: string): Promise<'ok' | 'err'>`
136
+
137
+ Format files matching a glob pattern using Prettier.
138
+
139
+ ```typescript
140
+ import { formatFiles } from 'ts-repo-utils';
141
+
142
+ // Format all TypeScript files in src
143
+ await formatFiles('src/**/*.ts');
144
+
145
+ // Format specific files
146
+ await formatFiles('src/{index,utils}.ts');
147
+ ```
148
+
149
+ #### `formatChanged(): Promise<'ok' | 'err'>`
150
+
151
+ Format only files that have been changed according to git status.
152
+
153
+ ```typescript
154
+ import { formatChanged } from 'ts-repo-utils';
155
+
156
+ // Format only modified files
157
+ await formatChanged();
158
+ ```
159
+
160
+ #### `formatDiffFrom(base?: string): Promise<'ok' | 'err'>`
161
+
162
+ Format only files that differ from the specified base branch or commit.
163
+
164
+ ```typescript
165
+ import { formatDiffFrom } from 'ts-repo-utils';
166
+
167
+ // Format files different from main branch
168
+ await formatDiffFrom('main');
169
+
170
+ // Format files different from specific commit
171
+ await formatDiffFrom('abc123');
172
+ ```
173
+
174
+ ### Index File Generation
175
+
176
+ #### `genIndex(config: GenIndexConfig): Promise<void>`
177
+
178
+ Generates index.mts files recursively in target directories with automatic barrel exports.
179
+
180
+ ```typescript
181
+ import { genIndex } from 'ts-repo-utils';
182
+
183
+ await genIndex({
184
+ targetDirectory: './src',
185
+ sourceExtension: '.ts',
186
+ exportExtension: '.js',
187
+ excludePatterns: ['*.test.ts', '*.spec.ts'],
188
+ });
189
+ ```
190
+
191
+ **Configuration Type:**
192
+
193
+ ```typescript
194
+ type GenIndexConfig = DeepReadonly<{
195
+ targetDirectory: string | string[]; // Target directories to generate index files for
196
+ sourceExtension?: `.${string}`; // File extension of source files (default: '.mts')
197
+ exportExtension?: `.${string}`; // Extension to use in exports (default: '.mjs')
198
+ excludePatterns?: string[]; // Glob patterns to exclude (default: excludes .d.* and .test.*)
199
+ }>;
200
+ ```
201
+
202
+ **Features:**
203
+
204
+ - Validates file extensions before generation
205
+ - Creates barrel exports for all subdirectories
206
+ - Supports complex glob exclusion patterns
207
+ - Automatically formats generated files
208
+ - Works with both single directories and directory arrays
209
+
210
+ ## Key Features
211
+
212
+ - **Type Safety**: All functions use strict TypeScript types with readonly parameters
213
+ - **Error Handling**: Comprehensive error handling with descriptive messages
214
+ - **Git Integration**: Built-in git status and diff utilities
215
+ - **Formatting**: Prettier integration with configuration resolution
216
+ - **ESM Support**: Designed for ES modules with .mts/.mjs extension handling
217
+ - **Concurrent Processing**: Uses Promise.all for performance optimization
218
+ - **Configurable**: Flexible configuration options with sensible defaults
219
+ - **Console Feedback**: Informative logging throughout operations
220
+
221
+ ## Common Patterns
222
+
223
+ ### Pre-commit Hook
224
+
225
+ ```typescript
226
+ import { formatChanged, assertExt, repoIsDirty } from 'ts-repo-utils';
227
+
228
+ // Format changed files
229
+ await formatChanged();
230
+
231
+ // Validate file extensions
232
+ await assertExt({
233
+ directories: [{ path: './src', extension: '.ts' }],
234
+ });
235
+
236
+ // Ensure no uncommitted changes remain
237
+ await assertRepoIsDirty();
238
+ ```
239
+
240
+ ### Build Pipeline
241
+
242
+ ```typescript
243
+ import { assertExt, genIndex, $, formatFiles } from 'ts-repo-utils';
244
+
245
+ // Validate extensions
246
+ await assertExt({
247
+ directories: [
248
+ { path: './src', extension: '.ts' },
249
+ { path: './scripts', extension: '.mjs' },
250
+ ],
251
+ });
252
+
253
+ // Generate barrel exports
254
+ await genIndex({ targetDirectory: './src' });
255
+
256
+ // Type check
257
+ await $('tsc --noEmit');
258
+
259
+ // Build
260
+ await $('rollup -c');
261
+
262
+ // Format output
263
+ await formatFiles('dist/**/*.js');
264
+ ```
265
+
266
+ ### Project Validation
267
+
268
+ ```typescript
269
+ import { pathExists, assertPathExists, assertRepoIsDirty } from 'ts-repo-utils';
270
+
271
+ // Check required files exist (exits with code 1 if files don't exist)
272
+ await assertPathExists('./package.json', 'Package manifest');
273
+ await assertPathExists('./tsconfig.json', 'TypeScript config');
274
+
275
+ // Verify clean repository state (exits with code 1 if repo is dirty)
276
+ await assertRepoIsDirty();
277
+ ```
278
+
279
+ ## License
280
+
281
+ Apache-2.0
@@ -0,0 +1,22 @@
1
+ import '../node-global.mjs';
2
+ /**
3
+ * Configuration for directory extension checking.
4
+ */
5
+ export type CheckExtConfig = DeepReadonly<{
6
+ /** Array of directory paths and their expected extensions */
7
+ directories: {
8
+ /** Directory path to check */
9
+ path: string;
10
+ /** Expected file extension (including the dot) */
11
+ extension: string;
12
+ /** Optional glob patterns to ignore (defaults to ['tsconfig.json', 'globals.d.*']) */
13
+ ignorePatterns?: string[];
14
+ }[];
15
+ }>;
16
+ /**
17
+ * Validates that all files in specified directories have the correct extensions.
18
+ * Exits with code 1 if any files have incorrect extensions.
19
+ * @param config - Configuration specifying directories and expected extensions.
20
+ */
21
+ export declare const assertExt: (config: CheckExtConfig) => Promise<void>;
22
+ //# sourceMappingURL=assert-ext.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-ext.d.mts","sourceRoot":"","sources":["../../src/functions/assert-ext.mts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;IACxC,6DAA6D;IAC7D,WAAW,EAAE;QACX,8BAA8B;QAC9B,IAAI,EAAE,MAAM,CAAC;QACb,kDAAkD;QAClD,SAAS,EAAE,MAAM,CAAC;QAClB,sFAAsF;QACtF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,EAAE,CAAC;CACL,CAAC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,cAAc,KAAG,OAAO,CAAC,IAAI,CA4DpE,CAAC"}
@@ -0,0 +1,74 @@
1
+ import '../node-global.mjs';
2
+ import { assertPathExists } from './assert-path-exists.mjs';
3
+
4
+ /**
5
+ * Validates that all files in specified directories have the correct extensions.
6
+ * Exits with code 1 if any files have incorrect extensions.
7
+ * @param config - Configuration specifying directories and expected extensions.
8
+ */
9
+ const assertExt = async (config) => {
10
+ const allIncorrectFiles = [];
11
+ // Check all directories in parallel
12
+ const results = await Promise.all(config.directories.map(async ({ path: dir, extension, ignorePatterns }) => {
13
+ try {
14
+ return await getFilesWithIncorrectExtension(dir, extension, ignorePatterns);
15
+ }
16
+ catch (error) {
17
+ console.error(`Failed to check directory ${dir}: ${String(error)}`);
18
+ return [];
19
+ }
20
+ }));
21
+ // Collect all incorrect files
22
+ results.forEach((incorrectFiles) => {
23
+ allIncorrectFiles.push(...incorrectFiles);
24
+ });
25
+ if (allIncorrectFiles.length > 0) {
26
+ const generateErrorMessage = () => {
27
+ // Group directories by extension for a cleaner message
28
+ const extensionGroups = new Map();
29
+ for (const { path: dirPath, extension } of config.directories) {
30
+ const relativePath = path.relative(process.cwd(), dirPath);
31
+ if (!extensionGroups.has(extension)) {
32
+ extensionGroups.set(extension, []);
33
+ }
34
+ extensionGroups.get(extension)?.push(relativePath);
35
+ }
36
+ // Generate message parts for each extension
37
+ const messageParts = Array.from(extensionGroups.entries()).map(([ext, dirs]) => {
38
+ const dirList = dirs.length === 1 ? dirs[0] : dirs.join(', ');
39
+ return `${dirList} should have ${ext} extension`;
40
+ });
41
+ return `All files in ${messageParts.join(' and ')}.`;
42
+ };
43
+ const errorMessage = [
44
+ 'Files with incorrect extensions found:',
45
+ ...allIncorrectFiles.map((file) => ` - ${file}`),
46
+ '',
47
+ generateErrorMessage(),
48
+ ].join('\n');
49
+ echo(errorMessage);
50
+ process.exit(1);
51
+ }
52
+ echo('✓ All files have correct extensions');
53
+ };
54
+ /**
55
+ * Checks if all files in a directory have the expected extension.
56
+ * @param dir - The directory to check.
57
+ * @param expectedExtension - The expected file extension.
58
+ * @param ignorePatterns - Optional glob patterns to ignore.
59
+ * @returns Array of files with incorrect extensions.
60
+ */
61
+ const getFilesWithIncorrectExtension = async (dir, expectedExtension, ignorePatterns) => {
62
+ await assertPathExists(dir, 'Directory');
63
+ const defaultIgnorePatterns = ['tsconfig.json', 'globals.d.*'];
64
+ const finalIgnorePatterns = ignorePatterns ?? defaultIgnorePatterns;
65
+ // Convert relative patterns to absolute paths for the glob ignore option
66
+ const absoluteIgnorePatterns = finalIgnorePatterns.map((pattern) => path.isAbsolute(pattern) ? pattern : `${dir}/${pattern}`);
67
+ const files = await glob(`${dir}/**/*`, {
68
+ ignore: absoluteIgnorePatterns,
69
+ });
70
+ return files.filter((file) => !file.endsWith(expectedExtension));
71
+ };
72
+
73
+ export { assertExt };
74
+ //# sourceMappingURL=assert-ext.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-ext.mjs","sources":["../../src/functions/assert-ext.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import '../node-global.mjs';
2
+ /**
3
+ * Checks if a file or directory exists.
4
+ * @param filePath - The path to check.
5
+ * @returns True if the path exists.
6
+ */
7
+ export declare const pathExists: (filePath: string) => Promise<boolean>;
8
+ /**
9
+ * Validates that a path exists and exits with code 1 if it doesn't.
10
+ * @param filePath - The path to validate.
11
+ * @param description - Description for error message (defaults to 'Path').
12
+ */
13
+ export declare const assertPathExists: (filePath: string, description?: string) => Promise<void>;
14
+ //# sourceMappingURL=assert-path-exists.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-path-exists.d.mts","sourceRoot":"","sources":["../../src/functions/assert-path-exists.mts"],"names":[],"mappings":"AACA,OAAO,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAOlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,oBAAoB,KACnB,OAAO,CAAC,IAAI,CAKd,CAAC"}
@@ -0,0 +1,31 @@
1
+ import * as fs_ from 'node:fs/promises';
2
+ import '../node-global.mjs';
3
+
4
+ /**
5
+ * Checks if a file or directory exists.
6
+ * @param filePath - The path to check.
7
+ * @returns True if the path exists.
8
+ */
9
+ const pathExists = async (filePath) => {
10
+ try {
11
+ await fs_.access(filePath);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ };
18
+ /**
19
+ * Validates that a path exists and exits with code 1 if it doesn't.
20
+ * @param filePath - The path to validate.
21
+ * @param description - Description for error message (defaults to 'Path').
22
+ */
23
+ const assertPathExists = async (filePath, description = 'Path') => {
24
+ if (!(await pathExists(filePath))) {
25
+ echo(`${description} does not exist: ${filePath}`);
26
+ process.exit(1);
27
+ }
28
+ };
29
+
30
+ export { assertPathExists, pathExists };
31
+ //# sourceMappingURL=assert-path-exists.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-path-exists.mjs","sources":["../../src/functions/assert-path-exists.mts"],"sourcesContent":[null],"names":["fs"],"mappings":";;;AAGA;;;;AAIG;MACU,UAAU,GAAG,OAAO,QAAgB,KAAsB;AACrE,IAAA,IAAI;AACF,QAAA,MAAMA,GAAE,CAAC,MAAM,CAAC,QAAQ,CAAC;AACzB,QAAA,OAAO,IAAI;;AACX,IAAA,MAAM;AACN,QAAA,OAAO,KAAK;;AAEhB;AAEA;;;;AAIG;AACI,MAAM,gBAAgB,GAAG,OAC9B,QAAgB,EAChB,WAAW,GAAG,MAAM,KACH;IACjB,IAAI,EAAE,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE;AACjC,QAAA,IAAI,CAAC,CAAG,EAAA,WAAW,oBAAoB,QAAQ,CAAA,CAAE,CAAC;AAClD,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;;AAEnB;;;;"}
@@ -0,0 +1,13 @@
1
+ import '../node-global.mjs';
2
+ /**
3
+ * Checks if the repository has uncommitted changes.
4
+ * @returns True if the repo is dirty, false otherwise.
5
+ * @throws Error if git command fails.
6
+ */
7
+ export declare const repoIsDirty: () => Promise<boolean>;
8
+ /**
9
+ * Checks if the repository is dirty and exits with code 1 if it is.
10
+ * Shows git status and diff output before exiting.
11
+ */
12
+ export declare const assertRepoIsDirty: () => Promise<void>;
13
+ //# sourceMappingURL=assert-repo-is-dirty.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-repo-is-dirty.d.mts","sourceRoot":"","sources":["../../src/functions/assert-repo-is-dirty.mts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,OAAO,CAGnD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CA6BtD,CAAC"}
@@ -0,0 +1,58 @@
1
+ import '../node-global.mjs';
2
+
3
+ /**
4
+ * Checks if the repository has uncommitted changes.
5
+ * @returns True if the repo is dirty, false otherwise.
6
+ * @throws Error if git command fails.
7
+ */
8
+ const repoIsDirty = async () => {
9
+ const status = await getGitStatus();
10
+ return status.isDirty;
11
+ };
12
+ /**
13
+ * Checks if the repository is dirty and exits with code 1 if it is.
14
+ * Shows git status and diff output before exiting.
15
+ */
16
+ const assertRepoIsDirty = async () => {
17
+ try {
18
+ const status = await getGitStatus();
19
+ if (!status.isDirty) {
20
+ echo('Repo is clean\n');
21
+ return;
22
+ }
23
+ echo('Repo is dirty\n');
24
+ echo('Changed files:\n');
25
+ echo(status.stdout);
26
+ // Show files not tracked by git and unstaged changes
27
+ const addResult = await $('git add -N .');
28
+ if (addResult.type === 'error') {
29
+ echo('Warning: Failed to add untracked files for diff\n');
30
+ }
31
+ const diffResult = await $('git diff');
32
+ if (diffResult.type === 'error') {
33
+ echo('Warning: Failed to show diff\n');
34
+ }
35
+ process.exit(1);
36
+ }
37
+ catch (error) {
38
+ echo(`Error checking repository status: ${String(error)}\n`);
39
+ process.exit(1);
40
+ }
41
+ };
42
+ /**
43
+ * Gets the git status of the repository.
44
+ * @returns An object containing status information.
45
+ */
46
+ const getGitStatus = async () => {
47
+ const res = await $('git status --porcelain');
48
+ if (res.type === 'error') {
49
+ throw new Error(`Failed to get git status: ${res.exception.message}`);
50
+ }
51
+ return {
52
+ isDirty: res.stdout.trim() !== '',
53
+ stdout: res.stdout,
54
+ };
55
+ };
56
+
57
+ export { assertRepoIsDirty, repoIsDirty };
58
+ //# sourceMappingURL=assert-repo-is-dirty.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-repo-is-dirty.mjs","sources":["../../src/functions/assert-repo-is-dirty.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAEA;;;;AAIG;AACU,MAAA,WAAW,GAAG,YAA6B;AACtD,IAAA,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE;IACnC,OAAO,MAAM,CAAC,OAAO;AACvB;AAEA;;;AAGG;AACU,MAAA,iBAAiB,GAAG,YAA0B;AACzD,IAAA,IAAI;AACF,QAAA,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE;AAEnC,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;YACnB,IAAI,CAAC,iBAAiB,CAAC;YACvB;;QAGF,IAAI,CAAC,iBAAiB,CAAC;QACvB,IAAI,CAAC,kBAAkB,CAAC;AACxB,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;;AAGnB,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,cAAc,CAAC;AACzC,QAAA,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE;YAC9B,IAAI,CAAC,mDAAmD,CAAC;;AAG3D,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,UAAU,CAAC;AACtC,QAAA,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO,EAAE;YAC/B,IAAI,CAAC,gCAAgC,CAAC;;AAGxC,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;;IACf,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,qCAAqC,MAAM,CAAC,KAAK,CAAC,CAAA,EAAA,CAAI,CAAC;AAC5D,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;;AAEnB;AAEA;;;AAGG;AACH,MAAM,YAAY,GAAG,YAGhB;AACH,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,wBAAwB,CAAC;AAE7C,IAAA,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,CAA6B,0BAAA,EAAA,GAAG,CAAC,SAAS,CAAC,OAAO,CAAE,CAAA,CAAC;;IAGvE,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;QACjC,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB;AACH,CAAC;;;;"}
@@ -0,0 +1,20 @@
1
+ import { type ExecException } from 'node:child_process';
2
+ export type ExecResult = Readonly<{
3
+ type: 'ok';
4
+ stdout: string;
5
+ stderr: string;
6
+ } | {
7
+ type: 'error';
8
+ exception: ExecException;
9
+ }>;
10
+ /**
11
+ * Executes a shell command asynchronously.
12
+ * @param cmd - The command to execute.
13
+ * @param options - Optional configuration for command execution.
14
+ * @returns A promise that resolves with the command result.
15
+ */
16
+ export declare const $: (cmd: string, options?: Readonly<{
17
+ silent?: boolean;
18
+ timeout?: number;
19
+ }>) => Promise<ExecResult>;
20
+ //# sourceMappingURL=exec-async.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec-async.d.mts","sourceRoot":"","sources":["../../src/functions/exec-async.mts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAC7B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,aAAa,CAAA;CAAE,CAC9C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,CAAC,GACZ,KAAK,MAAM,EACX,UAAS,QAAQ,CAAC;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAM,KAC7D,OAAO,CAAC,UAAU,CA2BpB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { exec } from 'node:child_process';
2
+
3
+ /**
4
+ * Executes a shell command asynchronously.
5
+ * @param cmd - The command to execute.
6
+ * @param options - Optional configuration for command execution.
7
+ * @returns A promise that resolves with the command result.
8
+ */
9
+ const $ = (cmd, options = {}) => {
10
+ const { silent = false, timeout = 30000 } = options;
11
+ if (!silent) {
12
+ console.log(`$ ${cmd}`);
13
+ }
14
+ return new Promise((resolve) => {
15
+ const execOptions = { timeout };
16
+ exec(cmd, execOptions, (error, stdout, stderr) => {
17
+ if (!silent) {
18
+ if (stdout !== '') {
19
+ console.log(stdout);
20
+ }
21
+ if (stderr !== '') {
22
+ console.error(stderr);
23
+ }
24
+ }
25
+ if (error !== null) {
26
+ resolve({ type: 'error', exception: error });
27
+ }
28
+ else {
29
+ resolve({ type: 'ok', stdout, stderr });
30
+ }
31
+ });
32
+ });
33
+ };
34
+
35
+ export { $ };
36
+ //# sourceMappingURL=exec-async.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec-async.mjs","sources":["../../src/functions/exec-async.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAOA;;;;;AAKG;AACU,MAAA,CAAC,GAAG,CACf,GAAW,EACX,OAAA,GAA4D,EAAE,KACvC;IACvB,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,OAAO;IAEnD,IAAI,CAAC,MAAM,EAAE;AACX,QAAA,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAA,CAAE,CAAC;;AAGzB,IAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAI;AAC7B,QAAA,MAAM,WAAW,GAAG,EAAE,OAAO,EAAE;AAE/B,QAAA,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,KAAI;YAC/C,IAAI,CAAC,MAAM,EAAE;AACX,gBAAA,IAAI,MAAM,KAAK,EAAE,EAAE;AACjB,oBAAA,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;;AAErB,gBAAA,IAAI,MAAM,KAAK,EAAE,EAAE;AACjB,oBAAA,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;;;AAIzB,YAAA,IAAI,KAAK,KAAK,IAAI,EAAE;gBAClB,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;iBACvC;gBACL,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;;AAE3C,SAAC,CAAC;AACJ,KAAC,CAAC;AACJ;;;;"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Format files matching the given glob pattern using Prettier
3
+ * @param pathGlob - Glob pattern to match files
4
+ * @returns 'ok' if successful, 'err' if any errors occurred
5
+ */
6
+ export declare const formatFiles: (pathGlob: string) => Promise<"ok" | "err">;
7
+ /**
8
+ * Format only files that have been changed (git status)
9
+ * @returns 'ok' if successful, 'err' if any errors occurred
10
+ */
11
+ export declare const formatChanged: () => Promise<"ok" | "err">;
12
+ /**
13
+ * Format only files that differ from the specified base branch or commit
14
+ * @param base - Base branch name or commit hash to compare against (defaults to 'main')
15
+ * @returns 'ok' if successful, 'err' if any errors occurred
16
+ */
17
+ export declare const formatDiffFrom: (base?: string) => Promise<"ok" | "err">;
18
+ //# sourceMappingURL=format.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.mts","sourceRoot":"","sources":["../../src/functions/format.mts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,GAAG,KAAK,CA6DxE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,IAAI,GAAG,KAAK,CAiF1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,OAAM,MAAe,KACpB,OAAO,CAAC,IAAI,GAAG,KAAK,CAsEtB,CAAC"}
@@ -0,0 +1,204 @@
1
+ import glob_ from 'fast-glob';
2
+ import { exec } from 'node:child_process';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { promisify } from 'node:util';
5
+ import * as prettier from 'prettier';
6
+
7
+ const execAsync = promisify(exec);
8
+ /**
9
+ * Format files matching the given glob pattern using Prettier
10
+ * @param pathGlob - Glob pattern to match files
11
+ * @returns 'ok' if successful, 'err' if any errors occurred
12
+ */
13
+ const formatFiles = async (pathGlob) => {
14
+ try {
15
+ // Find all files matching the glob
16
+ const files = await glob_(pathGlob, {
17
+ absolute: true,
18
+ ignore: ['**/node_modules/**', '**/.git/**'],
19
+ dot: true,
20
+ });
21
+ if (files.length === 0) {
22
+ console.log('No files found matching pattern:', pathGlob);
23
+ return 'ok';
24
+ }
25
+ console.log(`Formatting ${files.length} files...`);
26
+ // Format each file
27
+ const results = await Promise.allSettled(files.map(async (filePath) => {
28
+ try {
29
+ // Read file content
30
+ const content = await readFile(filePath, 'utf8');
31
+ // Resolve prettier config for this file
32
+ const options = await prettier.resolveConfig(filePath);
33
+ // Check if file is ignored by prettier
34
+ const fileInfo = await prettier.getFileInfo(filePath, {
35
+ ignorePath: '.prettierignore',
36
+ });
37
+ if (fileInfo.ignored) {
38
+ console.log(`Skipping ignored file: ${filePath}`);
39
+ return;
40
+ }
41
+ // Format the content
42
+ const formatted = await prettier.format(content, {
43
+ ...options,
44
+ filepath: filePath,
45
+ });
46
+ // Only write if content changed
47
+ if (formatted !== content) {
48
+ await writeFile(filePath, formatted, 'utf8');
49
+ console.log(`Formatted: ${filePath}`);
50
+ }
51
+ }
52
+ catch (error) {
53
+ console.error(`Error formatting ${filePath}:`, error);
54
+ throw error;
55
+ }
56
+ }));
57
+ // Check if any formatting failed
58
+ const hasErrors = results.some((result) => result.status === 'rejected');
59
+ return hasErrors ? 'err' : 'ok';
60
+ }
61
+ catch (error) {
62
+ console.error('Error in formatFiles:', error);
63
+ return 'err';
64
+ }
65
+ };
66
+ /**
67
+ * Format only files that have been changed (git status)
68
+ * @returns 'ok' if successful, 'err' if any errors occurred
69
+ */
70
+ const formatChanged = async () => {
71
+ try {
72
+ // Get changed files from git status
73
+ const { stdout, stderr } = await execAsync('git status --porcelain');
74
+ if (stderr !== '') {
75
+ console.error('Git error:', stderr);
76
+ return 'err';
77
+ }
78
+ // Parse git status output
79
+ const files = stdout
80
+ .split('\n')
81
+ .filter((line) => line.trim() !== '')
82
+ .map((line) => {
83
+ // Status format: "XY filename" where X and Y are status codes
84
+ const match = /^..\s+(.+)$/u.exec(line);
85
+ return match?.[1];
86
+ })
87
+ .filter((file) =>
88
+ // Filter out deleted files (status starts with 'D')
89
+ file !== undefined && !stdout.includes(`D ${file}`));
90
+ if (files.length === 0) {
91
+ console.log('No changed files to format');
92
+ return 'ok';
93
+ }
94
+ console.log('Formatting changed files:', files);
95
+ // Format each changed file
96
+ const results = await Promise.allSettled(files.map(async (filePath) => {
97
+ try {
98
+ // Check if file exists and is not deleted
99
+ const content = await readFile(filePath, 'utf8').catch(() => null);
100
+ if (content === null) {
101
+ console.log(`Skipping non-existent file: ${filePath}`);
102
+ return;
103
+ }
104
+ // Resolve prettier config for this file
105
+ const options = await prettier.resolveConfig(filePath);
106
+ // Check if file is ignored by prettier
107
+ const fileInfo = await prettier.getFileInfo(filePath, {
108
+ ignorePath: '.prettierignore',
109
+ });
110
+ if (fileInfo.ignored) {
111
+ console.log(`Skipping ignored file: ${filePath}`);
112
+ return;
113
+ }
114
+ // Format the content
115
+ const formatted = await prettier.format(content, {
116
+ ...options,
117
+ filepath: filePath,
118
+ });
119
+ // Only write if content changed
120
+ if (formatted !== content) {
121
+ await writeFile(filePath, formatted, 'utf8');
122
+ console.log(`Formatted: ${filePath}`);
123
+ }
124
+ }
125
+ catch (error) {
126
+ console.error(`Error formatting ${filePath}:`, error);
127
+ throw error;
128
+ }
129
+ }));
130
+ // Check if any formatting failed
131
+ const hasErrors = results.some((result) => result.status === 'rejected');
132
+ return hasErrors ? 'err' : 'ok';
133
+ }
134
+ catch (error) {
135
+ console.error('Error in formatChanged:', error);
136
+ return 'err';
137
+ }
138
+ };
139
+ /**
140
+ * Format only files that differ from the specified base branch or commit
141
+ * @param base - Base branch name or commit hash to compare against (defaults to 'main')
142
+ * @returns 'ok' if successful, 'err' if any errors occurred
143
+ */
144
+ const formatDiffFrom = async (base = 'main') => {
145
+ try {
146
+ // Get files that differ from base branch/commit (excluding deleted files)
147
+ const { stdout, stderr } = await execAsync(`git diff --name-only ${base} --diff-filter=d`);
148
+ if (stderr !== '') {
149
+ console.error('Git error:', stderr);
150
+ return 'err';
151
+ }
152
+ // Parse git diff output
153
+ const files = stdout
154
+ .split('\n')
155
+ .map((line) => line.trim())
156
+ .filter((line) => line !== '');
157
+ if (files.length === 0) {
158
+ console.log(`No files differ from ${base}`);
159
+ return 'ok';
160
+ }
161
+ console.log(`Formatting files that differ from ${base}:`, files);
162
+ // Format each file
163
+ const results = await Promise.allSettled(files.map(async (filePath) => {
164
+ try {
165
+ // Read file content
166
+ const content = await readFile(filePath, 'utf8');
167
+ // Resolve prettier config for this file
168
+ const options = await prettier.resolveConfig(filePath);
169
+ // Check if file is ignored by prettier
170
+ const fileInfo = await prettier.getFileInfo(filePath, {
171
+ ignorePath: '.prettierignore',
172
+ });
173
+ if (fileInfo.ignored) {
174
+ console.log(`Skipping ignored file: ${filePath}`);
175
+ return;
176
+ }
177
+ // Format the content
178
+ const formatted = await prettier.format(content, {
179
+ ...options,
180
+ filepath: filePath,
181
+ });
182
+ // Only write if content changed
183
+ if (formatted !== content) {
184
+ await writeFile(filePath, formatted, 'utf8');
185
+ console.log(`Formatted: ${filePath}`);
186
+ }
187
+ }
188
+ catch (error) {
189
+ console.error(`Error formatting ${filePath}:`, error);
190
+ throw error;
191
+ }
192
+ }));
193
+ // Check if any formatting failed
194
+ const hasErrors = results.some((result) => result.status === 'rejected');
195
+ return hasErrors ? 'err' : 'ok';
196
+ }
197
+ catch (error) {
198
+ console.error('Error in formatDiffFrom:', error);
199
+ return 'err';
200
+ }
201
+ };
202
+
203
+ export { formatChanged, formatDiffFrom, formatFiles };
204
+ //# sourceMappingURL=format.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.mjs","sources":["../../src/functions/format.mts"],"sourcesContent":[null],"names":["glob"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import '../node-global.mjs';
2
+ /**
3
+ * Configuration for index file generation.
4
+ */
5
+ export type GenIndexConfig = DeepReadonly<{
6
+ /** Target directories to generate index files for (string or array of strings) */
7
+ targetDirectory: string | string[];
8
+ /** File extension of source files to export (default: '.mts') */
9
+ sourceExtension?: `.${string}`;
10
+ /** File extension to use in export statements (default: '.mjs') */
11
+ exportExtension?: `.${string}`;
12
+ /** Glob patterns of files to exclude from exports (default: excludes .d.* and .test.* files) */
13
+ excludePatterns?: string[];
14
+ }>;
15
+ /**
16
+ * Generates index.mts files recursively in `config.targetDirectory`.
17
+ * @param config - Configuration for index file generation
18
+ * @throws Error if any step fails.
19
+ */
20
+ export declare const genIndex: (config: GenIndexConfig) => Promise<void>;
21
+ //# sourceMappingURL=gen-index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gen-index.d.mts","sourceRoot":"","sources":["../../src/functions/gen-index.mts"],"names":[],"mappings":"AACA,OAAO,oBAAoB,CAAC;AAI5B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;IACxC,kFAAkF;IAClF,eAAe,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEnC,iEAAiE;IACjE,eAAe,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC;IAE/B,mEAAmE;IACnE,eAAe,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC;IAE/B,gGAAgG;IAChG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAU,QAAQ,cAAc,KAAG,OAAO,CAAC,IAAI,CA4DnE,CAAC"}
@@ -0,0 +1,161 @@
1
+ import micromatch from 'micromatch';
2
+ import '../node-global.mjs';
3
+ import { assertExt } from './assert-ext.mjs';
4
+ import { assertPathExists } from './assert-path-exists.mjs';
5
+
6
+ /**
7
+ * Generates index.mts files recursively in `config.targetDirectory`.
8
+ * @param config - Configuration for index file generation
9
+ * @throws Error if any step fails.
10
+ */
11
+ const genIndex = async (config) => {
12
+ echo('Starting index file generation...\n');
13
+ // Merge config with defaults
14
+ const filledConfig = fillConfig(config);
15
+ // Normalize target directories to array
16
+ const targetDirs = typeof config.targetDirectory === 'string'
17
+ ? [config.targetDirectory]
18
+ : config.targetDirectory;
19
+ try {
20
+ // Step 1: Validate file extensions
21
+ echo('1. Validating file extensions...');
22
+ await assertExt({
23
+ directories: [
24
+ {
25
+ path: path.resolve(projectRootPath, './src'),
26
+ extension: '.mts',
27
+ ignorePatterns: ['tsconfig.json', 'globals.d.mts'],
28
+ },
29
+ {
30
+ path: path.resolve(projectRootPath, './scripts'),
31
+ extension: '.mts',
32
+ ignorePatterns: ['tsconfig.json'],
33
+ },
34
+ ],
35
+ });
36
+ echo('✓ File extensions validated\n');
37
+ // Step 2: Verify target directories exist
38
+ for (const dir of targetDirs) {
39
+ const resolvedDir = path.resolve(dir);
40
+ // eslint-disable-next-line no-await-in-loop
41
+ await assertPathExists(resolvedDir, `Target directory: ${dir}`);
42
+ }
43
+ // Step 3: Generate index files
44
+ echo('2. Generating index files...');
45
+ for (const dir of targetDirs) {
46
+ const resolvedDir = path.resolve(dir);
47
+ // eslint-disable-next-line no-await-in-loop
48
+ await generateIndexFileForDir(resolvedDir, filledConfig);
49
+ }
50
+ echo('✓ Index files generated\n');
51
+ // Step 4: Format generated files
52
+ echo('3. Formatting generated files...');
53
+ const fmtResult = await $('npm run fmt');
54
+ if (fmtResult.type === 'error') {
55
+ throw new Error(`Formatting failed: ${fmtResult.exception.message}`);
56
+ }
57
+ echo('✓ Formatting completed\n');
58
+ echo('✅ Index file generation completed successfully!\n');
59
+ }
60
+ catch (error) {
61
+ echo(`❌ Index generation failed: ${String(error)}\n`);
62
+ throw error;
63
+ }
64
+ };
65
+ const fillConfig = (config) => {
66
+ const sourceExtension = config.sourceExtension ?? '.mts';
67
+ const exportExtension = config.exportExtension ?? '.mjs'; // For ESM imports, .mts resolves to .mjs
68
+ return {
69
+ targetDirectory: config.targetDirectory,
70
+ sourceExtension,
71
+ exportExtension,
72
+ excludePatterns: config.excludePatterns ?? [
73
+ `*.d${sourceExtension}`,
74
+ `*.test${sourceExtension}`,
75
+ ],
76
+ };
77
+ };
78
+ /**
79
+ * Generates an index.mts file for the given directory.
80
+ * Recursively calls itself for subdirectories.
81
+ * @param dirPath - The absolute path to the directory to process.
82
+ * @param config - The merged configuration object.
83
+ * @param baseDir - The base directory path for calculating relative paths (optional, defaults to dirPath).
84
+ * @throws Error if directory processing fails.
85
+ */
86
+ const generateIndexFileForDir = async (dirPath, config, baseDir) => {
87
+ try {
88
+ const actualBaseDir = baseDir ?? dirPath;
89
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
90
+ const subDirectories = [];
91
+ const filesToExport = [];
92
+ for (const entry of entries) {
93
+ const entryName = entry.name;
94
+ const entryPath = path.join(dirPath, entryName);
95
+ const relativePath = path.relative(actualBaseDir, entryPath);
96
+ if (entry.isDirectory()) {
97
+ subDirectories.push(entryName);
98
+ // Recursively call for subdirectories first
99
+ // eslint-disable-next-line no-await-in-loop
100
+ await generateIndexFileForDir(entryPath, config, actualBaseDir);
101
+ }
102
+ else if (entry.isFile() && shouldExportFile(relativePath, config)) {
103
+ filesToExport.push(entryName);
104
+ }
105
+ }
106
+ const indexContent = generateIndexContent(subDirectories, filesToExport, config);
107
+ const indexPath = path.join(dirPath, `index${config.sourceExtension}`);
108
+ await fs.writeFile(indexPath, indexContent);
109
+ echo(`Generated: ${path.relative(process.cwd(), indexPath)}`);
110
+ }
111
+ catch (error) {
112
+ throw new Error(`Failed to generate index for directory ${dirPath}: ${String(error)}`);
113
+ }
114
+ };
115
+ /**
116
+ * Determines if a file should be exported in the index file.
117
+ * @param filePath - The relative path to the file from the target directory.
118
+ * @param config - The merged configuration object.
119
+ * @returns True if the file should be exported.
120
+ */
121
+ const shouldExportFile = (filePath, config) => {
122
+ const fileName = path.basename(filePath);
123
+ // Must have the correct source extension
124
+ if (!fileName.endsWith(config.sourceExtension)) {
125
+ return false;
126
+ }
127
+ // Don't export the index file itself
128
+ if (fileName === `index${config.sourceExtension}`) {
129
+ return false;
130
+ }
131
+ // Check against exclusion patterns
132
+ for (const pattern of config.excludePatterns) {
133
+ if (micromatch.isMatch(filePath, pattern) ||
134
+ micromatch.isMatch(fileName, pattern)) {
135
+ return false;
136
+ }
137
+ }
138
+ return true;
139
+ };
140
+ /**
141
+ * Generates the content for an index file.
142
+ * @param subDirectories - Array of subdirectory names.
143
+ * @param filesToExport - Array of file names to export.
144
+ * @param config - The merged configuration object.
145
+ * @returns The index file content.
146
+ */
147
+ const generateIndexContent = (subDirectories, filesToExport, config) => {
148
+ const exportStatements = [
149
+ ...subDirectories.map((subDir) => `export * from "./${subDir}/index${config.exportExtension}";`),
150
+ ...filesToExport.map((file) => {
151
+ const fileNameWithoutExt = path.basename(file, config.sourceExtension);
152
+ return `export * from "./${fileNameWithoutExt}${config.exportExtension}";`;
153
+ }),
154
+ ];
155
+ return exportStatements.length === 0
156
+ ? 'export {};'
157
+ : exportStatements.join('\n');
158
+ };
159
+
160
+ export { genIndex };
161
+ //# sourceMappingURL=gen-index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gen-index.mjs","sources":["../../src/functions/gen-index.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from './assert-ext.mjs';
2
+ export * from './assert-path-exists.mjs';
3
+ export * from './assert-repo-is-dirty.mjs';
4
+ export * from './exec-async.mjs';
5
+ export * from './format.mjs';
6
+ export * from './gen-index.mjs';
7
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/functions/index.mts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { assertExt } from './assert-ext.mjs';
2
+ export { assertPathExists, pathExists } from './assert-path-exists.mjs';
3
+ export { assertRepoIsDirty, repoIsDirty } from './assert-repo-is-dirty.mjs';
4
+ export { $ } from './exec-async.mjs';
5
+ export { formatChanged, formatDiffFrom, formatFiles } from './format.mjs';
6
+ export { genIndex } from './gen-index.mjs';
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -0,0 +1 @@
1
+ /// <reference types="ts-type-forge" />
@@ -0,0 +1,2 @@
1
+ export * from './functions/index.mjs';
2
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ export { assertExt } from './functions/assert-ext.mjs';
2
+ export { assertPathExists, pathExists } from './functions/assert-path-exists.mjs';
3
+ export { assertRepoIsDirty, repoIsDirty } from './functions/assert-repo-is-dirty.mjs';
4
+ export { $ } from './functions/exec-async.mjs';
5
+ export { formatChanged, formatDiffFrom, formatFiles } from './functions/format.mjs';
6
+ export { genIndex } from './functions/gen-index.mjs';
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -0,0 +1,14 @@
1
+ import { default as glob_ } from 'fast-glob';
2
+ import * as fs_ from 'node:fs/promises';
3
+ import * as path_ from 'node:path';
4
+ import { $ as $_ } from './functions/exec-async.mjs';
5
+ import { projectRootPath as projectRootPath_ } from './project-root-path.mjs';
6
+ declare global {
7
+ const $: typeof $_;
8
+ const echo: typeof console.log;
9
+ const projectRootPath: typeof projectRootPath_;
10
+ const path: typeof path_;
11
+ const fs: typeof fs_;
12
+ const glob: typeof glob_;
13
+ }
14
+ //# sourceMappingURL=node-global.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-global.d.mts","sourceRoot":"","sources":["../src/node-global.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAc9E,OAAO,CAAC,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IAC/B,MAAM,eAAe,EAAE,OAAO,gBAAgB,CAAC;IAE/C,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,EAAE,EAAE,OAAO,GAAG,CAAC;IACrB,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC;CAC1B"}
@@ -0,0 +1,16 @@
1
+ import glob_ from 'fast-glob';
2
+ import * as fs_ from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import { $ } from './functions/exec-async.mjs';
5
+ import { projectRootPath } from './project-root-path.mjs';
6
+
7
+ const globalsDef = {
8
+ $: $,
9
+ echo: console.log,
10
+ projectRootPath: projectRootPath,
11
+ path: path,
12
+ fs: fs_,
13
+ glob: glob_,
14
+ };
15
+ Object.assign(globalThis, globalsDef);
16
+ //# sourceMappingURL=node-global.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-global.mjs","sources":["../src/node-global.mts"],"sourcesContent":[null],"names":["$_","projectRootPath_","path_"],"mappings":";;;;;;AAMA,MAAM,UAAU,GAAG;AACjB,IAAA,CAAC,EAAEA,CAAE;IACL,IAAI,EAAE,OAAO,CAAC,GAAG;AACjB,IAAA,eAAe,EAAEC,eAAgB;AAEjC,IAAA,IAAI,EAAEC,IAAK;AACX,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,IAAI,EAAE,KAAK;CACH;AAEV,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const projectRootPath: string;
2
+ //# sourceMappingURL=project-root-path.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-root-path.d.mts","sourceRoot":"","sources":["../src/project-root-path.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,QAA0C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import * as path from 'node:path';
2
+
3
+ const projectRootPath = path.resolve(import.meta.dirname, '..');
4
+
5
+ export { projectRootPath };
6
+ //# sourceMappingURL=project-root-path.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-root-path.mjs","sources":["../src/project-root-path.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAEa,MAAA,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI;;;;"}
@@ -0,0 +1 @@
1
+ {"include":["."]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-repo-utils",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -18,8 +18,8 @@ export type CheckExtConfig = DeepReadonly<{
18
18
 
19
19
  /**
20
20
  * Validates that all files in specified directories have the correct extensions.
21
+ * Exits with code 1 if any files have incorrect extensions.
21
22
  * @param config - Configuration specifying directories and expected extensions.
22
- * @throws Error with details of all incorrect files found.
23
23
  */
24
24
  export const assertExt = async (config: CheckExtConfig): Promise<void> => {
25
25
  const allIncorrectFiles: string[] = [];
@@ -76,7 +76,8 @@ export const assertExt = async (config: CheckExtConfig): Promise<void> => {
76
76
  generateErrorMessage(),
77
77
  ].join('\n');
78
78
 
79
- throw new Error(errorMessage);
79
+ echo(errorMessage);
80
+ process.exit(1);
80
81
  }
81
82
 
82
83
  echo('✓ All files have correct extensions');
@@ -1,4 +1,5 @@
1
1
  import * as fs from 'node:fs/promises';
2
+ import '../node-global.mjs';
2
3
 
3
4
  /**
4
5
  * Checks if a file or directory exists.
@@ -15,16 +16,16 @@ export const pathExists = async (filePath: string): Promise<boolean> => {
15
16
  };
16
17
 
17
18
  /**
18
- * Validates that a path exists and throws if it doesn't.
19
+ * Validates that a path exists and exits with code 1 if it doesn't.
19
20
  * @param filePath - The path to validate.
20
- * @param description - Description for error message.
21
- * @throws Error if path doesn't exist.
21
+ * @param description - Description for error message (defaults to 'Path').
22
22
  */
23
23
  export const assertPathExists = async (
24
24
  filePath: string,
25
25
  description = 'Path',
26
26
  ): Promise<void> => {
27
27
  if (!(await pathExists(filePath))) {
28
- throw new Error(`${description} does not exist: ${filePath}`);
28
+ echo(`${description} does not exist: ${filePath}`);
29
+ process.exit(1);
29
30
  }
30
31
  };
@@ -12,7 +12,7 @@ export const repoIsDirty = async (): Promise<boolean> => {
12
12
 
13
13
  /**
14
14
  * Checks if the repository is dirty and exits with code 1 if it is.
15
- * @throws Error if git command fails.
15
+ * Shows git status and diff output before exiting.
16
16
  */
17
17
  export const assertRepoIsDirty = async (): Promise<void> => {
18
18
  try {