Skip to content

Hooks

Execute code at different stages of the build process.

Available Hooks

HookWhenUse Case
onPreBuildBefore build startsValidation, setup, notifications
onModifyAssetsAfter assets copiedTransform files, inject code
onBeforePackageBefore Electron packagingFinal modifications
onPostBuildAfter build completeCleanup, notifications, upload
onBuildErrorOn build failureError reporting
onBuildProgressDuring buildProgress tracking

Hook Signatures

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.