šŸš€ RunJS 1.3.2

If you need to return the result of a function assign it to a variable:

ret = function() {
 let c = 1
 return a+b+c
}()
1 Like
const vm = require('vm')
exports.run = async function (options) {
    const code = "ret = function() { " + options.code + " }()";
    ...
}

:upside_down_face:

Maybe one more option to select user preference? ā€œRun within functionā€

WOW! this is awesomeā€¦ thanks for doing this JonL

1 Like

Sticking to the standard means less maintenance and problems if node guys decide to introduce changes to vm.

Additionally I need to understand better how promise handling works in VM context so I have to test that before prepending a variable to a function.

Is it unbearable for your eyes having to add a variable and assign it to the function result? I understand itā€™s kind of unsettling not being able to use return in the main code. I believe this is by design to secure the sandbox.

I am more concerned about the lack of a proper control in the UI that handles code although this is expected as nobody has asked for itā€¦until now :slight_smile:

@george do you guys have by any chance an undocumented control for code with linting and beautifier?

1 Like

Not a problem!

Thereā€™s a bug with the Output checkbox: it comes ticked by default, but it doesnā€™t really output anything. If I untick and tick it again it works

Edit: As a new feature, is it possible to define the schema of the output? Like what happens with API Action or Set Value (this last oneā€™s broken due to a bug)

Regarding the output toggling this should be ā€œfixedā€ with the last version 1.1.0
I changed it to default to false.

Related to:

For custom extensions the output schema is defined in a json file and itā€™s static but itā€™s true that it is available for the API action. @george is this undocumented or not available for custom modules?

Update 1.2.0

Breaking Change

  • Due to wrapping the custom code in an IIFE you need to explicitly return something so it shows in the output i.e. return 'hello world'

Added

  • IIFE wrapper. Wrapped the custom code in an IIFE to improve performance of vm.
  • Forward console. You can now use console object functions in your code i.e. console.log() and the output will be forwarded to the server console.
  • UI changes

Fixed

  • Fixed initial values of several checkboxes in the UI
1 Like

@JonL Iā€™m struggling with getting it to work, Iā€™m not sure if I am using the extension wrongly or if my code is wrong.
I do think this is the exact use case for your extension though, need to apply a little bit of JS to manipulate dataā€¦

  1. This is my data, from a db query:
 "query_products": [
    {
      "id": 13,
      "ac_tags": "tag1, tag2"

    },
    {
      "id": 14,
      "ac_tags": "tag3"
    }
  ],
  1. I want to loop over the data and for each product: split the ac_tags and then push the values into one array. Expected outcome:
    acTagsArr = ["tag1, tag2, tag3"];

  2. Iā€™m trying to get this done with the following JS:

acTags = []; //declare my output var
tagsSplit = tags.split(','); //Split the 'ac_tags' so I get an array where 1 tag = 1 item
acTags.push(...tagsSplit); //push the split items into the output var

Iā€™m expecting an output of: acTagsArr = ["tag1", "tag2", "tag3"] (still need to remove the comma and make it a single string.)

  1. These are the settings of the plugin

  2. This is the location (in a repeat)

  3. This is my current output:

"repeat_push_to_array": [
    {
    },
    {
    }
  ]

What version of RunJS are you running?

This code should work with the latest 1.2.0:

return tags.split(",")

Also note you have a space after each comma (double-check if this is the case), so this is the code:

return tags.split(", ")

For previous versions of RunJS, omit the return keyword:

tags.split(",");

The method .split() already returns an Array, so you can return it directly without creating acTags2 :slight_smile:

Edit: Actually, your problem is youā€™re not actually returning acTags

acTags = []; //declare my output var
tagsSplit = tags.split(','); //Split the 'ac_tags' so I get an array where 1 tag = 1 item
acTags.push(...tagsSplit); //push the split items into the output var

return acTags; // RunJS 1.2.0
acTags; // Previous RunJS versions
1 Like

Thanks @Apple appreciate the help!

I got a bit further, I didnā€™t realise we need to use return now.

With this code:

return tags.split(",").map(item => item.trim());

I get:

 "repeatACtags": [
    {
      "ac_tags": [
        "tag1"
      ]
    },
    {
      "ac_tags": [
        "tag2",
        "tag3"
      ]
    }
  ]

so now I want to make it one array, which Iā€™ve done before in a similar way:

This is outputting nothing. Iā€™m guessing because Iā€™m not passing the acTagsArr to the RunJS. But if I do that I think my variable will stay within this scope of this RunJS.

Any idea?

Your array declaration (#1) doesnā€™t matter to RunJS (even if you pass it in Data), the scope is isolated as you guessed (pass by value, not by reference)

Similarly, the step #3 to #1 doesnā€™t happen - the value returned by RunJS is not magically filled into acTagsArr (RunJS has no reference to it). You need to put a Set Value acTagsArr after the RunJS step, and you select in the Data Binding Picker the value RunJS returns

Okay I think you mean similar to this?
Current set up:

Output:
The acTagsArr that I set with ā€˜SetValueā€™ is getting the values pushed as expected.

 "acTagsArr": [
    "tag1",
    "tag2",
    "tag3"
  ]

Then I just need the last step: .join(',')
So I put it in the repeat now, because it should join the array after the last repeat.

Outcome:

 "repeatACtags": [
    {
      "ac_tags": [
        "BOUGHT | NTSL Ebook"
      ],
      "pushToArray": 1,
      "joinArray": "tag1"
    },
    {
      "ac_tags": [
        "these are tags",
        "here"
      ],
      "pushToArray": 3,
      "joinArray": "tag1,tag2,tag3"
    }
  ]

But then Iā€™m not sure how I can get that final joined array.
Trying like this:

But this is not giving me any output.
EDIT: Which makes sense to me now because itā€™s an array, so I need to select the last item somehow. Iā€™ll try this out now

Please note the variable scope, thereā€™s no guarantee youā€™ll be able to access the variable joinArray outside the Repeat step. You need to define a global name for that (name ā€œjoinArrayā€, global name ā€œjoinArrayGlobalā€
And then on finalArray you get {{joinArrayGlobal}} - you wonā€™t see this in the Data Binding Picker

Happy to help :slight_smile:

Oh wow that works perfectly. First time Iā€™m using the Global Name propertyā€¦

Thanks @Apple I appreciate it a lot!!

Iā€™m still wondering if I can replace my custom formatter with this extension?
Iā€™m using a pushSpread formatter which is just:

exports.pushSpread = function (arr, obj) {
    return arr.push(...obj);
}

image

return acTagsArr.push(...ac_tags)

One liner:

return [tags.map((e) => e.ac_tags).join(', ')]

Should return a single array of tags comma separated.

But acTagsArr is defined as a ā€˜set valueā€™ image . So with the custom formatter I can push things into this array. But with the runjs Iā€™m not sure how to push into this array

Today I learned :point_up:

But with the runjs Iā€™m not sure how to push into this array

Yeahā€¦ Probably would have to convert acTagsArr to a global variable, and then Set Value on it with the output of RunJS.

Here comes the king of one-liners, Mr. Jon Liner :merman:

1 Like

I love one liners. Itā€™s like completing the newspaper puzzle :joy: itā€™s a hobby

Hmm nice ideaā€¦ Need to move on now though so wonā€™t try it, but thanks for all the input. Learning a lot :slight_smile:

@JonL Very curious how youā€™d approach thisā€¦ Your one liner looks good but leaves me with 2 issues still:

  1. the join is not foolproof. Right now the tags can be separated with or without trailing spaces depending on user input. (Iā€™ll sanitize that input better in the future but Iā€™d like server side code that can handle exceptions)

  2. The global variable issue. See my earlier post: šŸš€ RunJS 1.2.0
    Any alternative idea on how to iterate over each object, grab the values of a certain key and add them to a global array?