usezap-cli
Version:
Zap CLI - Command-line interface for Zap API client
177 lines (153 loc) • 4.7 kB
JavaScript
const path = require('path');
const fs = require('fs-extra');
const fsPromises = require('fs/promises');
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 hasZapExtension = (filename) => {
if (!filename || typeof filename !== 'string') return false;
return ['zap'].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 searchForZapFiles = (dir) => {
return searchForFiles(dir, '.zap');
};
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 [];
}
};
/**
* 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,
hasZapExtension,
createDirectory,
searchForFiles,
searchForZapFiles,
stripExtension,
getSubDirectories,
sanitizeName,
validateName
};