├── examples └── simple.yml ├── .gitignore ├── CHANGELOG.md ├── .github └── workflows │ └── workflow.yml ├── package.json ├── README.md ├── index.js └── index.test.js /examples/simple.yml: -------------------------------------------------------------------------------- 1 | a: 2 | z: Hello 3 | b: World 4 | c: 5 | a: Foo 6 | d: Bar 7 | c: Baz 8 | b: 9 | z: 1 10 | v: 2 11 | a: 3 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # tmp directory for unit testing 4 | /tmp 5 | 6 | # dependencies 7 | /node_modules 8 | 9 | # misc 10 | .DS_Store 11 | .env 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | .vscode 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.3.0] - 2022-04-12 8 | ### Chore 9 | - Updated dependencies 10 | ### Fixed 11 | - Fixed dry-run not working without a true boolean. 12 | 13 | ## [1.2.0] - 2020-08-07 14 | ### Chore 15 | - Updated dependencies 16 | 17 | ## [1.1.1] - 2018-11-12 18 | ### Fixed 19 | - Fixed error logging when write failed. 20 | 21 | ## [1.1.0] - 2018-10-24 22 | ### Added 23 | - Option to pass in indentation size via --indent 24 | 25 | ## [1.0.0] - 2017-10-31 26 | ### Added 27 | - First version of the yml-sorter -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [14.x, 15.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - uses: actions/cache@v2 21 | with: 22 | path: ~/.npm 23 | key: ${{ runner.os }}-${{ matrix.node-version }}-node-${{ hashFiles('**/package-lock.json') }} 24 | restore-keys: | 25 | ${{ runner.os }}-node- 26 | - name: Setup timezone 27 | uses: zcong1993/setup-timezone@v1.1.0 28 | with: 29 | timezone: Europe/Amsterdam 30 | - run: npm install 31 | - run: npm run test 32 | env: 33 | CI: true -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yml-sorter", 3 | "version": "1.3.0", 4 | "description": "Sorts yml files alphabetically", 5 | "preferGlobal": true, 6 | "bin": { 7 | "yml-sorter": "index.js" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "jest test --no-cache", 12 | "test:watch": "jest --watch" 13 | }, 14 | "license": "ISC", 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/42BV/yml-sorter.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/42BV/yml-sorter/issues" 21 | }, 22 | "homepage": "https://github.com/42BV/yml-sorter#readme", 23 | "keywords": [ 24 | "yml", 25 | "sort", 26 | "alphabetically" 27 | ], 28 | "author": "Maarten Hus", 29 | "dependencies": { 30 | "js-yaml": "4.0.0", 31 | "yargs": "16.2.0" 32 | }, 33 | "devDependencies": { 34 | "jest": "26.6.3", 35 | "prettier": "2.2.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is a tool which allows you to sort yml files via a CLI. 4 | 5 | The tool is basically a tiny wrapper around the great [js-yaml](https://github.com/nodeca/js-yaml). 6 | 7 | # Installation 8 | 9 | `npm install -g yml-sorter` 10 | 11 | # Usage 12 | 13 | ``` 14 | Usage: yml-sorter [options] 15 | 16 | Options: 17 | --input, -i The yml file which needs to be sorted [required] 18 | --output, -o The file to wich to write the output 19 | --dry-run, -d Only outputs the proposed sort to the terminal [default: false] 20 | --indent, --id Indentation width to use (in spaces) [default: 2] 21 | -h, --help Show help [boolean] 22 | 23 | Examples: 24 | yml-sorter --input application-yml Sorts the file application.yml alphabetically. 25 | yml-sorter --input application-yml --output dragons.yml Sorts and writes the output to dragons.yml 26 | yml-sorter --input application-yml --dry-run Writes the output to the terminal 27 | yml-sorter --input application-yml --indent 4 Indent with 4 spaces 28 | 29 | With love from 42.nl 30 | ``` 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | const argv = require("yargs") 4 | .usage("Usage: yml-sorter [options]") 5 | .example( 6 | "yml-sorter --input application-yml", 7 | "Sorts the file application.yml alphabetically." 8 | ) 9 | .example( 10 | "yml-sorter --input application-yml --output dragons.yml", 11 | "Sorts and writes the output to dragons.yml" 12 | ) 13 | .example( 14 | "yml-sorter --input application-yml --dry-run", 15 | "Writes the output to the terminal" 16 | ) 17 | .example( 18 | "yml-sorter --input application-yml --indent 4", 19 | "Indent with 4 spaces" 20 | ) 21 | .option("input", { 22 | alias: "i", 23 | describe: "The yml file which needs to be sorted" 24 | }) 25 | .option("output", { 26 | alias: "o", 27 | describe: "The file to wich to write the output" 28 | }) 29 | .option("dry-run", { 30 | alias: "d", 31 | type: "boolean", 32 | default: false, 33 | describe: "Only outputs the proposed sort to the terminal" 34 | }) 35 | .option("indent", { 36 | alias: "id", 37 | default: 2, 38 | describe: "Indentation width to use (in spaces)" 39 | }) 40 | .demandOption(["input"]) 41 | .help("h") 42 | .alias("h", "help") 43 | .epilog("With love from 42.nl") 44 | .wrap(null).argv; 45 | 46 | const yaml = require("js-yaml"); 47 | const fs = require("fs"); 48 | 49 | // Get document, or throw exception on error 50 | try { 51 | const doc = yaml.load(fs.readFileSync(argv.input, "utf8")); 52 | 53 | const input = yaml.dump(doc, { sortKeys: true, indent: argv.indent }); 54 | 55 | if (argv["dry-run"]) { 56 | console.log(input); 57 | } else { 58 | const output = argv.output ? argv.output : argv.input; 59 | 60 | fs.writeFile(output, input, error => { 61 | if (error) { 62 | return console.error(error); 63 | } 64 | }); 65 | } 66 | } catch (error) { 67 | console.error(error); 68 | } 69 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | const execSync = require("child_process").execSync; 2 | const fs = require("fs"); 3 | 4 | const tmpDir = "./tmp"; 5 | 6 | beforeEach(() => prepareTmpDirectory()); 7 | 8 | describe("input flag", () => { 9 | test("that --input flag works", () => { 10 | const result = inputCheck("--input"); 11 | expect(result).toMatchInlineSnapshot(` 12 | "a: 13 | b: World 14 | z: Hello 15 | b: 16 | a: 3 17 | v: 2 18 | z: 1 19 | c: 20 | a: Foo 21 | c: Baz 22 | d: Bar 23 | " 24 | `); 25 | }); 26 | 27 | test("that -i flag works", () => { 28 | const result = inputCheck("-i"); 29 | expect(result).toMatchInlineSnapshot(` 30 | "a: 31 | b: World 32 | z: Hello 33 | b: 34 | a: 3 35 | v: 2 36 | z: 1 37 | c: 38 | a: Foo 39 | c: Baz 40 | d: Bar 41 | " 42 | `); 43 | }); 44 | 45 | function inputCheck(flag) { 46 | execSync(`node index.js ${flag} ${tmpDir}/simple.yml`); 47 | 48 | // It should have overwritten the file and not create a new one 49 | const files = fs.readdirSync(tmpDir); 50 | expect(files.length).toBe(1); 51 | expect(files).toEqual(["simple.yml"]); 52 | 53 | return fs.readFileSync(`${tmpDir}/simple.yml`, "utf-8"); 54 | } 55 | }); 56 | 57 | describe("output flag", () => { 58 | test("that --output flag works", () => { 59 | const result = outputCheck("--output"); 60 | expect(result).toMatchInlineSnapshot(` 61 | "a: 62 | b: World 63 | z: Hello 64 | b: 65 | a: 3 66 | v: 2 67 | z: 1 68 | c: 69 | a: Foo 70 | c: Baz 71 | d: Bar 72 | " 73 | `); 74 | }); 75 | 76 | test("that -o flag works", () => { 77 | const result = outputCheck("-o"); 78 | expect(result).toMatchInlineSnapshot(` 79 | "a: 80 | b: World 81 | z: Hello 82 | b: 83 | a: 3 84 | v: 2 85 | z: 1 86 | c: 87 | a: Foo 88 | c: Baz 89 | d: Bar 90 | " 91 | `); 92 | }); 93 | 94 | function outputCheck(flag) { 95 | execSync( 96 | `node index.js --input ${tmpDir}/simple.yml ${flag} ${tmpDir}/output.yml` 97 | ); 98 | 99 | // It should have created a new file 100 | const files = fs.readdirSync(tmpDir); 101 | expect(files.length).toBe(2); 102 | expect(files).toEqual(["output.yml", "simple.yml"]); 103 | 104 | return fs.readFileSync(`${tmpDir}/output.yml`, "utf-8"); 105 | } 106 | }); 107 | 108 | describe("dry-run flag", () => { 109 | test("that --dry-run flag works", () => { 110 | const result = dryRunCheck("--dry-run"); 111 | expect(result).toMatchInlineSnapshot(` 112 | "a: 113 | b: World 114 | z: Hello 115 | b: 116 | a: 3 117 | v: 2 118 | z: 1 119 | c: 120 | a: Foo 121 | c: Baz 122 | d: Bar 123 | 124 | " 125 | `); 126 | }); 127 | 128 | test("that -d flag works", () => { 129 | const result = dryRunCheck("-d"); 130 | expect(result).toMatchInlineSnapshot(` 131 | "a: 132 | b: World 133 | z: Hello 134 | b: 135 | a: 3 136 | v: 2 137 | z: 1 138 | c: 139 | a: Foo 140 | c: Baz 141 | d: Bar 142 | 143 | " 144 | `); 145 | }); 146 | 147 | test("that -d with true", () => { 148 | const result = dryRunCheck("-d true"); 149 | expect(result).toMatchInlineSnapshot(` 150 | "a: 151 | b: World 152 | z: Hello 153 | b: 154 | a: 3 155 | v: 2 156 | z: 1 157 | c: 158 | a: Foo 159 | c: Baz 160 | d: Bar 161 | 162 | " 163 | `); 164 | }); 165 | 166 | function dryRunCheck(flag) { 167 | const result = execSync( 168 | `node index.js --input ${tmpDir}/simple.yml ${flag}` 169 | ); 170 | 171 | // It should have run without creating a file because it was dry run 172 | const files = fs.readdirSync(tmpDir); 173 | expect(files.length).toBe(1); 174 | expect(files).toEqual(["simple.yml"]); 175 | 176 | return result.toString(); 177 | } 178 | }); 179 | 180 | describe("indent flag", () => { 181 | test("that --indent flag works", () => { 182 | const result = indentCheck("--indent"); 183 | expect(result).toMatchInlineSnapshot(` 184 | "a: 185 | b: World 186 | z: Hello 187 | b: 188 | a: 3 189 | v: 2 190 | z: 1 191 | c: 192 | a: Foo 193 | c: Baz 194 | d: Bar 195 | " 196 | `); 197 | }); 198 | 199 | test("that -id flag works", () => { 200 | const result = indentCheck("--id"); 201 | expect(result).toMatchInlineSnapshot(` 202 | "a: 203 | b: World 204 | z: Hello 205 | b: 206 | a: 3 207 | v: 2 208 | z: 1 209 | c: 210 | a: Foo 211 | c: Baz 212 | d: Bar 213 | " 214 | `); 215 | }); 216 | 217 | function indentCheck(flag) { 218 | execSync(`node index.js --input ${tmpDir}/simple.yml ${flag} 8`); 219 | 220 | // It should have overwritten the file and not create a new one 221 | // with an indent of 8 222 | const files = fs.readdirSync(tmpDir); 223 | expect(files.length).toBe(1); 224 | expect(files).toEqual(["simple.yml"]); 225 | 226 | return fs.readFileSync(`${tmpDir}/simple.yml`, "utf-8"); 227 | } 228 | }); 229 | 230 | describe("help flag", () => { 231 | test("that --help flag works", () => { 232 | const result = helpCheck("--help"); 233 | expect(result).toMatchInlineSnapshot(` 234 | "Usage: yml-sorter [options] 235 | 236 | Options: 237 | --version Show version number [boolean] 238 | -i, --input The yml file which needs to be sorted [required] 239 | -o, --output The file to wich to write the output 240 | -d, --dry-run Only outputs the proposed sort to the terminal [boolean] [default: false] 241 | --indent, --id Indentation width to use (in spaces) [default: 2] 242 | -h, --help Show help [boolean] 243 | 244 | Examples: 245 | yml-sorter --input application-yml Sorts the file application.yml alphabetically. 246 | yml-sorter --input application-yml --output dragons.yml Sorts and writes the output to dragons.yml 247 | yml-sorter --input application-yml --dry-run Writes the output to the terminal 248 | yml-sorter --input application-yml --indent 4 Indent with 4 spaces 249 | 250 | With love from 42.nl 251 | " 252 | `); 253 | }); 254 | 255 | test("that -h flag works", () => { 256 | const result = helpCheck("-h"); 257 | expect(result).toMatchInlineSnapshot(` 258 | "Usage: yml-sorter [options] 259 | 260 | Options: 261 | --version Show version number [boolean] 262 | -i, --input The yml file which needs to be sorted [required] 263 | -o, --output The file to wich to write the output 264 | -d, --dry-run Only outputs the proposed sort to the terminal [boolean] [default: false] 265 | --indent, --id Indentation width to use (in spaces) [default: 2] 266 | -h, --help Show help [boolean] 267 | 268 | Examples: 269 | yml-sorter --input application-yml Sorts the file application.yml alphabetically. 270 | yml-sorter --input application-yml --output dragons.yml Sorts and writes the output to dragons.yml 271 | yml-sorter --input application-yml --dry-run Writes the output to the terminal 272 | yml-sorter --input application-yml --indent 4 Indent with 4 spaces 273 | 274 | With love from 42.nl 275 | " 276 | `); 277 | }); 278 | 279 | function helpCheck(flag) { 280 | const result = execSync(`node index.js ${flag}`); 281 | 282 | return result.toString(); 283 | } 284 | }); 285 | 286 | const examplesDir = "./examples"; 287 | 288 | // Clears / creates tmp directory and copies all examples so they 289 | // remain pristine. 290 | function prepareTmpDirectory() { 291 | if (fs.existsSync(tmpDir)) { 292 | fs.rmdirSync(tmpDir, { recursive: true }); 293 | } 294 | fs.mkdirSync(tmpDir); 295 | 296 | const result = fs.readdirSync(examplesDir); 297 | result.forEach((file) => { 298 | fs.copyFileSync(`${examplesDir}/${file}`, `${tmpDir}/${file}`); 299 | }); 300 | } 301 | --------------------------------------------------------------------------------