UNPKG

@builder.io/mitosis

Version:

Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io

288 lines (283 loc) 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.componentToSwift = void 0; const dedent_1 = require("../../helpers/dedent"); const fast_clone_1 = require("../../helpers/fast-clone"); const generic_format_1 = require("../../helpers/generic-format"); const get_props_1 = require("../../helpers/get-props"); const get_refs_1 = require("../../helpers/get-refs"); const getters_to_functions_1 = require("../../helpers/getters-to-functions"); const merge_options_1 = require("../../helpers/merge-options"); const on_event_1 = require("../../helpers/on-event"); const patterns_1 = require("../../helpers/patterns"); const process_code_1 = require("../../helpers/plugins/process-code"); const slots_1 = require("../../helpers/slots"); const strip_meta_properties_1 = require("../../helpers/strip-meta-properties"); const plugins_1 = require("../../modules/plugins"); const blocks_1 = require("./blocks"); const helpers_1 = require("./helpers"); const DEFAULT_OPTIONS = { stateType: 'state', formatCode: true, includeTypes: true, includePreview: true, classPrefix: '', }; const componentToSwift = (userProvidedOptions) => ({ component }) => { var _a, _b; const options = (0, merge_options_1.initializeOptions)({ target: 'swift', component, defaults: DEFAULT_OPTIONS, userOptions: userProvidedOptions, }); options.plugins = [ ...(options.plugins || []), (0, on_event_1.processOnEventHooksPlugin)(), (0, process_code_1.CODE_PROCESSOR_PLUGIN)((codeType) => { switch (codeType) { case 'bindings': case 'properties': case 'hooks': case 'hooks-deps': case 'hooks-deps-array': case 'state': case 'context-set': case 'dynamic-jsx-elements': case 'types': return (x) => (0, helpers_1.convertConsoleLogToPrint)(x); } }), (0, process_code_1.CODE_PROCESSOR_PLUGIN)((codeType) => { switch (codeType) { case 'hooks': return (code) => (0, helpers_1.stripStateAndProps)({ json, options })(code); case 'bindings': case 'hooks-deps': case 'state': return (code) => (0, patterns_1.stripGetter)((0, helpers_1.stripStateAndProps)({ json, options })(code)); case 'properties': case 'context-set': return (code) => (0, helpers_1.stripStateAndProps)({ json, options })(code); case 'dynamic-jsx-elements': case 'hooks-deps-array': case 'types': return (x) => (0, helpers_1.convertConsoleLogToPrint)(x); } }), ]; // Make a copy we can safely mutate let json = (0, fast_clone_1.fastClone)(component); json = (0, plugins_1.runPreJsonPlugins)({ json, plugins: options.plugins }); (0, getters_to_functions_1.gettersToFunctions)(json); const componentName = options.classPrefix + (json.name || ((_a = json.meta.useMetadata) === null || _a === void 0 ? void 0 : _a.name) || 'MitosisComponent'); // Process props const filteredProps = Array.from((0, get_props_1.getProps)(json)).filter((prop) => !(0, slots_1.isSlotProperty)(prop)); const props = Array.from(new Set(filteredProps)); // Process refs (not directly applicable in SwiftUI, will be converted to @State) const refs = Array.from((0, get_refs_1.getRefs)(json)) .map((0, helpers_1.stripStateAndProps)({ json, options })) .filter((x) => !props.includes(x)); json = (0, plugins_1.runPostJsonPlugins)({ json, plugins: options.plugins }); (0, strip_meta_properties_1.stripMetaProperties)(json); // Generate state variables const stateProperties = Object.entries(json.state) .filter(([_, value]) => { // Skip methods - they'll be handled separately return !((value === null || value === void 0 ? void 0 : value.type) === 'method' || ((value === null || value === void 0 ? void 0 : value.code) && (value.code.includes('function') || value.code.includes('=>')))); }) .map(([key, value]) => { var _a, _b, _c, _d, _e; // Check for value properties safely const propertyType = value === null || value === void 0 ? void 0 : value.propertyType; // Determine Swift type - handle missing type property let valueType = 'Any'; if (value === null || value === void 0 ? void 0 : value.typeParameter) { valueType = value.typeParameter; } else { // Try to infer type from code if possible if (((_a = value === null || value === void 0 ? void 0 : value.code) === null || _a === void 0 ? void 0 : _a.includes('"')) || ((_b = value === null || value === void 0 ? void 0 : value.code) === null || _b === void 0 ? void 0 : _b.includes("'"))) { valueType = 'string'; } else if ((_c = value === null || value === void 0 ? void 0 : value.code) === null || _c === void 0 ? void 0 : _c.match(/^[0-9]+(\.[0-9]+)?$/)) { valueType = 'number'; } else if ((value === null || value === void 0 ? void 0 : value.code) === 'true' || (value === null || value === void 0 ? void 0 : value.code) === 'false') { valueType = 'boolean'; } else if (((_d = value === null || value === void 0 ? void 0 : value.code) === null || _d === void 0 ? void 0 : _d.startsWith('[')) || ((_e = value === null || value === void 0 ? void 0 : value.code) === null || _e === void 0 ? void 0 : _e.startsWith('Array'))) { valueType = 'Array<Any>'; } } const typeAnnotation = (0, helpers_1.getStatePropertyTypeAnnotation)(propertyType, valueType); const swiftType = (0, helpers_1.getSwiftType)(valueType); let stateDeclaration = `${typeAnnotation} ${key}: ${swiftType}`; // Add default value if present if (value === null || value === void 0 ? void 0 : value.code) { stateDeclaration += ` = ${value.code}`; } else { // Add default initialization based on type switch (swiftType) { case 'String': stateDeclaration += ' = ""'; break; case 'Bool': stateDeclaration += ' = false'; break; case 'Double': case 'Int': stateDeclaration += ' = 0'; break; case 'Array<String>': case '[String]': stateDeclaration += ' = []'; break; default: if (swiftType.includes('Array') || swiftType.includes('[')) { stateDeclaration += ' = []'; } else if (swiftType !== 'Any' && swiftType !== 'Void') { stateDeclaration += '/* initialize with appropriate default */'; } } } return stateDeclaration; }) .join('\n '); // Generate state function variables with inline closure assignment const functionStateProperties = []; Object.entries(json.state) .filter(([_, value]) => { return ((value === null || value === void 0 ? void 0 : value.type) === 'method' || ((value === null || value === void 0 ? void 0 : value.code) && (value.code.includes('function') || value.code.includes('=>')))); }) .forEach(([key, value]) => { if (!(value === null || value === void 0 ? void 0 : value.code)) { // Handle empty function with inline closure functionStateProperties.push(`private var ${key}: () -> Void = { () in /* Empty function */ }`); return; } // Convert the JS function to Swift const processedCode = (0, helpers_1.stripStateAndProps)({ json, options })(value.code); const { swiftCode, signature } = (0, helpers_1.convertJsFunctionToSwift)(processedCode, `_${key}`); // Parse signature to get parameter list and return type const signatureMatch = signature.match(/func _([^(]+)\(([^)]*)\) -> ([^{]+)/); if (signatureMatch) { const [, funcName, params, returnType] = signatureMatch; // Create the function type for the state variable const paramTypes = params ? params .split(',') .map((p) => { var _a; return ((_a = p.trim().split(':')[1]) === null || _a === void 0 ? void 0 : _a.trim()) || 'Any'; }) .join(', ') : ''; const functionType = params ? `(${paramTypes}) -> ${returnType.trim()}` : `() -> ${returnType.trim()}`; // Extract function body from swiftCode const bodyMatch = swiftCode.match(/{\s*([\s\S]*?)\s*}/); const functionBody = bodyMatch ? bodyMatch[1].trim() : '/* Empty function body */'; // Build closure syntax for inline assignment const closureSyntax = params ? `{ (${params}) -> ${returnType.trim()} in\n ${functionBody}\n }` : `{ () -> ${returnType.trim()} in\n ${functionBody}\n }`; // Add the state variable declaration with inline closure assignment functionStateProperties.push(`var ${key}: ${functionType} = ${closureSyntax}`); } else { // Fallback if signature parsing fails functionStateProperties.push(`var ${key}: () -> Void = { () in /* Could not parse function */ }`); } }); // Process lifecycle methods const lifecycleMethods = []; // Only add onInit if needed and if there are no function state properties // (to avoid duplicate initializers) if ((_b = json.hooks.onInit) === null || _b === void 0 ? void 0 : _b.code) { lifecycleMethods.push((0, dedent_1.dedent) ` init() { ${json.hooks.onInit.code} } `); } // Generate SwiftUI component let str = (0, dedent_1.dedent) ` import SwiftUI struct ${componentName}: View { ${props .map((prop) => { var _a, _b, _c, _d; const propType = ((_b = (_a = json.props) === null || _a === void 0 ? void 0 : _a[prop]) === null || _b === void 0 ? void 0 : _b.propertyType) || 'Any'; const swiftType = (0, helpers_1.getSwiftType)(propType); return `let ${prop}: ${swiftType}${((_d = (_c = json.props) === null || _c === void 0 ? void 0 : _c[prop]) === null || _d === void 0 ? void 0 : _d.optional) ? '?' : ''}`; }) .join('\n ')} ${stateProperties} ${functionStateProperties.length > 0 ? '\n // Function state variables\n ' + functionStateProperties.join('\n ') : ''} var body: some View { ${json.children .map((item) => (0, blocks_1.blockToSwift)({ json: item, options: options, parentComponent: json, })) .join('\n')} ${lifecycleMethods.filter((method) => method.startsWith('.')).join('\n ')} } ${lifecycleMethods.filter((method) => !method.startsWith('.')).join('\n ')} } `; // Add preview if enabled if (options.includePreview) { str += (0, dedent_1.dedent) ` \n #if DEBUG struct ${componentName}_Previews: PreviewProvider { static var previews: some View { ${componentName}( ${props .map((prop) => { var _a, _b, _c, _d; const propType = ((_b = (_a = json.props) === null || _a === void 0 ? void 0 : _a[prop]) === null || _b === void 0 ? void 0 : _b.propertyType) || 'Any'; const swiftType = (0, helpers_1.getSwiftType)(propType); // Generate appropriate preview values based on type let previewValue = ''; switch (swiftType) { case 'String': previewValue = '"Preview"'; break; case 'Bool': previewValue = 'false'; break; case 'Double': case 'Int': previewValue = '0'; break; default: if ((_d = (_c = json.props) === null || _c === void 0 ? void 0 : _c[prop]) === null || _d === void 0 ? void 0 : _d.optional) { previewValue = 'nil'; } else { previewValue = '/* provide preview value */'; } } return `${prop}: ${previewValue}`; }) .join(',\n ')} ) } } #endif `; } str = (0, plugins_1.runPreCodePlugins)({ json, code: str, plugins: options.plugins }); str = (0, generic_format_1.format)(str); str = (0, plugins_1.runPostCodePlugins)({ json, code: str, plugins: options.plugins }); return str; }; exports.componentToSwift = componentToSwift;