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.