@cldmv/slothlet 2.8.0 → 2.9.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.
- package/AGENT-USAGE.md +1 -1
- package/README.md +253 -1578
- package/dist/lib/helpers/als-eventemitter.mjs +4 -5
- package/dist/lib/helpers/api_builder/add_api.mjs +237 -0
- package/dist/lib/helpers/api_builder/analysis.mjs +522 -0
- package/dist/lib/helpers/api_builder/construction.mjs +457 -0
- package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
- package/dist/lib/helpers/api_builder.mjs +16 -1567
- package/dist/lib/helpers/utilities.mjs +121 -0
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +44 -17
- package/dist/lib/runtime/runtime-livebindings.mjs +18 -3
- package/dist/lib/runtime/runtime.mjs +3 -3
- package/dist/slothlet.mjs +146 -746
- package/docs/API-RULES-CONDITIONS.md +508 -0
- package/{API-RULES.md → docs/API-RULES.md} +127 -72
- package/package.json +11 -9
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +60 -0
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder.d.mts +5 -448
- package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +120 -0
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +7 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +8 -0
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +0 -11
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
- package/API-RULES-CONDITIONS.md +0 -367
|
@@ -18,1575 +18,24 @@
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
import fs from "node:fs/promises";
|
|
22
|
-
import path from "node:path";
|
|
23
|
-
import { types as utilTypes } from "node:util";
|
|
24
|
-
import { pathToFileURL } from "node:url";
|
|
25
|
-
import { multidefault_analyzeModules } from "@cldmv/slothlet/helpers/multidefault";
|
|
26
|
-
import { setActiveInstance } from "@cldmv/slothlet/helpers/instance-manager";
|
|
27
21
|
|
|
22
|
+
export {
|
|
23
|
+
isLikelySerializable,
|
|
24
|
+
analyzeModule,
|
|
25
|
+
processModuleFromAnalysis,
|
|
26
|
+
analyzeDirectoryStructure,
|
|
27
|
+
getCategoryBuildingDecisions
|
|
28
|
+
} from "@cldmv/slothlet/helpers/api_builder/analysis";
|
|
28
29
|
|
|
30
|
+
export {
|
|
31
|
+
getFlatteningDecision,
|
|
32
|
+
applyFunctionNamePreference,
|
|
33
|
+
processModuleForAPI,
|
|
34
|
+
buildCategoryDecisions
|
|
35
|
+
} from "@cldmv/slothlet/helpers/api_builder/decisions";
|
|
29
36
|
|
|
37
|
+
export { buildCategoryStructure, buildRootAPI, toapiPathKey, shouldIncludeFile } from "@cldmv/slothlet/helpers/api_builder/construction";
|
|
30
38
|
|
|
39
|
+
export { safeDefine, deepMerge, mutateLiveBindingFunction } from "@cldmv/slothlet/helpers/utilities";
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
function isLikelySerializable(val) {
|
|
34
|
-
const type = typeof val;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (type !== "object" || val === null) {
|
|
38
|
-
return type === "string" || type === "number" || type === "boolean" || type === "undefined";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
Array.isArray(val) || val instanceof Date || val instanceof RegExp || val?.constructor === Object || typeof val.toJSON === "function"
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export async function analyzeModule(modulePath, options = {}) {
|
|
53
|
-
const { debug = false, instance = null } = options;
|
|
54
|
-
|
|
55
|
-
const moduleUrl = pathToFileURL(modulePath).href;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let importUrl = moduleUrl;
|
|
59
|
-
if (instance && instance.instanceId) {
|
|
60
|
-
const runtimeType = instance.config?.runtime || "async";
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (runtimeType === "live") {
|
|
64
|
-
const separator = moduleUrl.includes("?") ? "&" : "?";
|
|
65
|
-
importUrl = `${moduleUrl}${separator}slothlet_instance=${instance.instanceId}`;
|
|
66
|
-
importUrl = `${importUrl}&slothlet_runtime=${runtimeType}`;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
setActiveInstance(instance.instanceId);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const rawModule = await import(importUrl);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
let processedModule = rawModule;
|
|
79
|
-
const isCjs = modulePath.endsWith(".cjs") && "default" in rawModule;
|
|
80
|
-
|
|
81
|
-
if (isCjs) {
|
|
82
|
-
processedModule = rawModule.default;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const hasDefault = !!processedModule.default;
|
|
86
|
-
const isFunction = typeof processedModule.default === "function";
|
|
87
|
-
const exports = Object.entries(processedModule);
|
|
88
|
-
const namedExports = Object.entries(processedModule).filter(([k]) => k !== "default");
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let defaultExportType = null;
|
|
92
|
-
if (hasDefault) {
|
|
93
|
-
defaultExportType = typeof processedModule.default === "function" ? "function" : "object";
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
let shouldWrapAsCallable = false;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
hasDefault &&
|
|
102
|
-
typeof processedModule.default === "object" &&
|
|
103
|
-
processedModule.default !== null &&
|
|
104
|
-
typeof processedModule.default.default === "function"
|
|
105
|
-
) {
|
|
106
|
-
shouldWrapAsCallable = true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!shouldWrapAsCallable) {
|
|
111
|
-
for (const [_, exportValue] of namedExports) {
|
|
112
|
-
if (typeof exportValue === "object" && exportValue !== null && typeof exportValue.default === "function") {
|
|
113
|
-
shouldWrapAsCallable = true;
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (debug) {
|
|
120
|
-
console.log(`[DEBUG] analyzeModule(${path.basename(modulePath)}):`, {
|
|
121
|
-
isCjs,
|
|
122
|
-
hasDefault,
|
|
123
|
-
isFunction,
|
|
124
|
-
defaultExportType,
|
|
125
|
-
shouldWrapAsCallable,
|
|
126
|
-
namedExportsCount: namedExports.length
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
rawModule,
|
|
132
|
-
processedModule,
|
|
133
|
-
isFunction,
|
|
134
|
-
hasDefault,
|
|
135
|
-
isCjs,
|
|
136
|
-
exports,
|
|
137
|
-
defaultExportType,
|
|
138
|
-
shouldWrapAsCallable,
|
|
139
|
-
namedExports,
|
|
140
|
-
metadata: { modulePath }
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
export function processModuleFromAnalysis(analysis, options = {}) {
|
|
146
|
-
const { instance, debug = false } = options;
|
|
147
|
-
const { processedModule, isFunction, hasDefault, shouldWrapAsCallable, namedExports } = analysis;
|
|
148
|
-
|
|
149
|
-
if (!instance) {
|
|
150
|
-
throw new Error("processModuleFromAnalysis requires instance parameter for _toapiPathKey access");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (isFunction) {
|
|
155
|
-
let fn = processedModule.default;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (hasDefault) {
|
|
159
|
-
try {
|
|
160
|
-
Object.defineProperty(fn, "__slothletDefault", {
|
|
161
|
-
value: true,
|
|
162
|
-
writable: false,
|
|
163
|
-
enumerable: false,
|
|
164
|
-
configurable: true
|
|
165
|
-
});
|
|
166
|
-
} catch {
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for (const [exportName, exportValue] of Object.entries(processedModule)) {
|
|
173
|
-
if (exportName !== "default") {
|
|
174
|
-
fn[instance._toapiPathKey(exportName)] = exportValue;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return fn;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (shouldWrapAsCallable) {
|
|
182
|
-
let callableObject = null;
|
|
183
|
-
let objectName = "callable";
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
hasDefault &&
|
|
188
|
-
typeof processedModule.default === "object" &&
|
|
189
|
-
processedModule.default !== null &&
|
|
190
|
-
typeof processedModule.default.default === "function"
|
|
191
|
-
) {
|
|
192
|
-
callableObject = processedModule.default;
|
|
193
|
-
objectName = processedModule.default.name || (namedExports[0] && namedExports[0][0] !== "default" ? namedExports[0][0] : "callable");
|
|
194
|
-
} else {
|
|
195
|
-
|
|
196
|
-
for (const [exportName, exportValue] of namedExports) {
|
|
197
|
-
if (typeof exportValue === "object" && exportValue !== null && typeof exportValue.default === "function") {
|
|
198
|
-
callableObject = exportValue;
|
|
199
|
-
objectName = exportName;
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (callableObject) {
|
|
206
|
-
const callableApi = {
|
|
207
|
-
[objectName]: function (...args) {
|
|
208
|
-
return callableObject.default.apply(callableObject, args);
|
|
209
|
-
}
|
|
210
|
-
}[objectName];
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
for (const [methodName, method] of Object.entries(callableObject)) {
|
|
214
|
-
if (methodName === "default") continue;
|
|
215
|
-
callableApi[methodName] = method;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (debug) {
|
|
219
|
-
console.log(`[DEBUG] Created callable wrapper for ${objectName}`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return callableApi;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (hasDefault && typeof processedModule.default === "object") {
|
|
228
|
-
const obj = processedModule.default;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const namedExportsToAdd = Object.entries(processedModule).filter(
|
|
232
|
-
([exportName, exportValue]) => exportName !== "default" && exportValue !== obj
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
if (namedExportsToAdd.length > 0) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const isCustomProxy = utilTypes?.isProxy?.(obj) ?? false;
|
|
239
|
-
|
|
240
|
-
if (isCustomProxy) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
for (const [exportName, exportValue] of namedExportsToAdd) {
|
|
244
|
-
const apiKey = instance._toapiPathKey(exportName);
|
|
245
|
-
obj[apiKey] = exportValue;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const proxyWithStructure = obj;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
proxyWithStructure.default = obj;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!proxyWithStructure.toJSON) {
|
|
275
|
-
Object.defineProperty(proxyWithStructure, "toJSON", {
|
|
276
|
-
value: function () {
|
|
277
|
-
|
|
278
|
-
const serializable = {};
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
for (const key of Reflect.ownKeys(this)) {
|
|
282
|
-
|
|
283
|
-
if (typeof key !== "string") continue;
|
|
284
|
-
|
|
285
|
-
const descriptor = Reflect.getOwnPropertyDescriptor(this, key);
|
|
286
|
-
if (!descriptor || !descriptor.enumerable) continue;
|
|
287
|
-
|
|
288
|
-
if (key === "default") {
|
|
289
|
-
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const value = this[key];
|
|
294
|
-
if (typeof value === "function") {
|
|
295
|
-
serializable[key] = "[Function]";
|
|
296
|
-
} else if (isLikelySerializable(value)) {
|
|
297
|
-
|
|
298
|
-
serializable[key] = value;
|
|
299
|
-
} else {
|
|
300
|
-
|
|
301
|
-
try {
|
|
302
|
-
JSON.stringify(value);
|
|
303
|
-
serializable[key] = value;
|
|
304
|
-
} catch {
|
|
305
|
-
serializable[key] = "[Non-serializable value]";
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
serializable._slothlet_proxy_info = {
|
|
312
|
-
type: "proxy",
|
|
313
|
-
circular_reference: "Property .default points to this object (excluded from serialization)",
|
|
314
|
-
warning: "This is a slothlet API proxy with circular .default reference"
|
|
315
|
-
};
|
|
316
|
-
return serializable;
|
|
317
|
-
},
|
|
318
|
-
writable: false,
|
|
319
|
-
enumerable: false,
|
|
320
|
-
configurable: true
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
for (const [exportName, exportValue] of namedExportsToAdd) {
|
|
326
|
-
const apiKey = instance._toapiPathKey(exportName);
|
|
327
|
-
if (!(apiKey in proxyWithStructure)) {
|
|
328
|
-
proxyWithStructure[apiKey] = exportValue;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return proxyWithStructure;
|
|
333
|
-
} else {
|
|
334
|
-
|
|
335
|
-
for (const [exportName, exportValue] of namedExportsToAdd) {
|
|
336
|
-
obj[instance._toapiPathKey(exportName)] = exportValue;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return obj;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return obj;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (namedExports.length > 0) {
|
|
348
|
-
const apiExport = {};
|
|
349
|
-
for (const [exportName, exportValue] of namedExports) {
|
|
350
|
-
apiExport[instance._toapiPathKey(exportName)] = exportValue;
|
|
351
|
-
}
|
|
352
|
-
return apiExport;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
throw new Error(`No valid exports found in processed module`);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
export async function analyzeDirectoryStructure(categoryPath, options = {}) {
|
|
361
|
-
const { instance, currentDepth = 0, debug = false } = options;
|
|
362
|
-
|
|
363
|
-
if (!instance || typeof instance._toapiPathKey !== "function") {
|
|
364
|
-
throw new Error("analyzeDirectoryStructure requires a valid slothlet instance");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const files = await fs.readdir(categoryPath, { withFileTypes: true });
|
|
368
|
-
const moduleFiles = files.filter((f) => instance._shouldIncludeFile(f));
|
|
369
|
-
const categoryName = instance._toapiPathKey(path.basename(categoryPath));
|
|
370
|
-
const subDirs = files.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
let processingStrategy;
|
|
374
|
-
if (moduleFiles.length === 0) {
|
|
375
|
-
processingStrategy = "empty";
|
|
376
|
-
} else if (moduleFiles.length === 1 && subDirs.length === 0) {
|
|
377
|
-
processingStrategy = "single-file";
|
|
378
|
-
} else {
|
|
379
|
-
processingStrategy = "multi-file";
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
let multiDefaultAnalysis = null;
|
|
384
|
-
if (processingStrategy === "multi-file") {
|
|
385
|
-
multiDefaultAnalysis = await multidefault_analyzeModules(moduleFiles, categoryPath, { debug, instance });
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const flatteningHints = {
|
|
390
|
-
shouldFlattenSingleFile: processingStrategy === "single-file",
|
|
391
|
-
shouldFlattenToParent: currentDepth > 0 && processingStrategy === "single-file",
|
|
392
|
-
hasMultipleDefaults: multiDefaultAnalysis?.hasMultipleDefaultExports || false,
|
|
393
|
-
selfReferentialFiles: multiDefaultAnalysis?.selfReferentialFiles || new Set()
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
if (debug) {
|
|
397
|
-
console.log(`[DEBUG] analyzeDirectoryStructure(${categoryName}):`, {
|
|
398
|
-
processingStrategy,
|
|
399
|
-
moduleCount: moduleFiles.length,
|
|
400
|
-
subDirCount: subDirs.length,
|
|
401
|
-
hasMultipleDefaults: flatteningHints.hasMultipleDefaults
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return {
|
|
406
|
-
isSingleFile: processingStrategy === "single-file",
|
|
407
|
-
shouldAutoFlatten: flatteningHints.shouldFlattenSingleFile,
|
|
408
|
-
categoryName,
|
|
409
|
-
moduleFiles,
|
|
410
|
-
subDirs,
|
|
411
|
-
multiDefaultAnalysis,
|
|
412
|
-
processingStrategy,
|
|
413
|
-
flatteningHints
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
export async function getCategoryBuildingDecisions(categoryPath, options = {}) {
|
|
419
|
-
const { instance, currentDepth = 0, maxDepth = Infinity, debug = false } = options;
|
|
420
|
-
|
|
421
|
-
if (!instance) {
|
|
422
|
-
throw new Error("getCategoryBuildingDecisions requires a valid slothlet instance");
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const analysis = await analyzeDirectoryStructure(categoryPath, { instance, currentDepth, maxDepth, debug });
|
|
426
|
-
const { processingStrategy, categoryName, moduleFiles, subDirs, multiDefaultAnalysis } = analysis;
|
|
427
|
-
|
|
428
|
-
const processedModules = [];
|
|
429
|
-
const subDirectories = [];
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (processingStrategy !== "empty") {
|
|
433
|
-
for (const file of moduleFiles) {
|
|
434
|
-
const moduleExt = path.extname(file.name);
|
|
435
|
-
const moduleName = instance._toapiPathKey(path.basename(file.name, moduleExt));
|
|
436
|
-
const modulePath = path.join(categoryPath, file.name);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const analysis = await analyzeModule(modulePath, {
|
|
440
|
-
debug,
|
|
441
|
-
instance
|
|
442
|
-
});
|
|
443
|
-
const processedModule = processModuleFromAnalysis(analysis, {
|
|
444
|
-
debug,
|
|
445
|
-
instance
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const flatteningInfo = {
|
|
450
|
-
shouldFlatten: false,
|
|
451
|
-
apiPathKey: moduleName,
|
|
452
|
-
reason: "default"
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (processingStrategy === "single-file") {
|
|
457
|
-
|
|
458
|
-
const functionNameMatchesFolder =
|
|
459
|
-
typeof processedModule === "function" &&
|
|
460
|
-
processedModule.name &&
|
|
461
|
-
processedModule.name.toLowerCase() === categoryName.toLowerCase();
|
|
462
|
-
|
|
463
|
-
const moduleNameMatchesCategory = moduleName === categoryName && typeof processedModule === "function" && currentDepth > 0;
|
|
464
|
-
|
|
465
|
-
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
466
|
-
flatteningInfo.shouldFlatten = true;
|
|
467
|
-
flatteningInfo.apiPathKey = processedModule.name;
|
|
468
|
-
flatteningInfo.reason = "function name matches folder";
|
|
469
|
-
} else if (moduleNameMatchesCategory) {
|
|
470
|
-
flatteningInfo.shouldFlatten = true;
|
|
471
|
-
flatteningInfo.apiPathKey = categoryName;
|
|
472
|
-
flatteningInfo.reason = "module name matches category";
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
processedModules.push({
|
|
477
|
-
file,
|
|
478
|
-
moduleName,
|
|
479
|
-
processedModule,
|
|
480
|
-
flattening: flatteningInfo
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
for (const subDirEntry of subDirs) {
|
|
487
|
-
if (currentDepth < maxDepth) {
|
|
488
|
-
const apiPathKey = instance._toapiPathKey(subDirEntry.name);
|
|
489
|
-
subDirectories.push({ dirEntry: subDirEntry, apiPathKey });
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
const upwardFlatteningCandidate = { shouldFlatten: false, apiPathKey: null };
|
|
495
|
-
if (processedModules.length === 1 && subDirectories.length === 0) {
|
|
496
|
-
const single = processedModules[0];
|
|
497
|
-
if (single.moduleName === categoryName) {
|
|
498
|
-
upwardFlatteningCandidate.shouldFlatten = true;
|
|
499
|
-
upwardFlatteningCandidate.apiPathKey = single.moduleName;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if (debug) {
|
|
504
|
-
console.log(`[DEBUG] getCategoryBuildingDecisions(${categoryName}):`, {
|
|
505
|
-
processingStrategy,
|
|
506
|
-
moduleCount: processedModules.length,
|
|
507
|
-
subDirCount: subDirectories.length,
|
|
508
|
-
upwardFlattening: upwardFlatteningCandidate.shouldFlatten
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
processingStrategy,
|
|
514
|
-
categoryName,
|
|
515
|
-
shouldFlattenSingle: processingStrategy === "single-file",
|
|
516
|
-
processedModules,
|
|
517
|
-
subDirectories,
|
|
518
|
-
multiDefaultAnalysis,
|
|
519
|
-
flatteningDecisions: analysis.flatteningHints,
|
|
520
|
-
upwardFlatteningCandidate
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
export function getFlatteningDecision(options) {
|
|
526
|
-
const {
|
|
527
|
-
mod,
|
|
528
|
-
fileName,
|
|
529
|
-
apiPathKey,
|
|
530
|
-
hasMultipleDefaultExports,
|
|
531
|
-
isSelfReferential,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
moduleHasDefault = !!mod.default,
|
|
535
|
-
categoryName,
|
|
536
|
-
|
|
537
|
-
totalModules = 1
|
|
538
|
-
} = options;
|
|
539
|
-
|
|
540
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (isSelfReferential) {
|
|
544
|
-
return {
|
|
545
|
-
shouldFlatten: false,
|
|
546
|
-
flattenToRoot: false,
|
|
547
|
-
flattenToCategory: false,
|
|
548
|
-
preserveAsNamespace: true,
|
|
549
|
-
useAutoFlattening: false,
|
|
550
|
-
reason: "self-referential export"
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (hasMultipleDefaultExports) {
|
|
556
|
-
if (moduleHasDefault) {
|
|
557
|
-
|
|
558
|
-
return {
|
|
559
|
-
shouldFlatten: false,
|
|
560
|
-
flattenToRoot: false,
|
|
561
|
-
flattenToCategory: false,
|
|
562
|
-
preserveAsNamespace: true,
|
|
563
|
-
useAutoFlattening: false,
|
|
564
|
-
reason: "multi-default context with default export"
|
|
565
|
-
};
|
|
566
|
-
} else {
|
|
567
|
-
|
|
568
|
-
return {
|
|
569
|
-
shouldFlatten: true,
|
|
570
|
-
flattenToRoot: true,
|
|
571
|
-
flattenToCategory: true,
|
|
572
|
-
preserveAsNamespace: false,
|
|
573
|
-
useAutoFlattening: false,
|
|
574
|
-
reason: "multi-default context without default export"
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
581
|
-
return {
|
|
582
|
-
shouldFlatten: true,
|
|
583
|
-
flattenToRoot: false,
|
|
584
|
-
flattenToCategory: false,
|
|
585
|
-
preserveAsNamespace: false,
|
|
586
|
-
useAutoFlattening: true,
|
|
587
|
-
reason: "auto-flatten single named export matching filename"
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0) {
|
|
593
|
-
return {
|
|
594
|
-
shouldFlatten: true,
|
|
595
|
-
flattenToRoot: false,
|
|
596
|
-
flattenToCategory: true,
|
|
597
|
-
preserveAsNamespace: false,
|
|
598
|
-
useAutoFlattening: false,
|
|
599
|
-
reason: "filename matches container, flatten to category"
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return {
|
|
619
|
-
shouldFlatten: false,
|
|
620
|
-
flattenToRoot: false,
|
|
621
|
-
flattenToCategory: false,
|
|
622
|
-
preserveAsNamespace: true,
|
|
623
|
-
useAutoFlattening: false,
|
|
624
|
-
reason: "traditional namespace preservation"
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
export function processModuleForAPI(options) {
|
|
630
|
-
const {
|
|
631
|
-
mod,
|
|
632
|
-
fileName,
|
|
633
|
-
apiPathKey,
|
|
634
|
-
hasMultipleDefaultExports,
|
|
635
|
-
isSelfReferential,
|
|
636
|
-
api,
|
|
637
|
-
getRootDefault,
|
|
638
|
-
setRootDefault,
|
|
639
|
-
context = {},
|
|
640
|
-
originalAnalysis = null
|
|
641
|
-
} = options;
|
|
642
|
-
|
|
643
|
-
const { debug = false, mode = "unknown", categoryName, totalModules = 1 } = context;
|
|
644
|
-
|
|
645
|
-
let processed = false;
|
|
646
|
-
let rootDefaultSet = false;
|
|
647
|
-
let flattened = false;
|
|
648
|
-
let namespaced = false;
|
|
649
|
-
const apiAssignments = {};
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const hasDefaultFunction = (mod && typeof mod.default === "function") || (mod && typeof mod === "function" && !mod.default);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const defaultFunction = mod?.default || (typeof mod === "function" ? mod : null);
|
|
658
|
-
|
|
659
|
-
if (hasDefaultFunction) {
|
|
660
|
-
processed = true;
|
|
661
|
-
|
|
662
|
-
if (hasMultipleDefaultExports && !isSelfReferential) {
|
|
663
|
-
|
|
664
|
-
apiAssignments[apiPathKey] = mod;
|
|
665
|
-
namespaced = true;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (debug) {
|
|
671
|
-
console.log(
|
|
672
|
-
`[DEBUG] ${mode}: Multi-default function - using filename '${apiPathKey}' for default export, mod type: ${typeof mod}, function name: ${defaultFunction?.name}`
|
|
673
|
-
);
|
|
674
|
-
}
|
|
675
|
-
} else if (isSelfReferential) {
|
|
676
|
-
|
|
677
|
-
apiAssignments[apiPathKey] = mod;
|
|
678
|
-
namespaced = true;
|
|
679
|
-
|
|
680
|
-
if (debug) {
|
|
681
|
-
console.log(`[DEBUG] ${mode}: Self-referential function - preserving ${fileName} as namespace`);
|
|
682
|
-
}
|
|
683
|
-
} else {
|
|
684
|
-
|
|
685
|
-
if (debug) {
|
|
686
|
-
console.log(
|
|
687
|
-
`[DEBUG] ${mode}: Processing traditional default function: hasMultipleDefaultExports=${hasMultipleDefaultExports}, rootDefaultFunction=${!!(getRootDefault && getRootDefault())}`
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (mode === "root" && getRootDefault && setRootDefault && !hasMultipleDefaultExports && !getRootDefault()) {
|
|
693
|
-
setRootDefault(defaultFunction);
|
|
694
|
-
rootDefaultSet = true;
|
|
695
|
-
|
|
696
|
-
if (debug) {
|
|
697
|
-
console.log(`[DEBUG] ${mode}: Set rootDefaultFunction to:`, defaultFunction.name);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
} else {
|
|
703
|
-
|
|
704
|
-
apiAssignments[apiPathKey] = mod;
|
|
705
|
-
namespaced = true;
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
} else {
|
|
712
|
-
|
|
713
|
-
processed = true;
|
|
714
|
-
|
|
715
|
-
if (debug) {
|
|
716
|
-
console.log(`[DEBUG] ${mode}: Processing non-function or named-only exports for ${fileName}`);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const decision = getFlatteningDecision({
|
|
721
|
-
mod,
|
|
722
|
-
fileName,
|
|
723
|
-
apiPathKey,
|
|
724
|
-
hasMultipleDefaultExports,
|
|
725
|
-
isSelfReferential,
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
moduleHasDefault: originalAnalysis ? originalAnalysis.hasDefault : !!mod.default,
|
|
730
|
-
categoryName,
|
|
731
|
-
totalModules,
|
|
732
|
-
debug
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
if (debug) {
|
|
736
|
-
console.log(`[DEBUG] ${mode}: Flattening decision for ${fileName}: ${decision.reason}`);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
if (decision.useAutoFlattening) {
|
|
740
|
-
|
|
741
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
742
|
-
apiAssignments[apiPathKey] = mod[moduleKeys[0]];
|
|
743
|
-
flattened = true;
|
|
744
|
-
} else if (decision.flattenToRoot || decision.flattenToCategory) {
|
|
745
|
-
|
|
746
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
747
|
-
for (const key of moduleKeys) {
|
|
748
|
-
apiAssignments[key] = mod[key];
|
|
749
|
-
if (debug) {
|
|
750
|
-
console.log(`[DEBUG] ${mode}: Flattened ${fileName}.${key} to ${decision.flattenToRoot ? "root" : "category"}.${key}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
flattened = true;
|
|
754
|
-
} else if (isSelfReferential) {
|
|
755
|
-
|
|
756
|
-
apiAssignments[apiPathKey] = mod[apiPathKey] || mod;
|
|
757
|
-
namespaced = true;
|
|
758
|
-
} else {
|
|
759
|
-
|
|
760
|
-
apiAssignments[apiPathKey] = mod;
|
|
761
|
-
namespaced = true;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
for (const [key, value] of Object.entries(apiAssignments)) {
|
|
767
|
-
if (debug && key && typeof value === "function" && value.name) {
|
|
768
|
-
console.log(`[DEBUG] ${mode}: Assigning key '${key}' to function '${value.name}'`);
|
|
769
|
-
}
|
|
770
|
-
api[key] = value;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
return {
|
|
774
|
-
processed,
|
|
775
|
-
rootDefaultSet,
|
|
776
|
-
flattened,
|
|
777
|
-
namespaced,
|
|
778
|
-
apiAssignments
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
export function applyFunctionNamePreference(options) {
|
|
784
|
-
const { mod, fileName, apiPathKey, categoryModules, toapiPathKey, debug = false } = options;
|
|
785
|
-
|
|
786
|
-
let hasPreferredName = false;
|
|
787
|
-
let preferredKey = apiPathKey;
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
for (const [, exportValue] of Object.entries(mod)) {
|
|
791
|
-
if (typeof exportValue === "function" && exportValue.name) {
|
|
792
|
-
const functionNameLower = exportValue.name.toLowerCase();
|
|
793
|
-
const filenameLower = fileName.toLowerCase();
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if (functionNameLower === filenameLower && exportValue.name !== apiPathKey) {
|
|
797
|
-
|
|
798
|
-
preferredKey = exportValue.name;
|
|
799
|
-
hasPreferredName = true;
|
|
800
|
-
|
|
801
|
-
if (debug) {
|
|
802
|
-
console.log(`[DEBUG] Using function name preference: ${exportValue.name} instead of ${apiPathKey} for ${fileName}`);
|
|
803
|
-
}
|
|
804
|
-
break;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
const sanitizedFunctionName = toapiPathKey(exportValue.name);
|
|
809
|
-
if (sanitizedFunctionName.toLowerCase() === apiPathKey.toLowerCase() && exportValue.name !== apiPathKey) {
|
|
810
|
-
preferredKey = exportValue.name;
|
|
811
|
-
hasPreferredName = true;
|
|
812
|
-
|
|
813
|
-
if (debug) {
|
|
814
|
-
console.log(`[DEBUG] Using function name preference: ${exportValue.name} instead of ${apiPathKey} for ${fileName}`);
|
|
815
|
-
}
|
|
816
|
-
break;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
if (hasPreferredName) {
|
|
822
|
-
|
|
823
|
-
categoryModules[preferredKey] = mod;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
return { hasPreferredName, preferredKey };
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
export async function buildCategoryStructure(categoryPath, options = {}) {
|
|
831
|
-
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler, instance } = options;
|
|
832
|
-
|
|
833
|
-
if (!instance || typeof instance._toapiPathKey !== "function" || typeof instance._shouldIncludeFile !== "function") {
|
|
834
|
-
throw new Error("buildCategoryStructure requires a valid slothlet instance");
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
const debug = instance.config?.debug || false;
|
|
838
|
-
|
|
839
|
-
if (debug) {
|
|
840
|
-
console.log(`[DEBUG] buildCategoryStructure called with path: ${categoryPath}, mode: ${mode}, depth: ${currentDepth}`);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
const files = await fs.readdir(categoryPath, { withFileTypes: true });
|
|
844
|
-
const moduleFiles = files.filter((f) => instance._shouldIncludeFile(f));
|
|
845
|
-
const categoryName = instance._toapiPathKey(path.basename(categoryPath));
|
|
846
|
-
const subDirs = files.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
if (moduleFiles.length === 1 && subDirs.length === 0) {
|
|
850
|
-
const moduleExt = path.extname(moduleFiles[0].name);
|
|
851
|
-
const moduleName = instance._toapiPathKey(path.basename(moduleFiles[0].name, moduleExt));
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const analysis = await analyzeModule(path.join(categoryPath, moduleFiles[0].name), {
|
|
855
|
-
debug,
|
|
856
|
-
instance
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const mod = processModuleFromAnalysis(analysis, { instance, debug });
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const functionNameMatchesFolder = typeof mod === "function" && mod.name && mod.name.toLowerCase() === categoryName.toLowerCase();
|
|
864
|
-
|
|
865
|
-
const functionNameMatchesFilename =
|
|
866
|
-
typeof mod === "function" &&
|
|
867
|
-
mod.name &&
|
|
868
|
-
instance._toapiPathKey(mod.name).toLowerCase() === instance._toapiPathKey(moduleName).toLowerCase() &&
|
|
869
|
-
mod.name !== instance._toapiPathKey(moduleName);
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
if (moduleName === categoryName && typeof mod === "function" && currentDepth > 0) {
|
|
878
|
-
try {
|
|
879
|
-
Object.defineProperty(mod, "name", { value: categoryName, configurable: true });
|
|
880
|
-
} catch {
|
|
881
|
-
|
|
882
|
-
}
|
|
883
|
-
return mod;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (moduleName === categoryName && mod && typeof mod === "object" && !Array.isArray(mod) && currentDepth > 0) {
|
|
888
|
-
if (debug) {
|
|
889
|
-
console.log(`[DEBUG] Single-file auto-flattening: ${categoryName}/${moduleFiles[0].name} -> flatten object contents`);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
894
|
-
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
895
|
-
return mod[moduleName];
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
if (moduleKeys.length > 1) {
|
|
905
|
-
if (debug) {
|
|
906
|
-
console.log(`[DEBUG] Default export flattening: ${categoryName}/${moduleFiles[0].name} -> flatten default object contents`);
|
|
907
|
-
}
|
|
908
|
-
return mod;
|
|
909
|
-
}
|
|
910
|
-
return mod;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (moduleFiles.length === 1 && currentDepth > 0 && mod && typeof mod === "object" && !Array.isArray(mod)) {
|
|
915
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
916
|
-
const fileName = moduleFiles[0].name.replace(/\.(mjs|cjs|js)$/, "");
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
const isGenericFilename = ["singlefile", "index", "main", "default"].includes(fileName.toLowerCase());
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (moduleKeys.length === 1 && isGenericFilename) {
|
|
923
|
-
if (debug) {
|
|
924
|
-
console.log(
|
|
925
|
-
`[DEBUG] Single-file parent-level auto-flattening: ${categoryName}/${moduleFiles[0].name} -> flatten to parent level`
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
const exportValue = mod[moduleKeys[0]];
|
|
929
|
-
return { [moduleKeys[0]]: exportValue };
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
935
|
-
try {
|
|
936
|
-
Object.defineProperty(mod, "name", { value: mod.name, configurable: true });
|
|
937
|
-
} catch {
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
return mod;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (functionNameMatchesFilename) {
|
|
945
|
-
return { [mod.name]: mod };
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
if (typeof mod === "function" && (!mod.name || mod.name === "default" || mod.__slothletDefault === true) && currentDepth > 0) {
|
|
950
|
-
try {
|
|
951
|
-
Object.defineProperty(mod, "name", { value: categoryName, configurable: true });
|
|
952
|
-
} catch {
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
return mod;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
960
|
-
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
961
|
-
return mod[moduleName];
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
return { [moduleName]: mod };
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
const categoryModules = {};
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
const analysis = await multidefault_analyzeModules(moduleFiles, categoryPath, { debug, instance });
|
|
973
|
-
const { totalDefaultExports, hasMultipleDefaultExports, selfReferentialFiles, defaultExportFiles: analysisDefaults } = analysis;
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const defaultExportFiles = [];
|
|
977
|
-
for (const { fileName } of analysisDefaults) {
|
|
978
|
-
const file = moduleFiles.find((f) => path.basename(f.name, path.extname(f.name)) === fileName);
|
|
979
|
-
if (file) {
|
|
980
|
-
const analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
981
|
-
debug,
|
|
982
|
-
instance
|
|
983
|
-
});
|
|
984
|
-
const processedMod = processModuleFromAnalysis(analysis, {
|
|
985
|
-
debug,
|
|
986
|
-
instance
|
|
987
|
-
});
|
|
988
|
-
defaultExportFiles.push({ file, moduleName: instance._toapiPathKey(fileName), mod: processedMod, analysis });
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
if (debug) {
|
|
993
|
-
console.log(`[DEBUG] buildCategoryStructure: Multi-default analysis results`);
|
|
994
|
-
console.log(`[DEBUG] - totalDefaultExports: ${totalDefaultExports}`);
|
|
995
|
-
console.log(`[DEBUG] - hasMultipleDefaultExports: ${hasMultipleDefaultExports}`);
|
|
996
|
-
console.log(`[DEBUG] - selfReferentialFiles: ${Array.from(selfReferentialFiles)}`);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
for (const file of moduleFiles) {
|
|
1001
|
-
const moduleExt = path.extname(file.name);
|
|
1002
|
-
const moduleName = instance._toapiPathKey(path.basename(file.name, moduleExt));
|
|
1003
|
-
const fileName = path.basename(file.name, moduleExt);
|
|
1004
|
-
const apiPathKey = instance._toapiPathKey(fileName);
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
let mod = null;
|
|
1008
|
-
let analysis = null;
|
|
1009
|
-
const existingDefault = defaultExportFiles.find((def) => def.moduleName === moduleName);
|
|
1010
|
-
if (existingDefault) {
|
|
1011
|
-
mod = existingDefault.mod;
|
|
1012
|
-
analysis = existingDefault.analysis;
|
|
1013
|
-
} else {
|
|
1014
|
-
analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
1015
|
-
debug,
|
|
1016
|
-
instance
|
|
1017
|
-
});
|
|
1018
|
-
mod = processModuleFromAnalysis(analysis, {
|
|
1019
|
-
debug,
|
|
1020
|
-
instance
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
processModuleForAPI({
|
|
1026
|
-
mod,
|
|
1027
|
-
fileName,
|
|
1028
|
-
apiPathKey,
|
|
1029
|
-
hasMultipleDefaultExports,
|
|
1030
|
-
isSelfReferential: selfReferentialFiles.has(moduleName),
|
|
1031
|
-
api: categoryModules,
|
|
1032
|
-
getRootDefault: () => null,
|
|
1033
|
-
setRootDefault: () => {},
|
|
1034
|
-
context: {
|
|
1035
|
-
debug,
|
|
1036
|
-
mode: "category",
|
|
1037
|
-
categoryName,
|
|
1038
|
-
totalModules: moduleFiles.length
|
|
1039
|
-
},
|
|
1040
|
-
originalAnalysis: analysis
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
for (const subDirEntry of subDirs) {
|
|
1046
|
-
if (currentDepth < maxDepth) {
|
|
1047
|
-
const key = instance._toapiPathKey(subDirEntry.name);
|
|
1048
|
-
const subDirPath = path.join(categoryPath, subDirEntry.name);
|
|
1049
|
-
let subModule;
|
|
1050
|
-
|
|
1051
|
-
if (mode === "lazy" && typeof subdirHandler === "function") {
|
|
1052
|
-
|
|
1053
|
-
subModule = subdirHandler({
|
|
1054
|
-
subDirEntry,
|
|
1055
|
-
subDirPath,
|
|
1056
|
-
key,
|
|
1057
|
-
categoryModules,
|
|
1058
|
-
currentDepth,
|
|
1059
|
-
maxDepth
|
|
1060
|
-
});
|
|
1061
|
-
} else {
|
|
1062
|
-
|
|
1063
|
-
subModule = await buildCategoryStructure(subDirPath, {
|
|
1064
|
-
currentDepth: currentDepth + 1,
|
|
1065
|
-
maxDepth,
|
|
1066
|
-
mode: "eager",
|
|
1067
|
-
instance
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if (
|
|
1074
|
-
typeof subModule === "function" &&
|
|
1075
|
-
subModule.name &&
|
|
1076
|
-
subModule.name.toLowerCase() === key.toLowerCase() &&
|
|
1077
|
-
subModule.name !== key
|
|
1078
|
-
) {
|
|
1079
|
-
categoryModules[subModule.name] = subModule;
|
|
1080
|
-
} else {
|
|
1081
|
-
categoryModules[key] = subModule;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
return categoryModules;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
export async function buildRootAPI(dir, options = {}) {
|
|
1091
|
-
const { lazy = false, maxDepth = Infinity, instance } = options;
|
|
1092
|
-
|
|
1093
|
-
if (!instance || typeof instance._shouldIncludeFile !== "function" || typeof instance._loadCategory !== "function") {
|
|
1094
|
-
throw new Error("buildRootAPI requires a valid slothlet instance");
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
const debug = instance.config?.debug || false;
|
|
1098
|
-
|
|
1099
|
-
if (debug) {
|
|
1100
|
-
console.log(`[DEBUG] buildRootAPI called with dir: ${dir}, lazy: ${lazy}, maxDepth: ${maxDepth}`);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1104
|
-
const api = {};
|
|
1105
|
-
let rootDefaultFunction = null;
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
const moduleFiles = entries.filter((e) => instance._shouldIncludeFile(e));
|
|
1109
|
-
|
|
1110
|
-
if (moduleFiles.length > 0) {
|
|
1111
|
-
|
|
1112
|
-
const analysis = await multidefault_analyzeModules(moduleFiles, dir, { debug, instance });
|
|
1113
|
-
const { hasMultipleDefaultExports, selfReferentialFiles } = analysis;
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
for (const entry of moduleFiles) {
|
|
1117
|
-
const ext = path.extname(entry.name);
|
|
1118
|
-
const fileName = path.basename(entry.name, ext);
|
|
1119
|
-
const apiPathKey = instance._toapiPathKey(fileName);
|
|
1120
|
-
|
|
1121
|
-
const analysis = await analyzeModule(path.join(dir, entry.name), {
|
|
1122
|
-
debug,
|
|
1123
|
-
instance
|
|
1124
|
-
});
|
|
1125
|
-
const mod = processModuleFromAnalysis(analysis, {
|
|
1126
|
-
debug,
|
|
1127
|
-
instance
|
|
1128
|
-
});
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
processModuleForAPI({
|
|
1132
|
-
mod,
|
|
1133
|
-
fileName,
|
|
1134
|
-
apiPathKey,
|
|
1135
|
-
hasMultipleDefaultExports,
|
|
1136
|
-
isSelfReferential: selfReferentialFiles.has(fileName),
|
|
1137
|
-
api,
|
|
1138
|
-
getRootDefault: () => rootDefaultFunction,
|
|
1139
|
-
setRootDefault: (fn) => {
|
|
1140
|
-
rootDefaultFunction = fn;
|
|
1141
|
-
},
|
|
1142
|
-
context: {
|
|
1143
|
-
debug,
|
|
1144
|
-
mode: "root",
|
|
1145
|
-
totalModules: moduleFiles.length
|
|
1146
|
-
}
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
for (const entry of entries) {
|
|
1153
|
-
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
1154
|
-
const categoryPath = path.join(dir, entry.name);
|
|
1155
|
-
|
|
1156
|
-
if (lazy) {
|
|
1157
|
-
|
|
1158
|
-
api[instance._toapiPathKey(entry.name)] = await instance._loadCategory(categoryPath, 0, maxDepth);
|
|
1159
|
-
} else {
|
|
1160
|
-
|
|
1161
|
-
api[instance._toapiPathKey(entry.name)] = await buildCategoryStructure(categoryPath, {
|
|
1162
|
-
currentDepth: 1,
|
|
1163
|
-
maxDepth,
|
|
1164
|
-
mode: "eager",
|
|
1165
|
-
instance
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
let finalApi;
|
|
1173
|
-
if (debug) {
|
|
1174
|
-
console.log(`[DEBUG] Final assembly: rootDefaultFunction=${!!rootDefaultFunction}`);
|
|
1175
|
-
console.log(`[DEBUG] API object keys before final assembly:`, Object.keys(api));
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
if (rootDefaultFunction) {
|
|
1179
|
-
|
|
1180
|
-
Object.assign(rootDefaultFunction, api);
|
|
1181
|
-
finalApi = rootDefaultFunction;
|
|
1182
|
-
|
|
1183
|
-
if (debug) {
|
|
1184
|
-
console.log(`[DEBUG] Applied root contributor pattern - final API is function`);
|
|
1185
|
-
}
|
|
1186
|
-
} else {
|
|
1187
|
-
|
|
1188
|
-
finalApi = api;
|
|
1189
|
-
|
|
1190
|
-
if (debug) {
|
|
1191
|
-
console.log(`[DEBUG] No root function - final API is object`);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
return finalApi;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
export async function buildCategoryDecisions(categoryPath, options = {}) {
|
|
1200
|
-
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
1201
|
-
const { instance } = options;
|
|
1202
|
-
|
|
1203
|
-
if (!instance || typeof instance._toapiPathKey !== "function") {
|
|
1204
|
-
throw new Error("buildCategoryDecisions requires instance parameter with _toapiPathKey method");
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
const debug = instance.config?.debug || false;
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
if (debug) {
|
|
1211
|
-
console.log(`[DEBUG] buildCategoryDecisions called with path: ${categoryPath}, mode: ${mode}`);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
const files = await fs.readdir(categoryPath, { withFileTypes: true });
|
|
1215
|
-
const moduleFiles = files.filter((f) => instance._shouldIncludeFile(f));
|
|
1216
|
-
const categoryName = instance._toapiPathKey(path.basename(categoryPath));
|
|
1217
|
-
const subDirs = files.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
1218
|
-
|
|
1219
|
-
const decisions = {
|
|
1220
|
-
type: null,
|
|
1221
|
-
categoryName,
|
|
1222
|
-
moduleFiles,
|
|
1223
|
-
subDirs,
|
|
1224
|
-
currentDepth,
|
|
1225
|
-
maxDepth,
|
|
1226
|
-
mode,
|
|
1227
|
-
subdirHandler,
|
|
1228
|
-
|
|
1229
|
-
singleFile: null,
|
|
1230
|
-
shouldFlatten: false,
|
|
1231
|
-
flattenType: null,
|
|
1232
|
-
preferredName: null,
|
|
1233
|
-
|
|
1234
|
-
multifileAnalysis: null,
|
|
1235
|
-
processedModules: [],
|
|
1236
|
-
categoryModules: {},
|
|
1237
|
-
|
|
1238
|
-
subdirectoryDecisions: []
|
|
1239
|
-
};
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
if (moduleFiles.length === 1 && subDirs.length === 0) {
|
|
1243
|
-
decisions.type = "single-file";
|
|
1244
|
-
const moduleFile = moduleFiles[0];
|
|
1245
|
-
const moduleExt = path.extname(moduleFile.name);
|
|
1246
|
-
const moduleName = instance._toapiPathKey(path.basename(moduleFile.name, moduleExt));
|
|
1247
|
-
|
|
1248
|
-
decisions.singleFile = {
|
|
1249
|
-
file: moduleFile,
|
|
1250
|
-
moduleName,
|
|
1251
|
-
moduleExt
|
|
1252
|
-
};
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
const analysis = await analyzeModule(path.join(categoryPath, moduleFile.name), {
|
|
1256
|
-
debug,
|
|
1257
|
-
instance
|
|
1258
|
-
});
|
|
1259
|
-
const mod = processModuleFromAnalysis(analysis, {
|
|
1260
|
-
debug,
|
|
1261
|
-
instance
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
decisions.singleFile.mod = mod;
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const functionNameMatchesFolder = typeof mod === "function" && mod.name && mod.name.toLowerCase() === categoryName.toLowerCase();
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
const functionNameMatchesFilename =
|
|
1271
|
-
typeof mod === "function" &&
|
|
1272
|
-
mod.name &&
|
|
1273
|
-
instance._toapiPathKey(mod.name).toLowerCase() === instance._toapiPathKey(moduleName).toLowerCase() &&
|
|
1274
|
-
mod.name !== instance._toapiPathKey(moduleName);
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
if (moduleName === categoryName && typeof mod === "function" && currentDepth > 0) {
|
|
1279
|
-
decisions.shouldFlatten = true;
|
|
1280
|
-
decisions.flattenType = "function-folder-match";
|
|
1281
|
-
decisions.preferredName = categoryName;
|
|
1282
|
-
return decisions;
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (analysis.hasDefault && analysis.defaultExportType === "object" && moduleName === categoryName && currentDepth > 0) {
|
|
1288
|
-
if (debug) {
|
|
1289
|
-
console.log(`[DEBUG] Default export flattening: ${categoryName}/${moduleFile.name} -> flatten default object contents`);
|
|
1290
|
-
}
|
|
1291
|
-
decisions.shouldFlatten = true;
|
|
1292
|
-
decisions.flattenType = "default-export-flatten";
|
|
1293
|
-
return decisions;
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
if (moduleName === categoryName && mod && typeof mod === "object" && !Array.isArray(mod) && currentDepth > 0) {
|
|
1299
|
-
|
|
1300
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
1301
|
-
if (debug) {
|
|
1302
|
-
console.log(
|
|
1303
|
-
`[DEBUG] Auto-flatten check: moduleName="${moduleName}" categoryName="${categoryName}" moduleKeys=[${moduleKeys}] match=${moduleKeys.length === 1 && moduleKeys[0] === moduleName}`
|
|
1304
|
-
);
|
|
1305
|
-
}
|
|
1306
|
-
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
1307
|
-
if (debug) {
|
|
1308
|
-
console.log(`[DEBUG] Single-file auto-flattening: ${categoryName}/${moduleFile.name} -> flatten object contents`);
|
|
1309
|
-
}
|
|
1310
|
-
decisions.shouldFlatten = true;
|
|
1311
|
-
decisions.flattenType = "object-auto-flatten";
|
|
1312
|
-
decisions.preferredName = moduleKeys[0];
|
|
1313
|
-
return decisions;
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
const fileBaseName = moduleFile.name.replace(/\.(mjs|cjs|js)$/, "");
|
|
1319
|
-
if (fileBaseName === categoryName && moduleKeys.length > 0) {
|
|
1320
|
-
if (debug) {
|
|
1321
|
-
console.log(
|
|
1322
|
-
`[DEBUG] Single-file filename-folder exact match flattening: ${categoryName}/${moduleFile.name} -> avoid double nesting`
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
decisions.shouldFlatten = true;
|
|
1326
|
-
decisions.flattenType = "filename-folder-match-flatten";
|
|
1327
|
-
return decisions;
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
if (moduleFiles.length === 1 && currentDepth > 0 && mod && typeof mod === "object" && !Array.isArray(mod)) {
|
|
1336
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
1337
|
-
const fileName = moduleFile.name.replace(/\.(mjs|cjs|js)$/, "");
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const isGenericFilename = ["singlefile", "index", "main", "default"].includes(fileName.toLowerCase());
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if (moduleKeys.length === 1 && isGenericFilename) {
|
|
1345
|
-
if (debug) {
|
|
1346
|
-
console.log(`[DEBUG] Single-file parent-level auto-flattening: ${categoryName}/${moduleFile.name} -> flatten to parent level`);
|
|
1347
|
-
}
|
|
1348
|
-
decisions.shouldFlatten = true;
|
|
1349
|
-
decisions.flattenType = "parent-level-flatten";
|
|
1350
|
-
decisions.preferredName = moduleKeys[0];
|
|
1351
|
-
return decisions;
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
1358
|
-
decisions.shouldFlatten = true;
|
|
1359
|
-
decisions.flattenType = "function-folder-match";
|
|
1360
|
-
decisions.preferredName = mod.name;
|
|
1361
|
-
return decisions;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
if (functionNameMatchesFilename) {
|
|
1366
|
-
decisions.shouldFlatten = false;
|
|
1367
|
-
decisions.preferredName = mod.name;
|
|
1368
|
-
return decisions;
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (
|
|
1375
|
-
typeof mod === "function" &&
|
|
1376
|
-
(!mod.name || mod.name === "default" || mod.__slothletDefault === true) &&
|
|
1377
|
-
currentDepth > 0
|
|
1378
|
-
) {
|
|
1379
|
-
decisions.shouldFlatten = true;
|
|
1380
|
-
decisions.flattenType = "default-function";
|
|
1381
|
-
decisions.preferredName = categoryName;
|
|
1382
|
-
return decisions;
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
1387
|
-
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
1388
|
-
|
|
1389
|
-
decisions.shouldFlatten = true;
|
|
1390
|
-
decisions.flattenType = "object-auto-flatten";
|
|
1391
|
-
decisions.preferredName = moduleName;
|
|
1392
|
-
return decisions;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
decisions.shouldFlatten = false;
|
|
1397
|
-
decisions.preferredName = moduleName;
|
|
1398
|
-
return decisions;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
decisions.type = "multi-file";
|
|
1403
|
-
if (debug) {
|
|
1404
|
-
console.log(`[DEBUG] buildCategoryDecisions: Processing multi-file case for ${categoryPath}`);
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
const analysis = await multidefault_analyzeModules(moduleFiles, categoryPath, { debug, instance });
|
|
1409
|
-
|
|
1410
|
-
decisions.multifileAnalysis = analysis;
|
|
1411
|
-
|
|
1412
|
-
const { totalDefaultExports, hasMultipleDefaultExports, selfReferentialFiles, defaultExportFiles: analysisDefaults } = analysis;
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
const defaultExportFiles = [];
|
|
1416
|
-
for (const { fileName } of analysisDefaults) {
|
|
1417
|
-
const file = moduleFiles.find((f) => path.basename(f.name, path.extname(f.name)) === fileName);
|
|
1418
|
-
if (file) {
|
|
1419
|
-
const analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
1420
|
-
debug,
|
|
1421
|
-
instance
|
|
1422
|
-
});
|
|
1423
|
-
const processedMod = processModuleFromAnalysis(analysis, {
|
|
1424
|
-
debug,
|
|
1425
|
-
instance
|
|
1426
|
-
});
|
|
1427
|
-
defaultExportFiles.push({ file, moduleName: instance._toapiPathKey(fileName), mod: processedMod, analysis });
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
if (debug) {
|
|
1432
|
-
console.log(`[DEBUG] buildCategoryDecisions: Using shared multidefault utility results`);
|
|
1433
|
-
console.log(`[DEBUG] - totalDefaultExports: ${totalDefaultExports}`);
|
|
1434
|
-
console.log(`[DEBUG] - hasMultipleDefaultExports: ${hasMultipleDefaultExports}`);
|
|
1435
|
-
console.log(`[DEBUG] - selfReferentialFiles: ${Array.from(selfReferentialFiles)}`);
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
for (const file of moduleFiles) {
|
|
1440
|
-
const moduleExt = path.extname(file.name);
|
|
1441
|
-
const moduleName = instance._toapiPathKey(path.basename(file.name, moduleExt));
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
let mod = null;
|
|
1445
|
-
let analysis = null;
|
|
1446
|
-
const existingDefault = defaultExportFiles.find((def) => def.moduleName === moduleName);
|
|
1447
|
-
if (existingDefault) {
|
|
1448
|
-
mod = existingDefault.mod;
|
|
1449
|
-
|
|
1450
|
-
analysis = existingDefault.analysis;
|
|
1451
|
-
} else {
|
|
1452
|
-
|
|
1453
|
-
analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
1454
|
-
debug,
|
|
1455
|
-
instance
|
|
1456
|
-
});
|
|
1457
|
-
mod = processModuleFromAnalysis(analysis, {
|
|
1458
|
-
debug,
|
|
1459
|
-
instance
|
|
1460
|
-
});
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
const moduleDecision = {
|
|
1464
|
-
file,
|
|
1465
|
-
moduleName,
|
|
1466
|
-
mod,
|
|
1467
|
-
type: null,
|
|
1468
|
-
apiPathKey: null,
|
|
1469
|
-
shouldFlatten: false,
|
|
1470
|
-
flattenType: null,
|
|
1471
|
-
specialHandling: null
|
|
1472
|
-
};
|
|
1473
|
-
|
|
1474
|
-
if (moduleName === categoryName && mod && typeof mod === "object") {
|
|
1475
|
-
moduleDecision.type = "category-match-object";
|
|
1476
|
-
moduleDecision.specialHandling = "category-merge";
|
|
1477
|
-
} else if (typeof mod === "function") {
|
|
1478
|
-
moduleDecision.type = "function";
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const isSelfReferential = selfReferentialFiles.has(moduleName);
|
|
1482
|
-
|
|
1483
|
-
if (hasMultipleDefaultExports && mod.__slothletDefault === true && !isSelfReferential) {
|
|
1484
|
-
|
|
1485
|
-
moduleDecision.apiPathKey = moduleName;
|
|
1486
|
-
moduleDecision.specialHandling = "multi-default-filename";
|
|
1487
|
-
if (debug) {
|
|
1488
|
-
console.log(
|
|
1489
|
-
`[DEBUG] Multi-default function case: ${moduleName} => ${moduleDecision.apiPathKey} (hasMultiple=${hasMultipleDefaultExports}, __slothletDefault=${mod.__slothletDefault}, isSelfRef=${isSelfReferential})`
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
} else if (selfReferentialFiles.has(moduleName)) {
|
|
1493
|
-
|
|
1494
|
-
moduleDecision.type = "self-referential";
|
|
1495
|
-
moduleDecision.specialHandling = "self-referential-namespace";
|
|
1496
|
-
} else {
|
|
1497
|
-
|
|
1498
|
-
const fnName = mod.name && mod.name !== "default" ? mod.name : moduleName;
|
|
1499
|
-
if (debug) {
|
|
1500
|
-
console.log(
|
|
1501
|
-
`[DEBUG] Standard function case: ${moduleName}, fnName=${fnName}, mod.__slothletDefault=${mod.__slothletDefault}, hasMultiple=${hasMultipleDefaultExports}`
|
|
1502
|
-
);
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
if (fnName && fnName.toLowerCase() === moduleName.toLowerCase() && fnName !== moduleName) {
|
|
1508
|
-
|
|
1509
|
-
moduleDecision.apiPathKey = fnName;
|
|
1510
|
-
moduleDecision.specialHandling = "prefer-function-name";
|
|
1511
|
-
} else {
|
|
1512
|
-
|
|
1513
|
-
moduleDecision.apiPathKey = instance._toapiPathKey(fnName);
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
} else {
|
|
1517
|
-
moduleDecision.type = "object";
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
let hasPreferredName = false;
|
|
1521
|
-
const modWithPreferredNames = {};
|
|
1522
|
-
|
|
1523
|
-
for (const [exportName, exportValue] of Object.entries(mod)) {
|
|
1524
|
-
if (
|
|
1525
|
-
typeof exportValue === "function" &&
|
|
1526
|
-
exportValue.name &&
|
|
1527
|
-
instance._toapiPathKey(exportValue.name).toLowerCase() === instance._toapiPathKey(moduleName).toLowerCase() &&
|
|
1528
|
-
exportValue.name !== instance._toapiPathKey(moduleName)
|
|
1529
|
-
) {
|
|
1530
|
-
|
|
1531
|
-
modWithPreferredNames[exportValue.name] = exportValue;
|
|
1532
|
-
hasPreferredName = true;
|
|
1533
|
-
} else {
|
|
1534
|
-
modWithPreferredNames[instance._toapiPathKey(exportName)] = exportValue;
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
if (hasPreferredName) {
|
|
1539
|
-
moduleDecision.specialHandling = "preferred-export-names";
|
|
1540
|
-
moduleDecision.processedExports = modWithPreferredNames;
|
|
1541
|
-
} else if (selfReferentialFiles.has(moduleName)) {
|
|
1542
|
-
|
|
1543
|
-
moduleDecision.type = "self-referential";
|
|
1544
|
-
moduleDecision.specialHandling = "self-referential-namespace";
|
|
1545
|
-
} else {
|
|
1546
|
-
|
|
1547
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
1548
|
-
const apiPathKey = instance._toapiPathKey(moduleName);
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
if (!hasMultipleDefaultExports && analysis.hasDefault && analysis.defaultExportType === "object") {
|
|
1553
|
-
moduleDecision.shouldFlatten = true;
|
|
1554
|
-
moduleDecision.flattenType = "single-default-object";
|
|
1555
|
-
moduleDecision.apiPathKey = apiPathKey;
|
|
1556
|
-
} else if (hasMultipleDefaultExports && !analysis.hasDefault && moduleKeys.length > 0) {
|
|
1557
|
-
|
|
1558
|
-
moduleDecision.shouldFlatten = true;
|
|
1559
|
-
moduleDecision.flattenType = "multi-default-no-default";
|
|
1560
|
-
} else if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
1561
|
-
|
|
1562
|
-
moduleDecision.shouldFlatten = true;
|
|
1563
|
-
moduleDecision.flattenType = "single-named-export-match";
|
|
1564
|
-
moduleDecision.apiPathKey = apiPathKey;
|
|
1565
|
-
} else if (!analysis.hasDefault && moduleKeys.length > 0 && moduleName === categoryName) {
|
|
1566
|
-
|
|
1567
|
-
moduleDecision.shouldFlatten = true;
|
|
1568
|
-
moduleDecision.flattenType = "category-name-match-flatten";
|
|
1569
|
-
} else {
|
|
1570
|
-
|
|
1571
|
-
moduleDecision.apiPathKey = apiPathKey;
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
decisions.processedModules.push(moduleDecision);
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
for (const subDir of subDirs) {
|
|
1581
|
-
const subDirPath = path.join(categoryPath, subDir.name);
|
|
1582
|
-
const subDirDecision = {
|
|
1583
|
-
name: subDir.name,
|
|
1584
|
-
path: subDirPath,
|
|
1585
|
-
apiPathKey: instance._toapiPathKey(subDir.name),
|
|
1586
|
-
shouldRecurse: currentDepth < maxDepth
|
|
1587
|
-
};
|
|
1588
|
-
decisions.subdirectoryDecisions.push(subDirDecision);
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
return decisions;
|
|
1592
|
-
}
|
|
41
|
+
export { addApiFromFolder } from "@cldmv/slothlet/helpers/api_builder/add_api";
|