@cldmv/slothlet 2.7.1 → 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 -1475
- 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 +197 -547
- package/docs/API-RULES-CONDITIONS.md +508 -0
- package/{API-RULES.md → docs/API-RULES.md} +127 -72
- package/index.cjs +2 -1
- package/index.mjs +2 -1
- 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 +23 -13
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
- package/API-RULES-CONDITIONS.md +0 -367
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 CLDMV/Shinrai
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import fs from "node:fs/promises";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { multidefault_analyzeModules } from "@cldmv/slothlet/helpers/multidefault";
|
|
24
|
+
import { analyzeModule, processModuleFromAnalysis } from "@cldmv/slothlet/helpers/api_builder/analysis";
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export function getFlatteningDecision(options) {
|
|
32
|
+
const {
|
|
33
|
+
mod,
|
|
34
|
+
fileName,
|
|
35
|
+
apiPathKey,
|
|
36
|
+
hasMultipleDefaultExports,
|
|
37
|
+
isSelfReferential,
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
moduleHasDefault = !!mod.default,
|
|
41
|
+
categoryName,
|
|
42
|
+
|
|
43
|
+
totalModules = 1
|
|
44
|
+
} = options;
|
|
45
|
+
|
|
46
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if (isSelfReferential) {
|
|
50
|
+
return {
|
|
51
|
+
shouldFlatten: false,
|
|
52
|
+
flattenToRoot: false,
|
|
53
|
+
flattenToCategory: false,
|
|
54
|
+
preserveAsNamespace: true,
|
|
55
|
+
useAutoFlattening: false,
|
|
56
|
+
reason: "self-referential export"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if (hasMultipleDefaultExports) {
|
|
62
|
+
if (moduleHasDefault) {
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
shouldFlatten: false,
|
|
66
|
+
flattenToRoot: false,
|
|
67
|
+
flattenToCategory: false,
|
|
68
|
+
preserveAsNamespace: true,
|
|
69
|
+
useAutoFlattening: false,
|
|
70
|
+
reason: "multi-default context with default export"
|
|
71
|
+
};
|
|
72
|
+
} else {
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
shouldFlatten: true,
|
|
76
|
+
flattenToRoot: true,
|
|
77
|
+
flattenToCategory: true,
|
|
78
|
+
preserveAsNamespace: false,
|
|
79
|
+
useAutoFlattening: false,
|
|
80
|
+
reason: "multi-default context without default export"
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
87
|
+
return {
|
|
88
|
+
shouldFlatten: true,
|
|
89
|
+
flattenToRoot: false,
|
|
90
|
+
flattenToCategory: false,
|
|
91
|
+
preserveAsNamespace: false,
|
|
92
|
+
useAutoFlattening: true,
|
|
93
|
+
reason: "auto-flatten single named export matching filename"
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0) {
|
|
99
|
+
return {
|
|
100
|
+
shouldFlatten: true,
|
|
101
|
+
flattenToRoot: false,
|
|
102
|
+
flattenToCategory: true,
|
|
103
|
+
preserveAsNamespace: false,
|
|
104
|
+
useAutoFlattening: false,
|
|
105
|
+
reason: "filename matches container, flatten to category"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
shouldFlatten: false,
|
|
126
|
+
flattenToRoot: false,
|
|
127
|
+
flattenToCategory: false,
|
|
128
|
+
preserveAsNamespace: true,
|
|
129
|
+
useAutoFlattening: false,
|
|
130
|
+
reason: "traditional namespace preservation"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
export function applyFunctionNamePreference(options) {
|
|
136
|
+
const { mod, fileName, apiPathKey, categoryModules, toapiPathKey, debug = false } = options;
|
|
137
|
+
|
|
138
|
+
let hasPreferredName = false;
|
|
139
|
+
let preferredKey = apiPathKey;
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
for (const [, exportValue] of Object.entries(mod)) {
|
|
143
|
+
if (typeof exportValue === "function" && exportValue.name) {
|
|
144
|
+
const functionNameLower = exportValue.name.toLowerCase();
|
|
145
|
+
const filenameLower = fileName.toLowerCase();
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if (functionNameLower === filenameLower && exportValue.name !== apiPathKey) {
|
|
149
|
+
|
|
150
|
+
preferredKey = exportValue.name;
|
|
151
|
+
hasPreferredName = true;
|
|
152
|
+
|
|
153
|
+
if (debug) {
|
|
154
|
+
console.log(`[DEBUG] Using function name preference: ${exportValue.name} instead of ${apiPathKey} for ${fileName}`);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
const sanitizedFunctionName = toapiPathKey(exportValue.name);
|
|
161
|
+
if (sanitizedFunctionName.toLowerCase() === apiPathKey.toLowerCase() && exportValue.name !== apiPathKey) {
|
|
162
|
+
preferredKey = exportValue.name;
|
|
163
|
+
hasPreferredName = true;
|
|
164
|
+
|
|
165
|
+
if (debug) {
|
|
166
|
+
console.log(`[DEBUG] Using function name preference: ${exportValue.name} instead of ${apiPathKey} for ${fileName}`);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (hasPreferredName) {
|
|
174
|
+
|
|
175
|
+
categoryModules[preferredKey] = mod;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { hasPreferredName, preferredKey };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
export function processModuleForAPI(options) {
|
|
187
|
+
const {
|
|
188
|
+
mod,
|
|
189
|
+
fileName,
|
|
190
|
+
apiPathKey,
|
|
191
|
+
hasMultipleDefaultExports,
|
|
192
|
+
isSelfReferential,
|
|
193
|
+
api,
|
|
194
|
+
getRootDefault,
|
|
195
|
+
setRootDefault,
|
|
196
|
+
context = {},
|
|
197
|
+
originalAnalysis = null
|
|
198
|
+
} = options;
|
|
199
|
+
|
|
200
|
+
const { debug = false, mode = "unknown", categoryName, totalModules = 1 } = context;
|
|
201
|
+
|
|
202
|
+
let processed = false;
|
|
203
|
+
let rootDefaultSet = false;
|
|
204
|
+
let flattened = false;
|
|
205
|
+
let namespaced = false;
|
|
206
|
+
const apiAssignments = {};
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
const hasDefaultFunction = (mod && typeof mod.default === "function") || (mod && typeof mod === "function" && !mod.default);
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
const defaultFunction = mod?.default || (typeof mod === "function" ? mod : null);
|
|
215
|
+
|
|
216
|
+
if (hasDefaultFunction) {
|
|
217
|
+
processed = true;
|
|
218
|
+
|
|
219
|
+
if (hasMultipleDefaultExports && !isSelfReferential) {
|
|
220
|
+
|
|
221
|
+
apiAssignments[apiPathKey] = mod;
|
|
222
|
+
namespaced = true;
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if (debug) {
|
|
228
|
+
console.log(
|
|
229
|
+
`[DEBUG] ${mode}: Multi-default function - using filename '${apiPathKey}' for default export, mod type: ${typeof mod}, function name: ${defaultFunction?.name}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
} else if (isSelfReferential) {
|
|
233
|
+
|
|
234
|
+
apiAssignments[apiPathKey] = mod;
|
|
235
|
+
namespaced = true;
|
|
236
|
+
|
|
237
|
+
if (debug) {
|
|
238
|
+
console.log(`[DEBUG] ${mode}: Self-referential function - preserving ${fileName} as namespace`);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
|
|
242
|
+
if (debug) {
|
|
243
|
+
console.log(
|
|
244
|
+
`[DEBUG] ${mode}: Processing traditional default function: hasMultipleDefaultExports=${hasMultipleDefaultExports}, rootDefaultFunction=${!!(getRootDefault && getRootDefault())}`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
if (mode === "root" && getRootDefault && setRootDefault && !hasMultipleDefaultExports && !getRootDefault()) {
|
|
250
|
+
setRootDefault(defaultFunction);
|
|
251
|
+
rootDefaultSet = true;
|
|
252
|
+
|
|
253
|
+
if (debug) {
|
|
254
|
+
console.log(`[DEBUG] ${mode}: Set rootDefaultFunction to:`, defaultFunction.name);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
} else {
|
|
260
|
+
|
|
261
|
+
apiAssignments[apiPathKey] = mod;
|
|
262
|
+
namespaced = true;
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
|
|
270
|
+
processed = true;
|
|
271
|
+
|
|
272
|
+
if (debug) {
|
|
273
|
+
console.log(`[DEBUG] ${mode}: Processing non-function or named-only exports for ${fileName}`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
const decision = getFlatteningDecision({
|
|
278
|
+
mod,
|
|
279
|
+
fileName,
|
|
280
|
+
apiPathKey,
|
|
281
|
+
hasMultipleDefaultExports,
|
|
282
|
+
isSelfReferential,
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
moduleHasDefault: originalAnalysis ? originalAnalysis.hasDefault : !!mod.default,
|
|
287
|
+
categoryName,
|
|
288
|
+
totalModules,
|
|
289
|
+
debug
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (debug) {
|
|
293
|
+
console.log(`[DEBUG] ${mode}: Flattening decision for ${fileName}: ${decision.reason}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (decision.useAutoFlattening) {
|
|
297
|
+
|
|
298
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
299
|
+
apiAssignments[apiPathKey] = mod[moduleKeys[0]];
|
|
300
|
+
flattened = true;
|
|
301
|
+
} else if (decision.flattenToRoot || decision.flattenToCategory) {
|
|
302
|
+
|
|
303
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
304
|
+
for (const key of moduleKeys) {
|
|
305
|
+
apiAssignments[key] = mod[key];
|
|
306
|
+
if (debug) {
|
|
307
|
+
console.log(`[DEBUG] ${mode}: Flattened ${fileName}.${key} to ${decision.flattenToRoot ? "root" : "category"}.${key}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
flattened = true;
|
|
311
|
+
} else if (isSelfReferential) {
|
|
312
|
+
|
|
313
|
+
apiAssignments[apiPathKey] = mod[apiPathKey] || mod;
|
|
314
|
+
namespaced = true;
|
|
315
|
+
} else {
|
|
316
|
+
|
|
317
|
+
apiAssignments[apiPathKey] = mod;
|
|
318
|
+
namespaced = true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
for (const [key, value] of Object.entries(apiAssignments)) {
|
|
324
|
+
if (debug && key && typeof value === "function" && value.name) {
|
|
325
|
+
console.log(`[DEBUG] ${mode}: Assigning key '${key}' to function '${value.name}'`);
|
|
326
|
+
}
|
|
327
|
+
api[key] = value;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
processed,
|
|
332
|
+
rootDefaultSet,
|
|
333
|
+
flattened,
|
|
334
|
+
namespaced,
|
|
335
|
+
apiAssignments
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
export async function buildCategoryDecisions(categoryPath, options = {}) {
|
|
345
|
+
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
346
|
+
const { instance } = options;
|
|
347
|
+
|
|
348
|
+
if (!instance || typeof instance._toapiPathKey !== "function") {
|
|
349
|
+
throw new Error("buildCategoryDecisions requires instance parameter with _toapiPathKey method");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const debug = instance.config?.debug || false;
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
if (debug) {
|
|
356
|
+
console.log(`[DEBUG] buildCategoryDecisions called with path: ${categoryPath}, mode: ${mode}`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const files = await fs.readdir(categoryPath, { withFileTypes: true });
|
|
360
|
+
const moduleFiles = files.filter((f) => instance._shouldIncludeFile(f));
|
|
361
|
+
const categoryName = instance._toapiPathKey(path.basename(categoryPath));
|
|
362
|
+
const subDirs = files.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
363
|
+
|
|
364
|
+
const decisions = {
|
|
365
|
+
type: null,
|
|
366
|
+
categoryName,
|
|
367
|
+
moduleFiles,
|
|
368
|
+
subDirs,
|
|
369
|
+
currentDepth,
|
|
370
|
+
maxDepth,
|
|
371
|
+
mode,
|
|
372
|
+
subdirHandler,
|
|
373
|
+
|
|
374
|
+
singleFile: null,
|
|
375
|
+
shouldFlatten: false,
|
|
376
|
+
flattenType: null,
|
|
377
|
+
preferredName: null,
|
|
378
|
+
|
|
379
|
+
multifileAnalysis: null,
|
|
380
|
+
processedModules: [],
|
|
381
|
+
categoryModules: {},
|
|
382
|
+
|
|
383
|
+
subdirectoryDecisions: []
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
if (moduleFiles.length === 1 && subDirs.length === 0) {
|
|
388
|
+
decisions.type = "single-file";
|
|
389
|
+
const moduleFile = moduleFiles[0];
|
|
390
|
+
const moduleExt = path.extname(moduleFile.name);
|
|
391
|
+
const moduleName = instance._toapiPathKey(path.basename(moduleFile.name, moduleExt));
|
|
392
|
+
|
|
393
|
+
decisions.singleFile = {
|
|
394
|
+
file: moduleFile,
|
|
395
|
+
moduleName,
|
|
396
|
+
moduleExt
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
const analysis = await analyzeModule(path.join(categoryPath, moduleFile.name), {
|
|
401
|
+
debug,
|
|
402
|
+
instance
|
|
403
|
+
});
|
|
404
|
+
const mod = processModuleFromAnalysis(analysis, {
|
|
405
|
+
debug,
|
|
406
|
+
instance
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
decisions.singleFile.mod = mod;
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
const functionNameMatchesFolder = typeof mod === "function" && mod.name && mod.name.toLowerCase() === categoryName.toLowerCase();
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
const functionNameMatchesFilename =
|
|
416
|
+
typeof mod === "function" &&
|
|
417
|
+
mod.name &&
|
|
418
|
+
instance._toapiPathKey(mod.name).toLowerCase() === instance._toapiPathKey(moduleName).toLowerCase() &&
|
|
419
|
+
mod.name !== instance._toapiPathKey(moduleName);
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
if (moduleName === categoryName && typeof mod === "function" && currentDepth > 0) {
|
|
424
|
+
decisions.shouldFlatten = true;
|
|
425
|
+
decisions.flattenType = "function-folder-match";
|
|
426
|
+
decisions.preferredName = categoryName;
|
|
427
|
+
return decisions;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
if (analysis.hasDefault && analysis.defaultExportType === "object" && moduleName === categoryName && currentDepth > 0) {
|
|
433
|
+
if (debug) {
|
|
434
|
+
console.log(`[DEBUG] Default export flattening: ${categoryName}/${moduleFile.name} -> flatten default object contents`);
|
|
435
|
+
}
|
|
436
|
+
decisions.shouldFlatten = true;
|
|
437
|
+
decisions.flattenType = "default-export-flatten";
|
|
438
|
+
return decisions;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
if (moduleName === categoryName && mod && typeof mod === "object" && !Array.isArray(mod) && currentDepth > 0) {
|
|
444
|
+
|
|
445
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
446
|
+
if (debug) {
|
|
447
|
+
console.log(
|
|
448
|
+
`[DEBUG] Auto-flatten check: moduleName="${moduleName}" categoryName="${categoryName}" moduleKeys=[${moduleKeys}] match=${moduleKeys.length === 1 && moduleKeys[0] === moduleName}`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
452
|
+
if (debug) {
|
|
453
|
+
console.log(`[DEBUG] Single-file auto-flattening: ${categoryName}/${moduleFile.name} -> flatten object contents`);
|
|
454
|
+
}
|
|
455
|
+
decisions.shouldFlatten = true;
|
|
456
|
+
decisions.flattenType = "object-auto-flatten";
|
|
457
|
+
decisions.preferredName = moduleKeys[0];
|
|
458
|
+
return decisions;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
const fileBaseName = moduleFile.name.replace(/\.(mjs|cjs|js)$/, "");
|
|
464
|
+
if (fileBaseName === categoryName && moduleKeys.length > 0) {
|
|
465
|
+
if (debug) {
|
|
466
|
+
console.log(
|
|
467
|
+
`[DEBUG] Single-file filename-folder exact match flattening: ${categoryName}/${moduleFile.name} -> avoid double nesting`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
decisions.shouldFlatten = true;
|
|
471
|
+
decisions.flattenType = "filename-folder-match-flatten";
|
|
472
|
+
return decisions;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
if (moduleFiles.length === 1 && currentDepth > 0 && mod && typeof mod === "object" && !Array.isArray(mod)) {
|
|
481
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
482
|
+
const fileName = moduleFile.name.replace(/\.(mjs|cjs|js)$/, "");
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
const isGenericFilename = ["singlefile", "index", "main", "default"].includes(fileName.toLowerCase());
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
if (moduleKeys.length === 1 && isGenericFilename) {
|
|
490
|
+
if (debug) {
|
|
491
|
+
console.log(`[DEBUG] Single-file parent-level auto-flattening: ${categoryName}/${moduleFile.name} -> flatten to parent level`);
|
|
492
|
+
}
|
|
493
|
+
decisions.shouldFlatten = true;
|
|
494
|
+
decisions.flattenType = "parent-level-flatten";
|
|
495
|
+
decisions.preferredName = moduleKeys[0];
|
|
496
|
+
return decisions;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
503
|
+
decisions.shouldFlatten = true;
|
|
504
|
+
decisions.flattenType = "function-folder-match";
|
|
505
|
+
decisions.preferredName = mod.name;
|
|
506
|
+
return decisions;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
if (functionNameMatchesFilename) {
|
|
511
|
+
decisions.shouldFlatten = false;
|
|
512
|
+
decisions.preferredName = mod.name;
|
|
513
|
+
return decisions;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
if (
|
|
520
|
+
typeof mod === "function" &&
|
|
521
|
+
(!mod.name || mod.name === "default" || mod.__slothletDefault === true) &&
|
|
522
|
+
currentDepth > 0
|
|
523
|
+
) {
|
|
524
|
+
decisions.shouldFlatten = true;
|
|
525
|
+
decisions.flattenType = "default-function";
|
|
526
|
+
decisions.preferredName = categoryName;
|
|
527
|
+
return decisions;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
532
|
+
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
533
|
+
|
|
534
|
+
decisions.shouldFlatten = true;
|
|
535
|
+
decisions.flattenType = "object-auto-flatten";
|
|
536
|
+
decisions.preferredName = moduleName;
|
|
537
|
+
return decisions;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
decisions.shouldFlatten = false;
|
|
542
|
+
decisions.preferredName = moduleName;
|
|
543
|
+
return decisions;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
decisions.type = "multi-file";
|
|
548
|
+
if (debug) {
|
|
549
|
+
console.log(`[DEBUG] buildCategoryDecisions: Processing multi-file case for ${categoryPath}`);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
const analysis = await multidefault_analyzeModules(moduleFiles, categoryPath, { debug, instance });
|
|
554
|
+
|
|
555
|
+
decisions.multifileAnalysis = analysis;
|
|
556
|
+
|
|
557
|
+
const { totalDefaultExports, hasMultipleDefaultExports, selfReferentialFiles, defaultExportFiles: analysisDefaults } = analysis;
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
const defaultExportFiles = [];
|
|
561
|
+
for (const { fileName } of analysisDefaults) {
|
|
562
|
+
const file = moduleFiles.find((f) => path.basename(f.name, path.extname(f.name)) === fileName);
|
|
563
|
+
if (file) {
|
|
564
|
+
const analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
565
|
+
debug,
|
|
566
|
+
instance
|
|
567
|
+
});
|
|
568
|
+
const processedMod = processModuleFromAnalysis(analysis, {
|
|
569
|
+
debug,
|
|
570
|
+
instance
|
|
571
|
+
});
|
|
572
|
+
defaultExportFiles.push({ file, moduleName: instance._toapiPathKey(fileName), mod: processedMod, analysis });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (debug) {
|
|
577
|
+
console.log(`[DEBUG] buildCategoryDecisions: Using shared multidefault utility results`);
|
|
578
|
+
console.log(`[DEBUG] - totalDefaultExports: ${totalDefaultExports}`);
|
|
579
|
+
console.log(`[DEBUG] - hasMultipleDefaultExports: ${hasMultipleDefaultExports}`);
|
|
580
|
+
console.log(`[DEBUG] - selfReferentialFiles: ${Array.from(selfReferentialFiles)}`);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
for (const file of moduleFiles) {
|
|
585
|
+
const moduleExt = path.extname(file.name);
|
|
586
|
+
const moduleName = instance._toapiPathKey(path.basename(file.name, moduleExt));
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
let mod = null;
|
|
590
|
+
let analysis = null;
|
|
591
|
+
const existingDefault = defaultExportFiles.find((def) => def.moduleName === moduleName);
|
|
592
|
+
if (existingDefault) {
|
|
593
|
+
mod = existingDefault.mod;
|
|
594
|
+
|
|
595
|
+
analysis = existingDefault.analysis;
|
|
596
|
+
} else {
|
|
597
|
+
|
|
598
|
+
analysis = await analyzeModule(path.join(categoryPath, file.name), {
|
|
599
|
+
debug,
|
|
600
|
+
instance
|
|
601
|
+
});
|
|
602
|
+
mod = processModuleFromAnalysis(analysis, {
|
|
603
|
+
debug,
|
|
604
|
+
instance
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const moduleDecision = {
|
|
609
|
+
file,
|
|
610
|
+
moduleName,
|
|
611
|
+
mod,
|
|
612
|
+
type: null,
|
|
613
|
+
apiPathKey: null,
|
|
614
|
+
shouldFlatten: false,
|
|
615
|
+
flattenType: null,
|
|
616
|
+
specialHandling: null
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
if (moduleName === categoryName && mod && typeof mod === "object") {
|
|
620
|
+
moduleDecision.type = "category-match-object";
|
|
621
|
+
moduleDecision.specialHandling = "category-merge";
|
|
622
|
+
} else if (typeof mod === "function") {
|
|
623
|
+
moduleDecision.type = "function";
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
const isSelfReferential = selfReferentialFiles.has(moduleName);
|
|
627
|
+
|
|
628
|
+
if (hasMultipleDefaultExports && mod.__slothletDefault === true && !isSelfReferential) {
|
|
629
|
+
|
|
630
|
+
moduleDecision.apiPathKey = moduleName;
|
|
631
|
+
moduleDecision.specialHandling = "multi-default-filename";
|
|
632
|
+
if (debug) {
|
|
633
|
+
console.log(
|
|
634
|
+
`[DEBUG] Multi-default function case: ${moduleName} => ${moduleDecision.apiPathKey} (hasMultiple=${hasMultipleDefaultExports}, __slothletDefault=${mod.__slothletDefault}, isSelfRef=${isSelfReferential})`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
} else if (selfReferentialFiles.has(moduleName)) {
|
|
638
|
+
|
|
639
|
+
moduleDecision.type = "self-referential";
|
|
640
|
+
moduleDecision.specialHandling = "self-referential-namespace";
|
|
641
|
+
} else {
|
|
642
|
+
|
|
643
|
+
const fnName = mod.name && mod.name !== "default" ? mod.name : moduleName;
|
|
644
|
+
if (debug) {
|
|
645
|
+
console.log(
|
|
646
|
+
`[DEBUG] Standard function case: ${moduleName}, fnName=${fnName}, mod.__slothletDefault=${mod.__slothletDefault}, hasMultiple=${hasMultipleDefaultExports}`
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
if (fnName && fnName.toLowerCase() === moduleName.toLowerCase() && fnName !== moduleName) {
|
|
653
|
+
|
|
654
|
+
moduleDecision.apiPathKey = fnName;
|
|
655
|
+
moduleDecision.specialHandling = "prefer-function-name";
|
|
656
|
+
} else {
|
|
657
|
+
|
|
658
|
+
moduleDecision.apiPathKey = instance._toapiPathKey(fnName);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
} else {
|
|
662
|
+
moduleDecision.type = "object";
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
let hasPreferredName = false;
|
|
666
|
+
const modWithPreferredNames = {};
|
|
667
|
+
|
|
668
|
+
for (const [exportName, exportValue] of Object.entries(mod)) {
|
|
669
|
+
if (
|
|
670
|
+
typeof exportValue === "function" &&
|
|
671
|
+
exportValue.name &&
|
|
672
|
+
instance._toapiPathKey(exportValue.name).toLowerCase() === instance._toapiPathKey(moduleName).toLowerCase() &&
|
|
673
|
+
exportValue.name !== instance._toapiPathKey(moduleName)
|
|
674
|
+
) {
|
|
675
|
+
|
|
676
|
+
modWithPreferredNames[exportValue.name] = exportValue;
|
|
677
|
+
hasPreferredName = true;
|
|
678
|
+
} else {
|
|
679
|
+
modWithPreferredNames[instance._toapiPathKey(exportName)] = exportValue;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (hasPreferredName) {
|
|
684
|
+
moduleDecision.specialHandling = "preferred-export-names";
|
|
685
|
+
moduleDecision.processedExports = modWithPreferredNames;
|
|
686
|
+
} else if (selfReferentialFiles.has(moduleName)) {
|
|
687
|
+
|
|
688
|
+
moduleDecision.type = "self-referential";
|
|
689
|
+
moduleDecision.specialHandling = "self-referential-namespace";
|
|
690
|
+
} else {
|
|
691
|
+
|
|
692
|
+
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
693
|
+
const apiPathKey = instance._toapiPathKey(moduleName);
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
if (!hasMultipleDefaultExports && analysis.hasDefault && analysis.defaultExportType === "object") {
|
|
698
|
+
moduleDecision.shouldFlatten = true;
|
|
699
|
+
moduleDecision.flattenType = "single-default-object";
|
|
700
|
+
moduleDecision.apiPathKey = apiPathKey;
|
|
701
|
+
} else if (hasMultipleDefaultExports && !analysis.hasDefault && moduleKeys.length > 0) {
|
|
702
|
+
|
|
703
|
+
moduleDecision.shouldFlatten = true;
|
|
704
|
+
moduleDecision.flattenType = "multi-default-no-default";
|
|
705
|
+
} else if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
706
|
+
|
|
707
|
+
moduleDecision.shouldFlatten = true;
|
|
708
|
+
moduleDecision.flattenType = "single-named-export-match";
|
|
709
|
+
moduleDecision.apiPathKey = apiPathKey;
|
|
710
|
+
} else if (!analysis.hasDefault && moduleKeys.length > 0 && moduleName === categoryName) {
|
|
711
|
+
|
|
712
|
+
moduleDecision.shouldFlatten = true;
|
|
713
|
+
moduleDecision.flattenType = "category-name-match-flatten";
|
|
714
|
+
} else {
|
|
715
|
+
|
|
716
|
+
moduleDecision.apiPathKey = apiPathKey;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
decisions.processedModules.push(moduleDecision);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
for (const subDir of subDirs) {
|
|
726
|
+
const subDirPath = path.join(categoryPath, subDir.name);
|
|
727
|
+
const subDirDecision = {
|
|
728
|
+
name: subDir.name,
|
|
729
|
+
path: subDirPath,
|
|
730
|
+
apiPathKey: instance._toapiPathKey(subDir.name),
|
|
731
|
+
shouldRecurse: currentDepth < maxDepth
|
|
732
|
+
};
|
|
733
|
+
decisions.subdirectoryDecisions.push(subDirDecision);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return decisions;
|
|
737
|
+
}
|