skpm: TypeError: exports[key] is not a function, when using handler actions

manifest.json

{
  "compatibleVersion": 3,
  "bundleVersion": 1,
  "commands": [
    {
      "name": "my-command",
      "identifier": "my-command-identifier",
      "script": "./my-command.js",
      "handlers": {
          "actions": {
              "ArtboardChanged.finish": "ArtboardChanged"
	  }
       }
    }
  ],
  "menu": {
    "isRoot": true,
    "items": [
      "my-command-identifier"
    ]
  }
}

my-command.js

export default function(context) {
  context.document.showMessage("It's alive 🙌")
}

export function ArtboardChanged(context) {
  console.log("ArtboardChanged")
}

error

TypeError: exports[key] is not a function. (In 'exports[key](context)', 'exports[key]' is undefined)
line: 487
stack: run
column: 17

Webpacked output is showing the functions properly:

that['ArtboardChanged'] = run.bind(this, 'ArtboardChanged');
that['onRun'] = run.bind(this, 'default')

Seems to be related to: https://github.com/skpm/skpm/blob/8731f82c2f2b748b79f5fc8e4440499d57b297c3/skpm-builder/src/utils/webpackConfig.js#L16

When key isn’t a function, but is instead this object:

{
    api = "<MOJavaScriptObject: 0x604000a37b60>";
    command = "<MSPluginCommand: 0x600000eee280>";
    document = "<MSDocument: 0x7fdb5b9063f0>";
    plugin = "<MSPluginBundle: 0x6000000fd000>";
    scriptPath = "/Users/bb/dev/test-plugin/plugin.sketchplugin/Contents/Sketch/my-command.js";
    scriptURL = "file:///Users/bb/dev/test-plugin/plugin.sketchplugin/Contents/Sketch/my-command.js";
    selection =     (
    );
}

Could replace L16 with an additional function type check

if (key === 'default' && typeof exports === 'function') {
    exports(context);
} else if(typeof exports[key] === 'function') {
    exports[key](context);
}

The plugin works fine, but the error is produced after running npm start and after each save (on watch) Any idea about that object being passed in?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 18 (9 by maintainers)

Most upvoted comments

For all of you who wonder why the provided code does not work its because context does not have a property called “document” in an action context. You have to get it like this:

var document = context.actionContext.document;

Your welcome.

npm run start will try to run each command after each build and the selection command can’t run by itself.

Use npm run watch to just build the command

use .js and not .cocoascript.

oh waow, that’s actually super interesting, and a bug in Sketch:

  • when you don’t specify “handlers”, the default handler is onRun
  • when you specify “handlers” (like here), the default handler become run

Turns out I’m defining a function called run so it gets called and crashed because an argument is missing.

(btw, I don’t think you want to use npm run start when using actions, it will run your handler on every same (hence the error). Use npm run build instead)