Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Options:
--indent, --id Indentation width (in spaces) [number] [default: 2]
-e, --encoding Input encoding [choices: "ascii", "utf8", "utf16le"] [default: "utf8"]
-q, --quotingStyle Strings will be quoted using this quoting style [choices: "single", "double"] [default: "single"]
-f, --forceQuotes Force quotes for all scalar values [boolean] [default: false]
-w, --lineWidth Wrap line width (-1 for unlimited width) [number] [default: 80]
-h, --help Show help [boolean]
--version Show version number [boolean]
Expand All @@ -34,5 +35,6 @@ Examples:
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 --forceQuotes --quotingStyle double Forces double quotes for all scalar values
cat config.yml | yaml-sort Sorts alphabetically from STDIN
```
6 changes: 6 additions & 0 deletions test/test-duplicate-keys.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
a: first value
b: value
a: second value # Duplicate key
c:
d: value1
d: value2 # Duplicate key
9 changes: 9 additions & 0 deletions test/test-multiple-sorted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
a: first document
b: 1
c:
d: true
---
x: second document
'y':
z: false
9 changes: 9 additions & 0 deletions test/test-multiple-unsorted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
c:
d: true
b: 1
a: first document
---
y:
z: false
x: second document
9 changes: 9 additions & 0 deletions test/test-multiple.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
c:
d: true
b: 1
a: first document
---
y:
z: false
x: second document
185 changes: 127 additions & 58 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ 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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -37,10 +37,10 @@ test('CLI w/ arg', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test.yml --stdout', 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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -49,13 +49,13 @@ test('CLI quoting style single', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-edges.yml --stdout --quotingStyle single', opts)
proc.exitCode(0)
proc.stdout.match('a: Lorem ipsum dolor sit amet, consectetur adipiscing elit...\n' +
'b:\n' +
' b: 35\n' +
' c:\n' +
' a: \'hello: "john"\'\n' +
' d: false\n' +
' e: \'"foo"\'\n' +
' f: \'\'\'foo\'\'\'\n')
'b:\n' +
' b: 35\n' +
' c:\n' +
' a: \'hello: "john"\'\n' +
' d: false\n' +
' e: \'"foo"\'\n' +
' f: \'\'\'foo\'\'\'\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -64,28 +64,28 @@ test('CLI quoting style double', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-edges.yml --stdout --quotingStyle double', opts)
proc.exitCode(0)
proc.stdout.match('a: Lorem ipsum dolor sit amet, consectetur adipiscing elit...\n' +
'b:\n' +
' b: 35\n' +
' c:\n' +
' a: "hello: \\"john\\""\n' +
' d: false\n' +
' e: "\\"foo\\""\n' +
' f: "\'foo\'"\n')
'b:\n' +
' b: 35\n' +
' c:\n' +
' a: "hello: \\"john\\""\n' +
' d: false\n' +
' e: "\\"foo\\""\n' +
' f: "\'foo\'"\n')
proc.stderr.match('')
proc.end()
})

test('CLI --output', (t) => {
const proc = spawn(t,
'../yaml-sort.js --input test.yml --output output.yml' +
' && cat output.yml' +
' && rm -f output.yml', opts)
' && 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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -94,24 +94,24 @@ test('CLI --output -', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test.yml --output -', 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')
'b:\n' +
' 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)
' && 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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -120,10 +120,10 @@ test('CLI (STDIN) (STDOUT)', (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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -132,10 +132,10 @@ test('CLI --indent', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test.yml --stdout --indent 4', 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')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -144,12 +144,12 @@ test('CLI --lineWidth', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test.yml --stdout --lineWidth 40', opts)
proc.exitCode(0)
proc.stdout.match('a: >-\n' +
' Lorem ipsum dolor sit amet, consectetur\n' +
' adipiscing elit...\n' +
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
' Lorem ipsum dolor sit amet, consectetur\n' +
' adipiscing elit...\n' +
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -158,10 +158,10 @@ test('CLI --lineWidth unlimited', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-long-line.yml --stdout --lineWidth -1', opts)
proc.exitCode(0)
proc.stdout.match('a: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam nec volutpat mi, ac consectetur est. Proin venenatis tortor ut erat interdum finibus.\n' +
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
'b:\n' +
' b: 35\n' +
' c:\n' +
' d: false\n')
proc.stderr.match('')
proc.end()
})
Expand All @@ -184,6 +184,23 @@ test('CLI --check SUCCESS', (t) => {
proc.end()
})

test('CLI --check invalid YAML (duplicate keys) FAIL', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-duplicate-keys.yml --check', opts)
proc.exitCode(1)
proc.stdout.match('')
proc.stderr.match(/duplicated mapping key/)

proc.end()
})

test('CLI --check valid YAML SUCCESS', (t) => {
const proc = spawn(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)
Expand All @@ -204,8 +221,8 @@ test('CLI --encoding SUCCESS', (t) => {
test('CLI --lineWidth SUCCESS', (t) => {
const proc = spawn(t,
'../yaml-sort.js --input test.yml --output output.yml' +
' && ../yaml-sort.js --input output.yml --check' +
' && rm -f output.yml', opts)
' && ../yaml-sort.js --input output.yml --check' +
' && rm -f output.yml', opts)
proc.exitCode(0)
proc.stdout.match('')
proc.stderr.match('')
Expand All @@ -229,3 +246,55 @@ test('CLI --check --stdout FAIL', (t) => {
proc.stderr.match(/Arguments check and stdout are mutually exclusive/)
proc.end()
})

test('CLI multiple YAML documents with single quotes', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-multiple.yml --stdout --quotingStyle single --forceQuotes', opts)
proc.exitCode(0)
proc.stdout.match(
'---\n' +
'a: \'first document\'\n' +
'b: 1\n' +
'c:\n' +
' d: true\n' +
'---\n' +
'x: \'second document\'\n' +
'\'y\':\n' +
' z: false\n'
)
proc.stderr.match('')
proc.end()
})

test('CLI multiple YAML documents with double quotes', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-multiple.yml --stdout --quotingStyle double --forceQuotes', opts)
proc.exitCode(0)
proc.stdout.match(
'---\n' +
'a: "first document"\n' +
'b: 1\n' +
'c:\n' +
' d: true\n' +
'---\n' +
'x: "second document"\n' +
'"y":\n' +
' z: false\n'
)
proc.stderr.match('')
proc.end()
})

test('CLI multiple YAML documents --check FAIL', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-multiple-unsorted.yml --check --quotingStyle single --forceQuotes', opts)
proc.exitCode(1)
proc.stdout.match('')
proc.stderr.match('\'test-multiple-unsorted.yml\' is not sorted and/or formatted (indent, line width).\n')
proc.end()
})

test('CLI multiple YAML documents --check SUCCESS', (t) => {
const proc = spawn(t, '../yaml-sort.js --input test-multiple-sorted.yml --check', opts)
proc.exitCode(0)
proc.stdout.match('')
proc.stderr.match('')
proc.end()
})
28 changes: 20 additions & 8 deletions yaml-sort.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const argv = yargs
describe: 'Strings will be quoted using this quoting style',
choices: ['single', 'double']
})
.option('forceQuotes', {
alias: 'f',
describe: 'Force quotes around all strings',
boolean: true
})
.option('lineWidth', {
alias: 'w',
default: 80,
Expand All @@ -88,28 +93,35 @@ argv.input.forEach((file) => {
}

const output =
argv.stdout || (argv.output === '.') || (isStdin && !argv.output)
? process.stdout.fd
: (argv.output ? argv.output : file)
argv.stdout || (argv.output === '.') || (isStdin && !argv.output)
? process.stdout.fd
: (argv.output ? argv.output : file)

const content = fs.readFileSync(isStdin ? process.stdin.fd : file, argv.encoding)

const sorted = yaml.dump(yaml.load(content), {
const documents = yaml.loadAll(content)

const sortedDocuments = documents.map(doc => yaml.dump(doc, {
sortKeys: true,
indent: argv.indent,
lineWidth: argv.lineWidth,
quotingType: argv.quotingStyle === 'double' ? '"' : "'"
})
quotingType: argv.quotingStyle === 'double' ? '"' : "'",
forceQuotes: argv.forceQuotes
}))

const hasDocumentStart = content.toString().trimStart().startsWith('---')

const sortedContent = (hasDocumentStart ? '---\n' : '') + sortedDocuments.join('---\n')

if (argv.check) {
if (sorted !== content.toString()) {
if (sortedContent !== content.toString()) {
success = false
console.warn(`'${file}' is not sorted and/or formatted (indent, line width).`)
}
} else {
fs.writeFile(
output,
sorted,
sortedContent,
(error) => {
if (error) {
success = false
Expand Down
Loading