@sketch-hq/sketch-assistant-utils
Version:
Utility functions and types for Sketch Assistants.
305 lines (304 loc) • 12.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.process = exports.traverse = exports.addObjectToCache = exports.createEmptyObjectCache = void 0;
const sketch_assistant_types_1 = require("@sketch-hq/sketch-assistant-types");
const FOREIGN_OBJECT_CONTEXTS = [
'foreignLayerStyles',
'foreignSymbols',
'foreignTextStyles',
'foreignSwatches',
];
/**
* Create an empty ObjectCache object.
*/
const createEmptyObjectCache = () => (Object.assign(Object.assign({}, Object.values(sketch_assistant_types_1.FileFormat.ClassValue).reduce((acc, curr) => {
return Object.assign({ [curr]: [] }, acc);
}, {})), { anyGroup: [], anyLayer: [], document: [] }));
exports.createEmptyObjectCache = createEmptyObjectCache;
/**
* Add a file format object to an ObjectCache instance.
*/
const addObjectToCache = (object, cache) => {
// Use heuristics to determine whether an object is "groupy" or "layery"
// and add to the appropriate cache key if so
if ('layers' in object)
cache.anyGroup.push(object);
if ('frame' in object)
cache.anyLayer.push(object);
// For each class value available in the file format add the object to cache.
// This approach is long-winded but gives near 100% type safety
switch (object._class) {
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableColorAsset:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableFlowConnection:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableForeignLayerStyle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableForeignSwatch:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableForeignSymbol:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableForeignTextStyle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableFreeformGroupLayout:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableGradientAsset:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableHotspotLayer:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableInferredGroupLayout:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSImmutableOverrideProperty:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSJSONFileReference:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.MSJSONOriginalDataReference:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Artboard:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.AssetCollection:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.AttributedString:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Bitmap:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Blur:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Border:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.BorderOptions:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Color:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ColorControls:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.CurvePoint:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ExportFormat:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ExportOptions:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Fill:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.FontDescriptor:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.FontReference:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Gradient:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.GradientStop:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.GraphicsContextSettings:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Group:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ImageCollection:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.InnerShadow:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.LayoutGrid:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Oval:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.OverrideValue:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Page:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ParagraphStyle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Polygon:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Rect:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Rectangle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.RulerData:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Shadow:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ShapeGroup:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.ShapePath:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SharedStyle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SharedStyleContainer:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SharedTextStyleContainer:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SimpleGrid:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Slice:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Star:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.StringAttribute:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Style:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Swatch:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SwatchContainer:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SymbolContainer:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SymbolInstance:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.SymbolMaster:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Text:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.TextStyle:
cache[object._class].push(object);
break;
case sketch_assistant_types_1.FileFormat.ClassValue.Triangle:
cache[object._class].push(object);
break;
case 'document':
cache[object._class].push(object);
break;
}
};
exports.addObjectToCache = addObjectToCache;
/**
* Recursively traverse a Sketch file, while populating various caches and maps.
*/
const traverse = ({ target, cancelToken, pointers, objects, foreignObjects, objectIds, pointer = '', foreignContext = false, }) => {
// Bail early if we've been passed a falsey value or the operation is cancelled
if (!target || cancelToken.cancelled)
return;
// Bail early if input is not an object
if (typeof target !== 'object')
return;
// If target is an array then traverse into each of its elements
if (Array.isArray(target)) {
for (let index = 0; index < target.length; index++) {
exports.traverse({
target: target[index],
cancelToken,
pointers,
objects,
foreignObjects,
objectIds,
pointer: `${pointer}/${index}`,
foreignContext,
});
}
return;
}
// If this is a Sketch file object with a `_class` then add it to the pointer map
if ('_class' in target) {
pointers.set(target, pointer);
exports.addObjectToCache(target, foreignContext ? foreignObjects : objects);
}
// If this is an object with an id then add the id to the id set
if ('do_objectID' in target) {
objectIds.add(target.do_objectID);
}
// Loop over its properties
for (const key in target) {
exports.traverse({
target: target[key],
cancelToken,
pointers,
objects,
foreignObjects,
objectIds,
pointer: `${pointer}/${key}`,
foreignContext: foreignContext || FOREIGN_OBJECT_CONTEXTS.includes(key),
});
}
};
exports.traverse = traverse;
/**
* Generate a ProcessedSketchFile object from a SketchFile object.
*/
const process = (file, cancelToken) => new Promise((resolve, reject) => {
try {
const objects = exports.createEmptyObjectCache();
const foreignObjects = exports.createEmptyObjectCache();
const pointers = new Map();
const start = Date.now();
const objectIds = new Set();
exports.traverse({
target: file.contents,
cancelToken,
pointers,
objects,
foreignObjects,
objectIds,
});
resolve({
original: file,
objects,
foreignObjects,
pointers,
objectIds,
profile: { numObjects: pointers.size, time: Date.now() - start },
});
}
catch (error) {
reject(error);
}
});
exports.process = process;