@highstate/cli 0.9.16 → 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/chunk-CMECLVT7.js +11 -0
- package/dist/chunk-CMECLVT7.js.map +1 -0
- package/dist/highstate.manifest.json +1 -1
- package/dist/{library-loader-CGEPTS4L.js → library-loader-6TJTW2HX.js} +28 -23
- package/dist/library-loader-6TJTW2HX.js.map +1 -0
- package/dist/main.js +467 -145
- package/dist/main.js.map +1 -1
- package/package.json +8 -11
- package/src/commands/backend/identity.ts +1 -1
- package/src/commands/build.ts +1 -0
- 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 +30 -14
- package/src/shared/schema-transformer.spec.ts +449 -26
- package/src/shared/schema-transformer.ts +304 -87
- package/src/shared/source-hash-calculator.ts +1 -2
- package/src/shared/utils.ts +6 -0
- package/src/shared/workspace.ts +240 -0
- package/dist/library-loader-CGEPTS4L.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
|
|
|
@@ -19,53 +25,99 @@ export const schemaTransformerPlugin: Plugin = {
|
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
export async function applySchemaTransformations(content: string): Promise<string> {
|
|
22
|
-
|
|
23
|
-
|
|
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)
|
|
24
33
|
|
|
34
|
+
// third pass: apply define function meta transformations
|
|
35
|
+
result = await applyDefineFunctionMetaTransformations(result)
|
|
36
|
+
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function applyZodMetaTransformations(content: string): Promise<string> {
|
|
41
|
+
const { program, comments } = await parseAsync("file.ts", content)
|
|
25
42
|
const parentStack: Node[] = []
|
|
43
|
+
let hasTransformations = false
|
|
44
|
+
const result = new MagicString(content)
|
|
26
45
|
|
|
27
46
|
walk(program, {
|
|
28
47
|
enter(node) {
|
|
29
48
|
parentStack.push(node)
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
// handle zod object patterns
|
|
51
|
+
if (isZodObjectProperty(node, parentStack)) {
|
|
52
|
+
const jsdoc = findLeadingComment(content, node, comments)
|
|
53
|
+
if (jsdoc) {
|
|
54
|
+
const description = cleanJsdoc(jsdoc.value)
|
|
55
|
+
const fieldName =
|
|
56
|
+
"name" in node.key && typeof node.key.name === "string" ? node.key.name : "unknown"
|
|
57
|
+
const originalValue = content.substring(node.value.start, node.value.end)
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
if (!originalValue.includes(".meta(")) {
|
|
60
|
+
const newValue = `${originalValue}.meta({ title: __camelCaseToHumanReadable("${fieldName}"), description: \`${description}\` })`
|
|
61
|
+
result.update(node.value.start, node.value.end, newValue)
|
|
62
|
+
hasTransformations = true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
38
65
|
}
|
|
66
|
+
},
|
|
67
|
+
leave() {
|
|
68
|
+
parentStack.pop()
|
|
69
|
+
},
|
|
70
|
+
})
|
|
39
71
|
|
|
40
|
-
|
|
41
|
-
if (!jsdoc) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
72
|
+
let finalResult = result.toString()
|
|
44
73
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
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
|
+
}
|
|
80
|
+
|
|
81
|
+
return finalResult
|
|
82
|
+
}
|
|
83
|
+
|
|
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)
|
|
89
|
+
|
|
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
|
+
}
|
|
69
121
|
}
|
|
70
122
|
},
|
|
71
123
|
leave() {
|
|
@@ -73,78 +125,139 @@ export async function applySchemaTransformations(content: string): Promise<strin
|
|
|
73
125
|
},
|
|
74
126
|
})
|
|
75
127
|
|
|
76
|
-
|
|
128
|
+
let finalResult = result.toString()
|
|
129
|
+
|
|
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
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return finalResult
|
|
77
138
|
}
|
|
78
139
|
|
|
79
|
-
function
|
|
80
|
-
const
|
|
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)
|
|
81
144
|
|
|
82
|
-
|
|
83
|
-
|
|
145
|
+
walk(program, {
|
|
146
|
+
enter(node) {
|
|
147
|
+
parentStack.push(node)
|
|
84
148
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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]
|
|
99
166
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
102
177
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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 = `,
|
|
106
192
|
|
|
107
|
-
return `${beforeBrace}${comma}
|
|
108
193
|
meta: {
|
|
109
194
|
description: \`${description}\`,
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
195
|
+
}`
|
|
196
|
+
result.appendLeft(insertPos, newMetaProperty)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
leave() {
|
|
205
|
+
parentStack.pop()
|
|
206
|
+
},
|
|
207
|
+
})
|
|
120
208
|
|
|
121
|
-
|
|
122
|
-
const fieldPattern = new RegExp(`^\\s*{[^}]*\\b${expectedField}\\s*:`, "s")
|
|
123
|
-
return fieldPattern.test(trimmed)
|
|
209
|
+
return result.toString()
|
|
124
210
|
}
|
|
125
211
|
|
|
126
|
-
function
|
|
127
|
-
|
|
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
|
|
128
218
|
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
129
219
|
const node = parentStack[i]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
138
237
|
}
|
|
139
238
|
}
|
|
239
|
+
|
|
140
240
|
return null
|
|
141
241
|
}
|
|
142
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
|
+
|
|
143
256
|
function getParentObjectKey(parentStack: Node[]): string | null {
|
|
144
|
-
//
|
|
257
|
+
// walk up the parent stack to find the parent object property
|
|
145
258
|
for (let i = parentStack.length - 2; i >= 0; i--) {
|
|
146
259
|
const node = parentStack[i]
|
|
147
|
-
if (node.type === "Property" && node.key.type === "Identifier") {
|
|
260
|
+
if (node.type === "Property" && "key" in node && node.key.type === "Identifier") {
|
|
148
261
|
return node.key.name
|
|
149
262
|
}
|
|
150
263
|
}
|
|
@@ -174,3 +287,107 @@ function cleanJsdoc(str: string) {
|
|
|
174
287
|
.trim()
|
|
175
288
|
)
|
|
176
289
|
}
|
|
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
|
+
|
|
315
|
+
function isInsideZodObject(parentStack: Node[]): boolean {
|
|
316
|
+
// look for z.object() call expression in the parent stack
|
|
317
|
+
for (let i = parentStack.length - 1; i >= 0; i--) {
|
|
318
|
+
const node = parentStack[i]
|
|
319
|
+
if (
|
|
320
|
+
node.type === "CallExpression" &&
|
|
321
|
+
"callee" in node &&
|
|
322
|
+
node.callee.type === "MemberExpression" &&
|
|
323
|
+
isZodObjectCall(node.callee)
|
|
324
|
+
) {
|
|
325
|
+
return true
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return false
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isZodObjectCall(memberExpression: Node): boolean {
|
|
332
|
+
if (
|
|
333
|
+
memberExpression.type !== "MemberExpression" ||
|
|
334
|
+
!("object" in memberExpression) ||
|
|
335
|
+
!("property" in memberExpression)
|
|
336
|
+
) {
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const member = memberExpression as MemberExpression
|
|
341
|
+
|
|
342
|
+
// handle direct z.object() calls
|
|
343
|
+
if (
|
|
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"
|
|
349
|
+
) {
|
|
350
|
+
return true
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// handle chained calls like z.discriminatedUnion().default().object()
|
|
354
|
+
// or any other chained Zod methods that end with .object()
|
|
355
|
+
if (
|
|
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"
|
|
361
|
+
) {
|
|
362
|
+
// recursively check if this is part of a z.* chain
|
|
363
|
+
return startsWithZodCall(member.object)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return false
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function startsWithZodCall(callExpression: CallExpression): boolean {
|
|
370
|
+
if (!callExpression || callExpression.type !== "CallExpression") {
|
|
371
|
+
return false
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (callExpression.callee.type === "MemberExpression") {
|
|
375
|
+
const callee = callExpression.callee
|
|
376
|
+
|
|
377
|
+
// check if this is a direct z.* call
|
|
378
|
+
if (
|
|
379
|
+
callee.object.type === "Identifier" &&
|
|
380
|
+
"name" in callee.object &&
|
|
381
|
+
callee.object.name === "z"
|
|
382
|
+
) {
|
|
383
|
+
return true
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// recursively check nested calls
|
|
387
|
+
if (callee.object.type === "CallExpression") {
|
|
388
|
+
return startsWithZodCall(callee.object)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return false
|
|
393
|
+
}
|
|
@@ -6,7 +6,6 @@ import { readPackageJSON, resolvePackageJSON, type PackageJson } from "pkg-types
|
|
|
6
6
|
import { crc32 } from "@aws-crypto/crc32"
|
|
7
7
|
import { resolve as importMetaResolve } from "import-meta-resolve"
|
|
8
8
|
import { z } from "zod"
|
|
9
|
-
import { int32ToBytes } from "@highstate/backend/shared"
|
|
10
9
|
import {
|
|
11
10
|
type HighstateManifest,
|
|
12
11
|
type HighstateConfig,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
highstateManifestSchema,
|
|
16
15
|
sourceHashConfigSchema,
|
|
17
16
|
} from "./schemas"
|
|
17
|
+
import { int32ToBytes } from "./utils"
|
|
18
18
|
|
|
19
19
|
type FileDependency =
|
|
20
20
|
| {
|
|
@@ -136,7 +136,6 @@ export class SourceHashCalculator {
|
|
|
136
136
|
}),
|
|
137
137
|
)
|
|
138
138
|
break
|
|
139
|
-
case "auto":
|
|
140
139
|
default:
|
|
141
140
|
promises.push(
|
|
142
141
|
this.getFileHash(fullPath).then(hash => ({
|