Appearance
Hooks
Execute code at different stages of the build process.
Available Hooks
| Hook | When | Use Case |
|---|---|---|
onGenerateRuntime | Before runtime generation | Chrome flags, early initialization |
onPreBuild | Before build starts | Validation, setup, notifications |
onModifyAssets | After assets copied | Transform files, inject code |
onBeforePackage | Before Electron packaging | Final modifications |
onPostBuild | After build complete | Cleanup, notifications, upload |
onBuildError | On build failure | Error reporting |
onBuildProgress | During build | Progress tracking |
Hook Signatures
onGenerateRuntime
Inject code that runs before app.ready() in the built game. This is the only way to set Chrome command line flags or perform early initialization.
javascript
onGenerateRuntime(context, settings) {
// context.gamePath - Source game folder
// context.config - Game configuration
// settings - Plugin settings values
// Return JavaScript code as a string
// This code will run in the Electron main process before app.ready()
return `_a.commandLine.appendSwitch('disable-frame-rate-limit');`;
}Important
The returned code runs in the main process runtime. Use _a to reference the Electron app module (already imported). Do not use require('electron') as it's already loaded.
onPreBuild
javascript
async onPreBuild(context, settings) {
// context.gamePath - Source game folder
// context.config - Game configuration
// settings - Plugin settings values
}onModifyAssets
javascript
async onModifyAssets(context, settings) {
// context.gamePath - Source game folder
// context.outputPath - Build output folder
// context.config - Game configuration
// context.platform - 'darwin', 'win32', 'linux'
// context.arch - 'arm64', 'x64', 'ia32'
// settings - Plugin settings values
}onBeforePackage
javascript
async onBeforePackage(context, settings) {
// Same as onModifyAssets
// Called just before Electron packaging
}onPostBuild
javascript
async onPostBuild(context, settings) {
// context.gamePath - Source game folder
// context.outputPath - Final build folder
// context.config - Game configuration
// context.platform - Target platform
// context.arch - Target architecture
// settings - Plugin settings values
}onBuildError
javascript
async onBuildError(context, settings) {
// context.gamePath - Source game folder
// context.config - Game configuration
// context.error - Error message
// settings - Plugin settings values
}onBuildProgress
javascript
onBuildProgress(context, settings) {
// context.gamePath - Source game folder
// context.config - Game configuration
// context.percent - Progress 0-100
// context.message - Status message
// settings - Plugin settings values
}Example: Complete Plugin
javascript
module.exports = {
name: 'Build Pipeline',
version: '1.0.0',
settings: [
{ id: 'validateAssets', type: 'checkbox', label: 'Validate assets', default: true },
{ id: 'webhookUrl', type: 'text', label: 'Webhook URL' }
],
async onPreBuild(context, settings) {
gemshell.log('Starting build...');
if (settings.validateAssets) {
// Validate required files exist
const required = ['index.html'];
for (const file of required) {
if (!gemshell.fs.exists(file)) {
throw new Error(`Missing required file: ${file}`);
}
}
gemshell.log('Asset validation passed');
}
},
async onModifyAssets(context, settings) {
gemshell.log('Processing assets...');
// Add build timestamp
const timestamp = new Date().toISOString();
gemshell.transform.injectIntoHtml(
`<meta name="build-time" content="${timestamp}">`,
'head'
);
},
async onBeforePackage(context, settings) {
gemshell.log('Preparing for packaging...');
// Remove development files
const devFiles = ['*.map', '*.ts', 'tsconfig.json'];
for (const pattern of devFiles) {
const files = gemshell.glob(pattern);
for (const file of files) {
gemshell.fs.remove(file);
}
}
},
async onPostBuild(context, settings) {
gemshell.log('Build complete!');
// Send webhook notification
if (settings.webhookUrl) {
await gemshell.http.post(settings.webhookUrl, {
event: 'build_complete',
game: context.config.title,
version: context.config.version,
platform: `${context.platform}-${context.arch}`,
time: new Date().toISOString()
});
}
},
async onBuildError(context, settings) {
gemshell.error('Build failed:', context.error);
if (settings.webhookUrl) {
await gemshell.http.post(settings.webhookUrl, {
event: 'build_failed',
game: context.config.title,
error: context.error,
time: new Date().toISOString()
});
}
},
onBuildProgress(context, settings) {
// Called frequently, keep lightweight
if (context.percent % 25 === 0) {
gemshell.log(`Progress: ${context.percent}%`);
}
}
};Error Handling
Throw errors to abort the build:
javascript
async onPreBuild(context, settings) {
if (!gemshell.fs.exists('index.html')) {
throw new Error('index.html is required');
}
}The error message will be shown to the user and onBuildError will be called.
Async vs Sync
All hooks can be async:
javascript
async onPostBuild(context, settings) {
// Async operations work
await gemshell.http.post(...);
await someAsyncFunction();
}Exception: onBuildProgress should be synchronous for performance.
