@kubb/core 5.0.0-alpha.29 → 5.0.0-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PluginDriver-C6VX0skO.d.ts → PluginDriver-D110FoJ-.d.ts} +265 -108
- package/dist/hooks.d.ts +1 -1
- package/dist/index.cjs +495 -529
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -80
- package/dist/index.js +496 -525
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/PluginDriver.ts +44 -10
- package/src/build.ts +87 -8
- package/src/createPlugin.ts +3 -1
- package/src/defineGenerator.ts +101 -134
- package/src/index.ts +1 -2
- package/src/renderNode.tsx +14 -186
- package/src/types.ts +213 -11
- package/src/utils/executeStrategies.ts +7 -1
package/dist/index.cjs
CHANGED
|
@@ -15,8 +15,8 @@ let _kubb_react_fabric_plugins = require("@kubb/react-fabric/plugins");
|
|
|
15
15
|
let node_perf_hooks = require("node:perf_hooks");
|
|
16
16
|
let fflate = require("fflate");
|
|
17
17
|
let tinyexec = require("tinyexec");
|
|
18
|
-
let node_process = require("node:process");
|
|
19
18
|
let _kubb_react_fabric_jsx_runtime = require("@kubb/react-fabric/jsx-runtime");
|
|
19
|
+
let node_process = require("node:process");
|
|
20
20
|
let remeda = require("remeda");
|
|
21
21
|
let empathic_package = require("empathic/package");
|
|
22
22
|
empathic_package = require_chunk.__toESM(empathic_package);
|
|
@@ -901,6 +901,7 @@ function validateConcurrency(concurrency) {
|
|
|
901
901
|
* - Each function receives the accumulated state from the previous call.
|
|
902
902
|
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
903
903
|
* - Returns an array of all individual results.
|
|
904
|
+
* @deprecated
|
|
904
905
|
*/
|
|
905
906
|
function hookSeq(promises) {
|
|
906
907
|
return promises.filter(Boolean).reduce((promise, func) => {
|
|
@@ -917,6 +918,7 @@ function hookSeq(promises) {
|
|
|
917
918
|
*
|
|
918
919
|
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
919
920
|
* - Subsequent functions are skipped once a match is found.
|
|
921
|
+
* @deprecated
|
|
920
922
|
*/
|
|
921
923
|
function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
922
924
|
let promise = Promise.resolve(null);
|
|
@@ -931,6 +933,7 @@ function hookFirst(promises, nullCheck = (state) => state !== null) {
|
|
|
931
933
|
*
|
|
932
934
|
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
933
935
|
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
936
|
+
* @deprecated
|
|
934
937
|
*/
|
|
935
938
|
function hookParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
936
939
|
const limit = pLimit(concurrency);
|
|
@@ -967,7 +970,13 @@ var PluginDriver = class {
|
|
|
967
970
|
constructor(config, options) {
|
|
968
971
|
this.config = config;
|
|
969
972
|
this.options = options;
|
|
970
|
-
config.plugins.map((plugin) => Object.assign({
|
|
973
|
+
config.plugins.map((plugin) => Object.assign({
|
|
974
|
+
buildStart() {},
|
|
975
|
+
buildEnd() {}
|
|
976
|
+
}, plugin)).filter((plugin) => {
|
|
977
|
+
if (typeof plugin.apply === "function") return plugin.apply(config);
|
|
978
|
+
return true;
|
|
979
|
+
}).sort((a, b) => {
|
|
971
980
|
if (b.pre?.includes(a.name)) return 1;
|
|
972
981
|
if (b.post?.includes(a.name)) return -1;
|
|
973
982
|
return 0;
|
|
@@ -983,9 +992,16 @@ var PluginDriver = class {
|
|
|
983
992
|
const baseContext = {
|
|
984
993
|
fabric: driver.options.fabric,
|
|
985
994
|
config: driver.config,
|
|
995
|
+
get root() {
|
|
996
|
+
return (0, node_path.resolve)(driver.config.root, driver.config.output.path);
|
|
997
|
+
},
|
|
998
|
+
getMode(output) {
|
|
999
|
+
return getMode((0, node_path.resolve)(driver.config.root, driver.config.output.path, output.path));
|
|
1000
|
+
},
|
|
1001
|
+
events: driver.options.events,
|
|
986
1002
|
plugin,
|
|
987
1003
|
getPlugin: driver.getPlugin.bind(driver),
|
|
988
|
-
|
|
1004
|
+
requirePlugin: driver.requirePlugin.bind(driver),
|
|
989
1005
|
driver,
|
|
990
1006
|
addFile: async (...files) => {
|
|
991
1007
|
await this.options.fabric.addFile(...files);
|
|
@@ -1005,6 +1021,15 @@ var PluginDriver = class {
|
|
|
1005
1021
|
get transformer() {
|
|
1006
1022
|
return plugin.transformer;
|
|
1007
1023
|
},
|
|
1024
|
+
warn(message) {
|
|
1025
|
+
driver.events.emit("warn", message);
|
|
1026
|
+
},
|
|
1027
|
+
error(error) {
|
|
1028
|
+
driver.events.emit("error", typeof error === "string" ? new Error(error) : error);
|
|
1029
|
+
},
|
|
1030
|
+
info(message) {
|
|
1031
|
+
driver.events.emit("info", message);
|
|
1032
|
+
},
|
|
1008
1033
|
openInStudio(options) {
|
|
1009
1034
|
if (!driver.config.devtools || driver.#studioIsOpen) return;
|
|
1010
1035
|
if (typeof driver.config.devtools !== "object") throw new Error("Devtools must be an object");
|
|
@@ -1015,8 +1040,8 @@ var PluginDriver = class {
|
|
|
1015
1040
|
}
|
|
1016
1041
|
};
|
|
1017
1042
|
const mergedExtras = {};
|
|
1018
|
-
for (const
|
|
1019
|
-
const result =
|
|
1043
|
+
for (const p of this.plugins.values()) if (typeof p.inject === "function") {
|
|
1044
|
+
const result = p.inject.call(baseContext);
|
|
1020
1045
|
if (result !== null && typeof result === "object") Object.assign(mergedExtras, result);
|
|
1021
1046
|
}
|
|
1022
1047
|
return {
|
|
@@ -1234,6 +1259,11 @@ var PluginDriver = class {
|
|
|
1234
1259
|
getPlugin(pluginName) {
|
|
1235
1260
|
return this.plugins.get(pluginName);
|
|
1236
1261
|
}
|
|
1262
|
+
requirePlugin(pluginName) {
|
|
1263
|
+
const plugin = this.plugins.get(pluginName);
|
|
1264
|
+
if (!plugin) throw new Error(`[kubb] Plugin "${pluginName}" is required but not found. Make sure it is included in your Kubb config.`);
|
|
1265
|
+
return plugin;
|
|
1266
|
+
}
|
|
1237
1267
|
/**
|
|
1238
1268
|
* Run an async plugin hook and return the result.
|
|
1239
1269
|
* @param hookName Name of the plugin hook. Must be either in `PluginHooks` or `OutputPluginValueHooks`.
|
|
@@ -1322,6 +1352,26 @@ var PluginDriver = class {
|
|
|
1322
1352
|
}
|
|
1323
1353
|
};
|
|
1324
1354
|
//#endregion
|
|
1355
|
+
//#region src/renderNode.tsx
|
|
1356
|
+
/**
|
|
1357
|
+
* Handles the return value of a plugin AST hook or generator method.
|
|
1358
|
+
*
|
|
1359
|
+
* - React element → rendered via an isolated react-fabric context, files merged into `fabric`
|
|
1360
|
+
* - `Array<FabricFile.File>` → upserted directly into `fabric`
|
|
1361
|
+
* - `void` / `null` / `undefined` → no-op (plugin handled it via `this.upsertFile`)
|
|
1362
|
+
*/
|
|
1363
|
+
async function applyHookResult(result, fabric) {
|
|
1364
|
+
if (!result) return;
|
|
1365
|
+
if (Array.isArray(result)) {
|
|
1366
|
+
await fabric.upsertFile(...result);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
1370
|
+
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, { children: result }));
|
|
1371
|
+
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
1372
|
+
fabricChild.unmount();
|
|
1373
|
+
}
|
|
1374
|
+
//#endregion
|
|
1325
1375
|
//#region src/createStorage.ts
|
|
1326
1376
|
/**
|
|
1327
1377
|
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
@@ -1419,7 +1469,7 @@ const fsStorage = createStorage(() => ({
|
|
|
1419
1469
|
}));
|
|
1420
1470
|
//#endregion
|
|
1421
1471
|
//#region package.json
|
|
1422
|
-
var version = "5.0.0-alpha.
|
|
1472
|
+
var version = "5.0.0-alpha.30";
|
|
1423
1473
|
//#endregion
|
|
1424
1474
|
//#region src/utils/diagnostics.ts
|
|
1425
1475
|
/**
|
|
@@ -1438,103 +1488,338 @@ function getDiagnosticInfo() {
|
|
|
1438
1488
|
};
|
|
1439
1489
|
}
|
|
1440
1490
|
//#endregion
|
|
1441
|
-
//#region src/
|
|
1491
|
+
//#region src/utils/TreeNode.ts
|
|
1442
1492
|
/**
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
1445
|
-
* - Validates the input path (when applicable).
|
|
1446
|
-
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1447
|
-
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
1448
|
-
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
1493
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
1494
|
+
* flat list of generated {@link FabricFile.File} entries.
|
|
1449
1495
|
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1496
|
+
* Each node represents either a directory or a file within the output tree.
|
|
1497
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
1498
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
1499
|
+
* `*Deep` helpers.
|
|
1452
1500
|
*/
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1501
|
+
var TreeNode = class TreeNode {
|
|
1502
|
+
data;
|
|
1503
|
+
parent;
|
|
1504
|
+
children = [];
|
|
1505
|
+
#cachedLeaves = void 0;
|
|
1506
|
+
constructor(data, parent) {
|
|
1507
|
+
this.data = data;
|
|
1508
|
+
this.parent = parent;
|
|
1509
|
+
}
|
|
1510
|
+
addChild(data) {
|
|
1511
|
+
const child = new TreeNode(data, this);
|
|
1512
|
+
if (!this.children) this.children = [];
|
|
1513
|
+
this.children.push(child);
|
|
1514
|
+
return child;
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
1518
|
+
*/
|
|
1519
|
+
get root() {
|
|
1520
|
+
if (!this.parent) return this;
|
|
1521
|
+
return this.parent.root;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
1525
|
+
*
|
|
1526
|
+
* Results are cached after the first traversal.
|
|
1527
|
+
*/
|
|
1528
|
+
get leaves() {
|
|
1529
|
+
if (!this.children || this.children.length === 0) return [this];
|
|
1530
|
+
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
1531
|
+
const leaves = [];
|
|
1532
|
+
for (const child of this.children) leaves.push(...child.leaves);
|
|
1533
|
+
this.#cachedLeaves = leaves;
|
|
1534
|
+
return leaves;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Visits this node and every descendant in depth-first order.
|
|
1538
|
+
*/
|
|
1539
|
+
forEach(callback) {
|
|
1540
|
+
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
1541
|
+
callback(this);
|
|
1542
|
+
for (const child of this.children) child.forEach(callback);
|
|
1543
|
+
return this;
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
|
|
1547
|
+
*/
|
|
1548
|
+
findDeep(predicate) {
|
|
1549
|
+
if (typeof predicate !== "function") throw new TypeError("find() predicate must be a function");
|
|
1550
|
+
return this.leaves.find(predicate);
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* Calls `callback` for every leaf of this node.
|
|
1554
|
+
*/
|
|
1555
|
+
forEachDeep(callback) {
|
|
1556
|
+
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
1557
|
+
this.leaves.forEach(callback);
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Returns all leaves that satisfy `callback`.
|
|
1561
|
+
*/
|
|
1562
|
+
filterDeep(callback) {
|
|
1563
|
+
if (typeof callback !== "function") throw new TypeError("filter() callback must be a function");
|
|
1564
|
+
return this.leaves.filter(callback);
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Maps every leaf through `callback` and returns the resulting array.
|
|
1568
|
+
*/
|
|
1569
|
+
mapDeep(callback) {
|
|
1570
|
+
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
1571
|
+
return this.leaves.map(callback);
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
1575
|
+
*
|
|
1576
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
1577
|
+
* - Returns `null` when no files match.
|
|
1578
|
+
*/
|
|
1579
|
+
static build(files, root) {
|
|
1580
|
+
try {
|
|
1581
|
+
const filteredTree = buildDirectoryTree(files, root);
|
|
1582
|
+
if (!filteredTree) return null;
|
|
1583
|
+
const treeNode = new TreeNode({
|
|
1584
|
+
name: filteredTree.name,
|
|
1585
|
+
path: filteredTree.path,
|
|
1586
|
+
file: filteredTree.file,
|
|
1587
|
+
type: getMode(filteredTree.path)
|
|
1480
1588
|
});
|
|
1589
|
+
const recurse = (node, item) => {
|
|
1590
|
+
const subNode = node.addChild({
|
|
1591
|
+
name: item.name,
|
|
1592
|
+
path: item.path,
|
|
1593
|
+
file: item.file,
|
|
1594
|
+
type: getMode(item.path)
|
|
1595
|
+
});
|
|
1596
|
+
if (item.children?.length) item.children?.forEach((child) => {
|
|
1597
|
+
recurse(subNode, child);
|
|
1598
|
+
});
|
|
1599
|
+
};
|
|
1600
|
+
filteredTree.children?.forEach((child) => {
|
|
1601
|
+
recurse(treeNode, child);
|
|
1602
|
+
});
|
|
1603
|
+
return treeNode;
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
throw new Error("Something went wrong with creating barrel files with the TreeNode class", { cause: error });
|
|
1481
1606
|
}
|
|
1482
|
-
} catch (caughtError) {
|
|
1483
|
-
if (isInputPath(userConfig)) {
|
|
1484
|
-
const error = caughtError;
|
|
1485
|
-
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
const definedConfig = {
|
|
1489
|
-
root: userConfig.root || process.cwd(),
|
|
1490
|
-
...userConfig,
|
|
1491
|
-
output: {
|
|
1492
|
-
write: true,
|
|
1493
|
-
barrelType: "named",
|
|
1494
|
-
extension: DEFAULT_EXTENSION,
|
|
1495
|
-
defaultBanner: DEFAULT_BANNER,
|
|
1496
|
-
...userConfig.output
|
|
1497
|
-
},
|
|
1498
|
-
devtools: userConfig.devtools ? {
|
|
1499
|
-
studioUrl: DEFAULT_STUDIO_URL,
|
|
1500
|
-
...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
|
|
1501
|
-
} : void 0,
|
|
1502
|
-
plugins: userConfig.plugins
|
|
1503
|
-
};
|
|
1504
|
-
const storage = definedConfig.output.write === false ? null : definedConfig.output.storage ?? fsStorage();
|
|
1505
|
-
if (definedConfig.output.clean) {
|
|
1506
|
-
await events.emit("debug", {
|
|
1507
|
-
date: /* @__PURE__ */ new Date(),
|
|
1508
|
-
logs: ["Cleaning output directories", ` • Output: ${definedConfig.output.path}`]
|
|
1509
|
-
});
|
|
1510
|
-
await storage?.clear((0, node_path.resolve)(definedConfig.root, definedConfig.output.path));
|
|
1511
1607
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
});
|
|
1608
|
+
};
|
|
1609
|
+
const normalizePath = (p) => p.replaceAll("\\", "/");
|
|
1610
|
+
function buildDirectoryTree(files, rootFolder = "") {
|
|
1611
|
+
const normalizedRootFolder = normalizePath(rootFolder);
|
|
1612
|
+
const rootPrefix = normalizedRootFolder.endsWith("/") ? normalizedRootFolder : `${normalizedRootFolder}/`;
|
|
1613
|
+
const filteredFiles = files.filter((file) => {
|
|
1614
|
+
const normalizedFilePath = normalizePath(file.path);
|
|
1615
|
+
return rootFolder ? normalizedFilePath.startsWith(rootPrefix) && !normalizedFilePath.endsWith(".json") : !normalizedFilePath.endsWith(".json");
|
|
1521
1616
|
});
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1617
|
+
if (filteredFiles.length === 0) return null;
|
|
1618
|
+
const root = {
|
|
1619
|
+
name: rootFolder || "",
|
|
1620
|
+
path: rootFolder || "",
|
|
1621
|
+
children: []
|
|
1622
|
+
};
|
|
1623
|
+
filteredFiles.forEach((file) => {
|
|
1624
|
+
const parts = file.path.slice(rootFolder.length).split("/").filter(Boolean);
|
|
1625
|
+
let currentLevel = root.children;
|
|
1626
|
+
let currentPath = normalizePath(rootFolder);
|
|
1627
|
+
parts.forEach((part, index) => {
|
|
1628
|
+
currentPath = node_path.default.posix.join(currentPath, part);
|
|
1629
|
+
let existingNode = currentLevel.find((node) => node.name === part);
|
|
1630
|
+
if (!existingNode) {
|
|
1631
|
+
if (index === parts.length - 1) existingNode = {
|
|
1632
|
+
name: part,
|
|
1633
|
+
file,
|
|
1634
|
+
path: currentPath
|
|
1635
|
+
};
|
|
1636
|
+
else existingNode = {
|
|
1637
|
+
name: part,
|
|
1638
|
+
path: currentPath,
|
|
1639
|
+
children: []
|
|
1640
|
+
};
|
|
1641
|
+
currentLevel.push(existingNode);
|
|
1642
|
+
}
|
|
1643
|
+
if (!existingNode.file) currentLevel = existingNode.children;
|
|
1528
1644
|
});
|
|
1529
|
-
if (source) {
|
|
1530
|
-
const key = (0, node_path.relative)((0, node_path.resolve)(definedConfig.root), file.path);
|
|
1531
|
-
await storage?.setItem(key, source);
|
|
1532
|
-
sources.set(file.path, source);
|
|
1533
|
-
}
|
|
1534
1645
|
});
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1646
|
+
return root;
|
|
1647
|
+
}
|
|
1648
|
+
//#endregion
|
|
1649
|
+
//#region src/utils/getBarrelFiles.ts
|
|
1650
|
+
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
1651
|
+
function getBarrelFilesByRoot(root, files) {
|
|
1652
|
+
const cachedFiles = /* @__PURE__ */ new Map();
|
|
1653
|
+
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
1654
|
+
if (!treeNode?.children || !treeNode.parent?.data.path) return;
|
|
1655
|
+
const barrelFile = {
|
|
1656
|
+
path: (0, node_path.join)(treeNode.parent?.data.path, "index.ts"),
|
|
1657
|
+
baseName: "index.ts",
|
|
1658
|
+
exports: [],
|
|
1659
|
+
imports: [],
|
|
1660
|
+
sources: []
|
|
1661
|
+
};
|
|
1662
|
+
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
1663
|
+
treeNode.leaves.forEach((item) => {
|
|
1664
|
+
if (!item.data.name) return;
|
|
1665
|
+
(item.data.file?.sources || []).forEach((source) => {
|
|
1666
|
+
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
1667
|
+
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
1668
|
+
barrelFile.exports.push({
|
|
1669
|
+
name: [source.name],
|
|
1670
|
+
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
1671
|
+
isTypeOnly: source.isTypeOnly
|
|
1672
|
+
});
|
|
1673
|
+
barrelFile.sources.push({
|
|
1674
|
+
name: source.name,
|
|
1675
|
+
isTypeOnly: source.isTypeOnly,
|
|
1676
|
+
value: "",
|
|
1677
|
+
isExportable: false,
|
|
1678
|
+
isIndexable: false
|
|
1679
|
+
});
|
|
1680
|
+
});
|
|
1681
|
+
});
|
|
1682
|
+
if (previousBarrelFile) {
|
|
1683
|
+
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
1684
|
+
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
1685
|
+
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
1686
|
+
});
|
|
1687
|
+
return [...cachedFiles.values()];
|
|
1688
|
+
}
|
|
1689
|
+
function trimExtName(text) {
|
|
1690
|
+
const dotIndex = text.lastIndexOf(".");
|
|
1691
|
+
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
1692
|
+
return text;
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
1696
|
+
*
|
|
1697
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
1698
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
1699
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
1700
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
1701
|
+
*/
|
|
1702
|
+
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
1703
|
+
if (!type || type === "propagate") return [];
|
|
1704
|
+
const pathToBuildFrom = (0, node_path.join)(root, output.path);
|
|
1705
|
+
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
1706
|
+
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
|
|
1707
|
+
if (type === "all") return barrelFiles.map((file) => {
|
|
1708
|
+
return {
|
|
1709
|
+
...file,
|
|
1710
|
+
exports: file.exports?.map((exportItem) => {
|
|
1711
|
+
return {
|
|
1712
|
+
...exportItem,
|
|
1713
|
+
name: void 0
|
|
1714
|
+
};
|
|
1715
|
+
})
|
|
1716
|
+
};
|
|
1717
|
+
});
|
|
1718
|
+
return barrelFiles.map((indexFile) => {
|
|
1719
|
+
return {
|
|
1720
|
+
...indexFile,
|
|
1721
|
+
meta
|
|
1722
|
+
};
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
//#endregion
|
|
1726
|
+
//#region src/build.ts
|
|
1727
|
+
/**
|
|
1728
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
1729
|
+
*
|
|
1730
|
+
* - Validates the input path (when applicable).
|
|
1731
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
1732
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
1733
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
1734
|
+
*
|
|
1735
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
1736
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
1737
|
+
*/
|
|
1738
|
+
async function setup(options) {
|
|
1739
|
+
const { config: userConfig, events = new AsyncEventEmitter() } = options;
|
|
1740
|
+
const sources = /* @__PURE__ */ new Map();
|
|
1741
|
+
const diagnosticInfo = getDiagnosticInfo();
|
|
1742
|
+
if (Array.isArray(userConfig.input)) await events.emit("warn", "This feature is still under development — use with caution");
|
|
1743
|
+
await events.emit("debug", {
|
|
1744
|
+
date: /* @__PURE__ */ new Date(),
|
|
1745
|
+
logs: [
|
|
1746
|
+
"Configuration:",
|
|
1747
|
+
` • Name: ${userConfig.name || "unnamed"}`,
|
|
1748
|
+
` • Root: ${userConfig.root || process.cwd()}`,
|
|
1749
|
+
` • Output: ${userConfig.output?.path || "not specified"}`,
|
|
1750
|
+
` • Plugins: ${userConfig.plugins?.length || 0}`,
|
|
1751
|
+
"Output Settings:",
|
|
1752
|
+
` • Storage: ${userConfig.output?.storage ? `custom(${userConfig.output.storage.name})` : userConfig.output?.write === false ? "disabled" : "filesystem (default)"}`,
|
|
1753
|
+
` • Formatter: ${userConfig.output?.format || "none"}`,
|
|
1754
|
+
` • Linter: ${userConfig.output?.lint || "none"}`,
|
|
1755
|
+
"Environment:",
|
|
1756
|
+
Object.entries(diagnosticInfo).map(([key, value]) => ` • ${key}: ${value}`).join("\n")
|
|
1757
|
+
]
|
|
1758
|
+
});
|
|
1759
|
+
try {
|
|
1760
|
+
if (isInputPath(userConfig) && !new URLPath(userConfig.input.path).isURL) {
|
|
1761
|
+
await exists(userConfig.input.path);
|
|
1762
|
+
await events.emit("debug", {
|
|
1763
|
+
date: /* @__PURE__ */ new Date(),
|
|
1764
|
+
logs: [`✓ Input file validated: ${userConfig.input.path}`]
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
} catch (caughtError) {
|
|
1768
|
+
if (isInputPath(userConfig)) {
|
|
1769
|
+
const error = caughtError;
|
|
1770
|
+
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
const definedConfig = {
|
|
1774
|
+
root: userConfig.root || process.cwd(),
|
|
1775
|
+
...userConfig,
|
|
1776
|
+
output: {
|
|
1777
|
+
write: true,
|
|
1778
|
+
barrelType: "named",
|
|
1779
|
+
extension: DEFAULT_EXTENSION,
|
|
1780
|
+
defaultBanner: DEFAULT_BANNER,
|
|
1781
|
+
...userConfig.output
|
|
1782
|
+
},
|
|
1783
|
+
devtools: userConfig.devtools ? {
|
|
1784
|
+
studioUrl: DEFAULT_STUDIO_URL,
|
|
1785
|
+
...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
|
|
1786
|
+
} : void 0,
|
|
1787
|
+
plugins: userConfig.plugins
|
|
1788
|
+
};
|
|
1789
|
+
const storage = definedConfig.output.write === false ? null : definedConfig.output.storage ?? fsStorage();
|
|
1790
|
+
if (definedConfig.output.clean) {
|
|
1791
|
+
await events.emit("debug", {
|
|
1792
|
+
date: /* @__PURE__ */ new Date(),
|
|
1793
|
+
logs: ["Cleaning output directories", ` • Output: ${definedConfig.output.path}`]
|
|
1794
|
+
});
|
|
1795
|
+
await storage?.clear((0, node_path.resolve)(definedConfig.root, definedConfig.output.path));
|
|
1796
|
+
}
|
|
1797
|
+
const fabric = (0, _kubb_react_fabric.createFabric)();
|
|
1798
|
+
fabric.use(_kubb_react_fabric_plugins.fsPlugin);
|
|
1799
|
+
fabric.use(_kubb_react_fabric_parsers.typescriptParser);
|
|
1800
|
+
fabric.context.on("files:processing:start", (files) => {
|
|
1801
|
+
events.emit("files:processing:start", files);
|
|
1802
|
+
events.emit("debug", {
|
|
1803
|
+
date: /* @__PURE__ */ new Date(),
|
|
1804
|
+
logs: [`Writing ${files.length} files...`]
|
|
1805
|
+
});
|
|
1806
|
+
});
|
|
1807
|
+
fabric.context.on("file:processing:update", async (params) => {
|
|
1808
|
+
const { file, source } = params;
|
|
1809
|
+
await events.emit("file:processing:update", {
|
|
1810
|
+
...params,
|
|
1811
|
+
config: definedConfig,
|
|
1812
|
+
source
|
|
1813
|
+
});
|
|
1814
|
+
if (source) {
|
|
1815
|
+
const key = (0, node_path.relative)((0, node_path.resolve)(definedConfig.root), file.path);
|
|
1816
|
+
await storage?.setItem(key, source);
|
|
1817
|
+
sources.set(file.path, source);
|
|
1818
|
+
}
|
|
1819
|
+
});
|
|
1820
|
+
fabric.context.on("files:processing:end", async (files) => {
|
|
1821
|
+
await events.emit("files:processing:end", files);
|
|
1822
|
+
await events.emit("debug", {
|
|
1538
1823
|
date: /* @__PURE__ */ new Date(),
|
|
1539
1824
|
logs: [`✓ File write process completed for ${files.length} files`]
|
|
1540
1825
|
});
|
|
@@ -1600,6 +1885,51 @@ async function build(options, overrides) {
|
|
|
1600
1885
|
};
|
|
1601
1886
|
}
|
|
1602
1887
|
/**
|
|
1888
|
+
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
1889
|
+
* (`schema`, `operation`, `operations`).
|
|
1890
|
+
*
|
|
1891
|
+
* - Each hook accepts a single handler **or an array** — all entries are called in sequence.
|
|
1892
|
+
* - Nodes that are excluded by `exclude`/`include` plugin options are skipped automatically.
|
|
1893
|
+
* - Return values are handled via `applyHookResult`: React elements are rendered,
|
|
1894
|
+
* `FabricFile.File[]` are written via upsert, and `void` is a no-op (manual handling).
|
|
1895
|
+
* - Barrel files are generated automatically when `output.barrelType` is set.
|
|
1896
|
+
*/
|
|
1897
|
+
async function runPluginAstHooks(plugin, context) {
|
|
1898
|
+
const { adapter, rootNode, resolver, fabric } = context;
|
|
1899
|
+
const { exclude, include, override } = plugin.options;
|
|
1900
|
+
if (!adapter || !rootNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`);
|
|
1901
|
+
const collectedOperations = [];
|
|
1902
|
+
await (0, _kubb_ast.walk)(rootNode, {
|
|
1903
|
+
depth: "shallow",
|
|
1904
|
+
async schema(node) {
|
|
1905
|
+
if (!plugin.schema) return;
|
|
1906
|
+
const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
|
|
1907
|
+
const options = resolver.resolveOptions(transformedNode, {
|
|
1908
|
+
options: plugin.options,
|
|
1909
|
+
exclude,
|
|
1910
|
+
include,
|
|
1911
|
+
override
|
|
1912
|
+
});
|
|
1913
|
+
if (options === null) return;
|
|
1914
|
+
await applyHookResult(await plugin.schema.call(context, transformedNode, options), fabric);
|
|
1915
|
+
},
|
|
1916
|
+
async operation(node) {
|
|
1917
|
+
const transformedNode = plugin.transformer ? (0, _kubb_ast.transform)(node, plugin.transformer) : node;
|
|
1918
|
+
const options = resolver.resolveOptions(transformedNode, {
|
|
1919
|
+
options: plugin.options,
|
|
1920
|
+
exclude,
|
|
1921
|
+
include,
|
|
1922
|
+
override
|
|
1923
|
+
});
|
|
1924
|
+
if (options !== null) {
|
|
1925
|
+
collectedOperations.push(transformedNode);
|
|
1926
|
+
if (plugin.operation) await applyHookResult(await plugin.operation.call(context, transformedNode, options), fabric);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
if (plugin.operations && collectedOperations.length > 0) await applyHookResult(await plugin.operations.call(context, collectedOperations, plugin.options), fabric);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1603
1933
|
* Runs a full Kubb build and captures errors instead of throwing.
|
|
1604
1934
|
*
|
|
1605
1935
|
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
@@ -1618,15 +1948,26 @@ async function safeBuild(options, overrides) {
|
|
|
1618
1948
|
for (const plugin of driver.plugins.values()) {
|
|
1619
1949
|
const context = driver.getContext(plugin);
|
|
1620
1950
|
const hrStart = process.hrtime();
|
|
1621
|
-
const
|
|
1951
|
+
const { output } = plugin.options ?? {};
|
|
1952
|
+
const root = (0, node_path.resolve)(config.root, config.output.path);
|
|
1622
1953
|
try {
|
|
1623
1954
|
const timestamp = /* @__PURE__ */ new Date();
|
|
1624
1955
|
await events.emit("plugin:start", plugin);
|
|
1625
1956
|
await events.emit("debug", {
|
|
1626
1957
|
date: timestamp,
|
|
1627
|
-
logs: ["
|
|
1958
|
+
logs: ["Starting plugin...", ` • Plugin Name: ${plugin.name}`]
|
|
1628
1959
|
});
|
|
1629
|
-
await
|
|
1960
|
+
await plugin.buildStart.call(context);
|
|
1961
|
+
if (plugin.schema || plugin.operation || plugin.operations) await runPluginAstHooks(plugin, context);
|
|
1962
|
+
if (output) {
|
|
1963
|
+
const barrelFiles = await getBarrelFiles(fabric.files, {
|
|
1964
|
+
type: output.barrelType ?? "named",
|
|
1965
|
+
root,
|
|
1966
|
+
output,
|
|
1967
|
+
meta: { pluginName: plugin.name }
|
|
1968
|
+
});
|
|
1969
|
+
await context.upsertFile(...barrelFiles);
|
|
1970
|
+
}
|
|
1630
1971
|
const duration = getElapsedMs(hrStart);
|
|
1631
1972
|
pluginTimings.set(plugin.name, duration);
|
|
1632
1973
|
await events.emit("plugin:end", plugin, {
|
|
@@ -1635,7 +1976,7 @@ async function safeBuild(options, overrides) {
|
|
|
1635
1976
|
});
|
|
1636
1977
|
await events.emit("debug", {
|
|
1637
1978
|
date: /* @__PURE__ */ new Date(),
|
|
1638
|
-
logs: [`✓ Plugin
|
|
1979
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
1639
1980
|
});
|
|
1640
1981
|
} catch (caughtError) {
|
|
1641
1982
|
const error = caughtError;
|
|
@@ -1649,7 +1990,7 @@ async function safeBuild(options, overrides) {
|
|
|
1649
1990
|
await events.emit("debug", {
|
|
1650
1991
|
date: errorTimestamp,
|
|
1651
1992
|
logs: [
|
|
1652
|
-
"✗ Plugin
|
|
1993
|
+
"✗ Plugin start failed",
|
|
1653
1994
|
` • Plugin Name: ${plugin.name}`,
|
|
1654
1995
|
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
1655
1996
|
" • Stack Trace:",
|
|
@@ -1703,6 +2044,10 @@ async function safeBuild(options, overrides) {
|
|
|
1703
2044
|
}
|
|
1704
2045
|
const files = [...fabric.files];
|
|
1705
2046
|
await fabric.write({ extension: config.output.extension });
|
|
2047
|
+
for (const plugin of driver.plugins.values()) if (plugin.buildEnd) {
|
|
2048
|
+
const context = driver.getContext(plugin);
|
|
2049
|
+
await plugin.buildEnd.call(context);
|
|
2050
|
+
}
|
|
1706
2051
|
return {
|
|
1707
2052
|
failedPlugins,
|
|
1708
2053
|
fabric,
|
|
@@ -1791,10 +2136,11 @@ function createAdapter(build) {
|
|
|
1791
2136
|
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
1792
2137
|
*
|
|
1793
2138
|
* @example
|
|
2139
|
+
* ```ts
|
|
1794
2140
|
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
1795
2141
|
* return {
|
|
1796
2142
|
* name: 'my-plugin',
|
|
1797
|
-
* options,
|
|
2143
|
+
* get options() { return options },
|
|
1798
2144
|
* resolvePath(baseName) { ... },
|
|
1799
2145
|
* resolveName(name, type) { ... },
|
|
1800
2146
|
* }
|
|
@@ -1802,38 +2148,64 @@ function createAdapter(build) {
|
|
|
1802
2148
|
*
|
|
1803
2149
|
* // instantiate
|
|
1804
2150
|
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
2151
|
+
* ```
|
|
1805
2152
|
*/
|
|
1806
2153
|
function createPlugin(build) {
|
|
1807
2154
|
return (options) => build(options ?? {});
|
|
1808
2155
|
}
|
|
1809
2156
|
//#endregion
|
|
1810
2157
|
//#region src/defineGenerator.ts
|
|
2158
|
+
/**
|
|
2159
|
+
* Defines a generator. Returns the object as-is with correct `this` typings.
|
|
2160
|
+
* No type discrimination (`type: 'react' | 'core'`) needed — `applyHookResult`
|
|
2161
|
+
* handles React elements and `File[]` uniformly.
|
|
2162
|
+
*/
|
|
1811
2163
|
function defineGenerator(generator) {
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
2164
|
+
return generator;
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Merges an array of generators into a single generator.
|
|
2168
|
+
*
|
|
2169
|
+
* The merged generator's `schema`, `operation`, and `operations` methods run
|
|
2170
|
+
* the corresponding method from each input generator in sequence, applying each
|
|
2171
|
+
* result via `applyHookResult`. This eliminates the need to write the loop
|
|
2172
|
+
* manually in each plugin.
|
|
2173
|
+
*
|
|
2174
|
+
* @param generators - Array of generators to merge into a single generator.
|
|
2175
|
+
*
|
|
2176
|
+
* @example
|
|
2177
|
+
* ```ts
|
|
2178
|
+
* const merged = mergeGenerators(generators)
|
|
2179
|
+
*
|
|
2180
|
+
* return {
|
|
2181
|
+
* name: pluginName,
|
|
2182
|
+
* schema: merged.schema,
|
|
2183
|
+
* operation: merged.operation,
|
|
2184
|
+
* operations: merged.operations,
|
|
2185
|
+
* }
|
|
2186
|
+
* ```
|
|
2187
|
+
*/
|
|
2188
|
+
function mergeGenerators(generators) {
|
|
1825
2189
|
return {
|
|
1826
|
-
|
|
1827
|
-
async
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2190
|
+
name: generators.length > 0 ? generators.map((g) => g.name).join("+") : "merged",
|
|
2191
|
+
async schema(node, options) {
|
|
2192
|
+
for (const gen of generators) {
|
|
2193
|
+
if (!gen.schema) continue;
|
|
2194
|
+
await applyHookResult(await gen.schema.call(this, node, options), this.fabric);
|
|
2195
|
+
}
|
|
1832
2196
|
},
|
|
1833
|
-
async
|
|
1834
|
-
|
|
2197
|
+
async operation(node, options) {
|
|
2198
|
+
for (const gen of generators) {
|
|
2199
|
+
if (!gen.operation) continue;
|
|
2200
|
+
await applyHookResult(await gen.operation.call(this, node, options), this.fabric);
|
|
2201
|
+
}
|
|
1835
2202
|
},
|
|
1836
|
-
|
|
2203
|
+
async operations(nodes, options) {
|
|
2204
|
+
for (const gen of generators) {
|
|
2205
|
+
if (!gen.operations) continue;
|
|
2206
|
+
await applyHookResult(await gen.operations.call(this, nodes, options), this.fabric);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
1837
2209
|
};
|
|
1838
2210
|
}
|
|
1839
2211
|
//#endregion
|
|
@@ -2207,172 +2579,6 @@ function defineResolver(build) {
|
|
|
2207
2579
|
};
|
|
2208
2580
|
}
|
|
2209
2581
|
//#endregion
|
|
2210
|
-
//#region src/renderNode.tsx
|
|
2211
|
-
/**
|
|
2212
|
-
* Renders a React component for a list of operation nodes (V2 generators).
|
|
2213
|
-
*/
|
|
2214
|
-
async function renderOperations(nodes, options) {
|
|
2215
|
-
const { config, fabric, plugin, Component, driver, adapter } = options;
|
|
2216
|
-
if (!Component) return;
|
|
2217
|
-
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2218
|
-
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, { children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2219
|
-
config,
|
|
2220
|
-
plugin,
|
|
2221
|
-
driver,
|
|
2222
|
-
adapter,
|
|
2223
|
-
nodes,
|
|
2224
|
-
options: options.options,
|
|
2225
|
-
resolver: options.resolver
|
|
2226
|
-
}) }));
|
|
2227
|
-
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2228
|
-
fabricChild.unmount();
|
|
2229
|
-
}
|
|
2230
|
-
/**
|
|
2231
|
-
* Renders a React component for a single operation node (V2 generators).
|
|
2232
|
-
*/
|
|
2233
|
-
async function renderOperation(node, options) {
|
|
2234
|
-
const { config, fabric, plugin, Component, adapter, driver } = options;
|
|
2235
|
-
if (!Component) return;
|
|
2236
|
-
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2237
|
-
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, { children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2238
|
-
config,
|
|
2239
|
-
plugin,
|
|
2240
|
-
driver,
|
|
2241
|
-
adapter,
|
|
2242
|
-
node,
|
|
2243
|
-
options: options.options,
|
|
2244
|
-
resolver: options.resolver
|
|
2245
|
-
}) }));
|
|
2246
|
-
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2247
|
-
fabricChild.unmount();
|
|
2248
|
-
}
|
|
2249
|
-
/**
|
|
2250
|
-
* Renders a React component for a single schema node (V2 generators).
|
|
2251
|
-
*/
|
|
2252
|
-
async function renderSchema(node, options) {
|
|
2253
|
-
const { config, fabric, plugin, Component, adapter, driver } = options;
|
|
2254
|
-
if (!Component) return;
|
|
2255
|
-
const fabricChild = (0, _kubb_react_fabric.createReactFabric)();
|
|
2256
|
-
await fabricChild.render(/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.Fabric, { children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(Component, {
|
|
2257
|
-
config,
|
|
2258
|
-
plugin,
|
|
2259
|
-
driver,
|
|
2260
|
-
adapter,
|
|
2261
|
-
node,
|
|
2262
|
-
options: options.options,
|
|
2263
|
-
resolver: options.resolver
|
|
2264
|
-
}) }));
|
|
2265
|
-
fabric.context.fileManager.upsert(...fabricChild.files);
|
|
2266
|
-
fabricChild.unmount();
|
|
2267
|
-
}
|
|
2268
|
-
/**
|
|
2269
|
-
* Dispatches a single schema node to all generators (react + core).
|
|
2270
|
-
* Resolves options per generator and skips excluded nodes.
|
|
2271
|
-
*/
|
|
2272
|
-
async function runGeneratorSchema(node, ctx) {
|
|
2273
|
-
const { generators, plugin, resolver, exclude, include, override, fabric, adapter, config, driver } = ctx;
|
|
2274
|
-
for (const generator of generators) {
|
|
2275
|
-
const options = resolver.resolveOptions(node, {
|
|
2276
|
-
options: plugin.options,
|
|
2277
|
-
exclude,
|
|
2278
|
-
include,
|
|
2279
|
-
override
|
|
2280
|
-
});
|
|
2281
|
-
if (options === null) continue;
|
|
2282
|
-
if (generator.type === "react" && generator.version === "2") await renderSchema(node, {
|
|
2283
|
-
options,
|
|
2284
|
-
resolver,
|
|
2285
|
-
adapter,
|
|
2286
|
-
config,
|
|
2287
|
-
fabric,
|
|
2288
|
-
Component: generator.Schema,
|
|
2289
|
-
plugin,
|
|
2290
|
-
driver
|
|
2291
|
-
});
|
|
2292
|
-
if (generator.type === "core" && generator.version === "2") {
|
|
2293
|
-
const files = await generator.schema?.({
|
|
2294
|
-
node,
|
|
2295
|
-
options,
|
|
2296
|
-
resolver,
|
|
2297
|
-
adapter,
|
|
2298
|
-
config,
|
|
2299
|
-
plugin,
|
|
2300
|
-
driver
|
|
2301
|
-
}) ?? [];
|
|
2302
|
-
await fabric.upsertFile(...files);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
/**
|
|
2307
|
-
* Dispatches a single operation node to all generators (react + core).
|
|
2308
|
-
* Resolves options per generator and skips excluded nodes.
|
|
2309
|
-
*/
|
|
2310
|
-
async function runGeneratorOperation(node, ctx) {
|
|
2311
|
-
const { generators, plugin, resolver, exclude, include, override, fabric, adapter, config, driver } = ctx;
|
|
2312
|
-
for (const generator of generators) {
|
|
2313
|
-
const options = resolver.resolveOptions(node, {
|
|
2314
|
-
options: plugin.options,
|
|
2315
|
-
exclude,
|
|
2316
|
-
include,
|
|
2317
|
-
override
|
|
2318
|
-
});
|
|
2319
|
-
if (options === null) continue;
|
|
2320
|
-
if (generator.type === "react" && generator.version === "2") await renderOperation(node, {
|
|
2321
|
-
options,
|
|
2322
|
-
resolver,
|
|
2323
|
-
adapter,
|
|
2324
|
-
config,
|
|
2325
|
-
fabric,
|
|
2326
|
-
Component: generator.Operation,
|
|
2327
|
-
plugin,
|
|
2328
|
-
driver
|
|
2329
|
-
});
|
|
2330
|
-
if (generator.type === "core" && generator.version === "2") {
|
|
2331
|
-
const files = await generator.operation?.({
|
|
2332
|
-
node,
|
|
2333
|
-
options,
|
|
2334
|
-
resolver,
|
|
2335
|
-
adapter,
|
|
2336
|
-
config,
|
|
2337
|
-
plugin,
|
|
2338
|
-
driver
|
|
2339
|
-
}) ?? [];
|
|
2340
|
-
await fabric.upsertFile(...files);
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
/**
|
|
2345
|
-
* Batch-dispatches all collected operation nodes to every generator (react + core).
|
|
2346
|
-
* Uses `plugin.options` directly — no per-node option resolution.
|
|
2347
|
-
*/
|
|
2348
|
-
async function runGeneratorOperations(nodes, ctx) {
|
|
2349
|
-
const { generators, plugin, resolver, fabric, adapter, config, driver } = ctx;
|
|
2350
|
-
for (const generator of generators) {
|
|
2351
|
-
if (generator.type === "react" && generator.version === "2") await renderOperations(nodes, {
|
|
2352
|
-
options: plugin.options,
|
|
2353
|
-
resolver,
|
|
2354
|
-
adapter,
|
|
2355
|
-
config,
|
|
2356
|
-
fabric,
|
|
2357
|
-
Component: generator.Operations,
|
|
2358
|
-
plugin,
|
|
2359
|
-
driver
|
|
2360
|
-
});
|
|
2361
|
-
if (generator.type === "core" && generator.version === "2") {
|
|
2362
|
-
const files = await generator.operations?.({
|
|
2363
|
-
nodes,
|
|
2364
|
-
options: plugin.options,
|
|
2365
|
-
resolver,
|
|
2366
|
-
adapter,
|
|
2367
|
-
config,
|
|
2368
|
-
plugin,
|
|
2369
|
-
driver
|
|
2370
|
-
}) ?? [];
|
|
2371
|
-
await fabric.upsertFile(...files);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
//#endregion
|
|
2376
2582
|
//#region src/storages/memoryStorage.ts
|
|
2377
2583
|
/**
|
|
2378
2584
|
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
@@ -2536,241 +2742,6 @@ async function detectFormatter() {
|
|
|
2536
2742
|
return null;
|
|
2537
2743
|
}
|
|
2538
2744
|
//#endregion
|
|
2539
|
-
//#region src/utils/TreeNode.ts
|
|
2540
|
-
/**
|
|
2541
|
-
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
2542
|
-
* flat list of generated {@link FabricFile.File} entries.
|
|
2543
|
-
*
|
|
2544
|
-
* Each node represents either a directory or a file within the output tree.
|
|
2545
|
-
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
2546
|
-
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
2547
|
-
* `*Deep` helpers.
|
|
2548
|
-
*/
|
|
2549
|
-
var TreeNode = class TreeNode {
|
|
2550
|
-
data;
|
|
2551
|
-
parent;
|
|
2552
|
-
children = [];
|
|
2553
|
-
#cachedLeaves = void 0;
|
|
2554
|
-
constructor(data, parent) {
|
|
2555
|
-
this.data = data;
|
|
2556
|
-
this.parent = parent;
|
|
2557
|
-
}
|
|
2558
|
-
addChild(data) {
|
|
2559
|
-
const child = new TreeNode(data, this);
|
|
2560
|
-
if (!this.children) this.children = [];
|
|
2561
|
-
this.children.push(child);
|
|
2562
|
-
return child;
|
|
2563
|
-
}
|
|
2564
|
-
/**
|
|
2565
|
-
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
2566
|
-
*/
|
|
2567
|
-
get root() {
|
|
2568
|
-
if (!this.parent) return this;
|
|
2569
|
-
return this.parent.root;
|
|
2570
|
-
}
|
|
2571
|
-
/**
|
|
2572
|
-
* Returns all leaf descendants (nodes with no children) of this node.
|
|
2573
|
-
*
|
|
2574
|
-
* Results are cached after the first traversal.
|
|
2575
|
-
*/
|
|
2576
|
-
get leaves() {
|
|
2577
|
-
if (!this.children || this.children.length === 0) return [this];
|
|
2578
|
-
if (this.#cachedLeaves) return this.#cachedLeaves;
|
|
2579
|
-
const leaves = [];
|
|
2580
|
-
for (const child of this.children) leaves.push(...child.leaves);
|
|
2581
|
-
this.#cachedLeaves = leaves;
|
|
2582
|
-
return leaves;
|
|
2583
|
-
}
|
|
2584
|
-
/**
|
|
2585
|
-
* Visits this node and every descendant in depth-first order.
|
|
2586
|
-
*/
|
|
2587
|
-
forEach(callback) {
|
|
2588
|
-
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
2589
|
-
callback(this);
|
|
2590
|
-
for (const child of this.children) child.forEach(callback);
|
|
2591
|
-
return this;
|
|
2592
|
-
}
|
|
2593
|
-
/**
|
|
2594
|
-
* Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
|
|
2595
|
-
*/
|
|
2596
|
-
findDeep(predicate) {
|
|
2597
|
-
if (typeof predicate !== "function") throw new TypeError("find() predicate must be a function");
|
|
2598
|
-
return this.leaves.find(predicate);
|
|
2599
|
-
}
|
|
2600
|
-
/**
|
|
2601
|
-
* Calls `callback` for every leaf of this node.
|
|
2602
|
-
*/
|
|
2603
|
-
forEachDeep(callback) {
|
|
2604
|
-
if (typeof callback !== "function") throw new TypeError("forEach() callback must be a function");
|
|
2605
|
-
this.leaves.forEach(callback);
|
|
2606
|
-
}
|
|
2607
|
-
/**
|
|
2608
|
-
* Returns all leaves that satisfy `callback`.
|
|
2609
|
-
*/
|
|
2610
|
-
filterDeep(callback) {
|
|
2611
|
-
if (typeof callback !== "function") throw new TypeError("filter() callback must be a function");
|
|
2612
|
-
return this.leaves.filter(callback);
|
|
2613
|
-
}
|
|
2614
|
-
/**
|
|
2615
|
-
* Maps every leaf through `callback` and returns the resulting array.
|
|
2616
|
-
*/
|
|
2617
|
-
mapDeep(callback) {
|
|
2618
|
-
if (typeof callback !== "function") throw new TypeError("map() callback must be a function");
|
|
2619
|
-
return this.leaves.map(callback);
|
|
2620
|
-
}
|
|
2621
|
-
/**
|
|
2622
|
-
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
2623
|
-
*
|
|
2624
|
-
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
2625
|
-
* - Returns `null` when no files match.
|
|
2626
|
-
*/
|
|
2627
|
-
static build(files, root) {
|
|
2628
|
-
try {
|
|
2629
|
-
const filteredTree = buildDirectoryTree(files, root);
|
|
2630
|
-
if (!filteredTree) return null;
|
|
2631
|
-
const treeNode = new TreeNode({
|
|
2632
|
-
name: filteredTree.name,
|
|
2633
|
-
path: filteredTree.path,
|
|
2634
|
-
file: filteredTree.file,
|
|
2635
|
-
type: getMode(filteredTree.path)
|
|
2636
|
-
});
|
|
2637
|
-
const recurse = (node, item) => {
|
|
2638
|
-
const subNode = node.addChild({
|
|
2639
|
-
name: item.name,
|
|
2640
|
-
path: item.path,
|
|
2641
|
-
file: item.file,
|
|
2642
|
-
type: getMode(item.path)
|
|
2643
|
-
});
|
|
2644
|
-
if (item.children?.length) item.children?.forEach((child) => {
|
|
2645
|
-
recurse(subNode, child);
|
|
2646
|
-
});
|
|
2647
|
-
};
|
|
2648
|
-
filteredTree.children?.forEach((child) => {
|
|
2649
|
-
recurse(treeNode, child);
|
|
2650
|
-
});
|
|
2651
|
-
return treeNode;
|
|
2652
|
-
} catch (error) {
|
|
2653
|
-
throw new Error("Something went wrong with creating barrel files with the TreeNode class", { cause: error });
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
};
|
|
2657
|
-
const normalizePath = (p) => p.replaceAll("\\", "/");
|
|
2658
|
-
function buildDirectoryTree(files, rootFolder = "") {
|
|
2659
|
-
const normalizedRootFolder = normalizePath(rootFolder);
|
|
2660
|
-
const rootPrefix = normalizedRootFolder.endsWith("/") ? normalizedRootFolder : `${normalizedRootFolder}/`;
|
|
2661
|
-
const filteredFiles = files.filter((file) => {
|
|
2662
|
-
const normalizedFilePath = normalizePath(file.path);
|
|
2663
|
-
return rootFolder ? normalizedFilePath.startsWith(rootPrefix) && !normalizedFilePath.endsWith(".json") : !normalizedFilePath.endsWith(".json");
|
|
2664
|
-
});
|
|
2665
|
-
if (filteredFiles.length === 0) return null;
|
|
2666
|
-
const root = {
|
|
2667
|
-
name: rootFolder || "",
|
|
2668
|
-
path: rootFolder || "",
|
|
2669
|
-
children: []
|
|
2670
|
-
};
|
|
2671
|
-
filteredFiles.forEach((file) => {
|
|
2672
|
-
const parts = file.path.slice(rootFolder.length).split("/").filter(Boolean);
|
|
2673
|
-
let currentLevel = root.children;
|
|
2674
|
-
let currentPath = normalizePath(rootFolder);
|
|
2675
|
-
parts.forEach((part, index) => {
|
|
2676
|
-
currentPath = node_path.default.posix.join(currentPath, part);
|
|
2677
|
-
let existingNode = currentLevel.find((node) => node.name === part);
|
|
2678
|
-
if (!existingNode) {
|
|
2679
|
-
if (index === parts.length - 1) existingNode = {
|
|
2680
|
-
name: part,
|
|
2681
|
-
file,
|
|
2682
|
-
path: currentPath
|
|
2683
|
-
};
|
|
2684
|
-
else existingNode = {
|
|
2685
|
-
name: part,
|
|
2686
|
-
path: currentPath,
|
|
2687
|
-
children: []
|
|
2688
|
-
};
|
|
2689
|
-
currentLevel.push(existingNode);
|
|
2690
|
-
}
|
|
2691
|
-
if (!existingNode.file) currentLevel = existingNode.children;
|
|
2692
|
-
});
|
|
2693
|
-
});
|
|
2694
|
-
return root;
|
|
2695
|
-
}
|
|
2696
|
-
//#endregion
|
|
2697
|
-
//#region src/utils/getBarrelFiles.ts
|
|
2698
|
-
/** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
|
|
2699
|
-
function getBarrelFilesByRoot(root, files) {
|
|
2700
|
-
const cachedFiles = /* @__PURE__ */ new Map();
|
|
2701
|
-
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
2702
|
-
if (!treeNode?.children || !treeNode.parent?.data.path) return;
|
|
2703
|
-
const barrelFile = {
|
|
2704
|
-
path: (0, node_path.join)(treeNode.parent?.data.path, "index.ts"),
|
|
2705
|
-
baseName: "index.ts",
|
|
2706
|
-
exports: [],
|
|
2707
|
-
imports: [],
|
|
2708
|
-
sources: []
|
|
2709
|
-
};
|
|
2710
|
-
const previousBarrelFile = cachedFiles.get(barrelFile.path);
|
|
2711
|
-
treeNode.leaves.forEach((item) => {
|
|
2712
|
-
if (!item.data.name) return;
|
|
2713
|
-
(item.data.file?.sources || []).forEach((source) => {
|
|
2714
|
-
if (!item.data.file?.path || !source.isIndexable || !source.name) return;
|
|
2715
|
-
if (previousBarrelFile?.sources.some((item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly)) return;
|
|
2716
|
-
barrelFile.exports.push({
|
|
2717
|
-
name: [source.name],
|
|
2718
|
-
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
2719
|
-
isTypeOnly: source.isTypeOnly
|
|
2720
|
-
});
|
|
2721
|
-
barrelFile.sources.push({
|
|
2722
|
-
name: source.name,
|
|
2723
|
-
isTypeOnly: source.isTypeOnly,
|
|
2724
|
-
value: "",
|
|
2725
|
-
isExportable: false,
|
|
2726
|
-
isIndexable: false
|
|
2727
|
-
});
|
|
2728
|
-
});
|
|
2729
|
-
});
|
|
2730
|
-
if (previousBarrelFile) {
|
|
2731
|
-
previousBarrelFile.sources.push(...barrelFile.sources);
|
|
2732
|
-
previousBarrelFile.exports?.push(...barrelFile.exports || []);
|
|
2733
|
-
} else cachedFiles.set(barrelFile.path, barrelFile);
|
|
2734
|
-
});
|
|
2735
|
-
return [...cachedFiles.values()];
|
|
2736
|
-
}
|
|
2737
|
-
function trimExtName(text) {
|
|
2738
|
-
const dotIndex = text.lastIndexOf(".");
|
|
2739
|
-
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
2740
|
-
return text;
|
|
2741
|
-
}
|
|
2742
|
-
/**
|
|
2743
|
-
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
2744
|
-
*
|
|
2745
|
-
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
2746
|
-
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
2747
|
-
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
2748
|
-
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
2749
|
-
*/
|
|
2750
|
-
async function getBarrelFiles(files, { type, meta = {}, root, output }) {
|
|
2751
|
-
if (!type || type === "propagate") return [];
|
|
2752
|
-
const pathToBuildFrom = (0, node_path.join)(root, output.path);
|
|
2753
|
-
if (trimExtName(pathToBuildFrom).endsWith("index")) return [];
|
|
2754
|
-
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files);
|
|
2755
|
-
if (type === "all") return barrelFiles.map((file) => {
|
|
2756
|
-
return {
|
|
2757
|
-
...file,
|
|
2758
|
-
exports: file.exports?.map((exportItem) => {
|
|
2759
|
-
return {
|
|
2760
|
-
...exportItem,
|
|
2761
|
-
name: void 0
|
|
2762
|
-
};
|
|
2763
|
-
})
|
|
2764
|
-
};
|
|
2765
|
-
});
|
|
2766
|
-
return barrelFiles.map((indexFile) => {
|
|
2767
|
-
return {
|
|
2768
|
-
...indexFile,
|
|
2769
|
-
meta
|
|
2770
|
-
};
|
|
2771
|
-
});
|
|
2772
|
-
}
|
|
2773
|
-
//#endregion
|
|
2774
2745
|
//#region src/utils/getConfigs.ts
|
|
2775
2746
|
/**
|
|
2776
2747
|
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
@@ -2954,12 +2925,7 @@ exports.isInputPath = isInputPath;
|
|
|
2954
2925
|
exports.linters = linters;
|
|
2955
2926
|
exports.logLevel = logLevel;
|
|
2956
2927
|
exports.memoryStorage = memoryStorage;
|
|
2957
|
-
exports.
|
|
2958
|
-
exports.renderOperations = renderOperations;
|
|
2959
|
-
exports.renderSchema = renderSchema;
|
|
2960
|
-
exports.runGeneratorOperation = runGeneratorOperation;
|
|
2961
|
-
exports.runGeneratorOperations = runGeneratorOperations;
|
|
2962
|
-
exports.runGeneratorSchema = runGeneratorSchema;
|
|
2928
|
+
exports.mergeGenerators = mergeGenerators;
|
|
2963
2929
|
exports.safeBuild = safeBuild;
|
|
2964
2930
|
exports.satisfiesDependency = satisfiesDependency;
|
|
2965
2931
|
exports.setup = setup;
|