@reliverse/dler 1.7.73 → 1.7.74
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/README.md +2 -2
- package/bin/app/agg/cmd.d.ts +40 -0
- package/bin/app/agg/cmd.js +833 -27
- package/bin/app/build/postbuild.js +1 -0
- package/bin/app/cmds.d.ts +230 -4
- package/bin/app/cmds.js +36 -19
- package/bin/app/config/cmd.d.ts +18 -0
- package/bin/app/config/cmd.js +43 -0
- package/bin/app/config/impl/typebox.d.ts +8 -0
- package/bin/app/config/impl/typebox.js +82 -0
- package/bin/app/mkdist/cmd.js +1 -1
- package/bin/app/pub/cmd.js +1 -1
- package/bin/app/rempts/cmd.js +146 -8
- package/bin/cli.js +14 -20
- package/bin/libs/cfg/cfg-impl/{cfg-types.d.ts → cfg-dler.d.ts} +76 -0
- package/bin/libs/cfg/cfg-mod.d.ts +2 -2
- package/bin/libs/cfg/cfg-mod.js +1 -1
- package/bin/libs/get/get-impl/get-core.d.ts +3 -0
- package/bin/libs/get/get-impl/get-core.js +450 -0
- package/bin/libs/get/get-mod.d.ts +0 -6
- package/bin/libs/get/get-mod.js +38 -459
- package/bin/libs/sdk/sdk-impl/build/providers/bun/single-file.d.ts +2 -2
- package/bin/libs/sdk/sdk-impl/config/core.d.ts +8 -0
- package/bin/libs/sdk/sdk-impl/config/core.js +231 -0
- package/bin/libs/sdk/sdk-impl/config/info.js +1 -1
- package/bin/libs/sdk/sdk-impl/config/{init.js → prepare.js} +3 -1
- package/bin/libs/sdk/sdk-impl/magic/magic-spells.js +2 -1
- package/bin/libs/sdk/sdk-impl/utils/pm/pm-meta.d.ts +1 -1
- package/bin/libs/sdk/sdk-impl/utils/pm/pm-meta.js +2 -2
- package/bin/libs/sdk/sdk-impl/utils/resolve-cross-libs.d.ts +1 -3
- package/bin/libs/sdk/sdk-impl/utils/resolve-cross-libs.js +432 -9
- package/bin/libs/sdk/sdk-mod.d.ts +7 -4
- package/bin/libs/sdk/sdk-mod.js +18 -7
- package/bin/mod.js +4 -1
- package/package.json +5 -5
- package/bin/app/agg/impl.d.ts +0 -39
- package/bin/app/agg/impl.js +0 -341
- package/bin/app/agg/run.d.ts +0 -1
- package/bin/app/agg/run.js +0 -136
- package/bin/libs/cfg/cfg-impl/cfg-consts.d.ts +0 -77
- package/bin/libs/cfg/cfg-impl/cfg-types.js +0 -0
- /package/bin/libs/cfg/cfg-impl/{cfg-consts.js → cfg-dler.js} +0 -0
- /package/bin/libs/sdk/sdk-impl/config/{init.d.ts → prepare.d.ts} +0 -0
package/bin/app/agg/cmd.js
CHANGED
|
@@ -1,100 +1,120 @@
|
|
|
1
1
|
import path from "@reliverse/pathkit";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import fs from "@reliverse/relifso";
|
|
3
|
+
import { relinka } from "@reliverse/relinka";
|
|
4
|
+
import {
|
|
5
|
+
selectPrompt,
|
|
6
|
+
confirmPrompt,
|
|
7
|
+
inputPrompt,
|
|
8
|
+
defineArgs,
|
|
9
|
+
defineCommand,
|
|
10
|
+
} from "@reliverse/rempts";
|
|
11
|
+
|
|
12
|
+
import { getConfigDler } from "~/libs/sdk/sdk-impl/config/load";
|
|
13
|
+
|
|
14
|
+
const AGGREGATOR_START = "// AUTO-GENERATED AGGREGATOR START (via `dler agg`)";
|
|
15
|
+
const AGGREGATOR_END = "// AUTO-GENERATED AGGREGATOR END";
|
|
16
|
+
|
|
4
17
|
export default defineCommand({
|
|
5
18
|
args: defineArgs({
|
|
6
19
|
imports: {
|
|
7
20
|
description: "If true, produce import lines instead of export lines",
|
|
8
|
-
type: "boolean"
|
|
21
|
+
type: "boolean",
|
|
9
22
|
},
|
|
10
23
|
input: {
|
|
11
24
|
description: "Directory containing .ts/.js files (--input <directory>)",
|
|
12
|
-
type: "string"
|
|
25
|
+
type: "string",
|
|
13
26
|
},
|
|
14
27
|
named: {
|
|
15
28
|
description: "Parse each file for named exports (function/class/const/let)",
|
|
16
29
|
type: "boolean",
|
|
17
|
-
default: true
|
|
30
|
+
default: true,
|
|
18
31
|
},
|
|
19
32
|
out: {
|
|
20
33
|
description: "Output aggregator file path (--out <fileName>)",
|
|
21
|
-
type: "string"
|
|
34
|
+
type: "string",
|
|
22
35
|
},
|
|
23
36
|
recursive: {
|
|
24
|
-
description:
|
|
37
|
+
description:
|
|
38
|
+
"Recursively scan subdirectories (default true) (false means only scan the files in the current directory and not subdirectories)",
|
|
25
39
|
type: "boolean",
|
|
26
|
-
default: true
|
|
40
|
+
default: true,
|
|
27
41
|
},
|
|
28
42
|
strip: {
|
|
29
43
|
description: "Remove specified path prefix from final imports/exports",
|
|
30
|
-
type: "string"
|
|
44
|
+
type: "string",
|
|
31
45
|
},
|
|
32
46
|
sort: {
|
|
33
47
|
description: "Sort aggregated lines alphabetically",
|
|
34
|
-
type: "boolean"
|
|
48
|
+
type: "boolean",
|
|
35
49
|
},
|
|
36
50
|
header: {
|
|
37
51
|
description: "Add a header comment to the aggregator output",
|
|
38
|
-
type: "string"
|
|
52
|
+
type: "string",
|
|
39
53
|
},
|
|
40
54
|
verbose: {
|
|
41
55
|
description: "Enable verbose logging",
|
|
42
|
-
type: "boolean"
|
|
56
|
+
type: "boolean",
|
|
43
57
|
},
|
|
44
58
|
includeInternal: {
|
|
45
59
|
description: "Include files marked as internal (starting with #)",
|
|
46
|
-
type: "boolean"
|
|
60
|
+
type: "boolean",
|
|
47
61
|
},
|
|
48
62
|
internalMarker: {
|
|
49
63
|
description: "Marker for internal files (default: #)",
|
|
50
64
|
type: "string",
|
|
51
|
-
default: "#"
|
|
65
|
+
default: "#",
|
|
52
66
|
},
|
|
53
67
|
override: {
|
|
54
68
|
description: "Override entire file instead of updating only the aggregator block",
|
|
55
|
-
type: "boolean"
|
|
69
|
+
type: "boolean",
|
|
56
70
|
},
|
|
57
71
|
extensions: {
|
|
58
|
-
description:
|
|
72
|
+
description:
|
|
73
|
+
"Comma-separated list of file extensions to process (default: .ts,.js,.mts,.cts,.mjs,.cjs)",
|
|
59
74
|
type: "string",
|
|
60
|
-
default: ".ts,.js,.mts,.cts,.mjs,.cjs"
|
|
75
|
+
default: ".ts,.js,.mts,.cts,.mjs,.cjs",
|
|
61
76
|
},
|
|
62
77
|
separateTypesFile: {
|
|
63
78
|
description: "Create a separate file for type exports",
|
|
64
|
-
type: "boolean"
|
|
79
|
+
type: "boolean",
|
|
65
80
|
},
|
|
66
81
|
typesOut: {
|
|
67
82
|
description: "Output file path for types (used when separateTypesFile is true)",
|
|
68
|
-
type: "string"
|
|
83
|
+
type: "string",
|
|
69
84
|
},
|
|
70
85
|
nonInteractive: {
|
|
71
86
|
description: "Disable interactive prompts and require all arguments to be provided via flags",
|
|
72
87
|
type: "boolean",
|
|
73
|
-
default: false
|
|
74
|
-
}
|
|
88
|
+
default: false,
|
|
89
|
+
},
|
|
75
90
|
}),
|
|
76
91
|
async run({ args }) {
|
|
77
92
|
const resolvedArgs = { ...args };
|
|
93
|
+
|
|
94
|
+
// Handle required arguments with prompts when nonInteractive is false
|
|
78
95
|
if (!args.nonInteractive) {
|
|
79
96
|
if (!args.input) {
|
|
80
97
|
resolvedArgs.input = await inputPrompt({
|
|
81
98
|
title: "Enter input directory containing .ts/.js files:",
|
|
82
|
-
defaultValue: ""
|
|
99
|
+
defaultValue: "",
|
|
83
100
|
});
|
|
84
101
|
}
|
|
102
|
+
|
|
85
103
|
if (!args.out) {
|
|
86
104
|
resolvedArgs.out = await inputPrompt({
|
|
87
105
|
title: "Enter output aggregator file path:",
|
|
88
|
-
defaultValue: ""
|
|
106
|
+
defaultValue: "",
|
|
89
107
|
});
|
|
90
108
|
}
|
|
109
|
+
|
|
91
110
|
if (args.separateTypesFile && !args.typesOut) {
|
|
92
111
|
resolvedArgs.typesOut = await inputPrompt({
|
|
93
112
|
title: "Enter output file path for types:",
|
|
94
|
-
defaultValue: resolvedArgs.out.replace(/\.(ts|js)$/, ".types.$1")
|
|
113
|
+
defaultValue: resolvedArgs.out.replace(/\.(ts|js)$/, ".types.$1"),
|
|
95
114
|
});
|
|
96
115
|
}
|
|
97
116
|
} else {
|
|
117
|
+
// Validate required arguments in non-interactive mode
|
|
98
118
|
if (!args.input) {
|
|
99
119
|
throw new Error("Missing required argument: --input");
|
|
100
120
|
}
|
|
@@ -103,10 +123,11 @@ export default defineCommand({
|
|
|
103
123
|
}
|
|
104
124
|
if (args.separateTypesFile && !args.typesOut) {
|
|
105
125
|
throw new Error(
|
|
106
|
-
"Missing required argument: --typesOut (required when --separateTypesFile is true)"
|
|
126
|
+
"Missing required argument: --typesOut (required when --separateTypesFile is true)",
|
|
107
127
|
);
|
|
108
128
|
}
|
|
109
129
|
}
|
|
130
|
+
|
|
110
131
|
await useAggregator({
|
|
111
132
|
inputDir: path.resolve(resolvedArgs.input),
|
|
112
133
|
isRecursive: !!resolvedArgs.recursive,
|
|
@@ -122,7 +143,792 @@ export default defineCommand({
|
|
|
122
143
|
overrideFile: !!resolvedArgs.override,
|
|
123
144
|
fileExtensions: resolvedArgs.extensions.split(",").map((ext) => ext.trim()),
|
|
124
145
|
separateTypesFile: !!resolvedArgs.separateTypesFile,
|
|
125
|
-
typesOutFile: resolvedArgs.typesOut ? path.resolve(resolvedArgs.typesOut) :
|
|
146
|
+
typesOutFile: resolvedArgs.typesOut ? path.resolve(resolvedArgs.typesOut) : undefined,
|
|
126
147
|
});
|
|
127
|
-
}
|
|
148
|
+
},
|
|
128
149
|
});
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Checks if a file exists at the given path
|
|
153
|
+
*/
|
|
154
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
155
|
+
return await fs.pathExists(filePath);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Checks if the first line of a file contains the disable aggregation comment
|
|
160
|
+
*/
|
|
161
|
+
async function isAggregationDisabled(filePath: string): Promise<boolean> {
|
|
162
|
+
try {
|
|
163
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
164
|
+
const firstLine = content.split("\n")[0]?.trim();
|
|
165
|
+
return firstLine === "// <dler-disable-agg>";
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Finds the main package based on dler configuration with fallbacks
|
|
173
|
+
*/
|
|
174
|
+
async function findMainEntryFile(config: any): Promise<string | null> {
|
|
175
|
+
const { coreEntryFile, coreEntrySrcDir } = config;
|
|
176
|
+
|
|
177
|
+
// Check the configured entry file first
|
|
178
|
+
if (coreEntryFile && coreEntrySrcDir) {
|
|
179
|
+
const configuredPath = path.join(coreEntrySrcDir, coreEntryFile);
|
|
180
|
+
if (await fileExists(configuredPath)) {
|
|
181
|
+
return configuredPath;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Fallback to common entry file patterns
|
|
186
|
+
const fallbackPatterns = [
|
|
187
|
+
path.join(coreEntrySrcDir || "src", "mod.ts"),
|
|
188
|
+
path.join(coreEntrySrcDir || "src", "index.ts"),
|
|
189
|
+
path.join(coreEntrySrcDir || "src", "mod.js"),
|
|
190
|
+
path.join(coreEntrySrcDir || "src", "index.js"),
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
for (const pattern of fallbackPatterns) {
|
|
194
|
+
if (await fileExists(pattern)) {
|
|
195
|
+
return pattern;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export async function promptAggCommand() {
|
|
203
|
+
// Try to load config and check for libs
|
|
204
|
+
const config = await getConfigDler();
|
|
205
|
+
let selectedLibName: string | null = null;
|
|
206
|
+
|
|
207
|
+
// Check for main package
|
|
208
|
+
const mainEntryFile = await findMainEntryFile(config);
|
|
209
|
+
const isMainDisabled = mainEntryFile ? await isAggregationDisabled(mainEntryFile) : false;
|
|
210
|
+
|
|
211
|
+
if (config?.libsList && Object.keys(config.libsList).length > 0) {
|
|
212
|
+
const libEntries = await Promise.all(
|
|
213
|
+
Object.entries(config.libsList).map(async ([name, lib]) => {
|
|
214
|
+
const libMainFile = `${config.libsDirSrc}/${lib.libMainFile}`;
|
|
215
|
+
const isLibDisabled = await isAggregationDisabled(libMainFile);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
name,
|
|
219
|
+
lib,
|
|
220
|
+
isDisabled: isLibDisabled,
|
|
221
|
+
};
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const libs = libEntries
|
|
226
|
+
.filter(({ isDisabled }) => !isDisabled)
|
|
227
|
+
.map(({ name, lib }) => ({
|
|
228
|
+
value: name,
|
|
229
|
+
label: name,
|
|
230
|
+
hint: `${config.libsDirSrc}/${lib.libDirName}/${lib.libDirName}-impl`,
|
|
231
|
+
}));
|
|
232
|
+
|
|
233
|
+
// Add main package option if found and not disabled
|
|
234
|
+
if (mainEntryFile && !isMainDisabled) {
|
|
235
|
+
libs.unshift({
|
|
236
|
+
value: "main",
|
|
237
|
+
label: "Main package",
|
|
238
|
+
hint: mainEntryFile,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Add "Skip" option
|
|
243
|
+
libs.push({ value: "", label: "Skip selection", hint: "" });
|
|
244
|
+
|
|
245
|
+
selectedLibName = await selectPrompt({
|
|
246
|
+
title: "Select a package to aggregate or skip",
|
|
247
|
+
options: libs,
|
|
248
|
+
});
|
|
249
|
+
} else if (mainEntryFile && !isMainDisabled) {
|
|
250
|
+
// If no libs but main package exists and is not disabled, offer it as the only option
|
|
251
|
+
const shouldUseMain = await confirmPrompt({
|
|
252
|
+
title: `Use main package for aggregation? (Found: ${mainEntryFile})`,
|
|
253
|
+
defaultValue: true,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if (shouldUseMain) {
|
|
257
|
+
selectedLibName = "main";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// If lib selected, use its config
|
|
262
|
+
let imports = false;
|
|
263
|
+
let input = "";
|
|
264
|
+
let named = true;
|
|
265
|
+
let out = "";
|
|
266
|
+
let recursive = true;
|
|
267
|
+
let strip = "";
|
|
268
|
+
let separateTypesFile = false;
|
|
269
|
+
let typesOut = "";
|
|
270
|
+
|
|
271
|
+
if (selectedLibName && selectedLibName !== "") {
|
|
272
|
+
if (selectedLibName === "main" && mainEntryFile && !isMainDisabled) {
|
|
273
|
+
// Use main package configuration
|
|
274
|
+
const entryDir = path.dirname(mainEntryFile);
|
|
275
|
+
|
|
276
|
+
input = entryDir;
|
|
277
|
+
out = mainEntryFile;
|
|
278
|
+
strip = entryDir;
|
|
279
|
+
} else if (selectedLibName === "main" && isMainDisabled) {
|
|
280
|
+
// Main package is disabled, exit early
|
|
281
|
+
relinka.log("Main package aggregation is disabled due to <dler-disable-agg> comment.");
|
|
282
|
+
return;
|
|
283
|
+
} else {
|
|
284
|
+
// Use library configuration
|
|
285
|
+
const libConfig = config?.libsList?.[selectedLibName];
|
|
286
|
+
if (config && libConfig) {
|
|
287
|
+
input = `${config.libsDirSrc}/${libConfig.libDirName}/${libConfig.libDirName}-impl`;
|
|
288
|
+
out = `${config.libsDirSrc}/${libConfig.libMainFile}`;
|
|
289
|
+
strip = `${config.libsDirSrc}/${libConfig.libDirName}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Only prompt for values not set by lib config
|
|
295
|
+
if (!selectedLibName || !input) {
|
|
296
|
+
input = await inputPrompt({
|
|
297
|
+
title: "Enter the input directory",
|
|
298
|
+
defaultValue: input,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Check if manually entered input corresponds to a disabled file
|
|
302
|
+
if (input) {
|
|
303
|
+
// Check if the input is pointing to a disabled main file (directory or file)
|
|
304
|
+
if (mainEntryFile && isMainDisabled) {
|
|
305
|
+
const mainEntryDir = path.dirname(mainEntryFile);
|
|
306
|
+
if (
|
|
307
|
+
path.resolve(input) === path.resolve(mainEntryDir) ||
|
|
308
|
+
path.resolve(input) === path.resolve(mainEntryFile)
|
|
309
|
+
) {
|
|
310
|
+
relinka.log("Main package aggregation is disabled due to <dler-disable-agg> comment.");
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check if the input is pointing to a disabled library
|
|
316
|
+
if (config?.libsList) {
|
|
317
|
+
for (const [libName, libConfig] of Object.entries(config.libsList)) {
|
|
318
|
+
const libImplPath = `${config.libsDirSrc}/${libConfig.libDirName}/${libConfig.libDirName}-impl`;
|
|
319
|
+
const libMainFile = `${config.libsDirSrc}/${libConfig.libMainFile}`;
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
path.resolve(input) === path.resolve(libImplPath) ||
|
|
323
|
+
path.resolve(input) === path.resolve(libMainFile)
|
|
324
|
+
) {
|
|
325
|
+
const isLibDisabled = await isAggregationDisabled(libMainFile);
|
|
326
|
+
if (isLibDisabled) {
|
|
327
|
+
relinka.log(
|
|
328
|
+
`Library "${libName}" aggregation is disabled due to <dler-disable-agg> comment.`,
|
|
329
|
+
);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Ask for verbose mode first to determine if we should show additional options
|
|
339
|
+
const verbose = await confirmPrompt({
|
|
340
|
+
title: "Enable verbose logging and additional options?",
|
|
341
|
+
defaultValue: false,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Default values for non-essential options
|
|
345
|
+
let sortLines = false;
|
|
346
|
+
let headerComment = "";
|
|
347
|
+
let includeInternal = false;
|
|
348
|
+
let internalMarker = "#";
|
|
349
|
+
let overrideFile = false;
|
|
350
|
+
let extensions = ".ts,.js,.mts,.cts,.mjs,.cjs";
|
|
351
|
+
|
|
352
|
+
// Only ask non-essential questions if verbose mode is enabled
|
|
353
|
+
if (verbose) {
|
|
354
|
+
sortLines = await confirmPrompt({
|
|
355
|
+
title: "Sort aggregated lines alphabetically?",
|
|
356
|
+
defaultValue: false,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
headerComment = await inputPrompt({
|
|
360
|
+
title: "Add a header comment to the aggregator output (optional):",
|
|
361
|
+
defaultValue: "",
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
includeInternal = await confirmPrompt({
|
|
365
|
+
title: "Include files marked as internal (starting with #)?",
|
|
366
|
+
defaultValue: false,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
internalMarker = await inputPrompt({
|
|
370
|
+
title: "Marker for internal files:",
|
|
371
|
+
defaultValue: "#",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
overrideFile = await confirmPrompt({
|
|
375
|
+
title: "Override entire file instead of updating only the aggregator block?",
|
|
376
|
+
defaultValue: false,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
extensions = await inputPrompt({
|
|
380
|
+
title: "File extensions to process (comma-separated):",
|
|
381
|
+
defaultValue: ".ts,.js,.mts,.cts,.mjs,.cjs",
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
imports = await confirmPrompt({
|
|
385
|
+
title: "Do you want to generate imports instead of exports? (N generates exports)",
|
|
386
|
+
defaultValue: imports,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
named = await confirmPrompt({
|
|
390
|
+
title: imports
|
|
391
|
+
? "Do you want to generate named imports?"
|
|
392
|
+
: "Do you want to generate named exports?",
|
|
393
|
+
defaultValue: named,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
recursive = await confirmPrompt({
|
|
397
|
+
title: "Do you want to recursively scan subdirectories?",
|
|
398
|
+
defaultValue: recursive,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
separateTypesFile = await confirmPrompt({
|
|
402
|
+
title: "Do you want to create a separate file for type exports?",
|
|
403
|
+
defaultValue: separateTypesFile,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!selectedLibName || !out) {
|
|
408
|
+
out = await inputPrompt({
|
|
409
|
+
title: "Enter the output file",
|
|
410
|
+
defaultValue: out,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (!selectedLibName || !strip) {
|
|
415
|
+
strip = await inputPrompt({
|
|
416
|
+
title: "Enter the path to strip from the final imports/exports",
|
|
417
|
+
defaultValue: strip,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (separateTypesFile) {
|
|
422
|
+
typesOut = await inputPrompt({
|
|
423
|
+
title: "Enter the output file for types",
|
|
424
|
+
defaultValue: out.replace(/\.(ts|js)$/, ".types.$1"),
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
await useAggregator({
|
|
429
|
+
inputDir: path.resolve(input),
|
|
430
|
+
isRecursive: recursive,
|
|
431
|
+
outFile: path.resolve(out),
|
|
432
|
+
stripPrefix: strip ? path.resolve(strip) : "",
|
|
433
|
+
useImport: imports,
|
|
434
|
+
useNamed: named,
|
|
435
|
+
sortLines: sortLines,
|
|
436
|
+
headerComment: headerComment,
|
|
437
|
+
verbose: verbose,
|
|
438
|
+
includeInternal: includeInternal,
|
|
439
|
+
internalMarker: internalMarker,
|
|
440
|
+
overrideFile: overrideFile,
|
|
441
|
+
fileExtensions: extensions.split(",").map((ext) => ext.trim()),
|
|
442
|
+
separateTypesFile: separateTypesFile,
|
|
443
|
+
typesOutFile: typesOut ? path.resolve(typesOut) : undefined,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Aggregator supporting:
|
|
449
|
+
* - --import or default export,
|
|
450
|
+
* - star or named exports,
|
|
451
|
+
* - separate "type" vs "value" for both import and export.
|
|
452
|
+
*
|
|
453
|
+
* Options:
|
|
454
|
+
* - Option to ignore specific directories (default: node_modules, .git)
|
|
455
|
+
* - Option to sort aggregated lines alphabetically.
|
|
456
|
+
* - Option to add a header comment in the aggregator output.
|
|
457
|
+
* - Option to enable verbose logging.
|
|
458
|
+
* - Deduplicates overloaded export names.
|
|
459
|
+
* - Skips files whose basenames start with an internal marker (default: "#")
|
|
460
|
+
* unless includeInternal is true or an alternative marker is provided.
|
|
461
|
+
* - By default, updates only the auto-generated block in the aggregator file,
|
|
462
|
+
* leaving any other content intact. Pass `overrideFile: true` to rewrite the entire file.
|
|
463
|
+
*/
|
|
464
|
+
export async function useAggregator({
|
|
465
|
+
inputDir,
|
|
466
|
+
isRecursive,
|
|
467
|
+
outFile,
|
|
468
|
+
stripPrefix,
|
|
469
|
+
useImport,
|
|
470
|
+
useNamed,
|
|
471
|
+
ignoreDirs = ["node_modules", ".git"],
|
|
472
|
+
sortLines = false,
|
|
473
|
+
headerComment = "",
|
|
474
|
+
verbose = false,
|
|
475
|
+
includeInternal = false,
|
|
476
|
+
internalMarker = "#",
|
|
477
|
+
overrideFile = false,
|
|
478
|
+
fileExtensions = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"],
|
|
479
|
+
separateTypesFile = false,
|
|
480
|
+
typesOutFile,
|
|
481
|
+
}: {
|
|
482
|
+
inputDir: string;
|
|
483
|
+
isRecursive: boolean;
|
|
484
|
+
outFile: string;
|
|
485
|
+
stripPrefix: string;
|
|
486
|
+
useImport: boolean;
|
|
487
|
+
useNamed: boolean;
|
|
488
|
+
ignoreDirs?: string[];
|
|
489
|
+
sortLines?: boolean;
|
|
490
|
+
headerComment?: string;
|
|
491
|
+
verbose?: boolean;
|
|
492
|
+
includeInternal?: boolean;
|
|
493
|
+
internalMarker?: string;
|
|
494
|
+
overrideFile?: boolean;
|
|
495
|
+
fileExtensions?: string[];
|
|
496
|
+
separateTypesFile?: boolean;
|
|
497
|
+
typesOutFile?: string;
|
|
498
|
+
}) {
|
|
499
|
+
try {
|
|
500
|
+
// Validate input directory
|
|
501
|
+
const st = await fs.stat(inputDir).catch(() => null);
|
|
502
|
+
if (!st?.isDirectory()) {
|
|
503
|
+
relinka("error", `Error: --input is not a valid directory: ${inputDir}`);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Validate output file directory exists or can be created
|
|
508
|
+
const outDir = path.dirname(outFile);
|
|
509
|
+
try {
|
|
510
|
+
await fs.ensureDir(outDir);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
relinka("error", `Error: Cannot create output directory: ${outDir}\n${error}`);
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Validate types output file directory if separateTypesFile is true
|
|
517
|
+
if (separateTypesFile && typesOutFile) {
|
|
518
|
+
const typesOutDir = path.dirname(typesOutFile);
|
|
519
|
+
try {
|
|
520
|
+
await fs.ensureDir(typesOutDir);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
relinka("error", `Error: Cannot create types output directory: ${typesOutDir}\n${error}`);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Validate output file extension matches input extensions
|
|
528
|
+
const outExt = path.extname(outFile).toLowerCase();
|
|
529
|
+
if (!fileExtensions.includes(outExt)) {
|
|
530
|
+
relinka(
|
|
531
|
+
"warn",
|
|
532
|
+
`Warning: Output file extension (${outExt}) doesn't match any of the input extensions: ${fileExtensions.join(", ")}`,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Validate strip prefix is a valid directory if provided
|
|
537
|
+
if (stripPrefix) {
|
|
538
|
+
const stripSt = await fs.stat(stripPrefix).catch(() => null);
|
|
539
|
+
if (!stripSt?.isDirectory()) {
|
|
540
|
+
relinka("error", `Error: --strip is not a valid directory: ${stripPrefix}`);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Collect files with specified extensions
|
|
546
|
+
if (verbose)
|
|
547
|
+
relinka(
|
|
548
|
+
"log",
|
|
549
|
+
`Scanning directory ${inputDir} for files with extensions: ${fileExtensions.join(", ")}`,
|
|
550
|
+
);
|
|
551
|
+
const filePaths = await collectFiles(
|
|
552
|
+
inputDir,
|
|
553
|
+
fileExtensions,
|
|
554
|
+
isRecursive,
|
|
555
|
+
ignoreDirs,
|
|
556
|
+
verbose,
|
|
557
|
+
includeInternal,
|
|
558
|
+
internalMarker,
|
|
559
|
+
outFile,
|
|
560
|
+
);
|
|
561
|
+
if (!filePaths.length) {
|
|
562
|
+
relinka(
|
|
563
|
+
"warn",
|
|
564
|
+
`No matching files found in ${inputDir} with extensions: ${fileExtensions.join(", ")}`,
|
|
565
|
+
);
|
|
566
|
+
if (!overrideFile) {
|
|
567
|
+
relinka("warn", "No changes will be made to the output file.");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Generate aggregator lines concurrently with unique star-import identifiers
|
|
573
|
+
const usedIdentifiers = new Set<string>();
|
|
574
|
+
const aggregatorLinesArrays = await Promise.all(
|
|
575
|
+
filePaths.map((fp) =>
|
|
576
|
+
generateAggregatorLines(
|
|
577
|
+
fp,
|
|
578
|
+
inputDir,
|
|
579
|
+
stripPrefix,
|
|
580
|
+
useImport,
|
|
581
|
+
useNamed,
|
|
582
|
+
usedIdentifiers,
|
|
583
|
+
).catch((error) => {
|
|
584
|
+
relinka("error", `Error processing file ${fp}: ${error}`);
|
|
585
|
+
return [];
|
|
586
|
+
}),
|
|
587
|
+
),
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
// Separate type and value lines
|
|
591
|
+
const allLines = aggregatorLinesArrays.flat();
|
|
592
|
+
const typeLines: string[] = [];
|
|
593
|
+
const valueLines: string[] = [];
|
|
594
|
+
|
|
595
|
+
for (const line of allLines) {
|
|
596
|
+
if (line.includes("type {")) {
|
|
597
|
+
typeLines.push(line);
|
|
598
|
+
} else {
|
|
599
|
+
valueLines.push(line);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Optionally sort lines alphabetically
|
|
604
|
+
if (sortLines) {
|
|
605
|
+
typeLines.sort();
|
|
606
|
+
valueLines.sort();
|
|
607
|
+
if (verbose) relinka("log", "Sorted aggregator lines alphabetically.");
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Build the aggregator block content
|
|
611
|
+
const buildAggregatorBlock = (lines: string[]) =>
|
|
612
|
+
`${headerComment ? `${headerComment}\n` : ""}${AGGREGATOR_START}\n${lines.join("\n")}\n${AGGREGATOR_END}\n`;
|
|
613
|
+
|
|
614
|
+
if (separateTypesFile && typesOutFile) {
|
|
615
|
+
// Write type exports to separate file
|
|
616
|
+
const typeBlock = buildAggregatorBlock(typeLines);
|
|
617
|
+
await fs.ensureFile(typesOutFile);
|
|
618
|
+
await fs.writeFile(typesOutFile, typeBlock, "utf8");
|
|
619
|
+
|
|
620
|
+
// Write value exports to main file, including type file import
|
|
621
|
+
const valueBlock = buildAggregatorBlock([
|
|
622
|
+
...valueLines,
|
|
623
|
+
`export * from "${path.relative(path.dirname(outFile), typesOutFile).replace(/\\/g, "/")}";`,
|
|
624
|
+
]);
|
|
625
|
+
await fs.ensureFile(outFile);
|
|
626
|
+
await fs.writeFile(outFile, valueBlock, "utf8");
|
|
627
|
+
|
|
628
|
+
relinka(
|
|
629
|
+
"success",
|
|
630
|
+
`Aggregator done: processed ${typeLines.length} type lines in: ${typesOutFile} and ${valueLines.length} value lines in: ${outFile}`,
|
|
631
|
+
);
|
|
632
|
+
} else {
|
|
633
|
+
// Write all lines to single file
|
|
634
|
+
const aggregatorBlock = buildAggregatorBlock(allLines);
|
|
635
|
+
await fs.ensureFile(outFile);
|
|
636
|
+
await fs.writeFile(outFile, aggregatorBlock, "utf8");
|
|
637
|
+
|
|
638
|
+
relinka("success", `Aggregator done: processed ${allLines.length} lines in: ${outFile}`);
|
|
639
|
+
}
|
|
640
|
+
} catch (error) {
|
|
641
|
+
relinka("error", `Aggregator failed: ${error}`);
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Build a relative import/export path, removing `stripPrefix` if it is truly a prefix,
|
|
648
|
+
* converting .ts -> .js, and ensuring it starts with "./" or "../".
|
|
649
|
+
*/
|
|
650
|
+
function buildPathRelative(filePath: string, inputDir: string, stripPrefix: string): string {
|
|
651
|
+
let resolved = path.resolve(filePath);
|
|
652
|
+
const resolvedStrip = stripPrefix ? path.resolve(stripPrefix) : "";
|
|
653
|
+
|
|
654
|
+
// If stripPrefix applies, remove it; otherwise, compute a relative path.
|
|
655
|
+
if (resolvedStrip && resolved.startsWith(resolvedStrip)) {
|
|
656
|
+
resolved = resolved.slice(resolvedStrip.length);
|
|
657
|
+
} else {
|
|
658
|
+
resolved = path.relative(path.resolve(inputDir), resolved);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Remove any leading path separator(s)
|
|
662
|
+
while (resolved.startsWith(path.sep)) {
|
|
663
|
+
resolved = resolved.slice(1);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Normalize backslashes to forward slashes
|
|
667
|
+
resolved = resolved.replace(/\\/g, "/");
|
|
668
|
+
|
|
669
|
+
// Convert .ts -> .js extension
|
|
670
|
+
if (resolved.toLowerCase().endsWith(".ts")) {
|
|
671
|
+
resolved = `${resolved.slice(0, -3)}.js`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Ensure the path starts with "./" or "../" only if it doesn't already
|
|
675
|
+
if (!resolved.startsWith("./") && !resolved.startsWith("../")) {
|
|
676
|
+
resolved = `./${resolved}`;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Fix any double slashes in the path
|
|
680
|
+
resolved = resolved.replace(/\/+/g, "/");
|
|
681
|
+
|
|
682
|
+
return resolved;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Recursively collects files with given extensions, ignoring specified directories
|
|
687
|
+
* and files marked as internal.
|
|
688
|
+
*/
|
|
689
|
+
async function collectFiles(
|
|
690
|
+
dir: string,
|
|
691
|
+
exts: string[],
|
|
692
|
+
recursive: boolean,
|
|
693
|
+
ignoreDirs: string[],
|
|
694
|
+
verbose: boolean,
|
|
695
|
+
includeInternal: boolean,
|
|
696
|
+
internalMarker: string,
|
|
697
|
+
outFile?: string,
|
|
698
|
+
): Promise<string[]> {
|
|
699
|
+
const found: string[] = [];
|
|
700
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
701
|
+
|
|
702
|
+
for (const entry of entries) {
|
|
703
|
+
const fullPath = path.join(dir, entry.name);
|
|
704
|
+
|
|
705
|
+
// Skip the output file if it matches
|
|
706
|
+
if (outFile && path.resolve(fullPath) === path.resolve(outFile)) {
|
|
707
|
+
if (verbose) {
|
|
708
|
+
relinka("log", `Skipping output file: ${fullPath}`);
|
|
709
|
+
}
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (entry.isDirectory()) {
|
|
714
|
+
if (ignoreDirs.includes(entry.name)) {
|
|
715
|
+
if (verbose) {
|
|
716
|
+
relinka("log", `Skipping ignored directory: ${fullPath}`);
|
|
717
|
+
}
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (recursive) {
|
|
721
|
+
const sub = await collectFiles(
|
|
722
|
+
fullPath,
|
|
723
|
+
exts,
|
|
724
|
+
recursive,
|
|
725
|
+
ignoreDirs,
|
|
726
|
+
verbose,
|
|
727
|
+
includeInternal,
|
|
728
|
+
internalMarker,
|
|
729
|
+
outFile,
|
|
730
|
+
);
|
|
731
|
+
found.push(...sub);
|
|
732
|
+
}
|
|
733
|
+
} else if (entry.isFile()) {
|
|
734
|
+
// Skip file if its basename starts with the internal marker and internal files are not included.
|
|
735
|
+
if (!includeInternal && path.basename(fullPath).startsWith(internalMarker)) {
|
|
736
|
+
if (verbose) {
|
|
737
|
+
relinka("log", `Skipping internal file: ${fullPath}`);
|
|
738
|
+
}
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (exts.some((ext) => entry.name.toLowerCase().endsWith(ext))) {
|
|
742
|
+
if (verbose) {
|
|
743
|
+
relinka("log", `Found file: ${fullPath}`);
|
|
744
|
+
}
|
|
745
|
+
found.push(fullPath);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return found;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Creates aggregator lines for a single file.
|
|
754
|
+
*
|
|
755
|
+
* If `useNamed` is true, parses named exports and produces up to two lines:
|
|
756
|
+
* - import type { ... } / export type { ... }
|
|
757
|
+
* - import { ... } / export { ... }
|
|
758
|
+
*
|
|
759
|
+
* If `useNamed` is false, produces a single star import or export.
|
|
760
|
+
*
|
|
761
|
+
* @param usedIdentifiers A set to track and ensure unique identifiers for star imports.
|
|
762
|
+
*/
|
|
763
|
+
async function generateAggregatorLines(
|
|
764
|
+
filePath: string,
|
|
765
|
+
inputDir: string,
|
|
766
|
+
stripPrefix: string,
|
|
767
|
+
useImport: boolean,
|
|
768
|
+
useNamed: boolean,
|
|
769
|
+
usedIdentifiers?: Set<string>,
|
|
770
|
+
): Promise<string[]> {
|
|
771
|
+
const importPath = buildPathRelative(filePath, inputDir, stripPrefix);
|
|
772
|
+
|
|
773
|
+
// Star import/export approach when not using named exports
|
|
774
|
+
if (!useNamed) {
|
|
775
|
+
if (useImport) {
|
|
776
|
+
let ident = guessStarImportIdentifier(filePath);
|
|
777
|
+
if (usedIdentifiers) {
|
|
778
|
+
let uniqueIdent = ident;
|
|
779
|
+
let counter = 1;
|
|
780
|
+
while (usedIdentifiers.has(uniqueIdent)) {
|
|
781
|
+
uniqueIdent = `${ident}_${counter}`;
|
|
782
|
+
counter++;
|
|
783
|
+
}
|
|
784
|
+
usedIdentifiers.add(uniqueIdent);
|
|
785
|
+
ident = uniqueIdent;
|
|
786
|
+
}
|
|
787
|
+
return [`import * as ${ident} from "${importPath}";`];
|
|
788
|
+
}
|
|
789
|
+
return [`export * from "${importPath}";`];
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// For named exports, parse the file to extract export names.
|
|
793
|
+
const { typeNames, valueNames } = await getNamedExports(filePath);
|
|
794
|
+
if (!typeNames.length && !valueNames.length) {
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (useImport) {
|
|
799
|
+
const lines: string[] = [];
|
|
800
|
+
if (typeNames.length > 0) {
|
|
801
|
+
lines.push(`import type { ${typeNames.join(", ")} } from "${importPath}";`);
|
|
802
|
+
}
|
|
803
|
+
if (valueNames.length > 0) {
|
|
804
|
+
lines.push(`import { ${valueNames.join(", ")} } from "${importPath}";`);
|
|
805
|
+
}
|
|
806
|
+
return lines;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// For exports
|
|
810
|
+
const lines: string[] = [];
|
|
811
|
+
if (typeNames.length > 0) {
|
|
812
|
+
lines.push(`export type { ${typeNames.join(", ")} } from "${importPath}";`);
|
|
813
|
+
}
|
|
814
|
+
if (valueNames.length > 0) {
|
|
815
|
+
lines.push(`export { ${valueNames.join(", ")} } from "${importPath}";`);
|
|
816
|
+
}
|
|
817
|
+
return lines;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Parses a file to extract named exports, separating type exports from value exports.
|
|
822
|
+
* Deduplicates export names (to handle overloads).
|
|
823
|
+
*/
|
|
824
|
+
async function getNamedExports(
|
|
825
|
+
filePath: string,
|
|
826
|
+
): Promise<{ typeNames: string[]; valueNames: string[] }> {
|
|
827
|
+
try {
|
|
828
|
+
const code = await fs.readFile(filePath, "utf8");
|
|
829
|
+
const typeNamesSet = new Set<string>();
|
|
830
|
+
const valueNamesSet = new Set<string>();
|
|
831
|
+
|
|
832
|
+
// Match various export patterns:
|
|
833
|
+
// 1. Regular exports: export const/let/var/function/class/interface/type/enum
|
|
834
|
+
// 2. Default exports: export default class/function/const/interface
|
|
835
|
+
// 3. Named exports: export { name, name2 as alias }
|
|
836
|
+
// 4. Re-exports: export { name } from './other'
|
|
837
|
+
// 5. Export assignments: export = name
|
|
838
|
+
const patterns = [
|
|
839
|
+
// Regular exports and default exports
|
|
840
|
+
/^export\s+(?:default\s+)?(?:async\s+)?(function|const|class|let|var|type|interface|enum)\s+([A-Za-z0-9_$]+)/gm,
|
|
841
|
+
// Named exports and re-exports
|
|
842
|
+
/^export\s*{([^}]+)}(?:\s+from\s+['"][^'"]+['"])?/gm,
|
|
843
|
+
// Export assignments
|
|
844
|
+
/^export\s*=\s*([A-Za-z0-9_$]+)/gm,
|
|
845
|
+
];
|
|
846
|
+
|
|
847
|
+
for (const pattern of patterns) {
|
|
848
|
+
let match: RegExpExecArray | null;
|
|
849
|
+
while (true) {
|
|
850
|
+
match = pattern.exec(code);
|
|
851
|
+
if (!match) break;
|
|
852
|
+
|
|
853
|
+
const matchGroups = match as RegExpExecArray & Record<number, string>;
|
|
854
|
+
if (pattern.source.includes("{([^}]+)}") && matchGroups[1]) {
|
|
855
|
+
// Handle named exports/re-exports
|
|
856
|
+
const exports = (matchGroups[1] ?? "").split(",").map(
|
|
857
|
+
(e) =>
|
|
858
|
+
e
|
|
859
|
+
?.trim()
|
|
860
|
+
?.split(/\s+as\s+/)?.[0]
|
|
861
|
+
?.trim() ?? "",
|
|
862
|
+
);
|
|
863
|
+
for (const exp of exports) {
|
|
864
|
+
// Skip 'type' keyword in named exports
|
|
865
|
+
const name = exp.replace(/^type\s+/, "");
|
|
866
|
+
if (exp.startsWith("type ")) {
|
|
867
|
+
typeNamesSet.add(name);
|
|
868
|
+
} else {
|
|
869
|
+
valueNamesSet.add(name);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
} else if (pattern.source.includes("=\\s*([A-Za-z0-9_$]+)") && matchGroups[1]) {
|
|
873
|
+
// Handle export assignments
|
|
874
|
+
valueNamesSet.add(matchGroups[1]);
|
|
875
|
+
} else {
|
|
876
|
+
// Handle regular exports
|
|
877
|
+
const keyword = matchGroups[1];
|
|
878
|
+
const name = matchGroups[2];
|
|
879
|
+
if (keyword && name) {
|
|
880
|
+
if (keyword === "type" || keyword === "interface" || keyword === "enum") {
|
|
881
|
+
typeNamesSet.add(name);
|
|
882
|
+
} else {
|
|
883
|
+
valueNamesSet.add(name);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return {
|
|
891
|
+
typeNames: Array.from(typeNamesSet),
|
|
892
|
+
valueNames: Array.from(valueNamesSet),
|
|
893
|
+
};
|
|
894
|
+
} catch (error) {
|
|
895
|
+
relinka("error", `Error reading file ${filePath}: ${error}`);
|
|
896
|
+
return { typeNames: [], valueNames: [] };
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Generates a valid identifier for star imports based on the file name.
|
|
902
|
+
*/
|
|
903
|
+
function guessStarImportIdentifier(filePath: string): string {
|
|
904
|
+
const base = path.basename(filePath, path.extname(filePath));
|
|
905
|
+
let identifier = base.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
906
|
+
if (/^\d/.test(identifier)) {
|
|
907
|
+
identifier = `_${identifier}`;
|
|
908
|
+
}
|
|
909
|
+
return identifier || "file";
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Prints usage examples based on whether dev mode or not.
|
|
914
|
+
*/
|
|
915
|
+
export function printUsage(isDev?: boolean) {
|
|
916
|
+
relinka("log", "====================");
|
|
917
|
+
relinka("log", "TOOLS USAGE EXAMPLES");
|
|
918
|
+
relinka("log", "====================");
|
|
919
|
+
relinka(
|
|
920
|
+
"log",
|
|
921
|
+
`${isDev ? "bun dev:agg" : "dler tools"} --tool agg --input <dir> --out <file> [options]`,
|
|
922
|
+
);
|
|
923
|
+
if (isDev) {
|
|
924
|
+
relinka(
|
|
925
|
+
"log",
|
|
926
|
+
"bun dev:tools agg --input src/libs/sdk/sdk-impl --out src/libs/sdk/sdk-mod.ts --recursive --named --strip src/libs/sdk",
|
|
927
|
+
);
|
|
928
|
+
} else {
|
|
929
|
+
relinka(
|
|
930
|
+
"log",
|
|
931
|
+
"dler tools --tool agg --input src/libs --out aggregator.ts --recursive --named",
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
}
|