@highstate/cli 0.9.15 → 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-CMECLVT7.js +11 -0
- package/dist/chunk-CMECLVT7.js.map +1 -0
- package/dist/highstate.manifest.json +1 -1
- package/dist/library-loader-ZABUULFB.js +83 -0
- package/dist/library-loader-ZABUULFB.js.map +1 -0
- package/dist/main.js +407 -1145
- package/dist/main.js.map +1 -1
- package/package.json +24 -6
- package/src/commands/backend/identity.ts +24 -0
- package/src/commands/build.ts +42 -5
- package/src/main.ts +2 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/library-loader.ts +129 -0
- package/src/shared/schema-transformer.spec.ts +489 -0
- package/src/shared/schema-transformer.ts +325 -13
- package/src/shared/schemas.ts +41 -0
- package/src/shared/source-hash-calculator.ts +129 -26
- package/src/shared/utils.ts +6 -0
package/src/commands/build.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { Plugin } from "esbuild"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
import { writeFile } from "node:fs/promises"
|
|
2
4
|
import { Command, Option } from "clipanion"
|
|
3
5
|
import { readPackageJSON, resolvePackageJSON } from "pkg-types"
|
|
4
6
|
import { mapKeys, mapValues, pipe } from "remeda"
|
|
5
7
|
import { build } from "tsup"
|
|
8
|
+
import { encode } from "@msgpack/msgpack"
|
|
6
9
|
import {
|
|
7
10
|
createBinTransformerPlugin,
|
|
8
11
|
logger,
|
|
9
12
|
schemaTransformerPlugin,
|
|
10
13
|
SourceHashCalculator,
|
|
14
|
+
highstateConfigSchema,
|
|
11
15
|
} from "../shared"
|
|
12
16
|
|
|
13
17
|
export class BuildCommand extends Command {
|
|
@@ -21,9 +25,20 @@ export class BuildCommand extends Command {
|
|
|
21
25
|
watch = Option.Boolean("--watch", false)
|
|
22
26
|
library = Option.Boolean("--library", false)
|
|
23
27
|
silent = Option.Boolean("--silent", true)
|
|
28
|
+
noSourceHash = Option.Boolean("--no-source-hash", false)
|
|
24
29
|
|
|
25
30
|
async execute(): Promise<void> {
|
|
26
31
|
const packageJson = await readPackageJSON()
|
|
32
|
+
|
|
33
|
+
const highstateConfig = highstateConfigSchema.parse(packageJson.highstate ?? {})
|
|
34
|
+
if (highstateConfig.type === "library") {
|
|
35
|
+
this.library = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (highstateConfig.type === "worker") {
|
|
39
|
+
this.noSourceHash = true
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
const exports = packageJson.exports
|
|
28
43
|
let bin = packageJson.bin
|
|
29
44
|
|
|
@@ -87,6 +102,7 @@ export class BuildCommand extends Command {
|
|
|
87
102
|
targetName,
|
|
88
103
|
distPath,
|
|
89
104
|
isBin,
|
|
105
|
+
key,
|
|
90
106
|
}
|
|
91
107
|
}),
|
|
92
108
|
mapKeys((_, value) => value.targetName),
|
|
@@ -113,25 +129,46 @@ export class BuildCommand extends Command {
|
|
|
113
129
|
sourcemap: true,
|
|
114
130
|
clean: true,
|
|
115
131
|
format: "esm",
|
|
116
|
-
target: "
|
|
132
|
+
target: "es2024",
|
|
133
|
+
platform: "node",
|
|
117
134
|
external: ["@pulumi/pulumi"],
|
|
118
135
|
esbuildPlugins,
|
|
136
|
+
treeshake: true,
|
|
137
|
+
removeNodeProtocol: false,
|
|
119
138
|
silent: this.silent || ["warn", "error", "fatal"].includes(logger.level),
|
|
120
139
|
})
|
|
121
140
|
|
|
122
141
|
const packageJsonPath = await resolvePackageJSON()
|
|
123
142
|
const upToDatePackageJson = await readPackageJSON()
|
|
124
143
|
|
|
125
|
-
|
|
126
|
-
if (!this.library) {
|
|
144
|
+
if (!this.noSourceHash) {
|
|
127
145
|
const sourceHashCalculator = new SourceHashCalculator(
|
|
128
146
|
packageJsonPath,
|
|
129
147
|
upToDatePackageJson,
|
|
130
148
|
logger,
|
|
131
149
|
)
|
|
132
|
-
const distPaths = Object.values(entry).map(value => value.distPath)
|
|
133
150
|
|
|
134
|
-
|
|
151
|
+
const distPathToExportKey = new Map<string, string>()
|
|
152
|
+
for (const value of Object.values(entry)) {
|
|
153
|
+
distPathToExportKey.set(value.distPath, value.key)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await sourceHashCalculator.writeHighstateManifest("./dist", distPathToExportKey)
|
|
135
157
|
}
|
|
158
|
+
|
|
159
|
+
// write the "highstate.library.json" file if the library flag is set
|
|
160
|
+
if (this.library) {
|
|
161
|
+
const { loadLibrary } = await import("../shared/library-loader.js")
|
|
162
|
+
const fullModulePaths = Object.values(entry).map(value => resolve(value.distPath))
|
|
163
|
+
|
|
164
|
+
logger.info("evaluating library components from modules: %s", fullModulePaths.join(", "))
|
|
165
|
+
|
|
166
|
+
const library = await loadLibrary(logger, fullModulePaths)
|
|
167
|
+
const libraryPath = resolve("./dist", "highstate.library.msgpack")
|
|
168
|
+
|
|
169
|
+
await writeFile(libraryPath, encode(library), "utf8")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
logger.info("build completed successfully")
|
|
136
173
|
}
|
|
137
174
|
}
|
package/src/main.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Builtins, Cli } from "clipanion"
|
|
|
2
2
|
import { version } from "../package.json"
|
|
3
3
|
import { DesignerCommand } from "./commands/designer"
|
|
4
4
|
import { BuildCommand } from "./commands/build"
|
|
5
|
+
import { BackendIdentityCommand } from "./commands/backend/identity"
|
|
5
6
|
|
|
6
7
|
const cli = new Cli({
|
|
7
8
|
binaryName: "highstate",
|
|
@@ -11,6 +12,7 @@ const cli = new Cli({
|
|
|
11
12
|
|
|
12
13
|
cli.register(BuildCommand)
|
|
13
14
|
cli.register(DesignerCommand)
|
|
15
|
+
cli.register(BackendIdentityCommand)
|
|
14
16
|
cli.register(Builtins.HelpCommand)
|
|
15
17
|
cli.register(Builtins.VersionCommand)
|
|
16
18
|
|
package/src/shared/index.ts
CHANGED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Logger } from "pino"
|
|
2
|
+
import console from "console"
|
|
3
|
+
import {
|
|
4
|
+
type Component,
|
|
5
|
+
type ComponentModel,
|
|
6
|
+
type Entity,
|
|
7
|
+
type EntityModel,
|
|
8
|
+
isComponent,
|
|
9
|
+
isEntity,
|
|
10
|
+
isUnitModel,
|
|
11
|
+
} from "@highstate/contract"
|
|
12
|
+
import { Crc32, crc32 } from "@aws-crypto/crc32"
|
|
13
|
+
import { encode } from "@msgpack/msgpack"
|
|
14
|
+
import { int32ToBytes } from "./utils"
|
|
15
|
+
|
|
16
|
+
export type Library = Readonly<{
|
|
17
|
+
components: Readonly<Record<string, ComponentModel>>
|
|
18
|
+
entities: Readonly<Record<string, EntityModel>>
|
|
19
|
+
}>
|
|
20
|
+
|
|
21
|
+
export async function loadLibrary(logger: Logger, modulePaths: string[]): Promise<Library> {
|
|
22
|
+
const modules: Record<string, unknown> = {}
|
|
23
|
+
for (const modulePath of modulePaths) {
|
|
24
|
+
try {
|
|
25
|
+
logger.debug({ modulePath }, "loading module")
|
|
26
|
+
modules[modulePath] = await import(modulePath)
|
|
27
|
+
|
|
28
|
+
logger.debug({ modulePath }, "module loaded")
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(error)
|
|
31
|
+
|
|
32
|
+
throw new Error(`Failed to load module "${modulePath}"`, { cause: error })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const components: Record<string, ComponentModel> = {}
|
|
37
|
+
const entities: Record<string, EntityModel> = {}
|
|
38
|
+
|
|
39
|
+
await _loadLibrary(modules, components, entities)
|
|
40
|
+
|
|
41
|
+
logger.info(
|
|
42
|
+
{
|
|
43
|
+
componentCount: Object.keys(components).length,
|
|
44
|
+
entityCount: Object.keys(entities).length,
|
|
45
|
+
},
|
|
46
|
+
"library loaded",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
logger.trace({ components, entities }, "library content")
|
|
50
|
+
|
|
51
|
+
return { components, entities }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function _loadLibrary(
|
|
55
|
+
value: unknown,
|
|
56
|
+
components: Record<string, ComponentModel>,
|
|
57
|
+
entities: Record<string, EntityModel>,
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
if (isComponent(value)) {
|
|
60
|
+
const entityHashes: number[] = []
|
|
61
|
+
for (const entity of value.entities.values()) {
|
|
62
|
+
entity.model.definitionHash ??= calculateEntityDefinitionHash(entity)
|
|
63
|
+
entityHashes.push(entity.model.definitionHash)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
components[value.model.type] = value.model
|
|
67
|
+
value.model.definitionHash = await calculateComponentDefinitionHash(value, entityHashes)
|
|
68
|
+
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (isEntity(value)) {
|
|
73
|
+
entities[value.type] = value.model
|
|
74
|
+
entities[value.type].definitionHash ??= calculateEntityDefinitionHash(value)
|
|
75
|
+
|
|
76
|
+
// @ts-expect-error remove the schema since it's not needed in the designer
|
|
77
|
+
delete value.model.schema
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof value !== "object" || value === null) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if ("_zod" in value) {
|
|
86
|
+
// this is a zod schema, we can skip it
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
for (const item of value) {
|
|
92
|
+
await _loadLibrary(item, components, entities)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const key in value) {
|
|
99
|
+
await _loadLibrary((value as Record<string, unknown>)[key], components, entities)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function calculateComponentDefinitionHash(
|
|
104
|
+
component: Component,
|
|
105
|
+
entityHashes: number[],
|
|
106
|
+
): Promise<number> {
|
|
107
|
+
const result = new Crc32()
|
|
108
|
+
|
|
109
|
+
// 1. include the full component model
|
|
110
|
+
result.update(encode(component.model))
|
|
111
|
+
|
|
112
|
+
if (!isUnitModel(component.model)) {
|
|
113
|
+
// 2. for composite components, include the content of the serialized create function
|
|
114
|
+
// const serializedCreate = await serializeFunction(component[originalCreate])
|
|
115
|
+
const serializedCreate = { text: "TODO: investigate why serializeFunction hangs" }
|
|
116
|
+
result.update(Buffer.from(serializedCreate.text))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 3. include the hashes of all entities
|
|
120
|
+
for (const entityHash of entityHashes) {
|
|
121
|
+
result.update(int32ToBytes(entityHash))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result.digest()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function calculateEntityDefinitionHash(entity: Entity): number {
|
|
128
|
+
return crc32(encode(entity.model))
|
|
129
|
+
}
|