@junaidatari/json2ts
Version:
Convert JSON objects to TypeScript interfaces automatically.
227 lines (209 loc) • 7.1 kB
JavaScript
#!/usr/bin/env node
/**
* CLI interface for the JSON to TypeScript Converter.
*
* This script provides a command-line interface for generating TypeScript interfaces
* from JSON data. It supports various configuration options for customizing the generation process.
*/
const path = require('node:path');
const fs = require('node:fs');
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
// classes
const { JsonToTsConverter, JsonToFlattenedTsConverter } = require('../index.js');
/**
* Command line arguments configuration using yargs
* @type {import('yargs').Argv}
*/
const argv = yargs(hideBin(process.argv))
.parserConfiguration({
'parse-numbers': false,
})
.version('0.0.5')
.usage('Usage: json2ts -f input.json -o output.ts')
.option('file', {
description: 'Path to the JSON file to be converted to TypeScript interfaces',
type: 'string',
alias: 'f',
})
.option('text', {
description: 'Raw JSON string to be converted to TypeScript interfaces',
type: 'string',
alias: 't',
})
.option('output', {
description: 'Path where the generated TypeScript interface file will be saved',
type: 'string',
alias: 'o',
})
.option('name', {
description: 'Name for the root TypeScript interface (default: RootObject)',
type: 'string',
alias: 'n',
})
.option('export', {
description: 'Export type for generated interfaces: "a" (all: default), "r" (root), or "n" (none)',
type: 'string',
choices: ['a', 'r', 'n'],
alias: 'e',
})
.option('property-case', {
description: 'Transform property names to a specific case format. Options:\n - c (camelCase)\n - l (lower_snake_case)\n - o (preserve original)\n - p (PascalCase)\n - u (UPPER_SNAKE_CASE)\n - k (kebab-case)',
type: 'string',
choices: ['c', 'l', 'o', 'p', 'u', 'k'],
alias: 'pc',
})
.option('flat', {
description: 'Generate a single flattened interface instead of multiple interfaces',
type: 'boolean',
alias: 'l',
})
.option('strict', {
description: 'Generate strict TypeScript types with exact property matching',
type: 'boolean',
alias: 's',
})
.option('readonly', {
description: 'Make all generated properties readonly',
type: 'boolean',
alias: 'r',
})
.option('optional', {
description: 'Make all generated properties optional',
type: 'boolean',
alias: 'op',
})
.default({
file: null,
text: null,
name: 'RootObject',
'export': 'r',
'property-case': 'o',
output: null,
})
.showHelpOnFail(true, 'Use --help for usage')
.completion('completion', 'Generate completion script for your shell')
.help()
.parse();
/**
* Prints the ASCII art header to the console
* @param {...any} l - Additional lines to print after the header
*/
function printHeader (...l) {
console.log(` █████ ████████ ███████████
░░███ ███░░░░███░█░░░███░░░█
░███ █████ ██████ ████████ ░░░ ░███░ ░███ ░ █████
░███ ███░░ ███░░███░░███░░███ ███████ ░███ ███░░
░███ ░░█████ ░███ ░███ ░███ ░███ ███░░░░ ░███ ░░█████
███ ░███ ░░░░███░███ ░███ ░███ ░███ ███ █ ░███ ░░░░███
░░████████ ██████ ░░██████ ████ █████░██████████ █████ ██████
░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░░ \n`);
console.log(...l);
}
function toCaseType (alias) {
switch (alias) {
case 'c':
return 'camel';
case 'l':
return 'lower_snake';
case 'p':
return 'pascal';
case 'u':
return 'upper_snake';
case 'k':
return 'kebab';
default:
return 'original';
}
}
/**
* Converts export type character to full string representation
* @param {string} type - The export type character ('a', 'r', or 'n')
* @returns {'all' | 'root' | 'none'} The full export type string
*/
function toExportType (type) {
switch (type) {
case 'a':
return 'all';
case 'r':
return 'root';
default:
return 'none';
}
}
/**
* Reads input from stdin if available
* @returns {Promise<string|null>} - Promise that resolves with the stdin data or null
*/
async function readStdin () {
if (process.stdin.isTTY) {
return null;
}
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
data += chunk;
});
process.stdin.on('end', () => {
resolve(data);
});
process.stdin.on('error', reject);
});
}
/**
* Main execution function
* Handles command-line arguments, processes JSON input, and generates TypeScript interfaces
*/
(async function() {
printHeader();
const dir = path.resolve(process.cwd());
const flat = Object.hasOwn(argv, 'flat');
const propertyCase = toCaseType(argv['property-case']);
const strict = Object.hasOwn(argv, 'strict');
const readonlyProperties = Object.hasOwn(argv, 'readonly');
const optionalProperties = Object.hasOwn(argv, 'optional');
let jsonData;
try {
if (argv.file) {
jsonData = fs.readFileSync(argv.file, { encoding: 'utf-8' });
} else if (argv.text) {
jsonData = argv.text;
} else {
// Try to read from stdin if no file or text is provided
jsonData = await readStdin();
}
if (!jsonData) {
console.error('Missing required arguments. Use --help for usage information.');
process.exit(1);
}
} catch (error) {
console.error('Error parsing JSON:', error.message);
process.exitCode = 1;
return;
}
const converter = flat ? JsonToFlattenedTsConverter : JsonToTsConverter;
const typescriptCode = converter.convert(
jsonData, argv.name || 'RootObject', toExportType(argv['export']), {
propertyCase,
strict,
readonlyProperties,
optionalProperties,
},
) + `\n`;
if (argv['output']) {
const outputFile = argv.output || path.join(dir, 'output.ts');
try {
fs.writeFileSync(outputFile, typescriptCode);
console.log(`Successfully wrote TypeScript definitions to: ${outputFile}`);
} catch (error) {
console.error('Error writing output file:', error.message);
process.exitCode = 1;
}
} else {
console.log(typescriptCode);
}
})().catch((err) => {
console.error(err.stack || err.message || err);
process.exitCode = 1;
});