@kubb/cli 5.14.5 → 5.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kubb.js +2 -1
- package/dist/{Telemetry-Dc79_cIA.js → Telemetry-B3edcFoB.js} +12 -86
- package/dist/Telemetry-B3edcFoB.js.map +1 -0
- package/dist/{Telemetry-Dc9DVxKQ.cjs → Telemetry-COWGqYmh.cjs} +14 -118
- package/dist/Telemetry-COWGqYmh.cjs.map +1 -0
- package/dist/{background-v5pDy3IJ.cjs → background-B24lIehn.cjs} +5 -6
- package/dist/{background-v5pDy3IJ.cjs.map → background-B24lIehn.cjs.map} +1 -1
- package/dist/{background-C877JFxE.js → background-D-7OGjvg.js} +1 -1
- package/dist/{background-3_KudPH-.js → background-_dKcGjM7.js} +5 -6
- package/dist/{background-3_KudPH-.js.map → background-_dKcGjM7.js.map} +1 -1
- package/dist/banner-BdTTDFkG.js +140 -0
- package/dist/banner-BdTTDFkG.js.map +1 -0
- package/dist/banner-wJFk-L-c.cjs +163 -0
- package/dist/banner-wJFk-L-c.cjs.map +1 -0
- package/dist/config-BL3HnFhb.js +1 -0
- package/dist/config-BWj9jBVj.cjs +62 -0
- package/dist/config-BWj9jBVj.cjs.map +1 -0
- package/dist/{config-CNqgDSu3.js → config-CQTxXvIG.js} +6 -52
- package/dist/config-CQTxXvIG.js.map +1 -0
- package/dist/{config-BG_Nr2CZ.cjs → config-CRrmj88c.cjs} +15 -62
- package/dist/config-CRrmj88c.cjs.map +1 -0
- package/dist/config-Uk3rbZfb.js +49 -0
- package/dist/config-Uk3rbZfb.js.map +1 -0
- package/dist/configFile-C0DIXNMD.js +993 -0
- package/dist/configFile-C0DIXNMD.js.map +1 -0
- package/dist/configFile-CoEpHdXG.cjs +1028 -0
- package/dist/configFile-CoEpHdXG.cjs.map +1 -0
- package/dist/{credentials-C8tjAhTo.js → credentials-B77iJx9H.js} +1 -1
- package/dist/{credentials-DDaKqtJn.cjs → credentials-DEsGwcGD.cjs} +3 -4
- package/dist/{credentials-DDaKqtJn.cjs.map → credentials-DEsGwcGD.cjs.map} +1 -1
- package/dist/{credentials-CZK6zPLv.js → credentials-osgjujE_.js} +3 -4
- package/dist/{credentials-CZK6zPLv.js.map → credentials-osgjujE_.js.map} +1 -1
- package/dist/env-C3xHRMez.js +1 -0
- package/dist/env-eu3XyKqJ.cjs +55 -0
- package/dist/env-eu3XyKqJ.cjs.map +1 -0
- package/dist/env-rv-VgSGN.js +34 -0
- package/dist/env-rv-VgSGN.js.map +1 -0
- package/dist/{errors-Cl69p_vI.cjs → errors-BponpdjN.cjs} +3 -4
- package/dist/{errors-Cl69p_vI.cjs.map → errors-BponpdjN.cjs.map} +1 -1
- package/dist/{errors-DmC83ALX.js → errors-wzqmMpMo.js} +3 -4
- package/dist/{errors-DmC83ALX.js.map → errors-wzqmMpMo.js.map} +1 -1
- package/dist/index.cjs +37 -2719
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +35 -2713
- package/dist/index.js.map +1 -1
- package/dist/installWithSpinner-DpnpxHq5.js +49 -0
- package/dist/installWithSpinner-DpnpxHq5.js.map +1 -0
- package/dist/installWithSpinner-ONGfIVUJ.cjs +56 -0
- package/dist/installWithSpinner-ONGfIVUJ.cjs.map +1 -0
- package/dist/{text-CEyJhBnh.js → intro-9PB8sE8z.js} +8 -52
- package/dist/intro-9PB8sE8z.js.map +1 -0
- package/dist/{text-CC2xdpGL.cjs → intro-YPaf1Imr.cjs} +12 -80
- package/dist/intro-YPaf1Imr.cjs.map +1 -0
- package/dist/isCIEnvironment-D6xtAAZ2.js +14 -0
- package/dist/isCIEnvironment-D6xtAAZ2.js.map +1 -0
- package/dist/isCIEnvironment-DiVOMI5Q.cjs +20 -0
- package/dist/isCIEnvironment-DiVOMI5Q.cjs.map +1 -0
- package/dist/mascot--q1xjKw1.js +63 -0
- package/dist/mascot--q1xjKw1.js.map +1 -0
- package/dist/mascot-JSA0j3Q_.cjs +80 -0
- package/dist/mascot-JSA0j3Q_.cjs.map +1 -0
- package/dist/mcp-CYPOFKnP.cjs +20 -0
- package/dist/mcp-CYPOFKnP.cjs.map +1 -0
- package/dist/mcp-CxeZR5vb.js +21 -0
- package/dist/mcp-CxeZR5vb.js.map +1 -0
- package/dist/package-D3Yss9Bb.js +5 -0
- package/dist/package-D3Yss9Bb.js.map +1 -0
- package/dist/package-aX0q9SZr.cjs +10 -0
- package/dist/package-aX0q9SZr.cjs.map +1 -0
- package/dist/{registry-ov1RYcD4.js → registry-B3eYk4LN.js} +7 -67
- package/dist/registry-B3eYk4LN.js.map +1 -0
- package/dist/{registry-BInbtBm7.cjs → registry-YE1pFWL-.cjs} +4 -82
- package/dist/registry-YE1pFWL-.cjs.map +1 -0
- package/dist/{reporters-DQz--AdE.cjs → reporters-2cgjvXwR.cjs} +32 -40
- package/dist/{reporters-DQz--AdE.cjs.map → reporters-2cgjvXwR.cjs.map} +1 -1
- package/dist/{reporters-BtD41K-C.js → reporters-BM430HdQ.js} +19 -27
- package/dist/{reporters-BtD41K-C.js.map → reporters-BM430HdQ.js.map} +1 -1
- package/dist/{rolldown-runtime-CPE0t6oh.cjs → rolldown-runtime-C3-Zg-KD.cjs} +1 -2
- package/dist/{rolldown-runtime-CRm0XQPb.js → rolldown-runtime-xo6NMXYd.js} +1 -2
- package/dist/run-1H9TGT8v.js +77 -0
- package/dist/run-1H9TGT8v.js.map +1 -0
- package/dist/run-B4zvF9WY.cjs +233 -0
- package/dist/run-B4zvF9WY.cjs.map +1 -0
- package/dist/run-BYP_t9dR.cjs +324 -0
- package/dist/run-BYP_t9dR.cjs.map +1 -0
- package/dist/{run-1oXPIkli.cjs → run-Bb19PlwV.cjs} +44 -37
- package/dist/run-Bb19PlwV.cjs.map +1 -0
- package/dist/{run-B03V0Poj.cjs → run-BoO7H8RA.cjs} +49 -41
- package/dist/run-BoO7H8RA.cjs.map +1 -0
- package/dist/run-BrM7-NSV.js +181 -0
- package/dist/run-BrM7-NSV.js.map +1 -0
- package/dist/{run-UsAMlUyy.js → run-C0KI-NE3.js} +9 -9
- package/dist/{run-UsAMlUyy.js.map → run-C0KI-NE3.js.map} +1 -1
- package/dist/run-CCLPt4hr.js +1 -0
- package/dist/run-CCsdLz3X.js +385 -0
- package/dist/run-CCsdLz3X.js.map +1 -0
- package/dist/{run-YMSJnvYl.js → run-CbX6tpqB.js} +31 -23
- package/dist/run-CbX6tpqB.js.map +1 -0
- package/dist/run-CrM4jsix.cjs +80 -0
- package/dist/run-CrM4jsix.cjs.map +1 -0
- package/dist/run-CvFakE3B.cjs +185 -0
- package/dist/run-CvFakE3B.cjs.map +1 -0
- package/dist/run-D-nn9-Wq.js +319 -0
- package/dist/run-D-nn9-Wq.js.map +1 -0
- package/dist/{run-cBSr2F1q.cjs → run-D5Z6nfzK.cjs} +5 -6
- package/dist/{run-cBSr2F1q.cjs.map → run-D5Z6nfzK.cjs.map} +1 -1
- package/dist/{run-BUzdEUaE.cjs → run-DCqmf5W1.cjs} +10 -10
- package/dist/{run-BUzdEUaE.cjs.map → run-DCqmf5W1.cjs.map} +1 -1
- package/dist/{run-BK-A9JDf.js → run-DFXhBS64.js} +5 -6
- package/dist/{run-BK-A9JDf.js.map → run-DFXhBS64.js.map} +1 -1
- package/dist/run-DgGZOC_x.cjs +389 -0
- package/dist/run-DgGZOC_x.cjs.map +1 -0
- package/dist/{run-DDwdeHgU.js → run-KqdudiWQ.js} +26 -20
- package/dist/run-KqdudiWQ.js.map +1 -0
- package/dist/run-RN_31Q4u.js +228 -0
- package/dist/run-RN_31Q4u.js.map +1 -0
- package/dist/shared-C0EtrdUm.cjs +123 -0
- package/dist/shared-C0EtrdUm.cjs.map +1 -0
- package/dist/shared-Ct9stKpp.js +94 -0
- package/dist/shared-Ct9stKpp.js.map +1 -0
- package/dist/{snapshot-BrwynBfC.cjs → snapshot-D7Ytw8S0.cjs} +13 -14
- package/dist/{snapshot-BrwynBfC.cjs.map → snapshot-D7Ytw8S0.cjs.map} +1 -1
- package/dist/{snapshot-D2E7nhEf.js → snapshot-n7dJGCWv.js} +10 -11
- package/dist/{snapshot-D2E7nhEf.js.map → snapshot-n7dJGCWv.js.map} +1 -1
- package/dist/spinner-BZaln7O8.js +160 -0
- package/dist/spinner-BZaln7O8.js.map +1 -0
- package/dist/spinner-CfB0sIPy.cjs +208 -0
- package/dist/spinner-CfB0sIPy.cjs.map +1 -0
- package/dist/{status-CxzhvjKs.cjs → status-Bc0_4T41.cjs} +7 -8
- package/dist/{status-CxzhvjKs.cjs.map → status-Bc0_4T41.cjs.map} +1 -1
- package/dist/{status-BHIOMCM0.js → status-DxSkp1cH.js} +6 -7
- package/dist/{status-BHIOMCM0.js.map → status-DxSkp1cH.js.map} +1 -1
- package/dist/studio-BpYwgFb0.cjs +218 -0
- package/dist/studio-BpYwgFb0.cjs.map +1 -0
- package/dist/studio-mw6HaPkr.js +215 -0
- package/dist/studio-mw6HaPkr.js.map +1 -0
- package/dist/studioWorker.js +4 -5
- package/dist/{summary-B219y888.js → summary-CbmQKA8l.js} +5 -6
- package/dist/{summary-B219y888.js.map → summary-CbmQKA8l.js.map} +1 -1
- package/dist/{summary-BOYsqub3.cjs → summary-Clvl5Hxs.cjs} +7 -8
- package/dist/{summary-BOYsqub3.cjs.map → summary-Clvl5Hxs.cjs.map} +1 -1
- package/dist/table-BlRe9YTZ.cjs +15 -0
- package/dist/table-BlRe9YTZ.cjs.map +1 -0
- package/dist/table-CR7iZJGo.js +10 -0
- package/dist/table-CR7iZJGo.js.map +1 -0
- package/dist/text-BmiI4s6N.js +13 -0
- package/dist/text-BmiI4s6N.js.map +1 -0
- package/dist/text-Ep3AulAS.cjs +24 -0
- package/dist/text-Ep3AulAS.cjs.map +1 -0
- package/dist/time-3BKoci3G.cjs +82 -0
- package/dist/time-3BKoci3G.cjs.map +1 -0
- package/dist/time-C-wU7VhC.js +65 -0
- package/dist/time-C-wU7VhC.js.map +1 -0
- package/dist/utils-BdibacYq.js +244 -0
- package/dist/utils-BdibacYq.js.map +1 -0
- package/dist/utils-CO89mxRU.cjs +290 -0
- package/dist/utils-CO89mxRU.cjs.map +1 -0
- package/dist/utils-CbJ6hS_t.js +222 -0
- package/dist/utils-CbJ6hS_t.js.map +1 -0
- package/dist/utils-CcARPB45.cjs +286 -0
- package/dist/utils-CcARPB45.cjs.map +1 -0
- package/dist/validate-8ZcWXfMt.js +28 -0
- package/dist/validate-8ZcWXfMt.js.map +1 -0
- package/dist/validate-CMSeDpGT.cjs +27 -0
- package/dist/validate-CMSeDpGT.cjs.map +1 -0
- package/package.json +7 -7
- package/dist/Telemetry-Dc79_cIA.js.map +0 -1
- package/dist/Telemetry-Dc9DVxKQ.cjs.map +0 -1
- package/dist/config-BG_Nr2CZ.cjs.map +0 -1
- package/dist/config-CNqgDSu3.js.map +0 -1
- package/dist/config-DWDpFSn1.js +0 -1
- package/dist/registry-BInbtBm7.cjs.map +0 -1
- package/dist/registry-ov1RYcD4.js.map +0 -1
- package/dist/run-1oXPIkli.cjs.map +0 -1
- package/dist/run-B03V0Poj.cjs.map +0 -1
- package/dist/run-DDwdeHgU.js.map +0 -1
- package/dist/run-YMSJnvYl.js.map +0 -1
- package/dist/run-v5aW1jjQ.js +0 -1
- package/dist/text-CC2xdpGL.cjs.map +0 -1
- package/dist/text-CEyJhBnh.js.map +0 -1
- package/dist/utils-8E1nF4C1.cjs +0 -645
- package/dist/utils-8E1nF4C1.cjs.map +0 -1
- package/dist/utils-D8VN40bN.js +0 -536
- package/dist/utils-D8VN40bN.js.map +0 -1
|
@@ -0,0 +1,993 @@
|
|
|
1
|
+
import "./rolldown-runtime-xo6NMXYd.js";
|
|
2
|
+
import { t as getErrorMessage } from "./errors-wzqmMpMo.js";
|
|
3
|
+
import { c as writeRaw, o as read } from "./spinner-BZaln7O8.js";
|
|
4
|
+
import { isPackageName } from "@kubb/core";
|
|
5
|
+
import { parse } from "yuku-parser";
|
|
6
|
+
// ../../internals/utils/src/casing.ts
|
|
7
|
+
/**
|
|
8
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
9
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
10
|
+
* and capitalizes each word according to `pascal`.
|
|
11
|
+
*
|
|
12
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
13
|
+
*/
|
|
14
|
+
function convert(text, pascal) {
|
|
15
|
+
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) => {
|
|
16
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
17
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
18
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
19
|
+
}
|
|
20
|
+
const cache = /* @__PURE__ */ new Map();
|
|
21
|
+
const cacheLimit = 1e5;
|
|
22
|
+
function toCamelOrPascal(text, pascal) {
|
|
23
|
+
const key = `${pascal ? "P" : "c"}${text}`;
|
|
24
|
+
const cached = cache.get(key);
|
|
25
|
+
if (cached !== void 0) return cached;
|
|
26
|
+
const result = convert(text, pascal);
|
|
27
|
+
if (cache.size >= cacheLimit) cache.clear();
|
|
28
|
+
cache.set(key, result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Converts `text` to camelCase.
|
|
33
|
+
*
|
|
34
|
+
* @example Word boundaries
|
|
35
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
36
|
+
*
|
|
37
|
+
* @example With a prefix
|
|
38
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
39
|
+
*/
|
|
40
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
41
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
42
|
+
}
|
|
43
|
+
// ../../internals/shared/src/configFile/ast.ts
|
|
44
|
+
/** Node types the config editor reads from the yuku AST. */
|
|
45
|
+
const nodeTypes = {
|
|
46
|
+
arrayExpression: "ArrayExpression",
|
|
47
|
+
arrowFunctionExpression: "ArrowFunctionExpression",
|
|
48
|
+
assignmentExpression: "AssignmentExpression",
|
|
49
|
+
blockStatement: "BlockStatement",
|
|
50
|
+
callExpression: "CallExpression",
|
|
51
|
+
exportDefaultDeclaration: "ExportDefaultDeclaration",
|
|
52
|
+
expressionStatement: "ExpressionStatement",
|
|
53
|
+
functionExpression: "FunctionExpression",
|
|
54
|
+
identifier: "Identifier",
|
|
55
|
+
importDeclaration: "ImportDeclaration",
|
|
56
|
+
importSpecifier: "ImportSpecifier",
|
|
57
|
+
literal: "Literal",
|
|
58
|
+
memberExpression: "MemberExpression",
|
|
59
|
+
objectExpression: "ObjectExpression",
|
|
60
|
+
objectPattern: "ObjectPattern",
|
|
61
|
+
property: "Property",
|
|
62
|
+
returnStatement: "ReturnStatement",
|
|
63
|
+
spreadElement: "SpreadElement",
|
|
64
|
+
tSAsExpression: "TSAsExpression",
|
|
65
|
+
tSSatisfiesExpression: "TSSatisfiesExpression",
|
|
66
|
+
templateLiteral: "TemplateLiteral",
|
|
67
|
+
unaryExpression: "UnaryExpression",
|
|
68
|
+
variableDeclaration: "VariableDeclaration"
|
|
69
|
+
};
|
|
70
|
+
const commentTypes = { line: "Line" };
|
|
71
|
+
const propertyKinds = { init: "init" };
|
|
72
|
+
const unaryOperators = {
|
|
73
|
+
minus: "-",
|
|
74
|
+
plus: "+"
|
|
75
|
+
};
|
|
76
|
+
const diagnosticSeverities = { error: "error" };
|
|
77
|
+
/**
|
|
78
|
+
* Parses `source` as a TypeScript module. Never throws: a syntax error, or a native parser that
|
|
79
|
+
* cannot load on this platform, comes back as a `reason`.
|
|
80
|
+
*/
|
|
81
|
+
function parseSource(source) {
|
|
82
|
+
try {
|
|
83
|
+
const { program, comments, diagnostics } = parse(source, {
|
|
84
|
+
lang: "ts",
|
|
85
|
+
sourceType: "module",
|
|
86
|
+
preserveParens: false
|
|
87
|
+
});
|
|
88
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === diagnosticSeverities.error)) return { reason: "the config file could not be parsed" };
|
|
89
|
+
return {
|
|
90
|
+
program,
|
|
91
|
+
comments
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return { reason: `the config parser could not load on this platform: ${getErrorMessage(error)}` };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Parses `source` as one expression, such as a commented-out `pluginTs({ ... })` call. */
|
|
98
|
+
function parseExpression(source) {
|
|
99
|
+
const parsed = parseSource(source);
|
|
100
|
+
if ("reason" in parsed) return void 0;
|
|
101
|
+
const [statement] = parsed.program.body;
|
|
102
|
+
return parsed.program.body.length === 1 && statement?.type === nodeTypes.expressionStatement ? statement.expression : void 0;
|
|
103
|
+
}
|
|
104
|
+
// ../../internals/shared/src/configFile/types.ts
|
|
105
|
+
const extensionKinds = {
|
|
106
|
+
plugin: "plugin",
|
|
107
|
+
adapter: "adapter",
|
|
108
|
+
parser: "parser"
|
|
109
|
+
};
|
|
110
|
+
const ConfigEditOperation = {
|
|
111
|
+
SET_OPTION: "set-option",
|
|
112
|
+
REMOVE_OPTION: "remove-option",
|
|
113
|
+
ADD_PLUGIN: "add-plugin",
|
|
114
|
+
REMOVE_EXTENSION: "remove-extension",
|
|
115
|
+
DISABLE_PLUGIN: "disable-plugin",
|
|
116
|
+
ENABLE_PLUGIN: "enable-plugin",
|
|
117
|
+
SET_INPUT: "set-input",
|
|
118
|
+
SET_OUTPUT: "set-output",
|
|
119
|
+
REMOVE_OUTPUT: "remove-output"
|
|
120
|
+
};
|
|
121
|
+
// ../../internals/shared/src/configFile/rules.ts
|
|
122
|
+
/** `@kubb/plugin-ts` becomes `plugin-ts`, the `name` Kubb plugin factories use. */
|
|
123
|
+
function toPluginName(packageName) {
|
|
124
|
+
return packageName.split("/").pop() ?? packageName;
|
|
125
|
+
}
|
|
126
|
+
/** `@kubb/plugin-react-query` becomes `pluginReactQuery`, the export a plugin package uses by convention. */
|
|
127
|
+
function toExportName(packageName) {
|
|
128
|
+
return camelCase(toPluginName(packageName));
|
|
129
|
+
}
|
|
130
|
+
const KUBB_PLUGIN_SPECIFIER = /^@kubb\/plugin-[\w.-]+$/;
|
|
131
|
+
/** Only a `@kubb/plugin-*` package or an installed extension of `kind` may be imported, never a path or URL. */
|
|
132
|
+
function isAllowedPlugin({ packageName, extensions, kind = extensionKinds.plugin }) {
|
|
133
|
+
if (!isPackageName(packageName)) return false;
|
|
134
|
+
return kind === extensionKinds.plugin && KUBB_PLUGIN_SPECIFIER.test(packageName) || extensions.get(packageName)?.manifest.kind === kind;
|
|
135
|
+
}
|
|
136
|
+
const UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
137
|
+
"__proto__",
|
|
138
|
+
"constructor",
|
|
139
|
+
"prototype"
|
|
140
|
+
]);
|
|
141
|
+
/** Whether `key` can be an option key without reaching the object's prototype. */
|
|
142
|
+
function isSafeKey(key) {
|
|
143
|
+
return !UNSAFE_KEYS.has(key);
|
|
144
|
+
}
|
|
145
|
+
function isPlainObject(value) {
|
|
146
|
+
if (typeof value !== "object" || value === null) return false;
|
|
147
|
+
const prototype = Object.getPrototypeOf(value);
|
|
148
|
+
return prototype === null || prototype === Object.prototype;
|
|
149
|
+
}
|
|
150
|
+
/** Whether `value` survives a JSON round trip, so an edit never writes a lossy copy into the file. */
|
|
151
|
+
function isOptionValue(value) {
|
|
152
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
153
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
154
|
+
if (Array.isArray(value)) return value.every(isOptionValue);
|
|
155
|
+
if (isPlainObject(value)) return Object.entries(value).every(([key, entry]) => isSafeKey(key) && isOptionValue(entry));
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
/** The root `output` members Studio may set, with the values each takes. */
|
|
159
|
+
const OUTPUT_VALUES = {
|
|
160
|
+
barrel: (value) => value === false || isPlainObject(value) && Object.keys(value).length === 1 && (value.type === "named" || value.type === "all"),
|
|
161
|
+
format: (value) => value === false || [
|
|
162
|
+
"auto",
|
|
163
|
+
"prettier",
|
|
164
|
+
"biome",
|
|
165
|
+
"oxfmt"
|
|
166
|
+
].includes(value),
|
|
167
|
+
lint: (value) => value === false || [
|
|
168
|
+
"auto",
|
|
169
|
+
"eslint",
|
|
170
|
+
"biome",
|
|
171
|
+
"oxlint"
|
|
172
|
+
].includes(value),
|
|
173
|
+
clean: (value) => typeof value === "boolean",
|
|
174
|
+
defaultBanner: (value) => value === false || value === "simple" || value === "full"
|
|
175
|
+
};
|
|
176
|
+
function isOutputKey(key) {
|
|
177
|
+
return Object.hasOwn(OUTPUT_VALUES, key);
|
|
178
|
+
}
|
|
179
|
+
function isOutputValue({ key, value }) {
|
|
180
|
+
return isOutputKey(key) && OUTPUT_VALUES[key](value);
|
|
181
|
+
}
|
|
182
|
+
function pickOutput(output) {
|
|
183
|
+
return Object.fromEntries(Object.entries(output).filter(([key]) => isOutputKey(key)));
|
|
184
|
+
}
|
|
185
|
+
// ../../internals/shared/src/configFile/text.ts
|
|
186
|
+
/**
|
|
187
|
+
* A valid JavaScript identifier, so an import name can only ever print as `import { name } from`,
|
|
188
|
+
* never as source that breaks out of the import statement.
|
|
189
|
+
*/
|
|
190
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/;
|
|
191
|
+
const quotes = {
|
|
192
|
+
single: `'`,
|
|
193
|
+
double: `"`
|
|
194
|
+
};
|
|
195
|
+
/** `source` with the text between `start` and `end` replaced by `text`. */
|
|
196
|
+
function replaceText(source, start, end, text = "") {
|
|
197
|
+
return `${source.slice(0, start)}${text}${source.slice(end)}`;
|
|
198
|
+
}
|
|
199
|
+
/** The 1-based line and 0-based column of `offset` in `source`. */
|
|
200
|
+
function getPosition(source, offset) {
|
|
201
|
+
return {
|
|
202
|
+
line: source.slice(0, offset).split("\n").length,
|
|
203
|
+
column: offset - source.lastIndexOf("\n", offset - 1) - 1
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/** Writes `value` as a single-line literal with the file's quote style. */
|
|
207
|
+
function printValue(value, quote) {
|
|
208
|
+
if (typeof value === "string") {
|
|
209
|
+
const escaped = JSON.stringify(value).slice(1, -1);
|
|
210
|
+
return quote === quotes.double ? `"${escaped}"` : `'${escaped.replaceAll(`\\"`, `"`).replaceAll(`'`, `\\'`)}'`;
|
|
211
|
+
}
|
|
212
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
213
|
+
if (Array.isArray(value)) return `[${value.map((item) => printValue(item, quote)).join(", ")}]`;
|
|
214
|
+
const entries = Object.entries(value);
|
|
215
|
+
return entries.length ? `{ ${entries.map(([key, item]) => `${printKey(key, quote)}: ${printValue(item, quote)}`).join(", ")} }` : "{}";
|
|
216
|
+
}
|
|
217
|
+
/** An object key as source text: bare when it is an identifier, quoted otherwise. */
|
|
218
|
+
function printKey(key, quote) {
|
|
219
|
+
return IDENTIFIER_PATTERN.test(key) ? key : printValue(key, quote);
|
|
220
|
+
}
|
|
221
|
+
/** `value` wrapped in one object per key of `path`: `wrapPath(['enum', 'type'], 'enum')` is `{ enum: { type: 'enum' } }`. */
|
|
222
|
+
function wrapPath(path, value) {
|
|
223
|
+
return path.reduceRight((inner, key) => ({ [key]: inner }), value);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Adds `text` as the last item of an object or array literal, following its layout: on a line of
|
|
227
|
+
* its own with the last item's indentation when the literal spans lines, inline otherwise.
|
|
228
|
+
*/
|
|
229
|
+
function appendItem(source, container, text) {
|
|
230
|
+
const { start, end } = container;
|
|
231
|
+
const last = (container.type === nodeTypes.objectExpression ? container.properties : container.elements).filter((item) => item !== null).at(-1);
|
|
232
|
+
if (!last) {
|
|
233
|
+
const inner = source.slice(start + 1, end - 1);
|
|
234
|
+
if (inner.trim()) return replaceText(source, start + 1, start + 1, /^\s/.test(inner) ? `${text},` : `${text}, `);
|
|
235
|
+
return replaceText(source, start, end, container.type === nodeTypes.objectExpression ? `{ ${text} }` : `[${text}]`);
|
|
236
|
+
}
|
|
237
|
+
const between = source.slice(last.end, end - 1);
|
|
238
|
+
if (!between.includes("\n")) return replaceText(source, last.end, last.end, `, ${text}`);
|
|
239
|
+
const lineStart = source.lastIndexOf("\n", last.start - 1) + 1;
|
|
240
|
+
const indent = source.slice(lineStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
241
|
+
const lineEnd = source.indexOf("\n", last.end);
|
|
242
|
+
const comma = /^\s*,/.test(between);
|
|
243
|
+
return `${source.slice(0, last.end)}${comma ? "" : ","}${source.slice(last.end, lineEnd)}\n${indent}${text}${comma ? "," : ""}${source.slice(lineEnd)}`;
|
|
244
|
+
}
|
|
245
|
+
/** Drops the item at `index` from an object or array literal, with its own lines when it sits on them alone. */
|
|
246
|
+
function removeItem(source, node, index) {
|
|
247
|
+
const items = node.type === nodeTypes.objectExpression ? node.properties : node.elements.filter((element) => element !== null);
|
|
248
|
+
if (items.length === 1) return replaceText(source, node.start, node.end, node.type === nodeTypes.objectExpression ? "{}" : "[]");
|
|
249
|
+
const { start, end } = items[index];
|
|
250
|
+
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
251
|
+
const nextNewline = source.indexOf("\n", end);
|
|
252
|
+
const lineEnd = nextNewline === -1 ? source.length : nextNewline;
|
|
253
|
+
if (!source.slice(lineStart, start).trim() && /^\s*,?\s*(\/\/.*)?$/.test(source.slice(end, lineEnd))) return replaceText(source, lineStart, Math.min(lineEnd + 1, source.length));
|
|
254
|
+
const next = items[index + 1];
|
|
255
|
+
return next ? replaceText(source, start, next.start) : replaceText(source, items[index - 1].end, end);
|
|
256
|
+
}
|
|
257
|
+
// ../../internals/shared/src/configFile/configFile.ts
|
|
258
|
+
/** The marker `disable-plugin` writes, with the block's line count so `enable-plugin` restores exactly those lines. */
|
|
259
|
+
const DISABLED_MARKER_PATTERN = /^kubb:disabled (.+)\s+(\d+)$/;
|
|
260
|
+
const OPERATIONS = new Set(Object.values(ConfigEditOperation));
|
|
261
|
+
const OPTION_OPERATIONS = /* @__PURE__ */ new Set([ConfigEditOperation.SET_OPTION, ConfigEditOperation.REMOVE_OPTION]);
|
|
262
|
+
const OUTPUT_OPERATIONS = /* @__PURE__ */ new Set([ConfigEditOperation.SET_OUTPUT, ConfigEditOperation.REMOVE_OUTPUT]);
|
|
263
|
+
const KINDS = new Set(Object.values(extensionKinds));
|
|
264
|
+
/** Identifiers the editor matches by name. */
|
|
265
|
+
const identifiers = {
|
|
266
|
+
defineConfig: "defineConfig",
|
|
267
|
+
require: "require",
|
|
268
|
+
module: "module",
|
|
269
|
+
exports: "exports"
|
|
270
|
+
};
|
|
271
|
+
const moduleKinds = {
|
|
272
|
+
esm: "esm",
|
|
273
|
+
cjs: "cjs"
|
|
274
|
+
};
|
|
275
|
+
function isNonEmptyString(value) {
|
|
276
|
+
return typeof value === "string" && value !== "";
|
|
277
|
+
}
|
|
278
|
+
/** Edits cross the wire, so their shape is checked before anything reads them. */
|
|
279
|
+
function validateEdit(edit) {
|
|
280
|
+
const { operation, plugin, config, path, kind, input } = typeof edit === "object" && edit !== null ? edit : {};
|
|
281
|
+
if (typeof operation !== "string" || !OPERATIONS.has(operation)) return `unknown operation ${JSON.stringify(operation)}`;
|
|
282
|
+
if (config !== void 0 && typeof config !== "string" && !Number.isSafeInteger(config)) return "config must name an entry or give its index";
|
|
283
|
+
if (operation === ConfigEditOperation.SET_INPUT) return typeof input === "string" && input.trim() !== "" ? void 0 : "the edit names no input";
|
|
284
|
+
const isOption = OPTION_OPERATIONS.has(operation);
|
|
285
|
+
const isOutput = OUTPUT_OPERATIONS.has(operation);
|
|
286
|
+
if (!isOutput && !isNonEmptyString(plugin)) return "the edit names no plugin";
|
|
287
|
+
if (kind !== void 0) {
|
|
288
|
+
if (typeof kind !== "string" || !KINDS.has(kind)) return `unknown kind ${JSON.stringify(kind)}`;
|
|
289
|
+
const takesKind = operation === ConfigEditOperation.ADD_PLUGIN || operation === ConfigEditOperation.REMOVE_EXTENSION;
|
|
290
|
+
if (takesKind && kind === extensionKinds.adapter) return `${operation} takes a plugin or a parser, since a config has one adapter`;
|
|
291
|
+
if (!isOption && !takesKind) return `${operation} edits the plugins array, so it takes no kind`;
|
|
292
|
+
}
|
|
293
|
+
if (!isOption && !isOutput) return void 0;
|
|
294
|
+
if (!Array.isArray(path) || !path.every(isNonEmptyString)) return "path must be a list of option names";
|
|
295
|
+
if (path.length === 0) return "no option path given";
|
|
296
|
+
const unsafe = path.find((key) => !isSafeKey(key));
|
|
297
|
+
if (unsafe !== void 0) return `${unsafe} cannot be used as an option name`;
|
|
298
|
+
if (isOutput && !isOutputKey(path[0])) return `output.${path[0]} can only be changed in the config file`;
|
|
299
|
+
}
|
|
300
|
+
/** Steps through `as`/`satisfies` and `() => ...` wrappers to the config object. */
|
|
301
|
+
function unwrapValue(node) {
|
|
302
|
+
if (!node) return void 0;
|
|
303
|
+
if (node.type === nodeTypes.tSAsExpression || node.type === nodeTypes.tSSatisfiesExpression) return unwrapValue(node.expression);
|
|
304
|
+
if (node.type !== nodeTypes.arrowFunctionExpression && node.type !== nodeTypes.functionExpression) return node;
|
|
305
|
+
if (node.body?.type !== nodeTypes.blockStatement) return unwrapValue(node.body);
|
|
306
|
+
const returned = node.body.body.find((statement) => statement.type === nodeTypes.returnStatement);
|
|
307
|
+
return unwrapValue(returned?.type === nodeTypes.returnStatement ? returned.argument : void 0);
|
|
308
|
+
}
|
|
309
|
+
function isModuleExports(node) {
|
|
310
|
+
return node.type === nodeTypes.memberExpression && !node.computed && node.object.type === nodeTypes.identifier && node.object.name === identifiers.module && node.property.type === nodeTypes.identifier && node.property.name === identifiers.exports;
|
|
311
|
+
}
|
|
312
|
+
/** The exported config value and how the file exports it. */
|
|
313
|
+
function findExport(program) {
|
|
314
|
+
for (const node of program.body) {
|
|
315
|
+
if (node.type === nodeTypes.exportDefaultDeclaration) return {
|
|
316
|
+
exported: node.declaration,
|
|
317
|
+
kind: moduleKinds.esm
|
|
318
|
+
};
|
|
319
|
+
if (node.type === nodeTypes.expressionStatement && node.expression.type === nodeTypes.assignmentExpression && isModuleExports(node.expression.left)) return {
|
|
320
|
+
exported: node.expression.right,
|
|
321
|
+
kind: moduleKinds.cjs
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
exported: void 0,
|
|
326
|
+
kind: moduleKinds.esm
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/** Every config object in `export default` or `module.exports = defineConfig(...)`, or why the file is unmanaged. */
|
|
330
|
+
function findConfigs(source) {
|
|
331
|
+
const parsed = parseSource(source);
|
|
332
|
+
if ("reason" in parsed) return parsed;
|
|
333
|
+
const { exported, kind } = findExport(parsed.program);
|
|
334
|
+
const value = unwrapValue(exported);
|
|
335
|
+
if (!value) return { reason: "no `export default` or `module.exports` found" };
|
|
336
|
+
if (value.type !== nodeTypes.callExpression || value.callee.type !== nodeTypes.identifier || value.callee.name !== identifiers.defineConfig) return { reason: "the exported config is not a defineConfig(...) call" };
|
|
337
|
+
const argument = unwrapValue(value.arguments[0]);
|
|
338
|
+
if (!argument) return { reason: "defineConfig(...) was called without a config" };
|
|
339
|
+
const entries = argument.type === nodeTypes.arrayExpression ? argument.elements.map((element) => unwrapValue(element)) : [argument];
|
|
340
|
+
const configs = entries.filter((entry) => entry?.type === nodeTypes.objectExpression);
|
|
341
|
+
if (configs.length !== entries.length) return { reason: "config is not an object literal" };
|
|
342
|
+
return {
|
|
343
|
+
...parsed,
|
|
344
|
+
configs,
|
|
345
|
+
kind
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function findConfigEntry(configs, ref) {
|
|
349
|
+
return typeof ref === "string" ? configs.find((entry) => getConfigName(entry) === ref) : configs[ref ?? 0];
|
|
350
|
+
}
|
|
351
|
+
function getKey(entry) {
|
|
352
|
+
if (entry.type !== nodeTypes.property || entry.computed) return void 0;
|
|
353
|
+
if (entry.key.type === nodeTypes.identifier) return entry.key.name;
|
|
354
|
+
return entry.key.type === nodeTypes.literal && typeof entry.key.value === "string" ? entry.key.value : void 0;
|
|
355
|
+
}
|
|
356
|
+
/** The last one wins when the key repeats, as at runtime. */
|
|
357
|
+
function findProperty(node, key) {
|
|
358
|
+
const index = node.properties.findLastIndex((entry) => getKey(entry) === key);
|
|
359
|
+
return index === -1 ? void 0 : {
|
|
360
|
+
member: node.properties[index],
|
|
361
|
+
index
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function getValue(node, key) {
|
|
365
|
+
const member = findProperty(node, key)?.member;
|
|
366
|
+
return member?.type === nodeTypes.property ? member.value : member;
|
|
367
|
+
}
|
|
368
|
+
function getConfigName(config) {
|
|
369
|
+
const name = getValue(config, "name");
|
|
370
|
+
return name?.type === nodeTypes.literal && typeof name.value === "string" ? name.value : void 0;
|
|
371
|
+
}
|
|
372
|
+
function isOverridden(node, index) {
|
|
373
|
+
return node.properties.slice(index + 1).some((entry) => entry.type === nodeTypes.spreadElement);
|
|
374
|
+
}
|
|
375
|
+
/** `key: value`, not a method, getter, setter, or `{ key }` shorthand. */
|
|
376
|
+
function isPlainProperty(member) {
|
|
377
|
+
return !member.method && !member.shorthand && member.kind === propertyKinds.init;
|
|
378
|
+
}
|
|
379
|
+
/** A literal's value, `undefined` for code. */
|
|
380
|
+
function readLiteral(node) {
|
|
381
|
+
switch (node?.type) {
|
|
382
|
+
case nodeTypes.literal: return "regex" in node || "bigint" in node ? void 0 : node.value;
|
|
383
|
+
case nodeTypes.templateLiteral: return node.expressions.length ? void 0 : node.quasis[0]?.value.cooked ?? "";
|
|
384
|
+
case nodeTypes.unaryExpression: {
|
|
385
|
+
const value = readLiteral(node.argument);
|
|
386
|
+
if (typeof value !== "number") return void 0;
|
|
387
|
+
if (node.operator === unaryOperators.minus) return -value;
|
|
388
|
+
if (node.operator === unaryOperators.plus) return value;
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
case nodeTypes.arrayExpression: {
|
|
392
|
+
const values = node.elements.map((element) => readLiteral(element));
|
|
393
|
+
return values.every((value) => value !== void 0) ? values : void 0;
|
|
394
|
+
}
|
|
395
|
+
case nodeTypes.objectExpression: {
|
|
396
|
+
const entries = {};
|
|
397
|
+
for (const entry of node.properties) {
|
|
398
|
+
if (entry.type !== nodeTypes.property) return void 0;
|
|
399
|
+
const key = getKey(entry);
|
|
400
|
+
const value = readLiteral(entry.value);
|
|
401
|
+
if (key === void 0 || !isSafeKey(key) || value === void 0) return void 0;
|
|
402
|
+
entries[key] = value;
|
|
403
|
+
}
|
|
404
|
+
return entries;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function getRequirePath(node) {
|
|
409
|
+
if (node?.type !== nodeTypes.callExpression || node.callee.type !== nodeTypes.identifier || node.callee.name !== identifiers.require) return void 0;
|
|
410
|
+
const [argument] = node.arguments;
|
|
411
|
+
return argument?.type === nodeTypes.literal && typeof argument.value === "string" ? argument.value : void 0;
|
|
412
|
+
}
|
|
413
|
+
/** `import { exported as local } from '...'`. A default or namespace import has no exported name, so its local name stands in. */
|
|
414
|
+
function importBindings(statement) {
|
|
415
|
+
return statement.specifiers.map((specifier) => {
|
|
416
|
+
const imported = specifier.type === nodeTypes.importSpecifier ? specifier.imported : specifier.local;
|
|
417
|
+
const exported = imported.type === nodeTypes.identifier ? imported.name : imported.value;
|
|
418
|
+
return [specifier.local.name, {
|
|
419
|
+
from: statement.source.value,
|
|
420
|
+
exported
|
|
421
|
+
}];
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
/** `const { exported: local } = require('...')`. */
|
|
425
|
+
function requireBindings(statement) {
|
|
426
|
+
return statement.declarations.flatMap((declarator) => {
|
|
427
|
+
const from = getRequirePath(declarator.init);
|
|
428
|
+
if (from === void 0 || declarator.id.type !== nodeTypes.objectPattern) return [];
|
|
429
|
+
return declarator.id.properties.flatMap((entry) => {
|
|
430
|
+
if (entry.type !== nodeTypes.property || entry.value.type !== nodeTypes.identifier) return [];
|
|
431
|
+
return [[entry.value.name, {
|
|
432
|
+
from,
|
|
433
|
+
exported: getKey(entry) ?? entry.value.name
|
|
434
|
+
}]];
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function getImports(program) {
|
|
439
|
+
return new Map(program.body.flatMap((statement) => {
|
|
440
|
+
if (statement.type === nodeTypes.importDeclaration) return importBindings(statement);
|
|
441
|
+
if (statement.type === nodeTypes.variableDeclaration) return requireBindings(statement);
|
|
442
|
+
return [];
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
function isImportStatement(node, kind) {
|
|
446
|
+
if (kind === moduleKinds.esm) return node.type === nodeTypes.importDeclaration;
|
|
447
|
+
return node.type === nodeTypes.variableDeclaration && node.declarations.some((declarator) => getRequirePath(declarator.init) !== void 0);
|
|
448
|
+
}
|
|
449
|
+
/** The quote of the first string literal in the file, so new code matches it. */
|
|
450
|
+
function detectQuote(node) {
|
|
451
|
+
if (!node || typeof node !== "object") return void 0;
|
|
452
|
+
const { type, raw } = node;
|
|
453
|
+
if (type === nodeTypes.literal && typeof raw === "string" && (raw[0] === quotes.single || raw[0] === quotes.double)) return raw[0];
|
|
454
|
+
for (const child of Object.values(node)) {
|
|
455
|
+
const quote = detectQuote(child);
|
|
456
|
+
if (quote) return quote;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/** The `adapter` value, or the elements of the `plugins` or `parsers` array. */
|
|
460
|
+
function factoryNodes(config, kind) {
|
|
461
|
+
if (kind === extensionKinds.adapter) return [getValue(config, "adapter")];
|
|
462
|
+
const array = getValue(config, `${kind}s`);
|
|
463
|
+
return array?.type === nodeTypes.arrayExpression ? array.elements : [];
|
|
464
|
+
}
|
|
465
|
+
/** The factory calls of one kind whose callee the file imports. */
|
|
466
|
+
function findCalls(program, config, kind = extensionKinds.plugin) {
|
|
467
|
+
const imports = getImports(program);
|
|
468
|
+
return factoryNodes(config, kind).flatMap((node) => {
|
|
469
|
+
if (node?.type !== nodeTypes.callExpression || node.callee.type !== nodeTypes.identifier) return [];
|
|
470
|
+
const imported = imports.get(node.callee.name);
|
|
471
|
+
return imported ? [{
|
|
472
|
+
importName: node.callee.name,
|
|
473
|
+
exportName: imported.exported,
|
|
474
|
+
packageName: imported.from,
|
|
475
|
+
call: node
|
|
476
|
+
}] : [];
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
/** Whether nothing but whitespace precedes `offset` on its line. */
|
|
480
|
+
function startsLine(source, offset) {
|
|
481
|
+
return source.slice(source.lastIndexOf("\n", offset - 1) + 1, offset).trim() === "";
|
|
482
|
+
}
|
|
483
|
+
/** The `disable-plugin` blocks in this config, read from marker comments since the call is no longer a node. */
|
|
484
|
+
function findDisabled(source, comments, config) {
|
|
485
|
+
const lines = source.split("\n");
|
|
486
|
+
return comments.flatMap((comment) => {
|
|
487
|
+
const match = comment.type === commentTypes.line ? comment.value.trim().match(DISABLED_MARKER_PATTERN) : null;
|
|
488
|
+
if (!match || comment.start < config.start || comment.end > config.end || !startsLine(source, comment.start)) return [];
|
|
489
|
+
const line = getPosition(source, comment.start).line;
|
|
490
|
+
const end = line + Number(match[2]);
|
|
491
|
+
const code = lines.slice(line, end).map((text) => text.replace(/^(\s*)\/\/ ?/, "$1").replace(/^\s+$/, ""));
|
|
492
|
+
return [{
|
|
493
|
+
packageName: match[1],
|
|
494
|
+
line,
|
|
495
|
+
end,
|
|
496
|
+
code
|
|
497
|
+
}];
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/** Literal members as `value` and code paths as `locked`, `undefined` for a computed key. */
|
|
501
|
+
function readEditable(node, prefix = []) {
|
|
502
|
+
const value = {};
|
|
503
|
+
const locked = [];
|
|
504
|
+
for (const [index, entry] of node.properties.entries()) {
|
|
505
|
+
if (entry.type === nodeTypes.spreadElement) continue;
|
|
506
|
+
const key = getKey(entry);
|
|
507
|
+
if (key === void 0) return void 0;
|
|
508
|
+
if (findProperty(node, key)?.index !== index) continue;
|
|
509
|
+
const path = [...prefix, key];
|
|
510
|
+
if (!isSafeKey(key)) {
|
|
511
|
+
locked.push(path);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const member = isOverridden(node, index) ? void 0 : entry.value;
|
|
515
|
+
const literal = readLiteral(member);
|
|
516
|
+
if (literal !== void 0) {
|
|
517
|
+
value[key] = literal;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (member?.type !== nodeTypes.objectExpression) {
|
|
521
|
+
locked.push(path);
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const nested = readEditable(member, path);
|
|
525
|
+
if (!nested) return void 0;
|
|
526
|
+
value[key] = nested.value;
|
|
527
|
+
locked.push(...nested.locked);
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
value,
|
|
531
|
+
locked
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function readObjectOptions(options) {
|
|
535
|
+
const entries = {};
|
|
536
|
+
for (const [index, entry] of options.properties.entries()) {
|
|
537
|
+
const key = getKey(entry);
|
|
538
|
+
if (key === void 0 || !isSafeKey(key)) continue;
|
|
539
|
+
const member = entry.type === nodeTypes.property && !isOverridden(options, index) ? entry.value : void 0;
|
|
540
|
+
const value = readLiteral(member);
|
|
541
|
+
if (value !== void 0) {
|
|
542
|
+
entries[key] = {
|
|
543
|
+
literal: true,
|
|
544
|
+
value
|
|
545
|
+
};
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
entries[key] = {
|
|
549
|
+
literal: false,
|
|
550
|
+
...member?.type === nodeTypes.objectExpression ? readEditable(member) : void 0
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
return entries;
|
|
554
|
+
}
|
|
555
|
+
function readCallOptions(call) {
|
|
556
|
+
const options = call.arguments[0];
|
|
557
|
+
return options?.type === nodeTypes.objectExpression ? readObjectOptions(options) : {};
|
|
558
|
+
}
|
|
559
|
+
function readDisabledOptions(code) {
|
|
560
|
+
const call = parseExpression(code.join("\n").trim().replace(/,$/, ""));
|
|
561
|
+
return call?.type === nodeTypes.callExpression ? readCallOptions(call) : {};
|
|
562
|
+
}
|
|
563
|
+
function toPluginView({ importName, packageName, call }) {
|
|
564
|
+
return {
|
|
565
|
+
importName,
|
|
566
|
+
packageName,
|
|
567
|
+
options: readCallOptions(call)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/** Active and disabled plugins, in source order so a disabled plugin keeps its place. */
|
|
571
|
+
function readPlugins({ source, parsed, config }) {
|
|
572
|
+
const importNames = new Map([...getImports(parsed.program)].map(([local, { from }]) => [from, local]));
|
|
573
|
+
const active = findCalls(parsed.program, config).map((call) => ({
|
|
574
|
+
line: getPosition(source, call.call.start).line,
|
|
575
|
+
plugin: toPluginView(call)
|
|
576
|
+
}));
|
|
577
|
+
const disabled = findDisabled(source, parsed.comments, config).map(({ packageName, line, code }) => ({
|
|
578
|
+
line,
|
|
579
|
+
plugin: {
|
|
580
|
+
importName: importNames.get(packageName) ?? toExportName(packageName),
|
|
581
|
+
packageName,
|
|
582
|
+
options: readDisabledOptions(code),
|
|
583
|
+
disabled: true
|
|
584
|
+
}
|
|
585
|
+
}));
|
|
586
|
+
return [...active, ...disabled].sort((a, b) => a.line - b.line).map(({ plugin }) => plugin);
|
|
587
|
+
}
|
|
588
|
+
function readConfigView({ source, parsed, config }) {
|
|
589
|
+
const [adapter] = findCalls(parsed.program, config, extensionKinds.adapter).map(toPluginView);
|
|
590
|
+
const parsers = findCalls(parsed.program, config, extensionKinds.parser).map(toPluginView);
|
|
591
|
+
const output = getOutput(config);
|
|
592
|
+
return {
|
|
593
|
+
name: getConfigName(config),
|
|
594
|
+
plugins: readPlugins({
|
|
595
|
+
source,
|
|
596
|
+
parsed,
|
|
597
|
+
config
|
|
598
|
+
}),
|
|
599
|
+
...adapter && { adapter },
|
|
600
|
+
...parsers.length ? { parsers } : {},
|
|
601
|
+
...!("reason" in output) && { output: pickOutput(readObjectOptions(output.object)) }
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
/** Which plugins the file declares and which options Studio may write. */
|
|
605
|
+
function readConfig(source) {
|
|
606
|
+
const parsed = findConfigs(source);
|
|
607
|
+
if ("reason" in parsed) return {
|
|
608
|
+
managed: false,
|
|
609
|
+
reason: parsed.reason
|
|
610
|
+
};
|
|
611
|
+
return {
|
|
612
|
+
managed: true,
|
|
613
|
+
configs: parsed.configs.map((config) => readConfigView({
|
|
614
|
+
source,
|
|
615
|
+
parsed,
|
|
616
|
+
config
|
|
617
|
+
}))
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
/** The modules the file imports, empty when it cannot be parsed. */
|
|
621
|
+
function readImports(source) {
|
|
622
|
+
const parsed = parseSource(source);
|
|
623
|
+
if ("reason" in parsed) return [];
|
|
624
|
+
return [...new Set([...getImports(parsed.program).values()].map(({ from }) => from))];
|
|
625
|
+
}
|
|
626
|
+
/** Follows `path` to its literal, or to the first missing key. Refuses a value set in code. */
|
|
627
|
+
function findPath(options, path) {
|
|
628
|
+
let object = options;
|
|
629
|
+
for (const [depth, key] of path.entries()) {
|
|
630
|
+
const found = findProperty(object, key);
|
|
631
|
+
if (!found) return {
|
|
632
|
+
object,
|
|
633
|
+
missing: depth
|
|
634
|
+
};
|
|
635
|
+
if (isOverridden(object, found.index)) return { reason: `${path.slice(0, depth + 1).join(".")} can be overridden by a spread after it, so it is customized in code` };
|
|
636
|
+
const { member, index } = found;
|
|
637
|
+
if (depth === path.length - 1) return member.type === nodeTypes.property && readLiteral(member.value) !== void 0 ? {
|
|
638
|
+
object,
|
|
639
|
+
index,
|
|
640
|
+
value: member.value
|
|
641
|
+
} : { reason: `${path.join(".")} is customized in code` };
|
|
642
|
+
if (member.type !== nodeTypes.property || member.value.type !== nodeTypes.objectExpression) return { reason: `${key} is not an object, so ${path.join(".")} cannot be reached` };
|
|
643
|
+
object = member.value;
|
|
644
|
+
}
|
|
645
|
+
return { reason: "no option path given" };
|
|
646
|
+
}
|
|
647
|
+
function setAtPath({ source, options, path, value, quote }) {
|
|
648
|
+
const target = findPath(options, path);
|
|
649
|
+
if ("reason" in target) return target;
|
|
650
|
+
if ("missing" in target) {
|
|
651
|
+
const [key, ...rest] = path.slice(target.missing);
|
|
652
|
+
return { source: appendItem(source, target.object, `${printKey(key, quote)}: ${printValue(wrapPath(rest, value), quote)}`) };
|
|
653
|
+
}
|
|
654
|
+
return { source: replaceText(source, target.value.start, target.value.end, printValue(value, quote)) };
|
|
655
|
+
}
|
|
656
|
+
function removeAtPath({ source, options, path }) {
|
|
657
|
+
const target = findPath(options, path);
|
|
658
|
+
if ("reason" in target) return target;
|
|
659
|
+
if ("missing" in target) return { source };
|
|
660
|
+
return { source: removeItem(source, target.object, target.index) };
|
|
661
|
+
}
|
|
662
|
+
function setOption(source, call, path, value, quote) {
|
|
663
|
+
if (!isOptionValue(value)) return { reason: "the value is not a literal that can be written to a config file" };
|
|
664
|
+
const options = call.arguments[0];
|
|
665
|
+
if (!options) {
|
|
666
|
+
const closing = call.end - 1;
|
|
667
|
+
if (source[closing] !== ")") return { reason: "the factory call could not be located in the file" };
|
|
668
|
+
return { source: replaceText(source, closing, closing, printValue(wrapPath(path, value), quote)) };
|
|
669
|
+
}
|
|
670
|
+
if (options.type !== nodeTypes.objectExpression) return { reason: "the factory was not called with an object literal" };
|
|
671
|
+
return setAtPath({
|
|
672
|
+
source,
|
|
673
|
+
options,
|
|
674
|
+
path,
|
|
675
|
+
value,
|
|
676
|
+
quote
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
function removeOption(source, call, path) {
|
|
680
|
+
const options = call.arguments[0];
|
|
681
|
+
if (!options) return { source };
|
|
682
|
+
if (options.type !== nodeTypes.objectExpression) return { reason: "the factory was not called with an object literal" };
|
|
683
|
+
return removeAtPath({
|
|
684
|
+
source,
|
|
685
|
+
options,
|
|
686
|
+
path
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
function editOption({ source, parsed, config, edit, quote, extensions }) {
|
|
690
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
691
|
+
const calls = findCalls(parsed.program, config, kind).filter((entry) => entry.packageName === edit.plugin);
|
|
692
|
+
const exportName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
693
|
+
const call = (calls.find((entry) => entry.exportName === exportName) ?? calls[0])?.call;
|
|
694
|
+
if (!call) {
|
|
695
|
+
if (kind === extensionKinds.adapter) return { reason: `${edit.plugin} is not the config's adapter` };
|
|
696
|
+
return { reason: kind === extensionKinds.plugin && findDisabled(source, parsed.comments, config).some((block) => block.packageName === edit.plugin) ? `${edit.plugin} is disabled, enable it first` : `${edit.plugin} is not in the ${kind}s array` };
|
|
697
|
+
}
|
|
698
|
+
if (edit.operation === ConfigEditOperation.SET_OPTION) return setOption(source, call, edit.path, edit.value, quote);
|
|
699
|
+
return removeOption(source, call, edit.path);
|
|
700
|
+
}
|
|
701
|
+
function setInput(source, config, input, quote) {
|
|
702
|
+
const value = printValue(input, quote);
|
|
703
|
+
const found = findProperty(config, "input");
|
|
704
|
+
if (!found) return { source: appendItem(source, config, `input: ${value}`) };
|
|
705
|
+
if (found.member.type !== nodeTypes.property || !isPlainProperty(found.member)) return { reason: "input is not a plain property" };
|
|
706
|
+
if (isOverridden(config, found.index)) return { reason: "a spread after input could override it" };
|
|
707
|
+
return { source: replaceText(source, found.member.value.start, found.member.value.end, value) };
|
|
708
|
+
}
|
|
709
|
+
function getOutput(config) {
|
|
710
|
+
const found = findProperty(config, "output");
|
|
711
|
+
if (found?.member.type !== nodeTypes.property || found.member.value.type !== nodeTypes.objectExpression) return { reason: "output is not an object literal" };
|
|
712
|
+
if (isOverridden(config, found.index)) return { reason: "a spread after output could override it" };
|
|
713
|
+
return { object: found.member.value };
|
|
714
|
+
}
|
|
715
|
+
function editOutput({ source, config, edit, quote }) {
|
|
716
|
+
const output = getOutput(config);
|
|
717
|
+
if ("reason" in output) return output;
|
|
718
|
+
if (edit.operation === ConfigEditOperation.REMOVE_OUTPUT) return removeAtPath({
|
|
719
|
+
source,
|
|
720
|
+
options: output.object,
|
|
721
|
+
path: edit.path
|
|
722
|
+
});
|
|
723
|
+
const [key, ...rest] = edit.path;
|
|
724
|
+
if (!isOptionValue(edit.value) || !isOutputValue({
|
|
725
|
+
key,
|
|
726
|
+
value: wrapPath(rest, edit.value)
|
|
727
|
+
})) return { reason: `the value is not one output.${key} takes` };
|
|
728
|
+
return setAtPath({
|
|
729
|
+
source,
|
|
730
|
+
options: output.object,
|
|
731
|
+
path: edit.path,
|
|
732
|
+
value: edit.value,
|
|
733
|
+
quote
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
/** Adds the import after the last one, matching its semicolon style. */
|
|
737
|
+
function addImport({ source, parsed, importNames, moduleSpecifier, quote }) {
|
|
738
|
+
const lastImport = parsed.program.body.findLast((node) => isImportStatement(node, parsed.kind));
|
|
739
|
+
const afterLine = lastImport ? getPosition(source, lastImport.end).line : 0;
|
|
740
|
+
const lines = source.split("\n");
|
|
741
|
+
const semicolon = afterLine > 0 && lines[afterLine - 1]?.trimEnd().endsWith(";") ? ";" : "";
|
|
742
|
+
const specifier = `${quote}${moduleSpecifier}${quote}`;
|
|
743
|
+
const statement = parsed.kind === moduleKinds.esm ? `import { ${importNames.join(", ")} } from ${specifier}` : `const { ${importNames.join(", ")} } = require(${specifier})`;
|
|
744
|
+
lines.splice(afterLine, 0, `${statement}${semicolon}`);
|
|
745
|
+
if (afterLine === 0) lines.splice(1, 0, "");
|
|
746
|
+
return lines.join("\n");
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* The parsers `defineConfig` applies when a config sets no `parsers`, by import name. Adding a parser
|
|
750
|
+
* to such a config writes these too, or the new array would replace them.
|
|
751
|
+
*/
|
|
752
|
+
const DEFAULT_PARSERS = [
|
|
753
|
+
{
|
|
754
|
+
importName: "parserTs",
|
|
755
|
+
packageName: "@kubb/parser-ts"
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
importName: "parserTsx",
|
|
759
|
+
packageName: "@kubb/parser-ts"
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
importName: "parserMd",
|
|
763
|
+
packageName: "@kubb/parser-md"
|
|
764
|
+
}
|
|
765
|
+
];
|
|
766
|
+
/**
|
|
767
|
+
* Adds a plugin to `plugins`, or with `kind: 'parser'` a parser to `parsers`. Replaying the same add
|
|
768
|
+
* is a no-op, while an import name taken by another package is an error.
|
|
769
|
+
*/
|
|
770
|
+
function addPlugin({ source, parsed, config, edit, quote, extensions }) {
|
|
771
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
772
|
+
const isParser = kind === extensionKinds.parser;
|
|
773
|
+
if (!isAllowedPlugin({
|
|
774
|
+
packageName: edit.plugin,
|
|
775
|
+
extensions,
|
|
776
|
+
kind
|
|
777
|
+
})) return { reason: isParser ? `"${edit.plugin}" is not an installed Kubb parser` : `"${edit.plugin}" is not a @kubb/plugin-* package or an installed Kubb plugin` };
|
|
778
|
+
const importName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
779
|
+
if (!IDENTIFIER_PATTERN.test(importName)) return { reason: `"${importName}" is not a valid import name` };
|
|
780
|
+
if (findCalls(parsed.program, config, kind).some((entry) => entry.packageName === edit.plugin && (!isParser || entry.exportName === importName))) return { source };
|
|
781
|
+
const options = edit.options ?? {};
|
|
782
|
+
if (!isOptionValue(options)) return { reason: "the options are not literals that can be written to a config file" };
|
|
783
|
+
const key = `${kind}s`;
|
|
784
|
+
const array = getValue(config, key);
|
|
785
|
+
if (array ? array.type !== nodeTypes.arrayExpression : !isParser) return { reason: `${key} is not an array literal` };
|
|
786
|
+
const defaults = isParser && !array ? DEFAULT_PARSERS.filter((parser) => parser.importName !== importName) : [];
|
|
787
|
+
const imports = getImports(parsed.program);
|
|
788
|
+
const missing = /* @__PURE__ */ new Map();
|
|
789
|
+
for (const item of [...defaults, {
|
|
790
|
+
importName,
|
|
791
|
+
packageName: edit.plugin
|
|
792
|
+
}]) {
|
|
793
|
+
const from = imports.get(item.importName)?.from;
|
|
794
|
+
if (from && from !== item.packageName) return { reason: `${item.importName} is already imported from ${from}` };
|
|
795
|
+
if (!from) missing.set(item.packageName, [...missing.get(item.packageName) ?? [], item.importName]);
|
|
796
|
+
}
|
|
797
|
+
const call = `${importName}(${Object.keys(options).length ? printValue(options, quote) : ""})`;
|
|
798
|
+
const next = array?.type === nodeTypes.arrayExpression ? appendItem(source, array, call) : appendItem(source, config, `parsers: [${[...defaults.map((parser) => `${parser.importName}()`), call].join(", ")}]`);
|
|
799
|
+
return { source: [...missing].reduceRight((current, [moduleSpecifier, importNames]) => addImport({
|
|
800
|
+
source: current,
|
|
801
|
+
parsed,
|
|
802
|
+
importNames,
|
|
803
|
+
moduleSpecifier,
|
|
804
|
+
quote
|
|
805
|
+
}), next) };
|
|
806
|
+
}
|
|
807
|
+
/** One item per line, so a call can be commented out alone. Skipped when comments sit between items. */
|
|
808
|
+
function splitPlugins(source, config) {
|
|
809
|
+
const array = getValue(config, "plugins");
|
|
810
|
+
if (array?.type !== nodeTypes.arrayExpression) return void 0;
|
|
811
|
+
const items = array.elements.flatMap((element) => element ? [element] : []);
|
|
812
|
+
const { start, end } = array;
|
|
813
|
+
const gaps = [source.slice(start + 1, items[0]?.start ?? end - 1), ...items.map((item, index) => source.slice(item.end, items[index + 1]?.start ?? end - 1))];
|
|
814
|
+
if (!items.length || gaps.some((gap) => !/^[\s,]*$/.test(gap))) return void 0;
|
|
815
|
+
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
816
|
+
const indent = source.slice(lineStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
817
|
+
return replaceText(source, start, end, `[\n${items.map((item) => `${indent} ${source.slice(item.start, item.end)},`).join("\n")}\n${indent}]`);
|
|
818
|
+
}
|
|
819
|
+
function disablePlugin(source, program, config, plugin) {
|
|
820
|
+
const call = findCalls(program, config).find((entry) => entry.packageName === plugin)?.call;
|
|
821
|
+
if (!call) return { reason: `${plugin} is not in the plugins array` };
|
|
822
|
+
const start = getPosition(source, call.start);
|
|
823
|
+
const end = getPosition(source, call.end);
|
|
824
|
+
const lines = source.split("\n");
|
|
825
|
+
const from = start.line - 1;
|
|
826
|
+
const to = end.line - 1;
|
|
827
|
+
const firstLine = lines[from] ?? "";
|
|
828
|
+
if (firstLine.slice(0, start.column).trim() || !/^,?\s*$/.test((lines[to] ?? "").slice(end.column))) return { reason: `${plugin} shares a line with other code, so it cannot be commented out safely` };
|
|
829
|
+
const indent = firstLine.match(/^\s*/)?.[0] ?? "";
|
|
830
|
+
const commented = lines.slice(from, to + 1).map((line) => line.trim() ? `${indent}// ${line.slice(indent.length)}` : `${indent}//`);
|
|
831
|
+
lines.splice(from, commented.length, `${indent}// kubb:disabled ${plugin} ${commented.length}`, ...commented);
|
|
832
|
+
return { source: lines.join("\n") };
|
|
833
|
+
}
|
|
834
|
+
/** Disables the plugin, first putting a one-line `plugins: [a(), b()]` (what `add-plugin` writes) on separate lines. */
|
|
835
|
+
function disablePluginInConfig({ source, parsed, config, edit }) {
|
|
836
|
+
const result = disablePlugin(source, parsed.program, config, edit.plugin);
|
|
837
|
+
if (!("reason" in result) || !result.reason.includes("shares a line")) return result;
|
|
838
|
+
const split = splitPlugins(source, config);
|
|
839
|
+
if (split === void 0) return result;
|
|
840
|
+
const reparsed = findConfigs(split);
|
|
841
|
+
const reparsedConfig = "reason" in reparsed ? void 0 : findConfigEntry(reparsed.configs, edit.config);
|
|
842
|
+
if ("reason" in reparsed || !reparsedConfig) return result;
|
|
843
|
+
return disablePlugin(split, reparsed.program, reparsedConfig, edit.plugin);
|
|
844
|
+
}
|
|
845
|
+
function enablePlugin(source, comments, config, plugin) {
|
|
846
|
+
const block = findDisabled(source, comments, config).find((entry) => entry.packageName === plugin);
|
|
847
|
+
if (!block) return { reason: `${plugin} is not disabled` };
|
|
848
|
+
const lines = source.split("\n");
|
|
849
|
+
lines.splice(block.line - 1, block.end - block.line + 1, ...block.code);
|
|
850
|
+
return { source: lines.join("\n") };
|
|
851
|
+
}
|
|
852
|
+
/** Drops the import statements of `packageName` once nothing else in the file uses their names. */
|
|
853
|
+
function removeUnusedImports(source, packageName) {
|
|
854
|
+
const parsed = parseSource(source);
|
|
855
|
+
if ("reason" in parsed) return source;
|
|
856
|
+
return parsed.program.body.reduceRight((current, statement) => {
|
|
857
|
+
const bindings = statement.type === nodeTypes.importDeclaration ? importBindings(statement) : statement.type === nodeTypes.variableDeclaration ? requireBindings(statement) : [];
|
|
858
|
+
if (!bindings.length || bindings.some(([, { from }]) => from !== packageName)) return current;
|
|
859
|
+
const rest = replaceText(current, statement.start, statement.end);
|
|
860
|
+
if (bindings.some(([local]) => new RegExp(`(?<![\\w$])${local.replaceAll("$", "\\$")}(?![\\w$])`).test(rest))) return current;
|
|
861
|
+
const lineStart = current.lastIndexOf("\n", statement.start - 1) + 1;
|
|
862
|
+
const lineEnd = current.indexOf("\n", statement.end);
|
|
863
|
+
const after = lineEnd === -1 ? current.length : lineEnd + 1;
|
|
864
|
+
return replaceText(current, lineStart, lineStart === 0 && current[after] === "\n" ? after + 1 : after);
|
|
865
|
+
}, source);
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Removes every call of a plugin or parser package, and the block `disable-plugin` commented out,
|
|
869
|
+
* then its unused imports. Removing what the config does not hold is a no-op. Refuses to empty
|
|
870
|
+
* `parsers`, since a config with no parser cannot print files.
|
|
871
|
+
*/
|
|
872
|
+
function removeExtension(source, edit) {
|
|
873
|
+
const kind = edit.kind ?? extensionKinds.plugin;
|
|
874
|
+
let current = source;
|
|
875
|
+
for (;;) {
|
|
876
|
+
const parsed = findConfigs(current);
|
|
877
|
+
if ("reason" in parsed) return parsed;
|
|
878
|
+
const config = findConfigEntry(parsed.configs, edit.config);
|
|
879
|
+
if (!config) return { reason: `no config entry found for ${JSON.stringify(edit.config)}` };
|
|
880
|
+
const array = getValue(config, `${kind}s`);
|
|
881
|
+
const calls = findCalls(parsed.program, config, kind).filter((entry) => entry.packageName === edit.plugin);
|
|
882
|
+
const block = findDisabled(current, parsed.comments, config).find((entry) => entry.packageName === edit.plugin);
|
|
883
|
+
if (calls[0] && array?.type === nodeTypes.arrayExpression) {
|
|
884
|
+
const elements = array.elements.filter((element) => element !== null);
|
|
885
|
+
if (kind === extensionKinds.parser && elements.length === calls.length) return { reason: `${edit.plugin} is the last parser in the parsers array` };
|
|
886
|
+
current = removeItem(current, array, elements.indexOf(calls[0].call));
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (block && kind === extensionKinds.plugin) {
|
|
890
|
+
const lines = current.split("\n");
|
|
891
|
+
lines.splice(block.line - 1, block.end - block.line + 1);
|
|
892
|
+
current = lines.join("\n");
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
return { source: current === source ? source : removeUnusedImports(current, edit.plugin) };
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/** Every edit re-parses the file, since the previous one rewrote it. */
|
|
899
|
+
function applyEdit({ source, edit, extensions }) {
|
|
900
|
+
const parsed = findConfigs(source);
|
|
901
|
+
if ("reason" in parsed) return parsed;
|
|
902
|
+
const config = findConfigEntry(parsed.configs, edit.config);
|
|
903
|
+
if (!config) return { reason: `no config entry found for ${JSON.stringify(edit.config)}` };
|
|
904
|
+
const quote = detectQuote(parsed.program) ?? quotes.single;
|
|
905
|
+
switch (edit.operation) {
|
|
906
|
+
case ConfigEditOperation.SET_INPUT: return setInput(source, config, edit.input, quote);
|
|
907
|
+
case ConfigEditOperation.SET_OUTPUT:
|
|
908
|
+
case ConfigEditOperation.REMOVE_OUTPUT: return editOutput({
|
|
909
|
+
source,
|
|
910
|
+
config,
|
|
911
|
+
edit,
|
|
912
|
+
quote
|
|
913
|
+
});
|
|
914
|
+
case ConfigEditOperation.DISABLE_PLUGIN: return disablePluginInConfig({
|
|
915
|
+
source,
|
|
916
|
+
parsed,
|
|
917
|
+
config,
|
|
918
|
+
edit
|
|
919
|
+
});
|
|
920
|
+
case ConfigEditOperation.ENABLE_PLUGIN: return enablePlugin(source, parsed.comments, config, edit.plugin);
|
|
921
|
+
case ConfigEditOperation.ADD_PLUGIN: return addPlugin({
|
|
922
|
+
source,
|
|
923
|
+
parsed,
|
|
924
|
+
config,
|
|
925
|
+
edit,
|
|
926
|
+
quote,
|
|
927
|
+
extensions
|
|
928
|
+
});
|
|
929
|
+
case ConfigEditOperation.REMOVE_EXTENSION: return removeExtension(source, edit);
|
|
930
|
+
case ConfigEditOperation.SET_OPTION:
|
|
931
|
+
case ConfigEditOperation.REMOVE_OPTION: return editOption({
|
|
932
|
+
source,
|
|
933
|
+
parsed,
|
|
934
|
+
config,
|
|
935
|
+
edit,
|
|
936
|
+
quote,
|
|
937
|
+
extensions
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
/** Applies edits in place, touching only the text each targets. A refused edit is reported and the rest still run. */
|
|
942
|
+
function editConfig({ source, edits, extensions = /* @__PURE__ */ new Map() }) {
|
|
943
|
+
let current = source;
|
|
944
|
+
const outcomes = edits.map((edit) => {
|
|
945
|
+
const invalid = validateEdit(edit);
|
|
946
|
+
if (invalid) return {
|
|
947
|
+
edit,
|
|
948
|
+
applied: false,
|
|
949
|
+
reason: invalid
|
|
950
|
+
};
|
|
951
|
+
try {
|
|
952
|
+
const result = applyEdit({
|
|
953
|
+
source: current,
|
|
954
|
+
edit,
|
|
955
|
+
extensions
|
|
956
|
+
});
|
|
957
|
+
if ("reason" in result) return {
|
|
958
|
+
edit,
|
|
959
|
+
applied: false,
|
|
960
|
+
reason: result.reason
|
|
961
|
+
};
|
|
962
|
+
current = result.source;
|
|
963
|
+
return {
|
|
964
|
+
edit,
|
|
965
|
+
applied: true
|
|
966
|
+
};
|
|
967
|
+
} catch (error) {
|
|
968
|
+
return {
|
|
969
|
+
edit,
|
|
970
|
+
applied: false,
|
|
971
|
+
reason: getErrorMessage(error)
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
return {
|
|
976
|
+
source: current,
|
|
977
|
+
outcomes,
|
|
978
|
+
changed: current !== source
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
/** Applies `edits` to `filePath`, writing only when something changed. */
|
|
982
|
+
async function editConfigFile({ filePath, edits, extensions }) {
|
|
983
|
+
const result = editConfig({
|
|
984
|
+
source: await read(filePath),
|
|
985
|
+
edits,
|
|
986
|
+
extensions
|
|
987
|
+
});
|
|
988
|
+
if (result.changed) await writeRaw(filePath, result.source);
|
|
989
|
+
return result;
|
|
990
|
+
}
|
|
991
|
+
export { ConfigEditOperation as a, readImports as i, editConfigFile as n, extensionKinds as o, readConfig as r, editConfig as t };
|
|
992
|
+
|
|
993
|
+
//# sourceMappingURL=configFile-C0DIXNMD.js.map
|