sketch-hq/SketchAPI

Allow programmatically supplying name to dom.export in JS API

cmcculloh-kr opened this issue · 1 comments

TL;DR: Allow user to provide name for file asset is to be exported to. Perhaps something like fileName:

        dom.export(artboard, {
            output: path,
            fileName: artboard.name,
            formats: format.fileFormat,
            scales: format.size,
            overwriting: true,
        });

Note: The above assumes that the fileFormat suffix will NOT be appended to the name. So a fileName: "bob" exported as PNG would end up being called bob. The suffix/prefix in sketch should likewise NOT be included. So, if the user specifies that the exported file should have a @2x suffix through sketch's interface, the final filename should still be bob. This will afford developers full control over their exported filenames.

Expanded explanation

Currently export does not (appear to) allow you to supply a name for the exported asset. Exports are named after a combination of their layerName + their export size (but completely ignoring the user's settings for suffix/prefix). So, a layer named bob set to export as PNG at 2x and a prefix of what-about- would result in a file named bob@2x.png instead of the (to me, expected) what-about-bob.png (note that the later would match Sketch's inbuilt export behavior, whereas the former does not, which makes things inconsistent for the user).

In order to work around this I have to export the asset, reconstruct the name Sketch gave it (for instance, it tacks @2x on to the artboard name regardless of what I have set in sketch, which I take to be a bug), and then move the asset from that location to the location I want it to be at (in order to rename it to what I would like it to be named).

Here's the code for that:

const fm = NSFileManager.defaultManager();
const dom = require('sketch/dom');
const doc = dom.getSelectedDocument();
const absPath = (path) => NSString.stringWithString(path).stringByExpandingTildeInPath();
const combinePath = (a, b) => NSString.stringWithString(a).stringByAppendingPathComponent(b);
const endsWith = (str, suffix) => str.indexOf(suffix, str.length - suffix.length) !== -1;
const outputPath = absPath('~/test');
const tempPath = combinePath(outputPath, '/~~~');


function makeExportName(artboard, format) {
    return [].concat(
        format.prefix ? format.prefix : [],
        artboard.name,
        format.suffix ? format.suffix : [],
        '.',
        format.fileFormat,
    ).join('')
}


const getArtboardsFromLayers = (layers) =>
    layers.flatMap((layer) => {
        switch (layer.type) {
            case 'Page':
            case 'Group':
            case 'Symbol':
                if (layer.layers) {
                    return getArtboardsFromLayers(layer.layers);
                }
                break;
            case 'Artboard':
                return [ layer ];
                break;
            default:
                return [];
        }
    });

const layers = doc.getLayersNamed('main');
const artboards = getArtboardsFromLayers(layers);
artboards.forEach((artboard) => {
    artboard.exportFormats.forEach((format) => {
        dom.export(artboard, {
            output: tempPath,
            formats: format.fileFormat,
            scales: format.size,
            overwriting: true,
        });
        const errorPtr = MOPointer.alloc().init();
        const files = fm.contentsOfDirectoryAtPath_error_(tempPath, errorPtr);
        console.log('exported');
        if (files && files.count()) {
            const tempFilePath = combinePath(tempPath, files.firstObject());

            // assets
            let outputFilePath = combinePath(outputPath, makeExportName(artboard, format));
            console.log('here: ', outputFilePath)
            if (endsWith(outputFilePath, 'null.png')) {
                outputFilePath = outputFilePath.replace('null.png', '.png');
            }
            if (outputFilePath.startsWith('null')) {
                outputFilePath = outputFilePath.replace('null', '');
            }
            outputFilePath = outputFilePath.replace('null', '');

            fm.removeItemAtPath_error_(outputFilePath, null);
            fm.moveItemAtPath_toPath_error_(tempFilePath, outputFilePath, null)
        }
    });
});

If this proposed change were implemented, I could instead just do:

const fm = NSFileManager.defaultManager();
const dom = require('sketch/dom');
const doc = dom.getSelectedDocument();
const absPath = (path) => NSString.stringWithString(path).stringByExpandingTildeInPath();
const combinePath = (a, b) => NSString.stringWithString(a).stringByAppendingPathComponent(b);
const endsWith = (str, suffix) => str.indexOf(suffix, str.length - suffix.length) !== -1;
const outputPath = absPath('~/test');
const tempPath = combinePath(outputPath, '/~~~');

const getArtboardsFromLayers = (layers) =>
    layers.flatMap((layer) => {
        switch (layer.type) {
            case 'Page':
            case 'Group':
            case 'Symbol':
                if (layer.layers) {
                    return getArtboardsFromLayers(layer.layers);
                }
                break;
            case 'Artboard':
                return [ layer ];
                break;
            default:
                return [];
        }
    });

const layers = doc.getLayersNamed('main');
const artboards = getArtboardsFromLayers(layers);
artboards.forEach((artboard) => {
    artboard.exportFormats.forEach((format) => {
        dom.export(artboard, {
            output: tempPath,
            fileName: artboard.name,
            formats: format.fileFormat,
            scales: format.size,
            overwriting: true,
        });
    });
});