A fast browserify integration for Karma that handles large projects with ease
npx @tessl/cli install tessl/npm-karma-browserify@8.1.0karma-browserify is a fast browserify integration plugin for Karma that handles large projects with ease. It provides a preprocessor that combines test files and dependencies into browserified bundles, utilizing watchify for incremental bundling during development. The plugin supports comprehensive browserify configuration options including transforms, plugins, external dependencies, and custom bundle configuration.
npm install --save-dev karma-browserify browserify watchifyThe package integrates with Karma through plugin registration:
// karma.conf.js
module.exports = function(config) {
config.set({
frameworks: ['browserify', 'jasmine'],
preprocessors: {
'test/**/*.js': ['browserify']
}
});
};// karma.conf.js
module.exports = function(config) {
config.set({
frameworks: ['browserify', 'jasmine'],
files: ['test/**/*.js'],
preprocessors: {
'test/**/*.js': ['browserify']
},
browserify: {
debug: true,
transform: ['brfs']
}
});
};karma-browserify is built around several key components:
Registers browserify framework with Karma to enable browserify bundling in test suites.
// Automatically registered when included in frameworks array
frameworks: ['browserify', 'jasmine']Preprocesses individual CommonJS test files for browserify bundling.
// Configure in karma.conf.js
preprocessors: {
'test/**/*.js': ['browserify']
}Handles the main browserify bundle creation and management.
// Internal preprocessor for *.browserify.js files
// Automatically configured by the pluginComplete browserify configuration support through the browserify config object.
interface BrowserifyConfig {
/** Enable source maps for debugging */
debug?: boolean;
/** Array of browserify transforms */
transform?: (string | [string, any])[];
/** Array of browserify plugins */
plugin?: (string | [string, any])[];
/** Custom bundle configuration function */
configure?: (bundle: BrowserifyBundle) => void;
/** Debounce delay for bundling operations (default: 700ms) */
bundleDelay?: number;
/** Custom require name for external requires (default: 'require') */
externalRequireName?: string;
/** File extensions to process */
extensions?: string[];
/** Base directory for relative paths */
basedir?: string;
/** Additional browserify options */
[key: string]: any;
}Usage Example:
browserify: {
debug: true,
transform: ['reactify', 'coffeeify', 'brfs'],
extensions: ['.js', '.jsx', '.coffee'],
configure: function(bundle) {
bundle.on('prebundle', function() {
bundle.external('foobar');
});
}
}Configuration for watchify (used during autoWatch mode).
interface WatchifyConfig {
/** Enable polling for file changes */
poll?: boolean;
/** Polling interval in milliseconds */
pollInterval?: number;
/** Additional watchify options */
[key: string]: any;
}Usage Example:
watchify: {
poll: true,
pollInterval: 1000
}Support for browserify transforms with options.
// Simple transform array
transform: ['reactify', 'coffeeify', 'brfs']
// Transform with options
transform: [
['reactify', { 'es6': true }],
'coffeeify',
'brfs'
]Support for browserify plugins with options.
// Simple plugin array
plugin: ['stringify']
// Plugin with options
plugin: [
['stringify', { extensions: ['.txt', '.html'] }]
]Advanced bundle configuration through the configure function.
configure: function(bundle) {
// Configure externals
bundle.on('prebundle', function() {
bundle.external('lodash');
bundle.external('jquery');
});
// Add transforms programmatically
bundle.once('prebundle', function() {
bundle.transform('babelify').plugin('proxyquireify/plugin');
});
}Events emitted by the browserify bundle instance.
// Emitted before bundling operation
bundle.on('prebundle', function() {
// Configure externals, transforms, etc.
});
// Emitted after bundling completion
bundle.on('bundled', function(err, content) {
// Handle bundle completion
});
// Watchify events (during autoWatch)
bundle.on('update', function(updatedFiles) {
// Handle file updates
});
bundle.on('log', function(message) {
// Handle log messages
});// Main plugin export object
{
'bro': ['type', Bro],
'framework:browserify': ['factory', framework],
'preprocessor:browserify': ['factory', testFilePreprocessor],
'preprocessor:browserify-bundle': ['factory', bundlePreprocessor],
'preprocess': ['factory', createPreprocessor] // Optional
}/**
* Main browserify integration class
* @param {BundleFile} [bundleFile] - Optional bundle file instance
*/
function Bro(bundleFile) {
/**
* Framework factory function with dependency injection
* @param {Object} emitter - Event emitter instance
* @param {Object} config - Karma configuration object
* @param {Object} logger - Logger instance
* @returns {BrowserifyBundle} Extended browserify bundle instance
*/
this.framework = function(emitter, config, logger) {
// Returns browserify instance with additional methods
};
this.framework.$inject = ['emitter', 'config', 'logger'];
/**
* Test file preprocessor factory
* @returns {Function} Preprocessor function (content, file, done) => void
*/
this.testFilePreprocessor = function() {
// Returns preprocessor function that processes individual test files
};
this.testFilePreprocessor.$inject = [];
/**
* Bundle preprocessor factory with dependency injection
* @param {Object} config - Configuration object containing browserify options
* @returns {Function} Bundle preprocessor function (content, file, done) => void
*/
this.bundlePreprocessor = function(config) {
// Returns bundle preprocessor function
};
this.bundlePreprocessor.$inject = ['config'];
}
// Constructor dependency injection
Bro.$inject = [];/**
* Manages temporary bundle files
*/
function BundleFile() {
/** File path of the bundle */
this.location = string;
/** Creates empty bundle file if it doesn't exist */
this.touch = function() {};
/** Writes content to bundle file */
this.update = function(content) {};
/** Removes bundle file from filesystem */
this.remove = function() {};
}
/**
* Generates temporary filename with suffix
* @param {string} suffix - File suffix
* @returns {string} Temporary file path
*/
BundleFile.getTempFileName = function(suffix) {};// Methods added to browserify instance
interface ExtendedBrowserifyBundle {
/**
* Bundle individual file and return stub
* @param {Object} file - File object with path property
* @param {Function} done - Callback function(err, stub)
*/
bundleFile(file, done): void;
/**
* Wait for bundle creation to stabilize and invoke callback
* @param {Function} [callback] - Optional callback(err, content)
*/
deferredBundle(callback?): void;
}The plugin handles various error conditions:
prebundle is used (replaced by configure)watchify is configured in browserify options// Bundle error template (replaces bundle content on error)
'throw new Error("bundle error (see logs)");'browserify: {
transform: ['coffeeify'],
extensions: ['.js', '.coffee']
}browserify: {
transform: [['reactify', { 'es6': true }]],
extensions: ['.js', '.jsx']
}browserify: {
configure: function(bundle) {
bundle.on('prebundle', function() {
bundle.external('lodash');
bundle.external('jquery');
});
}
}browserify: {
debug: true
}browserify: {
bundleDelay: 1000 // Wait 1 second before bundling (default: 700ms)
}/**
* Default time to wait for additional file change notifications
* before performing a rebundling operation
*/
const DEFAULT_BUNDLE_DELAY = 700;
/**
* Error template used when bundle creation fails
*/
const BUNDLE_ERROR_TPL = 'throw new Error("bundle error (see logs)");';