create-envexample
Version:
A simple CLI tool to create .env.example files from your .env files, making it easier to share environment variable structures without exposing sensitive data
170 lines (150 loc) • 5.49 kB
JavaScript
import { time } from "console";
import * as fs from "fs/promises";
import * as path from "path";
import { fileURLToPath } from 'url';
// Parse command-line arguments
const parseArgs = (args) => {
const options = {
sort: false,
preserveComments: true,
noComments: false,
outputPath: path.join(process.cwd(), ".env.example"),
delimiter: "=",
verbose: false,
};
for (let i = 2; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '-o':
case '--output':
options.outputPath = args[++i];
break;
case '--sort':
options.sort = true;
options.preserveComments = false;
break;
case '--no-comments':
options.noComments = true;
break;
case '--delimiter':
options.delimiter = args[++i] || "=";
break;
case '--verbose':
options.verbose = true;
break;
case '-h':
case '--help':
showHelp();
process.exit(0);
}
}
return options;
};
// Display help information
const showHelp = () => {
console.log(`
Usage: create-envexample [options]
Options:
-o, --output <path> Specify output file path (default: .env.example)
--sort Sort both variables and comments
--no-comments Remove all comments from output
--delimiter <char> Specify custom delimiter (default: =)
--verbose Enable verbose mode for debugging
-h, --help Show help information
Examples:
$ create-envexample
$ create-envexample -o custom.env.example
$ create-envexample --sort
$ create-envexample --no-comments
$ create-envexample --delimiter :
`);
};
// Create .env.example file
const createEnvExample = async (options = {}) => {
try {
const envFilePath = path.join(process.cwd(), ".env");
const outputPath = options.outputPath;
// Check if .env file exists
try {
await fs.access(envFilePath);
} catch {
throw new Error(".env file not found in current directory");
}
// Read .env file content
const data = await fs.readFile(envFilePath, "utf8");
if (!data.trim()) {
throw new Error(".env file is empty");
}
// Process each line
const lines = data.split("\n");
const processedLines = [];
const ignoredKeys = await loadIgnoredKeys();
for (const line of lines) {
const trimmedLine = line.trim();
// Handle comments
if (trimmedLine.startsWith("#")) {
if (!options.noComments) {
processedLines.push(line);
}
continue;
}
// Handle key-value pairs
if (trimmedLine && !ignoredKeys.has(trimmedLine.split(options.delimiter)[0]?.trim())) {
const [key] = line.split(options.delimiter);
if (key && key.trim()) {
processedLines.push(`${key.trim()}${options.delimiter}`);
}
}
}
// Sort lines if required
const sortedLines = options.sort
? processedLines.sort((a, b) => {
const aIsComment = a.trim().startsWith("#");
const bIsComment = b.trim().startsWith("#");
if (!options.preserveComments) {
return a.localeCompare(b);
}
if (aIsComment && !bIsComment) return -1;
if (!aIsComment && bIsComment) return 1;
if (aIsComment && bIsComment) return 0;
return a.localeCompare(b);
})
: processedLines;
// Add timestamp header
const timestamp = new Date().toUTCString("en-US", { timeZone: "UTC" });
const content = [
...sortedLines,
options.noComments ? '' : `# Generated on: ${timestamp} by create-envexample`,
]
.filter(Boolean)
.join("\n");
// Write to output file
await fs.writeFile(outputPath, content);
console.log(`Environment example file created successfully at ${outputPath}`);
} catch (err) {
console.error(`Error: ${err.message}`);
process.exit(1);
}
};
// Load ignored keys from .envignore file
const loadIgnoredKeys = async () => {
const ignoreFilePath = path.join(process.cwd(), ".envignore");
try {
await fs.access(ignoreFilePath);
const data = await fs.readFile(ignoreFilePath, "utf8");
return new Set(data.split("\n").map((line) => line.trim()).filter(Boolean));
} catch {
return new Set(); // No .envignore file found
}
};
// Replace the CommonJS-style module check with ES modules version
const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);
if (isMainModule) {
const options = parseArgs(process.argv);
if (options.verbose) {
console.log("Options:", options);
}
createEnvExample(options).catch(console.error);
}
export { createEnvExample };