mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-04 08:50:33 +08:00
Type initialization using objects
This commit is contained in:
139
cypress/e2e/entities.cy.js
Normal file
139
cypress/e2e/entities.cy.js
Normal file
@@ -0,0 +1,139 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import ComplexEntity from "../fixtures/ComplexEntity"
|
||||
import SimpleEntity from "../fixtures/SimpleEntity"
|
||||
import SimpleObject from "../fixtures/SimpleObject"
|
||||
|
||||
describe("Entity initialization", () => {
|
||||
before(() => {
|
||||
expect(SimpleEntity,).to.be.a("function")
|
||||
expect(ComplexEntity).to.be.a("function")
|
||||
})
|
||||
|
||||
context("SimpleEntity", () => {
|
||||
const entity = new SimpleEntity()
|
||||
it("has 8 keys", () => expect(Object.keys(entity).length).to.equal(8))
|
||||
it("has someNumber equal to 567", () => expect(entity)
|
||||
.to.have.property("someNumber")
|
||||
.which.is.a("number")
|
||||
.and.is.equal(567)
|
||||
)
|
||||
it("has someString equal to alpha", () => expect(entity)
|
||||
.to.have.property("someString")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("alpha")
|
||||
)
|
||||
it("has someString2 equal to beta", () => expect(entity)
|
||||
.to.have.property("someString2")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("beta")
|
||||
)
|
||||
it("has someBoolean true", () => expect(entity)
|
||||
.to.have.property("someBoolean")
|
||||
.which.is.a("boolean")
|
||||
.and.is.true
|
||||
)
|
||||
it("has someBoolean2 false", () => expect(entity)
|
||||
.to.have.property("someBoolean2")
|
||||
.which.is.a("boolean")
|
||||
.and.is.false
|
||||
)
|
||||
it("has someObjectString equal to gamma", () => expect(entity)
|
||||
.to.have.property("someObjectString")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("gamma")
|
||||
)
|
||||
it("has someArray with numbers", () => expect(entity)
|
||||
.to.have.property("someArray")
|
||||
.which.is.an("array")
|
||||
.and.is.deep.equal([400, 500, 600, 700, 800])
|
||||
)
|
||||
it("has someSet with numbers", () => expect(entity)
|
||||
.to.have.property("someSet")
|
||||
.which.is.instanceOf(Set)
|
||||
.and.is.deep.equal(new Set([10, 20, 30, 40, 50, 60, 70]))
|
||||
)
|
||||
})
|
||||
|
||||
context("ComplexEntity", () => {
|
||||
const entity = new ComplexEntity()
|
||||
it("has 8 keys", () => expect(Object.keys(entity).length).to.equal(8))
|
||||
it("has alpha equal to 32", () => expect(entity)
|
||||
.to.have.property("alpha")
|
||||
.which.is.a("number")
|
||||
.and.is.equal(32)
|
||||
)
|
||||
it("has bravo equal to 78", () => expect(entity)
|
||||
.to.have.property("bravo")
|
||||
.which.is.a("number")
|
||||
.and.is.equal(78)
|
||||
)
|
||||
it("has charlie equal to beta", () => expect(entity)
|
||||
.to.have.property("charlie")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("Charlie")
|
||||
)
|
||||
it("has delta null", () => expect(entity)
|
||||
.to.have.property("delta")
|
||||
.which.is.null
|
||||
)
|
||||
it("has echo equal to echo", () => expect(entity)
|
||||
.to.have.property("echo")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("echo")
|
||||
)
|
||||
it("has foxtrot false", () => expect(entity)
|
||||
.to.have.property("foxtrot")
|
||||
.which.is.a("boolean")
|
||||
.and.is.false
|
||||
)
|
||||
it("has golf empty array", () => expect(entity)
|
||||
.to.have.property("golf")
|
||||
.which.is.an("array")
|
||||
.and.is.empty
|
||||
)
|
||||
it("has hotel null", () => expect(entity)
|
||||
.to.have.property("hotel")
|
||||
.which.is.null
|
||||
)
|
||||
it("has india empty array", () => expect(entity)
|
||||
.to.have.property("india")
|
||||
.which.is.an("array")
|
||||
.and.is.empty
|
||||
)
|
||||
it("has juliett array of strings", () => expect(entity)
|
||||
.to.have.property("juliett")
|
||||
.which.is.an("array")
|
||||
.and.is.deep.equal(["a", "b", "c", "d", "e"])
|
||||
)
|
||||
it("has kilo array of booleans", () => expect(entity)
|
||||
.to.have.property("kilo")
|
||||
.which.is.an("array")
|
||||
.and.is.deep.equal([true, false, false, true, true])
|
||||
)
|
||||
it("has lima undefined", () => expect(entity)
|
||||
.to.have.property("lima")
|
||||
.which.is.undefined
|
||||
)
|
||||
it("has mike equal to Foo", () => expect(entity)
|
||||
.to.have.property("mike")
|
||||
.which.is.a("string")
|
||||
.and.is.equal("Bar")
|
||||
)
|
||||
it("has november equal to 0", () => expect(entity)
|
||||
.to.have.property("november")
|
||||
.which.is.a("number")
|
||||
.and.is.equal(0)
|
||||
)
|
||||
it("has oscar a SimpleObject", () => expect(entity)
|
||||
.to.have.property("oscar")
|
||||
.which.is.instanceOf(SimpleObject)
|
||||
.and.is.deep.equal({ a: 8, b: 9 })
|
||||
)
|
||||
it("has papa a SimpleObject", () => expect(entity)
|
||||
.to.have.property("papa")
|
||||
.which.is.instanceOf(SimpleObject)
|
||||
.and.is.deep.equal({ a: 12, b: 13 })
|
||||
)
|
||||
})
|
||||
})
|
||||
64
cypress/fixtures/ComplexEntity.js
Normal file
64
cypress/fixtures/ComplexEntity.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import IEntity from "../../js/entity/IEntity"
|
||||
import UnionType from "../../js/entity/UnionType"
|
||||
import SimpleObject from "./SimpleObject"
|
||||
|
||||
export default class ComplexEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
alpha: 32,
|
||||
bravo: {
|
||||
type: Number,
|
||||
value: 78,
|
||||
},
|
||||
charlie: {
|
||||
type: String,
|
||||
value: "Charlie",
|
||||
},
|
||||
delta: {
|
||||
type: String,
|
||||
value: null,
|
||||
},
|
||||
echo: "echo",
|
||||
foxtrot: {
|
||||
value: false,
|
||||
},
|
||||
golf: {
|
||||
type: Array,
|
||||
},
|
||||
hotel: {
|
||||
type: Array,
|
||||
value: null,
|
||||
},
|
||||
india: {
|
||||
type: [Number]
|
||||
},
|
||||
juliett: {
|
||||
type: [String],
|
||||
value: ["a", "b", "c", "d", "e"],
|
||||
},
|
||||
kilo: {
|
||||
type: [Boolean],
|
||||
value: () => [true, false, false, true, true],
|
||||
},
|
||||
lima: {
|
||||
type: String,
|
||||
value: "Foo",
|
||||
showDefault: false,
|
||||
},
|
||||
mike: {
|
||||
type: new UnionType(Number, String, Array),
|
||||
value: "Bar",
|
||||
},
|
||||
november: {
|
||||
type: new UnionType(Number, String, Array),
|
||||
},
|
||||
oscar: {
|
||||
type: SimpleObject
|
||||
},
|
||||
papa: () => new SimpleObject(12, 13),
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
}
|
||||
19
cypress/fixtures/SimpleEntity.js
Normal file
19
cypress/fixtures/SimpleEntity.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import IEntity from "../../js/entity/IEntity"
|
||||
|
||||
export default class SimpleEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
someNumber: 567,
|
||||
someString: "alpha",
|
||||
someString2: "beta",
|
||||
someBoolean: true,
|
||||
someBoolean2: false,
|
||||
someObjectString: new String("gamma"),
|
||||
someArray: [400, 500, 600, 700, 800],
|
||||
someSet: new Set([10, 20, 30, 40, 50, 60, 70]),
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
}
|
||||
7
cypress/fixtures/SimpleObject.js
Normal file
7
cypress/fixtures/SimpleObject.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default class SimpleObject {
|
||||
|
||||
constructor(a = 8, b = 9) {
|
||||
this.a = a
|
||||
this.b = b
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
1090
dist/ueblueprint.js
vendored
1090
dist/ueblueprint.js
vendored
File diff suppressed because it is too large
Load Diff
12
dist/ueblueprint.min.js
vendored
12
dist/ueblueprint.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,17 +1,13 @@
|
||||
import CalculatedType from "./entity/CalculatedType"
|
||||
import TypeInitialization from "./entity/TypeInitialization"
|
||||
import UnionType from "./entity/UnionType"
|
||||
import SubAttributesDeclaration from "./entity/SubObject"
|
||||
|
||||
/**
|
||||
* @typedef {import("./element/IElement").default} IElement
|
||||
* @typedef {import("./entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("./entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
|
||||
* @typedef {import("./entity/IEntity").AttributeInformation} TypeInformation
|
||||
* @typedef {import("./entity/IEntity").default} IEntity
|
||||
* @typedef {import("./entity/IEntity").EntityConstructor} EntityConstructor
|
||||
* @typedef {import("./entity/LinearColorEntity").default} LinearColorEntity
|
||||
* @typedef {import("./entity/TypeInitialization").AnyValue} AnyValue
|
||||
*/
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import("./entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
*/
|
||||
|
||||
export default class Utility {
|
||||
@@ -98,22 +94,15 @@ export default class Utility {
|
||||
/**
|
||||
* @param {IEntity} entity
|
||||
* @param {String[]} keys
|
||||
* @param {any} propertyDefinition
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
static isSerialized(
|
||||
entity,
|
||||
keys,
|
||||
propertyDefinition = Utility.objectGet(/** @type {EntityConstructor} */(entity.constructor).attributes, keys)
|
||||
attribute = Utility.objectGet(/** @type {EntityConstructor} */(entity.constructor).attributes, keys)
|
||||
) {
|
||||
if (propertyDefinition instanceof CalculatedType) {
|
||||
return Utility.isSerialized(entity, keys, propertyDefinition.calculate(entity))
|
||||
}
|
||||
if (propertyDefinition instanceof TypeInitialization) {
|
||||
if (propertyDefinition.serialized) {
|
||||
return true
|
||||
}
|
||||
return Utility.isSerialized(entity, keys, propertyDefinition.type)
|
||||
if (attribute.constructor === Object) {
|
||||
return /** @type {TypeInformation} */(attribute).serialized
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -124,7 +113,10 @@ export default class Utility {
|
||||
return undefined
|
||||
}
|
||||
if (!(keys instanceof Array)) {
|
||||
throw new TypeError("Expected keys to be an array.")
|
||||
throw new TypeError("UEBlueprint: Expected keys to be an array")
|
||||
}
|
||||
if (target instanceof SubAttributesDeclaration) {
|
||||
target = target.attributes
|
||||
}
|
||||
if (keys.length == 0 || !(keys[0] in target) || target[keys[0]] === undefined) {
|
||||
return defaultValue
|
||||
@@ -158,9 +150,13 @@ export default class Utility {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AnyValue} a
|
||||
* @param {AnyValue} b
|
||||
*/
|
||||
static equals(a, b) {
|
||||
a = TypeInitialization.sanitize(a)
|
||||
b = TypeInitialization.sanitize(b)
|
||||
a = Utility.sanitize(a)
|
||||
b = Utility.sanitize(b)
|
||||
if (a === b) {
|
||||
return true
|
||||
}
|
||||
@@ -169,25 +165,45 @@ export default class Utility {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AnyValue | AnyValueConstructor<IEntity>} value
|
||||
* @returns {AnyValueConstructor<IEntity> | AnyValueConstructor<IEntity>[]}
|
||||
/**
|
||||
* @param {null | AnyValue | TypeInformation} value
|
||||
* @returns {AnyValueConstructor}
|
||||
*/
|
||||
static getType(value) {
|
||||
if (value === null) {
|
||||
return null
|
||||
}
|
||||
if (value instanceof TypeInitialization) {
|
||||
return Utility.getType(value.type)
|
||||
if (value.constructor === Object && value.type instanceof Function) {
|
||||
// @ts-expect-error
|
||||
return value.type
|
||||
}
|
||||
if (value instanceof UnionType) {
|
||||
return value.types
|
||||
return /** @type {AnyValueConstructor} */(value?.constructor)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AnyValue} value
|
||||
* @param {AnyValueConstructor} type
|
||||
*/
|
||||
static isValueOfType(value, type) {
|
||||
return value === null || (value instanceof type || value.constructor === type)
|
||||
}
|
||||
|
||||
/** @param {AnyValue} value */
|
||||
static sanitize(value, targetType = /** @type {AnyValueConstructor} */(value?.constructor)) {
|
||||
if (targetType instanceof Array) {
|
||||
let type = targetType.find(t => Utility.isValueOfType(value, t))
|
||||
if (!type) {
|
||||
type = targetType[0]
|
||||
}
|
||||
targetType = type
|
||||
}
|
||||
if (value instanceof Function) {
|
||||
// value is already a constructor
|
||||
return value
|
||||
if (targetType && !Utility.isValueOfType(value, targetType)) {
|
||||
value = new targetType(value)
|
||||
}
|
||||
return /** @type {AnyValueConstructor<IEntity>} */(value?.constructor)
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
value = value.valueOf() // Get the relative primitive value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,4 +363,9 @@ export default class Utility {
|
||||
}
|
||||
requestAnimationFrame(doAnimation)
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
static warn(value) {
|
||||
console.warn("UEBlueprint: " + value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,6 @@ import { LitElement } from "lit"
|
||||
*/
|
||||
export default class IElement extends LitElement {
|
||||
|
||||
/** @type {PropertyDeclarations} */
|
||||
static properties = {
|
||||
}
|
||||
|
||||
#nextUpdatedCallbacks = []
|
||||
|
||||
/** @type {Blueprint} */
|
||||
|
||||
@@ -21,8 +21,8 @@ import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate"
|
||||
import VectorPinTemplate from "../template/pin/VectorPinTemplate"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/PinReferenceEntity").default} PinReferenceEntity
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
|
||||
* @typedef {import("./LinkElement").LinkElementConstructor} LinkElementConstructor
|
||||
* @typedef {import("./NodeElement").default} NodeElement
|
||||
* @typedef {import("lit").CSSResult} CSSResult
|
||||
|
||||
@@ -6,10 +6,13 @@ export default class ByteEntity extends IntegerEntity {
|
||||
value: 0,
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} values */
|
||||
constructor(values = 0) {
|
||||
super(values)
|
||||
/** @type {Number} */
|
||||
const value = Math.round(this.value)
|
||||
this.value = value >= 0 && value < 1 << 8 ? value : 0
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import IEntity from "./IEntity"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class FunctionReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
MemberParent: new TypeInitialization(ObjectReferenceEntity, false),
|
||||
MemberParent: {
|
||||
type: ObjectReferenceEntity,
|
||||
showDefault: false
|
||||
},
|
||||
MemberName: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {ObjectReferenceEntity} */ this.MemberParent
|
||||
|
||||
@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
|
||||
export default class GuidEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
value: String,
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
static generateGuid(random = true) {
|
||||
|
||||
@@ -1,19 +1,43 @@
|
||||
import CalculatedType from "./CalculatedType"
|
||||
import Observable from "../Observable"
|
||||
import SerializerFactory from "../serialization/SerializerFactory"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
import SubAttributesDeclaration from "./SubObject"
|
||||
import UnionType from "./UnionType"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/** @typedef {typeof IEntity} EntityConstructor */
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
* @typedef {new (Object) => T} IEntityConstructor
|
||||
* @typedef {(entity: IEntity) => AnyValue} ValueSupplier
|
||||
* @typedef {(entity: IEntity) => AnyValueConstructor<AnyValue>} TypeSupplier
|
||||
* @typedef {IEntity | String | Number | Boolean} AnySimpleValue
|
||||
* @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue
|
||||
* @typedef {{
|
||||
* [key: String]: AttributeInformation | AnyValue | SubAttributesDeclaration
|
||||
* }} AttributeDeclarations
|
||||
* @typedef {typeof IEntity} EntityConstructor
|
||||
* @typedef {{
|
||||
* type?: AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | TypeSupplier,
|
||||
* value?: AnyValue | ValueSupplier,
|
||||
* showDefault?: Boolean,
|
||||
* nullable?: Boolean,
|
||||
* ignored?: Boolean,
|
||||
* serialized?: Boolean,
|
||||
* }} AttributeInformation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {(new () => T) | EntityConstructor | StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor} AnyValueConstructor
|
||||
*/
|
||||
|
||||
export default class IEntity extends Observable {
|
||||
|
||||
/** @type {AttributeDeclarations} */
|
||||
static attributes = {}
|
||||
static defaultAttribute = {
|
||||
showDefault: true,
|
||||
nullable: false,
|
||||
ignored: false,
|
||||
serialized: false,
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super()
|
||||
@@ -24,82 +48,94 @@ export default class IEntity extends Observable {
|
||||
* @param {String} prefix
|
||||
*/
|
||||
const defineAllAttributes = (target, attributes, values = {}, prefix = "") => {
|
||||
const valuesPropertyNames = Object.getOwnPropertyNames(values)
|
||||
for (let attribute of Utility.mergeArrays(Object.getOwnPropertyNames(attributes), valuesPropertyNames)) {
|
||||
let value = Utility.objectGet(values, [attribute])
|
||||
let defaultValue = attributes[attribute]
|
||||
let defaultType = Utility.getType(defaultValue)
|
||||
if (defaultValue instanceof CalculatedType) {
|
||||
defaultValue = defaultValue.calculate(this)
|
||||
defaultType = Utility.getType(defaultValue)
|
||||
}
|
||||
if (defaultValue != null && defaultValue === defaultType) {
|
||||
defaultValue = new defaultType()
|
||||
const valuesNames = Object.getOwnPropertyNames(values)
|
||||
for (let attributeName of Utility.mergeArrays(Object.getOwnPropertyNames(attributes), valuesNames)) {
|
||||
let value = Utility.objectGet(values, [attributeName])
|
||||
/** @type {AttributeInformation} */
|
||||
let attribute = attributes[attributeName]
|
||||
|
||||
if (attribute instanceof SubAttributesDeclaration) {
|
||||
target[attributeName] = {}
|
||||
defineAllAttributes(
|
||||
target[attributeName],
|
||||
attribute.attributes,
|
||||
values[attributeName],
|
||||
attributeName + "."
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!suppressWarns) {
|
||||
if (!(attribute in attributes)) {
|
||||
console.warn(
|
||||
`Attribute ${prefix}${attribute} in the serialized data is not defined in ${this.constructor.name}.attributes`
|
||||
if (!(attributeName in attributes)) {
|
||||
Utility.warn(
|
||||
`Attribute ${prefix}${attributeName} in the serialized data is not defined in `
|
||||
+ `${this.constructor.name}.attributes`
|
||||
)
|
||||
} else if (
|
||||
valuesPropertyNames.length > 0
|
||||
&& !(attribute in values)
|
||||
&& defaultValue !== undefined
|
||||
&& !(defaultValue instanceof TypeInitialization && (!defaultValue.showDefault || defaultValue.ignored))
|
||||
valuesNames.length > 0
|
||||
&& !(attributeName in values)
|
||||
&& !(!attribute.showDefault || attribute.ignored)
|
||||
) {
|
||||
console.warn(
|
||||
`${this.constructor.name} will add attribute ${prefix}${attribute} not defined in the serialized data`
|
||||
Utility.warn(
|
||||
`${this.constructor.name} will add attribute ${prefix}${attributeName} not defined in the `
|
||||
+ "serialized data"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Not instanceof because all objects are instenceof Object, exact match needed
|
||||
// @ts-expect-error
|
||||
if (defaultType === Object) {
|
||||
target[attribute] = {}
|
||||
defineAllAttributes(target[attribute], attributes[attribute], values[attribute], attribute + ".")
|
||||
continue
|
||||
let defaultValue = attribute.value
|
||||
let defaultType = attribute.type
|
||||
if (attribute.serialized && defaultType instanceof Function) {
|
||||
// If the attribute is serialized, the type must contain a function providing the type
|
||||
defaultType = /** @type {TypeSupplier} */(defaultType)(this)
|
||||
}
|
||||
if (defaultType instanceof Array) {
|
||||
defaultType = Array
|
||||
}
|
||||
if (defaultValue instanceof Function) {
|
||||
defaultValue = defaultValue(this)
|
||||
}
|
||||
if (defaultType instanceof UnionType) {
|
||||
if (defaultValue != undefined) {
|
||||
defaultType = defaultType.types.find(
|
||||
type => defaultValue instanceof type || defaultValue.constructor == type
|
||||
) ?? defaultType.getFirstType()
|
||||
} else {
|
||||
defaultType = defaultType.getFirstType()
|
||||
}
|
||||
}
|
||||
if (defaultType === undefined) {
|
||||
defaultType = Utility.getType(defaultValue)
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
// Remember value can still be null
|
||||
if (
|
||||
value?.constructor === String
|
||||
&& defaultValue instanceof TypeInitialization
|
||||
&& defaultValue.serialized
|
||||
&& defaultValue.type !== String
|
||||
) {
|
||||
// @ts-expect-error
|
||||
value = SerializerFactory.getSerializer(defaultValue.type).deserialize(value)
|
||||
if (value?.constructor === String && attribute.serialized && defaultType !== String) {
|
||||
value = SerializerFactory
|
||||
.getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType))
|
||||
.deserialize(/** @type {String} */(value))
|
||||
}
|
||||
target[attribute] = TypeInitialization.sanitize(value, Utility.getType(defaultValue))
|
||||
target[attributeName] = Utility.sanitize(value, /** @type {AnyValueConstructor<*>} */(defaultType))
|
||||
continue // We have a value, need nothing more
|
||||
}
|
||||
|
||||
if (defaultValue instanceof TypeInitialization) {
|
||||
if (!defaultValue.showDefault) {
|
||||
target[attribute] = undefined // Declare undefined to preserve the order of attributes
|
||||
continue
|
||||
}
|
||||
if (defaultValue.serialized) {
|
||||
defaultValue = ""
|
||||
} else {
|
||||
defaultType = defaultValue.type
|
||||
defaultValue = defaultValue.value
|
||||
if (defaultValue instanceof Function) {
|
||||
defaultValue = defaultValue()
|
||||
}
|
||||
if (defaultValue === undefined) {
|
||||
defaultValue = Utility.sanitize(new /** @type {AnyValueConstructor<*>} */(defaultType)())
|
||||
}
|
||||
if (!attribute.showDefault) {
|
||||
target[attributeName] = undefined // Declare undefined to preserve the order of attributes
|
||||
continue
|
||||
}
|
||||
if (attribute.serialized) {
|
||||
if (defaultType !== String && defaultValue.constructor === String) {
|
||||
defaultValue = SerializerFactory
|
||||
.getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType))
|
||||
.deserialize(defaultValue)
|
||||
}
|
||||
}
|
||||
if (defaultValue instanceof UnionType) {
|
||||
defaultType = defaultValue.getFirstType()
|
||||
defaultValue = TypeInitialization.sanitize(null, defaultType)
|
||||
}
|
||||
if (defaultValue instanceof Array) {
|
||||
defaultValue = []
|
||||
}
|
||||
target[attribute] = TypeInitialization.sanitize(defaultValue, defaultType)
|
||||
target[attributeName] = Utility.sanitize(
|
||||
/** @type {AnyValue} */(defaultValue),
|
||||
/** @type {AnyValueConstructor<AnyValue>} */(defaultType)
|
||||
)
|
||||
}
|
||||
}
|
||||
const attributes = /** @type {typeof IEntity} */(this.constructor).attributes
|
||||
@@ -112,6 +148,45 @@ export default class IEntity extends Observable {
|
||||
defineAllAttributes(this, attributes, values)
|
||||
}
|
||||
|
||||
/** @param {AttributeDeclarations} attributes */
|
||||
static cleanupAttributes(attributes, prefix = "") {
|
||||
for (const attributeName in attributes) {
|
||||
if (attributes[attributeName] instanceof SubAttributesDeclaration) {
|
||||
this.cleanupAttributes(
|
||||
/** @type {SubAttributesDeclaration} */(attributes[attributeName]).attributes,
|
||||
prefix + "." + attributeName
|
||||
)
|
||||
continue
|
||||
}
|
||||
if (attributes[attributeName].constructor !== Object) {
|
||||
attributes[attributeName] = {
|
||||
value: attributes[attributeName],
|
||||
}
|
||||
}
|
||||
const attribute = /** @type {AttributeInformation} */(attributes[attributeName])
|
||||
if (attribute.type === undefined && !(attribute.value instanceof Function)) {
|
||||
attribute.type = Utility.getType(attribute.value)
|
||||
}
|
||||
attributes[attributeName] = {
|
||||
...IEntity.defaultAttribute,
|
||||
...attribute,
|
||||
}
|
||||
if (attribute.value === undefined && attribute.type === undefined) {
|
||||
throw new Error(
|
||||
`UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}`
|
||||
+ attributeName
|
||||
)
|
||||
}
|
||||
if (attribute.value === null) {
|
||||
attributes[attributeName].nullable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static isValueOfType(value, type) {
|
||||
return value != null && (value instanceof type || value.constructor === type)
|
||||
}
|
||||
|
||||
unexpectedKeys() {
|
||||
// @ts-expect-error
|
||||
return Object.getOwnPropertyNames(this).length - Object.getOwnPropertyNames(this.constructor.attributes).length
|
||||
|
||||
@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
|
||||
export default class IdentifierEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
value: String,
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
static attributeConverter = {
|
||||
|
||||
@@ -6,10 +6,13 @@ export default class IntegerEntity extends IEntity {
|
||||
value: 0,
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} values */
|
||||
constructor(values = 0) {
|
||||
super(values)
|
||||
/** @type {Number} */
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} value */
|
||||
constructor(value = 0) {
|
||||
super(value)
|
||||
this.value = Math.round(this.value)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ export default class InvariantTextEntity extends IEntity {
|
||||
|
||||
static lookbehind = "INVTEXT"
|
||||
static attributes = {
|
||||
value: String,
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -9,7 +9,13 @@ export default class KeyBindingEntity extends IEntity {
|
||||
bCtrl: false,
|
||||
bAlt: false,
|
||||
bCmd: false,
|
||||
Key: IdentifierEntity,
|
||||
Key: {
|
||||
type: IdentifierEntity
|
||||
},
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
|
||||
@@ -1,18 +1,43 @@
|
||||
import IEntity from "./IEntity"
|
||||
import RealUnitEntity from "./UnitRealEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
|
||||
export default class LinearColorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
R: RealUnitEntity,
|
||||
G: RealUnitEntity,
|
||||
B: RealUnitEntity,
|
||||
A: new TypeInitialization(RealUnitEntity, true, () => new RealUnitEntity(1), false, false),
|
||||
H: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
|
||||
S: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
|
||||
V: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
|
||||
R: {
|
||||
type: RealUnitEntity,
|
||||
},
|
||||
G: {
|
||||
type: RealUnitEntity,
|
||||
},
|
||||
B: {
|
||||
type: RealUnitEntity,
|
||||
},
|
||||
A: {
|
||||
type: RealUnitEntity,
|
||||
value: () => new RealUnitEntity(1),
|
||||
showDefault: true,
|
||||
},
|
||||
H: {
|
||||
type: RealUnitEntity,
|
||||
showDefault: true,
|
||||
ignored: true,
|
||||
},
|
||||
S: {
|
||||
type: RealUnitEntity,
|
||||
showDefault: true,
|
||||
ignored: true,
|
||||
},
|
||||
V: {
|
||||
type: RealUnitEntity,
|
||||
showDefault: true,
|
||||
ignored: true,
|
||||
},
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
/** @param {Number} x */
|
||||
|
||||
@@ -5,9 +5,13 @@ export default class LocalizedTextEntity extends IEntity {
|
||||
|
||||
static lookbehind = "NSLOCTEXT"
|
||||
static attributes = {
|
||||
namespace: String,
|
||||
key: String,
|
||||
value: String,
|
||||
namespace: "",
|
||||
key: "",
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -5,9 +5,19 @@ import ObjectReferenceEntity from "./ObjectReferenceEntity"
|
||||
export default class MacroGraphReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
MacroGraph: ObjectReferenceEntity,
|
||||
GraphBlueprint: ObjectReferenceEntity,
|
||||
GraphGuid: GuidEntity,
|
||||
MacroGraph: {
|
||||
type: ObjectReferenceEntity,
|
||||
},
|
||||
GraphBlueprint: {
|
||||
type: ObjectReferenceEntity,
|
||||
},
|
||||
GraphGuid: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -9,45 +9,130 @@ import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity"
|
||||
import PinEntity from "./PinEntity"
|
||||
import SymbolEntity from "./SymbolEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
import VariableReferenceEntity from "./VariableReferenceEntity"
|
||||
|
||||
export default class ObjectEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
Class: ObjectReferenceEntity,
|
||||
Class: {
|
||||
type: ObjectReferenceEntity,
|
||||
},
|
||||
Name: "",
|
||||
bIsPureFunc: new TypeInitialization(Boolean, false, false),
|
||||
VariableReference: new TypeInitialization(VariableReferenceEntity, false, null),
|
||||
SelfContextInfo: new TypeInitialization(SymbolEntity, false, null),
|
||||
FunctionReference: new TypeInitialization(FunctionReferenceEntity, false, null,),
|
||||
EventReference: new TypeInitialization(FunctionReferenceEntity, false, null,),
|
||||
TargetType: new TypeInitialization(ObjectReferenceEntity, false, null),
|
||||
MacroGraphReference: new TypeInitialization(MacroGraphReferenceEntity, false, null),
|
||||
Enum: new TypeInitialization(ObjectReferenceEntity, false),
|
||||
CommentColor: new TypeInitialization(LinearColorEntity, false),
|
||||
bCommentBubbleVisible_InDetailsPanel: new TypeInitialization(Boolean, false),
|
||||
bColorCommentBubble: new TypeInitialization(Boolean, false, false),
|
||||
MoveMode: new TypeInitialization(SymbolEntity, false),
|
||||
NodePosX: IntegerEntity,
|
||||
NodePosY: IntegerEntity,
|
||||
NodeWidth: new TypeInitialization(IntegerEntity, false),
|
||||
NodeHeight: new TypeInitialization(IntegerEntity, false),
|
||||
bCommentBubblePinned: new TypeInitialization(Boolean, false),
|
||||
bCommentBubbleVisible: new TypeInitialization(Boolean, false),
|
||||
NodeComment: new TypeInitialization(String, false),
|
||||
AdvancedPinDisplay: new TypeInitialization(IdentifierEntity, false, null),
|
||||
EnabledState: new TypeInitialization(IdentifierEntity, false, null),
|
||||
NodeGuid: GuidEntity,
|
||||
ErrorType: new TypeInitialization(IntegerEntity, false),
|
||||
ErrorMsg: new TypeInitialization(String, false, ""),
|
||||
CustomProperties: [PinEntity],
|
||||
bIsPureFunc: {
|
||||
value: false,
|
||||
showDefault: false,
|
||||
},
|
||||
VariableReference: {
|
||||
type: VariableReferenceEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
SelfContextInfo: {
|
||||
type: SymbolEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
FunctionReference: {
|
||||
type: FunctionReferenceEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
EventReference: {
|
||||
type: FunctionReferenceEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
TargetType: {
|
||||
type: ObjectReferenceEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
MacroGraphReference: {
|
||||
type: MacroGraphReferenceEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
Enum: {
|
||||
type: ObjectReferenceEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
CommentColor: {
|
||||
type: LinearColorEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
bCommentBubbleVisible_InDetailsPanel: {
|
||||
type: Boolean,
|
||||
showDefault: false,
|
||||
},
|
||||
bColorCommentBubble: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
showDefault: false,
|
||||
},
|
||||
MoveMode: {
|
||||
type: SymbolEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
NodePosX: {
|
||||
type: IntegerEntity,
|
||||
},
|
||||
NodePosY: {
|
||||
type: IntegerEntity,
|
||||
},
|
||||
NodeWidth: {
|
||||
type: IntegerEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
NodeHeight: {
|
||||
type: IntegerEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
bCommentBubblePinned: {
|
||||
type: Boolean,
|
||||
showDefault: false,
|
||||
},
|
||||
bCommentBubbleVisible: {
|
||||
type: Boolean,
|
||||
showDefault: false,
|
||||
},
|
||||
NodeComment: {
|
||||
type: String,
|
||||
showDefault: false,
|
||||
},
|
||||
AdvancedPinDisplay: {
|
||||
type: IdentifierEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
EnabledState: {
|
||||
type: IdentifierEntity,
|
||||
value: null,
|
||||
showDefault: false,
|
||||
},
|
||||
NodeGuid: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
ErrorType: {
|
||||
type: IntegerEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
ErrorMsg: {
|
||||
type: String,
|
||||
value: "",
|
||||
showDefault: false,
|
||||
},
|
||||
CustomProperties: {
|
||||
type: [PinEntity]
|
||||
},
|
||||
}
|
||||
|
||||
static nameRegex = /^(\w+?)(?:_(\d+))?$/
|
||||
static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {ObjectReferenceEntity} */ this.Class
|
||||
|
||||
@@ -3,12 +3,16 @@ import IEntity from "./IEntity"
|
||||
export default class ObjectReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
type: String,
|
||||
path: String,
|
||||
type: "",
|
||||
path: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
if (values.constructor !== Object) {
|
||||
if (values.constructor === String) {
|
||||
values = {
|
||||
path: values
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
|
||||
export default class PathSymbolEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
value: String,
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import CalculatedType from "./CalculatedType"
|
||||
import GuidEntity from "./GuidEntity"
|
||||
import IEntity from "./IEntity"
|
||||
import IntegerEntity from "./IntegerEntity"
|
||||
@@ -10,13 +9,13 @@ import RotatorEntity from "./RotatorEntity"
|
||||
import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity"
|
||||
import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity"
|
||||
import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
import SubAttributesDeclaration from "./SubObject"
|
||||
import UnionType from "./UnionType"
|
||||
import Utility from "../Utility"
|
||||
import Vector2DEntity from "./Vector2DEntity"
|
||||
import VectorEntity from "./VectorEntity"
|
||||
|
||||
/** @typedef {import("./TypeInitialization").AnyValue} AnyValue */
|
||||
/** @typedef {import("./IEntity").AnyValue} AnyValue */
|
||||
|
||||
/** @template {AnyValue} T */
|
||||
export default class PinEntity extends IEntity {
|
||||
@@ -40,38 +39,67 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
static lookbehind = "Pin"
|
||||
static attributes = {
|
||||
PinId: GuidEntity,
|
||||
PinId: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
PinName: "",
|
||||
PinFriendlyName: new TypeInitialization(new UnionType(LocalizedTextEntity, String), false, null),
|
||||
PinToolTip: new TypeInitialization(String, false, ""),
|
||||
Direction: new TypeInitialization(String, false, ""),
|
||||
PinType: {
|
||||
PinFriendlyName: {
|
||||
type: new UnionType(LocalizedTextEntity, String),
|
||||
showDefault: false,
|
||||
},
|
||||
PinToolTip: {
|
||||
type: String,
|
||||
showDefault: false,
|
||||
},
|
||||
Direction: {
|
||||
type: String,
|
||||
showDefault: false,
|
||||
},
|
||||
PinType: new SubAttributesDeclaration({
|
||||
PinCategory: "",
|
||||
PinSubCategory: "",
|
||||
PinSubCategoryObject: ObjectReferenceEntity,
|
||||
PinSubCategoryMemberReference: null,
|
||||
PinValueType: null,
|
||||
ContainerType: ObjectReferenceEntity,
|
||||
PinSubCategoryObject: {
|
||||
type: ObjectReferenceEntity,
|
||||
},
|
||||
PinSubCategoryMemberReference: {
|
||||
type: ObjectReferenceEntity,
|
||||
value: null,
|
||||
},
|
||||
PinValueType: {
|
||||
type: String,
|
||||
value: null,
|
||||
},
|
||||
ContainerType: {
|
||||
type: ObjectReferenceEntity,
|
||||
},
|
||||
bIsReference: false,
|
||||
bIsConst: false,
|
||||
bIsWeakPointer: false,
|
||||
bIsUObjectWrapper: false,
|
||||
bSerializeAsSinglePrecisionFloat: false,
|
||||
}),
|
||||
LinkedTo: {
|
||||
type: [PinReferenceEntity],
|
||||
showDefault: false,
|
||||
},
|
||||
DefaultValue: {
|
||||
/** @param {PinEntity} pinEntity */
|
||||
type: pinEntity => pinEntity.getEntityType(true) ?? String,
|
||||
serialized: true,
|
||||
showDefault: false,
|
||||
},
|
||||
AutogeneratedDefaultValue: {
|
||||
type: String,
|
||||
showDefault: false,
|
||||
},
|
||||
DefaultObject: {
|
||||
type: ObjectReferenceEntity,
|
||||
showDefault: false,
|
||||
value: null,
|
||||
},
|
||||
PersistentGuid: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
LinkedTo: new TypeInitialization([PinReferenceEntity], false),
|
||||
DefaultValue:
|
||||
new CalculatedType(
|
||||
/** @param {PinEntity} pinEntity */
|
||||
pinEntity => new TypeInitialization(
|
||||
pinEntity.getEntityType(true) ?? String,
|
||||
false,
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
),
|
||||
AutogeneratedDefaultValue: new TypeInitialization(String, false),
|
||||
DefaultObject: new TypeInitialization(ObjectReferenceEntity, false, null),
|
||||
PersistentGuid: GuidEntity,
|
||||
bHidden: false,
|
||||
bNotConnectable: false,
|
||||
bDefaultValueIsReadOnly: false,
|
||||
@@ -80,6 +108,10 @@ export default class PinEntity extends IEntity {
|
||||
bOrphanedPin: false,
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {GuidEntity} */ this.PinId
|
||||
|
||||
@@ -5,8 +5,16 @@ import PathSymbolEntity from "./PathSymbolEntity"
|
||||
export default class PinReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
objectName: PathSymbolEntity,
|
||||
pinGuid: GuidEntity,
|
||||
objectName: {
|
||||
type: PathSymbolEntity,
|
||||
},
|
||||
pinGuid: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -3,9 +3,19 @@ import IEntity from "./IEntity"
|
||||
export default class RotatorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
R: Number,
|
||||
P: Number,
|
||||
Y: Number,
|
||||
R: {
|
||||
value: 0,
|
||||
},
|
||||
P: {
|
||||
value: 0,
|
||||
},
|
||||
Y: {
|
||||
value: 0,
|
||||
},
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
@@ -14,4 +24,16 @@ export default class RotatorEntity extends IEntity {
|
||||
/** @type {Number} */ this.P
|
||||
/** @type {Number} */ this.Y
|
||||
}
|
||||
|
||||
getRoll() {
|
||||
return this.R
|
||||
}
|
||||
|
||||
getPitch() {
|
||||
return this.P
|
||||
}
|
||||
|
||||
getYaw() {
|
||||
return this.Y
|
||||
}
|
||||
}
|
||||
|
||||
9
js/entity/SubObject.js
Normal file
9
js/entity/SubObject.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @typedef {import("./IEntity").AttributeDeclarations} AttributeDeclarations */
|
||||
|
||||
export default class SubAttributesDeclaration {
|
||||
|
||||
/** @param {AttributeDeclarations} attributes */
|
||||
constructor(attributes) {
|
||||
this.attributes = attributes
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,11 @@ import IEntity from "./IEntity"
|
||||
export default class SymbolEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
value: String
|
||||
value: "",
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import UnionType from "./UnionType"
|
||||
|
||||
/**
|
||||
* @typedef {IEntity | String | Number | Boolean | Array} AnyValue
|
||||
* @typedef {import("./IEntity").default} IEntity
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("./IEntity").IEntityConstructor<T>} IEntityConstructor
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {IEntityConstructor<T> | StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | UnionType} AnyValueConstructor
|
||||
*/
|
||||
|
||||
/** @template {AnyValue} T */
|
||||
export default class TypeInitialization {
|
||||
|
||||
/** @type {AnyValueConstructor<T>|AnyValueConstructor<T>[]} */
|
||||
#type
|
||||
get type() {
|
||||
return this.#type
|
||||
}
|
||||
set type(v) {
|
||||
this.#type = v
|
||||
}
|
||||
|
||||
#showDefault = true
|
||||
get showDefault() {
|
||||
return this.#showDefault
|
||||
}
|
||||
set showDefault(v) {
|
||||
this.#showDefault = v
|
||||
}
|
||||
|
||||
/** @type {T | T[] | String | (() => T) | (() => T[])} */
|
||||
#value
|
||||
get value() {
|
||||
return this.#value
|
||||
}
|
||||
set value(v) {
|
||||
this.#value = v
|
||||
}
|
||||
|
||||
/** @type {Boolean} */
|
||||
#serialized
|
||||
get serialized() {
|
||||
return this.#serialized
|
||||
}
|
||||
set serialized(v) {
|
||||
this.#serialized = v
|
||||
}
|
||||
|
||||
#ignored
|
||||
get ignored() {
|
||||
return this.#ignored
|
||||
}
|
||||
set ignored(v) {
|
||||
this.#ignored = v
|
||||
}
|
||||
|
||||
static isValueOfType(value, type) {
|
||||
return value != null && (value instanceof type || value.constructor === type)
|
||||
}
|
||||
|
||||
static sanitize(value, targetType) {
|
||||
if (targetType === undefined) {
|
||||
targetType = value?.constructor
|
||||
}
|
||||
if (targetType instanceof Array) {
|
||||
let type = targetType.find(t => TypeInitialization.isValueOfType(value, t))
|
||||
if (!type) {
|
||||
type = targetType[0]
|
||||
}
|
||||
targetType = type
|
||||
}
|
||||
if (targetType && !TypeInitialization.isValueOfType(value, targetType)) {
|
||||
value = new targetType(value)
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
value = value.valueOf() // Get the relative primitive value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AnyValueConstructor<T>|AnyValueConstructor<T>[]} type
|
||||
* @param {Boolean} showDefault
|
||||
* @param {T | T[] | String | (() => T) | (() => T[])} value
|
||||
* @param {Boolean} serialized
|
||||
*/
|
||||
constructor(type, showDefault = true, value = undefined, serialized = false, ignored = false) {
|
||||
if (value === undefined) {
|
||||
if (type instanceof Array) {
|
||||
value = []
|
||||
} else {
|
||||
value = () => TypeInitialization.sanitize(new type())
|
||||
}
|
||||
}
|
||||
this.#type = type
|
||||
this.#showDefault = showDefault
|
||||
this.#value = value
|
||||
this.#serialized = serialized
|
||||
this.#ignored = ignored
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import("./TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
*/
|
||||
/** @typedef {import("./IEntity").AnyValueConstructor<*>} AnyValueConstructor */
|
||||
|
||||
export default class UnionType {
|
||||
|
||||
@@ -10,7 +7,7 @@ export default class UnionType {
|
||||
return this.#types
|
||||
}
|
||||
|
||||
/** @param {...AnyValueConstructor<any>} types */
|
||||
/** @param {...AnyValueConstructor} types */
|
||||
constructor(...types) {
|
||||
this.#types = types
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ export default class RealUnitEntity extends IEntity {
|
||||
value: 0,
|
||||
}
|
||||
|
||||
static {
|
||||
this.cleanupAttributes(this.attributes)
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} values */
|
||||
constructor(values = 0) {
|
||||
super(values)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import IEntity from "./IEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class UnknownKeysEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
lookbehind: new TypeInitialization(String, false, "", false, true)
|
||||
lookbehind:
|
||||
{
|
||||
value: "",
|
||||
showDefault: false,
|
||||
ignore: true,
|
||||
},
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import IEntity from "./IEntity"
|
||||
import GuidEntity from "./GuidEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class VariableReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
MemberScope: new TypeInitialization(String, false),
|
||||
MemberName: String,
|
||||
MemberGuid: GuidEntity,
|
||||
bSelfContext: new TypeInitialization(Boolean, false, false)
|
||||
MemberScope: {
|
||||
value: "",
|
||||
showDefault: false,
|
||||
},
|
||||
MemberName: "",
|
||||
MemberGuid: {
|
||||
type: GuidEntity,
|
||||
},
|
||||
bSelfContext: {
|
||||
value: false,
|
||||
showDefault: false,
|
||||
},
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import IEntity from "./IEntity"
|
||||
export default class Vector2DEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
X: Number,
|
||||
Y: Number,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -3,9 +3,9 @@ import IEntity from "./IEntity"
|
||||
export default class VectorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
X: Number,
|
||||
Y: Number,
|
||||
Z: Number,
|
||||
X: 0,
|
||||
Y: 0,
|
||||
Z: 0,
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
|
||||
@@ -2,11 +2,8 @@ import GeneralSerializer from "./GeneralSerializer"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -19,7 +16,7 @@ export default class CustomSerializer extends GeneralSerializer {
|
||||
|
||||
/**
|
||||
* @param {(v: T, insideString: Boolean) => String} objectWriter
|
||||
* @param {AnyValueConstructor<T>} entityType
|
||||
* @param {AnyValueConstructor} entityType
|
||||
*/
|
||||
constructor(objectWriter, entityType) {
|
||||
super(undefined, entityType)
|
||||
|
||||
@@ -3,11 +3,8 @@ import ISerializer from "./ISerializer"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -18,7 +15,7 @@ export default class GeneralSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {(value: String, entity: T) => String} wrap
|
||||
* @param {AnyValueConstructor<T>} entityType
|
||||
* @param {AnyValueConstructor} entityType
|
||||
*/
|
||||
constructor(wrap, entityType, attributePrefix, attributeSeparator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
|
||||
wrap = wrap ?? (v => `(${v})`)
|
||||
@@ -35,7 +32,6 @@ export default class GeneralSerializer extends ISerializer {
|
||||
let grammar = Grammar.getGrammarForType(ISerializer.grammar, this.entityType)
|
||||
const parseResult = grammar.parse(value)
|
||||
if (!parseResult.status) {
|
||||
// @ts-expect-error
|
||||
throw new Error(`Error when trying to parse the entity ${this.entityType.prototype.constructor.name}.`)
|
||||
}
|
||||
return parseResult.value
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import ByteEntity from "../entity/ByteEntity"
|
||||
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
|
||||
import GuidEntity from "../entity/GuidEntity"
|
||||
import IdentifierEntity from "../entity/IdentifierEntity"
|
||||
@@ -17,17 +18,17 @@ import PinReferenceEntity from "../entity/PinReferenceEntity"
|
||||
import RealUnitEntity from "../entity/UnitRealEntity"
|
||||
import RotatorEntity from "../entity/RotatorEntity"
|
||||
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
|
||||
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
|
||||
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
|
||||
import SymbolEntity from "../entity/SymbolEntity"
|
||||
import TypeInitialization from "../entity/TypeInitialization"
|
||||
import UnionType from "../entity/UnionType"
|
||||
import UnknownKeysEntity from "../entity/UnknownKeysEntity"
|
||||
import Utility from "../Utility"
|
||||
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
|
||||
import Vector2DEntity from "../entity/Vector2DEntity"
|
||||
import VectorEntity from "../entity/VectorEntity"
|
||||
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
|
||||
import ByteEntity from "../entity/ByteEntity"
|
||||
|
||||
/** @typedef {import ("../entity/IEntity").AttributeInformation} AttributeInformation */
|
||||
|
||||
let P = Parsimmon
|
||||
|
||||
@@ -36,30 +37,35 @@ export default class Grammar {
|
||||
/* --- Factory --- */
|
||||
|
||||
/** @param {Grammar} r */
|
||||
static getGrammarForType(r, attributeType, defaultGrammar = r.AttributeAnyValue) {
|
||||
if (attributeType instanceof TypeInitialization) {
|
||||
let result = Grammar.getGrammarForType(r, attributeType.type, defaultGrammar)
|
||||
if (attributeType.serialized && !(attributeType.type instanceof String)) {
|
||||
static getGrammarForType(r, attribute, defaultGrammar = r.AttributeAnyValue) {
|
||||
if (attribute.constructor === Object) {
|
||||
attribute = /** @type {AttributeInformation} */(attribute)
|
||||
let type = attribute.type
|
||||
let result
|
||||
if (type instanceof Array) {
|
||||
result = Grammar.getGrammarForType(r, type[0])
|
||||
.trim(P.optWhitespace)
|
||||
.sepBy(P.string(","))
|
||||
.skip(P.regex(/,?\s*/))
|
||||
.wrap(P.string("("), P.string(")"))
|
||||
} else if (type instanceof UnionType) {
|
||||
result = type.types
|
||||
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
|
||||
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
|
||||
? r.AttributeAnyValue
|
||||
: accum.or(cur))
|
||||
} else {
|
||||
result = Grammar.getGrammarForType(r, type, defaultGrammar)
|
||||
}
|
||||
if (attribute.serialized && !(type instanceof String)) {
|
||||
result = result.wrap(P.string('"'), P.string('"'))
|
||||
}
|
||||
if (attribute.nullable) {
|
||||
result = result.or(r.Null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (Utility.getType(attributeType)) {
|
||||
case Array:
|
||||
return P.seqMap(
|
||||
P.string("("),
|
||||
attributeType
|
||||
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
|
||||
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
|
||||
? r.AttributeAnyValue
|
||||
: accum.or(cur)
|
||||
)
|
||||
.trim(P.optWhitespace)
|
||||
.sepBy(P.string(","))
|
||||
.skip(P.regex(/,?\s*/)),
|
||||
P.string(")"),
|
||||
(_0, grammar, _2) => grammar
|
||||
)
|
||||
switch (attribute) {
|
||||
case Boolean:
|
||||
return r.Boolean
|
||||
case ByteEntity:
|
||||
@@ -102,12 +108,6 @@ export default class Grammar {
|
||||
return r.String
|
||||
case SymbolEntity:
|
||||
return r.Symbol
|
||||
case UnionType:
|
||||
return attributeType.types
|
||||
.map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
|
||||
.reduce((accum, cur) => !cur || accum === r.AttributeAnyValue
|
||||
? r.AttributeAnyValue
|
||||
: accum.or(cur))
|
||||
case VariableReferenceEntity:
|
||||
return r.VariableReference
|
||||
case Vector2DEntity:
|
||||
@@ -145,7 +145,10 @@ export default class Grammar {
|
||||
// Once the attribute name is known, look into entityType.attributes to get its type
|
||||
const attributeKey = attributeName.split(".")
|
||||
const attribute = Utility.objectGet(entityType.attributes, attributeKey)
|
||||
let attributeValueGrammar = Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
|
||||
let attributeValueGrammar =
|
||||
attribute.constructor === Object && /** @type {AttributeInformation} */(attribute).serialized
|
||||
? r.String
|
||||
: Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
|
||||
// Returns a setter function for the attribute
|
||||
return attributeValueGrammar.map(attributeValue =>
|
||||
entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import Grammar from "./Grammar"
|
||||
import Parsimmon from "parsimmon"
|
||||
import SerializerFactory from "./SerializerFactory"
|
||||
import TypeInitialization from "../entity/TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
import IEntity from "../entity/IEntity"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
|
||||
*/
|
||||
|
||||
/** @template {AnyValue} T */
|
||||
@@ -18,7 +15,7 @@ export default class ISerializer {
|
||||
|
||||
static grammar = Parsimmon.createLanguage(new Grammar())
|
||||
|
||||
/** @param {AnyValueConstructor<T>} entityType */
|
||||
/** @param {AnyValueConstructor} entityType */
|
||||
constructor(
|
||||
entityType,
|
||||
attributePrefix = "",
|
||||
@@ -76,7 +73,11 @@ export default class ISerializer {
|
||||
if (!serializer) {
|
||||
throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`)
|
||||
}
|
||||
return serializer.write(entity, value, insideString)
|
||||
return serializer.write(
|
||||
value instanceof IEntity ? value : entity,
|
||||
value,
|
||||
insideString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,13 +91,12 @@ export default class ISerializer {
|
||||
let fullKey = key.concat("")
|
||||
const last = fullKey.length - 1
|
||||
const attributes = /** @type {EntityConstructor} */(object.constructor).attributes
|
||||
const keys =
|
||||
attributes ?
|
||||
Utility.mergeArrays(
|
||||
Object.getOwnPropertyNames(attributes),
|
||||
Object.getOwnPropertyNames(object)
|
||||
)
|
||||
: Object.getOwnPropertyNames(object)
|
||||
const keys = attributes
|
||||
? Utility.mergeArrays(
|
||||
Object.getOwnPropertyNames(attributes),
|
||||
Object.getOwnPropertyNames(object)
|
||||
)
|
||||
: Object.getOwnPropertyNames(object)
|
||||
for (const property of keys) {
|
||||
fullKey[last] = property
|
||||
const value = object[property]
|
||||
@@ -125,10 +125,9 @@ export default class ISerializer {
|
||||
}
|
||||
|
||||
showProperty(entity, object, attributeKey, attributeValue) {
|
||||
// @ts-expect-error
|
||||
const attributes = /** @type {EntityConstructor} */(this.entityType).attributes
|
||||
const attribute = Utility.objectGet(attributes, attributeKey)
|
||||
if (attribute instanceof TypeInitialization) {
|
||||
if (attribute.constructor === Object) {
|
||||
if (attribute.ignored) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("../entity/IEntity").AnyValueConstructor<T>} AnyValueConstructor
|
||||
*/
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
@@ -16,6 +17,11 @@ export default class SerializerFactory {
|
||||
/** @type {Map<AnyValueConstructor<AnyValue>, ISerializer<AnyValue>>} */
|
||||
static #serializers = new Map()
|
||||
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @param {AnyValueConstructor<T>} entity
|
||||
* @param {ISerializer<T>} object
|
||||
*/
|
||||
static registerSerializer(entity, object) {
|
||||
SerializerFactory.#serializers.set(entity, object)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import Utility from "../Utility"
|
||||
import GeneralSerializer from "./GeneralSerializer"
|
||||
|
||||
/** @typedef {import("../entity/TypeInitialization").AnyValue} AnyValue */
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @typedef {import("../entity/TypeInitialization").AnyValueConstructor<T>} AnyValueConstructor
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {AnyValue} T
|
||||
* @extends {GeneralSerializer<T>}
|
||||
*/
|
||||
export default class ToStringSerializer extends GeneralSerializer {
|
||||
|
||||
/** @param {AnyValueConstructor<T>} entityType */
|
||||
/** @param {AnyValueConstructor} entityType */
|
||||
constructor(entityType) {
|
||||
super(undefined, entityType)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import RealUnitEntity from "../entity/UnitRealEntity"
|
||||
import RotatorEntity from "../entity/RotatorEntity"
|
||||
import SerializerFactory from "./SerializerFactory"
|
||||
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
|
||||
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
|
||||
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
|
||||
import SymbolEntity from "../entity/SymbolEntity"
|
||||
import ToStringSerializer from "./ToStringSerializer"
|
||||
@@ -27,7 +28,11 @@ import Utility from "../Utility"
|
||||
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
|
||||
import Vector2DEntity from "../entity/Vector2DEntity"
|
||||
import VectorEntity from "../entity/VectorEntity"
|
||||
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").AnySimpleValue} AnySimpleValue
|
||||
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
|
||||
*/
|
||||
|
||||
export default function initializeSerializerFactory() {
|
||||
|
||||
@@ -44,11 +49,10 @@ export default function initializeSerializerFactory() {
|
||||
SerializerFactory.registerSerializer(
|
||||
Array,
|
||||
new CustomSerializer(
|
||||
/** @param {Array} array */
|
||||
/** @param {AnySimpleValue[]} array */
|
||||
(array, insideString) =>
|
||||
`(${array
|
||||
.map(v =>
|
||||
// @ts-expect-error
|
||||
SerializerFactory.getSerializer(Utility.getType(v)).serialize(v, insideString) + ","
|
||||
)
|
||||
.join("")
|
||||
|
||||
Reference in New Issue
Block a user