@sketch-hq/sketch-assistant-utils
Version:
Utility functions and types for Sketch Assistants.
138 lines (137 loc) • 5.54 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runMultipleAssistants = exports.createRunRejection = exports.isRunRejection = void 0;
const assistant_1 = require("../../assistant");
const run_assistant_1 = require("../run-assistant");
const ignore_1 = require("../ignore");
const assistant_config_1 = require("../../assistant-config");
function isRunRejection(val) {
if (!val)
return false;
if (typeof val !== 'object')
return false;
if (!('code' in val))
return false;
return true;
}
exports.isRunRejection = isRunRejection;
/**
* Create a RunRejection object.
*/
const createRunRejection = (message, code = 'runError', error) => {
return {
message: `Run failed: ${error instanceof Error ? `${message} ${error.message}` : message}`,
code,
};
};
exports.createRunRejection = createRunRejection;
/**
* Throw with a "cancelled" rejection if the current operation signals a cancellation from outside.
*/
const exitIfCancelled = (cancelToken) => {
if (cancelToken.cancelled) {
throw exports.createRunRejection('Run cancelled by external signal', 'cancelled');
}
};
/**
* Return the set of active rules for an Assistant. Active rules are 1) activated in the config,
* 2) supported by the current platform and 3) not full ignored.
*/
const getActiveRules = (assistant, env, ignore) => assistant.rules
.filter((rule) => assistant_config_1.isRuleActive(assistant.config, rule.name)) // Rule turned on in config
.filter((rule) => (rule.runtime ? rule.runtime === env.runtime : true)) // Rule platform is supported
.filter((rule) => !ignore_1.isRuleFullIgnored(ignore, assistant.name, rule.name));
/**
* Wrapped by the exported function so even uncaught errors are consistently returned
* as RunRejections.
*/
const innerRunMultipleAssistants = (input) => __awaiter(void 0, void 0, void 0, function* () {
const { cancelToken, env, processedFile, assistants, getImageMetadata, timeBudgets } = input;
let { ignore } = input;
if (Object.keys(assistants).length === 0) {
throw exports.createRunRejection('No Assistants found to run');
}
ignore = ignore_1.pruneAssistants(ignore, assistants);
ignore = ignore_1.pruneObjects(ignore, processedFile);
const results = {};
const definitions = [];
// Prepare assistants, that is, resolve the Assistant package exports into
// assistant functions ready for running.
for (const [assistantName, assistant] of Object.entries(assistants)) {
try {
const def = yield assistant_1.prepare(assistant, env);
if (def.name !== assistantName) {
throw Error(`Assistant name "${def.name}" does not match it's package name "${assistantName}"`);
}
definitions.push(def);
}
catch (error) {
results[assistantName] = {
code: 'error',
error: {
message: `Assistant preparation failed: ${error.message}`,
},
};
}
}
exitIfCancelled(cancelToken);
// Caculate the rule timeout
const totalRules = definitions.reduce((acc, assistant) => acc + getActiveRules(assistant, env, ignore).length, 0);
const ruleTimeout = Math.min(Math.max(Math.trunc(timeBudgets.totalMs / totalRules), timeBudgets.minRuleTimeoutMs), timeBudgets.maxRuleTimeoutMs);
// For each definition prune any ignored rules, no longer present in the
// Assistant
for (const assistant of definitions) {
ignore = ignore_1.pruneRules(ignore, assistant);
}
// Run assistants against the file.
for (const assistant of definitions) {
if (cancelToken.cancelled)
break;
try {
results[assistant.name] = {
code: 'success',
result: yield run_assistant_1.runAssistant(processedFile, assistant, env, cancelToken, getImageMetadata, ignore, ruleTimeout),
};
}
catch (error) {
results[assistant.name] = {
code: 'error',
error: {
message: `Assistant run failed: ${error.message}`,
},
};
}
}
exitIfCancelled(cancelToken);
return {
assistants: results,
ignore,
input,
};
});
/**
* Run multiple assistants.
*/
const runMultipleAssistants = (input) => __awaiter(void 0, void 0, void 0, function* () {
try {
return yield innerRunMultipleAssistants(input);
}
catch (error) {
if (isRunRejection(error)) {
throw error;
}
else {
throw exports.createRunRejection('Unhandled error', 'runError', error);
}
}
});
exports.runMultipleAssistants = runMultipleAssistants;