UNPKG

wails-auto-ipc

Version:

generate single app.go for wails app from multiple files

173 lines (164 loc) 5.04 kB
// @ts-check import { NeinthComponent } from 'neinth'; import { trySync } from 'vivth'; /** * @typedef {{appGoString:string, isFunc:boolean, funcName:string, argsName:Array<string>}} generatedAppPatialReturnType */ export class templateApp { static #packageName = 'wails-auto-ipc'; /** * @type {string} */ static #autoGeneratedNotice = '// auto generated, edit with `./go_` instead;'; /** * @type {string} */ static #invalidStartsWith = '-'; /** * @type {string} */ static #appGoSeparator = '/** generated IPC on this point onwards */'; static #appContextRegex = new RegExp(`appContext\\s+context\\s*\\.\\s*Context\\s*`, 'gm'); /** * @param {string} argumentsString * @returns {boolean} */ static #hasAppContext = (argumentsString) => { return templateApp.#appContextRegex.test(argumentsString); }; /** * @type {string} */ static #go_Path = 'go_'; /** * Validates that a .go file exports a top-level function * matching the provided base name (already stripped of extension). * @param {string} baseName - Filename without extension * @param {string} content - Raw content of the .go file * @returns {boolean} */ static isValidExported(baseName, content) { if (baseName.startsWith(templateApp.#invalidStartsWith)) { return false; } if (baseName[0] !== baseName[0].toUpperCase()) { return false; } const [result, error] = trySync(() => { const exportName = baseName[0].toUpperCase() + baseName.slice(1); const cleanedContent = content.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, ''); const exportRegex = new RegExp(`^\\s*(func|type)\\s+${exportName}\\b`, 'm'); return exportRegex.test(cleanedContent); }); if (error) { return false; } return result; } /** * @param {string} exportName * @param {string} content * @returns {generatedAppPatialReturnType|undefined} */ static #generateAppPartialType = (exportName, content) => { if (!content.match(new RegExp(`^\\s*type\\s+${exportName}`, 'gm'))) { return undefined; } return { appGoString: `type ${exportName} = ${templateApp.#go_Path}.${exportName}`, isFunc: false, argsName: [], funcName: '', }; }; /** * @param {string} baseName * @param {string} content * @returns {generatedAppPatialReturnType} */ static generateAppPartial(baseName, content) { const exportName = baseName[0].toUpperCase() + baseName.slice(1); const funcRegex = new RegExp( `^\\s*func\\s+${exportName}\\s*\\(([^)]*)\\)\\s*(?:\\(([^)]*)\\)|([a-zA-Z0-9_\\*]+))?`, 'm' ); const match = content.match(funcRegex); if (!match) { const type_ = templateApp.#generateAppPartialType(exportName, content); if (!type_) { throw new Error(`Function ${exportName} not found in content`); } return type_; } const [_, args_, returns1, returns2] = match; const hasAppContext = templateApp.#hasAppContext(args_); const args = args_.trim(); const returns = returns1 ? `(${returns1.trim()})` : returns2 ? returns2.trim() : ''; const argumentNamesArray = args ? args .split(',') .map((a) => a.trim().split(/\s+/)[0]) .filter((name) => name !== 'appContext') : []; const argNames = args ? argumentNamesArray.join(', ') : ''; const ipcArgs = hasAppContext ? args.replace(templateApp.#appContextRegex, '') : args; const passedArguments = hasAppContext ? `app.ctx, ${argNames.replace('appContext', '')}` : `${argNames}`; const returnLine = returns ? ` ${returns}` : ''; return { appGoString: `func (app *App) ${baseName}(${ipcArgs})${returnLine} {\n\treturn ${ templateApp.#go_Path }.${baseName}(${passedArguments})\n}` .replace(/ , /g, ' ') .replace(/\(\, /g, '(') .replace(/\(app.ctx, \)/g, '(app.ctx)'), funcName: baseName, isFunc: true, argsName: argumentNamesArray, }; } /** * @param {string[]} addedStrings * @returns {string} */ static generateAppGoFull = (addedStrings) => { const [result, error] = trySync(() => { const template = `${templateApp.#autoGeneratedNotice} package main import ( "context" "${templateApp.#packageName}/go_" ) // App struct type App struct { ctx context.Context } // NewApp creates a new App application struct func NewApp() *App { return &App{} } // startup is called when the app starts. The context is saved // so we can call the runtime methods func (a *App) startup(ctx context.Context) { a.ctx = ctx }`; const finalString = `${template}\n\n${templateApp.#appGoSeparator}\n\n${addedStrings.join( '\n\n' )}`; return finalString; }); if (error) { console.error({ error, message: 'failed to generate ./app.go' }); return; } return result; }; } /** * @type {NeinthComponent<typeof templateApp>} */ const neinthInstance = new NeinthComponent(async function () { return templateApp; }); export default neinthInstance;