UNPKG

@usebruno/cli

Version:

With Bruno CLI, you can now run your API collections with ease using simple command line commands.

205 lines (180 loc) 5.08 kB
const path = require('path'); const fs = require('fs-extra'); const fsPromises = require('fs/promises'); const { parse } = require('csv-parse'); const exists = async (p) => { try { await fsPromises.access(p); return true; } catch (_) { return false; } }; const isSymbolicLink = (filepath) => { try { return fs.existsSync(filepath) && fs.lstatSync(filepath).isSymbolicLink(); } catch (_) { return false; } }; const isFile = (filepath) => { try { return fs.existsSync(filepath) && fs.lstatSync(filepath).isFile(); } catch (_) { return false; } }; const isDirectory = (dirPath) => { try { return fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory(); } catch (_) { return false; } }; const normalizeAndResolvePath = (pathname) => { if (isSymbolicLink(pathname)) { const absPath = path.dirname(pathname); const targetPath = path.resolve(absPath, fs.readlinkSync(pathname)); if (isFile(targetPath) || isDirectory(targetPath)) { return path.resolve(targetPath); } console.error(`Cannot resolve link target "${pathname}" (${targetPath}).`); return ''; } return path.resolve(pathname); }; const writeFile = async (pathname, content) => { try { fs.writeFileSync(pathname, content, { encoding: 'utf8' }); } catch (err) { return Promise.reject(err); } }; const hasJsonExtension = (filename) => { if (!filename || typeof filename !== 'string') return false; return ['json'].some((ext) => filename.toLowerCase().endsWith(`.${ext}`)); }; const hasBruExtension = (filename) => { if (!filename || typeof filename !== 'string') return false; return ['bru'].some((ext) => filename.toLowerCase().endsWith(`.${ext}`)); }; const createDirectory = async (dir) => { if (!dir) { throw new Error(`directory: path is null`); } if (fs.existsSync(dir)) { throw new Error(`directory: ${dir} already exists`); } return fs.mkdirSync(dir); }; const searchForFiles = (dir, extension) => { let results = []; const files = fs.readdirSync(dir); for (const file of files) { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { results = results.concat(searchForFiles(filePath, extension)); } else if (path.extname(file) === extension) { results.push(filePath); } } return results; }; const searchForBruFiles = (dir) => { return searchForFiles(dir, '.bru'); }; const stripExtension = (filename = '') => { return filename.replace(/\.[^/.]+$/, ''); }; const getSubDirectories = (dir) => { try { const files = fs.readdirSync(dir); const subDirectories = files .filter((file) => { return fs.lstatSync(path.join(dir, file)).isDirectory(); }) .sort(); return subDirectories; } catch (err) { return []; } }; const parseCSV = async (csvData) => { // Remove BOM character if present before parsing if (typeof csvData === 'string') { csvData = csvData.replace(/^\uFEFF/, ''); } return new Promise((resolve, reject) => { parse( csvData, { columns: true, skip_empty_lines: true, trim: true }, (err, data) => { if (err) { console.error(err); reject(err); } else { resolve(data); } } ); }); } /** * Sanitizes a filename to make it safe for filesystem operations * * @param {string} name - The name to sanitize * @returns {string} - The sanitized name */ const sanitizeName = (name) => { if (!name) return ''; const invalidCharacters = /[<>:"/\\|?*\x00-\x1F]/g; return name .replace(invalidCharacters, '-') // replace invalid characters with hyphens .replace(/^[.\s-]+/, '') // remove leading dots, hyphens and spaces .replace(/[.\s]+$/, ''); // remove trailing dots and spaces (keep trailing hyphens) }; /** * Validates if a name is valid for the filesystem * * @param {string} name - The name to validate * @returns {boolean} - True if the name is valid, false otherwise */ const validateName = (name) => { if (!name) return false; const reservedDeviceNames = /^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])$/i; const firstCharacter = /^[^.\s\-\<>:"/\\|?*\x00-\x1F]/; // no dot, space, or hyphen at start const middleCharacters = /^[^<>:"/\\|?*\x00-\x1F]*$/; // no invalid characters const lastCharacter = /[^.\s]$/; // no dot or space at end, hyphen allowed if (name.length > 255) return false; // max name length if (reservedDeviceNames.test(name)) return false; // windows reserved names return ( firstCharacter.test(name) && middleCharacters.test(name) && lastCharacter.test(name) ); }; module.exports = { exists, isSymbolicLink, isFile, isDirectory, normalizeAndResolvePath, writeFile, hasJsonExtension, hasBruExtension, createDirectory, searchForFiles, searchForBruFiles, stripExtension, getSubDirectories, parseCSV, sanitizeName, validateName };