Skip to content

Commit

Permalink
Rework, jsonxl support, streams, verbose, dry run
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Feb 7, 2025
1 parent a89075e commit 3adcb4c
Show file tree
Hide file tree
Showing 10 changed files with 648 additions and 114 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## next

- Bumped jora to 1.0.0-beta.13
- Added support for JSONXL (snapshot9) as input encoding
- Added `--encoding` option to specify output encoding, supported JSON and JSONXL
- Added `--force` option to enforce overwritting a file
- Added `--dry-run` option
- Added `--verbose` option
- Changed behavior for writing to an existing file specified with the --output option. The operation now fails unless the --force option is used
- Removed `--sandbox` option (until re-implemented)

## 1.5.1 (July 29, 2021)

- Bumped jora-sandbox to 1.3.0
Expand Down
2 changes: 1 addition & 1 deletion bin/jora
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as cli from '../src/index.js';

try {
cli.run();
await cli.run();
} catch (e) {
// output user frendly message if cli error
if (cli.isCliError(e)) {
Expand Down
223 changes: 125 additions & 98 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,46 @@
import fs from 'fs';
import path from 'path';
import * as cli from 'clap';
import tempfile from 'tempfile';
import open from 'open';
import createSandboxFileContent from 'jora-sandbox';
// import tempfile from 'tempfile';
// import open from 'open';
// import createSandboxFileContent from 'jora-sandbox';
import jora from 'jora';
import { colorize, colorsSupported } from './colorize.js';

function readFromStream(stream, processBuffer) {
const buffer = [];

stream
.setEncoding('utf8')
.on('data', chunk => buffer.push(chunk))
.on('end', () => processBuffer(buffer.join('')));
}

function processOptions(options, args) {
const query = options.query || args[0];
const pretty = options.pretty || false;
const color = options.color && colorsSupported;
const sandbox = options.sandbox || false;
let inputFile = options.input;
let outputFile = options.output;

if (process.stdin.isTTY && process.argv.length <= 2) {
return null;
}

if (!inputFile) {
inputFile = '<stdin>';
} else {
inputFile = path.resolve(process.cwd(), inputFile);
}

if (outputFile) {
outputFile = path.resolve(process.cwd(), outputFile);
}

return {
query,
pretty,
color,
sandbox,
inputFile,
outputFile
};
}

function convertUndefinedToNull(value) {
return value === undefined ? null : value;
}
import { colorsSupported } from './colorize.js';
import { readFromFile, readFromStdin } from './read-from-stream.js';
import { createDefaultReporter, SilentWriter, StreamWriter, TTYWriter } from './reporter.js';
import { writeToDestination } from './write.js';

// function processOptions(options, args) {
// const query = options.query || args[0];
// const pretty = options.pretty || false;
// const color = options.color && colorsSupported;
// const sandbox = options.sandbox || false;
// let inputFile = options.input;
// let outputFile = options.output;

// if (process.stdin.isTTY && process.argv.length <= 2) {
// return null;
// }

// if (!inputFile) {
// inputFile = '<stdin>';
// } else {
// inputFile = path.resolve(process.cwd(), inputFile);
// }

// if (outputFile) {
// outputFile = path.resolve(process.cwd(), outputFile);
// }

// return {
// query,
// pretty,
// color,
// sandbox,
// inputFile,
// outputFile
// };
// }

function safeOperation(name, fn) {
try {
Expand All @@ -62,79 +52,116 @@ function safeOperation(name, fn) {
}
}

function prepareQuery(options) {
function prepareQuery(query) {
return safeOperation('Jora query prepare', () =>
jora(options.query || '')
jora(query || '')
);
}

function prepareData(source) {
return safeOperation('JSON parse', () =>
JSON.parse(source)
);
}

function performQuery(query, data, context) {
function performQuery(queryFn, data, context) {
return safeOperation('Query perform', () =>
query(data, context)
queryFn(data, context)
);
}

function serializeResult(data, options) {
return safeOperation('Serialize query result', () =>
JSON.stringify(data, null, options.pretty || undefined)
);
}
function normFilepath(value) {
if (typeof value !== 'string') {
return value;
}

function processStream(options) {
const inputStream = options.inputFile === '<stdin>'
? process.stdin
: fs.createReadStream(options.inputFile);

readFromStream(inputStream, async function(source) {
const data = prepareData(source);

if (options.sandbox) {
const filepath = tempfile({ extension: '.html' });
fs.writeFileSync(filepath, createSandboxFileContent(
{ name: options.inputFile, data },
options.query
));
open(filepath);
return;
}
return path.relative(process.cwd(), path.resolve(process.cwd(), value));
}

const query = prepareQuery(options);
const result = performQuery(query, data, undefined);
const serializedResult = serializeResult(convertUndefinedToNull(result), options);
function normFormat(value) {
if (encodings.includes(value)) {
return value;
}

if (options.outputFile) {
fs.writeFileSync(options.outputFile, serializedResult, 'utf-8');
} else {
console.log(options.color ? colorize(serializedResult) : serializedResult);
}
});
throw new cli.Error(`Bad value "${value}" for ${this.long} option, supported values: ${encodings.join(', ')}`);
}

const encodings = ['json', 'jsonxl'];
const command = cli.command('jora [query]')
.version('', '', '', () => console.log(JSON.parse(fs.readFileSync('./package.json')).version))
.option('-q, --query <query>', 'Jora query')
.option('-i, --input <filename>', 'Input file')
.option('-q, --query <query>', 'Jora query', normFilepath)
.option('-i, --input <filename>', 'Input file', normFilepath)
.option('-o, --output <filename>', 'Output file (outputs to stdout if not set)')
.option('-e, --encoding <encoding>', 'Output encoding: json (default), jsonxl (snapshot9)', normFormat, 'json')
.option('--dry-run', 'Don\'t output result, only report what it would have done')
.option('-f, --force', 'Force overwriting output file')
.option('-p, --pretty [indent]', 'Pretty print with optionally specified indentation (4 spaces by default)', value =>
value === undefined ? 4 : Number(value) || false
, false)
.option('--no-color', 'Suppress color output')
.option('-s, --sandbox', 'Output data and query in sandbox')
.action(function({ options: _options, args }) {
const options = processOptions(_options, args);

if (options === null) {
// .option('-s, --sandbox', 'Output data and query in sandbox')
.option('--verbose', 'Output debug info about actions')
.action(async function({ options, args }) {
if (process.stdin.isTTY && !options.input) {
command.run(['--help']);
return;
}

processStream(options);
const startTime = Date.now();
const displayInfo = options.verbose || options.dryRun;
const writer = process.stderr.isTTY
? new TTYWriter(process.stderr, !displayInfo)
: displayInfo
? new StreamWriter(process.stderr)
: new SilentWriter();
const setStageProgress = createDefaultReporter(writer);

let input;

try {
input = !options.input
? await readFromStdin(setStageProgress)
: await readFromFile(options.input, setStageProgress);
} catch (e) {
if (e.name === 'SyntaxError') {
throw new cli.Error('JSON parse error: ' + e.message);
}

throw e;
}

// if (options.sandbox) {
// const filepath = tempfile({ extension: '.html' });
// fs.writeFileSync(filepath, createSandboxFileContent(
// { name: options.inputFile, data },
// options.query
// ));
// open(filepath);
// return;
// }

const query = options.query || args[0];
const queryFn = prepareQuery(query);
const resultData = performQuery(queryFn, input.data, undefined);
const encoding = options.encoding;

writer.log();
await writeToDestination(resultData, {
encoding,
displayInfo,
color: options.color && colorsSupported,
forceRewrite: options.force,
dryRun: options.dryRun,
pretty: options.pretty,
outputPath: options.output,
inputPath: options.input
}, setStageProgress);

// if (options.output) {
// pipeline(
// stringifyChunked(result, null, options.pretty),
// fs.createWriteStream(options.outputFile)
// );
// } else {
// const serializedResult = JSON.stringify(result ?? null, null, options.pretty);
// console.log(options.color ? colorize(serializedResult) : serializedResult);
// }

setStageProgress('done', { time: Date.now() - startTime });
});

export const run = command.run.bind(command);
Expand Down
Loading

0 comments on commit 3adcb4c

Please sign in to comment.