JavaScript build tool, similar to Make or Rake
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Direct access to Jake's task class hierarchy for programmatic task creation and advanced build system development. These classes provide the foundation for all task types in Jake.
The base Task class that all Jake tasks inherit from. Extends EventEmitter for lifecycle events.
/**
* Base Task class for all Jake tasks
*/
class Task extends EventEmitter {
constructor(name, prereqs, action, opts);
// Core properties
name: string; // Task name
prereqs: string[]; // Prerequisites to run before this task
action: Function; // Task action function
async: boolean; // Whether task runs asynchronously
taskStatus: string; // Current status (UNSTARTED/STARTED/DONE/ERROR)
description: string; // Task description for help
args: any[]; // Arguments passed to task
value: any; // Return value from task action
concurrency: number; // Concurrency level for prerequisites
startTime: number; // Task start timestamp
endTime: number; // Task completion timestamp
directory: string; // Directory to change to before running
namespace: Namespace; // Parent namespace
// Computed properties
get fullName(): string; // Full namespaced task name
get params(): string; // Formatted parameter list for display
// Core methods
invoke(...args: any[]): void; // Run prereqs then this task
execute(...args: any[]): void; // Run only this task (skip prereqs)
reenable(deep?: boolean): void; // Reset task to allow re-running
isNeeded(): boolean; // Check if task needs to run
complete(value?: any): void; // Mark task as complete
// Static properties
static runStatuses: {
UNSTARTED: string;
STARTED: string;
DONE: string;
ERROR: string;
};
// Static methods
static getBaseNamespacePath(fullName: string): string; // Extract namespace path
static getBaseTaskName(fullName: string): string; // Extract task name
}Task Events:
interface TaskEvents {
'start': () => void; // Task started running
'complete': (value: any) => void; // Task completed successfully
'skip': () => void; // Task was skipped
'error': (err: Error) => void; // Task failed with error
}Usage Examples:
// Create task programmatically
const myTask = new jake.Task('compile', ['clean'], function () {
console.log('Compiling...');
jake.exec(['tsc'], { printStdout: true });
});
// Add description and add to namespace
myTask.description = 'Compile TypeScript files';
jake.currentNamespace.addTask(myTask);
// Monitor task execution
myTask.on('start', function () {
console.log('Task started:', this.name);
});
myTask.on('complete', function (value) {
console.log('Task completed:', this.name, 'Result:', value);
});
myTask.on('error', function (err) {
console.error('Task failed:', this.name, err.message);
});
// Execute task
myTask.invoke();
// Re-enable task for multiple runs
myTask.reenable();
myTask.execute(); // Run again without prereqsFile-based tasks that automatically check modification times to determine if execution is needed.
/**
* FileTask class for file-based tasks with modification time checking
*/
class FileTask extends Task {
constructor(name, prereqs, action, opts);
// Additional properties
dummy: boolean; // Whether this is a dummy file task
modTime: Date; // File modification time
// Overridden methods
isNeeded(): boolean; // Check if file needs rebuilding
updateModTime(): void; // Update modification time from filesystem
complete(value?: any): void; // Complete and update mod time
}Usage Examples:
// Create file task programmatically
const jsTask = new jake.FileTask('dist/app.js', ['src/app.js', 'src/utils.js'], function () {
console.log('Building JavaScript bundle...');
jake.exec(['webpack src/app.js dist/app.js'], function () {
complete();
});
}, { async: true });
jake.currentNamespace.addTask(jsTask);
// Check if file needs updating
if (jsTask.isNeeded()) {
console.log('File is out of date, will rebuild');
} else {
console.log('File is up to date');
}
// Monitor file modification time
console.log('Current mod time:', jsTask.modTime);
jsTask.updateModTime();
console.log('Updated mod time:', jsTask.modTime);
// Create dummy file task for existing files
const configTask = new jake.FileTask('config.json');
configTask.dummy = true;
configTask.updateModTime();Directory creation tasks that ensure directories exist for build outputs.
/**
* DirectoryTask class for ensuring directories exist
*/
class DirectoryTask extends FileTask {
constructor(name, prereqs, action, opts);
// Inherits all FileTask properties and methods
// Default action creates the directory using jake.mkdirP
}Usage Examples:
// Create directory task programmatically
const distDir = new jake.DirectoryTask('dist');
jake.currentNamespace.addTask(distDir);
// Create nested directory task
const assetsDirs = new jake.DirectoryTask('dist/assets/images');
jake.currentNamespace.addTask(assetsDirs);
// Directory task with custom action
const buildDir = new jake.DirectoryTask('build', [], function () {
jake.mkdirP(this.name);
console.log('Created build directory with custom setup');
// Additional setup for build directory
jake.cpR('templates', 'build/templates');
});
// Use directory tasks as prerequisites
const bundleTask = new jake.FileTask('dist/bundle.js', ['dist', 'src/**/*.js'], function () {
// dist directory is guaranteed to exist
jake.exec(['browserify src/main.js -o dist/bundle.js']);
});Specialized task for creating distributable packages in various archive formats.
/**
* PackageTask class for creating distributable packages
*/
class PackageTask {
constructor(name, version, prereqs, definition);
// Core properties
name: string; // Project name
version: string; // Project version
prereqs: string[]; // Prerequisites to run before packaging
packageDir: string; // Directory for package output (default: 'pkg')
packageFiles: FileList; // Files to include in package
// Archive format flags
needTar: boolean; // Create .tgz archive
needTarGz: boolean; // Create .tar.gz archive
needTarBz2: boolean; // Create .tar.bz2 archive
needJar: boolean; // Create .jar archive
needZip: boolean; // Create .zip archive
// Archive configuration
manifestFile: string; // JAR manifest file path
tarCommand: string; // Tar command (default: 'tar')
jarCommand: string; // Jar command (default: 'jar')
zipCommand: string; // Zip command (default: 'zip')
archiveNoBaseDir: boolean; // Archive contents without base directory
archiveChangeDir: string; // Directory to change to before archiving
archiveContentDir: string; // Specific content directory to archive
// Methods
packageName(): string; // Get package name (name-version)
packageDirPath(): string; // Get full path to package directory
}Usage Examples:
// Create package task programmatically
const pkg = new jake.PackageTask('myapp', '2.1.0', ['build', 'test'], function () {
this.packageFiles.include([
'dist/**',
'lib/**',
'package.json',
'README.md',
'LICENSE'
]);
this.packageFiles.exclude([
'dist/**/*.map',
'lib/**/*.test.js'
]);
this.needTarGz = true;
this.needZip = true;
this.packageDir = 'releases';
});
// Access package properties
console.log('Package name:', pkg.packageName()); // 'myapp-2.1.0'
console.log('Package directory:', pkg.packageDirPath()); // './releases'
// Package creates these tasks automatically:
// - package: Build complete package
// - repackage: Rebuild package
// - clobberPackage: Remove package directorySpecialized task for version management and package publishing workflows.
/**
* PublishTask class for version management and publishing
*/
class PublishTask {
constructor(name, prereqs, opts, definition);
// Core properties
name: string; // Project name
prereqs: string[]; // Prerequisites to run before publishing
packageFiles: FileList; // Files to include in package
publishCmd: string; // Publish command template
publishMessage: string; // Success message
gitCmd: string; // Git command
versionFiles: string[]; // Files to update with new version
scheduleDelay: number; // Delay before publish command
// Methods
createPublishCommand?(version: string): string[]; // Custom publish command generator
}Usage Examples:
// Create publish task programmatically
const pub = new jake.PublishTask('myapp', ['build', 'test'], {
publishCmd: 'npm publish --registry=https://registry.npmjs.org %filename',
publishMessage: 'Successfully published to npm!',
versionFiles: ['package.json', 'bower.json']
}, function () {
this.packageFiles.include([
'dist/**',
'lib/**',
'package.json',
'README.md'
]);
// Custom publish command
this.createPublishCommand = function (version) {
return [
'npm publish --tag=latest',
'git tag v' + version,
'git push origin v' + version
];
};
});
// Publish creates these tasks automatically:
// - publish: Create new version and release
// - publishExisting: Release existing version
// - version: Update version files and push to gitSpecialized task for running test suites with setup/teardown support.
/**
* TestTask class for running test suites
*/
class TestTask {
constructor(name, prereqs, definition);
// Properties
testName: string; // Name of test namespace and task (default: 'test')
testFiles: FileList; // List of test files to load
showDescription: boolean; // Show task in `jake -T` (default: true)
totalTests: number; // Total number of tests to run
executedTests: number; // Number of successfully executed tests
}Usage Examples:
// Create test task programmatically
const test = new jake.TestTask('myproject', ['build'], function () {
this.testName = 'spec';
this.testFiles.include([
'test/unit/**/*.js',
'test/integration/**/*.js'
]);
this.testFiles.exclude('test/**/*.helper.js');
this.showDescription = true;
});
// Access test statistics
console.log('Total tests:', test.totalTests);
console.log('Executed tests:', test.executedTests);
// Test creates these tasks automatically:
// - spec (or testName): Main test task
// - spec:run: Internal test execution task// Create custom task type by extending Task
class CompileTask extends jake.Task {
constructor(source, target, compiler, opts) {
const name = target;
const prereqs = [source];
const action = function () {
const cmd = `${compiler} ${source} -o ${target}`;
jake.exec([cmd], { printStdout: true });
};
super(name, prereqs, action, opts);
this.source = source;
this.target = target;
this.compiler = compiler;
}
isNeeded() {
// Custom logic for determining if compilation is needed
const fs = require('fs');
if (!fs.existsSync(this.target)) {
return true;
}
const sourceStat = fs.statSync(this.source);
const targetStat = fs.statSync(this.target);
return sourceStat.mtime > targetStat.mtime;
}
}
// Use custom task type
const tsTask = new CompileTask('src/app.ts', 'dist/app.js', 'tsc');
jake.currentNamespace.addTask(tsTask);// Compose complex tasks from simpler ones
class BuildPipeline {
constructor(name, stages) {
this.name = name;
this.stages = stages;
this.tasks = [];
this.createTasks();
}
createTasks() {
const stageNames = [];
this.stages.forEach((stage, index) => {
const stageName = `${this.name}:${stage.name}`;
const prereqs = index > 0 ? [stageNames[index - 1]] : stage.prereqs || [];
const task = new jake.Task(stageName, prereqs, stage.action, stage.opts);
task.description = stage.description;
jake.currentNamespace.addTask(task);
this.tasks.push(task);
stageNames.push(stageName);
});
// Create main task that runs all stages
const mainTask = new jake.Task(this.name, [stageNames[stageNames.length - 1]], function () {
console.log(`${this.name} pipeline completed successfully`);
});
jake.currentNamespace.addTask(mainTask);
this.tasks.push(mainTask);
}
}
// Use build pipeline
const pipeline = new BuildPipeline('deploy', [
{
name: 'test',
description: 'Run all tests',
action: function () {
jake.exec(['npm test'], { printStdout: true });
}
},
{
name: 'build',
description: 'Build application',
action: function () {
jake.exec(['npm run build'], { printStdout: true });
}
},
{
name: 'package',
description: 'Create deployment package',
action: function () {
jake.exec(['tar -czf deploy.tar.gz dist/'], { printStdout: true });
}
},
{
name: 'upload',
description: 'Upload to server',
action: function () {
jake.exec(['scp deploy.tar.gz server:/tmp/'], { printStdout: true });
},
opts: { async: true }
}
]);// Task performance monitoring
class TaskMonitor {
constructor() {
this.metrics = {};
}
attachTo(task) {
task.on('start', () => {
this.metrics[task.name] = {
startTime: Date.now(),
name: task.name
};
});
task.on('complete', () => {
const metric = this.metrics[task.name];
if (metric) {
metric.endTime = Date.now();
metric.duration = metric.endTime - metric.startTime;
metric.status = 'completed';
}
});
task.on('error', (err) => {
const metric = this.metrics[task.name];
if (metric) {
metric.endTime = Date.now();
metric.duration = metric.endTime - metric.startTime;
metric.status = 'failed';
metric.error = err.message;
}
});
}
getReport() {
return Object.values(this.metrics).map(metric => ({
task: metric.name,
duration: metric.duration + 'ms',
status: metric.status,
error: metric.error
}));
}
}
// Use task monitor
const monitor = new TaskMonitor();
// Attach to all tasks
jake.parseAllTasks();
for (const taskName in jake.Task) {
monitor.attachTo(jake.Task[taskName]);
}
// Add reporting task
task('report', function () {
console.table(monitor.getReport());
});