ts-repo-utils 4.0.2 → 5.0.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.
@@ -3,21 +3,23 @@ import '../node-global.mjs';
3
3
  * Configuration for index file generation.
4
4
  */
5
5
  export type GenIndexConfig = DeepReadonly<{
6
- /** Command to run for formatting generated files (default: 'npm run fmt') */
7
- formatCommand: string;
8
6
  /** Target directories to generate index files for (string or array of strings) */
9
- targetDirectory: string | string[];
10
- /** File extension of source files to export (default: '.mts') */
11
- sourceExtension?: `.${string}`;
12
- /** File extension to use in export statements (default: '.mjs') */
7
+ targetDirectory: string | readonly string[];
8
+ /** Glob patterns of files to exclude from exports (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'`) */
9
+ excludePatterns?: readonly string[];
10
+ /** File extensions of source files to export (default: ['.ts', '.tsx']) */
11
+ sourceExtensions?: readonly `.${string}`[];
12
+ /** File extension of index files to generate (default: '.ts') */
13
+ indexExtension?: `.${string}`;
14
+ /** File extension to use in export statements (default: '.js') */
13
15
  exportExtension?: `.${string}`;
14
- /** Glob patterns of files to exclude from exports (default: excludes .d.* and .test.* files) */
15
- excludePatterns?: string[];
16
+ /** Command to run for formatting generated files (default: 'npm run fmt') */
17
+ formatCommand?: string;
16
18
  /** Whether to suppress output during execution (default: false) */
17
19
  silent?: boolean;
18
20
  }>;
19
21
  /**
20
- * Generates index.mts files recursively in `config.targetDirectory`.
22
+ * Generates index.ts files recursively in `config.targetDirectory`.
21
23
  * @param config - Configuration for index file generation
22
24
  * @throws Error if any step fails.
23
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"gen-index.d.mts","sourceRoot":"","sources":["../../src/functions/gen-index.mts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;IACxC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IAEtB,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;IAE3B,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAU,QAAQ,cAAc,KAAG,OAAO,CAAC,IAAI,CA4CnE,CAAC"}
1
+ {"version":3,"file":"gen-index.d.mts","sourceRoot":"","sources":["../../src/functions/gen-index.mts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;IACxC,kFAAkF;IAClF,eAAe,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAE5C,+GAA+G;IAC/G,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,IAAI,MAAM,EAAE,EAAE,CAAC;IAE3C,iEAAiE;IACjE,cAAc,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC;IAE9B,kEAAkE;IAClE,eAAe,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC;IAE/B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAYH;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAU,QAAQ,cAAc,KAAG,OAAO,CAAC,IAAI,CA8CnE,CAAC"}
@@ -1,10 +1,10 @@
1
1
  import micromatch from 'micromatch';
2
- import { Result } from 'ts-data-forge';
2
+ import { Result, ISet, Arr, isString } from 'ts-data-forge';
3
3
  import '../node-global.mjs';
4
4
  import { assertPathExists } from './assert-path-exists.mjs';
5
5
 
6
6
  /**
7
- * Generates index.mts files recursively in `config.targetDirectory`.
7
+ * Generates index.ts files recursively in `config.targetDirectory`.
8
8
  * @param config - Configuration for index file generation
9
9
  * @throws Error if any step fails.
10
10
  */
@@ -32,14 +32,16 @@ const genIndex = async (config) => {
32
32
  }
33
33
  echo('✓ Index files generated\n');
34
34
  // Step 3: Format generated files
35
- echo('3. Formatting generated files...');
36
- const fmtResult = await $(filledConfig.formatCommand, {
37
- silent: filledConfig.silent,
38
- });
39
- if (Result.isErr(fmtResult)) {
40
- throw new Error(`Formatting failed: ${fmtResult.value.message}`);
35
+ if (filledConfig.formatCommand !== undefined) {
36
+ echo('3. Formatting generated files...');
37
+ const fmtResult = await $(filledConfig.formatCommand, {
38
+ silent: filledConfig.silent,
39
+ });
40
+ if (Result.isErr(fmtResult)) {
41
+ throw new Error(`Formatting failed: ${fmtResult.value.message}`);
42
+ }
43
+ echo('✓ Formatting completed\n');
41
44
  }
42
- echo('✓ Formatting completed\n');
43
45
  echo('✅ Index file generation completed successfully!\n');
44
46
  }
45
47
  catch (error) {
@@ -49,26 +51,37 @@ const genIndex = async (config) => {
49
51
  };
50
52
  /**
51
53
  * Fills the configuration with default values.
54
+ * Default values:
55
+ * - sourceExtensions: ['.ts']
56
+ * - indexExtension: '.ts'
57
+ * - exportExtension: '.js'
58
+ * - excludePatterns: ['**\/*.{test,spec}.?(c|m)[jt]s?(x)']
59
+ * - silent: false
52
60
  * @param config - The input configuration object.
53
61
  * @returns The configuration object with all required properties filled with defaults.
54
62
  */
55
63
  const fillConfig = (config) => {
56
- const sourceExtension = config.sourceExtension ?? '.mts';
57
- const exportExtension = config.exportExtension ?? '.mjs'; // For ESM imports, .mts resolves to .mjs
64
+ const sourceExtensions = config.sourceExtensions ?? ['.ts'];
65
+ const exportExtension = config.exportExtension ?? '.js'; // For ESM imports, .mts resolves to .mjs
58
66
  return {
59
67
  formatCommand: config.formatCommand,
60
- targetDirectory: config.targetDirectory,
61
- sourceExtension,
68
+ targetDirectory: ISet.create(isString(config.targetDirectory)
69
+ ? [config.targetDirectory]
70
+ : config.targetDirectory),
71
+ excludePatterns: ISet.create(Arr.generate(function* () {
72
+ if (config.excludePatterns !== undefined) {
73
+ yield* config.excludePatterns;
74
+ }
75
+ yield '**/*.{test,spec}.?(c|m)[jt]s?(x)';
76
+ })),
77
+ sourceExtensions: ISet.create(sourceExtensions),
78
+ indexExtension: config.indexExtension ?? '.ts',
62
79
  exportExtension,
63
- excludePatterns: config.excludePatterns ?? [
64
- `*.d${sourceExtension}`,
65
- `*.test${sourceExtension}`,
66
- ],
67
80
  silent: config.silent ?? false,
68
81
  };
69
82
  };
70
83
  /**
71
- * Generates an index.mts file for the given directory.
84
+ * Generates an index.ts file for the given directory.
72
85
  * Recursively calls itself for subdirectories.
73
86
  * @param dirPath - The absolute path to the directory to process.
74
87
  * @param config - The merged configuration object.
@@ -85,6 +98,10 @@ const generateIndexFileForDir = async (dirPath, config, baseDir) => {
85
98
  const entryName = entry.name;
86
99
  const entryPath = path.join(dirPath, entryName);
87
100
  const relativePath = path.relative(actualBaseDir, entryPath);
101
+ if (config.excludePatterns.some((pat) => micromatch.isMatch(relativePath, pat) ||
102
+ micromatch.isMatch(entryName, pat))) {
103
+ continue; // Skip excluded directories/files
104
+ }
88
105
  if (entry.isDirectory()) {
89
106
  subDirectories.push(entryName);
90
107
  // Recursively call for subdirectories first
@@ -96,7 +113,7 @@ const generateIndexFileForDir = async (dirPath, config, baseDir) => {
96
113
  }
97
114
  }
98
115
  const indexContent = generateIndexContent(subDirectories, filesToExport, config);
99
- const indexPath = path.join(dirPath, `index${config.sourceExtension}`);
116
+ const indexPath = path.join(dirPath, `index${config.indexExtension}`);
100
117
  await fs.writeFile(indexPath, indexContent);
101
118
  echo(`Generated: ${path.relative(process.cwd(), indexPath)}`);
102
119
  }
@@ -106,22 +123,28 @@ const generateIndexFileForDir = async (dirPath, config, baseDir) => {
106
123
  };
107
124
  /**
108
125
  * Determines if a file should be exported in the index file.
126
+ * A file is exported if:
127
+ * - It has one of the configured source extensions
128
+ * - It's not an index file itself
129
+ * - It doesn't match any exclusion patterns
109
130
  * @param filePath - The relative path to the file from the target directory.
110
131
  * @param config - The merged configuration object.
111
132
  * @returns True if the file should be exported.
112
133
  */
113
134
  const shouldExportFile = (filePath, config) => {
114
135
  const fileName = path.basename(filePath);
136
+ const ext = path.extname(fileName);
115
137
  // Must have the correct source extension
116
- if (!fileName.endsWith(config.sourceExtension)) {
138
+ if (!config.sourceExtensions.has(ext)) {
117
139
  return false;
118
140
  }
119
141
  // Don't export the index file itself
120
- if (fileName === `index${config.sourceExtension}`) {
142
+ if (/^index\.[cm]?[jt]s[x]?$/u.test(fileName) // Matches index.ts, index.mts, index.js, index.tsx
143
+ ) {
121
144
  return false;
122
145
  }
123
146
  // Check against exclusion patterns
124
- for (const pattern of config.excludePatterns) {
147
+ for (const pattern of config.excludePatterns.values()) {
125
148
  if (micromatch.isMatch(filePath, pattern) ||
126
149
  micromatch.isMatch(fileName, pattern)) {
127
150
  return false;
@@ -140,7 +163,7 @@ const generateIndexContent = (subDirectories, filesToExport, config) => {
140
163
  const exportStatements = [
141
164
  ...subDirectories.map((subDir) => `export * from "./${subDir}/index${config.exportExtension}";`),
142
165
  ...filesToExport.map((file) => {
143
- const fileNameWithoutExt = path.basename(file, config.sourceExtension);
166
+ const fileNameWithoutExt = path.basename(file, path.extname(file));
144
167
  return `export * from "./${fileNameWithoutExt}${config.exportExtension}";`;
145
168
  }),
146
169
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"gen-index.mjs","sources":["../../src/functions/gen-index.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gen-index.mjs","sources":["../../src/functions/gen-index.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-repo-utils",
3
- "version": "4.0.2",
3
+ "version": "5.0.1",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -1,5 +1,5 @@
1
1
  import micromatch from 'micromatch';
2
- import { Result } from 'ts-data-forge';
2
+ import { Arr, ISet, isString, Result } from 'ts-data-forge';
3
3
  import '../node-global.mjs';
4
4
  import { assertPathExists } from './assert-path-exists.mjs';
5
5
 
@@ -7,27 +7,40 @@ import { assertPathExists } from './assert-path-exists.mjs';
7
7
  * Configuration for index file generation.
8
8
  */
9
9
  export type GenIndexConfig = DeepReadonly<{
10
- /** Command to run for formatting generated files (default: 'npm run fmt') */
11
- formatCommand: string;
12
-
13
10
  /** Target directories to generate index files for (string or array of strings) */
14
- targetDirectory: string | string[];
11
+ targetDirectory: string | readonly string[];
12
+
13
+ /** Glob patterns of files to exclude from exports (default: excludes `'**\/*.{test,spec}.?(c|m)[jt]s?(x)'`) */
14
+ excludePatterns?: readonly string[];
15
+
16
+ /** File extensions of source files to export (default: ['.ts', '.tsx']) */
17
+ sourceExtensions?: readonly `.${string}`[];
15
18
 
16
- /** File extension of source files to export (default: '.mts') */
17
- sourceExtension?: `.${string}`;
19
+ /** File extension of index files to generate (default: '.ts') */
20
+ indexExtension?: `.${string}`;
18
21
 
19
- /** File extension to use in export statements (default: '.mjs') */
22
+ /** File extension to use in export statements (default: '.js') */
20
23
  exportExtension?: `.${string}`;
21
24
 
22
- /** Glob patterns of files to exclude from exports (default: excludes .d.* and .test.* files) */
23
- excludePatterns?: string[];
25
+ /** Command to run for formatting generated files (default: 'npm run fmt') */
26
+ formatCommand?: string;
24
27
 
25
28
  /** Whether to suppress output during execution (default: false) */
26
29
  silent?: boolean;
27
30
  }>;
28
31
 
32
+ type GenIndexConfigInternal = DeepReadonly<{
33
+ formatCommand: string | undefined;
34
+ targetDirectory: ISet<string>;
35
+ excludePatterns: ISet<string>;
36
+ sourceExtensions: ISet<`.${string}`>;
37
+ indexExtension: `.${string}`;
38
+ exportExtension: `.${string}`;
39
+ silent: boolean;
40
+ }>;
41
+
29
42
  /**
30
- * Generates index.mts files recursively in `config.targetDirectory`.
43
+ * Generates index.ts files recursively in `config.targetDirectory`.
31
44
  * @param config - Configuration for index file generation
32
45
  * @throws Error if any step fails.
33
46
  */
@@ -35,7 +48,7 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
35
48
  echo('Starting index file generation...\n');
36
49
 
37
50
  // Merge config with defaults
38
- const filledConfig: DeepRequired<GenIndexConfig> = fillConfig(config);
51
+ const filledConfig: GenIndexConfigInternal = fillConfig(config);
39
52
 
40
53
  // Normalize target directories to array
41
54
  const targetDirs =
@@ -61,14 +74,16 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
61
74
  echo('✓ Index files generated\n');
62
75
 
63
76
  // Step 3: Format generated files
64
- echo('3. Formatting generated files...');
65
- const fmtResult = await $(filledConfig.formatCommand, {
66
- silent: filledConfig.silent,
67
- });
68
- if (Result.isErr(fmtResult)) {
69
- throw new Error(`Formatting failed: ${fmtResult.value.message}`);
77
+ if (filledConfig.formatCommand !== undefined) {
78
+ echo('3. Formatting generated files...');
79
+ const fmtResult = await $(filledConfig.formatCommand, {
80
+ silent: filledConfig.silent,
81
+ });
82
+ if (Result.isErr(fmtResult)) {
83
+ throw new Error(`Formatting failed: ${fmtResult.value.message}`);
84
+ }
85
+ echo('✓ Formatting completed\n');
70
86
  }
71
- echo('✓ Formatting completed\n');
72
87
 
73
88
  echo('✅ Index file generation completed successfully!\n');
74
89
  } catch (error) {
@@ -79,28 +94,43 @@ export const genIndex = async (config: GenIndexConfig): Promise<void> => {
79
94
 
80
95
  /**
81
96
  * Fills the configuration with default values.
97
+ * Default values:
98
+ * - sourceExtensions: ['.ts']
99
+ * - indexExtension: '.ts'
100
+ * - exportExtension: '.js'
101
+ * - excludePatterns: ['**\/*.{test,spec}.?(c|m)[jt]s?(x)']
102
+ * - silent: false
82
103
  * @param config - The input configuration object.
83
104
  * @returns The configuration object with all required properties filled with defaults.
84
105
  */
85
- const fillConfig = (config: GenIndexConfig): DeepRequired<GenIndexConfig> => {
86
- const sourceExtension = config.sourceExtension ?? '.mts';
87
- const exportExtension = config.exportExtension ?? '.mjs'; // For ESM imports, .mts resolves to .mjs
106
+ const fillConfig = (config: GenIndexConfig): GenIndexConfigInternal => {
107
+ const sourceExtensions = config.sourceExtensions ?? ['.ts'];
108
+ const exportExtension = config.exportExtension ?? '.js'; // For ESM imports, .mts resolves to .mjs
88
109
 
89
110
  return {
90
111
  formatCommand: config.formatCommand,
91
- targetDirectory: config.targetDirectory,
92
- sourceExtension,
112
+ targetDirectory: ISet.create(
113
+ isString(config.targetDirectory)
114
+ ? [config.targetDirectory]
115
+ : config.targetDirectory,
116
+ ),
117
+ excludePatterns: ISet.create(
118
+ Arr.generate(function* () {
119
+ if (config.excludePatterns !== undefined) {
120
+ yield* config.excludePatterns;
121
+ }
122
+ yield '**/*.{test,spec}.?(c|m)[jt]s?(x)';
123
+ }),
124
+ ),
125
+ sourceExtensions: ISet.create(sourceExtensions),
126
+ indexExtension: config.indexExtension ?? '.ts',
93
127
  exportExtension,
94
- excludePatterns: config.excludePatterns ?? [
95
- `*.d${sourceExtension}`,
96
- `*.test${sourceExtension}`,
97
- ],
98
128
  silent: config.silent ?? false,
99
129
  };
100
130
  };
101
131
 
102
132
  /**
103
- * Generates an index.mts file for the given directory.
133
+ * Generates an index.ts file for the given directory.
104
134
  * Recursively calls itself for subdirectories.
105
135
  * @param dirPath - The absolute path to the directory to process.
106
136
  * @param config - The merged configuration object.
@@ -109,11 +139,7 @@ const fillConfig = (config: GenIndexConfig): DeepRequired<GenIndexConfig> => {
109
139
  */
110
140
  const generateIndexFileForDir = async (
111
141
  dirPath: string,
112
- config: DeepReadonly<{
113
- sourceExtension: `.${string}`;
114
- exportExtension: `.${string}`;
115
- excludePatterns: string[];
116
- }>,
142
+ config: GenIndexConfigInternal,
117
143
  baseDir?: string,
118
144
  ): Promise<void> => {
119
145
  try {
@@ -128,6 +154,16 @@ const generateIndexFileForDir = async (
128
154
  const entryPath = path.join(dirPath, entryName);
129
155
  const relativePath = path.relative(actualBaseDir, entryPath);
130
156
 
157
+ if (
158
+ config.excludePatterns.some(
159
+ (pat) =>
160
+ micromatch.isMatch(relativePath, pat) ||
161
+ micromatch.isMatch(entryName, pat),
162
+ )
163
+ ) {
164
+ continue; // Skip excluded directories/files
165
+ }
166
+
131
167
  if (entry.isDirectory()) {
132
168
  subDirectories.push(entryName);
133
169
  // Recursively call for subdirectories first
@@ -144,7 +180,7 @@ const generateIndexFileForDir = async (
144
180
  config,
145
181
  );
146
182
 
147
- const indexPath = path.join(dirPath, `index${config.sourceExtension}`);
183
+ const indexPath = path.join(dirPath, `index${config.indexExtension}`);
148
184
 
149
185
  await fs.writeFile(indexPath, indexContent);
150
186
  echo(`Generated: ${path.relative(process.cwd(), indexPath)}`);
@@ -157,31 +193,36 @@ const generateIndexFileForDir = async (
157
193
 
158
194
  /**
159
195
  * Determines if a file should be exported in the index file.
196
+ * A file is exported if:
197
+ * - It has one of the configured source extensions
198
+ * - It's not an index file itself
199
+ * - It doesn't match any exclusion patterns
160
200
  * @param filePath - The relative path to the file from the target directory.
161
201
  * @param config - The merged configuration object.
162
202
  * @returns True if the file should be exported.
163
203
  */
164
204
  const shouldExportFile = (
165
205
  filePath: string,
166
- config: DeepReadonly<{
167
- sourceExtension: `.${string}`;
168
- excludePatterns: string[];
169
- }>,
206
+ config: GenIndexConfigInternal,
170
207
  ): boolean => {
171
208
  const fileName = path.basename(filePath);
172
209
 
210
+ const ext = path.extname(fileName);
211
+
173
212
  // Must have the correct source extension
174
- if (!fileName.endsWith(config.sourceExtension)) {
213
+ if (!config.sourceExtensions.has(ext)) {
175
214
  return false;
176
215
  }
177
216
 
178
217
  // Don't export the index file itself
179
- if (fileName === `index${config.sourceExtension}`) {
218
+ if (
219
+ /^index\.[cm]?[jt]s[x]?$/u.test(fileName) // Matches index.ts, index.mts, index.js, index.tsx
220
+ ) {
180
221
  return false;
181
222
  }
182
223
 
183
224
  // Check against exclusion patterns
184
- for (const pattern of config.excludePatterns) {
225
+ for (const pattern of config.excludePatterns.values()) {
185
226
  if (
186
227
  micromatch.isMatch(filePath, pattern) ||
187
228
  micromatch.isMatch(fileName, pattern)
@@ -203,17 +244,14 @@ const shouldExportFile = (
203
244
  const generateIndexContent = (
204
245
  subDirectories: readonly string[],
205
246
  filesToExport: readonly string[],
206
- config: DeepReadonly<{
207
- sourceExtension: string;
208
- exportExtension: `.${string}`;
209
- }>,
247
+ config: GenIndexConfigInternal,
210
248
  ): string => {
211
249
  const exportStatements = [
212
250
  ...subDirectories.map(
213
251
  (subDir) => `export * from "./${subDir}/index${config.exportExtension}";`,
214
252
  ),
215
253
  ...filesToExport.map((file) => {
216
- const fileNameWithoutExt = path.basename(file, config.sourceExtension);
254
+ const fileNameWithoutExt = path.basename(file, path.extname(file));
217
255
 
218
256
  return `export * from "./${fileNameWithoutExt}${config.exportExtension}";`;
219
257
  }),
@@ -0,0 +1,18 @@
1
+ describe('index file regex', () => {
2
+ const reg = /^index\.[cm]?[jt]s[x]?$/u;
3
+
4
+ test.each([
5
+ ['index.ts', true],
6
+ ['index.js', true],
7
+ ['index.mts', true],
8
+ ['index.mjs', true],
9
+ ['index.cts', true],
10
+ ['index.cjs', true],
11
+ ['index.tsx', true],
12
+ ['index.jsx', true],
13
+ ['not-index.ts', false],
14
+ ['index.txt', false],
15
+ ] as const)('reg.test($0) to be $1', (fileName, expected) => {
16
+ expect(reg.test(fileName)).toBe(expected);
17
+ });
18
+ });