├── .gitattributes ├── .gitignore ├── .travis.yml ├── .editorconfig ├── package.json ├── .jshintrc ├── LICENSE ├── index.js ├── README.md └── tests └── test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules 3 | /npm-debug.log 4 | /tmp 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.12' 5 | - 'node' 6 | - 'stable' 7 | notifications: 8 | email: 9 | on_success: never 10 | on_failure: always 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style = space 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{js,json,html,jade,md}] 10 | indent_size = 4 11 | 12 | [*.js] 13 | jslint_happy = true 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-sort", 3 | "description": "Sort files in stream by path or any custom sort comparator", 4 | "repository": "pgilad/gulp-sort", 5 | "version": "2.0.0", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Gilad Peleg", 9 | "email": "giladp007@gmail.com", 10 | "url": "http://giladpeleg.com" 11 | }, 12 | "main": "index.js", 13 | "files": [ 14 | "index.js" 15 | ], 16 | "scripts": { 17 | "test": "mocha tests/*.js" 18 | }, 19 | "keywords": [ 20 | "gulpplugin", 21 | "gulp", 22 | "sort", 23 | "paths", 24 | "order", 25 | "comparator" 26 | ], 27 | "dependencies": { 28 | "through2": "^2.0.1" 29 | }, 30 | "devDependencies": { 31 | "gulp-util": "^3.0.4", 32 | "mocha": "^2.1.0", 33 | "stable": "^0.1.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr": 50, 3 | "bitwise": false, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "forin": true, 8 | "immed": true, 9 | "indent": 4, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": false, 13 | "noempty": true, 14 | "nonew": true, 15 | "regexp": true, 16 | "quotmark": "single", 17 | "undef": true, 18 | "unused": true, 19 | "strict": false, 20 | "trailing": true, 21 | "maxparams": 7, 22 | "maxdepth": 5, 23 | "maxstatements": 25, 24 | "maxcomplexity": 10, 25 | "maxlen": 140, 26 | "funcscope": false, 27 | "globalstrict": true, 28 | "multistr": true, 29 | "browser": true, 30 | "devel": true, 31 | "jquery": true, 32 | "node": true, 33 | "nonstandard": true, 34 | "onevar": false, 35 | "white": true, 36 | "globals": { 37 | "_": false, 38 | "describe": false, 39 | "it": false, 40 | "before": false, 41 | "beforeEach": false, 42 | "after": false, 43 | "afterEach": false 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Gilad Peleg (https://www.giladpeleg.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require('through2'); 2 | 3 | function defaultComparator(a, b) { 4 | return a.path.localeCompare(b.path); 5 | } 6 | 7 | module.exports = function gulpSort(params) { 8 | var asc = true; 9 | var comparator; 10 | var files = []; 11 | var customSortFn; 12 | 13 | if (typeof params === 'function') { 14 | // params is the sort comparator 15 | comparator = params; 16 | params = {}; 17 | } else { 18 | params = params || {}; 19 | asc = typeof params.asc !== 'undefined' ? params.asc : asc; 20 | comparator = params.comparator || defaultComparator; 21 | customSortFn = params.customSortFn; 22 | } 23 | 24 | return through.obj(function (file, enc, cb) { 25 | files.push(file); 26 | cb(); 27 | }, function (cb) { 28 | if (customSortFn) { 29 | // expect customSortFn to return the files array 30 | files = customSortFn(files, comparator); 31 | } else { 32 | // sort in-place 33 | files.sort(comparator); 34 | } 35 | if (!asc) { 36 | files.reverse(); 37 | } 38 | files.forEach(function (file) { 39 | this.push(file); 40 | }, this); 41 | cb(); 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [gulp](https://github.com/wearefractal/gulp)-sort 2 | > Sort files in stream by path or any custom sort comparator 3 | 4 | [![Build Status](http://img.shields.io/travis/pgilad/gulp-sort/master.svg?style=flat)](https://travis-ci.org/pgilad/gulp-sort) 5 | 6 | ## Install 7 | 8 | `$ npm install gulp-sort --save-dev` 9 | 10 | ## Usage 11 | 12 | ```js 13 | var sort = require('gulp-sort'); 14 | 15 | // default sort 16 | gulp.src('./src/js/**/*.js') 17 | .pipe(sort()) 18 | .pipe(gulp.dest('./build/js')); 19 | 20 | // pass in a custom comparator function 21 | gulp.src('./src/js/**/*.js') 22 | .pipe(sort(customComparator)) 23 | .pipe(gulp.dest('./build/js')); 24 | 25 | // sort descending 26 | gulp.src('./src/js/**/*.js') 27 | .pipe(sort({ 28 | asc: false 29 | })) 30 | .pipe(gulp.dest('./build/js')); 31 | 32 | // sort with a custom comparator 33 | gulp.src('./src/js/**/*.js') 34 | .pipe(sort({ 35 | comparator: function(file1, file2) { 36 | if (file1.path.indexOf('build') > -1) { 37 | return 1; 38 | } 39 | if (file2.path.indexOf('build') > -1) { 40 | return -1; 41 | } 42 | return 0; 43 | } 44 | })) 45 | .pipe(gulp.dest('./build/js')); 46 | 47 | // sort with a custom sort function 48 | var stable = require('stable'); 49 | gulp.src('./src/js/**/*.js') 50 | .pipe(sort({ 51 | customSortFn: function(files, comparator) { 52 | return stable(files, comparator); 53 | } 54 | })) 55 | .pipe(gulp.dest('./build/js')); 56 | ``` 57 | 58 | ## Options 59 | 60 | `gulp-sort` takes in an optional [comparator](#comparator) function, or dictionary with following params: 61 | 62 | ### asc 63 | 64 | Sort ascending. Defaults to true. Specify false to sort descending. 65 | 66 | ### comparator 67 | 68 | Comparator function to use. `comparator(file1, file2)`. Defaults to `localeCompare` of file paths. 69 | 70 | ### customSortFn 71 | 72 | Use `customSortFn` in order to control the sorting yourself (useful for stable sorts). 73 | 74 | `customSortFn` signature is as follows: 75 | 76 | `customSortFn(, )` 77 | 78 | - `files` being the vinyl file objects that were passed in 79 | - `comparator` is the default comparator used, or a custom one that was passed as param 80 | 81 | This function is expected to return back the sorted list of files. 82 | 83 | ## License 84 | 85 | MIT © [Gilad Peleg](https://www.giladpeleg.com) 86 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | /* global describe,it */ 2 | 'use strict'; 3 | var assert = require('assert'); 4 | var gutil = require('gulp-util'); 5 | var sort = require('../index'); 6 | var stable = require('stable'); 7 | 8 | describe('gulp-sort', function () { 9 | it('should sort files based on path', function (cb) { 10 | var stream = sort(); 11 | var files = []; 12 | stream.on('data', function (file) { 13 | files.push(file); 14 | }).on('error', function (err) { 15 | assert.false(err); 16 | }).on('end', function () { 17 | assert.ok(/index-1/.test(files[0].path)); 18 | assert.ok(/index-2/.test(files[1].path)); 19 | assert.ok(/index-5/.test(files[2].path)); 20 | cb(); 21 | }); 22 | 23 | stream.write(new gutil.File({ 24 | path: './index-5.js', 25 | contents: new Buffer('data') 26 | })); 27 | stream.write(new gutil.File({ 28 | path: './index-2.js', 29 | contents: new Buffer('data') 30 | })); 31 | stream.write(new gutil.File({ 32 | path: './index-1.js', 33 | contents: new Buffer('data') 34 | })); 35 | stream.end(); 36 | }); 37 | 38 | it('should sort files based on path descending', function (cb) { 39 | var stream = sort({ 40 | asc: false 41 | }); 42 | var files = []; 43 | stream.on('data', function (file) { 44 | files.push(file); 45 | }).on('error', function (err) { 46 | assert.false(err); 47 | }).on('end', function () { 48 | assert.ok(/index-1/.test(files[2].path)); 49 | assert.ok(/index-2/.test(files[1].path)); 50 | assert.ok(/index-5/.test(files[0].path)); 51 | cb(); 52 | }); 53 | 54 | stream.write(new gutil.File({ 55 | path: './index-1.js', 56 | contents: new Buffer('data') 57 | })); 58 | stream.write(new gutil.File({ 59 | path: './index-2.js', 60 | contents: new Buffer('data') 61 | })); 62 | stream.write(new gutil.File({ 63 | path: './index-5.js', 64 | contents: new Buffer('data') 65 | })); 66 | stream.end(); 67 | }); 68 | 69 | it('should sort files with custom comparator', function (cb) { 70 | var stream = sort({ 71 | comparator: function (file1, file2) { 72 | var num1 = file1.path.match(/index-(\d)/)[1]; 73 | var num2 = file2.path.match(/index-(\d)/)[1]; 74 | return parseInt(num1) - parseInt(num2); 75 | } 76 | }); 77 | var files = []; 78 | stream.on('data', function (file) { 79 | files.push(file); 80 | }).on('error', function (err) { 81 | assert.false(err); 82 | }).on('end', function () { 83 | assert.ok(/index-1/.test(files[0].path)); 84 | assert.ok(/index-2/.test(files[1].path)); 85 | assert.ok(/index-7/.test(files[2].path)); 86 | cb(); 87 | }); 88 | 89 | stream.write(new gutil.File({ 90 | path: './index-7.js', 91 | contents: new Buffer('data') 92 | })); 93 | stream.write(new gutil.File({ 94 | path: './index-1.js', 95 | contents: new Buffer('data') 96 | })); 97 | stream.write(new gutil.File({ 98 | path: './index-2.js', 99 | contents: new Buffer('data') 100 | })); 101 | stream.end(); 102 | }); 103 | 104 | it('should sort files with custom comparator passed as only param', function (cb) { 105 | var stream = sort(function (file1, file2) { 106 | var num1 = file1.path.match(/index-(\d)/)[1]; 107 | var num2 = file2.path.match(/index-(\d)/)[1]; 108 | return parseInt(num1) - parseInt(num2); 109 | }); 110 | var files = []; 111 | stream.on('data', function (file) { 112 | files.push(file); 113 | }).on('error', function (err) { 114 | assert.false(err); 115 | }).on('end', function () { 116 | assert.ok(/index-1/.test(files[0].path)); 117 | assert.ok(/index-2/.test(files[1].path)); 118 | assert.ok(/index-7/.test(files[2].path)); 119 | cb(); 120 | }); 121 | 122 | stream.write(new gutil.File({ 123 | path: './index-7.js', 124 | contents: new Buffer('data') 125 | })); 126 | stream.write(new gutil.File({ 127 | path: './index-1.js', 128 | contents: new Buffer('data') 129 | })); 130 | stream.write(new gutil.File({ 131 | path: './index-2.js', 132 | contents: new Buffer('data') 133 | })); 134 | stream.end(); 135 | }); 136 | 137 | it('should sort files with custom sort function', function (cb) { 138 | var stream = sort({ 139 | customSortFn: function(files, comparator) { 140 | return stable(files, comparator); 141 | } 142 | }); 143 | var files = []; 144 | stream.on('data', function (file) { 145 | files.push(file); 146 | }).on('error', function (err) { 147 | assert.false(err); 148 | }).on('end', function () { 149 | assert.ok(/index-a/.test(files[0].path)); 150 | assert.ok(/index-A/.test(files[1].path)); 151 | assert.ok(/index-b/.test(files[2].path)); 152 | assert.ok(/index-B/.test(files[3].path)); 153 | cb(); 154 | }); 155 | 156 | stream.write(new gutil.File({ 157 | path: './index-b.js', 158 | contents: new Buffer('data') 159 | })); 160 | stream.write(new gutil.File({ 161 | path: './index-A.js', 162 | contents: new Buffer('data') 163 | })); 164 | stream.write(new gutil.File({ 165 | path: './index-a.js', 166 | contents: new Buffer('data') 167 | })); 168 | stream.write(new gutil.File({ 169 | path: './index-B.js', 170 | contents: new Buffer('data') 171 | })); 172 | stream.end(); 173 | }); 174 | }); 175 | --------------------------------------------------------------------------------