Execute Node.js script

Is there a way to execute Node.js script?

I have found “Execute a JavaScript For Automation” and “Execute Shell script” but I haven’t found a way to require node modules in “JavaScript For Automation” (ReferenceError: Can’t find variable: require) or run node from “Execute Shell script” (line 1: node: command not found) - I have node installed

Thanks for help

I haven’t tried this much from a JavaScript for Applications action (except for simple use of js-beautify, see below), but you should get a few pointers from this page:

In particular:

A simple approach for basic cases is to use a shell script action, or from inside a JXA action, use .doShellScript()

For example, calling npm js-beautify here:

// Assumes installation of node.js 
// http://nodejs.org/download/
// with npm at:
var pathNPM = '/usr/local/bin';

// ...
	oDoc.text = app.doShellScript(
		'export PATH=$PATH:' + pathNPM +
		'; pbpaste | js-beautify -f - -t -j --good-stuff -p -n -w 80'
	);

I hope you figure this out. I tried to do the same a few months ago, but I just couldn't follow the instructions in the JXA-Cookbook that @ComplexPoint provided.

If you do get it working, would you please post a complete working example here?
It would be a tremendous help.

Thanks.

Has anyone ever figured this out? I’m trying to run a node command without opening a terminal window

Your ENV_PATH Maestro var is probably not set.

Reference this page: https://wiki.keyboardmaestro.com/action/Execute_a_Shell_Script#Path_in_Shell_Scripts

Specifically the section Setting the ENV_PATH Keyboard Maestro Variable

( and you need, of course, to check where you really have npm and the node.js modules installed, for any path settings to work )

Oh this is interesting. I had the same question myself, about using node in a plain jxa script that, because I couldn’t figure it out, I solved by upgrading Keyboard Maestro and creating a macro.

How ironic.

Yet, if anyone has the answer for how to do this generally, I’d love to hear it too.

1 Like

FWIW you can set the path in the bash script. See this article ... https://stackoverflow.com/questions/41358018/how-to-set-path-only-in-bash-script-temporarily

For me this means a bash script of:
export PATH="$PATH:/usr/local/bin"
nodeApp $KMVAR_VariableName

Thanks for sharing, I'm still interesting in this problem.

Can you explain your example a little bit more please? I'm not sure how adjusting PATH should help with running node scripts – I already have node available in my $PATH

I'm far from an expert on this but my understanding is that bash scripts run in KM don't use the path set in .bashrc or .bash_config.

Therefore you need to set that path - either locally in the KM macro (my suggestion) or globally using ENV_PATH variable

Not in the vanilla shell used by KM – only the Terminal.app command line shell.

Any environment variables needed by your script in the fresh shell launched by KM will need to be explicitly set, as @modusop suggests.

OK, I've managed to "glue" together something that works, but I'm not definitely proud of that and hope there's some better solution:

Recipe to Slugify content of clipboard

Keyboard Maestro ♥️ Node.js will become keyboard-maestro-love-node.js

Keyboard%20Maestro%20Editor%20%E2%80%94%20Slugify%20clipboard%202020-01-31%2013-17-20

Please please tell me there's a better way :smiley:

You can also run JS scripts as regular shell scripts which is what I've been doing recently, this has the advantage that you can use regular node tooling (sane IDEs, autocomplete, restart script on save) which makes doing development much much easier. Then you just need to pipe the input from KM. Easy peasy lemon squeezy

slugify.js

#!/usr/bin/env node
const getStdin = require('get-stdin')
const slugify = require('slugify')

;(async () => {
  const input = await getStdin()
  const slugifiedInput = slugify(input)
  console.log(slugifiedInput)
})()

example repo here https://github.com/ninjakttty/km_node_script_example

@ninjakttty very uber cool :pray:

But how did you solve not having node available as mentioned before?

To make your script work on my machine, I had to adjust first line to not so ubercool #!/usr/bin/env /Users/strajk/.nvm/versions/node/v10.17.0/bin/node

And thanks for teaching me about #!...

I just tested I forgot that I had the ENV_PATH set https://wiki.keyboardmaestro.com/action/Execute_a_Shell_Script#Path_in_Shell_Scripts

I also use nvm so I have this set in my zshrc right above where nvm is loaded export NVM_SYMLINK_CURRENT=true which makes a symlink to nvm's current version, e.g. the path $NVM_DIR/current/bin is then always pointing to the current node version for me, I did have to expand it out so my ENV_PATH looks like

/usr/bin:/bin:/usr/sbin:/sbin:/Users/yuri/.nvm/current/bin

I've got node 13.8.0 on my machine and you can pass flags in as well to env
top-level await is a new JS thing that's coming soon but it's currently hidden behind a feature flag but you can get rid of the IIFE by using the mjs extension and passing it in --harmony-top-level-await.
--no-warnings is needed because node throws a warning saying your using an experimental feature.

slugify.mjs

#!/usr/bin/env node --harmony-top-level-await --no-warnings

import getStdin from 'get-stdin'
import slugify from 'slugify'

const input = await getStdin()
const slugifiedInput = slugify(input)
console.log(slugifiedInput)
1 Like

Yep, this was the critical part – it works like a charm!
Thx so much, learned a lot :slight_smile: @ninjakttty (btw epic username :D)

1 Like

Hi @ninjakttty,

Thanks for the veiled connection between "best slug" and "UCSC". I presume you are an alumnus, right?

image

1 Like

You got me dead to rights, the best school mascot ever!

1 Like

2 Likes