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

nova-extension-utils

v1.4.0

Published

Shared utilites for developing Nova.app extensions

Downloads

8,926

Readme

nova-extension-utils

This package provides sharable utilities for building Nova extensions.

Features

installWrappedDependencies

This function provides a concurrent-safe, reproducible, non-global-polluting mechanism for installing external nodejs dependencies executed by your extension, without bundling them within the extension artifact and increasing extension size. This is especially useful in dev, as it won't trigger reloads of the extension recursively.

To use it, you must have a valid package.json and npm-shrinkwrap.json file in your .novaextension directory. They'll be copied into your extension's global storage and used to install dependencies specified in the shrinkwrap file. An atomic file lock is used to prevent multiple workspaces writing over each other.

installWrappedDependencies

This is an async function that handles installation. Call it during extension activation. It takes a two parameters

  1. A composite disposable that on disposal kills the installation process and unlocks. Be sure to dispose when the extension deactivates.
  2. An optional options object with the optional properties.
    • console an object who's properties override those of Console. Pass null to fully disable console calls.

registerDependencyUnlockCommand

Registers a global command that will force unlock. Not required, but can be useful to cleanup if crashes happen. Make sure to define the command to give users access.

getDependencyDirectory

Returns a path to the directory containing the installed node_modules directory.

import { dependencyManagement } from "nova-extension-utils";

const compositeDisposable = new CompositeDisposable();

dependencyManagement.registerDependencyUnlockCommand(
  "com.example.extension.unlock"
);

async function asyncActivate() {
  await dependencyManagement.installWrappedDependencies(compositeDisposable, {
    console: {
      log(...args: Array<unknown>) {
        console.log("dependency management:", ...args);
      },
    },
  });

  const execPath = nova.path.join(
    dependencyManagement.getDependencyDirectory(),
    "node_modules",
    ".bin",
    "executable"
  );
  const process = new Process(execPath);
  compositeDisposable.add({
    dispose() {
      process.terminate();
    },
  });
  process.start();
}

export function activate() {
  console.log("activating...");
  return asyncActivate()
    .catch((err) => {
      console.error(err);
    })
    .then(() => {
      console.log("activated");
    });
}

export function deactivate() {
  compositeDisposable.dispose();
}

asyncNova

showChoicePalette

Asyncronous, generic, non-index-based access to the choice palette.

import type * as lspTypes from "vscode-languageserver-protocol";
import { asyncNova } from "nova-extension-utils";

async function foo(items: lspTypes.CompletionItem[]) {
  const choice: lspTypes.CompletionItem | null = await asyncNova(
    items,
    (item) => `${item.label}${item.detail ? `- ${item.detail}` : ""}`,
    { placeholder: "suggestions" }
  );
  if (!choice) {
    return;
  }
  console.log(choice);
}

cleanPath

Function to nicely format paths and file URIs for user-display. Replaces $HOME with ~/, removes volume and file://, and replaces workspace path with ./.

import { cleanPath } from "nova-extension-utils";

console.log(cleanPath(editor.document.uri));

preferences

getOverridableBoolean

This provides a common pattern to have a boolean preference that can be set globally or per-workspace, but allows the workspace to override the global preference. The user has the ability to set a preference globally, but they can override it in each workspace. I recommend defaulting to the least-destructive/mutating action globally, to help make it easier to work in shared codebases.

This expects the preference to be set up properly in your extension manifest. You'll configure a global-level boolean (with any default value), and a workspace-level enum (with three possible values of "null", "false", and "true" and a default of "null") with the same preference key.

{
  "config": [
    {
      "key": "apexskier.example.config.myPreference",
      "title": "Example",
      "type": "boolean",
      "default": false
    }
  ],

  "configWorkspace": [
    {
      "key": "apexskier.example.config.myPreference",
      "title": "Example",
      "type": "enum",
      "values": [
        ["null", "Inherit from Global Settings"],
        ["false", "Disable"],
        ["true", "Enable"]
      ],
      "default": "null"
    }
  ]
}
import { preferences } from "nova-extension-utils";

const defaultPrefValue = false;
const prefValue: boolean =
  preferences.getOverridableBoolean("apexskier.example.config.myPreference") ??
  defaultPrefValue;