util
Set of functions often needed when using Node.js.
Table of contents
Presentation
This repository provides utils functions needed to work with files. It has no external dependency and two preferences:
prefer url over filesystem path
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
prefer url string over url object
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 an url object while debugging
Screenshot of an url string while debugging
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 lack support for url string
fs
module accepts url object since version 7.6 but not url string.
Passing an url string to a function from fs
will always throw ENOENT error.
import { readFileSync } from "fs"
readFileSync(import.meta.url)
const { readFileSync } = require("fs")
readFileSync(`file://${__filename}`)
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.
href
┌────────────────────────────────────────┴──────────────────────────────────────────────┐
origin │
┌────────────┴──────────────┐ │
│ authority │
│ ┌───────────────┴───────────────────────────┐ │
│ │ host ressource
│ │ ┌──────────┴────────────────┐ ┌──────────────┴────────┬────────┐
│ │ hostname │ pathname │ │
│ │ ┌──────────────┴────────────┐ │ ┌──────┴──────┐ │ │
protocol userinfo subdomain domain │ │ filename │ │
┌─┴──┐ ┌───┴────┐ │ ┌────────┴───────┐ │ │ ┌───┴─────┐ │ │
scheme │username password lowerleveldomains secondleveldomain topleveldomain port dirname basename extension search hash
┌──┴───┐│┌──┴───┐ ┌──┴───┐ ┌──┬─┬─┴─────┬───┐┌───────┴───────┐ ┌──────┴──────┐┌┴┐┌────┴─────┐ ┌──┴───┐ ┌───┴───┐ ┌────┴────┐ ┌┴┐
│ │││ │ │ │ │ │ │ │ ││ │ │ ││ ││ │ │ │ │ │ │ │ │ │
scheme://username:password@test.abcdedgh.www.secondleveldomain.topleveldomain:123/hello/world/basename.extension?name=ferret#hash
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/
.
assertAndNormalizeDirectoryUrl code example
import { assertAndNormalizeDirectoryUrl } from "@jsenv/util"
assertAndNormalizeDirectoryUrl("/directory")
assertAndNormalizeDirectoryUrl("C:\\directory")
unit test • implementation
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
.
assertAndNormalizeFileUrl code example
import { assertAndNormalizeFileUrl } from "@jsenv/util"
assertAndNormalizeFileUrl("/directory/file.js")
assertAndNormalizeFileUrl("C:\\directory\\file.js")
unit test • implementation
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.
assertDirectoryPresence code example
import { assertDirectoryPresence } from "@jsenv/util"
await assertDirectoryPresence("file:///Users/directory/")
unit test • implementation
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.
assertFilePresence code example
import { assertFilePresence } from "@jsenv/util"
await assertFilePresence("file:///Users/directory/file.js")
unit test • implementation
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.
bufferToEtag code example
import { bufferToEtag } from "@jsenv/util"
const eTag = bufferToEtag(Buffer.from("Hello world"))
const otherEtag = bufferToEtag(Buffer.from("Hello world"))
eTag === otherEtag
unit test • implementation • Buffer documentation on Node.js • eTag documentation on MDN
collectFiles
collectFiles
is an async function collectings a subset of files inside a directory.
collectFiles code example
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 test • implementation
comparePathnames
comparePathnames
is a function compare two pathnames and returning which pathnames comes first in a filesystem.
comparePathnames code example
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
copyFileSystemNode code example
import { copyFileSystemNode } from "@jsenv/util"
await copyFileSystemNode(`file:///file.js`, "file:///destination/file.js")
await copyFileSystemNode(`file:///directory`, "file:///destination/directory")
unit test • implementation
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.
ensureEmptyDirectory code example
import { ensureEmptyDirectory } from "@jsenv/util"
await ensureEmptyDirectory(`file:///directory`)
unit test • implementation
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.
ensureParentDirectories code example
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.
writeDirectory code example
import { writeDirectory } from "@jsenv/util"
await writeDirectory(`file:///directory`)
unit test • implementation
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.
fileSystemPathToUrl code example
import { fileSystemPathToUrl } from "@jsenv/util"
fileSystemPathToUrl("/directory/file.js")
unit test • implementation
isFileSystemPath
isFileSystemPath
is a function receiving a string and returning a boolean indicating if this string is a filesystem path.
isFileSystemPath code example
import { isFileSystemPath } from "@jsenv/util"
isFileSystemPath("/directory/file.js")
isFileSystemPath("C:\\directory\\file.js")
isFileSystemPath("directory/file.js")
isFileSystemPath("file:///directory/file.js")
unit test • implementation
moveFileSystemNode
moveFileSystemNode
is an async function moving a filesystem node to a destination.
moveFileSystemNode code example
import { moveFileSystemNode } from "@jsenv/util"
await moveFileSystemNode("file:///file.js", "file:///destination/file.js")
await moveFileSystemNode("file:///directory", "file:///destination/directory")
unit test • implementation
readDirectory
readDirectory
is an async function returning an array of string representing all filesystem nodes inside that directory.
readDirectory code example
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.
readFileSystemNodeModificationTime code example
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.
readFile code example
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 test • implementation
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.
readFileSystemNodeStat code example
import { readFileSystemNodeStat } from "@jsenv/util"
const stats = await readFileSystemNodeStat("file:///directory/file.js")
unit test • implementation • stats object documentation on Node.js
readSymbolicLink
readSymbolicLink
is an async function returning a symbolic link target as url string.
readFileSystemNodeStat code example
import { readSymbolicLink } from "@jsenv/util"
const targetUrlOrRelativeUrl = await readSymbolicLink("file:///directory/link")
implementation • symlink 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.
registerDirectoryLifecycle code example
import { registerDirectoryLifecycle } from "@jsenv/util"
const contentMap = {}
const unregister = registerDirectoryLifecycle("file:///directory", {
added: ({ relativeUrl, type }) => {
contentMap[relativeUrl] = type
},
removed: ({ relativeUrl }) => {
delete contentMap[relativeUrl]
},
})
unregister()
unit test • implementation
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.
registerFileLifecycle code example
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,
})
unregister()
unit test • implementation
removeFileSystemNode
removeFileSystemNode
is an async function removing a node (directory, file, symbolic link) from the filesystem.
removeFileSystemNode code example
import { removeFileSystemNode } from "@jsenv/util"
await removeFileSystemNode("file:///file.js")
await removeFileSystemNode("file:///directory")
unit test • implementation
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.
resolveUrl code example
import { resolveUrl } from "@jsenv/util"
resolveUrl("file.js", "file:///directory/")
unit test • implementation
Note about url resolution and directory
When working with directory urls, it is important to have a trailing /
.
new URL("foo.js", "file:///dir").href
new URL("foo.js", `file:///dir/`).href
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:///")
Difference between resolveUrl and URL
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)
resolveUrl("http://example.com", undefined)
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.
urlIsInsideOf code example
import { urlIsInsideOf } from "@jsenv/util"
urlIsInsideOf("file:///directory/file.js", "file:///directory/")
urlIsInsideOf("file:///file.js", "file:///directory/")
unit test • implementation
urlToBasename
urlToBasename
is receiving an url and returning its basename.
urlToBasename code example
import { urlToBasename } from "@jsenv/util"
urlToBasename("file:///directory/file.js")
urlToBasename("file:///directory/")
urlToBasename("http://example.com")
unit test • implementation
urlToExtension
urlToExtension
is receiving an url and returning its extension.
urlToExtension code example
import { urlToExtension } from "@jsenv/util"
urlToExtension("file:///directory/file.js")
urlToExtension("file:///directory/file.")
urlToExtension("http://example.com/file")
unit test • implementation
urlToFilename
urlToFilename
is receiving an url and returning its filename.
urlToFilename code example
import { urlToFilename } from "@jsenv/util"
urlToFilename("file:///directory/file.js")
urlToFilename("file:///directory/file.")
urlToFilename("http://example.com/file")
unit test • implementation
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.
urlToFileSystemPath code example
import { urlToFileSystemPath } from "@jsenv/util"
urlToFileSystemPath("file:///directory/file.js")
urlToFileSystemPath("file://C:/directory/file.js")
unit test • implementation
urlToOrigin
urlToOrigin
is a function receiving an url and returning its origin.
urlToOrigin code example
import { urlToOrigin } from "@jsenv/util"
urlToOrigin("file:///directory/file.js")
urlToOrigin("http://example.com/file.js")
unit test • implementation
urlToParentUrl
urlToParentUrl
is a function receiving an url and returning its parent url if any or the url itself.
urlToParentUrl code example
import { urlToParentUrl } from "@jsenv/util"
urlToParentUrl("http://example.com/dir/file.js")
urlToParentUrl("http://example.com/dir/")
urlToParentUrl("http://example.com/")
unit test • implementation
urlToPathname
urlToPathname
is a function receiving an url and returning its pathname.
urlToPathname code example
import { urlToPathname } from "@jsenv/util"
urlToPathname("http://example.com/dir/file.js")
urlToPathname("http://example.com/dir/")
urlToPathname("http://example.com/")
unit test • implementation
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.
urlToRelativeUrl code example
import { urlToRelativeUrl } from "@jsenv/util"
urlToRelativeUrl("file:///directory/file.js", "file:///directory/")
urlToRelativeUrl("file:///directory/index.js", "file:///directory/foo/file.js")
unit test • implementation
urlToRessource
urlToRessource
is a function receiving an url and returning its ressource.
urlToRessource code example
import { urlToRessource } from "@jsenv/util"
urlToRessource("http://example.com/dir/file.js?foo=bar#10")
unit test • implementation
urlToScheme
urlToScheme
is a function receiving an url and returning its scheme.
urlToScheme code example
import { urlToScheme } from "@jsenv/util"
urlToScheme("http://example.com")
urlToScheme("file:///dir/file.js")
urlToScheme("about:blank")
unit test • implementation
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.
writeFile code example
import { writeFile } from "@jsenv/util"
await writeFile("file:///directory/file.txt", "Hello world")
unit test • implementation
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.
writeFileSystemNodeModificationTime code example
import { writeFileSystemNodeModificationTime } from "@jsenv/util"
await writeFileSystemNodeModificationTime("file:///directory/file.js", Date.now())
unit test • implementation
writeSymbolicLink
writeSymbolicLink
is an async function writing a symlink link to a file or directory on the filesystem.
writeSymbolicLink code example
import { writeSymbolicLink } from "@jsenv/util"
await writeSymbolicLink("file:///foo.js", "./bar.js")
implementation • symlink 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