diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index c652f8d..df666fd 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [10.x, 12.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index d427b76..1c37640 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,19 @@ This tool is basically a tiny wrapper around [js-yaml](https://github.com/nodeca Usage: yaml-sort [options] Options: - --input, -i The YAML file(s) which needs to be sorted [array] [required] - --output, -o The YAML file to output sorted content to [string] - --stdout, -s Output the proposed sort to STDOUT only [boolean] [default: false] - --check, -k Check if the given file(s) is already sorted [boolean] [default: false] - --indent, --id Indentation width to use (in spaces) [number] [default: 2] - --lineWidth, -w Wrap line width [number] [default: 80] - -h, --help Show help [boolean] - --version Show version number [boolean] + -i, --input The YAML file(s) which needs to be sorted [array] [default: "-"] + -o, --output The YAML file to output sorted content to [string] + -s, --stdout Output the proposed sort to STDOUT only [boolean] [default: false] + -k, --check Check if the given file(s) is already sorted [boolean] [default: false] + --indent, --id Indentation width to use (in spaces) [number] [default: 2] + -e, --encoding Input encoding [choices: "ascii", "utf8", "utf16le"] [default: "utf8"] + -w, --lineWidth Wrap line width [number] [default: 80] + -h, --help Show help [boolean] + --version Show version number [boolean] Examples: - yaml-sort --input config.yml Sorts alphabetically and overwrites the file config.yml. + yaml-sort --input config.yml Sorts alphabetically and overwrites the file config.yml yaml-sort --input config.yml --lineWidth 100 --stdout Sorts the file config.yml and output result to STDOUT wrapped to 100 columns - yaml-sort --input config.yml --indent 4 --output sorted.yml Indents with 4 spaces and outputs result to file sorted.yml. + yaml-sort --input config.yml --indent 4 --output sorted.yml Indents with 4 spaces and outputs result to file sorted.yml + cat config.yml | yaml-sort Sorts alphabetically from STDIN ``` \ No newline at end of file diff --git a/package.json b/package.json index 68dfa5d..df174a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yaml-sort", - "version": "1.1.1", + "version": "1.2.0", "description": "CLI tool to sort YAML files alphabetically", "main": "yaml-sort.js", "scripts": { diff --git a/test/test-utf16le.yml b/test/test-utf16le.yml new file mode 100644 index 0000000..a4fe5f9 Binary files /dev/null and b/test/test-utf16le.yml differ diff --git a/test/test.js b/test/test.js index 39ce685..fc8d017 100755 --- a/test/test.js +++ b/test/test.js @@ -6,10 +6,31 @@ const test = require('tape') const spawn = require('tape-spawn') const opts = { cwd: __dirname } +test('CLI --help', (t) => { + const proc = spawn(t, '../yaml-sort.js -h', opts) + proc.stdout.match(/^Usage:/) + proc.stderr.match('') + proc.exitCode(0) + proc.end() +}) + test('CLI w/o arg', (t) => { const proc = spawn(t, '../yaml-sort.js', opts) + proc.stdout.match('') proc.exitCode(1) - proc.stderr.match(/Missing required argument: input/) + proc.end() +}) + +test('CLI w/o arg (STDIN)', (t) => { + const proc = spawn(t, 'cat test.yml | ../yaml-sort.js', opts) + proc.exitCode(0) + proc.stdout.match('a: Lorem ipsum dolor sit amet, consectetur adipiscing elit...\n' + + 'b:\n' + + ' b: 35\n' + + ' c:\n' + + ' d: false\n' + + '\n') + proc.stderr.match('') proc.end() }) @@ -22,6 +43,7 @@ test('CLI w/ arg', (t) => { ' c:\n' + ' d: false\n' + '\n') + proc.stderr.match('') proc.end() }) @@ -36,6 +58,22 @@ test('CLI --output', (t) => { ' b: 35\n' + ' c:\n' + ' d: false\n') + proc.stderr.match('') + proc.end() +}) + +test('CLI --output (STDIN)', (t) => { + const proc = spawn(t, + 'cat test.yml | ../yaml-sort.js --input - --output output.yml' + + ' && cat output.yml' + + ' && rm -f output.yml', opts) + proc.exitCode(0) + proc.stdout.match('a: Lorem ipsum dolor sit amet, consectetur adipiscing elit...\n' + + 'b:\n' + + ' b: 35\n' + + ' c:\n' + + ' d: false\n') + proc.stderr.match('') proc.end() }) @@ -48,6 +86,7 @@ test('CLI --indent', (t) => { ' c:\n' + ' d: false\n' + '\n') + proc.stderr.match('') proc.end() }) @@ -62,6 +101,7 @@ test('CLI --lineWidth', (t) => { ' c:\n' + ' d: false\n' + '\n') + proc.stderr.match('') proc.end() }) @@ -79,6 +119,24 @@ test('CLI --check SUCCESS', (t) => { '../yaml-sort.js --input sorted.yml --check', opts) proc.exitCode(0) proc.stdout.match('') + proc.stderr.match('') + proc.end() +}) + +test('CLI --encoding FAIL', (t) => { + const proc = spawn(t, + '../yaml-sort.js --input test-utf16le.yml --stdout', opts) + proc.exitCode(1) + proc.stdout.match('') + proc.stderr.match(/null byte is not allowed in input/) + proc.end() +}) + +test('CLI --encoding SUCCESS', (t) => { + const proc = spawn(t, + '../yaml-sort.js --input test-utf16le.yml --stdout --encoding utf16le', opts) + proc.exitCode(0) + proc.stderr.match('') proc.end() }) @@ -89,5 +147,24 @@ test('CLI --lineWidth SUCCESS', (t) => { ' && rm -f output.yml', opts) proc.exitCode(0) proc.stdout.match('') + proc.stderr.match('') + proc.end() +}) + +test('CLI --output --stdout FAIL', (t) => { + const proc = spawn(t, + '../yaml-sort.js --input test.yml --stdout --output output.yml', opts) + proc.exitCode(1) + proc.stdout.match('') + proc.stderr.match(/Arguments stdout and output are mutually exclusive/) + proc.end() +}) + +test('CLI --check --stdout FAIL', (t) => { + const proc = spawn(t, + '../yaml-sort.js --input test.yml --check --stdout', opts) + proc.exitCode(1) + proc.stdout.match('') + proc.stderr.match(/Arguments check and stdout are mutually exclusive/) proc.end() }) diff --git a/yaml-sort.js b/yaml-sort.js index ed48225..999302c 100755 --- a/yaml-sort.js +++ b/yaml-sort.js @@ -2,24 +2,26 @@ 'use strict' -const argv = require('yargs') +const fs = require('fs') +const yaml = require('js-yaml') +const yargs = require('yargs') + +const argv = yargs .usage('Usage: $0 [options]') - .example( - '$0 --input config.yml', - 'Sorts alphabetically and overwrites the file config.yml' - ) - .example( - '$0 --input config.yml --lineWidth 100 --stdout', - 'Sorts the file config.yml and output result to STDOUT wrapped to 100 columns' - ) - .example( - '$0 --input config.yml --indent 4 --output sorted.yml', - 'Indents with 4 spaces and outputs result to file sorted.yml' - ) + .example([ + ['$0 --input config.yml', + 'Sorts alphabetically and overwrites the file config.yml'], + ['$0 --input config.yml --lineWidth 100 --stdout', + 'Sorts the file config.yml and output result to STDOUT wrapped to 100 columns'], + ['$0 --input config.yml --indent 4 --output sorted.yml', + 'Indents with 4 spaces and outputs result to file sorted.yml'], + ['cat config.yml | $0', + 'Sorts alphabetically from STDIN'] + ]) .option('input', { alias: 'i', describe: 'The YAML file(s) which needs to be sorted', - demandOption: 'Please provide an input file', + default: '-', string: true, array: true }) @@ -30,14 +32,14 @@ const argv = require('yargs') }) .option('stdout', { alias: 's', - default: false, describe: 'Output the proposed sort to STDOUT only', + conflicts: 'output', boolean: true }) .option('check', { alias: 'k', - default: false, describe: 'Check if the given file(s) is already sorted', + conflicts: ['output', 'stdout'], boolean: true }) .option('indent', { @@ -46,27 +48,36 @@ const argv = require('yargs') describe: 'Indentation width to use (in spaces)', number: true }) + .option('encoding', { + alias: 'e', + default: 'utf8', + describe: 'Input encoding', + choices: ['ascii', 'utf8', 'utf16le'] + }) .option('lineWidth', { alias: 'w', default: 80, describe: 'Wrap line width', number: true }) - .demandOption(['input']) .help('h') .alias('h', 'help') .version() .wrap(null) .argv -const yaml = require('js-yaml') -const fs = require('fs') - let success = true argv.input.forEach((file) => { try { - const content = fs.readFileSync(file, 'utf8') + const isStdin = file === '-' + + if (isStdin && process.stdin.isTTY) { + yargs.showHelp() + process.exit(22) + } + + const content = fs.readFileSync(isStdin ? process.stdin.fd : file, argv.encoding) const sorted = yaml.dump(yaml.load(content), { sortKeys: true, @@ -79,7 +90,7 @@ argv.input.forEach((file) => { success = false console.warn(`'${file}' is not sorted and/or formatted (indent, line width).`) } - } else if (argv.stdout) { + } else if (argv.stdout || (isStdin && !argv.output)) { console.log(sorted) } else { fs.writeFile( @@ -87,7 +98,8 @@ argv.input.forEach((file) => { sorted, (error) => { if (error) { - return console.error(error) + success = false + console.error(error) } }) }