npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@figliolia/decorators

v2.0.2

Published

Decorators for everyday programming

Downloads

279

Readme

Decorators

Decorators for everyday programming.

In this library you'll find a collection of useful shorthands for boosting productivity through the stage 3 Decorators API.

Installation

npm i @figliolia/decorators
# or 
yarn add @figliolia/decorators

API

  1. Bound
  2. Cache
  3. Debounce
  4. Throttle
  5. Animation Frame
  6. Log Browser
  7. Log Server
  8. Measure Browser
  9. Measure Server
  10. Unsafe Chainable

Bound

Implicitely bind class methods to their instances

import { bound } from "@figliolia/decorators";

class MyClass {
  private someField = "value";

  @bound
  public someMethod() {
    return this.someField;
  }
}

const { someMethod } = new MyClass();
// Works
someMethod();

Cache

Cache previous calls to a given method

import { cache } from "@figliolia/decorators";

class MyClass {
  @cache
  public expensive(x: number, y: number) {
    // Compute expensive value
  }
}

const instance = new MyClass();
// computes on first execution
instance.expensive(1, 2);
// reads from cache then onwards as long as arguments match
instance.expensive(1, 2);

Debounce

Debounces calls to a given method for a pre-determined duration

import { debounce } from "@figliolia/decorators";

class MyClass {
  @debounce(300)
  public getData(query: string) {
    void fetch(`/api/data?query=${query}`);
  }
}

const instance = new MyClass();
// Invokes 300ms following the last call
void instance.getData("searching for something");

Throttle

Throttles calls to a given method for a pre-determined duration

import { throttle } from "@figliolia/decorators";

class MyClass {
  @throttle(300)
  public getData(query: string) {
    void fetch(`/api/data?query=${query}`);
  }
}

const instance = new MyClass();
// Invokes immediately, then disables later calls for 300ms
void instance.getData("searching for something");

Animation Frame

Invokes the target method using calls to requestAnimationFrame

import { animationFrame } from "@figliolia/decorators";

class MyClass {
  node: HTMLElement;
  constructor(ID: string) {
    this.node = document.getElementById(ID);
  }

  @animationFrame
  public animate() {
    const { translate } = this.node.style;
    const current = parseInt(translate.slice(0, -2));
    if(current === 100) {
      return;
    }
    this.node.style.translate = `${current + 1}px`;
    this.animate()
  }
}

const instance = new MyClass("elementID");
// animates an element's translateX property
instance.animate();

Log Browser

Logs invokation arguments and return values for a given method as long as the NODE_ENV is not production

This method will use native colorized logging (in chrome and firefox)

import { logBrowser } from "@figliolia/decorators";

class MyClass {
  @logBrowser
  public async getData(query: string) {
    return fetch(`/api/data?query=${query}`);
  }
}

const instance = new MyClass();
void instance.getData("searching for something");

Log Server

Logs invokation arguments and return values for a given method as long as the NODE_ENV is not production

This method will use Chalk for colorized logging

import { logServer } from "@figliolia/decorators";

class MyClass {
  @logServer
  public async getData(query: string) {
    return fetch(`/api/data?query=${query}`);
  }
}

const instance = new MyClass();
void instance.getData("searching for something");

Measure Browser

Logs the duration occupied by the target method at runtime

import { measureBrowser } from "@figliolia/decorators";

class MyClass {
  @measureBrowser
  public expensive() {
    for(let i = 0; i < 1_000_000; i++) {

    }
  }
}

const instance = new MyClass();
instance.expensive();

Measure Server

Logs the duration occupied by the target method at runtime

import { measureServer } from "@figliolia/decorators";

class MyClass {
  @measureServer
  public expensive() {
    for(let i = 0; i < 1_000_000; i++) {

    }
  }
}

const instance = new MyClass();
instance.expensive();

Unsafe Chainable

Overrides the return type of a given method, replacing it with the class instance

import { chainable } from "@figliolia/decorators";

class MyClass {
  @chainable
  public async method1() {
    
  }
  @chainable
  public async method2() {
    
  }
}

const instance = new MyClass().method1().method(2);
// instance = MyClass

Stage 3 Decorator Proposal Vs. Experimental Decorators

If you're a typescript developer, the Experimental decorators API (found under the experimentalDecorators flag) has likely become familiar to you in recent years. While it's not far from the JavaScript Stage 3 Proposal, there are significant syntactical mismatches between each decorators implementation that make interoperability impossible.

Therefore, this library will not use TypeScript's experimental implementation, and instead favor JavaScript's Stage 3 Proposal. To read more on the Stage 3 proposal and where it varies from experimental decorators, I'd recommend this article.

Limitations

Typescipt is limited in it's ability to transform types based on the presence of a decorator. This means, that if you modify argments or a return type in a decorator, Typescript will not know about it (yet). For the time-being (if you're a typescript user), I'd recommend not mutating method or property declarations in your decorators, and instead limit your usage functional transforms that maintain your application's type safety.

For JavaScript Users

Using this library without a syntax transform will not work any browser or node.js at the time of writing (August 12th, 2024). To ensure your code works smoothly across varying runtimes, check out the Babel's Transform for Stage 3 Decorators.