impro
Version:
Image processing engine
165 lines (142 loc) • 4.71 kB
JavaScript
const Pipeline = require('./Pipeline');
function pickProperties(obj, properties) {
if (!obj) return {};
const ret = {};
for (const property of properties) {
ret[property] = obj[property];
}
return ret;
}
module.exports = class Impro {
constructor(options) {
if (!options || typeof options === 'object') {
options = Object.assign({}, options);
} else {
throw new Error('invalid options');
}
this.options = options;
this.engineByName = {};
this.engineNamesByOperationName = {};
this.isSupportedByEngineNameAndInputType = {};
this.isSupportedByEngineNameAndOutputType = {};
this.isTypeByName = {};
this.supportedOptions = [
'allowOperation',
'maxInputPixels',
'maxOutputPixels',
'sharpCache',
'svgAssetPath',
'sharpFailOnError',
];
this.restrictedOptions = ['svgAssetPath'];
this._Pipeline = class extends Pipeline {};
[
'type',
'source',
'maxInputPixels',
'maxOutputPixels',
].forEach((propertyName) => this.registerMethod(propertyName));
}
createPipeline(options, operations) {
if (typeof options === 'string' || Array.isArray(options)) {
operations = options;
options = undefined;
}
const pipeline = new this._Pipeline(this, {
...options,
engines: pickProperties(options, Object.keys(this.engineByName)), // Allow disabling via createPipeline({<engineName>: false})
supportedOptions: this.supportedOptions,
});
if (operations) {
if (!Array.isArray(operations)) {
throw new Error(
'Pipeline creation can only be supplied an operations array'
);
}
operations.forEach((operation) => pipeline.add(operation));
}
return pipeline;
}
registerMethod(operationName) {
if (!this._Pipeline.prototype[operationName]) {
const _impro = this;
this._Pipeline.prototype[operationName] = function (...args) {
if (
!(
_impro.engineByName[operationName] ||
_impro.isValidOperation(operationName, args)
)
) {
throw new Error(
`invalid operation or arguments: ${operationName}=${JSON.stringify(
args
)}`
);
}
return this.add({ name: operationName, args });
};
}
this[operationName] = (...args) =>
this.createPipeline()[operationName](...args);
return this;
}
add(operation) {
return this.createPipeline().add(operation);
}
use(options) {
const engineName = options.name;
if (typeof options.unavailable === 'undefined') {
options.unavailable = true;
}
if (typeof options.validateOperation === 'undefined') {
// Will allow all options.operations that don't take any arguments:
options.validateOperation = () => {};
}
this.engineByName[options.name] = options;
this.registerMethod(engineName);
[engineName].concat(options.operations || []).forEach((operationName) => {
(this.engineNamesByOperationName[operationName] =
this.engineNamesByOperationName[operationName] || []).push(engineName);
this.registerMethod(operationName);
});
this.isSupportedByEngineNameAndInputType[engineName] = {};
(options.inputTypes || []).forEach((type) => {
this.isTypeByName[type] = true;
this.isSupportedByEngineNameAndInputType[engineName][type] = true;
});
this.isSupportedByEngineNameAndOutputType[engineName] = {};
(options.outputTypes || []).forEach((type) => {
this.registerMethod(type);
this.isTypeByName[type] = true;
(this.engineNamesByOperationName[type] =
this.engineNamesByOperationName[type] || []).push(engineName);
this.isSupportedByEngineNameAndOutputType[engineName][type] = true;
});
return this;
}
getEngine(engineName) {
let engine;
engineName = typeof engineName === 'string' ? engineName : '';
if (engineName && (engine = this.engineByName[engineName])) {
return engine;
} else {
throw new Error(`unknown engine ${engineName || 'unknown'}`);
}
}
isOperationSupportedByEngine(name, engineName) {
return (this.engineNamesByOperationName[name] || []).includes(engineName);
}
isValidOperation(name, args) {
const engineNames = this.engineNamesByOperationName[name];
return (
engineNames &&
engineNames.some(function (engineName) {
const isValid = this.engineByName[engineName].validateOperation(
name,
args
);
return isValid || (typeof isValid === 'undefined' && args.length === 0);
}, this)
);
}
};