Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
bryanberger opened this issue Oct 31, 2017 · 18 comments

Comments

Projects
None yet
5 participants
@bryanberger
Copy link

commented Oct 31, 2017

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:

exports[key](context);

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?

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Oct 31, 2017

hum yeah I need to clean that up. I'm not sure I tested npm start with named exports, might be the culprit

@mathieudutour mathieudutour added the bug label Oct 31, 2017

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 9, 2017

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)

@richardpenner

This comment has been minimized.

Copy link

commented Nov 10, 2017

I'm seeing this same error. I have a working plugin but am trying to adopt this toolchain. I've removed almost everything but can't get around this.
Manifest.json:

{
  "compatibleVersion": 3,
  "bundleVersion": 1,
  "commands": [
    {
      "name": "Settings 4",
      "identifier" : "show-settings",
      "script": "./dialog.cocoascript"
    }
  ],
  "menu": {
    "title": "Figure-1-Sketch",
    "items": [
      "show-settings"
    ]
  }
}

dialog.cocoascript:

export default function(context){
    log("hi mom!");
}

Any ideas?

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 10, 2017

use .js and not .cocoascript.

@richardpenner

This comment has been minimized.

Copy link

commented Nov 10, 2017

OK, just one more thought: skpm is promoted on developer.sketchapp.com, where cocoascript examples abound. You may want to make it clearer that cocoascript is not supported – in my case it would've saved a lot of time.

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 10, 2017

yes I need to change the examples

@mateus

This comment has been minimized.

Copy link

commented Nov 28, 2017

I'm having the same issue. So what's the proper way to have a script with handlers?

// plugin.js
export default function (context) {
  ...
}

export function onSelectionChanged(context) {
  ...
}
// manifest.json
...

"script" : "plugin.js",
"handlers": {
  "actions" : {
      "SelectionChanged.finish" : "onSelectionChanged"
    }
}

This code is certainly not right.

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 28, 2017

yes you need to define the handler:

"handlers": {
  "run": "default",
  "actions" : {
      "SelectionChanged.finish" : "onSelectionChanged"
    }
}

but honestly, I would use another file. If you use the same file, you might end up being confused about the context it's being run into, etc..

@mateus

This comment has been minimized.

Copy link

commented Nov 28, 2017

This also does not seam to work:

// manifest.js

    "script" : "plugin.js",
      "handlers": {
        "run": "default",
        "actions": {
          "SelectionChanged.finish" : "onSelectionChanged"
        }
      }
// plugin.js

import { createWebview } from './helpers';

export default function (context) {
  createWebview(context, {}, 'Title');
}

export function onSelectionChanged(context) {
  // Do Something
}

I should be doing something incorrect here. What would be the right way to do it using another file as you suggested?

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 28, 2017

"commands": [
    {
      "name": "onSelection",
      "identifier": "my-command-identifier.selection",
      "handlers": {
        "actions": {
          "SelectionChanged.finish" : "onSelectionChanged"
        }
      },
      "script": "./on-selection.js"
    },
    {
      "name": "my-command",
      "identifier": "my-command-identifier.normal-command",
      "script": "./plugin.js"
    }
  ],
@mateus

This comment has been minimized.

Copy link

commented Nov 28, 2017

Using these commands who should the on-selection.js look like then?

Also, by not including the key run inside handlers I get this error:

Error in command onSelection Script at path project/plugin.sketchplugin/Contents/Sketch/on-selection.js does not contain a handler function named: run.
@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 28, 2017

your on-selection.js should look like this:

export function onSelectionChanged(context) {
  // Do Something
}
@mateus

This comment has been minimized.

Copy link

commented Nov 29, 2017

Yeah. My configuration looks exactly what you are suggesting but I still can't get it to work.

"commands": [
    {
      "name": "onSelection",
      "identifier": "my-command-identifier.selection",
      "handlers": {
        "actions": {
          "SelectionChanged.finish" : "onSelectionChanged"
        }
      },
      "script": "./selection-changed.js"
    },
    {
      "name": "search",
      "identifier": "my-command-identifier.normal-command",
      "script": "./plugin.js"
    }
  ],
// plugin.js
import createWebview from './utils/webview';

export default function(context) {
  createWebview(context, {}, 'Title');
}
// selection-changed.js

export function onSelectionChanged(context) {
  context.document.showMessage("It's changed");
}

I'm still getting that error about not having run.

Error in command onSelection Script at path /Users/mateus/Documents/project/plugin.sketchplugin/Contents/Sketch/selection-changed.js does not contain a handler function named: run
@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 29, 2017

when does the error happen? when you select an layer?

@mateus

This comment has been minimized.

Copy link

commented Nov 29, 2017

I see it on the sketch-dev-tools logs and also on the terminal after run npm run start.

@mathieudutour

This comment has been minimized.

Copy link
Member

commented Nov 29, 2017

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

@sten-anderssen

This comment has been minimized.

Copy link

commented Jan 7, 2018

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.

@mateus

This comment has been minimized.

Copy link

commented Jan 11, 2018

Yeah, took me some time to figure this out. Sorry I didn't mention it here. Thanks @kohaXun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.