wails-auto-ipc
Version:
generate single app.go for wails app from multiple files
173 lines (164 loc) • 5.04 kB
JavaScript
// @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;