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

@jsenv/util

v4.1.1

Published

Set of functions often needed when using Node.js.

Downloads

348

Readme

util

Set of functions often needed when using Node.js.

github package npm package github ci codecov coverage

Table of contents

Presentation

This repository provides utils functions needed to work with files. It has no external dependency and two preferences:

An url is better than a filesystem path because it does not care about the underlying filesystem format.

  • A file url: file:///directory/file.js
  • A Windows file path: C:\\directory\\file.js
  • A Linux file path: /directory/file.js

There is a deliberate preference for url string over url object in the documentation and codebase.

const urlString = "file:///directory/file.js"
const urlObject = new URL("file:///directory/file.js")

A string is a simpler primitive than an url object and it becomes important while debugging.

screenshot of url object while debugging in vscode

screenshot of url string while debugging in vscode

This repository also provides some utils around urls not provided by Node.js. For instance it exports urlToRelativeUrl which can be seen as the equivalent of path.relative for urls.

Finally exported functions fully support url, even url string while native fs module does not.

fs module accepts url object since version 7.6 but not url string.

screenshot of readFile documentation changelog

Passing an url string to a function from fs will always throw ENOENT error.

import { readFileSync } from "fs"

readFileSync(import.meta.url) // throw ENOENT
const { readFileSync } = require("fs")

readFileSync(`file://${__filename}`) // throw ENOENT

Node.js made this choice for performance reasons but it hurts my productivity.

Example

The code below is a basic example reading package.json file as buffer.

import { readFileSync } from "fs"
import { resolveUrl, urlToFileSystemPath } from "@jsenv/util"

const packageFileUrl = resolveUrl("package.json", import.meta.url)
const packageFilePath = urlToFileSystemPath(packageFileUrl)
const packageFileBuffer = readFileSync(packageFilePath)

With times more functions were added, all util are documented a bit further.

Installation

npm install @jsenv/util

Terminology

This documentation and source code uses some wording explained in this part.

Urls parts

You can refer to figure below to see how each part of an url is named.

fileSystemNode

fileSystemNode word is used when a function does not assume what it is going to interact with: file, directory, or something else. For example copyFileSystemNode(fromUrl, toUrl, options) will take whatever is at fromUrl and copy it at toUrl.

assertAndNormalizeDirectoryUrl

assertAndNormalizeDirectoryUrl is a function ensuring the received value can be normalized to a directory url string. This function is great to make a function accept various values as directory url and normalize it to a standard directory url like file:///directory/.

import { assertAndNormalizeDirectoryUrl } from "@jsenv/util"

assertAndNormalizeDirectoryUrl("/directory") // file:///directory/
assertAndNormalizeDirectoryUrl("C:\\directory") // file://C:/directory/

unit testimplementation

assertAndNormalizeFileUrl

assertAndNormalizeFileUrl is a function ensuring the received value can be normalized to a file url string. This function is great to make a function accept various values as file url and normalize it to a standard file url like file:///directory/file.js.

import { assertAndNormalizeFileUrl } from "@jsenv/util"

assertAndNormalizeFileUrl("/directory/file.js") // file:///directory/file.js
assertAndNormalizeFileUrl("C:\\directory\\file.js") // file:///C:/directory/file.js

unit testimplementation

assertDirectoryPresence

assertDirectoryPresence is an async function throwing if directory does not exists on the filesystem. This function is great when code expects a directory to exist before going further.

import { assertDirectoryPresence } from "@jsenv/util"

await assertDirectoryPresence("file:///Users/directory/")

unit testimplementation

assertFilePresence

assertFilePresence is an async function throwing if a file does not exists on the filesystem. This function is great to when code expects a file to exist before going further.

import { assertFilePresence } from "@jsenv/util"

await assertFilePresence("file:///Users/directory/file.js")

unit testimplementation

bufferToEtag

bufferToEtag is a function receiving a buffer and converting it into an eTag. This function returns a hash (a small string) representing a file content. You can later check if the file content has changed by comparing a previously generated eTag with the current file content.

import { bufferToEtag } from "@jsenv/util"

const eTag = bufferToEtag(Buffer.from("Hello world"))
const otherEtag = bufferToEtag(Buffer.from("Hello world"))
eTag === otherEtag // true

unit testimplementationBuffer documentation on Node.jseTag documentation on MDN

collectFiles

collectFiles is an async function collectings a subset of files inside a directory.

import { collectFiles } from "@jsenv/util"

const files = await collectFiles({
  directoryUrl: "file:///Users/you/directory",
  structuredMetaMap: {
    whatever: {
      "./**/*.js": 42,
    },
  },
  predicate: (meta) => {
    return meta.whatever === 42
  },
})

unit testimplementation

comparePathnames

comparePathnames is a function compare two pathnames and returning which pathnames comes first in a filesystem.

import { comparePathnames } from "@jsenv/util"

const pathnames = ["a/b.js", "a.js"]
pathnames.sort(comparePathnames)

implementation

copyFileSystemNode

copyFileSystemNode is an async function creating a copy of the filesystem node at a given destination

import { copyFileSystemNode } from "@jsenv/util"

await copyFileSystemNode(`file:///file.js`, "file:///destination/file.js")
await copyFileSystemNode(`file:///directory`, "file:///destination/directory")

unit testimplementation

ensureEmptyDirectory

ensureEmptyDirectory is an async function ensuring a directory is empty. It removes a directory content when it exists or create an empty directory. This function was written for testing. It is meant to clean up a directory in case a previous test execution let some files and you want to clean them before running your test.

import { ensureEmptyDirectory } from "@jsenv/util"

await ensureEmptyDirectory(`file:///directory`)

unit testimplementation

ensureParentDirectories

ensureParentDirectories is an async function creating every directory leading to a file. This function is useful to ensure a given file directories exists before doing any operation on that file.

import { ensureParentDirectories } from "@jsenv/util"

await ensureParentDirectories(`file:///directory/subdirectory/file.js`)

implementation

writeDirectory

writeDirectory is an async function creating a directory on the filesystem. writeDirectory is equivalent to fs.promises.mkdir but accepts url strings as directory path.

import { writeDirectory } from "@jsenv/util"

await writeDirectory(`file:///directory`)

unit testimplementation

fileSystemPathToUrl

fileSystemPathToUrl is a function returning a filesystem path from an url string. fileSystemPathToUrl is equivalent to pathToFileURL from Node.js but returns string instead of url objects.

import { fileSystemPathToUrl } from "@jsenv/util"

fileSystemPathToUrl("/directory/file.js")

unit testimplementation

isFileSystemPath

isFileSystemPath is a function receiving a string and returning a boolean indicating if this string is a filesystem path.

import { isFileSystemPath } from "@jsenv/util"

isFileSystemPath("/directory/file.js") // true
isFileSystemPath("C:\\directory\\file.js") // true
isFileSystemPath("directory/file.js") // false
isFileSystemPath("file:///directory/file.js") // false

unit testimplementation

moveFileSystemNode

moveFileSystemNode is an async function moving a filesystem node to a destination.

import { moveFileSystemNode } from "@jsenv/util"

await moveFileSystemNode("file:///file.js", "file:///destination/file.js")
await moveFileSystemNode("file:///directory", "file:///destination/directory")

unit testimplementation

readDirectory

readDirectory is an async function returning an array of string representing all filesystem nodes inside that directory.

import { readDirectory } from "@jsenv/util"

const content = await readDirectory("file:///directory")

implementation

readFileSystemNodeModificationTime

readFileSystemNodeModificationTime is an async function returning a number of milliseconds representing the date when the file was modified.

import { readFileSystemNodeModificationTime } from "@jsenv/util"

const mtimeMs = await readFileSystemNodeModificationTime("file:///directory/file.js")

implementation

readFile

readFile is an async function returning the content of a file as string, buffer, or json.

import { readFile } from "@jsenv/util"

const fileContentAsString = await readFile("file:///directory/file.json")
const fileContentAsBuffer = await readFile("file:///directory/file.json", { as: "buffer" })
const fileContentAsJSON = await readFile("file:///directory/file.json", { as: "json" })

unit testimplementation

readFileSystemNodeStat

readFileSystemNodeStat is an async function returning a filesystem node stats object. readFileSystemNodeStat is equivalent to fs.promises.stats from Node.js but accepts url strings as file path.

import { readFileSystemNodeStat } from "@jsenv/util"

const stats = await readFileSystemNodeStat("file:///directory/file.js")

unit testimplementationstats object documentation on Node.js

readSymbolicLink

readSymbolicLink is an async function returning a symbolic link target as url string.

import { readSymbolicLink } from "@jsenv/util"

const targetUrlOrRelativeUrl = await readSymbolicLink("file:///directory/link")

implementationsymlink documentation on Node.js

registerDirectoryLifecycle

registerDirectoryLifecycle is a function watching a directory at a given path and calling added, updated, removed according to what is happening inside that directory. Usually, filesystem takes less than 100ms to notify something has changed.

import { registerDirectoryLifecycle } from "@jsenv/util"

const contentMap = {}
const unregister = registerDirectoryLifecycle("file:///directory", {
  added: ({ relativeUrl, type }) => {
    contentMap[relativeUrl] = type
  },
  removed: ({ relativeUrl }) => {
    delete contentMap[relativeUrl]
  },
})

// you can call unregister when you want to stop watching the directory
unregister()

unit testimplementation

registerFileLifecycle

registerFileLifecycle is a function watching a file and calling added, updated, removed according to what is happening to that file. Usually, filesystem takes less than 100ms to notify something has changed.

import { readFileSync } from "fs"
import { registerFileLifecycle } from "@jsenv/file-watcher"

const filePath = "/file.config.json"
let currentConfig = null
const unregister = registerFileLifecycle(filePath, {
  added: () => {
    currentConfig = JSON.parse(String(readFileSync(filePath)))
  },
  updated: () => {
    currentConfig = JSON.parse(String(readFileSync(filePath)))
  },
  removed: () => {
    currentConfig = null
  },
  notifyExistent: true,
})

// you can call unregister() when you want to stop watching the file
unregister()

unit testimplementation

removeFileSystemNode

removeFileSystemNode is an async function removing a node (directory, file, symbolic link) from the filesystem.

import { removeFileSystemNode } from "@jsenv/util"

await removeFileSystemNode("file:///file.js")
await removeFileSystemNode("file:///directory")

unit testimplementation

resolveUrl

resolveUrl is a function receiving two arguments called specifier and baseUrl. Both arguments are required. resolveUrl applies url resolution between specifier and baseUrl and returns the corresponding absolute url string.

import { resolveUrl } from "@jsenv/util"

resolveUrl("file.js", "file:///directory/") // file:///directory/file.js

unit testimplementation

When working with directory urls, it is important to have a trailing /.

new URL("foo.js", "file:///dir").href // file:///foo.js
new URL("foo.js", `file:///dir/`).href // file:///dir/foo.js

For this reason, if you have a variable holding a directory url, be sure to put a trailing slash.

import { resolveUrl } from "@jsenv/util"

const directoryUrl = resolveUrl("./dir/", "file:///")

Using resolveUrl means code wants to perform url resolution between something that can be relative: specifier, and something absolute: baseUrl.

For this reason resolveUrl will throw if baseUrl is undefined. This is a major difference with URL constructor that would not throw in such case.

import { resolveUrl } from "@jsenv/util"

new URL("http://example.com", undefined) // does not throw

resolveUrl("http://example.com", undefined) // throw "baseUrl is missing to resolve http://example.com"

Technically, http://example.com is already absolute and does not need a baseUrl to be resolved. But, receiving undefined when an absolute url was expected indicates there is something wrong in the code.

This is a feature that helps to catch bugs.

urlIsInsideOf

urlIsInsideOf is a function returning a boolean indicating if an url is inside an other url.

import { urlIsInsideOf } from "@jsenv/util"

urlIsInsideOf("file:///directory/file.js", "file:///directory/") // true
urlIsInsideOf("file:///file.js", "file:///directory/") // false

unit testimplementation

urlToBasename

urlToBasename is receiving an url and returning its basename.

import { urlToBasename } from "@jsenv/util"

urlToBasename("file:///directory/file.js") // "file"
urlToBasename("file:///directory/") // "directory"
urlToBasename("http://example.com") // ""

unit testimplementation

urlToExtension

urlToExtension is receiving an url and returning its extension.

import { urlToExtension } from "@jsenv/util"

urlToExtension("file:///directory/file.js") // ".js"
urlToExtension("file:///directory/file.") // "."
urlToExtension("http://example.com/file") // ""

unit testimplementation

urlToFilename

urlToFilename is receiving an url and returning its filename.

import { urlToFilename } from "@jsenv/util"

urlToFilename("file:///directory/file.js") // "file.js"
urlToFilename("file:///directory/file.") // "file."
urlToFilename("http://example.com/file") // "file"

unit testimplementation

urlToFileSystemPath

urlToFileSystemPath is a function returning a filesystem path from an url. urlToFileSystemPath is equivalent to pathToFileURL from Node.js but returns string instead of url objects.

import { urlToFileSystemPath } from "@jsenv/util"

// on mac or linux
urlToFileSystemPath("file:///directory/file.js") // /directory/file.js

// on windows
urlToFileSystemPath("file://C:/directory/file.js") // C:\\directory\\file.js

unit testimplementation

urlToOrigin

urlToOrigin is a function receiving an url and returning its origin.

import { urlToOrigin } from "@jsenv/util"

urlToOrigin("file:///directory/file.js") // "file://"
urlToOrigin("http://example.com/file.js") // "http://example.com"

unit testimplementation

urlToParentUrl

urlToParentUrl is a function receiving an url and returning its parent url if any or the url itself.

import { urlToParentUrl } from "@jsenv/util"

urlToParentUrl("http://example.com/dir/file.js") // "http://example.com/dir/"
urlToParentUrl("http://example.com/dir/") // "http://example.com/"
urlToParentUrl("http://example.com/") // "http://example.com/"

unit testimplementation

urlToPathname

urlToPathname is a function receiving an url and returning its pathname.

import { urlToPathname } from "@jsenv/util"

urlToPathname("http://example.com/dir/file.js") // "/dir/file.js"
urlToPathname("http://example.com/dir/") // "/dir/"
urlToPathname("http://example.com/") // "/"

unit testimplementation

urlToRelativeUrl

urlToRelativeUrl is a function receiving two absolute urls and returning the first url relative to the second one. urlToRelativeUrl is the url equivalent to path.relative from Node.js.

import { urlToRelativeUrl } from "@jsenv/util"

urlToRelativeUrl("file:///directory/file.js", "file:///directory/") // file.js
urlToRelativeUrl("file:///directory/index.js", "file:///directory/foo/file.js") // ../index.js

unit testimplementation

urlToRessource

urlToRessource is a function receiving an url and returning its ressource.

import { urlToRessource } from "@jsenv/util"

urlToRessource("http://example.com/dir/file.js?foo=bar#10") // "/dir/file.js?foo=bar#10"

unit testimplementation

urlToScheme

urlToScheme is a function receiving an url and returning its scheme.

import { urlToScheme } from "@jsenv/util"

urlToScheme("http://example.com") // "http"
urlToScheme("file:///dir/file.js") // "file"
urlToScheme("about:blank") // "about"

unit testimplementation

writeFile

writeFile is an async function writing file and its content on the filesystem. This function auto create file parent directories if they do not exists.

import { writeFile } from "@jsenv/util"

await writeFile("file:///directory/file.txt", "Hello world")

unit testimplementation

writeFileSystemNodeModificationTime

writeFileSystemNodeModificationTime is an async function writing file and its content on the filesystem. writeFileSystemNodeModificationTime is like fs.promises.utimes but accepts url strings as file path.

import { writeFileSystemNodeModificationTime } from "@jsenv/util"

await writeFileSystemNodeModificationTime("file:///directory/file.js", Date.now())

unit testimplementation

writeSymbolicLink

writeSymbolicLink is an async function writing a symlink link to a file or directory on the filesystem.

import { writeSymbolicLink } from "@jsenv/util"

await writeSymbolicLink("file:///foo.js", "./bar.js")

implementationsymlink documentation on Node.js

Advanced api

There is a few more functions but they are more specific, you probably don't need them: Advanced api