Skip to main content

Creating a Decorator

As pointed out in the core concepts section for Decorators, they are used to gather additional custom data that you want to have available when inspecting a package.

Interface

At its core the Decorator must match an object with the following shape:

interface IDecorator<T> {
readonly key: string;
readonly name: string;
apply: (args: IApplyArgs) => Promise<T>;
}

key

A key to distinguish the Decorator, used to query the data that was collected

name

A human readable version of the key for the Decorator

Used by the logger to prefix messages.

apply method

The apply method is asynchronous, it should return a Promise with the custom data that should be stored.

It gets called with an argument with the following shape:

Argument

export interface IApplyArgs {
p: Package; //The current Package
logger: (msg: string) => void; //Method to log messages
}

Querying data

To get the data you use the getDecoratorData method on the Package class.

const data = pkg.getDecoratorData("decorator_key");

Note that getDecoratorData will throw if the key doesn't exist, as outlined here!

Example

For the sake of this tutorial we will simply calculate and store the depth of the package in the dependency tree.

class DependencyDepthDecorator {
key: "depthdecorator";
name: "Depth Decorator";

async apply({ p }) {
let current = p;
let depth = 0;

//walk up all the way to the root
while(current.parent) {
depth++;
current = current.parent;
}

//return the data you want to collect
return {
depth;
}
}
}

Querying the data

try {
const { depth } = p.getDecoratorData("depthdecorator"); //key needs to match

console.log(`Dependency ${p.fullName} was found at depth ${depth} in the dependency tree`);
//e.g "Dependency foo@1.2.3 was found at depth 3 in the dependency tree"
} catch {
//depth data couldn't be found
}