Skip to content

Commit

Permalink
A bunch of changes to fs functions, removing a few of them too
Browse files Browse the repository at this point in the history
  • Loading branch information
msikma committed Sep 5, 2021
1 parent 830dc27 commit 6a38423
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 147 deletions.
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -40,7 +40,6 @@
"got": "^9.6.0",
"lodash": "^4.17.15",
"markdown-escape": "^1.0.2",
"mkdirp": "^0.5.1",
"standard-slugify": "^2.0.1",
"string-width": "^4.2.0",
"turndown": "^4.0.0",
Expand Down
107 changes: 44 additions & 63 deletions src/util/fs.js
@@ -1,19 +1,19 @@
// dada-cli-tools - Libraries for making CLI programs <https://github.com/msikma/dada-cli-tools>
// © MIT license

import path from 'path'
import os from 'os'
import filesize from 'filesize'
import { promises as fs, constants, readFileSync } from 'fs'
import { homedir } from 'os'
import { parse, basename } from 'path'
import mkdirp from 'mkdirp'
import process from 'process'

/** Max number of times we'll try to figure out a different filename in writeFileSafely(). */
const MAX_FILENAME_RETRIES = 99

/** Returns the formatted size of a file. */
export const formatFilesize = async (path, opts = {}) => {
const stat = await fs.stat(path)
export const formatFilesize = async (filepath, opts = {}) => {
const stat = await fs.stat(filepath)
return formatBytes(stat.size, opts)
}

Expand All @@ -23,36 +23,34 @@ export const formatBytes = (bytes, opts = {}) => {
}

/** This modifies the tilde (~) to point to the user's home. */
export const resolveTilde = (pathStr) => {
let path = pathStr.trim()
export const resolveTilde = (filepath, deslash = true, resolve = true, homedir = os.homedir()) => {
let fpath = filepath.trim()

// The tilde is only valid if it's at the start of the string.
if (path.startsWith('~')) {
path = homedir() + path.slice(1)
if (fpath.startsWith('~')) {
fpath = homedir + fpath.slice(1)
}
return path
}

/** Splits up a filename into the basename (including the path) and file extension. */
export const splitFilename = filename => {
const fn = String(filename)
const bits = fn.split('.')
if (bits.length === 1) {
return { basename: fn, extension: '' }
// Optionally, fully resolve the path (which also deslashes it).
if (resolve) {
return path.resolve(fpath)
}
const basename = bits.slice(0, -1).join('.')
const extension = bits.slice(-1)[0]
return {
basename,
extension

// Optionally, deslash the path (remove trailing / character).
if (deslash) {
if (fpath === '/') return fpath
return fpath.slice(-1) === '/' ? fpath.slice(0, -1) : fpath
}

return fpath
}

/**
* Changes a file's extension.
*/
export const changeExtension = (filename, ext) => {
const { basename } = splitFilename(filename)
return `${basename}.${ext}`
const parsed = path.parse(filename)
return `${parsed.name}.${ext}`
}

/**
Expand All @@ -63,12 +61,12 @@ export const changeExtension = (filename, ext) => {
* determining the new name once, rather than checking each possibility.
*/
export const getSafeFilename = async (target, separator = '', allowSafeFilename = true, limit = MAX_FILENAME_RETRIES) => {
const { basename, extension } = splitFilename(target)
const { name, ext } = path.parse(target)

let targetName = { basename, suffix: 0 }, targetNameStr
let targetName = { name, suffix: 0 }, targetNameStr
while (true) {
// Increment the name suffix and see if we can create this file.
targetNameStr = `${targetName.basename}${separator}${targetName.suffix > 0 ? targetName.suffix : ''}.${extension}`
targetNameStr = `${targetName.name}${separator}${targetName.suffix > 0 ? targetName.suffix : ''}.${ext}`

if (await fileExists(targetNameStr)) {
targetName.suffix += 1
Expand Down Expand Up @@ -113,64 +111,47 @@ export const writeFileSafely = async (target, content, options) => {
}

/** Checks whether we can access (read, modify) a path. */
export const canAccess = async (path) => {
export const canAccess = async (filepath) => {
try {
// If access is possible, this will return null. Failure will throw.
return await fs.access(path, constants.F_OK | constants.W_OK) == null
return await fs.access(filepath, constants.F_OK | constants.W_OK) == null
}
catch (err) {
return false
}
}

/** Checks whether we can access (read) a file. As canAccess(). */
export const fileExists = async (path) => {
export const fileExists = async (filepath) => {
try {
return await fs.access(path, constants.F_OK) == null
return await fs.access(filepath, constants.F_OK) == null
}
catch (err) {
return false
}
}

/** Ensures that a directory exists. Returns a promise. */
export const ensureDir = (path) => new Promise((resolve, reject) => (
mkdirp(path, (err) => {
if (err) return reject(err)
return resolve(true)
})
))

/** Ensures that a directory exists. Returns a promise resolving to a boolean. */
export const ensureDirBool = async path => {
try {
await ensureDir(path)
return true
}
catch (_) {
return false
}
}

/** Loads a JSON file synchronously. */
export const readJSONSync = (path, encoding = 'utf8') => {
return JSON.parse(readFileSync(path, encoding))
}

/** Loads a JSON file asynchronously. */
export const readJSON = async (path, encoding = 'utf8') => {
const data = await fs.readFile(path, encoding)
const content = JSON.parse(data)
return content
export const ensureDir = async (filepath) => {
return !!(await fs.mkdir(filepath, { recursive: true }))
}

/** Returns the directory name for a full path. */
export const dirName = (path) => {
const info = parse(path)
return info.dir
/** Loads a JSON file, either sync or async. */
export const readJSON = (filepath, async = true, encoding = 'utf8') => {
if (async) {
return readJSONAsync(filepath, encoding)
}
return JSON.parse(readFileSync(filepath, encoding))
}

/** Returns the name of the currently running program. */
export const progName = () => (
basename(process.argv[1])
path.basename(process.argv[1])
)

/** Loads a JSON file async. */
const readJSONAsync = async (filepath, encoding = 'utf8') => {
const data = await fs.readFile(filepath, encoding)
const content = JSON.parse(data)
return content
}

0 comments on commit 6a38423

Please sign in to comment.