Javascript Function Question

Now I am reaching out on here because I know there are some experienced javascript people.

One aspect which always gets me is “Re-Useable” Functions.
I can see the use for them but just never been able to get my head around them.

Today, I want to see if I can overcome my fear :slight_smile:

Here is a function I have put together which is basically checking to see if the active layer name includes “Paint here”.

Question:
How can I use this as a re-useable function, call it and use the result from anywhere.

async function isPaintLayer() {
  
  let layername = app.activeDocument.layers[0].name;
  if (layername.includes("Paint here")) {
  
}

It depeneds on structure you’re using, but basically, if you have this function set globally, you can call it from anywhere:

const isPaint = await isPaintLayer()

Although I’m not sure why you have async there - if it’s not intended, then await is also unnecessary. Also probably return value should be a boolean - return layername.includes("Paint here")


Simplest example would be, if you had some helper functions file

// utils.js

export const isPaintLayer = () => {
  let layername = app.activeDocument.layers[0].name;
  
  return layername.includes("Paint here")
}

Then in some other file

import {isPaintLayer} from "utils.js"

funtion someFunc() {
  // some logic

  if (isPaintLayer()) {
    // do stuff
  }
}

I’m pretty sure someone will give a more advanced examples of this :slight_smile:

Thanks, I will study that and see if I can use it.

You can do this in quite a few ways – depending on the framework(s) you’re using, the idioms may be a bit different. If you’re using webpack or parcel at all, then @Karmalakas 's example is a great one.

For completeness’ sake, you can also use the CommonJS2 method of defining and using modules. (But if you use a bundler, it’s more efficient to use import and export, and the bundler can do more optimization on the size of the package if you do so.)

In this example, you use require to load a JS file, and inside the file, you use module.exports to export the functions inside. (One big difference here is if you try to use Node modules: UXP doesn’t do Node-style module resolution where Node will search the node_modules folder and its hierarchy for you. If you want to use Node modules, a bundler is almost always a prerequisite.)

So, you can have a file named ./lib/commonUtils.js (or whatever name and path make sense here):

const { app } = require("photoshop"); // this `app` is local to the module
                                      // other modules can't use it just
                                      // because you add it here.

/* This is a private function we won't export in this example,
   but you could export it if you wanted. */
const layerNameIncludes = (layer, searchTerm) => layer.name.includes(searchTerm);

/* These will be publicly exported */
const isPaintLayer = layer => layerNameIncludes(layer, "Paint Here");

const topLayer = () => app.activeDocument.layers[0];

module.exports = {
    topLayer,     /* this makes "topLayer" usable by other modules */
    isPaintLayer  /* ditto */
}

/* Note: the export is just an object. You could also export constants,
   enumerations, and other data as well. */

Then in some file in your code base, you can reuse them as follows:

const { topLayer, isPaintLayer } = require("./lib/commonUtils.js");

/* somewhere later in the code */
if (isPaintLayer(topLayer()) {
    /* your logic here */
}

The biggest thing here is that you have to make sure that the path to your other JS file is correct, and (I don’t know about others) this is sometimes the most difficult thing to keep straight in my brain, especially if a file is far away in another part of a large code base. VSCode et al help manage this to some degree, but you’ll typically run into an error at runtime when you get this wrong. (This is different when using a bundler: the bundler will fail to compile your package at build time if your path is incorrect when using import and export.)

The above example assumes that your main file is in the plugin bundle’s root directory, and that the commonUtils.js file is in a folder called lib.

Important: while require does indeed use paths, these are relative to both the file you’re using require in, and to the plugin bundle itself. This means that you can’t include a file from outside of your plugin’s code bundle. That is, you can’t reach out into the user’s file system to load in other code files.

You’ll often also see our code use require("photoshop") where there’s no relative or absolute path in use. These are specially provided by the runtime environment and do not represent real files in your plugin’s bundle. A common error when using require is forgetting to use a relative (./some/path) or absolute path (/some/path)… at which point the JS runtime will complain that it couldn’t find a module matching the name you provided. Only these injected modules (like photoshop or uxp) can be used without a relative or absolute path specified.

2 Likes

Thanks @kerrishotts for a great detailed explanation and this along with what @Karmalakas put is a great help

@kerrishotts I’ve been unable to make this work. Two files in the same folder: index.js and other.js. In other.js

const function testMultipleFiles() {
	const app = require('photoshop').app;
	await app.showAlert("Multiple files worked.");
}

module.exports = { testMultipleFiles }

and in index.js

const { testMultipleFiles } = require('./other.js');

The plugin doesn’t even run, so none of index.js is compiling.