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.
- package/dist/functions/gen-index.d.mts +11 -9
- package/dist/functions/gen-index.d.mts.map +1 -1
- package/dist/functions/gen-index.mjs +46 -23
- package/dist/functions/gen-index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/functions/gen-index.mts +85 -47
- package/src/functions/gen-index.test.mts +18 -0
|
@@ -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
|
-
/**
|
|
11
|
-
|
|
12
|
-
/** File
|
|
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
|
-
/**
|
|
15
|
-
|
|
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.
|
|
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,
|
|
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.
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
57
|
-
const exportExtension = config.exportExtension ?? '.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 (!
|
|
138
|
+
if (!config.sourceExtensions.has(ext)) {
|
|
117
139
|
return false;
|
|
118
140
|
}
|
|
119
141
|
// Don't export the index file itself
|
|
120
|
-
if (fileName
|
|
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,
|
|
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":";;;;;
|
|
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,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
|
|
17
|
-
|
|
19
|
+
/** File extension of index files to generate (default: '.ts') */
|
|
20
|
+
indexExtension?: `.${string}`;
|
|
18
21
|
|
|
19
|
-
/** File extension to use in export statements (default: '.
|
|
22
|
+
/** File extension to use in export statements (default: '.js') */
|
|
20
23
|
exportExtension?: `.${string}`;
|
|
21
24
|
|
|
22
|
-
/**
|
|
23
|
-
|
|
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.
|
|
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:
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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):
|
|
86
|
-
const
|
|
87
|
-
const exportExtension = config.exportExtension ?? '.
|
|
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:
|
|
92
|
-
|
|
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.
|
|
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:
|
|
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.
|
|
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:
|
|
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 (!
|
|
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 (
|
|
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:
|
|
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,
|
|
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
|
+
});
|