UNPKG

@mendix/pluggable-widgets-tools

Version:
278 lines (277 loc) 12.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateClientTypes = generateClientTypes; exports.hasOptionalDataSource = hasOptionalDataSource; exports.toAttributeClientType = toAttributeClientType; exports.toExpressionClientType = toExpressionClientType; exports.toAssociationOutputType = toAssociationOutputType; exports.toAttributeOutputType = toAttributeOutputType; exports.toUniqueUnionType = toUniqueUnionType; const helpers_1 = require("./helpers"); function generateClientTypes(widgetName, properties, systemProperties, isNative) { function resolveProp(key) { return properties.find(p => p.$.key === key); } const isLabeled = systemProperties.some(p => p.$.key === "Label"); const results = Array.of(); results.push(isNative ? `export interface ${widgetName}Props<Style> { name: string; style: Style[]; ${generateClientTypeBody(properties, true, results, resolveProp)} }` : `export interface ${widgetName}ContainerProps { name: string;${!isLabeled ? ` class: string; style?: CSSProperties;` : ""} tabIndex?: number;${isLabeled ? ` id: string;` : ""} ${generateClientTypeBody(properties, false, results, resolveProp)} }`); return results; } function isEmbeddedOnChangeAction(propertyPath, properties) { return properties.some(prop => { if (prop.$.onChange === propertyPath) { return true; } if (prop.$.type === "object" && prop.properties && prop.properties.length > 0) { return prop.properties.some(prop => isEmbeddedOnChangeAction(`../${propertyPath}`, (0, helpers_1.extractProperties)(prop))); } return false; }); } function generateClientTypeBody(properties, isNative, generatedTypes, resolveProp) { return properties .filter(prop => { if (prop.$.type === "action" && isEmbeddedOnChangeAction(prop.$.key, properties)) { return false; } if (prop.$.type === "datasource" && prop.$.isLinked) { return false; } return true; }) .map(prop => ` ${prop.$.key}${isOptionalProp(prop, resolveProp) ? "?" : ""}: ${toClientPropType(prop, isNative, generatedTypes, resolveProp)};`) .join("\n"); } function isOptionalProp(prop, resolveProp) { var _a, _b; switch (prop.$.type) { case "string": case "object": return false; case "action": return true; case "selection": return (((_b = (_a = prop.selectionTypes) === null || _a === void 0 ? void 0 : _a.flatMap(t => t.selectionType.map(t => t.$.name))) !== null && _b !== void 0 ? _b : []).includes("None") || hasOptionalDataSource(prop, resolveProp)); default: return prop.$.required === "false" || hasOptionalDataSource(prop, resolveProp); } } function hasOptionalDataSource(prop, resolveProp) { var _a; return prop.$.dataSource && ((_a = resolveProp(prop.$.dataSource)) === null || _a === void 0 ? void 0 : _a.$.required) === "false"; } function toActionVariablesOutputType(actionVariables) { const types = actionVariables === null || actionVariables === void 0 ? void 0 : actionVariables.flatMap(av => av.actionVariable).map(avt => `${avt.$.key}: ${toOption(toAttributeClientType(avt.$.type))}`).join("; "); return types ? `<{ ${types} }>` : ""; } function toClientPropType(prop, isNative, generatedTypes, resolveProp) { var _a, _b, _c, _d, _e; switch (prop.$.type) { case "boolean": return "boolean"; case "string": return "string"; case "action": const variableTypes = toActionVariablesOutputType(prop.actionVariables); return (prop.$.dataSource ? "ListActionValue" : "ActionValue") + variableTypes; case "textTemplate": return prop.$.dataSource ? "ListExpressionValue<string>" : "DynamicValue<string>"; case "integer": return "number"; case "decimal": return "Big"; case "icon": return isNative ? "DynamicValue<NativeIcon>" : "DynamicValue<WebIcon>"; case "image": return isNative ? "DynamicValue<NativeImage>" : "DynamicValue<WebImage>"; case "file": return "DynamicValue<FileValue>"; case "datasource": return "ListValue"; case "attribute": { if (!((_a = prop.attributeTypes) === null || _a === void 0 ? void 0 : _a.length)) { throw new Error("[XML] Attribute property requires attributeTypes element"); } const types = prop.attributeTypes .flatMap(ats => ats.attributeType) .map(at => toAttributeClientType(at.$.name)); const unionType = toUniqueUnionType(types); const linkedToDataSource = !!prop.$.dataSource; if (prop.$.isMetaData === "true") { if (!linkedToDataSource) { throw new Error(`[XML] Attribute property can only have isMetaData="true" when linked to a datasource`); } return `AttributeMetaData<${unionType}>`; } if (!((_b = prop.associationTypes) === null || _b === void 0 ? void 0 : _b.length)) { return toAttributeOutputType("Reference", linkedToDataSource, unionType); } else { const reftypes = prop.associationTypes .flatMap(ats => ats.associationType) .map(at => toAttributeOutputType(at.$.name, linkedToDataSource, unionType)); return toUniqueUnionType(reftypes); } } case "association": { if (!((_c = prop.associationTypes) === null || _c === void 0 ? void 0 : _c.length)) { throw new Error("[XML] Association property requires associationTypes element"); } const linkedToDataSource = !!prop.$.dataSource; if (prop.$.isMetaData === "true") { if (!linkedToDataSource) { throw new Error(`[XML] Association property can only have isMetaData="true" when linked to a datasource`); } return "AssociationMetaData"; } const types = prop.associationTypes .flatMap(ats => ats.associationType) .map(at => toAssociationOutputType(at.$.name, linkedToDataSource)); return toUniqueUnionType(types); } case "expression": if (!prop.returnType || prop.returnType.length === 0) { throw new Error("[XML] Expression property requires returnType element"); } const type = toExpressionClientType(prop.returnType[0], resolveProp); return prop.$.dataSource ? `ListExpressionValue<${type}>` : `DynamicValue<${type}>`; case "enumeration": const typeName = (0, helpers_1.capitalizeFirstLetter)(prop.$.key) + "Enum"; generatedTypes.push(generateEnum(typeName, prop)); return typeName; case "object": if (!((_d = prop.properties) === null || _d === void 0 ? void 0 : _d.length)) { throw new Error("[XML] Object property requires properties element"); } const childType = (0, helpers_1.capitalizeFirstLetter)(prop.$.key) + "Type"; const childProperties = (0, helpers_1.extractProperties)(prop.properties[0]); const resolveChildProp = (key) => key.startsWith("../") ? resolveProp(key.substring(3)) : childProperties.find(p => p.$.key === key); generatedTypes.push(`export interface ${childType} { ${generateClientTypeBody(childProperties, isNative, generatedTypes, resolveChildProp)} }`); return prop.$.isList === "true" ? `${childType}[]` : childType; case "widgets": return prop.$.dataSource ? "ListWidgetValue" : "ReactNode"; case "selection": if (!((_e = prop.selectionTypes) === null || _e === void 0 ? void 0 : _e.length)) { throw new Error("[XML] Selection property requires selectionTypes element"); } const selectionTypes = prop.selectionTypes.flatMap(s => s.selectionType).map(s => s.$.name); const clientTypes = selectionTypes.filter(s => s !== "None").map(toSelectionClientType); return toUniqueUnionType(clientTypes); default: return "any"; } } function generateEnum(typeName, prop) { var _a, _b; if (!((_a = prop.enumerationValues) === null || _a === void 0 ? void 0 : _a.length) || !((_b = prop.enumerationValues[0].enumerationValue) === null || _b === void 0 ? void 0 : _b.length)) { throw new Error("[XML] Enumeration property requires enumerations element"); } const members = prop.enumerationValues[0].enumerationValue.map(type => `"${type.$.key}"`); return `export type ${typeName} = ${members.join(" | ")};`; } function toAttributeClientType(xmlType) { switch (xmlType) { case "Boolean": return "boolean"; case "DateTime": return "Date"; case "AutoNumber": case "Decimal": case "Integer": case "Long": return "Big"; case "HashString": case "String": case "Enum": return "string"; default: return "any"; } } function toExpressionClientType(returnTypeProp, resolveProp) { var _a, _b; const { type, assignableTo } = (_a = returnTypeProp.$) !== null && _a !== void 0 ? _a : {}; if ((type && assignableTo) || (!type && !assignableTo)) { throw new Error("[XML] Invalid return type for expression property: either type or assignableTo must be specified."); } if (type) { return toAttributeClientType(type); } const resolvedProperty = resolveProp(assignableTo); if (!resolvedProperty) { throw new Error(`[XML] Invalid return type for expression property: invalid property path '${assignableTo}' in assignableTo attribute.`); } if (resolvedProperty.$.type !== "attribute") { throw new Error(`[XML] Invalid return type for expression property: assignableTo property '${assignableTo}' must be of type Attribute.`); } const { attributeTypes } = resolvedProperty; if (!((_b = attributeTypes === null || attributeTypes === void 0 ? void 0 : attributeTypes[0]) === null || _b === void 0 ? void 0 : _b.attributeType)) { throw new Error(`[XML] Invalid return type for expression property: assignableTo property '${assignableTo}' must have attribute types.`); } const allowedTypes = ["Boolean", "DateTime", "Enum", "Integer", "Long", "String", "Decimal"]; const types = attributeTypes .map(ats => ats.attributeType) .reduce((a, i) => a.concat(i), []) .map(at => at.$.name); const unsupportedTypes = types.filter(t => !allowedTypes.includes(t)); if (unsupportedTypes.length !== 0) { throw new Error(`[XML] Invalid return type for expression property: assignableTo property '${assignableTo}' has unsupported attribute type ${(0, helpers_1.commasAnd)(unsupportedTypes)}.`); } return toUniqueUnionType(types.map(toAttributeClientType)); } function toAssociationOutputType(xmlType, linkedToDataSource) { switch (xmlType) { case "Reference": return linkedToDataSource ? "ListReferenceValue" : "ReferenceValue"; case "ReferenceSet": return linkedToDataSource ? "ListReferenceSetValue" : "ReferenceSetValue"; default: return "any"; } } function toAttributeOutputType(xmlType, linkedToDataSource, unionAttributeType) { switch (xmlType) { case "Reference": return linkedToDataSource ? `ListAttributeValue<${unionAttributeType}>` : `EditableValue<${unionAttributeType}>`; case "ReferenceSet": return linkedToDataSource ? `ListAttributeListValue<${unionAttributeType}>` : `EditableListValue<${unionAttributeType}>`; default: return "any"; } } function toSelectionClientType(xmlType) { switch (xmlType) { case "Single": return "SelectionSingleValue"; case "Multi": return "SelectionMultiValue"; default: return "any"; } } function toOption(type) { return `Option<${type}>`; } function toUniqueUnionType(types) { return types.length ? Array.from(new Set(types)).join(" | ") : "any"; }