├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jscsrc ├── .jshintrc ├── README.md ├── dev.png ├── gulp ├── config.js ├── tasks │ ├── browserify.js │ ├── clean.js │ ├── default.js │ ├── imagemin.js │ ├── jade.js │ ├── lint.js │ ├── localhost.js │ ├── rev.js │ ├── sass.js │ ├── uglify.js │ └── watch.js └── util │ ├── error-converter.js │ └── error-notifier.js ├── gulpfile.js ├── npm-shrinkwrap.json ├── package.json ├── prod.png └── src ├── images └── js-logo-badge-512.png ├── index.jade ├── js ├── app.js └── templates │ └── layout.jade └── sass ├── _variables.sass └── app.sass /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | 7 | [*.js] 8 | charset = utf-8 9 | indent_size = 2 10 | indent_style = tab 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /node_modules/* 3 | /npm-debug.log 4 | /.sass-cache/* -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowDanglingUnderscores": true, 3 | "disallowEmptyBlocks": true, 4 | "disallowFunctionDeclarations": true, 5 | "disallowKeywordsOnNewLine": [ 6 | "else" 7 | ], 8 | "disallowKeywords": [ 9 | "with" 10 | ], 11 | "disallowMixedSpacesAndTabs": true, 12 | "disallowMultipleLineBreaks": true, 13 | "disallowMultipleLineStrings": true, 14 | "disallowMultipleVarDecl": true, 15 | "disallowNewlineBeforeBlockStatements": true, 16 | "disallowOperatorBeforeLineBreak": [ 17 | "." 18 | ], 19 | "disallowPaddingNewlinesInBlocks": true, 20 | "disallowQuotedKeysInObjects": true, 21 | "disallowSpaceAfterObjectKeys": true, 22 | "disallowSpaceAfterPrefixUnaryOperators": true, 23 | "disallowSpaceBeforePostfixUnaryOperators": true, 24 | "disallowSpacesInCallExpression": true, 25 | "disallowSpacesInsideParentheses": true, 26 | "disallowTrailingComma": true, 27 | "disallowTrailingWhitespace": true, 28 | "disallowYodaConditions": true, 29 | "maximumLineLength": 120, 30 | "requireBlocksOnNewline": true, 31 | "requireCamelCaseOrUpperCaseIdentifiers": true, 32 | "requireCapitalizedConstructors": true, 33 | "requireCommaBeforeLineBreak": true, 34 | "requireCurlyBraces": [ 35 | "if", 36 | "else", 37 | "for", 38 | "while", 39 | "do", 40 | "try", 41 | "catch" 42 | ], 43 | "requireDotNotation": true, 44 | "requireLineBreakAfterVariableAssignment": true, 45 | "requireLineFeedAtFileEnd": true, 46 | "requirePaddingNewlinesBeforeKeywords": [ 47 | "do", 48 | "for", 49 | "if", 50 | "switch", 51 | "try", 52 | "while", 53 | "function" 54 | ], 55 | "requireParenthesesAroundIIFE": true, 56 | "requireSpaceAfterBinaryOperators": true, 57 | "requireSpaceAfterKeywords": true, 58 | "requireSpaceAfterLineComment": true, 59 | "requireSpaceBeforeBinaryOperators": true, 60 | "requireSpaceBeforeBlockStatements": true, 61 | "requireSpaceBeforeKeywords": [ 62 | "else", 63 | "while", 64 | "catch" 65 | ], 66 | "requireSpaceBeforeObjectValues": true, 67 | "requireSpacesInConditionalExpression": true, 68 | "requireSpacesInForStatement": true, 69 | "requireSpacesInNamedFunctionExpression": { 70 | "beforeOpeningRoundBrace": true 71 | }, 72 | // https://github.com/jscs-dev/node-jscs/issues/923 73 | // "requireSpacesInsideArrayBrackets": { 74 | // "allExcept": [ 75 | // "[", 76 | // "]", 77 | // "{", 78 | // "}" 79 | // ] 80 | // }, 81 | "requireSpacesInsideObjectBrackets": { 82 | "allExcept": [ 83 | "}", 84 | ")" 85 | ] 86 | }, 87 | "safeContextKeyword": "self", 88 | "validateIndentation": "\t", 89 | "validateLineBreaks": "LF", 90 | "validateParameterSeparator": ", ", 91 | "validateQuoteMarks": { 92 | "escape": true, 93 | "mark": "'" 94 | } 95 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 1000, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 4, // {int} Number of spaces to use for indentation 16 | "latedef" : false, // true: Require variables/functions to be defined before being used 17 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` & `--` 23 | "quotmark" : false, // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : "vars", // true: Require all defined variables be used 30 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode 31 | "maxparams" : false, // {int} Max number of formal params allowed per function 32 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 33 | "maxstatements" : false, // {int} Max number statements per function 34 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 35 | "maxlen" : false, // {int} Max number of characters per line 36 | 37 | // Relaxing 38 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 39 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 40 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 41 | "eqnull" : false, // true: Tolerate use of `== null` 42 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 43 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 44 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 45 | // (ex: `for each`, multiple try/catch, function expression…) 46 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 47 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 48 | "funcscope" : false, // true: Tolerate defining variables inside control statements 49 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 50 | "iterator" : false, // true: Tolerate using the `__iterator__` property 51 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 52 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 53 | "laxcomma" : false, // true: Tolerate comma-first style coding 54 | "loopfunc" : true, // true: Tolerate functions being defined in loops 55 | "multistr" : false, // true: Tolerate multi-line strings 56 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 57 | "notypeof" : false, // true: Tolerate invalid typeof operator values 58 | "proto" : false, // true: Tolerate using the `__proto__` property 59 | "scripturl" : false, // true: Tolerate script-targeted URLs 60 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 61 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 62 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 63 | "validthis" : false, // true: Tolerate using this in a non-constructor function 64 | 65 | // Environments 66 | "browser" : true, // Web Browser (window, document, etc) 67 | "browserify" : true, // Browserify (node.js code in the browser) 68 | "couch" : false, // CouchDB 69 | "devel" : false, // Development/debugging (alert, confirm, etc) 70 | "dojo" : false, // Dojo Toolkit 71 | "jasmine" : false, // Jasmine 72 | "jquery" : false, // jQuery 73 | "mocha" : false, // Mocha 74 | "mootools" : false, // MooTools 75 | "node" : false, // Node.js 76 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 77 | "prototypejs" : false, // Prototype and Scriptaculous 78 | "qunit" : false, // QUnit 79 | "rhino" : false, // Rhino 80 | "shelljs" : false, // ShellJS 81 | "worker" : false, // Web Workers 82 | "wsh" : false, // Windows Scripting Host 83 | "yui" : false, // Yahoo User Interface 84 | 85 | // Custom Globals 86 | "globals": { 87 | "app": false, 88 | "console": false 89 | } 90 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gulp-sass-jade-starter 2 | ====================== 3 | 4 | Starter Gulp project with examples of how to accomplish some common tasks and workflows. 5 | 6 | Includes the following tools, tasks, and workflows: 7 | 8 | - [SASS](http://sass-lang.com/) with [autoprefixer](https://github.com/sindresorhus/gulp-autoprefixer) 9 | - [Browserify](http://browserify.org/) using [jadeify](https://github.com/domenic/jadeify) transform 10 | - [Watchify](https://github.com/substack/watchify) (caching version of browserify for super fast rebuilds) 11 | - [BrowserSync](http://browsersync.io) for live reloading and a static server 12 | - [Image optimization](https://www.npmjs.com/package/gulp-imagemin) 13 | - Separate compression task using [Uglify.js](https://github.com/terinjokes/gulp-uglify) for production builds 14 | - Static asset revisioning by appending content hash to filenames using [gulp-rev](https://github.com/sindresorhus/gulp-rev) 15 | 16 | - - - 17 | 18 | ## Installation 19 | 20 | If you've never used Node or npm before, you'll need to install Node. 21 | If you use homebrew, do: 22 | 23 | ``` 24 | brew install node 25 | ``` 26 | 27 | Otherwise, you can download and install from [here](http://nodejs.org/download/). 28 | 29 | #### Install npm dependencies 30 | ``` 31 | npm install 32 | ``` 33 | 34 | This runs through all dependencies listed in `package.json` and downloads them to a `node_modules` folder in your project directory. 35 | 36 | #### The `gulp` command 37 | To run the version of gulp installed local to the project, in the root of your this project, you'd run 38 | 39 | ``` 40 | ./node_modules/.bin/gulp 41 | ``` 42 | 43 | **WAT.** Why can't I just run `gulp`? Well, you could install gulp globally with `npm install -g gulp`, which will add the gulp script to your global bin folder, but it's always better to use the version that's specified in your project's package.json. My solution to this is to simply alias `./node_modules/.bin/gulp` to `gulp`. Open up `~/.zshrc` or `~./bashrc` and add the following line: 44 | 45 | ``` 46 | alias gulp='node_modules/.bin/gulp' 47 | ``` 48 | Now, running `gulp` in the project directory will use the version specified and installed from the `package.json` file. 49 | 50 | - - - 51 | 52 | ## Development 53 | 54 | ```shell 55 | gulp 56 | ``` 57 | 58 | This will run the `default` gulp task defined in `gulp/tasks/default.js`. 59 | Without further arguments, the task is run in development mode, where it has these dependencies: 60 | 61 | ![task dependencies dev](dev.png) 62 | 63 | - The `clean` task cleans your dist folder 64 | - `lint` checks the `.js` files in both source and gulp directories for codestyle validations (rules are defined in `.jscsrc` and `.jshintrc`) and shows them on both the console and the operating system notification area 65 | - `image` copies images from source to dist folder 66 | - `jade` compiles `index.jade` from source to `index.html` in dist 67 | - `sass` compiles `.sass` files from source to a single `.css` files with inline sourcemap in dist 68 | - `browserify` runs using `watchify` and the `jadeify` transform, compiling `app.js` from source with all its dependencies, including jade templates, to a single `app.js` file with inline sourcemap in dist 69 | - `localhost` boots up a local web server that serves your dist folder at `localhost:3000` 70 | - `watch` starts watching source files and will re-run the appropriate tasks when those files change (excluding the compilation of `.js` files, which is handled by `watchify` 71 | 72 | - - - 73 | 74 | ## Starting a production build 75 | 76 | ```shell 77 | gulp --prod 78 | ``` 79 | 80 | This will run the default task defined in `gulp/tasks/default.js` in production mode, which has these task dependencies: 81 | 82 | ![task dependencies prod](prod.png) 83 | 84 | - The `clean` task cleans your dist folder 85 | - `lint` checks the `.js` files in both source and gulp directories for codestyle validations (rules are defined in `.jscsrc` and `.jshintrc`) and cancels the build when an error is encountered 86 | - `image` places minified copies of images from source folder in dist folder 87 | - `sass` compiles `.sass` files from source to a `.css` files with a sourcemap comment and corresponding `.css.map` files 88 | - `browserify` runs using the `jadeify` transform, compiling `app.js` from source with all its dependencies, including handlebars templates, to `app.js` and `app.js.map` files in dist 89 | - `uglify` minfies the resulting `app.js` while keeping sourcemaps intact 90 | - `rev` appends content hashes to the filenames of all image, js and css files in dist folder, deletes the original files and writes a `rev-manifest.json` mapping the original filesnames to those with content hashes 91 | - `jade` reads the `rev-manifest.json` and compiles `index.jade` from source to `index.html` in dist, changing the paths of the used files to the hashed ones. 92 | 93 | ## Configuration 94 | All paths and plugin settings have been abstracted into a centralized config object in `gulp/config.js`. Adapt the paths and settings to the structure and needs of your project. -------------------------------------------------------------------------------- /dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codepunkt/gulp-sass-jade-starter/6dfe67c52baae633909ce9123fd8d774a04c68f7/dev.png -------------------------------------------------------------------------------- /gulp/config.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); 2 | var util = require('gulp-util'); 3 | var base = '.'; 4 | var dist = base + '/dist'; 5 | 6 | module.exports = { 7 | path: { 8 | dist: dist, 9 | gulp: base + '/gulp', 10 | src: base + '/src' 11 | }, 12 | 13 | autoprefixer: { 14 | browsers: '> 5% in DE' 15 | }, 16 | 17 | browserify: { 18 | // Enable source maps 19 | debug: true, 20 | // Require files without specifying extensions 21 | extensions: [ '.jade' ] 22 | }, 23 | 24 | localhost: { 25 | server: { 26 | baseDir: dist, 27 | middleware: function (req, res, next) { 28 | if (url.parse(req.url).pathname.indexOf('.') === -1) { 29 | req.url = '/index.html'; 30 | } 31 | next(); 32 | } 33 | }, 34 | https: false, 35 | open: false 36 | }, 37 | 38 | sass: { 39 | indentedSyntax: true 40 | }, 41 | 42 | lint: { 43 | cacheKey: 'lint', 44 | errorConverter: require('./util/error-converter'), 45 | errorNotifier: require('./util/error-notifier'), 46 | jscs: { 47 | configPath: base + '/.jscsrc' 48 | } 49 | }, 50 | 51 | base: { 52 | logError: function (err) { 53 | util.log(err.message); 54 | this.emit('end'); 55 | }, 56 | throwError: function (err) { 57 | throw err; 58 | } 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /gulp/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | var browserify = require('browserify'); 2 | var browserSync = require('browser-sync'); 3 | var watchify = require('watchify'); 4 | var exorcist = require('exorcist'); 5 | var source = require('vinyl-source-stream'); 6 | var _ = require('lodash'); 7 | 8 | module.exports = function (gulp, config, $) { 9 | var prod = $.util.env.prod; 10 | 11 | gulp.task('browserify', [ 'clean', 'lint' ], function () { 12 | var bundle = function () { 13 | return app.bundle() 14 | .on('error', config.base[ prod ? 'throwError' : 'logError' ]) 15 | .pipe(prod ? exorcist(config.path.dist + '/js/app.js.map') : $.util.noop()) 16 | .pipe(source('app.js')) 17 | .pipe(gulp.dest(config.path.dist + '/js')) 18 | .pipe(browserSync.reload({ stream: true })); 19 | }; 20 | 21 | var app = browserify( 22 | config.path.src + '/js/app.js', 23 | _.defaults(config.browserify, watchify.args) 24 | ); 25 | 26 | app = watchify(app); 27 | app.on('update', bundle); 28 | 29 | return bundle(); 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var del = require('del'); 2 | 3 | module.exports = function (gulp, config) { 4 | gulp.task('clean', function (callback) { 5 | del([ config.path.dist + '/**' ], callback); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /gulp/tasks/default.js: -------------------------------------------------------------------------------- 1 | module.exports = function (gulp, config, $) { 2 | var prod = $.util.env.prod; 3 | 4 | gulp.task('default', [ prod ? 'jade' : 'watch' ], function (callback) { 5 | if (prod) { 6 | gulp.src('/') 7 | .pipe($.notify({ 8 | title: 'Task Builder', 9 | message: 'Successfully built application' 10 | })) 11 | .pipe(prod ? $.exit() : $.util.noop()); 12 | } 13 | callback(); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /gulp/tasks/imagemin.js: -------------------------------------------------------------------------------- 1 | module.exports = function (gulp, config, $) { 2 | gulp.task('imagemin', [ 'clean' ], function () { 3 | return gulp.src(config.path.src + '/images/*') 4 | .pipe($.util.env.prod ? $.imagemin() : $.util.noop()) 5 | .pipe(gulp.dest(config.path.dist + '/images')); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /gulp/tasks/jade.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var reload = require('browser-sync').reload; 3 | 4 | module.exports = function (gulp, config, $) { 5 | var jade = function (maps) { 6 | try { 7 | maps = JSON.parse(fs.readFileSync(config.path.dist + '/rev-manifest.json', 'utf8')); 8 | } catch (e) { 9 | maps = {}; 10 | } 11 | 12 | var locals = { 13 | maps: JSON.stringify(maps), 14 | url: function (url) { 15 | return maps[url] || url; 16 | } 17 | }; 18 | 19 | return gulp.src(config.path.src + '/*.jade') 20 | .pipe($.jade({ locals: locals })) 21 | .pipe(gulp.dest(config.path.dist)) 22 | .pipe(reload({ stream: true })); 23 | }; 24 | 25 | gulp.task('jade', [ $.util.env.prod ? 'rev' : 'clean' ], jade); 26 | gulp.task('jade:watch', jade); 27 | }; 28 | -------------------------------------------------------------------------------- /gulp/tasks/lint.js: -------------------------------------------------------------------------------- 1 | module.exports = function (gulp, config, $) { 2 | gulp.task('lint', function () { 3 | var sources = [ 4 | config.path.src + '/**/*.js', 5 | config.path.gulp + '/**/*.js' 6 | ]; 7 | 8 | return gulp.src(sources) 9 | // run on changed files only 10 | .pipe($.cached(config.lint.cacheKey)) 11 | // run jshint 12 | .pipe($.jshint()) 13 | // run jscs, break on errors in production 14 | .pipe($.jscs(config.lint.jscs)) 15 | .on('error', $.util.env.prod ? config.base.throwError : $.util.noop) 16 | // add jscs errors to jshint errors 17 | .pipe($.jscsStylish()) 18 | // show errors on console and as notification 19 | .pipe($.jshint.reporter('jshint-stylish')) 20 | .pipe($.tap(config.lint.errorNotifier)) 21 | // break on jshint errors in production 22 | .pipe($.util.env.prod ? $.jshint.reporter('fail') : $.util.noop()); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /gulp/tasks/localhost.js: -------------------------------------------------------------------------------- 1 | var browserSync = require('browser-sync'); 2 | 3 | module.exports = function (gulp, config, $) { 4 | gulp.task('localhost', [ 5 | 'jade', 6 | 'sass', 7 | 'imagemin', 8 | $.util.env.prod ? 'uglify' : 'browserify' 9 | ], function () { 10 | browserSync(config.localhost); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /gulp/tasks/rev.js: -------------------------------------------------------------------------------- 1 | var del = require('del'); 2 | var es = require('event-stream'); 3 | var paths = require('vinyl-paths'); 4 | 5 | module.exports = function (gulp, config, $) { 6 | gulp.task('rev', [ 'sass', 'uglify', 'imagemin' ], function (callback) { 7 | var image = paths(); 8 | var source = paths(); 9 | var map = paths(); 10 | var onlyMaps = $.filter([ '**/*.*', '!**/*.map' ]); 11 | 12 | var imageStream = gulp.src(config.path.dist + '/**/*.{jpg,png,svg}') 13 | .pipe(image) 14 | .pipe($.rev()); 15 | 16 | var sourceStream = gulp.src(config.path.dist + '/**/*.{js,css}') 17 | .pipe($.sourcemaps.init({ loadMaps: true })) 18 | .pipe(source) 19 | .pipe($.rev()) 20 | .pipe($.sourcemaps.write('.')); 21 | 22 | gulp.src(config.path.dist + '/**/*.map').pipe(map); 23 | 24 | es.merge(imageStream, sourceStream) 25 | .pipe(gulp.dest(config.path.dist)) 26 | .pipe(onlyMaps) 27 | .pipe($.size({ showFiles: true, gzip: true })) 28 | .pipe(onlyMaps.restore()) 29 | .pipe($.rev.manifest()) 30 | .pipe(gulp.dest(config.path.dist)) 31 | .on('end', function () { 32 | del([].concat(image.paths, source.paths, map.paths), callback); 33 | }); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | var assign = require('lodash.assign'); 2 | var reload = require('browser-sync').reload; 3 | 4 | module.exports = function (gulp, config, $) { 5 | var prod = $.util.env.prod; 6 | 7 | var sass = function () { 8 | var source = config.path.src + '/sass/app.sass'; 9 | 10 | return gulp.src(source) 11 | .pipe($.sourcemaps.init()) 12 | .pipe($.sass(assign({}, config.sass, { 13 | errLogToConsole: !prod, 14 | outputStyle: prod ? 'compressed' : 'nested' 15 | }))) 16 | .pipe($.autoprefixer(config.autoprefixer)) 17 | .pipe($.sourcemaps.write.apply(null, prod ? [ '.' ] : [])) 18 | .pipe(gulp.dest(config.path.dist + '/css')) 19 | .pipe(reload({ stream: true })); 20 | }; 21 | 22 | gulp.task('sass', [ 'clean' ], sass); 23 | gulp.task('sass:watch', sass); 24 | }; 25 | -------------------------------------------------------------------------------- /gulp/tasks/uglify.js: -------------------------------------------------------------------------------- 1 | module.exports = function (gulp, config, $) { 2 | gulp.task('uglify', [ 'browserify' ], function () { 3 | return gulp.src(config.path.dist + '/js/*.js') 4 | // load existing source maps 5 | .pipe($.sourcemaps.init({ loadMaps: true })) 6 | // minify them 7 | .pipe($.uglify()) 8 | // write minified file and sourcemap to disk 9 | .pipe($.sourcemaps.write()) 10 | .pipe(gulp.dest(config.path.dist + '/js')); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /gulp/tasks/watch.js: -------------------------------------------------------------------------------- 1 | module.exports = function (gulp, config, $) { 2 | // Watch files to rebuild on changes. 3 | // watching js files for recompilation with browserify is done by watchify 4 | gulp.task('watch', [ 'localhost' ], function () { 5 | gulp.watch([ 6 | config.path.src + '/*.jade' 7 | ], [ 'jade:watch' ]); 8 | 9 | gulp.watch([ 10 | config.path.src + '/sass/**/*.sass' 11 | ], [ 'sass:watch' ]); 12 | 13 | gulp.watch([ 14 | config.path.src + '/**/*.js', 15 | config.path.gulp + '/**/*.js' 16 | ], [ 'lint' ]); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /gulp/util/error-converter.js: -------------------------------------------------------------------------------- 1 | module.exports = function (file) { 2 | // return if there are no jscs errors 3 | if (file.jscs.success) { 4 | return; 5 | } 6 | 7 | // otherwise, set jshint success to `false` and add jscs errors 8 | // to jshint results as warnings 9 | file.jshint.success = false; 10 | file.jshint.results = file.jshint.results || []; 11 | file.jscs.errors.forEach(function (error) { 12 | file.jshint.results.push({ 13 | file: file.path, 14 | error: { 15 | character: error.column, 16 | code: 'W ' + error.rule, 17 | line: error.line, 18 | reason: error.message 19 | } 20 | }); 21 | }); 22 | 23 | // sort errors by line number 24 | file.jshint.results.sort(function (a, b) { 25 | return a.error.line - b.error.line; 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /gulp/util/error-notifier.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var notifier = require('node-notifier'); 3 | 4 | module.exports = function (file) { 5 | if (file.jshint.success) { 6 | return; 7 | } 8 | 9 | var first = file.jshint.results[0]; 10 | 11 | notifier.notify({ 12 | title: path.relative(process.cwd(), file.path) + ':' + first.error.line, 13 | message: first.error.reason 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var glob = require('glob'); 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | var config = require('./gulp/config'); 5 | 6 | glob.sync('./gulp/tasks/**/*.js').forEach(function (fileName) { 7 | require(fileName)(gulp, config, $); 8 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-sass-jade-starter", 3 | "version": "1.0.0-alpha1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "author": { 11 | "email": "gonsfx@googlemail.com", 12 | "name": "Christoph Werner" 13 | }, 14 | "browserify": { 15 | "transform": [ 16 | "jadeify" 17 | ] 18 | }, 19 | "license": "BSD-2-Clause", 20 | "dependencies": { 21 | "browser-sync": "^1.8.3", 22 | "browserify": "8.0.1", 23 | "del": "^1.1.1", 24 | "event-stream": "^3.2.1", 25 | "exorcist": "^0.1.6", 26 | "glob": "^4.3.2", 27 | "gulp": "^3.8.10", 28 | "gulp-autoprefixer": "^2.1.0", 29 | "gulp-cached": "^1.0.2", 30 | "gulp-debug": "^2.0.0", 31 | "gulp-exit": "0.0.2", 32 | "gulp-filter": "^2.0.0", 33 | "gulp-if": "^1.2.5", 34 | "gulp-imagemin": "^2.1.0", 35 | "gulp-jade": "^0.10.0", 36 | "gulp-jscs": "^1.4.0", 37 | "gulp-jscs-stylish": "^1.0.0", 38 | "gulp-jshint": "^1.9.0", 39 | "gulp-lab": "^1.0.4", 40 | "gulp-load-plugins": "^0.8.0", 41 | "gulp-notify": "^2.1.0", 42 | "gulp-plumber": "^0.6.6", 43 | "gulp-rename": "^1.2.0", 44 | "gulp-rev": "^3.0.0", 45 | "gulp-sass": "^1.3.3", 46 | "gulp-size": "^1.2.0", 47 | "gulp-sourcemaps": "^1.3.0", 48 | "gulp-tap": "^0.1.3", 49 | "gulp-uglify": "^1.0.2", 50 | "gulp-util": "^3.0.2", 51 | "gulp-watch": "^3.0.0", 52 | "jade": "^1.8.2", 53 | "jadeify": "^4.0.0", 54 | "jshint-stylish": "^1.0.0", 55 | "lodash": "~2.4.1", 56 | "lodash.assign": "^3.1.0", 57 | "map-stream": "0.0.5", 58 | "node-notifier": "^4.0.3", 59 | "vinyl-paths": "^1.0.0", 60 | "vinyl-source-stream": "^1.0.0", 61 | "vinyl-transform": "^1.0.0", 62 | "watchify": "^2.2.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /prod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codepunkt/gulp-sass-jade-starter/6dfe67c52baae633909ce9123fd8d774a04c68f7/prod.png -------------------------------------------------------------------------------- /src/images/js-logo-badge-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codepunkt/gulp-sass-jade-starter/6dfe67c52baae633909ce9123fd8d774a04c68f7/src/images/js-logo-badge-512.png -------------------------------------------------------------------------------- /src/index.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='de') 3 | head 4 | meta(charset='utf-8') 5 | title App 6 | link(rel='stylesheet', href=url('css/app.css')) 7 | script. 8 | var maps = #{maps}; 9 | window.url = #{url}; 10 | body 11 | script(src=url('js/app.js')) 12 | -------------------------------------------------------------------------------- /src/js/app.js: -------------------------------------------------------------------------------- 1 | var layout = require('./templates/layout'); 2 | 3 | window.app = { 4 | init: function () { 5 | document.querySelector('body').innerHTML = layout({}); 6 | } 7 | }; 8 | 9 | window.app.init(); 10 | -------------------------------------------------------------------------------- /src/js/templates/layout.jade: -------------------------------------------------------------------------------- 1 | h1 App 2 | section.app 3 | header.app__menu 4 | h1 header 5 | section.app__main 6 | img(src=url('images/js-logo-badge-512.png')) 7 | footer.app__footer 8 | p Footer -------------------------------------------------------------------------------- /src/sass/_variables.sass: -------------------------------------------------------------------------------- 1 | $primary-color: #bada55 2 | 3 | .app 4 | &__menu 5 | transform: scale(1.5, 0.9) translate(125px, -10px) rotate(-2deg) 6 | color: #c0ff33 -------------------------------------------------------------------------------- /src/sass/app.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | 3 | body 4 | background: $primary-color --------------------------------------------------------------------------------