extra-filesystem 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/checksum-file.ts +14 -0
- package/src/copy-sync.ts +7 -0
- package/src/copy.ts +10 -0
- package/src/create-temp-dir-sync.ts +8 -0
- package/src/create-temp-dir.ts +8 -0
- package/src/create-temp-file-sync.ts +8 -0
- package/src/create-temp-file.ts +8 -0
- package/src/create-temp-name-sync.ts +5 -0
- package/src/create-temp-name.ts +5 -0
- package/src/empty-dir-sync.ts +8 -0
- package/src/empty-dir.ts +9 -0
- package/src/ensure-dir-sync.ts +5 -0
- package/src/ensure-dir.ts +5 -0
- package/src/ensure-file-sync.ts +14 -0
- package/src/ensure-file.ts +15 -0
- package/src/find-all-dirnames.ts +22 -0
- package/src/find-all-filenames.ts +20 -0
- package/src/find-up-package-filename-sync.ts +14 -0
- package/src/find-up-package-filename.ts +14 -0
- package/src/get-long-extension.ts +13 -0
- package/src/get-short-basename.ts +6 -0
- package/src/index.ts +47 -0
- package/src/is-directory-sync.ts +6 -0
- package/src/is-directory.ts +6 -0
- package/src/is-file-sync.ts +6 -0
- package/src/is-file.ts +6 -0
- package/src/is-readable.ts +11 -0
- package/src/is-sub-path-of.ts +9 -0
- package/src/is-writable.ts +11 -0
- package/src/move-sync.ts +7 -0
- package/src/move.ts +7 -0
- package/src/path-exists-sync.ts +13 -0
- package/src/path-exists.ts +13 -0
- package/src/read-file-line-by-line-sync.ts +25 -0
- package/src/read-file-line-by-line.ts +14 -0
- package/src/read-json-file-sync.ts +9 -0
- package/src/read-json-file.ts +9 -0
- package/src/read-ndjson-file-sync.ts +14 -0
- package/src/read-ndjson-file.ts +14 -0
- package/src/read-yaml-file-sync.ts +11 -0
- package/src/read-yaml-file.ts +11 -0
- package/src/remove-sync.ts +5 -0
- package/src/remove.ts +5 -0
- package/src/write-iterable-to-file.ts +12 -0
- package/src/write-json-file-sync.ts +10 -0
- package/src/write-json-file.ts +10 -0
- package/src/write-yaml-file-sync.ts +7 -0
- package/src/write-yaml-file.ts +7 -0
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
|
|
4
|
+
export async function checksumFile(
|
|
5
|
+
algorithm: string
|
|
6
|
+
, filename: string
|
|
7
|
+
): Promise<string> {
|
|
8
|
+
const hash = crypto.createHash(algorithm)
|
|
9
|
+
const stream = fs.createReadStream(filename)
|
|
10
|
+
for await (const chunk of stream) {
|
|
11
|
+
hash.update(chunk)
|
|
12
|
+
}
|
|
13
|
+
return hash.digest('hex')
|
|
14
|
+
}
|
package/src/copy-sync.ts
ADDED
package/src/copy.ts
ADDED
package/src/empty-dir.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import { remove } from './remove.js'
|
|
3
|
+
import { each } from 'extra-promise'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
export async function emptyDir(dirname: string): Promise<void> {
|
|
7
|
+
const names = await fs.readdir(dirname)
|
|
8
|
+
await each(names, name => remove(path.join(dirname, name)))
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ensureDirSync } from './ensure-dir-sync.js'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import { pass } from '@blackglory/prelude'
|
|
5
|
+
|
|
6
|
+
export function ensureFileSync(filename: string): void {
|
|
7
|
+
const dir = path.dirname(filename)
|
|
8
|
+
ensureDirSync(dir)
|
|
9
|
+
try {
|
|
10
|
+
fs.closeSync(fs.openSync(filename, 'wx'))
|
|
11
|
+
} catch {
|
|
12
|
+
pass()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ensureDir } from './ensure-dir.js'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import fs from 'fs/promises'
|
|
4
|
+
import { pass } from '@blackglory/prelude'
|
|
5
|
+
|
|
6
|
+
export async function ensureFile(filename: string): Promise<void> {
|
|
7
|
+
const dir = path.dirname(filename)
|
|
8
|
+
await ensureDir(dir)
|
|
9
|
+
try {
|
|
10
|
+
const handle = await fs.open(filename, 'wx')
|
|
11
|
+
await handle.close()
|
|
12
|
+
} catch {
|
|
13
|
+
pass()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export async function* findAllDirnames(
|
|
5
|
+
dirname: string
|
|
6
|
+
, predicate: (dirname: string) => boolean = _ => true
|
|
7
|
+
): AsyncIterableIterator<string> {
|
|
8
|
+
const subDirnames = await getSubDirnames(dirname)
|
|
9
|
+
for (const dirname of subDirnames) {
|
|
10
|
+
if (predicate(dirname)) {
|
|
11
|
+
yield dirname
|
|
12
|
+
yield* findAllDirnames(dirname, predicate)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function getSubDirnames(dirname: string): Promise<string[]> {
|
|
18
|
+
const dirents = await fs.readdir(dirname, { withFileTypes: true })
|
|
19
|
+
return dirents
|
|
20
|
+
.filter(x => x.isDirectory())
|
|
21
|
+
.map(x => path.join(dirname, x.name))
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export async function* findAllFilenames(
|
|
5
|
+
dirname: string
|
|
6
|
+
, predicate: (dirname: string) => boolean = _ => true
|
|
7
|
+
): AsyncIterableIterator<string> {
|
|
8
|
+
const dirents = await fs.readdir(dirname, { withFileTypes: true })
|
|
9
|
+
for (const dirent of dirents) {
|
|
10
|
+
if (dirent.isDirectory()) {
|
|
11
|
+
const subDirname = path.join(dirname, dirent.name)
|
|
12
|
+
if (predicate(subDirname)) {
|
|
13
|
+
yield* findAllFilenames(subDirname, predicate)
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
const filename = path.join(dirname, dirent.name)
|
|
17
|
+
yield filename
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { pathExistsSync } from './path-exists-sync.js'
|
|
3
|
+
|
|
4
|
+
export function findUpPackageFilenameSync(pathname: string): string | undefined {
|
|
5
|
+
pathname = path.resolve(pathname)
|
|
6
|
+
while (true) {
|
|
7
|
+
const filename = path.resolve(pathname, 'package.json')
|
|
8
|
+
if (pathExistsSync(filename)) return filename
|
|
9
|
+
|
|
10
|
+
const newpathname = path.resolve(pathname, '..')
|
|
11
|
+
if (newpathname === pathname) return
|
|
12
|
+
pathname = newpathname
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { pathExists } from './path-exists.js'
|
|
3
|
+
|
|
4
|
+
export async function findUpPackageFilename(pathname: string): Promise<string | undefined> {
|
|
5
|
+
pathname = path.resolve(pathname)
|
|
6
|
+
while (true) {
|
|
7
|
+
const filename = path.resolve(pathname, 'package.json')
|
|
8
|
+
if (await pathExists(filename)) return filename
|
|
9
|
+
|
|
10
|
+
const newpathname = path.resolve(pathname, '..')
|
|
11
|
+
if (newpathname === pathname) return
|
|
12
|
+
pathname = newpathname
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
export function getLongExtension(filename: string): string {
|
|
4
|
+
let result = ''
|
|
5
|
+
let remainder = filename
|
|
6
|
+
while (true) {
|
|
7
|
+
const extension = path.extname(remainder)
|
|
8
|
+
if (!extension) break
|
|
9
|
+
remainder = path.basename(remainder, extension)
|
|
10
|
+
result = extension + result
|
|
11
|
+
}
|
|
12
|
+
return result
|
|
13
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export * from './create-temp-dir.js'
|
|
2
|
+
export * from './create-temp-dir-sync.js'
|
|
3
|
+
export * from './create-temp-file.js'
|
|
4
|
+
export * from './create-temp-file-sync.js'
|
|
5
|
+
export * from './create-temp-name.js'
|
|
6
|
+
export * from './create-temp-name-sync.js'
|
|
7
|
+
export * from './empty-dir.js'
|
|
8
|
+
export * from './empty-dir-sync.js'
|
|
9
|
+
export * from './ensure-dir.js'
|
|
10
|
+
export * from './ensure-dir-sync.js'
|
|
11
|
+
export * from './ensure-file.js'
|
|
12
|
+
export * from './ensure-file-sync.js'
|
|
13
|
+
export * from './path-exists.js'
|
|
14
|
+
export * from './path-exists-sync.js'
|
|
15
|
+
export * from './read-ndjson-file.js'
|
|
16
|
+
export * from './read-ndjson-file-sync.js'
|
|
17
|
+
export * from './read-json-file.js'
|
|
18
|
+
export * from './read-json-file-sync.js'
|
|
19
|
+
export * from './read-yaml-file.js'
|
|
20
|
+
export * from './read-yaml-file-sync.js'
|
|
21
|
+
export * from './write-json-file.js'
|
|
22
|
+
export * from './write-json-file-sync.js'
|
|
23
|
+
export * from './write-yaml-file.js'
|
|
24
|
+
export * from './write-yaml-file-sync.js'
|
|
25
|
+
export * from './write-iterable-to-file.js'
|
|
26
|
+
export * from './read-file-line-by-line.js'
|
|
27
|
+
export * from './read-file-line-by-line-sync.js'
|
|
28
|
+
export * from './move.js'
|
|
29
|
+
export * from './move-sync.js'
|
|
30
|
+
export * from './copy.js'
|
|
31
|
+
export * from './copy-sync.js'
|
|
32
|
+
export * from './remove.js'
|
|
33
|
+
export * from './remove-sync.js'
|
|
34
|
+
export * from './find-all-filenames.js'
|
|
35
|
+
export * from './find-all-dirnames.js'
|
|
36
|
+
export * from './get-long-extension.js'
|
|
37
|
+
export * from './get-short-basename.js'
|
|
38
|
+
export * from './is-directory.js'
|
|
39
|
+
export * from './is-directory-sync.js'
|
|
40
|
+
export * from './is-file.js'
|
|
41
|
+
export * from './is-file-sync.js'
|
|
42
|
+
export * from './is-readable.js'
|
|
43
|
+
export * from './is-writable.js'
|
|
44
|
+
export * from './is-sub-path-of.js'
|
|
45
|
+
export * from './checksum-file.js'
|
|
46
|
+
export * from './find-up-package-filename.js'
|
|
47
|
+
export * from './find-up-package-filename-sync.js'
|
package/src/is-file.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
// Source: https://stackoverflow.com/a/45242825/5462167
|
|
4
|
+
export function isSubPathOf(subject: string, object: string): boolean {
|
|
5
|
+
const relative = path.relative(object, subject)
|
|
6
|
+
return relative !== ''
|
|
7
|
+
&& !relative.startsWith('..')
|
|
8
|
+
&& !path.isAbsolute(relative)
|
|
9
|
+
}
|
package/src/move-sync.ts
ADDED
package/src/move.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Note: Combining pathExists with other fs functions will introduce race conditions.
|
|
5
|
+
*/
|
|
6
|
+
export function pathExistsSync(path: string): boolean {
|
|
7
|
+
try {
|
|
8
|
+
fs.accessSync(path)
|
|
9
|
+
return true
|
|
10
|
+
} catch {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Note: Combining pathExists with other fs functions will introduce race conditions.
|
|
5
|
+
*/
|
|
6
|
+
export async function pathExists(path: string): Promise<boolean> {
|
|
7
|
+
try {
|
|
8
|
+
await fs.access(path)
|
|
9
|
+
return true
|
|
10
|
+
} catch {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
|
|
3
|
+
export function* readFileLineByLineSync(
|
|
4
|
+
filename: string
|
|
5
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
6
|
+
): IterableIterator<string> {
|
|
7
|
+
const fd = fs.openSync(filename, 'r')
|
|
8
|
+
const bufferSize = 1024 * 64
|
|
9
|
+
const buffer = Buffer.alloc(bufferSize)
|
|
10
|
+
|
|
11
|
+
let bytesRead: number
|
|
12
|
+
let remainingLine = ''
|
|
13
|
+
while ((bytesRead = fs.readSync(fd, buffer, 0, bufferSize, null)) !== 0) {
|
|
14
|
+
const str = buffer.toString(encoding, 0, bytesRead)
|
|
15
|
+
const lines = str.split(/\r?\n/)
|
|
16
|
+
lines[0] = remainingLine + lines[0]
|
|
17
|
+
while (lines.length > 1) {
|
|
18
|
+
yield lines.shift()!
|
|
19
|
+
}
|
|
20
|
+
remainingLine = lines.shift()!
|
|
21
|
+
}
|
|
22
|
+
if (remainingLine) yield remainingLine
|
|
23
|
+
|
|
24
|
+
fs.closeSync(fd)
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import readline from 'readline'
|
|
3
|
+
|
|
4
|
+
export function readFileLineByLine(
|
|
5
|
+
filename: string
|
|
6
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
7
|
+
): AsyncIterable<string> {
|
|
8
|
+
const fileStream = fs.createReadStream(filename, { encoding })
|
|
9
|
+
|
|
10
|
+
return readline.createInterface({
|
|
11
|
+
input: fileStream
|
|
12
|
+
, crlfDelay: Infinity
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { filter, map } from 'iterable-operator'
|
|
2
|
+
import { readFileLineByLineSync } from './read-file-line-by-line-sync.js'
|
|
3
|
+
import { pipe } from 'extra-utils'
|
|
4
|
+
|
|
5
|
+
export function readNDJSONFileSync<T>(
|
|
6
|
+
filename: string
|
|
7
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
8
|
+
): IterableIterator<T> {
|
|
9
|
+
return pipe(
|
|
10
|
+
readFileLineByLineSync(filename, encoding)
|
|
11
|
+
, iter => filter(iter, line => line.trim() !== '')
|
|
12
|
+
, iter => map(iter, line => JSON.parse(line))
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { filterAsync, mapAsync } from 'iterable-operator'
|
|
2
|
+
import { readFileLineByLine } from './read-file-line-by-line.js'
|
|
3
|
+
import { pipe } from 'extra-utils'
|
|
4
|
+
|
|
5
|
+
export function readNDJSONFile<T>(
|
|
6
|
+
filename: string
|
|
7
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
8
|
+
): AsyncIterableIterator<T> {
|
|
9
|
+
return pipe(
|
|
10
|
+
readFileLineByLine(filename, encoding)
|
|
11
|
+
, iter => filterAsync(iter, line => line.trim() !== '')
|
|
12
|
+
, iter => mapAsync(iter, line => JSON.parse(line))
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import YAML from 'js-yaml'
|
|
3
|
+
|
|
4
|
+
export function readYAMLFileSync<T>(
|
|
5
|
+
filename: string
|
|
6
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
7
|
+
): T {
|
|
8
|
+
const text = fs.readFileSync(filename, encoding)
|
|
9
|
+
const data = YAML.load(text)
|
|
10
|
+
return data as T
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import YAML from 'js-yaml'
|
|
3
|
+
|
|
4
|
+
export async function readYAMLFile<T>(
|
|
5
|
+
filename: string
|
|
6
|
+
, encoding: BufferEncoding = 'utf-8'
|
|
7
|
+
): Promise<T> {
|
|
8
|
+
const text = await fs.readFile(filename, encoding)
|
|
9
|
+
const data = YAML.load(text)
|
|
10
|
+
return data as T
|
|
11
|
+
}
|
package/src/remove.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import stream from 'stream'
|
|
3
|
+
import { pipeline } from 'stream/promises'
|
|
4
|
+
|
|
5
|
+
export async function writeIterableToFile(
|
|
6
|
+
filename: string
|
|
7
|
+
, iterable: Iterable<string> | AsyncIterable<string>
|
|
8
|
+
): Promise<void> {
|
|
9
|
+
const readStream = stream.Readable.from(iterable)
|
|
10
|
+
const writeStream = fs.createWriteStream(filename)
|
|
11
|
+
await pipeline(readStream, writeStream)
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
|
|
3
|
+
export async function writeJSONFile(
|
|
4
|
+
filename: string
|
|
5
|
+
, data: unknown
|
|
6
|
+
, options: { spaces?: number } = {}
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
const text = JSON.stringify(data, undefined, options.spaces)
|
|
9
|
+
await fs.writeFile(filename, text, 'utf-8')
|
|
10
|
+
}
|