@kubb/studio 5.5.2 → 5.5.4
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/configFile-BTVeov27.cjs +585 -0
- package/dist/configFile-BTVeov27.cjs.map +1 -0
- package/dist/configFile-BymXUi9P.js +584 -0
- package/dist/configFile-BymXUi9P.js.map +1 -0
- package/dist/index.cjs +17 -1092
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -1059
- package/dist/index.js.map +1 -1
- package/dist/resolveConfig-D7I3Nj60.cjs +360 -0
- package/dist/resolveConfig-D7I3Nj60.cjs.map +1 -0
- package/dist/resolveConfig-DMIG_MH-.js +325 -0
- package/dist/resolveConfig-DMIG_MH-.js.map +1 -0
- package/dist/rolldown-runtime-qbf5tadS.cjs +38 -0
- package/dist/snapshotPackage-BwM3LFmZ.cjs +176 -0
- package/dist/snapshotPackage-BwM3LFmZ.cjs.map +1 -0
- package/dist/snapshotPackage-I8bt8KTR.js +175 -0
- package/dist/snapshotPackage-I8bt8KTR.js.map +1 -0
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -1,84 +1,27 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __name = (target, value) => __defProp(target, "name", {
|
|
6
|
-
value,
|
|
7
|
-
configurable: true
|
|
8
|
-
});
|
|
9
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
12
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
15
|
-
key = keys[i];
|
|
16
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
17
|
-
get: ((k) => from[k]).bind(null, key),
|
|
18
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
24
|
-
value: mod,
|
|
25
|
-
enumerable: true
|
|
26
|
-
}) : target, mod));
|
|
27
|
-
//#endregion
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
|
|
3
|
+
const require_resolveConfig = require("./resolveConfig-D7I3Nj60.cjs");
|
|
28
4
|
const require_protocol = require("./protocol.cjs");
|
|
29
5
|
let node_crypto = require("node:crypto");
|
|
30
6
|
let node_timers_promises = require("node:timers/promises");
|
|
31
7
|
let node_process = require("node:process");
|
|
32
|
-
node_process = __toESM(node_process, 1);
|
|
8
|
+
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
33
9
|
let node_child_process = require("node:child_process");
|
|
34
10
|
let node_fs_promises = require("node:fs/promises");
|
|
35
11
|
let node_path = require("node:path");
|
|
36
|
-
node_path = __toESM(node_path, 1);
|
|
12
|
+
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
37
13
|
let ofetch = require("ofetch");
|
|
38
14
|
let node_util = require("node:util");
|
|
39
15
|
let unstorage = require("unstorage");
|
|
40
16
|
let unstorage_drivers_fs = require("unstorage/drivers/fs");
|
|
41
|
-
unstorage_drivers_fs = __toESM(unstorage_drivers_fs, 1);
|
|
17
|
+
unstorage_drivers_fs = require_rolldown_runtime.__toESM(unstorage_drivers_fs, 1);
|
|
42
18
|
let node_os = require("node:os");
|
|
43
19
|
let _kubb_core = require("@kubb/core");
|
|
44
|
-
let magicast = require("magicast");
|
|
45
|
-
let node_fs = require("node:fs");
|
|
46
20
|
let node_module = require("node:module");
|
|
47
|
-
let node_url = require("node:url");
|
|
48
|
-
let remeda = require("remeda");
|
|
49
|
-
let node_zlib = require("node:zlib");
|
|
50
|
-
let tsdown = require("tsdown");
|
|
51
21
|
let capnweb = require("capnweb");
|
|
52
22
|
let ws = require("ws");
|
|
53
|
-
ws = __toESM(ws, 1);
|
|
23
|
+
ws = require_rolldown_runtime.__toESM(ws, 1);
|
|
54
24
|
let tinyexec = require("tinyexec");
|
|
55
|
-
//#region ../../internals/utils/src/casing.ts
|
|
56
|
-
/**
|
|
57
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
58
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
59
|
-
* and capitalizes each word according to `pascal`.
|
|
60
|
-
*
|
|
61
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
62
|
-
*/
|
|
63
|
-
function toCamelOrPascal(text, pascal) {
|
|
64
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
65
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
66
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
67
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Converts `text` to camelCase.
|
|
71
|
-
*
|
|
72
|
-
* @example Word boundaries
|
|
73
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
74
|
-
*
|
|
75
|
-
* @example With a prefix
|
|
76
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
77
|
-
*/
|
|
78
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
79
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
80
|
-
}
|
|
81
|
-
//#endregion
|
|
82
25
|
//#region ../../internals/utils/src/errors.ts
|
|
83
26
|
/**
|
|
84
27
|
* Coerces an unknown thrown value to an `Error` instance.
|
|
@@ -107,91 +50,6 @@ function getErrorMessage(value) {
|
|
|
107
50
|
return value instanceof Error ? value.message : String(value);
|
|
108
51
|
}
|
|
109
52
|
//#endregion
|
|
110
|
-
//#region ../../internals/utils/src/runtime.ts
|
|
111
|
-
/**
|
|
112
|
-
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
113
|
-
*
|
|
114
|
-
* Prefer the shared {@link runtime} instance over constructing your own.
|
|
115
|
-
*/
|
|
116
|
-
var Runtime = class {
|
|
117
|
-
/**
|
|
118
|
-
* `true` when the current process is running under Bun.
|
|
119
|
-
*
|
|
120
|
-
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
121
|
-
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
122
|
-
* otherwise look like Node.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```ts
|
|
126
|
-
* if (runtime.isBun) {
|
|
127
|
-
* await Bun.write(path, data)
|
|
128
|
-
* }
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
get isBun() {
|
|
132
|
-
return typeof Bun !== "undefined";
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* `true` when the current process is running under Deno.
|
|
136
|
-
*/
|
|
137
|
-
get isDeno() {
|
|
138
|
-
return typeof globalThis.Deno !== "undefined";
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* `true` when the current process is running under Node.
|
|
142
|
-
*
|
|
143
|
-
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
144
|
-
*/
|
|
145
|
-
get isNode() {
|
|
146
|
-
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Name of the runtime executing the current process.
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* ```ts
|
|
153
|
-
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
|
-
get name() {
|
|
157
|
-
if (this.isBun) return "bun";
|
|
158
|
-
if (this.isDeno) return "deno";
|
|
159
|
-
return "node";
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Version of the active runtime, or an empty string when it cannot be read.
|
|
163
|
-
*
|
|
164
|
-
* @example
|
|
165
|
-
* ```ts
|
|
166
|
-
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
|
-
get version() {
|
|
170
|
-
if (this.isBun) return process.versions.bun ?? "";
|
|
171
|
-
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
172
|
-
return process.versions?.node ?? "";
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
/**
|
|
176
|
-
* Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
|
|
177
|
-
*/
|
|
178
|
-
const runtime = new Runtime();
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region ../../internals/utils/src/fs.ts
|
|
181
|
-
/**
|
|
182
|
-
* Reads the file at `path` as a UTF-8 string.
|
|
183
|
-
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
184
|
-
*
|
|
185
|
-
* @example
|
|
186
|
-
* ```ts
|
|
187
|
-
* const source = await read('./src/Pet.ts')
|
|
188
|
-
* ```
|
|
189
|
-
*/
|
|
190
|
-
async function read(path) {
|
|
191
|
-
if (runtime.isBun) return Bun.file(path).text();
|
|
192
|
-
return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
|
|
193
|
-
}
|
|
194
|
-
//#endregion
|
|
195
53
|
//#region ../../internals/utils/src/promise.ts
|
|
196
54
|
/**
|
|
197
55
|
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
@@ -342,7 +200,7 @@ async function detectTool$1(candidates) {
|
|
|
342
200
|
for (const candidate of candidates) if (await isToolAvailable(candidate)) return candidate;
|
|
343
201
|
return null;
|
|
344
202
|
}
|
|
345
|
-
__name(detectTool$1, "detectTool");
|
|
203
|
+
require_rolldown_runtime.__name(detectTool$1, "detectTool");
|
|
346
204
|
/**
|
|
347
205
|
* Tokenizes a shell command string, respecting single and double quotes.
|
|
348
206
|
*
|
|
@@ -725,944 +583,7 @@ function resolveAgentCapacity(env = process.env) {
|
|
|
725
583
|
}
|
|
726
584
|
//#endregion
|
|
727
585
|
//#region package.json
|
|
728
|
-
var version = "5.5.
|
|
729
|
-
//#endregion
|
|
730
|
-
//#region src/operations/resolveConfig.ts
|
|
731
|
-
/**
|
|
732
|
-
* Imports a package, falling back to how the user's project would resolve it.
|
|
733
|
-
*
|
|
734
|
-
* `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,
|
|
735
|
-
* `npm i -g`) only sees its own `node_modules` and misses the plugins installed next to the user's
|
|
736
|
-
* config. The retry resolves from `process.cwd()` instead.
|
|
737
|
-
*/
|
|
738
|
-
async function importFromProject(packageName) {
|
|
739
|
-
try {
|
|
740
|
-
return await import(packageName);
|
|
741
|
-
} catch {
|
|
742
|
-
const resolved = (0, node_module.createRequire)((0, node_url.pathToFileURL)(`${process.cwd()}/`)).resolve(packageName);
|
|
743
|
-
const esm = resolved.replace(/\.cjs$/, ".js");
|
|
744
|
-
return await import((0, node_url.pathToFileURL)(esm !== resolved && (0, node_fs.existsSync)(esm) ? esm : resolved).href);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Strips the `@kubb/` scope from a plugin package name, matching the `name` convention Kubb
|
|
749
|
-
* plugin factories use internally.
|
|
750
|
-
*
|
|
751
|
-
* @example
|
|
752
|
-
* ```ts
|
|
753
|
-
* toPluginName('@kubb/plugin-ts') // 'plugin-ts'
|
|
754
|
-
* ```
|
|
755
|
-
*/
|
|
756
|
-
function toPluginName(packageName) {
|
|
757
|
-
return packageName.split("/").pop() ?? packageName;
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Adds the `@kubb/` scope a plugin's package carries but its `name` does not, the inverse of
|
|
761
|
-
* {@link toPluginName}. A name outside the `plugin-` convention is left alone, so a third-party
|
|
762
|
-
* plugin is not reported as one of Kubb's.
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* ```ts
|
|
766
|
-
* toPackageName('plugin-ts') // '@kubb/plugin-ts'
|
|
767
|
-
* toPackageName('@acme/my-plugin') // '@acme/my-plugin'
|
|
768
|
-
* ```
|
|
769
|
-
*/
|
|
770
|
-
function toPackageName(name) {
|
|
771
|
-
return name.startsWith("plugin-") ? `@kubb/${name}` : name;
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* Derives the conventional named export for a `@kubb/*` plugin package from its package name.
|
|
775
|
-
*
|
|
776
|
-
* @example
|
|
777
|
-
* ```ts
|
|
778
|
-
* toExportName('@kubb/plugin-react-query') // 'pluginReactQuery'
|
|
779
|
-
* toExportName('@kubb/plugin-ts') // 'pluginTs'
|
|
780
|
-
* ```
|
|
781
|
-
*/
|
|
782
|
-
function toExportName(packageName) {
|
|
783
|
-
return camelCase(toPluginName(packageName));
|
|
784
|
-
}
|
|
785
|
-
/**
|
|
786
|
-
* A `@kubb/plugin-*` package specifier. Nothing else may reach `import()`: only Kubb's own
|
|
787
|
-
* plugins are supported, so a payload naming anything else, a third-party package or a path, is
|
|
788
|
-
* refused before it can execute.
|
|
789
|
-
*/
|
|
790
|
-
const KUBB_PLUGIN_SPECIFIER = /^@kubb\/plugin-[\w.-]+$/;
|
|
791
|
-
/**
|
|
792
|
-
* Whether `name` is a `@kubb/plugin-*` specifier. Exported so `configFile.ts` can refuse the same
|
|
793
|
-
* shape before printing a Studio-supplied plugin name into the config file's source text.
|
|
794
|
-
*/
|
|
795
|
-
function isKubbPluginSpecifier(name) {
|
|
796
|
-
return KUBB_PLUGIN_SPECIFIER.test(name);
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Dynamically imports a `@kubb/plugin-*` package and returns its factory function.
|
|
800
|
-
*
|
|
801
|
-
* Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`
|
|
802
|
-
* build ARG, no runtime installation is possible in the distroless container.
|
|
803
|
-
*
|
|
804
|
-
* Resolution order: the camelCase named export the package name implies (e.g. `pluginTs`), then
|
|
805
|
-
* the default export.
|
|
806
|
-
*
|
|
807
|
-
* @throws if the package cannot be imported or exports no callable factory.
|
|
808
|
-
*/
|
|
809
|
-
async function loadPluginFactory(packageName) {
|
|
810
|
-
if (!isKubbPluginSpecifier(packageName)) throw new Error(`Plugin "${packageName}" is not a @kubb/plugin-* package. Kubb Studio only supports Kubb's own plugins.`);
|
|
811
|
-
let mod;
|
|
812
|
-
try {
|
|
813
|
-
mod = await importFromProject(packageName);
|
|
814
|
-
} catch (cause) {
|
|
815
|
-
throw new Error(`Plugin "${packageName}" could not be loaded. Make sure it is installed: \`npm install ${packageName}\``, { cause });
|
|
816
|
-
}
|
|
817
|
-
const exportName = toExportName(packageName);
|
|
818
|
-
if (typeof mod[exportName] === "function") return mod[exportName];
|
|
819
|
-
if (typeof mod["default"] === "function") return mod["default"];
|
|
820
|
-
throw new Error(`Plugin "${packageName}" does not export a callable factory. Tried the named export "${exportName}" and "default".`);
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Resolves each plugin entry by dynamically importing the `@kubb/plugin-*` package and
|
|
824
|
-
* calling its factory with the provided options.
|
|
825
|
-
*
|
|
826
|
-
* Packages must be pre-installed in the Docker image at build time, use the `KUBB_PACKAGES`
|
|
827
|
-
* build ARG to control which ones are available at runtime.
|
|
828
|
-
*
|
|
829
|
-
* @example
|
|
830
|
-
* ```ts
|
|
831
|
-
* { name: '@kubb/plugin-react-query', options: { output: { path: './hooks' } } }
|
|
832
|
-
* { name: '@kubb/plugin-ts', options: { output: { path: './types' } } }
|
|
833
|
-
* ```
|
|
834
|
-
*/
|
|
835
|
-
async function resolvePlugins(plugins) {
|
|
836
|
-
return Promise.all(plugins.map(async ({ name, options }) => {
|
|
837
|
-
return (await loadPluginFactory(name))(options ?? {});
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Whether `value` comes back unchanged from a JSON round trip. A function, a `RegExp` or another
|
|
842
|
-
* class instance does not: the agent reports options to Studio as JSON, so what Studio sends back
|
|
843
|
-
* for such a value is a lossy copy (`macros: [{ name }]` without its hooks, `pattern: {}`).
|
|
844
|
-
*/
|
|
845
|
-
function survivesJson(value) {
|
|
846
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
847
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
848
|
-
if (Array.isArray(value)) return value.every(survivesJson);
|
|
849
|
-
if ((0, remeda.isPlainObject)(value)) return Object.values(value).every(survivesJson);
|
|
850
|
-
return false;
|
|
851
|
-
}
|
|
852
|
-
/**
|
|
853
|
-
* Deep-merges Studio's options over the disk options, with Studio taking priority, except where the
|
|
854
|
-
* disk value cannot survive JSON. Studio only ever saw a lossy copy of such a value, so the disk
|
|
855
|
-
* value is kept rather than replaced by what Studio echoes back. Plain objects merge key by key, so
|
|
856
|
-
* Studio can still change a literal that sits next to a function (e.g. `group.type` beside `group.name`).
|
|
857
|
-
*
|
|
858
|
-
* @example
|
|
859
|
-
* ```ts
|
|
860
|
-
* mergeOptions({ macros: [camelCaseMacro], syntaxType: 'type' }, { macros: [{ name: 'camelCase' }], syntaxType: 'interface' })
|
|
861
|
-
* // { macros: [camelCaseMacro], syntaxType: 'interface' }
|
|
862
|
-
* ```
|
|
863
|
-
*/
|
|
864
|
-
function mergeOptions(disk, studio) {
|
|
865
|
-
const merged = { ...disk };
|
|
866
|
-
for (const [key, value] of Object.entries(studio)) {
|
|
867
|
-
const current = disk[key];
|
|
868
|
-
if ((0, remeda.isPlainObject)(current) && (0, remeda.isPlainObject)(value)) {
|
|
869
|
-
merged[key] = mergeOptions(current, value);
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
if (current !== void 0 && !survivesJson(current)) continue;
|
|
873
|
-
merged[key] = value;
|
|
874
|
-
}
|
|
875
|
-
return merged;
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Merges studio plugin options with disk config plugins.
|
|
879
|
-
* Studio takes priority: options from studio win over disk (except values only the disk config
|
|
880
|
-
* can express, see {@link mergeOptions}), and a plugin Studio explicitly
|
|
881
|
-
* disabled is dropped even when the disk config still lists it. Disk plugins without a studio
|
|
882
|
-
* counterpart are kept as-is. Studio plugins not present on disk are appended.
|
|
883
|
-
*
|
|
884
|
-
* For plugins present in both configs, the plugin is re-instantiated with merged options
|
|
885
|
-
* so that all internal closures correctly reference the merged values.
|
|
886
|
-
*/
|
|
887
|
-
async function mergePlugins(diskPlugins, studioPlugins) {
|
|
888
|
-
const disabledNames = new Set((studioPlugins ?? []).filter((entry) => entry.disabled).map((entry) => toPluginName(entry.name)));
|
|
889
|
-
const activeDiskPlugins = disabledNames.size ? diskPlugins?.filter((plugin) => !disabledNames.has(plugin.name)) : diskPlugins;
|
|
890
|
-
const activeStudioPlugins = studioPlugins?.filter((entry) => !entry.disabled);
|
|
891
|
-
if (!activeDiskPlugins && !activeStudioPlugins?.length) return void 0;
|
|
892
|
-
if (!activeStudioPlugins?.length) return activeDiskPlugins;
|
|
893
|
-
if (!activeDiskPlugins) return resolvePlugins(activeStudioPlugins);
|
|
894
|
-
const studioEntryByName = new Map(activeStudioPlugins.map((entry) => [toPluginName(entry.name), entry]));
|
|
895
|
-
const diskNames = new Set(activeDiskPlugins.map((plugin) => plugin.name));
|
|
896
|
-
const merged = await Promise.all(activeDiskPlugins.map(async (diskPlugin) => {
|
|
897
|
-
const studioEntry = studioEntryByName.get(diskPlugin.name);
|
|
898
|
-
if (!studioEntry) return diskPlugin;
|
|
899
|
-
const options = mergeOptions(diskPlugin.options ?? {}, studioEntry.options ?? {});
|
|
900
|
-
const [resolved] = await resolvePlugins([{
|
|
901
|
-
name: studioEntry.name,
|
|
902
|
-
options
|
|
903
|
-
}]);
|
|
904
|
-
return resolved ?? diskPlugin;
|
|
905
|
-
}));
|
|
906
|
-
const studioOnly = activeStudioPlugins.filter((entry) => !diskNames.has(toPluginName(entry.name)));
|
|
907
|
-
return [...merged, ...await resolvePlugins(studioOnly)];
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Merges Studio-provided adapter option overrides into the disk config's adapter.
|
|
911
|
-
*
|
|
912
|
-
* Adapter instances carry live functions (`parse`, `getImports`, ...) that can't survive
|
|
913
|
-
* JSON serialization over the WebSocket, so `studioOptions` is treated as an options patch
|
|
914
|
-
* rather than a replacement adapter. Re-invokes the same `@kubb/adapter-<name>` factory the
|
|
915
|
-
* disk config used, with the merged options, so the resulting instance has fresh closures
|
|
916
|
-
* over the merged values instead of a plain object missing `parse`.
|
|
917
|
-
*/
|
|
918
|
-
async function mergeAdapter(diskAdapter, studioOptions) {
|
|
919
|
-
if (!studioOptions || !diskAdapter) return diskAdapter;
|
|
920
|
-
const packageName = `@kubb/adapter-${diskAdapter.name}`;
|
|
921
|
-
const factory = (await importFromProject(packageName))[toExportName(packageName)];
|
|
922
|
-
if (typeof factory !== "function") return diskAdapter;
|
|
923
|
-
return factory(mergeOptions(diskAdapter.options ?? {}, studioOptions));
|
|
924
|
-
}
|
|
925
|
-
//#endregion
|
|
926
|
-
//#region src/operations/configFile.ts
|
|
927
|
-
/**
|
|
928
|
-
* A valid JavaScript identifier, so an import name can only ever print as `import { name } from`,
|
|
929
|
-
* never as source that breaks out of the import statement.
|
|
930
|
-
*/
|
|
931
|
-
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
932
|
-
/**
|
|
933
|
-
* `key: value` as an object literal property, in the file's quote and key style.
|
|
934
|
-
*
|
|
935
|
-
* Uses magicast's literal builder for the key/value nodes, then wraps them as a Babel
|
|
936
|
-
* `ObjectProperty`, the type the rest of this file reads.
|
|
937
|
-
*/
|
|
938
|
-
function literalProperty({ key, value }) {
|
|
939
|
-
const built = magicast.builders.literal({ [key]: value }).properties[0];
|
|
940
|
-
return {
|
|
941
|
-
type: "ObjectProperty",
|
|
942
|
-
key: built.key,
|
|
943
|
-
value: built.value,
|
|
944
|
-
computed: false,
|
|
945
|
-
shorthand: false
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* Marks the comment block a `disable-plugin` leaves behind, so `enable-plugin` can find its way
|
|
950
|
-
* back to the exact lines it commented out. Carries the block's line count, so `enable-plugin`
|
|
951
|
-
* restores exactly those lines instead of scanning forward through whatever comments follow.
|
|
952
|
-
*/
|
|
953
|
-
const DISABLED_MARKER = "kubb:disabled";
|
|
954
|
-
/**
|
|
955
|
-
* The one line `disable-plugin` writes above the comment block it produces for `plugin`.
|
|
956
|
-
*/
|
|
957
|
-
function formatMarker(plugin, lineCount, indent = "") {
|
|
958
|
-
return `${indent}// ${DISABLED_MARKER} ${plugin} ${lineCount}`;
|
|
959
|
-
}
|
|
960
|
-
/**
|
|
961
|
-
* The plugin and comment-block length a marker line names, when `line` is one.
|
|
962
|
-
*/
|
|
963
|
-
function parseMarker(line) {
|
|
964
|
-
const trimmed = line.trim();
|
|
965
|
-
if (!trimmed.startsWith(`// ${DISABLED_MARKER} `)) return;
|
|
966
|
-
const match = trimmed.slice(`// ${DISABLED_MARKER} `.length).match(/^(.+)\s+(\d+)$/);
|
|
967
|
-
return match ? {
|
|
968
|
-
plugin: match[1],
|
|
969
|
-
lineCount: Number(match[2])
|
|
970
|
-
} : void 0;
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Steps through a config's wrappers to the object literal underneath: a `satisfies`/`as`
|
|
974
|
-
* assertion, a `() => ...` factory, or a factory whose block body returns the config.
|
|
975
|
-
*/
|
|
976
|
-
function unwrap(node) {
|
|
977
|
-
if (!node) return;
|
|
978
|
-
if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression") return unwrap(node.expression);
|
|
979
|
-
if (node.type !== "ArrowFunctionExpression" && node.type !== "FunctionExpression") return node;
|
|
980
|
-
if (node.body.type !== "BlockStatement") return unwrap(node.body);
|
|
981
|
-
return unwrap(node.body.body.find((statement) => statement.type === "ReturnStatement")?.argument);
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Every config object in `export default defineConfig(...)`, or why the file is unmanaged.
|
|
985
|
-
*
|
|
986
|
-
* An array export gets one entry per element, matching {@link ConfigRef}'s numeric index.
|
|
987
|
-
*
|
|
988
|
-
* Walks the parsed AST rather than magicast's proxies, which throw on node types they cannot
|
|
989
|
-
* cast, most of what an unmanaged config file is made of.
|
|
990
|
-
*/
|
|
991
|
-
function findConfigs(mod) {
|
|
992
|
-
const exported = unwrap((mod.$ast.type === "Program" ? mod.$ast.body : []).find((node) => node.type === "ExportDefaultDeclaration")?.declaration);
|
|
993
|
-
if (!exported) return { reason: "no default export found" };
|
|
994
|
-
if (exported.type !== "CallExpression" || exported.callee.type !== "Identifier" || exported.callee.name !== "defineConfig") return { reason: "default export is not a defineConfig(...) call" };
|
|
995
|
-
const argument = unwrap(exported.arguments[0]);
|
|
996
|
-
if (!argument) return { reason: "defineConfig(...) was called without a config" };
|
|
997
|
-
if (argument.type === "ArrayExpression") {
|
|
998
|
-
const configs = [];
|
|
999
|
-
for (const element of argument.elements) {
|
|
1000
|
-
const entry = unwrap(element);
|
|
1001
|
-
if (entry?.type !== "ObjectExpression") return { reason: "config is not an object literal" };
|
|
1002
|
-
configs.push(entry);
|
|
1003
|
-
}
|
|
1004
|
-
return { configs };
|
|
1005
|
-
}
|
|
1006
|
-
if (argument.type !== "ObjectExpression") return { reason: "config is not an object literal" };
|
|
1007
|
-
return { configs: [argument] };
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* The config entry an edit names, defaulting to the first when it names none.
|
|
1011
|
-
*/
|
|
1012
|
-
function selectConfig(configs, ref) {
|
|
1013
|
-
if (ref === void 0) return configs[0];
|
|
1014
|
-
if (typeof ref === "number") return configs[ref];
|
|
1015
|
-
return configs.find((config) => configName(config) === ref);
|
|
1016
|
-
}
|
|
1017
|
-
function configName(config) {
|
|
1018
|
-
const name = property(config, "name");
|
|
1019
|
-
return name?.type === "StringLiteral" ? name.value : void 0;
|
|
1020
|
-
}
|
|
1021
|
-
/**
|
|
1022
|
-
* The name of an object literal property, for the two key shapes a config uses: `key: value` and
|
|
1023
|
-
* `'key': value`. `undefined` for a computed key, which the patcher never touches.
|
|
1024
|
-
*/
|
|
1025
|
-
function propertyKey(entry) {
|
|
1026
|
-
if (entry.key.type === "Identifier") return entry.key.name;
|
|
1027
|
-
if (entry.key.type === "StringLiteral") return entry.key.value;
|
|
1028
|
-
}
|
|
1029
|
-
/**
|
|
1030
|
-
* The index of an object literal's own property named `key`, `-1` when it has none.
|
|
1031
|
-
*/
|
|
1032
|
-
function entryIndex({ node, key }) {
|
|
1033
|
-
return node.properties.findIndex((entry) => entry.type === "ObjectProperty" && propertyKey(entry) === key);
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* The value node of an object literal's own property.
|
|
1037
|
-
*/
|
|
1038
|
-
function property(node, key) {
|
|
1039
|
-
const index = entryIndex({
|
|
1040
|
-
node,
|
|
1041
|
-
key
|
|
1042
|
-
});
|
|
1043
|
-
return index === -1 ? void 0 : node.properties[index].value;
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Writes `key: value` on an object literal, replacing the value when the property is already there.
|
|
1047
|
-
*
|
|
1048
|
-
* An existing property has its value swapped in place rather than being replaced whole, so recast
|
|
1049
|
-
* reprints only that value and leaves the object's own layout alone.
|
|
1050
|
-
*/
|
|
1051
|
-
function setProperty({ node, key, value }) {
|
|
1052
|
-
const entry = literalProperty({
|
|
1053
|
-
key,
|
|
1054
|
-
value
|
|
1055
|
-
});
|
|
1056
|
-
const index = entryIndex({
|
|
1057
|
-
node,
|
|
1058
|
-
key
|
|
1059
|
-
});
|
|
1060
|
-
if (index === -1) {
|
|
1061
|
-
node.properties.push(entry);
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
node.properties[index].value = entry.value;
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Drops `key` from an object literal.
|
|
1068
|
-
*/
|
|
1069
|
-
function removeProperty({ node, key }) {
|
|
1070
|
-
const index = entryIndex({
|
|
1071
|
-
node,
|
|
1072
|
-
key
|
|
1073
|
-
});
|
|
1074
|
-
if (index !== -1) node.properties.splice(index, 1);
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Reads a literal node's value: a primitive, or an object/array built only from primitives.
|
|
1078
|
-
* `undefined` for anything else, so a caller can use this both to read a value and to check
|
|
1079
|
-
* whether a node is a literal at all.
|
|
1080
|
-
*/
|
|
1081
|
-
function readLiteral(node) {
|
|
1082
|
-
if (!node) return;
|
|
1083
|
-
if (node.type === "StringLiteral" || node.type === "NumericLiteral" || node.type === "BooleanLiteral") return node.value;
|
|
1084
|
-
if (node.type === "NullLiteral") return null;
|
|
1085
|
-
if (node.type === "TemplateLiteral") return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? "" : void 0;
|
|
1086
|
-
if (node.type === "UnaryExpression") {
|
|
1087
|
-
const value = readLiteral(node.argument);
|
|
1088
|
-
if (typeof value !== "number") return;
|
|
1089
|
-
if (node.operator === "-") return -value;
|
|
1090
|
-
if (node.operator === "+") return value;
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
if (node.type === "ArrayExpression") {
|
|
1094
|
-
const values = node.elements.map((element) => element ? readLiteral(element) : void 0);
|
|
1095
|
-
return values.every((value) => value !== void 0) ? values : void 0;
|
|
1096
|
-
}
|
|
1097
|
-
if (node.type === "ObjectExpression") {
|
|
1098
|
-
const entries = {};
|
|
1099
|
-
for (const entry of node.properties) {
|
|
1100
|
-
if (entry.type !== "ObjectProperty") return;
|
|
1101
|
-
const key = propertyKey(entry);
|
|
1102
|
-
const value = readLiteral(entry.value);
|
|
1103
|
-
if (key === void 0 || value === void 0) return;
|
|
1104
|
-
entries[key] = value;
|
|
1105
|
-
}
|
|
1106
|
-
return entries;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
/**
|
|
1110
|
-
* Maps a factory identifier in the file back to the module it was imported from.
|
|
1111
|
-
*/
|
|
1112
|
-
function importedFrom(mod) {
|
|
1113
|
-
return new Map(mod.imports.$items.map((item) => [item.local, item.from]));
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Every `pluginX(...)` element of a config's plugins array that resolves to an import.
|
|
1117
|
-
*/
|
|
1118
|
-
function pluginCalls(mod, config) {
|
|
1119
|
-
const plugins = property(config, "plugins");
|
|
1120
|
-
if (plugins?.type !== "ArrayExpression") return [];
|
|
1121
|
-
const imports = importedFrom(mod);
|
|
1122
|
-
return plugins.elements.flatMap((element) => {
|
|
1123
|
-
if (element?.type !== "CallExpression" || element.callee.type !== "Identifier") return [];
|
|
1124
|
-
const packageName = imports.get(element.callee.name);
|
|
1125
|
-
return packageName ? [{
|
|
1126
|
-
importName: element.callee.name,
|
|
1127
|
-
packageName,
|
|
1128
|
-
call: element
|
|
1129
|
-
}] : [];
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Plugins a previous `disable-plugin` commented out of this config, keyed by package name.
|
|
1134
|
-
*
|
|
1135
|
-
* Read from the marker lines rather than the AST, since a commented-out call is no longer a node.
|
|
1136
|
-
*/
|
|
1137
|
-
function disabledMarkers(source) {
|
|
1138
|
-
return source.split("\n").flatMap((line, index) => {
|
|
1139
|
-
const marker = parseMarker(line);
|
|
1140
|
-
return marker ? [{
|
|
1141
|
-
packageName: marker.plugin,
|
|
1142
|
-
line: index + 1
|
|
1143
|
-
}] : [];
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Reads which plugins the file declares and which of their options Studio may write.
|
|
1148
|
-
*
|
|
1149
|
-
* @example
|
|
1150
|
-
* ```ts
|
|
1151
|
-
* const view = readConfig(await readFile('kubb.config.ts', 'utf8'))
|
|
1152
|
-
* if (view.managed) {
|
|
1153
|
-
* view.configs.forEach((config) => console.log(config.name, config.plugins.length))
|
|
1154
|
-
* }
|
|
1155
|
-
* ```
|
|
1156
|
-
*/
|
|
1157
|
-
function readConfig(source) {
|
|
1158
|
-
let mod;
|
|
1159
|
-
try {
|
|
1160
|
-
mod = (0, magicast.parseModule)(source);
|
|
1161
|
-
} catch {
|
|
1162
|
-
return {
|
|
1163
|
-
managed: false,
|
|
1164
|
-
reason: "the config file could not be parsed"
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
const found = findConfigs(mod);
|
|
1168
|
-
if ("reason" in found) return {
|
|
1169
|
-
managed: false,
|
|
1170
|
-
reason: found.reason
|
|
1171
|
-
};
|
|
1172
|
-
const importNames = new Map([...importedFrom(mod)].map(([local, from]) => [from, local]));
|
|
1173
|
-
const disabled = disabledMarkers(source);
|
|
1174
|
-
return {
|
|
1175
|
-
managed: true,
|
|
1176
|
-
configs: found.configs.map((config) => {
|
|
1177
|
-
const plugins = pluginCalls(mod, config).map(({ importName, packageName, call }) => {
|
|
1178
|
-
const entries = {};
|
|
1179
|
-
const options = call.arguments[0];
|
|
1180
|
-
if (options?.type === "ObjectExpression") for (const entry of options.properties) {
|
|
1181
|
-
if (entry.type !== "ObjectProperty") continue;
|
|
1182
|
-
const key = propertyKey(entry);
|
|
1183
|
-
if (key === void 0) continue;
|
|
1184
|
-
const value = readLiteral(entry.value);
|
|
1185
|
-
entries[key] = value === void 0 ? { literal: false } : {
|
|
1186
|
-
literal: true,
|
|
1187
|
-
value
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
return {
|
|
1191
|
-
importName,
|
|
1192
|
-
packageName,
|
|
1193
|
-
options: entries
|
|
1194
|
-
};
|
|
1195
|
-
});
|
|
1196
|
-
const start = config.loc?.start.line ?? 0;
|
|
1197
|
-
const end = config.loc?.end.line ?? Number.POSITIVE_INFINITY;
|
|
1198
|
-
for (const { packageName } of disabled.filter((entry) => entry.line >= start && entry.line <= end)) plugins.push({
|
|
1199
|
-
importName: importNames.get(packageName) ?? toExportName(packageName),
|
|
1200
|
-
packageName,
|
|
1201
|
-
options: {},
|
|
1202
|
-
disabled: true
|
|
1203
|
-
});
|
|
1204
|
-
return {
|
|
1205
|
-
name: configName(config),
|
|
1206
|
-
plugins
|
|
1207
|
-
};
|
|
1208
|
-
})
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
/**
|
|
1212
|
-
* Whether a value can be written into a config file as a literal.
|
|
1213
|
-
*
|
|
1214
|
-
* This is the trust boundary for edits that arrive over the agent WebSocket: a function, `undefined`,
|
|
1215
|
-
* or a non-finite number is refused rather than printed into the user's source.
|
|
1216
|
-
*/
|
|
1217
|
-
function isOptionValue(value) {
|
|
1218
|
-
if (value === null) return true;
|
|
1219
|
-
if (typeof value === "string" || typeof value === "boolean") return true;
|
|
1220
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
1221
|
-
if (Array.isArray(value)) return value.every(isOptionValue);
|
|
1222
|
-
if (typeof value === "object") return Object.values(value).every(isOptionValue);
|
|
1223
|
-
return false;
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* The options object of a plugin call, when it was called with one.
|
|
1227
|
-
*/
|
|
1228
|
-
function getOptions(call) {
|
|
1229
|
-
const options = call.arguments[0];
|
|
1230
|
-
return options?.type === "ObjectExpression" ? options : void 0;
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* The options object of a plugin call, creating an empty one when the plugin was called bare.
|
|
1234
|
-
*/
|
|
1235
|
-
function ensureOptions(call) {
|
|
1236
|
-
if (call.arguments.length === 0) call.arguments.push({
|
|
1237
|
-
type: "ObjectExpression",
|
|
1238
|
-
properties: []
|
|
1239
|
-
});
|
|
1240
|
-
return getOptions(call);
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Walks `path` down to the object holding its last key, descending only through object literals.
|
|
1244
|
-
*/
|
|
1245
|
-
function optionParent(options, path) {
|
|
1246
|
-
let object = options;
|
|
1247
|
-
for (const [index, key] of path.entries()) {
|
|
1248
|
-
if (index === path.length - 1) return {
|
|
1249
|
-
object,
|
|
1250
|
-
key
|
|
1251
|
-
};
|
|
1252
|
-
if (property(object, key) === void 0) setProperty({
|
|
1253
|
-
node: object,
|
|
1254
|
-
key,
|
|
1255
|
-
value: {}
|
|
1256
|
-
});
|
|
1257
|
-
const next = property(object, key);
|
|
1258
|
-
if (next?.type !== "ObjectExpression") return { reason: `${key} is not an object, so ${path.join(".")} cannot be reached` };
|
|
1259
|
-
object = next;
|
|
1260
|
-
}
|
|
1261
|
-
return { reason: "no option path given" };
|
|
1262
|
-
}
|
|
1263
|
-
/**
|
|
1264
|
-
* Writes `value` at `path` inside a plugin call's options, creating the options object and any
|
|
1265
|
-
* intermediate object along the path as needed. Refuses when the current value at `path` is
|
|
1266
|
-
* something other than a literal, so an option customized in code is never overwritten.
|
|
1267
|
-
*/
|
|
1268
|
-
function applySet(call, path, value) {
|
|
1269
|
-
if (!isOptionValue(value)) return "the value is not a literal that can be written to a config file";
|
|
1270
|
-
const options = ensureOptions(call);
|
|
1271
|
-
if (!options) return "the plugin was not called with an object literal";
|
|
1272
|
-
const target = optionParent(options, path);
|
|
1273
|
-
if ("reason" in target) return target.reason;
|
|
1274
|
-
const current = property(target.object, target.key);
|
|
1275
|
-
if (current !== void 0 && readLiteral(current) === void 0) return `${path.join(".")} is customized in code`;
|
|
1276
|
-
setProperty({
|
|
1277
|
-
node: target.object,
|
|
1278
|
-
key: target.key,
|
|
1279
|
-
value
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Deletes the property at `path` inside a plugin call's options, falling the plugin back to its
|
|
1284
|
-
* default for that option. Refuses when the value at `path` is not a literal, for the same reason
|
|
1285
|
-
* `applySet` does.
|
|
1286
|
-
*/
|
|
1287
|
-
function applyRemove(call, path) {
|
|
1288
|
-
const options = getOptions(call);
|
|
1289
|
-
if (!options) return "the plugin has no options to remove";
|
|
1290
|
-
const target = optionParent(options, path);
|
|
1291
|
-
if ("reason" in target) return target.reason;
|
|
1292
|
-
const current = property(target.object, target.key);
|
|
1293
|
-
if (current === void 0) return `${path.join(".")} is not set`;
|
|
1294
|
-
if (readLiteral(current) === void 0) return `${path.join(".")} is customized in code`;
|
|
1295
|
-
removeProperty({
|
|
1296
|
-
node: target.object,
|
|
1297
|
-
key: target.key
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Adds a `pluginX(...)` call to a config's plugins array. Replaying the same add is a no-op, while
|
|
1302
|
-
* an import name collision with an unrelated package remains an error.
|
|
1303
|
-
*/
|
|
1304
|
-
function applyAddPlugin(mod, config, edit) {
|
|
1305
|
-
if (!isKubbPluginSpecifier(edit.plugin)) return { reason: `"${edit.plugin}" is not a @kubb/plugin-* package` };
|
|
1306
|
-
const importName = edit.importName ?? toExportName(edit.plugin);
|
|
1307
|
-
if (!IDENTIFIER.test(importName)) return { reason: `"${importName}" is not a valid import name` };
|
|
1308
|
-
if (pluginCalls(mod, config).some((plugin) => plugin.packageName === edit.plugin)) return { noop: true };
|
|
1309
|
-
const taken = importedFrom(mod).get(importName);
|
|
1310
|
-
if (taken && taken !== edit.plugin) return { reason: `${importName} is already imported from ${taken}` };
|
|
1311
|
-
const options = edit.options ?? {};
|
|
1312
|
-
if (!isOptionValue(options)) return { reason: "the options are not literals that can be written to a config file" };
|
|
1313
|
-
const plugins = property(config, "plugins");
|
|
1314
|
-
if (plugins?.type !== "ArrayExpression") return { reason: "plugins is not an array literal" };
|
|
1315
|
-
const call = Object.keys(options).length ? magicast.builders.functionCall(importName, options) : magicast.builders.functionCall(importName);
|
|
1316
|
-
plugins.elements.push(call.$ast);
|
|
1317
|
-
return taken ? {} : { addImport: {
|
|
1318
|
-
importName,
|
|
1319
|
-
moduleSpecifier: edit.plugin
|
|
1320
|
-
} };
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Comments out a plugin call in place, keeping its options on disk so `enable-plugin` can restore
|
|
1324
|
-
* them exactly. Operates on `source` text rather than the AST: a commented-out call is no longer a
|
|
1325
|
-
* node magicast can address, and the surrounding array must not reflow when its element count
|
|
1326
|
-
* never actually changes.
|
|
1327
|
-
*/
|
|
1328
|
-
function disablePlugin(source, mod, config, plugin) {
|
|
1329
|
-
const target = pluginCalls(mod, config).find((entry) => entry.packageName === plugin);
|
|
1330
|
-
if (!target) return { reason: `${plugin} is not in the plugins array` };
|
|
1331
|
-
const loc = target.call.loc;
|
|
1332
|
-
if (!loc?.start || !loc.end) return { reason: `${plugin} has no source location to comment out` };
|
|
1333
|
-
const lines = source.split("\n");
|
|
1334
|
-
const from = loc.start.line - 1;
|
|
1335
|
-
const to = loc.end.line - 1;
|
|
1336
|
-
const firstLine = lines[from] ?? "";
|
|
1337
|
-
const lastLine = lines[to] ?? "";
|
|
1338
|
-
if (firstLine.slice(0, loc.start.column).trim() !== "" || !/^,?\s*$/.test(lastLine.slice(loc.end.column))) return { reason: `${plugin} shares a line with other code, so it cannot be commented out safely` };
|
|
1339
|
-
const indent = firstLine.match(/^\s*/)?.[0] ?? "";
|
|
1340
|
-
const commented = lines.slice(from, to + 1).map((line) => line.trim() ? `${indent}// ${line.slice(indent.length)}` : indent ? `${indent}//` : "//");
|
|
1341
|
-
lines.splice(from, to - from + 1, formatMarker(plugin, commented.length, indent), ...commented);
|
|
1342
|
-
return { source: lines.join("\n") };
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Uncomments the block a previous `disable-plugin` left behind for `plugin`.
|
|
1346
|
-
*/
|
|
1347
|
-
function enablePlugin(source, plugin) {
|
|
1348
|
-
const lines = source.split("\n");
|
|
1349
|
-
for (const [index, line] of lines.entries()) {
|
|
1350
|
-
const marker = parseMarker(line);
|
|
1351
|
-
if (marker?.plugin !== plugin) continue;
|
|
1352
|
-
const end = index + 1 + marker.lineCount;
|
|
1353
|
-
const restored = lines.slice(index + 1, end).map((commented) => commented.replace(/^(\s*)\/\/ ?/, "$1"));
|
|
1354
|
-
lines.splice(index, end - index, ...restored);
|
|
1355
|
-
return { source: lines.join("\n") };
|
|
1356
|
-
}
|
|
1357
|
-
return { reason: `${plugin} is not disabled` };
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Re-parses `source` and resolves the config entry an edit targets. Every edit re-parses rather
|
|
1361
|
-
* than sharing one module across the batch, since the disable/enable edits rewrite `source` as
|
|
1362
|
-
* text and would otherwise leave the others working from a stale tree.
|
|
1363
|
-
*/
|
|
1364
|
-
function parseTarget(source, ref) {
|
|
1365
|
-
let mod;
|
|
1366
|
-
try {
|
|
1367
|
-
mod = (0, magicast.parseModule)(source);
|
|
1368
|
-
} catch {
|
|
1369
|
-
return { reason: "the config file could not be parsed" };
|
|
1370
|
-
}
|
|
1371
|
-
const found = findConfigs(mod);
|
|
1372
|
-
if ("reason" in found) return { reason: found.reason };
|
|
1373
|
-
const config = selectConfig(found.configs, ref);
|
|
1374
|
-
if (!config) return { reason: `no config entry found for ${JSON.stringify(ref)}` };
|
|
1375
|
-
return {
|
|
1376
|
-
mod,
|
|
1377
|
-
config
|
|
1378
|
-
};
|
|
1379
|
-
}
|
|
1380
|
-
/**
|
|
1381
|
-
* Applies edits to a `kubb.config.ts` in place. Every node the edits do not touch keeps its
|
|
1382
|
-
* original text, so comments, formatting, and hand-written code around the config survive.
|
|
1383
|
-
*
|
|
1384
|
-
* Edits are independent: one that cannot be applied is reported in `outcomes` and the rest still run.
|
|
1385
|
-
*
|
|
1386
|
-
* @example
|
|
1387
|
-
* ```ts
|
|
1388
|
-
* const { source, outcomes } = applyConfigEdits(current, [
|
|
1389
|
-
* { operation: 'set', plugin: '@kubb/plugin-ts', path: ['enum', 'type'], value: 'enum' },
|
|
1390
|
-
* ])
|
|
1391
|
-
* ```
|
|
1392
|
-
*
|
|
1393
|
-
* @note recast always reprints a semicolon on a reprinted statement, so editing a block-body
|
|
1394
|
-
* `defineConfig` in a semicolon-free file adds one to the `return` line. This is a known gap.
|
|
1395
|
-
* Strip it when `detectCodeFormat` reports `useSemi: false`, if it turns out to matter in practice.
|
|
1396
|
-
*/
|
|
1397
|
-
function applyConfigEdits(source, edits) {
|
|
1398
|
-
let current = source;
|
|
1399
|
-
const format = (0, magicast.detectCodeFormat)(source);
|
|
1400
|
-
const endsWithNewline = source.endsWith("\n");
|
|
1401
|
-
const outcomes = edits.map((edit) => {
|
|
1402
|
-
const target = parseTarget(current, edit.config);
|
|
1403
|
-
if ("reason" in target) return {
|
|
1404
|
-
edit,
|
|
1405
|
-
applied: false,
|
|
1406
|
-
reason: target.reason
|
|
1407
|
-
};
|
|
1408
|
-
const { mod, config } = target;
|
|
1409
|
-
if (edit.operation === "disable-plugin" || edit.operation === "enable-plugin") {
|
|
1410
|
-
const result = edit.operation === "disable-plugin" ? disablePlugin(current, mod, config, edit.plugin) : enablePlugin(current, edit.plugin);
|
|
1411
|
-
if ("reason" in result) return {
|
|
1412
|
-
edit,
|
|
1413
|
-
applied: false,
|
|
1414
|
-
reason: result.reason
|
|
1415
|
-
};
|
|
1416
|
-
current = result.source;
|
|
1417
|
-
return {
|
|
1418
|
-
edit,
|
|
1419
|
-
applied: true
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
if (edit.operation === "add-plugin") {
|
|
1423
|
-
const result = applyAddPlugin(mod, config, edit);
|
|
1424
|
-
if ("reason" in result) return {
|
|
1425
|
-
edit,
|
|
1426
|
-
applied: false,
|
|
1427
|
-
reason: result.reason
|
|
1428
|
-
};
|
|
1429
|
-
if ("noop" in result) return {
|
|
1430
|
-
edit,
|
|
1431
|
-
applied: true
|
|
1432
|
-
};
|
|
1433
|
-
const afterLine = lastImportEndLine(mod);
|
|
1434
|
-
let next = (0, magicast.generateCode)(mod, { format }).code;
|
|
1435
|
-
if (result.addImport) next = insertImportLine({
|
|
1436
|
-
source: next,
|
|
1437
|
-
afterLine,
|
|
1438
|
-
...result.addImport
|
|
1439
|
-
});
|
|
1440
|
-
current = withTrailingNewline(next, endsWithNewline);
|
|
1441
|
-
return {
|
|
1442
|
-
edit,
|
|
1443
|
-
applied: true
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
const pluginCall = pluginCalls(mod, config).find((plugin) => plugin.packageName === edit.plugin);
|
|
1447
|
-
if (!pluginCall) return {
|
|
1448
|
-
edit,
|
|
1449
|
-
applied: false,
|
|
1450
|
-
reason: `${edit.plugin} is not in the plugins array`
|
|
1451
|
-
};
|
|
1452
|
-
const reason = edit.operation === "set" ? applySet(pluginCall.call, edit.path, edit.value) : applyRemove(pluginCall.call, edit.path);
|
|
1453
|
-
if (!reason) current = withTrailingNewline((0, magicast.generateCode)(mod, { format }).code, endsWithNewline);
|
|
1454
|
-
return {
|
|
1455
|
-
edit,
|
|
1456
|
-
applied: !reason,
|
|
1457
|
-
reason
|
|
1458
|
-
};
|
|
1459
|
-
});
|
|
1460
|
-
return {
|
|
1461
|
-
source: current,
|
|
1462
|
-
outcomes,
|
|
1463
|
-
changed: current !== source
|
|
1464
|
-
};
|
|
1465
|
-
}
|
|
1466
|
-
async function writeConfigEdits({ filePath, edits }) {
|
|
1467
|
-
const result = applyConfigEdits(await read(filePath), edits);
|
|
1468
|
-
if (result.changed) await (0, node_fs_promises.writeFile)(filePath, result.source, "utf-8");
|
|
1469
|
-
return result;
|
|
1470
|
-
}
|
|
1471
|
-
/**
|
|
1472
|
-
* The 1-based line where the file's last import declaration ends, or `0` when it has none. Read
|
|
1473
|
-
* off the parsed module, so a multi-line `import {\n x,\n} from '...'` reports its closing line
|
|
1474
|
-
* rather than the `import` keyword.
|
|
1475
|
-
*/
|
|
1476
|
-
function lastImportEndLine(mod) {
|
|
1477
|
-
return (mod.$ast.type === "Program" ? mod.$ast.body : []).filter((node) => node.type === "ImportDeclaration").at(-1)?.loc?.end.line ?? 0;
|
|
1478
|
-
}
|
|
1479
|
-
/**
|
|
1480
|
-
* Writes an import after the last one already in the file, matching its quote style and whether it
|
|
1481
|
-
* ends in a semicolon. `afterLine` is where that last import ends, `0` for a file with none.
|
|
1482
|
-
*
|
|
1483
|
-
* Written as plain text rather than through magicast's import builder, which prints a brand-new
|
|
1484
|
-
* import declaration with its own default spacing and a semicolon regardless of `format`, since
|
|
1485
|
-
* that formatting only governs nodes recast can diff against the original source.
|
|
1486
|
-
*/
|
|
1487
|
-
function insertImportLine({ source, importName, moduleSpecifier, afterLine }) {
|
|
1488
|
-
const lines = source.split("\n");
|
|
1489
|
-
const lastImportLine = afterLine > 0 ? lines[afterLine - 1] : void 0;
|
|
1490
|
-
const quote = lastImportLine?.includes(`"`) ? `"` : `'`;
|
|
1491
|
-
const line = `import { ${importName} } from ${quote}${moduleSpecifier}${quote}${lastImportLine?.trimEnd().endsWith(";") ? ";" : ""}`;
|
|
1492
|
-
lines.splice(afterLine, 0, ...afterLine > 0 ? [line] : [line, ""]);
|
|
1493
|
-
return lines.join("\n");
|
|
1494
|
-
}
|
|
1495
|
-
/**
|
|
1496
|
-
* `generateCode` always drops the file's trailing newline. Restore it when the input had one.
|
|
1497
|
-
*/
|
|
1498
|
-
function withTrailingNewline(code, hadTrailingNewline) {
|
|
1499
|
-
if (!hadTrailingNewline || code.endsWith("\n")) return code;
|
|
1500
|
-
return `${code}\n`;
|
|
1501
|
-
}
|
|
1502
|
-
//#endregion
|
|
1503
|
-
//#region src/operations/snapshotPackage.ts
|
|
1504
|
-
const gzipAsync = (0, node_util.promisify)(node_zlib.gzip);
|
|
1505
|
-
const UPLOAD_TIMEOUT_MS = 12e4;
|
|
1506
|
-
/**
|
|
1507
|
-
* Maps a generated file's path to its place inside the tarball, stripping everything before a
|
|
1508
|
-
* `src`/`dist` segment and any `..`/empty path segment so a crafted file name cannot escape the
|
|
1509
|
-
* `package/` root.
|
|
1510
|
-
*/
|
|
1511
|
-
function packagePath(filePath) {
|
|
1512
|
-
const normalized = filePath.replaceAll("\\", "/");
|
|
1513
|
-
return `package/${(normalized.match(/\/(?:src|dist)\/.*$/)?.[0].slice(1) ?? normalized.replace(/^\/+/, "")).split("/").filter((part) => part && part !== "." && part !== "..").join("/")}`;
|
|
1514
|
-
}
|
|
1515
|
-
/**
|
|
1516
|
-
* Splits a tarball entry path into the legacy 100-byte `name` field and, when the path does not
|
|
1517
|
-
* fit, the 155-byte USTAR `prefix` field that extends it. Throws rather than silently truncating
|
|
1518
|
-
* a path the format cannot address (max 256 bytes: 100 name + 1 separator + 155 prefix).
|
|
1519
|
-
*/
|
|
1520
|
-
function splitEntryPath(path) {
|
|
1521
|
-
if (Buffer.byteLength(path, "utf8") <= 100) return {
|
|
1522
|
-
name: path,
|
|
1523
|
-
prefix: ""
|
|
1524
|
-
};
|
|
1525
|
-
for (let i = path.length - 1; i >= 0; i--) {
|
|
1526
|
-
if (path[i] !== "/") continue;
|
|
1527
|
-
const prefix = path.slice(0, i);
|
|
1528
|
-
const name = path.slice(i + 1);
|
|
1529
|
-
if (Buffer.byteLength(prefix, "utf8") <= 155 && Buffer.byteLength(name, "utf8") <= 100) return {
|
|
1530
|
-
name,
|
|
1531
|
-
prefix
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
throw new Error(`Snapshot path is too long for a tar entry: ${path}`);
|
|
1535
|
-
}
|
|
1536
|
-
function header(path, size) {
|
|
1537
|
-
const { name, prefix } = splitEntryPath(path);
|
|
1538
|
-
const value = Buffer.alloc(512);
|
|
1539
|
-
value.write(name, 0, "utf8");
|
|
1540
|
-
value.write("0000644\0", 100, "ascii");
|
|
1541
|
-
value.write("0000000\0", 108, "ascii");
|
|
1542
|
-
value.write("0000000\0", 116, "ascii");
|
|
1543
|
-
value.write(`${size.toString(8).padStart(11, "0")}\0`, 124, "ascii");
|
|
1544
|
-
value.write(`${Math.floor(Date.now() / 1e3).toString(8).padStart(11, "0")}\0`, 136, "ascii");
|
|
1545
|
-
value.fill(32, 148, 156);
|
|
1546
|
-
value.write("0", 156, "ascii");
|
|
1547
|
-
value.write("ustar\0", 257, "ascii");
|
|
1548
|
-
value.write("00", 263, "ascii");
|
|
1549
|
-
value.write("0000000\0", 265, "ascii");
|
|
1550
|
-
value.write("0000000\0", 297, "ascii");
|
|
1551
|
-
value.write(prefix, 345, "utf8");
|
|
1552
|
-
const checksum = [...value].reduce((sum, byte) => sum + byte, 0);
|
|
1553
|
-
value.write(`${checksum.toString(8).padStart(6, "0")}\0 `, 148, "ascii");
|
|
1554
|
-
return value;
|
|
1555
|
-
}
|
|
1556
|
-
/**
|
|
1557
|
-
* Packs a generation's files into a gzipped, npm-installable tarball: a `package/` root with the
|
|
1558
|
-
* generated sources, a `tsdown`-built `dist/` (esm + cjs), and a `package.json` manifest.
|
|
1559
|
-
*/
|
|
1560
|
-
async function createSnapshotPackage(files, packageInfo) {
|
|
1561
|
-
const root = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "kubb-snapshot-"));
|
|
1562
|
-
const dist = (0, node_path.join)(root, "dist");
|
|
1563
|
-
const resolvedPaths = Object.entries(files).map(([name, content]) => ({
|
|
1564
|
-
name,
|
|
1565
|
-
content,
|
|
1566
|
-
target: packagePath(name)
|
|
1567
|
-
}));
|
|
1568
|
-
const targetOwners = /* @__PURE__ */ new Map();
|
|
1569
|
-
for (const { name, target } of resolvedPaths) {
|
|
1570
|
-
const owner = targetOwners.get(target);
|
|
1571
|
-
if (owner) throw new Error(`Snapshot has two generated files that sanitize to the same path "${target}": "${owner}" and "${name}"`);
|
|
1572
|
-
targetOwners.set(target, name);
|
|
1573
|
-
}
|
|
1574
|
-
try {
|
|
1575
|
-
await (0, node_fs_promises.mkdir)(dist);
|
|
1576
|
-
await Promise.all(resolvedPaths.map(async ({ content, target }) => {
|
|
1577
|
-
const path = (0, node_path.join)(root, target.slice(8));
|
|
1578
|
-
await (0, node_fs_promises.mkdir)((0, node_path.join)(path, ".."), { recursive: true });
|
|
1579
|
-
await (0, node_fs_promises.writeFile)(path, content);
|
|
1580
|
-
}));
|
|
1581
|
-
const sourceEntries = resolvedPaths.filter(({ name }) => /\.(?:[cm]?[jt]sx?)$/.test(name)).map(({ target }) => (0, node_path.join)(root, target.slice(8)));
|
|
1582
|
-
if (sourceEntries.length) await (0, tsdown.build)({
|
|
1583
|
-
entry: sourceEntries,
|
|
1584
|
-
outDir: dist,
|
|
1585
|
-
format: ["esm", "cjs"],
|
|
1586
|
-
dts: false,
|
|
1587
|
-
sourcemap: false,
|
|
1588
|
-
unbundle: true,
|
|
1589
|
-
report: false,
|
|
1590
|
-
logLevel: "silent",
|
|
1591
|
-
fixedExtension: true
|
|
1592
|
-
});
|
|
1593
|
-
const builtEntries = await Promise.all((await Array.fromAsync((0, node_fs_promises.glob)("**/*", {
|
|
1594
|
-
cwd: dist,
|
|
1595
|
-
withFileTypes: true
|
|
1596
|
-
}))).filter((entry) => entry.isFile()).map(async (entry) => {
|
|
1597
|
-
const filePath = (0, node_path.join)(entry.parentPath, entry.name);
|
|
1598
|
-
return [`package/dist/${(0, node_path.relative)(dist, filePath).split(node_path.sep).join("/")}`, await (0, node_fs_promises.readFile)(filePath, "utf8")];
|
|
1599
|
-
}));
|
|
1600
|
-
const builtPaths = new Set(builtEntries.map(([path]) => path));
|
|
1601
|
-
const hasBarrel = builtPaths.has("package/dist/index.mjs") && builtPaths.has("package/dist/index.cjs");
|
|
1602
|
-
const barrelFields = hasBarrel ? {
|
|
1603
|
-
main: "./dist/index.cjs",
|
|
1604
|
-
module: "./dist/index.mjs"
|
|
1605
|
-
} : {};
|
|
1606
|
-
const barrelExport = hasBarrel ? { ".": {
|
|
1607
|
-
import: "./dist/index.mjs",
|
|
1608
|
-
require: "./dist/index.cjs"
|
|
1609
|
-
} } : {};
|
|
1610
|
-
const entries = {
|
|
1611
|
-
"package/package.json": JSON.stringify({
|
|
1612
|
-
...packageInfo,
|
|
1613
|
-
type: "module",
|
|
1614
|
-
...barrelFields,
|
|
1615
|
-
exports: {
|
|
1616
|
-
...barrelExport,
|
|
1617
|
-
"./*": {
|
|
1618
|
-
import: "./dist/*.mjs",
|
|
1619
|
-
require: "./dist/*.cjs"
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}, null, 2),
|
|
1623
|
-
...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),
|
|
1624
|
-
...Object.fromEntries(builtEntries)
|
|
1625
|
-
};
|
|
1626
|
-
const chunks = [];
|
|
1627
|
-
for (const [name, content] of Object.entries(entries)) {
|
|
1628
|
-
const bytes = Buffer.from(content);
|
|
1629
|
-
chunks.push(header(name, bytes.length), bytes, Buffer.alloc((512 - bytes.length % 512) % 512));
|
|
1630
|
-
}
|
|
1631
|
-
const bytes = await gzipAsync(Buffer.concat([...chunks, Buffer.alloc(1024)]));
|
|
1632
|
-
return {
|
|
1633
|
-
bytes,
|
|
1634
|
-
integrity: `sha512-${(0, node_crypto.createHash)("sha512").update(bytes).digest("base64")}`
|
|
1635
|
-
};
|
|
1636
|
-
} finally {
|
|
1637
|
-
await (0, node_fs_promises.rm)(root, {
|
|
1638
|
-
recursive: true,
|
|
1639
|
-
force: true
|
|
1640
|
-
});
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
async function uploadSnapshot({ bytes, uploadPath, studioUrl, token, shutdown }) {
|
|
1644
|
-
const uploadUrl = new URL(uploadPath, studioUrl);
|
|
1645
|
-
if (uploadUrl.origin !== new URL(studioUrl).origin) throw new Error("Snapshot upload path must stay on the Studio origin");
|
|
1646
|
-
const timeout = AbortSignal.timeout(UPLOAD_TIMEOUT_MS);
|
|
1647
|
-
const signal = shutdown ? AbortSignal.any([shutdown, timeout]) : timeout;
|
|
1648
|
-
const redirect = await fetch(uploadUrl, {
|
|
1649
|
-
method: "PUT",
|
|
1650
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
1651
|
-
redirect: "manual",
|
|
1652
|
-
signal
|
|
1653
|
-
});
|
|
1654
|
-
const storageUrl = redirect.headers.get("location");
|
|
1655
|
-
if (redirect.status !== 307 || !storageUrl) throw new Error(`Studio did not provide a storage URL (status ${redirect.status})`);
|
|
1656
|
-
const storage = new URL(storageUrl);
|
|
1657
|
-
if (storage.protocol !== "https:" && storage.hostname !== "localhost" && storage.hostname !== "127.0.0.1") throw new Error(`Refusing snapshot upload to ${storage.origin}`);
|
|
1658
|
-
const response = await fetch(storage, {
|
|
1659
|
-
method: "PUT",
|
|
1660
|
-
body: new Uint8Array(bytes),
|
|
1661
|
-
redirect: "error",
|
|
1662
|
-
signal
|
|
1663
|
-
});
|
|
1664
|
-
if (!response.ok) throw new Error(`Snapshot upload failed with status ${response.status}`);
|
|
1665
|
-
}
|
|
586
|
+
var version = "5.5.4";
|
|
1666
587
|
//#endregion
|
|
1667
588
|
//#region src/operations/generations.ts
|
|
1668
589
|
const READ_CONCURRENCY = 50;
|
|
@@ -1803,7 +724,7 @@ function relativeStoragePath(root, filePath) {
|
|
|
1803
724
|
return ((0, node_path.isAbsolute)(filePath) ? (0, node_path.relative)((0, node_path.resolve)(root), filePath) : filePath).replaceAll("\\", "/");
|
|
1804
725
|
}
|
|
1805
726
|
async function resolvePeerDependencies(names) {
|
|
1806
|
-
const uniqueNames = [...new Set(names.map(toPackageName))];
|
|
727
|
+
const uniqueNames = [...new Set(names.map(require_resolveConfig.toPackageName))];
|
|
1807
728
|
const peerDependencies = {};
|
|
1808
729
|
const missingDependencies = [];
|
|
1809
730
|
const versions = await Promise.all(uniqueNames.map(async (name) => {
|
|
@@ -2645,7 +1566,8 @@ var StudioSession = class {
|
|
|
2645
1566
|
async #readConfigFileView(source) {
|
|
2646
1567
|
if (!this.#canEditConfig) return;
|
|
2647
1568
|
try {
|
|
2648
|
-
|
|
1569
|
+
const { readConfig } = await Promise.resolve().then(() => require("./configFile-BTVeov27.cjs"));
|
|
1570
|
+
return readConfig(source ?? await require_resolveConfig.read(this.#options.configFile));
|
|
2649
1571
|
} catch (error) {
|
|
2650
1572
|
await this.#warn(`Could not read ${this.#options.configFile}: ${getErrorMessage(error)}`);
|
|
2651
1573
|
return;
|
|
@@ -2664,7 +1586,7 @@ var StudioSession = class {
|
|
|
2664
1586
|
path: configPath,
|
|
2665
1587
|
file,
|
|
2666
1588
|
plugins: config.plugins.map((plugin) => ({
|
|
2667
|
-
name: toPackageName(plugin.name),
|
|
1589
|
+
name: require_resolveConfig.toPackageName(plugin.name),
|
|
2668
1590
|
options: plugin.options ?? {}
|
|
2669
1591
|
}))
|
|
2670
1592
|
},
|
|
@@ -2757,8 +1679,8 @@ var StudioSession = class {
|
|
|
2757
1679
|
await this.#hooks.callHook("studio:command:start", { command });
|
|
2758
1680
|
const config = await loadConfig();
|
|
2759
1681
|
const patch = data.config;
|
|
2760
|
-
const plugins = await mergePlugins(config.plugins, patch?.plugins);
|
|
2761
|
-
const adapter = await mergeAdapter(config.adapter, patch?.adapter);
|
|
1682
|
+
const plugins = await require_resolveConfig.mergePlugins(config.plugins, patch?.plugins);
|
|
1683
|
+
const adapter = await require_resolveConfig.mergeAdapter(config.adapter, patch?.adapter);
|
|
2762
1684
|
const inputOverride = this.#isSandbox ? patch?.input ?? "" : void 0;
|
|
2763
1685
|
if (permissions.allowWrite && this.#isSandbox) await this.#warn("Running in a sandbox, so writing files is disabled");
|
|
2764
1686
|
if (patch?.input && !this.#isSandbox) await this.#warn("Ignored the spec from Studio: generating from a Studio spec is only available to a sandbox agent");
|
|
@@ -2817,6 +1739,7 @@ var StudioSession = class {
|
|
|
2817
1739
|
disk: disk ? { hashes: disk.hashes } : void 0
|
|
2818
1740
|
};
|
|
2819
1741
|
} finally {
|
|
1742
|
+
this.#lastGeneration = void 0;
|
|
2820
1743
|
if (root !== this.#options.root) await removeJobRoot(root);
|
|
2821
1744
|
this.#isGenerating = false;
|
|
2822
1745
|
}
|
|
@@ -2847,6 +1770,7 @@ var StudioSession = class {
|
|
|
2847
1770
|
}
|
|
2848
1771
|
if (this.#isGenerating) return refuse("a generation is in progress");
|
|
2849
1772
|
try {
|
|
1773
|
+
const { writeConfigEdits } = await Promise.resolve().then(() => require("./configFile-BTVeov27.cjs"));
|
|
2850
1774
|
const { source: patched, outcomes, changed } = await writeConfigEdits({
|
|
2851
1775
|
filePath: configFile,
|
|
2852
1776
|
edits
|
|
@@ -2878,6 +1802,7 @@ var StudioSession = class {
|
|
|
2878
1802
|
const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency));
|
|
2879
1803
|
if (missing.length) return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(", ")}`, `Missing dependencies: ${missing.join(", ")}`);
|
|
2880
1804
|
try {
|
|
1805
|
+
const { createSnapshotPackage, uploadSnapshot } = await Promise.resolve().then(() => require("./snapshotPackage-BwM3LFmZ.cjs"));
|
|
2881
1806
|
const { files } = await this.#generations.read({
|
|
2882
1807
|
generation,
|
|
2883
1808
|
source: "output",
|