Socket
Socket
Sign inDemoInstall

@jsenv/filesystem

Package Overview
Dependencies
2
Maintainers
2
Versions
62
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.4.0 to 2.5.0

7

package.json
{
"name": "@jsenv/filesystem",
"version": "2.4.0",
"version": "2.5.0",
"description": "Collection of functions to interact with filesystem in Node.js",

@@ -19,4 +19,3 @@ "license": "MIT",

"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
"access": "public"
},

@@ -51,3 +50,3 @@ "type": "module",

"dependencies": {
"@jsenv/cancellation": "3.0.0",
"@jsenv/abort": "3.1.1",
"@jsenv/url-meta": "6.0.1"

@@ -54,0 +53,0 @@ },

@@ -1,2 +0,2 @@

import { createCancellationToken, createOperation } from "@jsenv/cancellation"
import { Abort } from "@jsenv/abort"
import {

@@ -16,3 +16,3 @@ normalizeStructuredMetaMap,

export const collectDirectoryMatchReport = async ({
cancellationToken = createCancellationToken(),
signal = new AbortController().signal,
directoryUrl,

@@ -34,7 +34,8 @@ structuredMetaMap,

const collectOperation = Abort.startOperation()
collectOperation.addAbortSignal(signal)
const visitDirectory = async (directoryUrl) => {
const directoryItems = await createOperation({
cancellationToken,
start: () => readDirectory(directoryUrl),
})
collectOperation.throwIfAborted()
const directoryItems = await readDirectory(directoryUrl)

@@ -49,15 +50,15 @@ await Promise.all(

const directoryChildNodeStats = await createOperation({
cancellationToken,
start: () =>
readFileSystemNodeStat(directoryChildNodeUrl, {
// we ignore symlink because recursively traversed
// so symlinked file will be discovered.
// Moreover if they lead outside of directoryPath it can become a problem
// like infinite recursion of whatever.
// that we could handle using an object of pathname already seen but it will be useless
// because directoryPath is recursively traversed
followLink: false,
}),
})
collectOperation.throwIfAborted()
const directoryChildNodeStats = await readFileSystemNodeStat(
directoryChildNodeUrl,
{
// we ignore symlink because recursively traversed
// so symlinked file will be discovered.
// Moreover if they lead outside of directoryPath it can become a problem
// like infinite recursion of whatever.
// that we could handle using an object of pathname already seen but it will be useless
// because directoryPath is recursively traversed
followLink: false,
},
)

@@ -110,7 +111,12 @@ if (directoryChildNodeStats.isDirectory()) {

}
await visitDirectory(rootDirectoryUrl)
return {
matchingArray: sortByRelativeUrl(matchingArray),
ignoredArray: sortByRelativeUrl(ignoredArray),
try {
await visitDirectory(rootDirectoryUrl)
return {
matchingArray: sortByRelativeUrl(matchingArray),
ignoredArray: sortByRelativeUrl(ignoredArray),
}
} finally {
await collectOperation.end()
}

@@ -117,0 +123,0 @@ }

@@ -1,2 +0,2 @@

import { createCancellationToken, createOperation } from "@jsenv/cancellation"
import { Abort } from "@jsenv/abort"
import {

@@ -15,7 +15,6 @@ normalizeStructuredMetaMap,

export const collectFiles = async ({
cancellationToken = createCancellationToken(),
signal = new AbortController().signal,
directoryUrl,
structuredMetaMap,
predicate,
matchingFileOperation = () => null,
}) => {

@@ -26,7 +25,2 @@ const rootDirectoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl)

}
if (typeof matchingFileOperation !== "function") {
throw new TypeError(
`matchingFileOperation must be a function, got ${matchingFileOperation}`,
)
}
const structuredMetaMapNormalized = normalizeStructuredMetaMap(

@@ -37,8 +31,9 @@ structuredMetaMap,

const collectOperation = Abort.startOperation()
collectOperation.addAbortSignal(signal)
const matchingFileResultArray = []
const visitDirectory = async (directoryUrl) => {
const directoryItems = await createOperation({
cancellationToken,
start: () => readDirectory(directoryUrl),
})
collectOperation.throwIfAborted()
const directoryItems = await readDirectory(directoryUrl)

@@ -49,15 +44,15 @@ await Promise.all(

const directoryChildNodeStats = await createOperation({
cancellationToken,
start: () =>
readFileSystemNodeStat(directoryChildNodeUrl, {
// we ignore symlink because recursively traversed
// so symlinked file will be discovered.
// Moreover if they lead outside of directoryPath it can become a problem
// like infinite recursion of whatever.
// that we could handle using an object of pathname already seen but it will be useless
// because directoryPath is recursively traversed
followLink: false,
}),
})
collectOperation.throwIfAborted()
const directoryChildNodeStats = await readFileSystemNodeStat(
directoryChildNodeUrl,
{
// we ignore symlink because recursively traversed
// so symlinked file will be discovered.
// Moreover if they lead outside of directoryPath it can become a problem
// like infinite recursion of whatever.
// that we could handle using an object of pathname already seen but it will be useless
// because directoryPath is recursively traversed
followLink: false,
},
)

@@ -92,12 +87,2 @@ if (directoryChildNodeStats.isDirectory()) {

)
const operationResult = await createOperation({
cancellationToken,
start: () =>
matchingFileOperation({
cancellationToken,
relativeUrl,
meta,
fileStats: directoryChildNodeStats,
}),
})
matchingFileResultArray.push({

@@ -108,3 +93,2 @@ url: new URL(relativeUrl, rootDirectoryUrl).href,

fileStats: directoryChildNodeStats,
operationResult,
})

@@ -116,12 +100,17 @@ return

}
await visitDirectory(rootDirectoryUrl)
// When we operate on thoose files later it feels more natural
// to perform operation in the same order they appear in the filesystem.
// It also allow to get a predictable return value.
// For that reason we sort matchingFileResultArray
matchingFileResultArray.sort((leftFile, rightFile) => {
return comparePathnames(leftFile.relativeUrl, rightFile.relativeUrl)
})
return matchingFileResultArray
try {
await visitDirectory(rootDirectoryUrl)
// When we operate on thoose files later it feels more natural
// to perform operation in the same order they appear in the filesystem.
// It also allow to get a predictable return value.
// For that reason we sort matchingFileResultArray
matchingFileResultArray.sort((leftFile, rightFile) => {
return comparePathnames(leftFile.relativeUrl, rightFile.relativeUrl)
})
return matchingFileResultArray
} finally {
await collectOperation.end()
}
}

@@ -0,1 +1,3 @@

import { Abort } from "@jsenv/abort"
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"

@@ -8,3 +10,2 @@ import { readFileSystemNodeStat } from "./readFileSystemNodeStat.js"

import { copyFileSystemNode } from "./copyFileSystemNode.js"
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"

@@ -14,2 +15,3 @@ import { statsToType } from "./internal/statsToType.js"

export const copyDirectoryContent = async ({
signal = new AbortController().signal,
from,

@@ -70,15 +72,25 @@ to,

const directoryEntries = await readDirectory(fromUrl)
await Promise.all(
directoryEntries.map(async (directoryEntry) => {
const from = resolveUrl(directoryEntry, fromUrl)
const to = resolveUrl(directoryEntry, toUrl)
await copyFileSystemNode({
from,
to,
overwrite,
followLink,
})
}),
)
const copyOperation = Abort.startOperation()
copyOperation.addAbortSignal(signal)
try {
copyOperation.throwIfAborted()
const directoryEntries = await readDirectory(fromUrl)
await Promise.all(
directoryEntries.map(async (directoryEntry) => {
const from = resolveUrl(directoryEntry, fromUrl)
const to = resolveUrl(directoryEntry, toUrl)
await copyOperation.withSignal(async (signal) => {
await copyFileSystemNode({
signal,
from,
to,
overwrite,
followLink,
})
})
}),
)
} finally {
await copyOperation.end()
}
}

@@ -1,3 +0,3 @@

/* eslint-disable import/max-dependencies */
import { copyFile as copyFileNode } from "node:fs"
import { Abort } from "@jsenv/abort"

@@ -24,2 +24,3 @@ import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"

export const copyFileSystemNode = async ({
signal = new AbortController().signal,
from,

@@ -72,23 +73,2 @@ to,

if (destinationStats) {
const sourceType = statsToType(sourceStats)
const destinationType = statsToType(destinationStats)
if (sourceType !== destinationType) {
throw new Error(
`cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
)
}
if (!overwrite) {
throw new Error(
`cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
)
}
// remove file, link, directory...
await removeFileSystemNode(toUrl, { recursive: true, allowUseless: true })
} else {
await ensureParentDirectories(toUrl)
}
if (sourceStats.isDirectory()) {

@@ -98,3 +78,7 @@ toUrl = ensureUrlTrailingSlash(toUrl)

const copyOperation = Abort.startOperation()
copyOperation.addAbortSignal(signal)
const visit = async (url, stats) => {
copyOperation.throwIfAborted()
if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {

@@ -203,3 +187,33 @@ await visitFile(url, stats)

await visit(fromUrl, sourceStats)
try {
if (destinationStats) {
const sourceType = statsToType(sourceStats)
const destinationType = statsToType(destinationStats)
if (sourceType !== destinationType) {
throw new Error(
`cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
)
}
if (!overwrite) {
throw new Error(
`cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
)
}
// remove file, link, directory...
await removeFileSystemNode(toUrl, {
signal: copyOperation.signal,
recursive: true,
allowUseless: true,
})
} else {
await ensureParentDirectories(toUrl)
}
copyOperation.throwIfAborted()
await visit(fromUrl, sourceStats)
} finally {
await copyOperation.end()
}
}

@@ -206,0 +220,0 @@

@@ -6,2 +6,3 @@ import { collectFiles } from "./collectFiles.js"

directoryUrl = process.cwd(),
signal,
) => {

@@ -27,2 +28,3 @@ if (typeof patterns === "string") {

const fileDatas = await collectFiles({
signal,
directoryUrl,

@@ -29,0 +31,0 @@ structuredMetaMap: {

@@ -0,1 +1,3 @@

import { Abort } from "@jsenv/abort"
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"

@@ -8,3 +10,2 @@ import { readFileSystemNodeStat } from "./readFileSystemNodeStat.js"

import { moveFileSystemNode } from "./moveFileSystemNode.js"
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"

@@ -14,2 +15,3 @@ import { statsToType } from "./internal/statsToType.js"

export const moveDirectoryContent = async ({
signal = new AbortController().signal,
from,

@@ -70,15 +72,26 @@ to,

const directoryEntries = await readDirectory(fromUrl)
await Promise.all(
directoryEntries.map(async (directoryEntry) => {
const from = resolveUrl(directoryEntry, fromUrl)
const to = resolveUrl(directoryEntry, toUrl)
await moveFileSystemNode({
from,
to,
overwrite,
followLink,
})
}),
)
const moveOperation = Abort.startOperation()
moveOperation.addAbortSignal(signal)
try {
moveOperation.throwIfAborted()
const directoryEntries = await readDirectory(fromUrl)
await Promise.all(
directoryEntries.map(async (directoryEntry) => {
const from = resolveUrl(directoryEntry, fromUrl)
const to = resolveUrl(directoryEntry, toUrl)
await moveOperation.withSignal(async (signal) => {
await moveFileSystemNode({
signal,
from,
to,
overwrite,
followLink,
})
})
}),
)
} finally {
await moveOperation.end()
}
}

@@ -1,3 +0,3 @@

/* eslint-disable import/max-dependencies */
import { rename } from "node:fs"
import { Abort } from "@jsenv/abort"

@@ -16,2 +16,3 @@ import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"

export const moveFileSystemNode = async ({
signal = new AbortController().signal,
from,

@@ -61,33 +62,47 @@ to,

if (destinationStats) {
const sourceType = statsToType(sourceStats)
const destinationType = statsToType(destinationStats)
const moveOperation = Abort.startOperation()
moveOperation.addAbortSignal(signal)
if (sourceType !== destinationType) {
throw new Error(
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
)
try {
if (destinationStats) {
const sourceType = statsToType(sourceStats)
const destinationType = statsToType(destinationStats)
if (sourceType !== destinationType) {
throw new Error(
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
)
}
if (!overwrite) {
throw new Error(
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
)
}
// remove file, link, directory...
await removeFileSystemNode(toUrl, {
signal: moveOperation.signal,
recursive: true,
})
} else {
await ensureParentDirectories(toUrl)
}
if (!overwrite) {
throw new Error(
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
)
}
// remove file, link, directory...
await removeFileSystemNode(toUrl, { recursive: true })
} else {
await ensureParentDirectories(toUrl)
moveOperation.throwIfAborted()
await moveNaive(fromPath, toPath, {
handleCrossDeviceError: async () => {
await copyFileSystemNode({
from: fromUrl,
to: toUrl,
preserveStat: true,
})
await removeFileSystemNode(fromUrl, {
signal: moveOperation.signal,
recursive: true,
})
},
})
} finally {
await moveOperation.end()
}
await moveNaive(fromPath, toPath, {
handleCrossDeviceError: async () => {
await copyFileSystemNode({
from: fromUrl,
to: toUrl,
preserveStat: true,
})
await removeFileSystemNode(fromUrl, { recursive: true })
},
})
}

@@ -94,0 +109,0 @@

import { unlink, rmdir, openSync, closeSync } from "node:fs"
import { Abort } from "@jsenv/abort"

@@ -13,2 +14,3 @@ import { ensureUrlTrailingSlash } from "./internal/ensureUrlTrailingSlash.js"

{
signal = new AbortController().signal,
allowUseless = false,

@@ -23,36 +25,45 @@ recursive = false,

const sourceStats = await readFileSystemNodeStat(sourceUrl, {
nullIfNotFound: true,
followLink: false,
})
if (!sourceStats) {
if (allowUseless) {
return
const removeOperation = Abort.startOperation()
removeOperation.addAbortSignal(signal)
try {
removeOperation.throwIfAborted()
const sourceStats = await readFileSystemNodeStat(sourceUrl, {
nullIfNotFound: true,
followLink: false,
})
if (!sourceStats) {
if (allowUseless) {
return
}
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`)
}
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`)
}
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
// FIFO and socket are ignored, not sure what they are exactly and what to do with them
// other libraries ignore them, let's do the same.
if (
sourceStats.isFile() ||
sourceStats.isSymbolicLink() ||
sourceStats.isCharacterDevice() ||
sourceStats.isBlockDevice()
) {
await removeNonDirectory(
sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl,
{
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
// FIFO and socket are ignored, not sure what they are exactly and what to do with them
// other libraries ignore them, let's do the same.
if (
sourceStats.isFile() ||
sourceStats.isSymbolicLink() ||
sourceStats.isCharacterDevice() ||
sourceStats.isBlockDevice()
) {
await removeNonDirectory(
sourceUrl.endsWith("/") ? sourceUrl.slice(0, -1) : sourceUrl,
{
maxRetries,
retryDelay,
},
)
} else if (sourceStats.isDirectory()) {
await removeDirectory(ensureUrlTrailingSlash(sourceUrl), {
signal: removeOperation.signal,
recursive,
maxRetries,
retryDelay,
},
)
} else if (sourceStats.isDirectory()) {
await removeDirectory(ensureUrlTrailingSlash(sourceUrl), {
recursive,
maxRetries,
retryDelay,
onlyContent,
})
onlyContent,
})
}
} finally {
await removeOperation.end()
}

@@ -110,5 +121,9 @@ }

rootDirectoryUrl,
{ maxRetries, retryDelay, recursive, onlyContent },
{ signal, maxRetries, retryDelay, recursive, onlyContent },
) => {
const removeDirectoryOperation = Abort.startOperation()
removeDirectoryOperation.addAbortSignal(signal)
const visit = async (sourceUrl) => {
removeDirectoryOperation.throwIfAborted()
const sourceStats = await readFileSystemNodeStat(sourceUrl, {

@@ -147,2 +162,3 @@ nullIfNotFound: true,

: {}
removeDirectoryOperation.throwIfAborted()
await removeDirectoryNaive(directoryPath, {

@@ -186,2 +202,3 @@ ...optionsFromRecursive,

const removeDirectoryContent = async (directoryUrl) => {
removeDirectoryOperation.throwIfAborted()
const names = await readDirectory(directoryUrl)

@@ -204,6 +221,10 @@ await Promise.all(

if (onlyContent) {
await removeDirectoryContent(rootDirectoryUrl)
} else {
await visitDirectory(rootDirectoryUrl)
try {
if (onlyContent) {
await removeDirectoryContent(rootDirectoryUrl)
} else {
await visitDirectory(rootDirectoryUrl)
}
} finally {
await removeDirectoryOperation.end()
}

@@ -210,0 +231,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc