Plugin issue in Athens

My plugin runs with the below error with 「Athens」2025.1 version:
artboard.sketchObject.children is not a function. (In ‘artboard.sketchObject.children()’, ‘artboard.sketchObject.children’ is undefined);

When my plugin executed the below code:

originArtboards.forEach(artboard => {
const cnt = artboard.sketchObject.children().count()
})

originArtboards.forEach(artboard => {
const children = artboard.sketchObject.children();
const layerEnumerator = children.objectEnumerator();
const layersCount = children.count();
})

The above code could run well in the previous all versions, so how to rewrite the code to compatible with the 「Athens」version

1 Like

Hey there,

While it’s not clear how children and layerEnumerator are used later in the script, one way to get rid of them is to use sketch.find JS API:

const sketch = require('sketch')

// ...

originArtboards.forEach(artboard => {
    // These children are normal JS objects,
    // so you can forEach/filter/map/etc them directly
    const children = sketch.find("*", artboard);
    const chldrenCount = children.length;
});

I noticed that the official plugin development API documentation lacks comprehensiveness. For example, APIs such as artboard.sketchObject.children or artboard.sketchObject.children().objectEnumerator() used in my plugin code are not explained in the official documentation. Could you please provide me with the latest and most detailed plugin development API documentation? Thank you very much.

So as soon as you see sketchObject in your code it means that everything that follows (children() and objectEnumerator() calls in this case) is a private API.

These private methods and functions are not documented by design – since they exist for Sketch itself to use, not plugin developers. Their usage in plugins, while sometimes justified and unavoidable even, usually predates the official JavaScript API – so there’s a good chance you may replace them with stable and more ergonomic modern counterparts.

For instance, objectEnumerator() is nothing more than an awkward way to iterate the children array – which is not needed when working with normal JS API arrays and objects.

And the undocumented children() call itself could be replaced by sketch.find() one I’ve mentioned above that does exactly the same.


I also agree that documentation should probably cover not only what’s available in the JS API, but also help with migration from outdated private APIs to modern public ones. Until it does, I’m here to answer any of your further questions!

My code:

context.page = context.document.selectedPage;
context.artboard = sketch.Artboard.fromNative(context.page.sketchObject.currentArtboard());

Code error:

context.page.sketchObject.currentArtboard is not a function。

How to find a replacement method?

I’m not familiar with the currentArtboard() method – does it return the first artboard that contains any of the currently selected layers?

I think you may want to do it like this:

let page = sketch.getSelectedDocument().selectedPage
let selection = page.selectedLayers

let artboardsWithSelection = selection.reduce((prev, layer) => {
  if (layer.type == sketch.Types.Artboard) {
    return prev.concat(layer);
  }
  return prev.concat(layer.getParentArtboard() ?? [])
}, [])

API Docs:

thank ! I have other questions These methods errors

let hasFixedLeft = layer?.sketchObject?.hasFixedLeft() || false;
let hasFixedRight = layer?.sketchObject?.hasFixedRight() || false;
let hasFixedTop = layer?.sketchObject?.hasFixedTop() || false;
let hasFixedBottom = layer?.sketchObject?.hasFixedBottom() || false;
let hasFixedWidth = layer?.sketchObject?.hasFixedWidth() || false;
let hasFixedHeight = layer?.sketchObject?.hasFixedHeight() || false;
let canFixLeft = layer?.sketchObject?.canFixLeft() || false;
let canFixRight = layer?.sketchObject?.canFixRight() || false;
let canFixTop = layer?.sketchObject?.canFixTop() || false;
let canFixBottom = layer?.sketchObject?.canFixBottom() || false;
let canFixWidth = layer?.sketchObject?.canFixWidth() || false;
let canFixHeight = layer?.sketchObject?.canFixHeight() || false;
export function exportImage(layer: Layer, format: SMExportFormat, path: string, name: string) {
    let document = context.sketchObject.document;
    let slice = MSExportRequest.exportRequestsFromExportableLayer(layer.sketchObject).firstObject();
    let savePath = [
        path,
        "/",
        format.prefix,
        name,
        format.suffix,
        ".",
        format.format
    ].join("");

    slice.scale = format.scale;
    slice.format = format.format;


    document.saveArtboardOrSlice_toFile(slice, savePath);
    return savePath;
}
  1. As for hasFixed*() methods, there’re JS APIs available to inspect layer’s sizing and pinning details:

    let hasFixedWidth = (layer.horizontalSizing === 0);
    let hasFixedHeight = (layer.verticalSizing === 0);
    

    Now determining whether a certain layer side is pinned is a bit more involved, but still very much possible:

    let hasFixedLeft = (
      // Here we have to check if *only* the left side is pinned ...
      layer.horizontalPins === 1
      ||
      // ... or both left *and* right sides are pinned
      layer.horizontalPins == 5
    );
    // Same for other side as well
    let hasFixedRight = (layer.horizontalPins === 4 || layer.horizontalPins == 5);
    // And for the vertical axis
    let hasFixedTop = (layer.verticalPins === 1 || layer.verticalPins == 5);
    let hasFixedBottom = (layer.verticalPins === 4 || layer.verticalPins == 5);
    

    This API is certainly very awkward to use, but you can write a simple wrapper function around them for convenience.

    FYI: Pins are only active on layers that either

    • a) belong to a Frame, or
    • b) don’t belong to a Frame nor Graphic, but to a regular non-top-level Group (ie it’s not a page-level one).

    You can check the type of a specific Group object via its groupBehavior property:

    if (layer.parent?.groupBehavior === 2) {
        // The parent is a <Graphic> group, which does NOT allow layer pinning.
        // In order words, layerHasFixedEdge() will always return `false` for any sublayer of this group
        const pinsAllowed = false;
    }
    
  2. All of the canFix*() checks are redundant in Athens as they’d always evaluate to true (unless you’re in a Graphic group, in which case the result will be false – again, see above).

  3. I mean your exportImage() function might as well be rewritten to use the JS API:

    function exportImage(layer, format, path, name) {
      let filename = [
        format.prefix, name, format.suffix,
        ".", format.format
      ].join("");
    
      sketch.export(layer, {
        output: path,
        formats: [format.format],
        filename: filename,
        scales: [format.scale]
      });
    
      return [path, filename].join("/")
    }