@highstate/cli 0.9.18 → 0.9.19
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/assets/tsconfig.base.json +7 -2
- package/dist/highstate.manifest.json +1 -1
- package/dist/{library-loader-ZABUULFB.js → library-loader-6TJTW2HX.js} +3 -3
- package/dist/library-loader-6TJTW2HX.js.map +1 -0
- package/dist/main.js +391 -170
- package/dist/main.js.map +1 -1
- package/package.json +5 -5
- package/src/commands/backend/identity.ts +1 -1
- package/src/commands/package/create.ts +35 -0
- package/src/commands/package/index.ts +4 -0
- package/src/commands/package/list.ts +41 -0
- package/src/commands/package/remove.ts +40 -0
- package/src/commands/package/update-references.ts +18 -0
- package/src/main.ts +10 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/library-loader.ts +1 -1
- package/src/shared/schema-transformer.spec.ts +178 -35
- package/src/shared/schema-transformer.ts +250 -244
- package/src/shared/source-hash-calculator.ts +0 -1
- package/src/shared/workspace.ts +240 -0
- package/dist/library-loader-ZABUULFB.js.map +0 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Plugin } from "esbuild"
|
|
2
2
|
import { readFile } from "node:fs/promises"
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
parseAsync,
|
|
5
|
+
type Comment,
|
|
6
|
+
type CallExpression,
|
|
7
|
+
type ObjectProperty,
|
|
8
|
+
type MemberExpression,
|
|
9
|
+
} from "oxc-parser"
|
|
4
10
|
import { walk, type Node } from "oxc-walker"
|
|
5
11
|
import MagicString from "magic-string"
|
|
6
12
|
|
|
@@ -18,276 +24,240 @@ export const schemaTransformerPlugin: Plugin = {
|
|
|
18
24
|
},
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
export async function applySchemaTransformations(content: string): Promise<string> {
|
|
28
|
+
// first pass: apply zod meta transformations
|
|
29
|
+
let result = await applyZodMetaTransformations(content)
|
|
30
|
+
|
|
31
|
+
// second pass: apply helper function transformations
|
|
32
|
+
result = await applyHelperFunctionTransformations(result)
|
|
33
|
+
|
|
34
|
+
// third pass: apply define function meta transformations
|
|
35
|
+
result = await applyDefineFunctionMetaTransformations(result)
|
|
36
|
+
|
|
37
|
+
return result
|
|
26
38
|
}
|
|
27
39
|
|
|
28
|
-
|
|
40
|
+
async function applyZodMetaTransformations(content: string): Promise<string> {
|
|
29
41
|
const { program, comments } = await parseAsync("file.ts", content)
|
|
30
|
-
|
|
31
|
-
const transformations: Transformation[] = []
|
|
32
42
|
const parentStack: Node[] = []
|
|
43
|
+
let hasTransformations = false
|
|
44
|
+
const result = new MagicString(content)
|
|
33
45
|
|
|
34
46
|
walk(program, {
|
|
35
47
|
enter(node) {
|
|
36
|
-
parentStack.push(node)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
) {
|
|
42
|
-
const jsdoc = comments.find(comment => isLeadingComment(content, node, comment))
|
|
48
|
+
parentStack.push(node)
|
|
49
|
+
|
|
50
|
+
// handle zod object patterns
|
|
51
|
+
if (isZodObjectProperty(node, parentStack)) {
|
|
52
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
43
53
|
if (jsdoc) {
|
|
44
54
|
const description = cleanJsdoc(jsdoc.value)
|
|
45
|
-
const fieldName =
|
|
55
|
+
const fieldName =
|
|
56
|
+
"name" in node.key && typeof node.key.name === "string" ? node.key.name : "unknown"
|
|
46
57
|
const originalValue = content.substring(node.value.start, node.value.end)
|
|
47
58
|
|
|
48
|
-
// Check if the field already has .meta() call
|
|
49
59
|
if (!originalValue.includes(".meta(")) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
newValue: `${originalValue}.meta({ title: __camelCaseToHumanReadable("${fieldName}"), description: \`${description}\` })`,
|
|
54
|
-
type: "zod-meta",
|
|
55
|
-
})
|
|
60
|
+
const newValue = `${originalValue}.meta({ title: __camelCaseToHumanReadable("${fieldName}"), description: \`${description}\` })`
|
|
61
|
+
result.update(node.value.start, node.value.end, newValue)
|
|
62
|
+
hasTransformations = true
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (node.type !== "Property" || node.key.type !== "Identifier") {
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const parentKey = getParentObjectKey(parentStack) || getMarkerFunctionName(parentStack)
|
|
66
|
-
if (!parentKey || !["inputs", "outputs", "args", "secrets"].includes(parentKey)) {
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const jsdoc = comments.find(comment => isLeadingComment(content, node, comment))
|
|
71
|
-
if (!jsdoc) {
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const description = cleanJsdoc(jsdoc.value)
|
|
76
|
-
const originalValue = content.substring(node.value.start, node.value.end)
|
|
77
|
-
|
|
78
|
-
const entityField = ["inputs", "outputs"].includes(parentKey) ? "entity" : "schema"
|
|
79
|
-
|
|
80
|
-
// Check if the value already has entity/schema structure
|
|
81
|
-
const isAlreadyStructured = isStructuredValue(originalValue, entityField)
|
|
82
|
-
|
|
83
|
-
if (isAlreadyStructured) {
|
|
84
|
-
// For already structured values, inject description directly into the object
|
|
85
|
-
const modifiedValue = injectDescriptionIntoObject(originalValue, description)
|
|
86
|
-
transformations.push({
|
|
87
|
-
start: node.value.start,
|
|
88
|
-
end: node.value.end,
|
|
89
|
-
newValue: modifiedValue,
|
|
90
|
-
type: "schema-structure",
|
|
91
|
-
})
|
|
92
|
-
} else {
|
|
93
|
-
// Transform to new structure
|
|
94
|
-
transformations.push({
|
|
95
|
-
start: node.value.start,
|
|
96
|
-
end: node.value.end,
|
|
97
|
-
newValue: `{
|
|
98
|
-
${entityField}: ${originalValue},
|
|
99
|
-
meta: {
|
|
100
|
-
description: \`${description}\`,
|
|
101
|
-
},
|
|
102
|
-
}`,
|
|
103
|
-
type: "schema-structure",
|
|
104
|
-
})
|
|
105
65
|
}
|
|
106
66
|
},
|
|
107
67
|
leave() {
|
|
108
68
|
parentStack.pop()
|
|
109
69
|
},
|
|
110
|
-
}) // Handle overlapping transformations by merging them
|
|
111
|
-
const zodMetaTransformations = transformations.filter(t => t.type === "zod-meta")
|
|
112
|
-
const schemaTransformations = transformations.filter(t => t.type === "schema-structure")
|
|
113
|
-
|
|
114
|
-
// For each schema transformation, check if it contains zod-meta transformations
|
|
115
|
-
const processedSchemaTransformations = schemaTransformations.map(schemaTransform => {
|
|
116
|
-
const containedZodMetas = zodMetaTransformations.filter(zodTransform => {
|
|
117
|
-
return schemaTransform.start <= zodTransform.start && schemaTransform.end >= zodTransform.end
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
if (containedZodMetas.length > 0) {
|
|
121
|
-
// Apply the zod-meta transformations to the original content first
|
|
122
|
-
const originalContent = content.substring(schemaTransform.start, schemaTransform.end)
|
|
123
|
-
const tempMagicString = new MagicString(originalContent)
|
|
124
|
-
|
|
125
|
-
// Adjust positions relative to the schema transformation start
|
|
126
|
-
containedZodMetas
|
|
127
|
-
.sort((a, b) => b.start - a.start)
|
|
128
|
-
.forEach(zodTransform => {
|
|
129
|
-
const relativeStart = zodTransform.start - schemaTransform.start
|
|
130
|
-
const relativeEnd = zodTransform.end - schemaTransform.start
|
|
131
|
-
tempMagicString.update(relativeStart, relativeEnd, zodTransform.newValue)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
const modifiedContent = tempMagicString.toString()
|
|
135
|
-
|
|
136
|
-
// Now create the schema transformation with the modified content
|
|
137
|
-
const entityField = schemaTransform.newValue.includes("entity:") ? "entity" : "schema"
|
|
138
|
-
const descriptionMatch = schemaTransform.newValue.match(/description: `([^`]+)`/)
|
|
139
|
-
|
|
140
|
-
if (descriptionMatch) {
|
|
141
|
-
const description = descriptionMatch[1]
|
|
142
|
-
|
|
143
|
-
// Check if the original content is already structured
|
|
144
|
-
const isAlreadyStructured = isStructuredValue(
|
|
145
|
-
content.substring(schemaTransform.start, schemaTransform.end),
|
|
146
|
-
entityField,
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
if (isAlreadyStructured) {
|
|
150
|
-
// Just inject the description into the existing structure with modified content
|
|
151
|
-
return {
|
|
152
|
-
...schemaTransform,
|
|
153
|
-
newValue: injectDescriptionIntoObject(modifiedContent, description),
|
|
154
|
-
containsZodMeta: true,
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
// Create new structure with modified content
|
|
158
|
-
return {
|
|
159
|
-
...schemaTransform,
|
|
160
|
-
newValue: `{
|
|
161
|
-
${entityField}: ${modifiedContent},
|
|
162
|
-
meta: {
|
|
163
|
-
description: \`${description}\`,
|
|
164
|
-
},
|
|
165
|
-
}`,
|
|
166
|
-
containsZodMeta: true,
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return { ...schemaTransform, containsZodMeta: false }
|
|
173
70
|
})
|
|
174
71
|
|
|
175
|
-
|
|
176
|
-
const independentZodMetas = zodMetaTransformations.filter(zodTransform => {
|
|
177
|
-
return !schemaTransformations.some(schemaTransform => {
|
|
178
|
-
return schemaTransform.start <= zodTransform.start && schemaTransform.end >= zodTransform.end
|
|
179
|
-
})
|
|
180
|
-
})
|
|
72
|
+
let finalResult = result.toString()
|
|
181
73
|
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
74
|
+
// add import at the beginning if needed
|
|
75
|
+
if (hasTransformations && !content.includes("__camelCaseToHumanReadable")) {
|
|
76
|
+
finalResult =
|
|
77
|
+
'import { camelCaseToHumanReadable as __camelCaseToHumanReadable } from "@highstate/contract"\n' +
|
|
78
|
+
finalResult
|
|
79
|
+
}
|
|
187
80
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const needsImport = hasZodMetaTransformations && !content.includes("__camelCaseToHumanReadable")
|
|
81
|
+
return finalResult
|
|
82
|
+
}
|
|
191
83
|
|
|
192
|
-
|
|
193
|
-
const
|
|
84
|
+
async function applyHelperFunctionTransformations(content: string): Promise<string> {
|
|
85
|
+
const { program, comments } = await parseAsync("file.ts", content)
|
|
86
|
+
const parentStack: Node[] = []
|
|
87
|
+
let hasTransformations = false
|
|
88
|
+
const result = new MagicString(content)
|
|
194
89
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
90
|
+
walk(program, {
|
|
91
|
+
enter(node) {
|
|
92
|
+
parentStack.push(node)
|
|
93
|
+
|
|
94
|
+
// handle properties in args, inputs, outputs, secrets objects
|
|
95
|
+
if (node.type === "Property" && "key" in node && node.key?.type === "Identifier") {
|
|
96
|
+
const propertyNode = node
|
|
97
|
+
const parentKey = getParentObjectKey(parentStack)
|
|
98
|
+
|
|
99
|
+
if (parentKey && ["inputs", "outputs", "args", "secrets"].includes(parentKey)) {
|
|
100
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
101
|
+
|
|
102
|
+
if (jsdoc) {
|
|
103
|
+
const description = cleanJsdoc(jsdoc.value)
|
|
104
|
+
const originalValue = content.substring(
|
|
105
|
+
propertyNode.value.start,
|
|
106
|
+
propertyNode.value.end,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
let helperFunction: string
|
|
110
|
+
if (["args", "secrets"].includes(parentKey)) {
|
|
111
|
+
helperFunction = "$addArgumentDescription"
|
|
112
|
+
} else {
|
|
113
|
+
helperFunction = "$addInputDescription"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const newValue = `${helperFunction}(${originalValue}, \`${description}\`)`
|
|
117
|
+
result.update(propertyNode.value.start, propertyNode.value.end, newValue)
|
|
118
|
+
hasTransformations = true
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
leave() {
|
|
124
|
+
parentStack.pop()
|
|
125
|
+
},
|
|
126
|
+
})
|
|
201
127
|
|
|
202
|
-
|
|
128
|
+
let finalResult = result.toString()
|
|
203
129
|
|
|
204
|
-
//
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
'import {
|
|
208
|
-
|
|
130
|
+
// add import at the beginning if needed
|
|
131
|
+
if (hasTransformations && !content.includes("$addArgumentDescription")) {
|
|
132
|
+
finalResult =
|
|
133
|
+
'import { $addArgumentDescription, $addInputDescription } from "@highstate/contract"\n' +
|
|
134
|
+
finalResult
|
|
209
135
|
}
|
|
210
136
|
|
|
211
|
-
return
|
|
137
|
+
return finalResult
|
|
212
138
|
}
|
|
213
139
|
|
|
214
|
-
function
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const metaRegex = /meta\s*:\s*\{/
|
|
219
|
-
|
|
220
|
-
if (metaRegex.test(trimmed)) {
|
|
221
|
-
// Check if the meta field already has a description
|
|
222
|
-
const hasDescription = /meta\s*:\s*\{[^}]*description\s*:/.test(trimmed)
|
|
223
|
-
|
|
224
|
-
if (hasDescription) {
|
|
225
|
-
// Replace existing description
|
|
226
|
-
return trimmed.replace(/description\s*:\s*`[^`]*`/, `description: \`${description}\``)
|
|
227
|
-
} else {
|
|
228
|
-
// Add description at the beginning of the meta object
|
|
229
|
-
return trimmed.replace(
|
|
230
|
-
/meta\s*:\s*\{/,
|
|
231
|
-
`meta: {
|
|
232
|
-
description: \`${description}\`,`,
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
// Add meta field at the end of the object (before the closing brace)
|
|
237
|
-
const lastBraceIndex = trimmed.lastIndexOf("}")
|
|
238
|
-
if (lastBraceIndex === -1) {
|
|
239
|
-
// Invalid object structure, return as is
|
|
240
|
-
return trimmed
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const beforeBrace = trimmed.substring(0, lastBraceIndex)
|
|
244
|
-
const afterBrace = trimmed.substring(lastBraceIndex)
|
|
140
|
+
async function applyDefineFunctionMetaTransformations(content: string): Promise<string> {
|
|
141
|
+
const { program, comments } = await parseAsync("file.ts", content)
|
|
142
|
+
const parentStack: Node[] = []
|
|
143
|
+
const result = new MagicString(content)
|
|
245
144
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
145
|
+
walk(program, {
|
|
146
|
+
enter(node) {
|
|
147
|
+
parentStack.push(node)
|
|
148
|
+
|
|
149
|
+
// handle defineUnit, defineEntity, defineComponent function calls
|
|
150
|
+
if (node.type === "CallExpression" && "callee" in node && node.callee.type === "Identifier") {
|
|
151
|
+
const callNode = node
|
|
152
|
+
const callee = callNode.callee
|
|
153
|
+
const functionName =
|
|
154
|
+
"name" in callee && typeof callee.name === "string" ? callee.name : undefined
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
functionName &&
|
|
158
|
+
["defineUnit", "defineEntity", "defineComponent"].includes(functionName)
|
|
159
|
+
) {
|
|
160
|
+
// look for JSDoc comment on the parent declaration/export, not the function call itself
|
|
161
|
+
const jsdoc = findJsdocForDefineFunction(content, parentStack, comments)
|
|
162
|
+
|
|
163
|
+
if (jsdoc && callNode.arguments && callNode.arguments.length > 0) {
|
|
164
|
+
const description = cleanJsdoc(jsdoc.value)
|
|
165
|
+
const firstArg = callNode.arguments[0]
|
|
166
|
+
|
|
167
|
+
// find meta property in the object expression
|
|
168
|
+
if (firstArg.type === "ObjectExpression" && "properties" in firstArg) {
|
|
169
|
+
const properties = firstArg.properties
|
|
170
|
+
const metaProperty = properties?.find(
|
|
171
|
+
prop =>
|
|
172
|
+
prop.type === "Property" &&
|
|
173
|
+
"key" in prop &&
|
|
174
|
+
prop.key?.type === "Identifier" &&
|
|
175
|
+
prop.key?.name === "meta",
|
|
176
|
+
) as ObjectProperty | undefined
|
|
177
|
+
|
|
178
|
+
if (metaProperty && "value" in metaProperty) {
|
|
179
|
+
// inject description into existing meta object
|
|
180
|
+
const originalMetaValue = content.substring(
|
|
181
|
+
metaProperty.value.start,
|
|
182
|
+
metaProperty.value.end,
|
|
183
|
+
)
|
|
184
|
+
const newMetaValue = injectDescriptionIntoMetaObject(originalMetaValue, description)
|
|
185
|
+
result.update(metaProperty.value.start, metaProperty.value.end, newMetaValue)
|
|
186
|
+
} else if (properties && properties.length > 0) {
|
|
187
|
+
// add meta property with description
|
|
188
|
+
const lastProperty = properties[properties.length - 1] as ObjectProperty
|
|
189
|
+
if (lastProperty && "end" in lastProperty) {
|
|
190
|
+
const insertPos = lastProperty.end
|
|
191
|
+
const newMetaProperty = `,
|
|
249
192
|
|
|
250
|
-
return `${beforeBrace}${comma}
|
|
251
193
|
meta: {
|
|
252
194
|
description: \`${description}\`,
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
195
|
+
}`
|
|
196
|
+
result.appendLeft(insertPos, newMetaProperty)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
leave() {
|
|
205
|
+
parentStack.pop()
|
|
206
|
+
},
|
|
207
|
+
})
|
|
263
208
|
|
|
264
|
-
|
|
265
|
-
const fieldPattern = new RegExp(`^\\s*{[^}]*\\b${expectedField}\\s*:`, "s")
|
|
266
|
-
return fieldPattern.test(trimmed)
|
|
209
|
+
return result.toString()
|
|
267
210
|
}
|
|
268
211
|
|
|
269
|
-
function
|
|
270
|
-
|
|
212
|
+
function findJsdocForDefineFunction(
|
|
213
|
+
content: string,
|
|
214
|
+
parentStack: Node[],
|
|
215
|
+
comments: Comment[],
|
|
216
|
+
): Comment | null {
|
|
217
|
+
// look for the variable declaration or export declaration that contains the function call
|
|
271
218
|
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
272
219
|
const node = parentStack[i]
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
220
|
+
|
|
221
|
+
// check for variable declaration (const x = defineUnit(...))
|
|
222
|
+
if (node.type === "VariableDeclarator" && "id" in node && node.id?.type === "Identifier") {
|
|
223
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
224
|
+
if (jsdoc) return jsdoc
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// check for export variable declaration (export const x = defineUnit(...))
|
|
228
|
+
if (node.type === "VariableDeclaration") {
|
|
229
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
230
|
+
if (jsdoc) return jsdoc
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// check for export declaration (export const x = ...)
|
|
234
|
+
if (node.type === "ExportNamedDeclaration" && "declaration" in node && node.declaration) {
|
|
235
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
236
|
+
if (jsdoc) return jsdoc
|
|
281
237
|
}
|
|
282
238
|
}
|
|
239
|
+
|
|
283
240
|
return null
|
|
284
241
|
}
|
|
285
242
|
|
|
243
|
+
function isZodObjectProperty(node: Node, parentStack: Node[]): node is ObjectProperty {
|
|
244
|
+
return (
|
|
245
|
+
node.type === "Property" &&
|
|
246
|
+
"key" in node &&
|
|
247
|
+
node.key?.type === "Identifier" &&
|
|
248
|
+
isInsideZodObject(parentStack)
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function findLeadingComment(content: string, node: Node, comments: Comment[]): Comment | null {
|
|
253
|
+
return comments.find(comment => isLeadingComment(content, node, comment)) ?? null
|
|
254
|
+
}
|
|
255
|
+
|
|
286
256
|
function getParentObjectKey(parentStack: Node[]): string | null {
|
|
287
|
-
//
|
|
257
|
+
// walk up the parent stack to find the parent object property
|
|
288
258
|
for (let i = parentStack.length - 2; i >= 0; i--) {
|
|
289
259
|
const node = parentStack[i]
|
|
290
|
-
if (node.type === "Property" && node.key.type === "Identifier") {
|
|
260
|
+
if (node.type === "Property" && "key" in node && node.key.type === "Identifier") {
|
|
291
261
|
return node.key.name
|
|
292
262
|
}
|
|
293
263
|
}
|
|
@@ -318,12 +288,37 @@ function cleanJsdoc(str: string) {
|
|
|
318
288
|
)
|
|
319
289
|
}
|
|
320
290
|
|
|
291
|
+
function injectDescriptionIntoMetaObject(objectString: string, description: string): string {
|
|
292
|
+
const trimmed = objectString.trim()
|
|
293
|
+
|
|
294
|
+
// Check if description already exists
|
|
295
|
+
const hasDescription = /description\s*:/.test(trimmed)
|
|
296
|
+
|
|
297
|
+
if (hasDescription) {
|
|
298
|
+
// Replace existing description
|
|
299
|
+
return trimmed.replace(/description\s*:\s*`[^`]*`/, `description: \`${description}\``)
|
|
300
|
+
} else {
|
|
301
|
+
// Add description field at the beginning of the object
|
|
302
|
+
const openBraceIndex = trimmed.indexOf("{")
|
|
303
|
+
if (openBraceIndex === -1) {
|
|
304
|
+
return trimmed
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const beforeBrace = trimmed.substring(0, openBraceIndex + 1)
|
|
308
|
+
const afterBrace = trimmed.substring(openBraceIndex + 1)
|
|
309
|
+
|
|
310
|
+
return `${beforeBrace}
|
|
311
|
+
description: \`${description}\`,${afterBrace}`
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
321
315
|
function isInsideZodObject(parentStack: Node[]): boolean {
|
|
322
|
-
//
|
|
316
|
+
// look for z.object() call expression in the parent stack
|
|
323
317
|
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
324
318
|
const node = parentStack[i]
|
|
325
319
|
if (
|
|
326
320
|
node.type === "CallExpression" &&
|
|
321
|
+
"callee" in node &&
|
|
327
322
|
node.callee.type === "MemberExpression" &&
|
|
328
323
|
isZodObjectCall(node.callee)
|
|
329
324
|
) {
|
|
@@ -334,52 +329,63 @@ function isInsideZodObject(parentStack: Node[]): boolean {
|
|
|
334
329
|
}
|
|
335
330
|
|
|
336
331
|
function isZodObjectCall(memberExpression: Node): boolean {
|
|
337
|
-
if (
|
|
332
|
+
if (
|
|
333
|
+
memberExpression.type !== "MemberExpression" ||
|
|
334
|
+
!("object" in memberExpression) ||
|
|
335
|
+
!("property" in memberExpression)
|
|
336
|
+
) {
|
|
338
337
|
return false
|
|
339
338
|
}
|
|
340
339
|
|
|
341
|
-
|
|
340
|
+
const member = memberExpression as MemberExpression
|
|
341
|
+
|
|
342
|
+
// handle direct z.object() calls
|
|
342
343
|
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
member.object.type === "Identifier" &&
|
|
345
|
+
"name" in member.object &&
|
|
346
|
+
member.object.name === "z" &&
|
|
347
|
+
member.property.type === "Identifier" &&
|
|
348
|
+
member.property.name === "object"
|
|
347
349
|
) {
|
|
348
350
|
return true
|
|
349
351
|
}
|
|
350
352
|
|
|
351
|
-
//
|
|
353
|
+
// handle chained calls like z.discriminatedUnion().default().object()
|
|
352
354
|
// or any other chained Zod methods that end with .object()
|
|
353
355
|
if (
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
member.property.type === "Identifier" &&
|
|
357
|
+
member.property.name === "object" &&
|
|
358
|
+
member.object.type === "CallExpression" &&
|
|
359
|
+
"callee" in member.object &&
|
|
360
|
+
member.object.callee.type === "MemberExpression"
|
|
358
361
|
) {
|
|
359
|
-
//
|
|
360
|
-
return startsWithZodCall(
|
|
362
|
+
// recursively check if this is part of a z.* chain
|
|
363
|
+
return startsWithZodCall(member.object)
|
|
361
364
|
}
|
|
362
365
|
|
|
363
366
|
return false
|
|
364
367
|
}
|
|
365
368
|
|
|
366
|
-
function startsWithZodCall(callExpression:
|
|
369
|
+
function startsWithZodCall(callExpression: CallExpression): boolean {
|
|
367
370
|
if (!callExpression || callExpression.type !== "CallExpression") {
|
|
368
371
|
return false
|
|
369
372
|
}
|
|
370
373
|
|
|
371
374
|
if (callExpression.callee.type === "MemberExpression") {
|
|
372
|
-
|
|
375
|
+
const callee = callExpression.callee
|
|
376
|
+
|
|
377
|
+
// check if this is a direct z.* call
|
|
373
378
|
if (
|
|
374
|
-
|
|
375
|
-
|
|
379
|
+
callee.object.type === "Identifier" &&
|
|
380
|
+
"name" in callee.object &&
|
|
381
|
+
callee.object.name === "z"
|
|
376
382
|
) {
|
|
377
383
|
return true
|
|
378
384
|
}
|
|
379
385
|
|
|
380
|
-
//
|
|
381
|
-
if (
|
|
382
|
-
return startsWithZodCall(
|
|
386
|
+
// recursively check nested calls
|
|
387
|
+
if (callee.object.type === "CallExpression") {
|
|
388
|
+
return startsWithZodCall(callee.object)
|
|
383
389
|
}
|
|
384
390
|
}
|
|
385
391
|
|