Skip to content

Commit

Permalink
xtended the --query option to accept a file path
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Feb 7, 2025
1 parent 9f2d703 commit 7891d17
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Added `--force` option to enforce overwritting a file
- Added `--dry-run` option
- Added `--verbose` option
- Extended the `--query` option to accept a file path containing a Jora query, the file must have a `.jora` extension
- 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)

Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,19 @@ jora version <package.json
# get all top level dependencies count
jora -i package.json -q '(dependencies.keys() + devDependencies.keys()).size()'
# find packages with more than a single version
npm ls --json | jora "
..(dependencies.entries().({ name: key, ...value }))
.group(=>name, =>version)
.({ name: key, versions: value })
.[versions.size() > 1]
";
# find packages with more than a single version (run query from a file)
npm ls --json | jora find-multi-version-packages.jora
```
> The content of `find-multi-version-packages.jora` may be as follows:
>
> ```js
> ..(dependencies.entries().({ name: key, ...value }))
> .group(=>name, =>version)
> .({ name: key, versions: value })
> .[versions.size() > 1]
> ```
## Caveats
`jora-cli` takes a valid JSON and produce a valid JSON as a result. However, `jora` language could produce some values that incompatable with JSON, such values are transforming:
Expand Down
20 changes: 18 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ function safeOperation(name, fn) {
}
}

function extractQuery(queryOrFilename) {
const maybeFilename = normFilepath(queryOrFilename);

if (typeof maybeFilename === 'string' && path.extname(maybeFilename) === '.jora') {
if (!fs.existsSync(maybeFilename)) {
throw new cli.Error(`ERROR! No such file or directory: ${maybeFilename}`);
}

return safeOperation('Read jora query from file', () =>
fs.readFileSync(maybeFilename, 'utf8')
);
}

return queryOrFilename;
}

function prepareQuery(query) {
return safeOperation('Jora query prepare', () =>
jora(query || '')
Expand Down Expand Up @@ -83,7 +99,7 @@ function normFormat(value) {
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', normFilepath)
.option('-q, --query <query>', 'Jora query or path to a query file with extension .jora', 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')
Expand Down Expand Up @@ -134,7 +150,7 @@ const command = cli.command('jora [query]')
// return;
// }

const query = options.query || args[0];
const query = extractQuery(options.query || args[0]);
const queryFn = prepareQuery(query);
const resultData = performQuery(queryFn, input.data, undefined);
const encoding = options.encoding;
Expand Down
53 changes: 53 additions & 0 deletions test/fixture.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "jora-cli",
"version": "2.0.0",
"description": "Command line interface for Jora",
"keywords": [
"cli",
"jora"
],
"maintainers": [
{
"name": "Roman Dvornov",
"email": "[email protected]",
"github-username": "lahmatiy"
}
],
"license": "MIT",
"repository": "discoveryjs/jora-cli",
"bin": {
"jora": "./bin/jora"
},
"type": "module",
"main": "./src/index.js",
"scripts": {
"test": "mocha --reporter progress",
"lint": "eslint src/*.js test/*.js",
"lint-and-test": "npm run lint && npm test",
"coverage": "c8 --reporter=lcovonly npm test",
"prepublishOnly": "npm run lint-and-test"
},
"dependencies": {
"@discoveryjs/json-ext": "^0.6.2",
"ansi-styles": "^6.2.1",
"clap": "^3.1.1",
"jora": "1.0.0-beta.13",
"jora-sandbox": "^1.3.0",
"open": "^10.1.0",
"supports-color": "^9.4.0",
"tempfile": "^5.0.0"
},
"devDependencies": {
"c8": "^10.1.2",
"eslint": "^8.57.1",
"mocha": "^10.7.3"
},
"engines": {
"node": ">=18.0.0"
},
"files": [
"bin",
"utils",
"index.js"
]
}
1 change: 1 addition & 0 deletions test/query.jora
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version
66 changes: 41 additions & 25 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import { fileURLToPath } from 'url';
import { parseJsonxl, style } from './helpers.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkgJson = path.join(__dirname, '../package.json');
const pkgJsonData = JSON.parse(fs.readFileSync(pkgJson));
const fixture = fs.readFileSync(path.join(__dirname, 'color-fixture.json'), 'utf8');
const queryFilename = path.join(__dirname, 'query.jora');
const fixtureFilename = path.join(__dirname, 'fixture.json');
const fixture = fs.readFileSync(fixtureFilename, 'utf8');
const fixtureData = JSON.parse(fixture);
const fixtureJsonxlFilename = path.join(__dirname, './fixture.jsonxl');
const fixtureJsonxlFilename = path.join(__dirname, 'fixture.jsonxl');
const fixtureJsonxl = fs.readFileSync(fixtureJsonxlFilename);
const fixtureJsonxlData = parseJsonxl(fixtureJsonxl);
const fixtureJsonxlJson = JSON.stringify(fixtureJsonxlData);
const fixtureExpected = fs.readFileSync(path.join(__dirname, 'color-fixture.expected'), 'utf8').trim();
const fixtureExpectedCompact = fs.readFileSync(path.join(__dirname, 'color-fixture.compact.expected'), 'utf8').trim();
const colorFixture = fs.readFileSync(path.join(__dirname, 'color-fixture.json'), 'utf8');
const colorFixtureData = JSON.parse(colorFixture);
const colorFixtureExpected = fs.readFileSync(path.join(__dirname, 'color-fixture.expected'), 'utf8').trim();
const colorFixtureExpectedCompact = fs.readFileSync(path.join(__dirname, 'color-fixture.compact.expected'), 'utf8').trim();
const envWithForceColors = Object.assign({}, process.env, {
FORCE_COLOR: true
});
Expand Down Expand Up @@ -116,20 +118,34 @@ describe('non-JSON primitives', () => {

it('should output version', () =>
run('-v')
.output(pkgJsonData.version)
.output(fixtureData.version)
);

it('should read content from stdin if no file specified', () =>
run('version')
.input(JSON.stringify(pkgJsonData))
.output(JSON.stringify(pkgJsonData.version))
.input(fixture)
.output(JSON.stringify(fixtureData.version))
);

it('should read from file', () =>
run('-i', pkgJson, '-q', 'version')
.output(JSON.stringify(pkgJsonData.version))
run('-i', fixtureFilename, '-q', 'version')
.output(JSON.stringify(fixtureData.version))
);

describe('query from a file', () => {
it('as arg', () =>
run(queryFilename)
.input(fixture)
.output(JSON.stringify(fixtureData.version))
);

it('as option', () =>
run('-q', queryFilename)
.input(fixture)
.output(JSON.stringify(fixtureData.version))
);
});

describe('jsonxl', () => {
it('should read from jsonxl file', () =>
run('-i', fixtureJsonxlFilename, '-q', 'version')
Expand Down Expand Up @@ -158,14 +174,14 @@ describe('jsonxl', () => {
describe('pretty print', function() {
it('indentation should be 4 spaces by default', () =>
run('dependencies.keys()', '-p')
.input(JSON.stringify(pkgJsonData))
.output(JSON.stringify(Object.keys(pkgJsonData.dependencies), null, 4))
.input(fixture)
.output(JSON.stringify(Object.keys(fixtureData.dependencies), null, 4))
);

it('indentation should be as specified', () =>
run('dependencies.keys()', '-p', '3')
.input(JSON.stringify(pkgJsonData))
.output(JSON.stringify(Object.keys(pkgJsonData.dependencies), null, 3))
.input(fixture)
.output(JSON.stringify(Object.keys(fixtureData.dependencies), null, 3))
);
});

Expand Down Expand Up @@ -194,10 +210,10 @@ describe('errors', function() {

// FIXME: skip colored output tests for Windows since no way currently to pass custom env variable (FORCE_COLOR) to a child process
// FIXME: --color temporary disabled
(false && process.platform !== 'win32' ? describe : describe.skip)('colored output', function() {
(process.platform !== 'win32' ? describe : describe.skip)('colored output', function() {
const tests = {
string: style('STRING', JSON.stringify(fixtureData.string)),
number: style('NUMBER', fixtureData.number),
string: style('STRING', JSON.stringify(colorFixtureData.string)),
number: style('NUMBER', colorFixtureData.number),
emptyArray: style('LEFT_BRACKET', '[', 'RIGHT_BRACKET', ']'),
emptyObject: style('LEFT_BRACE', '{', 'RIGHT_BRACE', '}'),
singlePropObject: style('LEFT_BRACE', '{', 'STRING_KEY', '"', 'STRING_KEY_CONTENT', 'foo', 'STRING_KEY', '"', 'COLON', ':', 'STRING', '"test"', 'RIGHT_BRACE', '}'),
Expand All @@ -209,7 +225,7 @@ describe('errors', function() {
Object.keys(tests).forEach(key => {
it(key, () =>
runWithForceColors(key)
.input(fixture)
.input(colorFixture)
.output(tests[key])
);
});
Expand All @@ -230,21 +246,21 @@ describe('errors', function() {
// FORCE_COLOR=true node bin/jora <test/color-fixture.json >test/color-fixture.compact.expected
it('compact', () =>
runWithForceColors()
.input(fixture)
.output(fixtureExpectedCompact)
.input(colorFixture)
.output(colorFixtureExpectedCompact)
);

// FORCE_COLOR=true node bin/jora -p <test/color-fixture.json >test/color-fixture.expected
it('pretty print', () =>
runWithForceColors('-p')
.input(fixture)
.output(fixtureExpected)
.input(colorFixture)
.output(colorFixtureExpected)
);
});

it('--no-color should suppress output coloring', () =>
runWithForceColors('--no-color')
.input(fixture)
.output(JSON.stringify(fixtureData))
.input(colorFixture)
.output(JSON.stringify(colorFixtureData))
);
});

0 comments on commit 7891d17

Please sign in to comment.