{"version":3,"sources":["../src/bundler/plugins.ts","../src/bundler/transform/phpParser.ts","../src/bundler/transform/apiMap.ts","../src/double/helpers.ts","../src/bundler/transform/typescriptGenerator.ts","../src/bundler/transform/typescriptUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { VitePlugin, createUnplugin } from \"unplugin\";\nimport { Bundler } from \"../double/bundler\";\nimport { getApiMap } from \"./transform/apiMap\";\nimport { updateTypescriptDefinition } from \"./transform/typescriptUpdater\";\n\n// TODO: Make this configurable\nconst doubleBasePath = process.cwd()\n\nconst phpFileRegex = /\\.php/\n\ntype UserOptions = {\n    bundler: Bundler\n};\n\nexport const unpluginPHP = createUnplugin((userOptions: UserOptions) => {\n    return {\n        name: 'double-php',\n        transformInclude(id) {\n            return phpFileRegex.test(id)\n        },\n        transform(phpSrc, id) {\n            if(phpFileRegex.test(id)) {\n                const phpFilePath = id.replace(/\\?.+/, '')\n                const doublePath = phpFilePath.replace(doubleBasePath, '').replace('.php', '')\n                let tsPath = doublePath\n                updateTypescriptDefinition(phpSrc, tsPath)\n                return {\n                    code: `export default ${JSON.stringify(getApiMap(phpSrc))}`,\n                    map: null,\n                }\n            }\n            return null\n        }\n    }\n})\n\nexport const doubleVitePlugin = (): VitePlugin => {\n    return unpluginPHP.vite({\n        bundler: 'vite',\n    })\n}\n\nexport const doubleWebpackPlugin = (): VitePlugin => {\n    return unpluginPHP.webpack({\n        bundler: 'webpack',\n    })\n}\n\nexport const doubleRollupPlugin = (): VitePlugin => {\n    return unpluginPHP.rollup({\n        bundler: 'rollup',\n    })\n}","import { Array, Class, Declaration, Engine, Entry, Expression, Method, New, Return } from 'php-parser'\nconst fs = require('fs')\ntype MethodDefinition = {\n    name: string\n    return: string\n}\n\ntype PHPMetaData = {\n    getters: MethodDefinition[]\n    actions: MethodDefinition[]\n}\n\nfunction treeToReturnTS(tree: Expression): string {\n    if (!tree) {\n        return 'any'\n    }\n\n    if (tree.kind === 'array') {\n        const arrayLike = tree as Array\n        // @ts-ignore this is a wrong type coming from php-parser\n        const children = arrayLike.items.map(item => treeToReturnTS(item))\n        if(!arrayLike.items[0]) {\n            return `[]`\n        }\n        // TODO: This does not work properly for all array definitions.\n        // For example for arrays where some children have explicit keys and others don't\n        // TODO: Take care of proper indentation\n\n        // @ts-ignore this is a wrong type coming from php-parser\n        const noItemHasKey = arrayLike.items.every(item => !item.hasOwnProperty('key') || [undefined, null].includes(item?.key))\n        \n        if (noItemHasKey) {\n            // Check if all children are the same\n            const allChildrenIdentical = !children.some(child => child !== children[0])\n            if(allChildrenIdentical) {\n                return `${children[0]}[]`\n            }\n            return `(${children.join(' | ')})[]`\n        } else {\n            let keylessIndex = 0\n            // @ts-ignore this is a wrong type coming from php-parser\n            const childData = arrayLike.items.map((item, idx) => {\n                // For items without a key, the key is a number starting from 0\n                // and being 1 larger than the last numeric key\n                // https://www.php.net/manual/en/language.types.array.php\n                let key = item.key?.value\n                if([undefined, null].includes(key)) {\n                    key = keylessIndex++\n                } else {\n                    if(item.key.kind === 'number') {\n                        keylessIndex = parseInt(item.key.value) + 1\n                    }\n                }\n                return key + ': ' + children[idx]\n            })\n            return `{\\n      ${childData.join('\\n      ')}\\n    }`\n            \n        }\n    }\n    if(tree.kind === 'entry') {\n        const entryLike = tree as Entry\n        if (!entryLike.key) {\n            return treeToReturnTS(entryLike.value)\n        }\n        if (entryLike.key) {\n            if (['string', 'number'].includes(entryLike.key.kind)) {\n                return treeToReturnTS(entryLike.value)\n            }\n        }\n    }\n\n    if(['number', 'string'].includes(tree.kind)) {\n        // @ts-ignore value actually does exist\n        return tree.kind\n    }\n    \n    return 'any'\n}\n\nexport const getPHPMetaData = (src: string): PHPMetaData => {\n    const parser = new Engine({\n        parser: {\n            extractDoc: true,\n            php7: true\n        },\n        ast: {\n            withPositions: false\n        }\n    });\n    \n    const responseData = {\n        getters: [],\n        actions: [],\n    }\n\n    try {\n        const returnValue = parser.parseCode(src, '').children.find(child => child.kind === 'return') as Return\n        \n        \n        // Sadly the php-parser typescript definition can't automatically detect which node type\n        // an entry has\n        // @ts-ignore\n        if(!returnValue?.expr?.what?.body) {\n            return responseData\n        }\n        const returnBody = ((returnValue.expr as New).what as Class).body\n        // fs.writeFileSync('debug.json', JSON.stringify(returnBody))\n        returnBody.forEach((bodyEntry: Declaration) => {\n            if (bodyEntry.kind === 'method') {\n                const method = bodyEntry as Method\n                \n                if(method.isStatic || method.visibility !== 'public') {\n                    return\n                }\n\n                let methodName = method.name\n                if(typeof methodName !== 'string') {\n                    methodName = methodName.name\n                }\n\n                let returnType = 'any'\n                const returnValue = method.body?.children.find(child => child.kind === 'return') as undefined | Return\n                if (returnValue) {\n                    returnType = treeToReturnTS(returnValue.expr)\n                }\n                \n                // We handle methods starting with a \"get\" followed by an uppercase character differently\n                // Those are methods we call to get our data object.\n                // All other methods will be available as actions to be called on demand\n                if (methodName.startsWith('get')) {\n\n                    const firstKeyCharacter = methodName.substring(3, 4)\n                    if (firstKeyCharacter === firstKeyCharacter.toLocaleUpperCase()) {\n                        // This is a getXyz method\n                        const nameWithoutPrefix = firstKeyCharacter.toLocaleLowerCase() + methodName.substring(4)\n                        responseData.getters.push({\n                            name: nameWithoutPrefix,\n                            return: returnType\n                        })\n                    }\n                } else {\n                    responseData.actions.push({\n                        name: methodName,\n                        return: returnType\n                    })\n                }\n            }\n        })\n    } catch(e) {\n        console.log('Could not parse PHP file:')\n        console.log(e)\n        return responseData\n    }\n\n    return responseData\n}","import { getPHPMetaData } from \"./phpParser\"\nconst fs = require('fs')\n\nexport type ApiMapEntry = { actions: string[], getters: string[] }\n\nexport function getApiMap(src: string): ApiMapEntry {\n    const metaData = getPHPMetaData(src)\n    return {\n        getters: metaData.getters.map(entry => entry.name),\n        actions: metaData.actions.map(entry => entry.name),\n    }\n}\n\nexport const updateApiMap = (src: string, doublePath: string) => {\n    const mapPath = './doubleApiMap.ts'\n    let map = {}\n    const mapPrefix = 'export const apiMap = '\n    if(fs.existsSync(mapPath)) {\n        try {\n            map = JSON.parse(fs.readFileSync(mapPath).toString().substr(mapPrefix.length - 1))\n        } catch(e) {\n            console.log(e)\n        }\n    }\n    const oldData = map[doublePath]\n    map[doublePath] = getApiMap(src)\n\n    // Only write the new file if something changed\n    if(JSON.stringify(oldData) != JSON.stringify(map[doublePath])) {\n        fs.writeFileSync(mapPath, mapPrefix + JSON.stringify(map))\n    }\n}","export const escapeRegex = (value: string) => {\n    return value.replace( /[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, \"\\\\$&\" )\n}","import { getPHPMetaData } from \"./phpParser\"\n\nexport const getTypescriptDefinition = (src: string, id: string) => {\n    const metaData = getPHPMetaData(src)\n    const tsID = id\n        .toLowerCase()\n        .replaceAll(/[^a-zA-Z0-9\\/]/g, 'x')\n        .split('/')\n        .filter((part: string) => !!part)\n        .map((part: string) => {\n            return `${part[0].toLocaleUpperCase()}${part.slice(1)}`\n        })\n        .join('')\n\n    const getters = metaData.getters.map(entry => {\n        return `\\n    ${entry.name}: ${entry.return}`\n    })\n\n    const actions = metaData.actions.map(entry => {\n        return `\\n    ${entry.name}: (options?: Record<string, any>) => Promise<unknown>`\n    })\n\n    const isLoading = metaData.actions.map(entry => {\n        return `\\n    ${entry.name}?: boolean`\n    })\n\n\n    const tsDefinition = `type ${tsID}MainType = {\n  state: { ${getters.join(\"\")}\n  }\n  actions: { ${actions.join(\"\")}\n  }\n  isLoading: { ${isLoading.join(\"\")}\n  }\n}\n`\n    return {\n        tsDefinition,\n        tsID,\n    }\n}\n","import { escapeRegex } from \"../../double/helpers\"\nimport { getTypescriptDefinition } from \"./typescriptGenerator\"\nconst path = require('path')\nconst fs = require('fs')\n\nexport const updateTypescriptDefinition = (src: string, doublePath: string) => {\n    const typesPath = './src/double.d.ts'\n\n    const {tsDefinition, tsID} = getTypescriptDefinition(src, doublePath)\n    if(!fs.existsSync(path.dirname(typesPath))) {\n        fs.mkdirSync(path.dirname(typesPath))\n    }\n    let existingTypes = ''\n    if(fs.existsSync(typesPath)) {\n        existingTypes = fs.readFileSync(typesPath).toString()\n    }\n    const beginIndicator = '// BEGIN: ' + escapeRegex(doublePath) + '\\n'\n    const endIndicator = '// END: ' + escapeRegex(doublePath) + '\\n\\n'\n    let newTypes = existingTypes.replace(new RegExp(beginIndicator + '[\\\\s\\\\S]*' + endIndicator), '')\n    newTypes += beginIndicator + tsDefinition + endIndicator\n\n    // Update the global `double` type\n    const globalBeginIndicator = '\\n\\ntype doubleTypes = {\\n'\n    const globalEndIndicator = '}\\n'\n    const globalTypeRegex = new RegExp(globalBeginIndicator + '([^}]*)' + globalEndIndicator)\n    const existingGlobalTypeBlock = newTypes.match(globalTypeRegex)\n    newTypes = newTypes.replace(globalTypeRegex, '')\n    let globalTypes = ''\n    if(existingGlobalTypeBlock) {\n        globalTypes = existingGlobalTypeBlock[1]\n        globalTypes = globalTypes.replace(new RegExp('\\[ \\t]*\\'' + doublePath + '\\':.*\\\\n'), '')\n    }\n\n    globalTypes += `  '${doublePath}': ${tsID}MainType\\n`\n\n    newTypes += globalBeginIndicator + globalTypes + globalEndIndicator\n\n    fs.writeFileSync(typesPath, newTypes)\n}"]}