@kite3d/engine 0.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/dist/authoring.d.ts +54 -0
- package/dist/authoring.js +165 -0
- package/dist/authoring.js.map +1 -0
- package/dist/authoringValidation.d.ts +110 -0
- package/dist/authoringValidation.js +488 -0
- package/dist/authoringValidation.js.map +1 -0
- package/dist/defaults.d.ts +2 -0
- package/dist/fileTypes.d.ts +5 -0
- package/dist/fileTypes.js +51 -0
- package/dist/fileTypes.js.map +1 -0
- package/dist/importMap.d.ts +15 -0
- package/dist/importMap.js +62 -0
- package/dist/importMap.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +2756 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.js +5 -0
- package/dist/migrations.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.js +13 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugins/GeneratorComponent.d.ts +40 -0
- package/dist/plugins/HtmlUiComponent.d.ts +52 -0
- package/dist/plugins/HtmlUiComponent.example.d.ts +0 -0
- package/dist/plugins/cannon/Cannon3DBodyComponent.d.ts +27 -0
- package/dist/plugins/cannon/Cannon3DShapeComponent.d.ts +57 -0
- package/dist/plugins/cannon/CannonPhysicsPlugin.d.ts +63 -0
- package/dist/plugins/cannon/CannonRagdollComponent.d.ts +148 -0
- package/dist/plugins/cannon/helper.d.ts +24 -0
- package/dist/plugins/cannon/threeToCannon.d.ts +63 -0
- package/dist/plugins/cannon/utils.d.ts +9 -0
- package/dist/projectFormat.js +108 -0
- package/dist/projectFormat.js.map +1 -0
- package/dist/runtime/createGame.d.ts +29 -0
- package/dist/runtime/index.d.ts +18 -0
- package/dist/runtime/migrations.d.ts +5 -0
- package/dist/runtime/nestedAssets.d.ts +26 -0
- package/dist/runtime/projectFormat.d.ts +83 -0
- package/dist/runtime/version.d.ts +1 -0
- package/dist/runtime.js +72835 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sceneSerialization.d.ts +17 -0
- package/dist/sceneSerialization.js +174 -0
- package/dist/sceneSerialization.js.map +1 -0
- package/dist/scripts.d.ts +17 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +86 -0
- package/src/authoring.ts +293 -0
- package/src/authoringValidation.ts +815 -0
- package/src/defaults.ts +277 -0
- package/src/fileTypes.ts +53 -0
- package/src/importMap.ts +105 -0
- package/src/index.ts +15 -0
- package/src/paths.ts +9 -0
- package/src/plugins/GeneratorComponent.ts +275 -0
- package/src/plugins/HtmlUiComponent.example.ts +202 -0
- package/src/plugins/HtmlUiComponent.md +219 -0
- package/src/plugins/HtmlUiComponent.ts +320 -0
- package/src/plugins/cannon/Cannon3DBodyComponent.ts +227 -0
- package/src/plugins/cannon/Cannon3DShapeComponent.ts +258 -0
- package/src/plugins/cannon/CannonPhysicsPlugin.ts +409 -0
- package/src/plugins/cannon/CannonRagdollComponent.ts +1170 -0
- package/src/plugins/cannon/helper.ts +255 -0
- package/src/plugins/cannon/threeToCannon.ts +441 -0
- package/src/plugins/cannon/utils.ts +185 -0
- package/src/runtime/createGame.ts +337 -0
- package/src/runtime/index.ts +19 -0
- package/src/runtime/migrations.ts +6 -0
- package/src/runtime/nestedAssets.ts +226 -0
- package/src/runtime/projectFormat.ts +233 -0
- package/src/runtime/version.ts +3 -0
- package/src/sceneSerialization.ts +289 -0
- package/src/scripts.ts +52 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyObject3DUserData,
|
|
3
|
+
getPartialProps,
|
|
4
|
+
IMaterial,
|
|
5
|
+
ImportResult,
|
|
6
|
+
ImportResultExtras,
|
|
7
|
+
IObject3D,
|
|
8
|
+
Quaternion,
|
|
9
|
+
setPartialProps,
|
|
10
|
+
ThreeViewer,
|
|
11
|
+
Vector3,
|
|
12
|
+
} from 'threepipe'
|
|
13
|
+
|
|
14
|
+
type RuntimeErrorHandler = (error: unknown) => void
|
|
15
|
+
|
|
16
|
+
const defaultObjectOverrides = ['visible', 'name', 'position', 'quaternion', 'scale']
|
|
17
|
+
const defaultMaterialOverrides = ['name']
|
|
18
|
+
const objectProperties: (keyof IObject3D)[] = [
|
|
19
|
+
'position',
|
|
20
|
+
'quaternion',
|
|
21
|
+
'scale',
|
|
22
|
+
'visible',
|
|
23
|
+
'castShadow',
|
|
24
|
+
'receiveShadow',
|
|
25
|
+
'frustumCulled',
|
|
26
|
+
'renderOrder',
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Loads the embedded rootPath references written by the editor's AssetTracker.
|
|
31
|
+
* This is deliberately runtime-only: it keeps root overrides, but has no file
|
|
32
|
+
* watching, picking, export hooks, refresh subscriptions, or editor registry.
|
|
33
|
+
*/
|
|
34
|
+
export class RuntimeNestedAssetLoader {
|
|
35
|
+
private readonly importer: ThreeViewer['assetManager']['importer']
|
|
36
|
+
private readonly cache = new Map<string, Promise<ImportResult | undefined>>()
|
|
37
|
+
private readonly pending = new Set<Promise<void>>()
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private readonly viewer: ThreeViewer,
|
|
41
|
+
private readonly onError: RuntimeErrorHandler,
|
|
42
|
+
) {
|
|
43
|
+
this.importer = viewer.assetManager.importer
|
|
44
|
+
this.importer.addEventListener('processRaw', this.onProcessRaw)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
dispose() {
|
|
48
|
+
this.importer.removeEventListener('processRaw', this.onProcessRaw)
|
|
49
|
+
this.cache.clear()
|
|
50
|
+
this.pending.clear()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async loadObjectDependencies(object: IObject3D): Promise<void> {
|
|
54
|
+
if (!object.traverse) return
|
|
55
|
+
|
|
56
|
+
const objects: IObject3D[] = []
|
|
57
|
+
const materials = new Set<IMaterial>()
|
|
58
|
+
object.traverse((child: IObject3D) => {
|
|
59
|
+
objects.push(child)
|
|
60
|
+
const material = (child as IObject3D & {material?: IMaterial | IMaterial[]}).material
|
|
61
|
+
if (material) {
|
|
62
|
+
const entries = Array.isArray(material) ? material : [material]
|
|
63
|
+
entries.forEach((entry) => entry?.isMaterial && materials.add(entry))
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const loads: Promise<void>[] = []
|
|
68
|
+
for (const child of objects) {
|
|
69
|
+
if (!this.isEmbeddedReference(child)) continue
|
|
70
|
+
if (child._loadingPromise) {
|
|
71
|
+
loads.push(child._loadingPromise.then(() => undefined))
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!(child.userData.sProperties as string[]).length) {
|
|
76
|
+
child.userData.sProperties = [...defaultObjectOverrides]
|
|
77
|
+
}
|
|
78
|
+
child._sChildren ||= []
|
|
79
|
+
const load = this.loadReference(child)
|
|
80
|
+
child._loadingPromise = load
|
|
81
|
+
loads.push(load)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const material of materials) {
|
|
85
|
+
if (!this.isEmbeddedReference(material)) continue
|
|
86
|
+
if (material._loadingPromise) {
|
|
87
|
+
loads.push(material._loadingPromise.then(() => undefined))
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!(material.userData.sProperties as string[]).length) {
|
|
92
|
+
material.userData.sProperties = [...defaultMaterialOverrides]
|
|
93
|
+
}
|
|
94
|
+
const load = this.loadReference(material)
|
|
95
|
+
material._loadingPromise = load
|
|
96
|
+
loads.push(load)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!loads.length) return
|
|
100
|
+
const loading = Promise.allSettled(loads).then((results) => {
|
|
101
|
+
for (const result of results) {
|
|
102
|
+
if (result.status === 'rejected') this.onError(result.reason)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
object._loadingPromise = loading
|
|
106
|
+
this.track(loading)
|
|
107
|
+
await loading
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async waitForPending(): Promise<void> {
|
|
111
|
+
while (this.pending.size) {
|
|
112
|
+
await Promise.all([...this.pending])
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private readonly onProcessRaw = (event: {data?: ImportResult}) => {
|
|
117
|
+
const object = event.data
|
|
118
|
+
if (!object?.isObject3D) return
|
|
119
|
+
const loading = this.loadObjectDependencies(object as IObject3D)
|
|
120
|
+
this.track(loading)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private isEmbeddedReference(value: IObject3D | IMaterial): boolean {
|
|
124
|
+
const rootPath = value.userData?.rootPath
|
|
125
|
+
return typeof rootPath === 'string'
|
|
126
|
+
&& !(value as ImportResultExtras).__rootPath
|
|
127
|
+
&& !value.userData.rootPathRefresh
|
|
128
|
+
&& Array.isArray(value.userData.sProperties)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private loadAsset(path: string, options: Record<string, unknown> | undefined) {
|
|
132
|
+
let loading = this.cache.get(path)
|
|
133
|
+
if (!loading) {
|
|
134
|
+
loading = this.importer.importSingle(path, options || {})
|
|
135
|
+
this.cache.set(path, loading)
|
|
136
|
+
}
|
|
137
|
+
return loading
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async loadReference(target: IObject3D | IMaterial): Promise<void> {
|
|
141
|
+
const path = target.userData.rootPath as string
|
|
142
|
+
const imported = await this.loadAsset(path, target.userData.rootPathOptions)
|
|
143
|
+
if (!imported) throw new Error(`Unable to load nested asset from ${path}`)
|
|
144
|
+
if (imported._loadingPromise) await imported._loadingPromise
|
|
145
|
+
|
|
146
|
+
const targetObject = target as IObject3D
|
|
147
|
+
const targetMaterial = target as IMaterial
|
|
148
|
+
if (targetObject.isObject3D && imported.isObject3D) {
|
|
149
|
+
this.updateObject(imported as IObject3D, targetObject)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
if (targetMaterial.isMaterial && imported.isMaterial) {
|
|
153
|
+
this.updateMaterial(imported as IMaterial, targetMaterial)
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Nested asset at ${path} does not match its reference type`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private updateObject(source: IObject3D, target: IObject3D) {
|
|
160
|
+
if (target._sChildren) {
|
|
161
|
+
for (const child of [...target.children]) {
|
|
162
|
+
if (!target._sChildren.includes(child)) child.removeFromParent()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const child of source.children) {
|
|
167
|
+
const clone = cloneObject(child)
|
|
168
|
+
clone.userData.excludeFromExport = true
|
|
169
|
+
target.add(clone)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const overrideProperties = target.userData.sProperties as string[]
|
|
173
|
+
const name = target.name
|
|
174
|
+
const visible = target.visible
|
|
175
|
+
for (const property of objectProperties) {
|
|
176
|
+
if (overrideProperties.includes(property)) continue
|
|
177
|
+
const value = source[property]
|
|
178
|
+
const current = target[property]
|
|
179
|
+
if (value && current && (value as Vector3).isVector3) {
|
|
180
|
+
(current as Vector3).copy(value as Vector3)
|
|
181
|
+
} else if (value && current && (value as Quaternion).isQuaternion) {
|
|
182
|
+
(current as Quaternion).copy(value as Quaternion)
|
|
183
|
+
} else if (value !== undefined) {
|
|
184
|
+
Object.assign(target, {[property]: value})
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const targetUserData = target.userData
|
|
189
|
+
target.userData = {}
|
|
190
|
+
copyObject3DUserData(target.userData, source.userData, ['uuid', 'sProperties'])
|
|
191
|
+
Object.assign(target.userData, targetUserData)
|
|
192
|
+
target.name = name
|
|
193
|
+
target.visible = visible
|
|
194
|
+
target.userData.uuid = target.uuid
|
|
195
|
+
target.userData.sProperties = overrideProperties
|
|
196
|
+
target.setDirty?.({source: 'RuntimeNestedAssetLoader'})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private updateMaterial(source: IMaterial, target: IMaterial) {
|
|
200
|
+
const overrideProperties = target.userData.sProperties as string[]
|
|
201
|
+
const overrides = getPartialProps(target, overrideProperties)
|
|
202
|
+
const name = target.name
|
|
203
|
+
target.setValues(source)
|
|
204
|
+
target.name = name
|
|
205
|
+
setPartialProps(overrides, target)
|
|
206
|
+
target.userData.uuid = target.uuid
|
|
207
|
+
target.userData.sProperties = overrideProperties
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private track(promise: Promise<void>) {
|
|
211
|
+
this.pending.add(promise)
|
|
212
|
+
const remove = () => this.pending.delete(promise)
|
|
213
|
+
promise.then(remove, remove)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function cloneObject(source: IObject3D): IObject3D {
|
|
218
|
+
const clone = source.clone(false) as IObject3D
|
|
219
|
+
const sourceRoot = source as IObject3D & {_tpRootPath?: string, _tpRootUid?: string}
|
|
220
|
+
const cloneRoot = clone as IObject3D & {_tpRootPath?: string, _tpRootUid?: string}
|
|
221
|
+
cloneRoot._tpRootPath = sourceRoot._tpRootPath
|
|
222
|
+
cloneRoot._tpRootUid = sourceRoot._tpRootUid || source.uuid
|
|
223
|
+
delete clone.userData.cloneParent
|
|
224
|
+
for (const child of source.children) clone.add(cloneObject(child))
|
|
225
|
+
return clone
|
|
226
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {parse, ParseError} from 'jsonc-parser'
|
|
2
|
+
|
|
3
|
+
export const settingsKey = 'kite3d'
|
|
4
|
+
export const assetUrlPrefix = `/${settingsKey}/`
|
|
5
|
+
|
|
6
|
+
export type JSONValue = string | number | boolean | null | JSONValue[] | {[key: string]: JSONValue}
|
|
7
|
+
export type ProjectPackageJSON = Record<string, JSONValue> & {mainScene: string}
|
|
8
|
+
|
|
9
|
+
export interface ProjectGeneratorState {
|
|
10
|
+
componentId: string
|
|
11
|
+
module: string
|
|
12
|
+
nodeIndex: number
|
|
13
|
+
nodeName: string
|
|
14
|
+
params: Record<string, unknown>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AssetsJSONManifest {
|
|
18
|
+
files: Record<string, {
|
|
19
|
+
path: string
|
|
20
|
+
}>
|
|
21
|
+
version: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ProjectDependency {
|
|
25
|
+
key: string
|
|
26
|
+
version: string
|
|
27
|
+
url?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ExternalPlugin {
|
|
31
|
+
import: string
|
|
32
|
+
/** @default `default` */
|
|
33
|
+
className?: string
|
|
34
|
+
/** @default true */
|
|
35
|
+
active?: boolean
|
|
36
|
+
/** Constructor parameters. */
|
|
37
|
+
params?: JSONValue[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ExternalScript {
|
|
41
|
+
import: string
|
|
42
|
+
/** @default true */
|
|
43
|
+
active?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ProjectViewerSettings {
|
|
47
|
+
msaa?: boolean
|
|
48
|
+
rgbm?: boolean
|
|
49
|
+
zPrepass?: boolean
|
|
50
|
+
renderScale?: number | 'auto'
|
|
51
|
+
maxRenderScale?: number
|
|
52
|
+
backgroundColor?: string | number | null
|
|
53
|
+
modelRootScale?: number
|
|
54
|
+
stencil?: boolean
|
|
55
|
+
debug?: boolean
|
|
56
|
+
tonemap?: boolean
|
|
57
|
+
camera?: {
|
|
58
|
+
type?: 'perspective' | 'orthographic'
|
|
59
|
+
controlsMode?: string
|
|
60
|
+
position?: [number, number, number]
|
|
61
|
+
target?: [number, number, number]
|
|
62
|
+
}
|
|
63
|
+
maxHDRIntensity?: number
|
|
64
|
+
powerPreference?: 'default' | 'high-performance' | 'low-power'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ProjectConfigSettings {
|
|
68
|
+
plugins: ExternalPlugin[]
|
|
69
|
+
scripts: ExternalScript[]
|
|
70
|
+
dependencies: ProjectDependency[]
|
|
71
|
+
viewer: ProjectViewerSettings
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ProjectConfigSettingsJSON {
|
|
75
|
+
plugins?: (ExternalPlugin | string)[]
|
|
76
|
+
imports?: Record<string, string>
|
|
77
|
+
scripts?: (ExternalScript | string)[]
|
|
78
|
+
viewer?: ProjectViewerSettings
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A configured script or plugin is package-backed only when it names a declared dependency exactly. */
|
|
82
|
+
export function isDependencyModuleSpecifier(
|
|
83
|
+
specifier: string,
|
|
84
|
+
packageJson: ProjectPackageJSON,
|
|
85
|
+
): boolean {
|
|
86
|
+
const dependencies = packageJson.dependencies
|
|
87
|
+
return isRecord(dependencies) && Object.prototype.hasOwnProperty.call(dependencies, specifier)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function parsePackageJSON(text: string): ProjectPackageJSON {
|
|
91
|
+
const json = parse(text) as unknown
|
|
92
|
+
if (!json || typeof json !== 'object' || Array.isArray(json)) {
|
|
93
|
+
throw new Error('Invalid package.json file: expected an object')
|
|
94
|
+
}
|
|
95
|
+
const packageJson = json as ProjectPackageJSON
|
|
96
|
+
if (typeof packageJson.mainScene !== 'string' || !packageJson.mainScene.toLowerCase().endsWith('.gltf')) {
|
|
97
|
+
throw new Error('package.json mainScene must name a text .gltf file')
|
|
98
|
+
}
|
|
99
|
+
return packageJson
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function parseAssetsJSONManifest(text: string): AssetsJSONManifest {
|
|
103
|
+
const json = parse(text) as AssetsJSONManifest
|
|
104
|
+
|
|
105
|
+
if (json.files && typeof json.files !== 'object') {
|
|
106
|
+
throw new Error('Invalid assets.json file: files should be an object')
|
|
107
|
+
}
|
|
108
|
+
if (json.version !== undefined && typeof json.version !== 'number') {
|
|
109
|
+
throw new Error('Invalid assets.json file: version should be a number')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!json.files) json.files = {}
|
|
113
|
+
if (!json.version) json.version = 1
|
|
114
|
+
|
|
115
|
+
return json
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function validateSceneSource(path: string, text: string): void {
|
|
119
|
+
const document = JSON.parse(text) as {asset?: unknown}
|
|
120
|
+
if (!document || typeof document !== 'object' || !document.asset) {
|
|
121
|
+
throw new Error(`${path} is not a JSON glTF document`)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function readProjectGeneratorStates(text: string): ProjectGeneratorState[] {
|
|
126
|
+
try {
|
|
127
|
+
const document = JSON.parse(text) as {
|
|
128
|
+
nodes?: Array<{
|
|
129
|
+
name?: unknown
|
|
130
|
+
extras?: {EntityComponentPlugin?: Record<string, {type?: unknown, state?: unknown}>}
|
|
131
|
+
}>
|
|
132
|
+
}
|
|
133
|
+
const generators: ProjectGeneratorState[] = []
|
|
134
|
+
for (const [nodeIndex, node] of (document.nodes || []).entries()) {
|
|
135
|
+
for (const [componentId, component] of Object.entries(node.extras?.EntityComponentPlugin || {})) {
|
|
136
|
+
if (component.type !== 'Generator' || !isRecord(component.state)) continue
|
|
137
|
+
generators.push({
|
|
138
|
+
componentId,
|
|
139
|
+
module: typeof component.state.module === 'string' ? component.state.module : '',
|
|
140
|
+
nodeIndex,
|
|
141
|
+
nodeName: typeof node.name === 'string' ? node.name : `Node ${nodeIndex}`,
|
|
142
|
+
params: isRecord(component.state.params) ? component.state.params : {},
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return generators
|
|
147
|
+
} catch {
|
|
148
|
+
return []
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function updateProjectGeneratorState(
|
|
153
|
+
text: string,
|
|
154
|
+
generator: Pick<ProjectGeneratorState, 'componentId' | 'nodeIndex' | 'nodeName'>,
|
|
155
|
+
update: Partial<Pick<ProjectGeneratorState, 'module' | 'params'>>,
|
|
156
|
+
): {text: string, state: Record<string, unknown>} {
|
|
157
|
+
const document = JSON.parse(text) as {
|
|
158
|
+
nodes?: Array<{extras?: {EntityComponentPlugin?: Record<string, {state?: unknown}>}}>
|
|
159
|
+
}
|
|
160
|
+
const state = document.nodes?.[generator.nodeIndex]?.extras?.EntityComponentPlugin?.[generator.componentId]?.state
|
|
161
|
+
if (!isRecord(state)) throw new Error(`Generator component is missing on ${generator.nodeName}`)
|
|
162
|
+
Object.assign(state, update)
|
|
163
|
+
return {text: JSON.stringify(document, null, 2), state}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function parsePackageJsonSettingsConfig(json: ProjectPackageJSON, _project?: unknown): Promise<ProjectConfigSettings> {
|
|
167
|
+
const config = (json[settingsKey] ?? {}) as ProjectConfigSettingsJSON
|
|
168
|
+
const dependencies: ProjectDependency[] = []
|
|
169
|
+
const packageDependencies = json.dependencies && typeof json.dependencies === 'object' && !Array.isArray(json.dependencies)
|
|
170
|
+
? json.dependencies as Record<string, string>
|
|
171
|
+
: {}
|
|
172
|
+
const deps: Record<string, string> = {
|
|
173
|
+
...packageDependencies,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
dependencies.push(...Object.entries(deps).map(([key, version]) => ({key, version})))
|
|
177
|
+
|
|
178
|
+
if (config.imports) {
|
|
179
|
+
dependencies.push(...Object.entries(config.imports).map(([key, url]) => ({
|
|
180
|
+
key,
|
|
181
|
+
url: !url.startsWith('@') ? url : undefined,
|
|
182
|
+
version: url.startsWith('@') ? url.slice(1) : '',
|
|
183
|
+
})))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const plugins = config.plugins?.map((entry) => {
|
|
187
|
+
if (typeof entry !== 'string') return entry
|
|
188
|
+
|
|
189
|
+
const match = entry.match(/\((.*)\)$/)
|
|
190
|
+
let params: JSONValue[] | undefined
|
|
191
|
+
let spec = entry
|
|
192
|
+
|
|
193
|
+
if (match) {
|
|
194
|
+
const paramString = match[1].trim()
|
|
195
|
+
spec = entry.slice(0, match.index).trim()
|
|
196
|
+
if (paramString) {
|
|
197
|
+
try {
|
|
198
|
+
const errors: ParseError[] = []
|
|
199
|
+
params = parse(`[${paramString}]`, errors)
|
|
200
|
+
if (!Array.isArray(params) || errors.length) {
|
|
201
|
+
console.error(errors)
|
|
202
|
+
throw new Error('Plugin params is not a valid array')
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error('Unable to parse plugin params, skipping plugin', entry, paramString, error)
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const separator = spec.lastIndexOf(':')
|
|
212
|
+
return {
|
|
213
|
+
import: separator !== -1 ? spec.slice(0, separator) : spec,
|
|
214
|
+
className: separator !== -1 ? spec.slice(separator + 1) : undefined,
|
|
215
|
+
params,
|
|
216
|
+
} satisfies ExternalPlugin
|
|
217
|
+
}) || []
|
|
218
|
+
|
|
219
|
+
const scripts = config.scripts?.map((entry) =>
|
|
220
|
+
typeof entry === 'string' ? {import: entry} : entry
|
|
221
|
+
) || []
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
plugins: plugins.filter(Boolean) as ExternalPlugin[],
|
|
225
|
+
scripts,
|
|
226
|
+
dependencies,
|
|
227
|
+
viewer: config.viewer || {},
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
232
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
233
|
+
}
|