2025-04-21 18:20:52 +02:00
|
|
|
/*
|
|
|
|
* Various helper methods to handle file system access
|
|
|
|
*/
|
|
|
|
import fs from 'fs'
|
|
|
|
import path from 'path'
|
|
|
|
import { glob } from 'glob'
|
2025-04-26 15:29:25 +02:00
|
|
|
import mustache from 'mustache'
|
2025-04-21 18:20:52 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Re-export these
|
|
|
|
*/
|
2025-04-26 15:29:25 +02:00
|
|
|
export { fs, path, glob, mustache }
|
2025-04-21 18:20:52 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The monorepo root folder
|
|
|
|
*/
|
|
|
|
export const root = path.resolve(path.basename(import.meta.url), '..')
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies a file
|
|
|
|
*
|
|
|
|
* @param {arrau} src - Source file
|
|
|
|
* @param {array} dst - Destination file
|
|
|
|
* @param {object} options - Options for the fs.cp call in NodeJS
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export async function cp(src, dst, options = {}) {
|
|
|
|
if (!Array.isArray(src)) src = [src]
|
|
|
|
if (!Array.isArray(dst)) dst = [dst]
|
|
|
|
try {
|
|
|
|
await fs.promises.cp(path.resolve(root, ...src), path.resolve(root, ...dst), options)
|
|
|
|
} catch (err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a file
|
|
|
|
*
|
|
|
|
* @param {array} file - Path to the file to remove
|
|
|
|
* @param {object} options - Options for NodeJS' rm method
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export async function rm(file, options = { force: true }) {
|
|
|
|
if (!Array.isArray(file)) file = [file]
|
|
|
|
try {
|
|
|
|
await fs.promises.rm(path.resolve(root, ...file), options)
|
|
|
|
} catch (err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a folder from disk with an optional glob pattern
|
|
|
|
*
|
|
|
|
* @param {string} (relative) path to the file to read
|
|
|
|
* @param {funtion} onError - a method to call on error
|
|
|
|
*
|
|
|
|
* @return {string} File contents, or false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function globDir(
|
|
|
|
folderPath, // The (relative) path to the folder
|
|
|
|
pattern = '**/*' // Glob pattern to match
|
|
|
|
) {
|
|
|
|
if (!Array.isArray(folderPath)) folderPath = [folderPath]
|
|
|
|
let list = []
|
|
|
|
try {
|
|
|
|
list = await glob(path.resolve(root, ...folderPath) + '/' + pattern)
|
|
|
|
} catch (err) {
|
|
|
|
if (err) console.log(err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a directory/folder
|
|
|
|
*
|
|
|
|
* @param {string} dirPath - (relative) path to the folder to create
|
|
|
|
* @param {funtion} onError - a method to call on error
|
|
|
|
*
|
|
|
|
* @return {string} File contents, or false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function mkdir(
|
|
|
|
dirPath, // The (relative) path to the folder to create
|
|
|
|
onError // Method to run on error
|
|
|
|
) {
|
|
|
|
if (!Array.isArray(dirPath)) dirPath = [dirPath]
|
|
|
|
let dir
|
|
|
|
try {
|
|
|
|
dir = path.resolve(root, ...dirPath)
|
|
|
|
await fs.promises.mkdir(dir, { recursive: true })
|
|
|
|
} catch (err) {
|
|
|
|
if (onError) onError(err)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a file from disk
|
|
|
|
*
|
|
|
|
* @param {string} (relative) path to the file to read
|
|
|
|
* @param {funtion} onError - a method to call on error
|
|
|
|
*
|
|
|
|
* @return {string} File contents, or false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function readFile(
|
|
|
|
filePath, // The (relative) path to the file
|
|
|
|
onError, // Method to run on error
|
|
|
|
binary = false
|
|
|
|
) {
|
|
|
|
if (!Array.isArray(filePath)) filePath = [filePath]
|
|
|
|
let content, file
|
|
|
|
try {
|
|
|
|
file = path.resolve(root, ...filePath)
|
|
|
|
content = await fs.promises.readFile(file, binary ? undefined : 'utf-8')
|
|
|
|
} catch (err) {
|
|
|
|
if (onError) onError(err)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a JSON file from disk and parses it
|
|
|
|
*
|
|
|
|
* @param {string} path - (relative) path to the file to read
|
|
|
|
* @param {string} onError - a string to log on error rather than the default
|
|
|
|
*
|
|
|
|
* @return {string} File contents, or false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function readJsonFile(
|
|
|
|
filePath, // The (relative) path to the file
|
|
|
|
onError // Method to run on error
|
|
|
|
) {
|
|
|
|
if (!Array.isArray(filePath)) filePath = [filePath]
|
|
|
|
let content
|
|
|
|
try {
|
|
|
|
content = await readFile(path.join(root, ...filePath), onError, true)
|
|
|
|
content = JSON.parse(content)
|
|
|
|
} catch (err) {
|
|
|
|
if (onError) onError(err)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
|
2025-04-26 15:29:25 +02:00
|
|
|
/**
|
|
|
|
* Templates out a file with mustache
|
|
|
|
*
|
|
|
|
* @param {array} from - The source template file
|
|
|
|
* @param {array} to - The destination file
|
|
|
|
* @param {object] data - Substitutions for the template
|
|
|
|
*/
|
|
|
|
export async function templateOut(from, to, data) {
|
|
|
|
if (!Array.isArray(from)) from = [from]
|
|
|
|
if (!Array.isArray(to)) to = [to]
|
|
|
|
try {
|
|
|
|
const src = await readFile(from)
|
|
|
|
await writeFile(to, mustache.render(src, data))
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2025-04-21 18:20:52 +02:00
|
|
|
/**
|
|
|
|
* Writes a file to disk
|
|
|
|
*
|
|
|
|
* @param {string} filePath - (relative) path to the file to write
|
|
|
|
* @param {string} data - the data to write to disk
|
|
|
|
* @param {function} log - a logger instance (or false)
|
|
|
|
* @param {octal} mode - a mode for chmod
|
|
|
|
*
|
|
|
|
* @return {bool} true of success, false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function writeFile(
|
|
|
|
filePath, // The (relative) path to the file
|
|
|
|
data, // The data to write to disk
|
|
|
|
log = false,
|
|
|
|
mode = 0o666
|
|
|
|
) {
|
|
|
|
if (!Array.isArray(filePath)) filePath = [filePath]
|
|
|
|
let file
|
|
|
|
try {
|
|
|
|
file = path.resolve(root, ...filePath)
|
|
|
|
await fs.promises.mkdir(path.dirname(file), { recursive: true })
|
|
|
|
await fs.promises.writeFile(file, data)
|
|
|
|
await fs.promises.chmod(file, mode)
|
|
|
|
} catch (err) {
|
|
|
|
if (log) log.warn(err, `Failed to write file: ${file}`)
|
|
|
|
else console.log(`Failed to write file: ${file}`)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes a JSON file to disk
|
|
|
|
*
|
|
|
|
* @param {string} filePath - (relative) path to the file to write
|
|
|
|
* @param {string} data - the data to write to disk as a Javascript object
|
|
|
|
*
|
|
|
|
* @return {bool} true of success, false in case of trouble
|
|
|
|
*/
|
|
|
|
export async function writeJsonFile(filePath, data, log, mode) {
|
|
|
|
return await writeFile(filePath, JSON.stringify(data, null, 2), log, mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the contents of a directory (non-recursive)
|
|
|
|
*
|
|
|
|
* @param {string} dirPath - (relative) path to the directory to read
|
|
|
|
* @param {funtion} onError - a method to call on error
|
|
|
|
*/
|
|
|
|
export async function readDirectory(dirPath, onError) {
|
|
|
|
if (!Array.isArray(dirPath)) dirPath = [dirPath]
|
|
|
|
let files
|
|
|
|
try {
|
|
|
|
const dir = path.resolve(root, ...dirPath)
|
|
|
|
files = await fs.promises.readdir(dir)
|
|
|
|
} catch (err) {
|
|
|
|
if (onError) onError(err)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return files
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies a folder recursively
|
|
|
|
*
|
|
|
|
* @param {string} srcDir - The source folder to copy
|
|
|
|
* @param {string} dstDir - The destination folder
|
|
|
|
*/
|
|
|
|
export async function copyFolderRecursively(srcDir, dstDir) {
|
|
|
|
/*
|
|
|
|
* Ensure target folder exists
|
|
|
|
*/
|
|
|
|
await mkdir(dstDir)
|
|
|
|
/*
|
|
|
|
* Glob all files to copy
|
|
|
|
* Generate relative from and to arrays
|
|
|
|
*/
|
|
|
|
const files = (await globDir(srcDir, '**/*'))
|
|
|
|
.sort()
|
|
|
|
.map((target) => target.split(root).pop().slice(1).split('/'))
|
|
|
|
.map((target) => ({
|
|
|
|
from: target,
|
|
|
|
to: [...dstDir, ...target.slice(srcDir.length)],
|
|
|
|
}))
|
|
|
|
/*
|
|
|
|
* Copy files, create folders
|
|
|
|
*/
|
|
|
|
for (const op of files) {
|
|
|
|
const stat = fs.statSync(path.join(root, ...op.from))
|
|
|
|
if (stat.isDirectory()) await mkdir(op.to)
|
|
|
|
else await cp(op.from, op.to)
|
|
|
|
}
|
|
|
|
}
|