├── .gitignore ├── .eslintrc ├── .editorconfig ├── gulpfile.js ├── package.json ├── LICENSE ├── index.js ├── .jscsrc └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules/ 3 | .tmp/ 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "amd": true, 7 | "mocha": true, 8 | "es6": true 9 | }, 10 | "plugins": [ 11 | "mocha" 12 | ], 13 | "rules": { 14 | "quotes": [ 2, "single" ], 15 | "mocha/no-exclusive-tests": 2 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = tab 12 | indent_size = 4 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // dogfooding FTW! 2 | const gulp = require('gulp'); 3 | const shelter = require('./index')(gulp); 4 | 5 | const lint = 'eslint *.js'; 6 | const clean = 'rimraf .tmp && mkdir .tmp'; 7 | const echoNpm = ` 8 | echo "hello from npm script" 9 | > .tmp/echonpm.txt 10 | `; 11 | const grepNpm = ` 12 | grep -q 13 | '^hello from npm script$' 14 | .tmp/echonpm.txt 15 | `; 16 | const testNpm = `npm run test:npm && ${grepNpm}`; 17 | const echoGulp = ` 18 | echo "hello from gulp task" 19 | > .tmp/echogulp.txt 20 | `; 21 | const grepGulp = ` 22 | grep -q 23 | '^hello from gulp task$' 24 | .tmp/echogulp.txt 25 | `; 26 | const testGulp = `gulp echo:gulp && ${grepGulp}`; 27 | 28 | // TODO: tests won't pass on Windows because of grep 29 | shelter({ 30 | 'test': { 31 | dsc: 'Test gulp-shelter', 32 | cmd: `${lint} && ${clean} && ${testNpm} && ${testGulp}` 33 | }, 34 | 'echo:npm': echoNpm, 35 | 'echo:gulp': echoGulp 36 | }); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-shelter", 3 | "version": "1.0.1", 4 | "description": "A cozy place for your shell scripts", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.com:louisremi/gulp-shelter.git" 8 | }, 9 | "keywords": [ 10 | "gulp", 11 | "gulp-run", 12 | "gulp-shell", 13 | "shell" 14 | ], 15 | "author": "@Louis_Remi", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/louisremi/gulp-shelter/issues" 19 | }, 20 | "homepage": "https://github.com/louisremi/gulp-shelter", 21 | "main": "index.js", 22 | "scripts": { 23 | "test": "gulp test", 24 | "help": "gulp --tasks", 25 | "test:npm": "gulp echo:npm" 26 | }, 27 | "dependencies": { 28 | "lodash": "^4.3.0" 29 | }, 30 | "devDependencies": { 31 | "eslint": "^2.1.0", 32 | "eslint-plugin-mocha": "^2.0.0", 33 | "gulp": "gulpjs/gulp#4.0", 34 | "rimraf": "^2.5.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Louis-Rémi Babé 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var childProcess = require('child_process'); 3 | var _ = require('lodash'); 4 | 5 | var gulp; 6 | 7 | function taskBuilder( name, _task ) { 8 | var task = ( 9 | typeof _task === 'string' ? 10 | { cmd: _task } : 11 | task = _task 12 | ); 13 | 14 | if ( task.cmd ) { 15 | task.cmd = ( 16 | task.cmd 17 | .trim() 18 | // prepend backslash to newlines 19 | .replace(/\n/g, ' \\\n') 20 | // replace tabs 21 | .replace(/\t/g, ' ') 22 | ); 23 | 24 | task.fn = function(done) { 25 | var opts = _.defaults( task.opts || {}, { 26 | cwd: process.cwd(), 27 | env: process.env, 28 | stdio: 'inherit', 29 | usePowerShell: false 30 | }); 31 | var spawnOpts = { 32 | cwd: opts.cwd, 33 | env: opts.env, 34 | stdio: opts.stdio 35 | }; 36 | var oldPath = opts.env.PATH; 37 | var subshell; 38 | 39 | // Include node_modules/.bin on the path 40 | spawnOpts.env.PATH = 41 | path.join( process.cwd(), 'node_modules', '.bin' ) + 42 | path.delimiter + oldPath; 43 | 44 | subshell = process.platform.lastIndexOf('win') === 0 ? 45 | ( opts.usePowerShell ? 46 | childProcess.spawn( 'powershell.exe', 47 | [ '-NonInteractive', '-NoLogo', '-Command', task.cmd ], 48 | spawnOpts ) : 49 | childProcess.spawn( 'cmd.exe', 50 | [ '/c', task.cmd ], spawnOpts ) 51 | ) : 52 | childProcess.spawn( 'sh', [ '-c', task.cmd ], spawnOpts ); 53 | 54 | subshell.once('close', function(code) { 55 | if ( code !== 0 ) { 56 | var err = new Error( 57 | 'Command `' + task.cmd + '` exited with code ' + code 58 | ); 59 | 60 | err.status = code; 61 | return done(err); 62 | } 63 | 64 | done(null); 65 | }); 66 | }; 67 | } 68 | 69 | if ( task.fn ) { 70 | gulp.task( name, task.fn ); 71 | if ( 'dsc' in task ) { 72 | task.fn.description = _task.dsc; 73 | } 74 | if ( 'flg' in task ) { 75 | task.fn.flags = _task.flg; 76 | } 77 | } 78 | } 79 | 80 | function shelter( tasks ) { 81 | if ( !tasks ) { 82 | return; 83 | } 84 | 85 | _.forEach(tasks, function( prop, key ) { 86 | taskBuilder( key, prop ); 87 | }); 88 | } 89 | 90 | shelter.task = taskBuilder; 91 | 92 | // module must be required with `require('gulp-shelter')(gulp)` 93 | // to be able to register tasks 94 | module.exports = function( _gulp ) { 95 | gulp = _gulp; 96 | return shelter; 97 | }; 98 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch" 10 | ], 11 | "requireOperatorBeforeLineBreak": true, 12 | "requireParenthesesAroundIIFE": true, 13 | "requireCommaBeforeLineBreak": true, 14 | "requireCamelCaseOrUpperCaseIdentifiers": true, 15 | "requireDotNotation": "except_snake_case", 16 | "requireSpacesInForStatement": true, 17 | "requireSpaceBetweenArguments": true, 18 | "maximumLineLength": { 19 | "value": 80, 20 | "tabSize": 4, 21 | "allowUrlComments": true, 22 | "allowRegex": true 23 | }, 24 | "validateQuoteMarks": { "mark": "'", "escape": true }, 25 | 26 | "disallowMixedSpacesAndTabs": "smart", 27 | "disallowTrailingWhitespace": true, 28 | "disallowMultipleLineStrings": true, 29 | "disallowTrailingComma": true, 30 | 31 | "requireSpaceBeforeBlockStatements": true, 32 | "requireSpacesInFunctionExpression": { 33 | "beforeOpeningCurlyBrace": true 34 | }, 35 | "requireSpaceAfterKeywords": [ 36 | "if", 37 | "else", 38 | "for", 39 | "while", 40 | "do", 41 | "switch", 42 | "return", 43 | "try", 44 | "catch" 45 | ], 46 | "requireSpacesInsideObjectBrackets": "allButNested", 47 | "requireSpacesInsideArrayBrackets": "allButNested", 48 | "requireSpacesInConditionalExpression": true, 49 | "requireSpaceAfterBinaryOperators": true, 50 | "requireLineFeedAtFileEnd": true, 51 | "requireSpaceBeforeBinaryOperators": [ 52 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 53 | "&=", "|=", "^=", "+=", 54 | 55 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 56 | "|", "^", "&&", "||", "===", "==", ">=", 57 | "<=", "<", ">", "!=", "!==" 58 | ], 59 | "requireSpacesInAnonymousFunctionExpression": { 60 | "beforeOpeningCurlyBrace": true 61 | }, 62 | "requireSpacesInNamedFunctionExpression": { 63 | "beforeOpeningCurlyBrace": true 64 | }, 65 | "validateLineBreaks": "LF", 66 | 67 | "disallowKeywords": [ "with" ], 68 | "disallowKeywordsOnNewLine": [ "else" ], 69 | "disallowSpacesInFunctionExpression": { 70 | "beforeOpeningRoundBrace": true 71 | }, 72 | "disallowSpacesInNamedFunctionExpression": { 73 | "beforeOpeningRoundBrace": true 74 | }, 75 | "disallowSpacesInAnonymousFunctionExpression": { 76 | "beforeOpeningRoundBrace": true 77 | }, 78 | "disallowSpaceAfterObjectKeys": true, 79 | "disallowSpaceAfterPrefixUnaryOperators": true, 80 | "disallowSpaceBeforePostfixUnaryOperators": true, 81 | "disallowSpaceBeforeBinaryOperators": [ ",", ":" ], 82 | "disallowMultipleLineBreaks": true 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-shelter 2 | A cozy place for your shell tasks 3 | 4 | ## Npm Scripts as a build tool, or Gulp tasks? 5 | 6 | Gulp-shelter brings the best of both worlds and makes everyone in the team happy. Read the [blog post](https://medium.com/@Louis_Remi/npm-scripts-vs-gulp-round-2-feat-gulp-shelter-c003db6a148b). 7 | 8 | ## Installing 9 | 10 | This is the recommended installation for a fresh **new project** with **[Node v4+](https://nodejs.org/)** installed locally. 11 | See [legacy config](#legacy-config) below if you're in a different situation. 12 | 13 | `npm install --save-dev gulp-shelter gulpjs/gulp#4.0` 14 | 15 | (Having Gulp v3 installed globally should not be a problem.) 16 | 17 | ## Using 18 | 19 | ```js 20 | const gulp = require('gulp'); 21 | const shelter = require('gulp-shelter')(gulp); 22 | const BRANCH = process.env.TRAVIS_BRANCH; 23 | 24 | // project config 25 | const project = 'projectName'; 26 | const main = `src/main.js`; 27 | const dest = `dist/${project}.js`; 28 | const domain = ( 29 | BRANCH === 'master' ? 'www.domain.com' : 30 | BRANCH === 'develop' ? 'dev.domain.com' : 31 | false 32 | ); 33 | 34 | // commands and fragments 35 | const browserifyFlags = ` 36 | --standalone ${project} 37 | --transform [ babelify --presets [ es2015 react ] ] 38 | --debug`; // --debug enables source-map 39 | const browserify = `browserify ${main} ${browserifyFlags}`; 40 | const exorcist = `exorcist ${dest}.map > ${dest}`; 41 | const watchify = `watchify ${main} ${browserifyFlags} -o ${dest}`; 42 | const browsersync = `browser-sync start --server --files "${dest}, index.html"`; 43 | const surge = domain ? `surge --project ./dist --domain ${domain}` : ':'; // ':' is noop in bash 44 | 45 | // tasks definitions 46 | shelter({ 47 | build: { 48 | dsc: `generate ${project} lib and external source-map`, 49 | cmd: `${browserify} | ${exorcist}` 50 | }, 51 | serve: { 52 | dsc: 'Open index.html and live-reload on changes', 53 | cmd: `${watchify} & ${browsersync}` 54 | }, 55 | deploy: { 56 | dsc: 'Run by Travis to automatically deploy on Surge', 57 | cmd: `${browserify} | ${exorcist} && ${surge}` 58 | } 59 | }); 60 | ``` 61 | 62 | Note that it's not required that your command consist of `{ dsc: …, cmd: … }` objects. 63 | A string will do as well, but adding a description will make your tasks discoverable and explicit using `gulp --tasks`; 64 | 65 | ### No global Gulp required 66 | 67 | One of the advantages of npm scripts is that they do not require any other command line tools to be installed. 68 | You can get the same benefit by adding the following lines to your `package.json`: 69 | ```json 70 | "scripts": { 71 | "gulp": "gulp", 72 | "help": "gulp --tasks" 73 | }, 74 | ``` 75 | 76 | Now tasks can be discovered by running **`npm run help`**, and individual tasks can be run with **`npm run gulp -- `**. 77 | 78 | ## Legacy config 79 | 80 | ### Older versions of Node 81 | 82 | Versions of Node prior to v4 did not support template strings out of the box. 83 | Thankfully, Gulp v3.9+ is able to read special gulpfiles that contain ES6 syntax. 84 | 85 | Install the following additional dependencies: 86 | 87 | `npm install --save-dev babel-core babel-register babel-preset-es2015` 88 | 89 | Setup Babel to use es2015, if you haven't already: 90 | 91 | `echo '{ "presets": ["es2015"] }' > .babelrc` 92 | 93 | And finally name or rename your gulpfile to **`gulpfile.babel.js`**. 94 | 95 | ### Older versions of Gulp 96 | 97 | If you plan on using the `npm run help` task described above with Gulp 3.X, 98 | you will need a local version of gulp-cli for task descriptions to be displayed in the console. 99 | 100 | `npm install --save-dev gulp-cli` 101 | 102 | ## License 103 | 104 | MIT 105 | --------------------------------------------------------------------------------