├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── bin └── setup ├── config.rb ├── environments ├── development.rb └── production.rb ├── gulpfile.js ├── config.json ├── index.js ├── lib │ ├── __tests__ │ │ └── pathToUrl.test.js │ ├── compileLogger.js │ ├── getEnabledTasks.js │ ├── handleErrors.js │ ├── pathToUrl.js │ ├── prettifyTime.js │ ├── repeatString.js │ ├── webpack-multi-config.js │ └── webpackManifest.js └── tasks │ ├── browserSync.js │ ├── clean.js │ ├── css.js │ ├── default.js │ ├── deploy.js │ ├── fonts.js │ ├── iconFont │ ├── generateIconSass.js │ ├── index.js │ └── template.sass │ ├── images.js │ ├── production.js │ ├── rev │ ├── index.js │ ├── rev-assets.js │ ├── rev-css.js │ ├── rev-update-references.js │ └── update-html.js │ ├── server.js │ ├── sizereport.js │ ├── static.js │ ├── svgSprite.js │ ├── watch.js │ └── webpackProduction.js ├── helpers ├── README.md └── gulp_asset_helper.rb ├── karma.conf.js ├── lib └── gulp.rb ├── package.json └── source ├── images └── middleman-logo.svg ├── index.html.erb ├── javascripts ├── _test.js └── all.js ├── layouts └── layout.erb ├── static └── favicon.ico └── stylesheets ├── _normalize.scss └── site.scss /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore the build directory 11 | /build 12 | 13 | # Ignore cache 14 | /.sass-cache 15 | /.cache 16 | 17 | # Ignore .DS_store file 18 | .DS_Store 19 | node_modules 20 | .tmp 21 | Gemfile.lock 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [1.4.2] - 2016-08-20 6 | 7 | ### Fixed 8 | - Add missing dependencies needed when installing 9 | 10 | ## [1.4.1] - 2016-08-20 11 | 12 | ### Changed 13 | - Update Middleman 14 | - Update Gulp Starter 15 | 16 | ## [1.4.0] - 2016-04-18 17 | 18 | ### Fixed 19 | - Add the `html` task back (it was missing for some reason) 20 | 21 | ### Added 22 | - Add coffeescript support 23 | - Add `node_modules` search for webpack requires 24 | 25 | ## [1.3.1] - 2016-04-11 26 | 27 | ### Changed 28 | - Remove unrelated comments 29 | - Correctly reference the icon sprite in Rails helper 30 | 31 | ## [1.3.0] - 2016-04-10 32 | 33 | ### Added 34 | - Utilise Middleman's environment configs 35 | - Added a setup script to install dependencies 36 | - Temp directory for external_pipeline is now a variable set from `config.json` only. 37 | 38 | ### Changed 39 | - Update the README with more information 40 | 41 | ## [1.2.0] - 2016-04-06 42 | 43 | ### Added 44 | - #4: File revving with Middleman Ruby helpers ported from Gulp Starter 45 | 46 | ## [1.1.0] - 2016-04-04 47 | 48 | ### Fixed 49 | - #1: Ignore duplicate files when running `middleman build` 50 | 51 | ### Added 52 | - Add minification of HTML 53 | 54 | ## [1.0.0] - 2016-04-04 55 | 56 | ### Added 57 | - Initial repo demo for screencast 58 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # If you do not have OpenSSL installed, change 2 | # the following line to use 'http://' 3 | source 'https://rubygems.org' 4 | 5 | # For faster file watcher updates on Windows: 6 | gem 'wdm', '~> 0.1.0', platforms: [:mswin, :mingw] 7 | 8 | # Windows does not come with time zone data 9 | gem 'tzinfo-data', platforms: [:mswin, :mingw, :jruby] 10 | 11 | # Middleman Gems 12 | gem 'middleman', '>= 4.1.1' 13 | gem 'middleman-minify-html' 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Craig Dennis 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Middleman + Gulp Starter 2 | Build static sites using [Middleman](https://middlemanapp.com/) with [Gulp Starter](https://github.com/vigetlabs/gulp-starter). 3 | 4 | 5 | 6 | Watch the screencast: https://youtu.be/-io8EeB3GHI 7 | 8 | ## Installation 9 | 1. Clone the repo — `git clone git@github.com:craigmdennis/middleman-gulp-starter.git` 10 | 1. Run setup — `bin/setup` 11 | 12 | Setup installs Bundler, Gem dependencies and NPM dependencies. 13 | 14 | ## Developing using Middleman + Gulp Starter 15 | 1. Run `middleman serve` to use the `environments/development` config 16 | 17 | Middleman will launch Gulp Starter as an external pipeline, in development mode. 18 | 19 | ## Production builds 20 | 1. Run `middleman build` to use the `environments/production` config 21 | 22 | Middleman will minify HTML and Gulp starter will handle everything else 23 | 24 | ## Running Middleman without Gulp Starter 25 | There may be [instances where you want to run the standard Middleman tasks without initiating Gulp Starter](https://github.com/craigmdennis/middleman-gulp-starter/pull/17#issue-147191865). To do that you simply need to pass in another environment. It doesn't even need to exist. 26 | 27 | `middleman serve -e debug` 28 | 29 | Now you can create `environments/debug` and add your own custom config in there. 30 | 31 | ## Revving Assets 32 | You'll no longer be able to use the built in Middleman asset helpers like `javascript_include_tag` on their own. Instead, you'll have to add a set of **gulp_asset_helpers**. Filenames get hashed so you can cache them forever. [Learn more](helpers/README.md) 33 | 34 | ```ruby 35 | gulp_asset_path('image/asset.jpg') # -> /image/logo-n39o4orb81.png 36 | gulp_js_path('app.js') # -> /javascripts/app-f43e9abc11.js 37 | gulp_css_path('app.css') # -> /stylesheets/app-d29e4cdb76.css 38 | gulp_image_path('logo.png') # -> /images/logo-n39o4orb81.png 39 | ``` 40 | 41 | So instead of this: 42 | ```erb 43 | <%= image_tag 'logo.png', alt: 'logo' %> 44 | ``` 45 | 46 | You would do this: 47 | ```erb 48 | <%= image_tag gulp_image_path('logo.png'), alt: 'logo' %> 49 | ``` 50 | 51 | Instead of this: 52 | ```erb 53 | <%= stylesheet_link_tag :site %> 54 | ``` 55 | 56 | You would do this: 57 | ```erb 58 | <%= stylesheet_link_tag gulp_css_path('site.css') %> 59 | ``` 60 | 61 | ### When NOT to use the `gulp_asset_path` helpers 62 | Due to the way that Middleman merges the temporary folder with the source folder in the sitemap, when you reference it you don't need to use the `gulp_image_path` helper as the files in the sitemap will already be the revved files. 63 | 64 | ```haml 65 | - sitemap.resources.select{ |r| r.path.start_with?("images/some-folder-of-images") }.each do |image| 66 | = image_tag image.path 67 | ``` 68 | 69 | You can disable asset revving in production on the [`config.json`](https://github.com/craigmdennis/middleman-gulp-starter/blob/master/gulpfile.js/config.json#L78) at the bottom. 70 | 71 | ## Contributing 72 | Please make sure to submit PRs to the latest feature branch. 73 | 74 | ## Release History 75 | Please consult the [official changelog](https://github.com/craigmdennis/middleman-gulp-starter/blob/master/CHANGELOG.md) 76 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | # Make sure Bundler is installed 6 | if [ "$(gem query -i -n bundler)" = "false" ]; then 7 | echo "Installing Bundler..." 8 | gem install bundler 9 | fi 10 | 11 | # Set up Ruby dependencies via Bundler 12 | echo "Installing Ruby gem dependencies..." 13 | bundle install 14 | 15 | # Install node packages 16 | echo "Installing Node package dependencies..." 17 | npm install 18 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Read from Gulp Starter's config.json file 2 | # and rev-manifest file (if present) 3 | require './lib/gulp' 4 | 5 | ### 6 | # Page options, layouts, aliases and proxies 7 | ### 8 | 9 | # Per-page layout changes: 10 | # 11 | # With no layout 12 | page '/*.xml', layout: false 13 | page '/*.json', layout: false 14 | page '/*.txt', layout: false 15 | 16 | # With alternative layout 17 | # page "/path/to/file.html", layout: :otherlayout 18 | 19 | # Proxy pages (http://middlemanapp.com/basics/dynamic-pages/) 20 | # proxy "/this-page-has-no-template.html", "/template-file.html", locals: { 21 | # which_fake_page: "Rendering a fake page with a local variable" } 22 | 23 | # General configuration 24 | 25 | ### 26 | # Helpers 27 | ### 28 | 29 | # Methods defined in the helpers block are available in templates 30 | # helpers do 31 | # def some_helper 32 | # "Helping" 33 | # end 34 | # end 35 | -------------------------------------------------------------------------------- /environments/development.rb: -------------------------------------------------------------------------------- 1 | # This environment is invoked by default when running `middleman server` 2 | activate :external_pipeline, 3 | name: :gulp, 4 | command: "npm run gulp", 5 | source: GULP_CONFIG['root']['dest'], 6 | latency: 1 7 | -------------------------------------------------------------------------------- /environments/production.rb: -------------------------------------------------------------------------------- 1 | # Initialise Gulp Starter when running `middleman build` 2 | activate :external_pipeline, 3 | name: :gulp, 4 | command: "npm run production", 5 | source: GULP_CONFIG['root']['dest'], 6 | latency: 1 7 | 8 | # Ignore the CSS file Middleman normally generates 9 | # Middleman expects `site.css.scss` → `site.css` 10 | # We strip the `.css` to prevent Gulp generating `site.css.css` 11 | ignore 'stylesheets/site' 12 | 13 | # Ignore static files 14 | ignore 'icons/*' 15 | ignore 'static/*' 16 | 17 | # Check to see if file revving is enabled 18 | rev_manifest = REV_MANIFEST if defined?(REV_MANIFEST) 19 | 20 | # If file revving is enabled we need to ignore the original files 21 | # as they will still get copied by Middleman 22 | if rev_manifest 23 | rev_manifest.each do |key, value| 24 | ignore key 25 | end 26 | end 27 | 28 | # Ignore the actual manifest file 29 | ignore 'rev-manifest.json' 30 | 31 | # HTML Optimisation 32 | activate :minify_html do |html| 33 | html.remove_quotes = false 34 | html.remove_intertag_spaces = true 35 | html.remove_http_protocol = true 36 | end 37 | -------------------------------------------------------------------------------- /gulpfile.js/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": { 3 | "src": "./source", 4 | "dest": "./.tmp" 5 | }, 6 | 7 | "tasks": { 8 | "browserSync": { 9 | "proxy": "http://localhost:4567", 10 | "reloadDelay" : 1000, 11 | "files": ["./source/**/*.{erb,html,haml}"] 12 | }, 13 | 14 | "static": { 15 | "src": "static", 16 | "dest": "./" 17 | }, 18 | 19 | "js": { 20 | "src": "javascripts", 21 | "dest": "javascripts", 22 | "entries": { 23 | "all": ["./all.js"] 24 | }, 25 | "extensions": ["js", "coffee", "json"], 26 | "babel": { 27 | "presets": ["es2015", "stage-1"], 28 | "plugins": [] 29 | }, 30 | "extractSharedJs": false 31 | }, 32 | "css": { 33 | "src": "stylesheets", 34 | "dest": "stylesheets", 35 | "autoprefixer": { 36 | "browsers": ["last 3 version"] 37 | }, 38 | "sass": { 39 | "indentedSyntax": false, 40 | "includePaths": [], 41 | "precision": 10 42 | }, 43 | "extensions": ["sass", "scss", "css"] 44 | }, 45 | 46 | "html": { 47 | "src": "./", 48 | "dest": "./", 49 | "extensions": ["html", "erb", "haml", "md", "yml"], 50 | "excludeFolders": ["images", "javascripts", "stylesheets"] 51 | }, 52 | 53 | "images": { 54 | "src": "images", 55 | "dest": "images", 56 | "extensions": ["jpg", "png", "svg", "gif"] 57 | }, 58 | 59 | "fonts": { 60 | "src": "fonts", 61 | "dest": "fonts", 62 | "extensions": ["woff2", "woff", "eot", "ttf", "svg"] 63 | }, 64 | 65 | "iconFont": { 66 | "src": "icons", 67 | "dest": "fonts", 68 | "sassDest": "generated", 69 | "extensions": ["woff2", "woff", "eot", "ttf", "svg"] 70 | }, 71 | 72 | "svgSprite": { 73 | "src": "icons", 74 | "dest": "images", 75 | "extensions": ["svg"] 76 | }, 77 | 78 | "production" : { 79 | "rev": true 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gulpfile.js/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | gulpfile.js 3 | =========== 4 | Rather than manage one giant configuration file responsible 5 | for creating multiple tasks, each task has been broken out into 6 | its own file in gulpfile.js/tasks. Any files in that directory get 7 | automatically required below. 8 | 9 | To add a new task, simply add a new task file that directory. 10 | gulpfile.js/tasks/default.js specifies the default set of tasks to run 11 | when you run `gulp`. 12 | */ 13 | 14 | var requireDir = require('require-dir') 15 | 16 | // Require all tasks in gulpfile.js/tasks, including subfolders 17 | requireDir('./tasks', { recurse: true }) 18 | -------------------------------------------------------------------------------- /gulpfile.js/lib/__tests__/pathToUrl.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert 2 | var pathToUrl = require('../pathToUrl') 3 | 4 | describe('pathToUrl', function() { 5 | it('converts Windows paths to a url path', function() { 6 | var urlPath = pathToUrl("\\Foo\\bar\\baz") 7 | assert.equal(urlPath, '/Foo/bar/baz') 8 | }) 9 | 10 | it('does not affect unix paths', function() { 11 | var unixPath = pathToUrl('/Foo/bar/baz/') 12 | assert.equal(unixPath, '/Foo/bar/baz/') 13 | }) 14 | 15 | it('normalizes path segments', function() { 16 | var joinedPath = pathToUrl('/','//Foo', 'bar', 'baz/') 17 | assert.equal(joinedPath, '/Foo/bar/baz/') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /gulpfile.js/lib/compileLogger.js: -------------------------------------------------------------------------------- 1 | var gutil = require("gulp-util") 2 | var prettifyTime = require('./prettifyTime') 3 | var handleErrors = require('./handleErrors') 4 | 5 | module.exports = function(err, stats) { 6 | if(err) throw new gutil.PluginError("webpack", err) 7 | 8 | var statColor = stats.compilation.warnings.length < 1 ? 'green' : 'yellow' 9 | 10 | if(stats.compilation.errors.length > 0) { 11 | stats.compilation.errors.forEach(function(error){ 12 | handleErrors(error) 13 | statColor = 'red' 14 | }) 15 | } else { 16 | var compileTime = prettifyTime(stats.endTime - stats.startTime) 17 | gutil.log(gutil.colors[statColor](stats)) 18 | gutil.log('Compiled with', gutil.colors.cyan('webpack'), 'in', gutil.colors.magenta(compileTime)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js/lib/getEnabledTasks.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var compact = require('lodash/compact') 3 | 4 | // Grouped by what can run in parallel 5 | var assetTasks = ['fonts', 'iconFont', 'images', 'svgSprite'] 6 | var codeTasks = ['css', 'js'] 7 | 8 | module.exports = function(env) { 9 | 10 | function matchFilter(task) { 11 | if(config.tasks[task]) { 12 | if(task === 'js') { 13 | task = env === 'production' ? 'webpack:production' : false 14 | } 15 | return task 16 | } 17 | } 18 | 19 | function exists(value) { 20 | return !!value 21 | } 22 | 23 | return { 24 | assetTasks: compact(assetTasks.map(matchFilter).filter(exists)), 25 | codeTasks: compact(codeTasks.map(matchFilter).filter(exists)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gulpfile.js/lib/handleErrors.js: -------------------------------------------------------------------------------- 1 | var notify = require("gulp-notify") 2 | 3 | module.exports = function(errorObject, callback) { 4 | notify.onError(errorObject.toString().split(': ').join(':\n')).apply(this, arguments) 5 | // Keep gulp from hanging on this task 6 | if (typeof this.emit === 'function') this.emit('end') 7 | } 8 | -------------------------------------------------------------------------------- /gulpfile.js/lib/pathToUrl.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | module.exports = function pathToUrl() { 4 | // Normalizes Windows file paths to valid url paths 5 | return path.join.apply(this, arguments).replace(/\\/g, '/') 6 | } 7 | -------------------------------------------------------------------------------- /gulpfile.js/lib/prettifyTime.js: -------------------------------------------------------------------------------- 1 | module.exports = function(milliseconds) { 2 | if(milliseconds > 999) { 3 | return (milliseconds / 1000).toFixed(2) + " s" 4 | } else { 5 | return milliseconds + ' ms' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /gulpfile.js/lib/repeatString.js: -------------------------------------------------------------------------------- 1 | module.exports = function(pattern, number) { 2 | var string = '' 3 | while (number > 0){ 4 | number-- 5 | string += pattern 6 | } 7 | return string 8 | } 9 | -------------------------------------------------------------------------------- /gulpfile.js/lib/webpack-multi-config.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.js) return 3 | 4 | var path = require('path') 5 | var pathToUrl = require('./pathToUrl') 6 | var webpack = require('webpack') 7 | var webpackManifest = require('./webpackManifest') 8 | 9 | module.exports = function(env) { 10 | var jsSrc = path.resolve(config.root.src, config.tasks.js.src) 11 | var jsDest = path.resolve(config.root.dest, config.tasks.js.dest) 12 | var publicPath = pathToUrl(config.tasks.js.dest, '/') 13 | 14 | var extensions = config.tasks.js.extensions.map(function(extension) { 15 | return '.' + extension 16 | }) 17 | 18 | var rev = config.tasks.production.rev && env === 'production' 19 | var filenamePattern = rev ? '[name]-[hash].js' : '[name].js' 20 | 21 | var webpackConfig = { 22 | context: jsSrc, 23 | plugins: [], 24 | resolve: { 25 | root: [ 26 | jsSrc, 27 | path.join(__dirname, "..", "gulp", "node_modules") 28 | ], 29 | extensions: [''].concat(extensions) 30 | }, 31 | module: { 32 | loaders: [ 33 | { 34 | test: /\.js$/, 35 | loader: 'babel-loader', 36 | exclude: /node_modules/, 37 | query: config.tasks.js.babel 38 | }, 39 | { 40 | test: /\.coffee$/, 41 | loader: "coffee-loader" }, 42 | { 43 | test: /\.(coffee\.md|litcoffee)$/, 44 | loader: "coffee-loader?literate" 45 | } 46 | ] 47 | } 48 | } 49 | 50 | if(env === 'development') { 51 | webpackConfig.devtool = 'inline-source-map' 52 | 53 | // Create new entries object with webpack-hot-middleware added 54 | for (var key in config.tasks.js.entries) { 55 | var entry = config.tasks.js.entries[key] 56 | config.tasks.js.entries[key] = ['webpack-hot-middleware/client?&reload=true'].concat(entry) 57 | } 58 | 59 | webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()) 60 | } 61 | 62 | if(env !== 'test') { 63 | // Karma doesn't need entry points or output settings 64 | webpackConfig.entry = config.tasks.js.entries 65 | 66 | webpackConfig.output= { 67 | path: path.normalize(jsDest), 68 | filename: filenamePattern, 69 | publicPath: publicPath 70 | } 71 | 72 | if(config.tasks.js.extractSharedJs) { 73 | // Factor out common dependencies into a shared.js 74 | webpackConfig.plugins.push( 75 | new webpack.optimize.CommonsChunkPlugin({ 76 | name: 'shared', 77 | filename: filenamePattern, 78 | }) 79 | ) 80 | } 81 | } 82 | 83 | if(env === 'production') { 84 | if(rev) { 85 | webpackConfig.plugins.push(new webpackManifest(publicPath, config.root.dest)) 86 | } 87 | webpackConfig.plugins.push( 88 | new webpack.DefinePlugin({ 89 | 'process.env': { 90 | 'NODE_ENV': JSON.stringify('production') 91 | } 92 | }), 93 | new webpack.optimize.DedupePlugin(), 94 | new webpack.optimize.UglifyJsPlugin({ 95 | output: { comments: false }, 96 | compress: { drop_console: true }, 97 | }), 98 | new webpack.NoErrorsPlugin() 99 | ) 100 | } 101 | 102 | return webpackConfig 103 | } 104 | -------------------------------------------------------------------------------- /gulpfile.js/lib/webpackManifest.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var fs = require('fs') 3 | 4 | module.exports = function(publicPath, dest, filename) { 5 | filename = filename || 'rev-manifest.json' 6 | 7 | return function() { 8 | this.plugin("done", function(stats) { 9 | var stats = stats.toJson() 10 | var chunks = stats.assetsByChunkName 11 | var manifest = {} 12 | 13 | for (var key in chunks) { 14 | var originalFilename = key + '.js' 15 | manifest[path.join(publicPath, originalFilename)] = path.join(publicPath, chunks[key]) 16 | } 17 | 18 | fs.writeFileSync( 19 | path.join(process.cwd(), dest, filename), 20 | JSON.stringify(manifest) 21 | ) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/browserSync.js: -------------------------------------------------------------------------------- 1 | if(global.production) return 2 | 3 | var browserSync = require('browser-sync') 4 | var gulp = require('gulp') 5 | var webpack = require('webpack') 6 | var webpackMultiConfig = require('../lib/webpack-multi-config') 7 | var config = require('../config') 8 | var pathToUrl = require('../lib/pathToUrl') 9 | 10 | var browserSyncTask = function() { 11 | 12 | var webpackConfig = webpackMultiConfig('development') 13 | var compiler = webpack(webpackConfig) 14 | var proxyConfig = config.tasks.browserSync.proxy || null; 15 | 16 | if (typeof(proxyConfig) === 'string') { 17 | config.tasks.browserSync.proxy = { 18 | target : proxyConfig 19 | } 20 | } 21 | 22 | var server = config.tasks.browserSync.proxy || config.tasks.browserSync.server; 23 | 24 | server.middleware = [ 25 | require('webpack-dev-middleware')(compiler, { 26 | stats: 'errors-only', 27 | publicPath: pathToUrl('/', webpackConfig.output.publicPath) 28 | }), 29 | require('webpack-hot-middleware')(compiler) 30 | ] 31 | 32 | browserSync.init(config.tasks.browserSync) 33 | } 34 | 35 | gulp.task('browserSync', browserSyncTask) 36 | module.exports = browserSyncTask 37 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var del = require('del') 3 | var config = require('../config') 4 | 5 | var cleanTask = function (cb) { 6 | del([config.root.dest]).then(function (paths) { 7 | cb() 8 | }) 9 | } 10 | 11 | gulp.task('clean', cleanTask) 12 | module.exports = cleanTask 13 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/css.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.css) return 3 | 4 | var gulp = require('gulp') 5 | var gulpif = require('gulp-if') 6 | var browserSync = require('browser-sync') 7 | var sass = require('gulp-sass') 8 | var sourcemaps = require('gulp-sourcemaps') 9 | var handleErrors = require('../lib/handleErrors') 10 | var autoprefixer = require('gulp-autoprefixer') 11 | var path = require('path') 12 | var cssnano = require('gulp-cssnano') 13 | 14 | var paths = { 15 | src: path.join(config.root.src, config.tasks.css.src, '/**/*.{' + config.tasks.css.extensions + '}'), 16 | dest: path.join(config.root.dest, config.tasks.css.dest) 17 | } 18 | 19 | var cssTask = function () { 20 | return gulp.src(paths.src) 21 | .pipe(gulpif(!global.production, sourcemaps.init())) 22 | .pipe(sass(config.tasks.css.sass)) 23 | .on('error', handleErrors) 24 | .pipe(autoprefixer(config.tasks.css.autoprefixer)) 25 | .pipe(gulpif(global.production, cssnano({autoprefixer: false}))) 26 | .pipe(gulpif(!global.production, sourcemaps.write())) 27 | .pipe(gulp.dest(paths.dest)) 28 | .pipe(browserSync.stream()) 29 | } 30 | 31 | gulp.task('css', cssTask) 32 | module.exports = cssTask 33 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/default.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var gulpSequence = require('gulp-sequence') 3 | var getEnabledTasks = require('../lib/getEnabledTasks') 4 | 5 | var defaultTask = function(cb) { 6 | var tasks = getEnabledTasks('watch') 7 | gulpSequence('clean', tasks.assetTasks, tasks.codeTasks, 'static', 'watch', cb) 8 | } 9 | 10 | gulp.task('default', defaultTask) 11 | module.exports = defaultTask 12 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/deploy.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var ghPages = require('gulp-gh-pages') 3 | var gulp = require('gulp') 4 | var open = require('open') 5 | var os = require('os') 6 | var package = require('../../package.json') 7 | var path = require('path') 8 | 9 | var settings = { 10 | url: package.homepage, 11 | src: path.join(config.root.dest, '/**/*'), 12 | ghPages: { 13 | cacheDir: path.join(os.tmpdir(), package.name) 14 | } 15 | } 16 | 17 | var deployTask = function() { 18 | return gulp.src(settings.src) 19 | .pipe(ghPages(settings.ghPages)) 20 | .on('end', function(){ 21 | open(settings.url) 22 | }) 23 | } 24 | 25 | gulp.task('deploy', ['production'], deployTask) 26 | module.exports = deployTask 27 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/fonts.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.fonts) return 3 | 4 | var browserSync = require('browser-sync') 5 | var changed = require('gulp-changed') 6 | var gulp = require('gulp') 7 | var path = require('path') 8 | 9 | var paths = { 10 | src: path.join(config.root.src, config.tasks.fonts.src, '/**/*.{' + config.tasks.fonts.extensions + '}'), 11 | dest: path.join(config.root.dest, config.tasks.fonts.dest) 12 | } 13 | 14 | var fontsTask = function() { 15 | return gulp.src([paths.src, '*!README.md']) 16 | .pipe(changed(paths.dest)) // Ignore unchanged files 17 | .pipe(gulp.dest(paths.dest)) 18 | .pipe(browserSync.stream()) 19 | } 20 | 21 | gulp.task('fonts', fontsTask) 22 | module.exports = fontsTask 23 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/iconFont/generateIconSass.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var render = require('gulp-nunjucks-render') 3 | var rename = require('gulp-rename') 4 | var handleErrors = require('../../lib/handleErrors') 5 | var gutil = require('gulp-util') 6 | var data = require('gulp-data') 7 | 8 | module.exports = function(config) { 9 | return function(glyphs, options) { 10 | gutil.log(gutil.colors.blue('Generating ' + config.sassDest + '/' + config.sassOutputName)) 11 | render.nunjucks.configure(config.nunjucks, { watch: false }) 12 | 13 | return gulp.src(config.template) 14 | .pipe(data({ 15 | icons: glyphs.map(function(glyph) { 16 | gutil.log(gutil.colors.green('+ adding ' + glyph.name + ' glyph')) 17 | return { 18 | name: glyph.name, 19 | code: glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase() 20 | } 21 | }), 22 | 23 | fontName: config.options.fontName, 24 | fontPath: config.fontPath, 25 | className: config.className, 26 | comment: '// DO NOT EDIT DIRECTLY!\n //Generated by gulpfile.js/tasks/iconFont.js\n //from ' + config.template 27 | })) 28 | .pipe(render({ 29 | path: config.template 30 | })) 31 | .on('error', handleErrors) 32 | .pipe(rename(config.sassOutputName)) 33 | .pipe(gulp.dest(config.sassDest)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/iconFont/index.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config') 2 | if(!config.tasks.iconFont) return 3 | 4 | var gulp = require('gulp') 5 | var iconfont = require('gulp-iconfont') 6 | var generateIconSass = require('./generateIconSass') 7 | var handleErrors = require('../../lib/handleErrors') 8 | var package = require('../../../package.json') 9 | var path = require('path') 10 | var url = require('url') 11 | 12 | var fontPath = path.join(config.root.dest, config.tasks.iconFont.dest) 13 | var cssPath = path.join(config.root.dest, config.tasks.css.dest) 14 | 15 | var settings = { 16 | name: package.name + ' icons', 17 | src: path.join(config.root.src, config.tasks.iconFont.src, '/*.svg'), 18 | dest: path.join(config.root.dest, config.tasks.iconFont.dest), 19 | sassDest: path.join(config.root.src, config.tasks.css.src, config.tasks.iconFont.sassDest), 20 | template: path.normalize('./gulpfile.js/tasks/iconFont/template.sass'), 21 | sassOutputName: '_icons.sass', 22 | fontPath: url.resolve('.',path.relative(cssPath, fontPath)), 23 | className: 'icon', 24 | options: { 25 | timestamp: 0, // see https://github.com/fontello/svg2ttf/issues/33 26 | fontName: 'icons', 27 | normalize: false, 28 | formats: config.tasks.iconFont.extensions 29 | } 30 | } 31 | 32 | var iconFontTask = function() { 33 | return gulp.src(settings.src) 34 | .pipe(iconfont(settings.options)) 35 | .on('glyphs', generateIconSass(settings)) 36 | .on('error', handleErrors) 37 | .pipe(gulp.dest(settings.dest)) 38 | } 39 | 40 | gulp.task('iconFont', iconFontTask) 41 | module.exports = iconFontTask 42 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/iconFont/template.sass: -------------------------------------------------------------------------------- 1 | // {{comment}} 2 | 3 | @font-face 4 | font-family: {{fontName}} 5 | src: url("{{fontPath}}/{{fontName}}.eot") 6 | src: url("{{fontPath}}/{{fontName}}.eot?#iefix") format('embedded-opentype'), url("{{fontPath}}/{{fontName}}.woff") format('woff'), url("{{fontPath}}/{{fontName}}.ttf") format('truetype'), url("{{fontPath}}/{{fontName}}.svg#{{fontName}}") format('svg') 7 | font-weight: normal 8 | font-style: normal 9 | 10 | =icon($content: '') 11 | &:before 12 | -moz-osx-font-smoothing: grayscale 13 | -webkit-font-smoothing: antialiased 14 | content: $content 15 | font-family: '{{fontName}}' 16 | font-style: normal 17 | font-variant: normal 18 | font-weight: normal 19 | line-height: 1 20 | speak: none 21 | text-transform: none 22 | @content 23 | 24 | .icon 25 | +icon 26 | 27 | {% for icon in icons -%} 28 | // Save variable 29 | $icon-{{icon.name}}: "\{{icon.code}}" 30 | // Save mixin 31 | =icon--{{icon.name}} 32 | +icon($icon-{{icon.name}}) 33 | @content 34 | // Expose as class 35 | .icon.-{{icon.name}}:before 36 | content: $icon-{{icon.name}} 37 | 38 | {% endfor %} 39 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/images.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.images) return 3 | 4 | var browserSync = require('browser-sync') 5 | var changed = require('gulp-changed') 6 | var gulp = require('gulp') 7 | var imagemin = require('gulp-imagemin') 8 | var path = require('path') 9 | 10 | var paths = { 11 | src: path.join(config.root.src, config.tasks.images.src, '/**/*.{' + config.tasks.images.extensions + '}'), 12 | dest: path.join(config.root.dest, config.tasks.images.dest) 13 | } 14 | 15 | var imagesTask = function() { 16 | return gulp.src([paths.src, , '*!README.md']) 17 | .pipe(changed(paths.dest)) // Ignore unchanged files 18 | .pipe(imagemin({progressive: true})) // Optimize 19 | .pipe(gulp.dest(paths.dest)) 20 | .pipe(browserSync.stream()) 21 | } 22 | 23 | gulp.task('images', imagesTask) 24 | module.exports = imagesTask 25 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/production.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var gulp = require('gulp') 3 | var gulpSequence = require('gulp-sequence') 4 | var getEnabledTasks = require('../lib/getEnabledTasks') 5 | 6 | var productionTask = function(cb) { 7 | global.production = true 8 | var tasks = getEnabledTasks('production') 9 | gulpSequence('clean', tasks.assetTasks, tasks.codeTasks, config.tasks.production.rev ? 'rev': false, 'size-report', 'static', cb) 10 | } 11 | 12 | gulp.task('production', productionTask) 13 | module.exports = productionTask 14 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/rev/index.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config') 2 | if(!config.tasks.production.rev) return 3 | 4 | var gulp = require('gulp') 5 | var gutil = require('gulp-util') 6 | var gulpSequence = require('gulp-sequence') 7 | 8 | // If you are familiar with Rails, this task the equivalent of `rake assets:precompile` 9 | var revTask = function(cb) { 10 | gulpSequence( 11 | // 1) Add md5 hashes to assets referenced by CSS and JS files 12 | 'rev-assets', 13 | // 2) Update asset references (images, fonts, etc) with reved filenames in compiled css + js 14 | 'rev-update-references', 15 | // 3) Rev and compress CSS and JS files (this is done after assets, so that if a referenced asset hash changes, the parent hash will change as well 16 | 'rev-css', 17 | // 4) Update asset references in HTML 18 | 'update-html', 19 | cb) 20 | } 21 | 22 | gulp.task('rev', revTask) 23 | module.exports = revTask 24 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/rev/rev-assets.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config') 2 | var gulp = require('gulp') 3 | var path = require('path') 4 | var rev = require('gulp-rev') 5 | var revNapkin = require('gulp-rev-napkin'); 6 | 7 | // 1) Add md5 hashes to assets referenced by CSS and JS files 8 | gulp.task('rev-assets', function() { 9 | // Ignore files that may reference assets. We'll rev them next. 10 | var ignoreThese = '!' + path.join(config.root.dest,'/**/*+(css|js|json|html)') 11 | 12 | return gulp.src([path.join(config.root.dest,'/**/*'), ignoreThese]) 13 | .pipe(rev()) 14 | .pipe(gulp.dest(config.root.dest)) 15 | .pipe(revNapkin({verbose: false})) 16 | .pipe(rev.manifest(path.join(config.root.dest, 'rev-manifest.json'), {merge: true})) 17 | .pipe(gulp.dest('')) 18 | }) 19 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/rev/rev-css.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config') 2 | var gulp = require('gulp') 3 | var path = require('path') 4 | var rev = require('gulp-rev') 5 | var revNapkin = require('gulp-rev-napkin') 6 | 7 | // 4) Rev and compress CSS and JS files (this is done after assets, so that if a 8 | // referenced asset hash changes, the parent hash will change as well 9 | gulp.task('rev-css', function(){ 10 | return gulp.src(path.join(config.root.dest,'/**/*.css')) 11 | .pipe(rev()) 12 | .pipe(gulp.dest(config.root.dest)) 13 | .pipe(revNapkin({verbose: false})) 14 | .pipe(rev.manifest(path.join(config.root.dest, 'rev-manifest.json'), {merge: true})) 15 | .pipe(gulp.dest('')) 16 | }) 17 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/rev/rev-update-references.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config') 2 | var gulp = require('gulp') 3 | var path = require('path') 4 | var revReplace = require('gulp-rev-replace') 5 | 6 | // 2) Update asset references with reved filenames in compiled css + js 7 | gulp.task('rev-update-references', function(){ 8 | var manifest = gulp.src(path.join(config.root.dest, "rev-manifest.json")) 9 | 10 | return gulp.src(path.join(config.root.dest,'/**/**.{css,js}')) 11 | .pipe(revReplace({manifest: manifest})) 12 | .pipe(gulp.dest(config.root.dest)) 13 | }) 14 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/rev/update-html.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var config = require('../../config') 3 | var revReplace = require('gulp-rev-replace') 4 | var path = require('path') 5 | 6 | // 5) Update asset references in HTML 7 | gulp.task('update-html', function(){ 8 | var manifest = gulp.src(path.join(config.root.dest, "/rev-manifest.json")) 9 | return gulp.src(path.join(config.root.dest, config.tasks.html.dest, '/**/*.html')) 10 | .pipe(revReplace({manifest: manifest})) 11 | .pipe(gulp.dest(path.join(config.root.dest, config.tasks.html.dest))) 12 | }) 13 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/server.js: -------------------------------------------------------------------------------- 1 | var compress = require('compression') 2 | var config = require('../config') 3 | var express = require('express') 4 | var gulp = require('gulp') 5 | var gutil = require('gulp-util') 6 | var logger = require('morgan') 7 | var open = require('open') 8 | var path = require('path') 9 | 10 | var settings = { 11 | root: path.resolve(process.cwd(), config.root.dest), 12 | port: process.env.PORT || 5000, 13 | logLevel: process.env.NODE_ENV ? 'combined' : 'dev', 14 | staticOptions: { 15 | extensions: ['html'], 16 | maxAge: '31556926' 17 | } 18 | } 19 | 20 | var serverTask = function() { 21 | var url = 'http://localhost:' + settings.port 22 | 23 | express() 24 | .use(compress()) 25 | .use(logger(settings.logLevel)) 26 | .use('/', express.static(settings.root, settings.staticOptions)) 27 | .listen(settings.port) 28 | 29 | gutil.log('production server started on ' + gutil.colors.green(url)) 30 | open(url) 31 | } 32 | 33 | gulp.task('server', serverTask) 34 | module.exports = serverTask 35 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/sizereport.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var gulp = require('gulp') 3 | var repeatString = require('../lib/repeatString') 4 | var sizereport = require('gulp-sizereport') 5 | 6 | gulp.task('size-report', function() { 7 | return gulp.src([config.root.dest + '/**/*', '*!rev-manifest.json']) 8 | .pipe(sizereport({ 9 | gzip: true 10 | })) 11 | }) 12 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/static.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var changed = require('gulp-changed') 3 | var gulp = require('gulp') 4 | var path = require('path') 5 | 6 | var paths = { 7 | src: [ 8 | path.join(config.root.src, config.tasks.static.src, '/**'), 9 | path.join('!' + config.root.src, config.tasks.static.src, '/README.md') 10 | ], 11 | dest: path.join(config.root.dest, config.tasks.static.dest) 12 | } 13 | 14 | var staticTask = function() { 15 | return gulp.src(paths.src) 16 | .pipe(changed(paths.dest)) // Ignore unchanged files 17 | .pipe(gulp.dest(paths.dest)) 18 | } 19 | 20 | gulp.task('static', staticTask) 21 | module.exports = staticTask 22 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/svgSprite.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.svgSprite) return 3 | 4 | var browserSync = require('browser-sync') 5 | var gulp = require('gulp') 6 | var imagemin = require('gulp-imagemin') 7 | var svgstore = require('gulp-svgstore') 8 | var path = require('path') 9 | 10 | var svgSpriteTask = function() { 11 | 12 | var settings = { 13 | src: path.join(config.root.src, config.tasks.svgSprite.src, '/*.svg'), 14 | dest: path.join(config.root.dest, config.tasks.svgSprite.dest) 15 | } 16 | 17 | return gulp.src(settings.src) 18 | .pipe(imagemin()) 19 | .pipe(svgstore()) 20 | .pipe(gulp.dest(settings.dest)) 21 | .pipe(browserSync.stream()) 22 | } 23 | 24 | gulp.task('svgSprite', svgSpriteTask) 25 | module.exports = svgSpriteTask 26 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/watch.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var gulp = require('gulp') 3 | var path = require('path') 4 | var watch = require('gulp-watch') 5 | 6 | var watchTask = function() { 7 | var watchableTasks = ['fonts', 'iconFont', 'images', 'svgSprite', 'css'] 8 | 9 | watchableTasks.forEach(function(taskName) { 10 | var task = config.tasks[taskName] 11 | if(task) { 12 | var glob = path.join(config.root.src, task.src, '**/*.{' + task.extensions.join(',') + '}') 13 | watch(glob, function() { 14 | require('./' + taskName)() 15 | }) 16 | } 17 | }) 18 | } 19 | 20 | gulp.task('watch', ['browserSync'], watchTask) 21 | module.exports = watchTask 22 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/webpackProduction.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | if(!config.tasks.js) return 3 | 4 | var config = require('../lib/webpack-multi-config')('production') 5 | var gulp = require('gulp') 6 | var logger = require('../lib/compileLogger') 7 | var webpack = require('webpack') 8 | 9 | var webpackProductionTask = function(callback) { 10 | webpack(config, function(err, stats) { 11 | logger(err, stats) 12 | callback() 13 | }) 14 | } 15 | 16 | gulp.task('webpack:production', webpackProductionTask) 17 | module.exports = webpackProductionTask 18 | -------------------------------------------------------------------------------- /helpers/README.md: -------------------------------------------------------------------------------- 1 | # Gulp Starter Middleman Asset Helpers 2 | 3 | Source files should be in a `source` directory in the root of the project, **not** in `app/assets`. You'll also no longer be using the built in Middleman asset helpers like `javascript_include_tag` and the like. Instead, you'll use a set of **gulp_asset_helpers** with regular markup. 4 | 5 | #### app/helpers/gulp_asset_helper.rb 6 | In production (`npm run production`), filenames get hashed so you can cache them forever. When the file or any of it's referenced assets changes, the hash changes. This works just like the Middleman asset pipeline, and we have similar helpers to ensure that the correct filenames are referenced in production: 7 | 8 | ```ruby 9 | gulp_asset_path('image/asset.jpg') # -> /image/logo-n39o4orb81.png 10 | gulp_js_path('app.js') # -> /javascripts/app-f43e9abc11.js 11 | gulp_css_path('app.css') # -> /stylesheets/app-d29e4cdb76.css 12 | gulp_image_path('logo.png') # -> /images/logo-n39o4orb81.png 13 | ``` 14 | 15 | So instead of this: 16 | ```erb 17 | <%= image_tag 'logo.png', alt: 'logo' %> 18 | ``` 19 | 20 | You would do this: 21 | ```ruby 22 | logo 23 | ``` 24 | 25 | Instead of this: 26 | ```erb 27 | <%= stylesheet_link_tag :site %> 28 | ``` 29 | 30 | You would do this: 31 | ```erb 32 | <%= stylesheet_link_tag gulp_css_path('site.css') %> 33 | ``` 34 | 35 | ##### Sprite helper 36 | There's also a `<%= sprite('id') %>` helper included for use with the `svgSpriteTask` task. It looks like: 37 | 38 | ``` 39 | def sprite(id, classes = "", viewBox = "0 0 24 24") 40 | "".html_safe 41 | end 42 | ``` 43 | -------------------------------------------------------------------------------- /helpers/gulp_asset_helper.rb: -------------------------------------------------------------------------------- 1 | module GulpAssetHelper 2 | def gulp_asset_path(path, type = nil) 3 | rev_manifest = nil 4 | 5 | # In development, check for the manifest every time 6 | if config[:environment].to_s != 'production' 7 | rev_manifest = JSON.parse(File.read(REV_MANIFEST_PATH)) if File.exist?(REV_MANIFEST_PATH) 8 | # In production, use the manifest cached in initializers/gulp.rb 9 | else 10 | rev_manifest = REV_MANIFEST if defined?(REV_MANIFEST) 11 | end 12 | 13 | root = GULP_CONFIG['root']['dest'].gsub(/(.*).tmp/, '/') 14 | asset_path = type ? File.join(GULP_CONFIG['tasks'][type.to_s]['dest'].to_s, path.to_s) : path.to_s 15 | asset_path = rev_manifest[asset_path] if rev_manifest 16 | asset_path = File.join(root, asset_path) 17 | File.absolute_path(asset_path, '/') 18 | end 19 | 20 | def gulp_js_path(path) 21 | gulp_asset_path(path, 'js') 22 | end 23 | 24 | def gulp_css_path(path) 25 | gulp_asset_path(path, 'css') 26 | end 27 | 28 | def gulp_image_path(path) 29 | gulp_asset_path(path, 'images') 30 | end 31 | 32 | def sprite(id, classes = "", viewBox = "0 0 24 24") 33 | "".html_safe 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var config = require('./gulpfile.js/config') 2 | var karmaWebpack = require('karma-webpack') 3 | var webpackConfig = require('./gulpfile.js/lib/webpack-multi-config') 4 | var path = require('path') 5 | 6 | var testSrc = path.join(config.root.src, config.tasks.js.src, '/**/__tests__/*') 7 | 8 | var karmaConfig = { 9 | frameworks: ['mocha', 'sinon-chai'], 10 | files: [ testSrc ], 11 | preprocessors: {}, 12 | webpack: webpackConfig('test'), 13 | singleRun: process.env.TRAVIS_CI === 'true', 14 | reporters: ['nyan'], 15 | browsers: [(process.env.TRAVIS_CI === 'true'? 'Firefox' : 'Chrome')] 16 | } 17 | 18 | karmaConfig.preprocessors[testSrc] = ['webpack'] 19 | 20 | module.exports = function(config) { 21 | config.set(karmaConfig) 22 | } 23 | -------------------------------------------------------------------------------- /lib/gulp.rb: -------------------------------------------------------------------------------- 1 | GULP_CONFIG = JSON.parse(File.read('gulpfile.js/config.json')) 2 | REV_MANIFEST_PATH = File.join(GULP_CONFIG['root']['dest'], 'rev-manifest.json') 3 | 4 | if File.exist?(REV_MANIFEST_PATH) 5 | REV_MANIFEST = JSON.parse(File.read(REV_MANIFEST_PATH)) 6 | end 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "middleman-gulp-starter", 3 | "version": "1.4.2", 4 | "description": "Middleman v4 + Gulp Starter", 5 | "engines": { 6 | "node": ">=0.12.0 ~5.9.0", 7 | "npm": ">=2.14.12 ~3.7.3" 8 | }, 9 | "homepage": "https://github.com/craigmdennis/middleman-gulp-starter", 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:craigmdennis/middleman-gulp-starter.git" 13 | }, 14 | "scripts": { 15 | "start": "gulp", 16 | "gulp": "gulp", 17 | "development": "gulp", 18 | "production": "gulp production", 19 | "deploy": "gulp deploy", 20 | "demo": "gulp production && gulp server", 21 | "test": "gulp production && karma start --single-run", 22 | "test:watch": "karma start", 23 | "test:gulp": "mocha ./gulpfile.js/**/*.test.js" 24 | }, 25 | "devDependencies": { 26 | "babel-core": "6.13.2", 27 | "babel-loader": "6.2.5", 28 | "babel-preset-es2015": "6.13.2", 29 | "babel-preset-stage-1": "6.13.0", 30 | "browser-sync": "2.14.0", 31 | "chai": "3.5.0", 32 | "coffee-loader": "^0.7.2", 33 | "coffee-script": "^1.10.0", 34 | "compression": "1.6.2", 35 | "del": "2.2.2", 36 | "express": "4.14.0", 37 | "gulp": "3.9.1", 38 | "gulp-autoprefixer": "3.1.1", 39 | "gulp-changed": "1.3.2", 40 | "gulp-cssnano": "2.1.2", 41 | "gulp-data": "^1.2.1", 42 | "gulp-gh-pages": "0.5.4", 43 | "gulp-iconfont": "8.0.1", 44 | "gulp-if": "2.0.1", 45 | "gulp-imagemin": "3.0.3", 46 | "gulp-notify": "2.2.0", 47 | "gulp-nunjucks-render": "^2.0.0", 48 | "gulp-rename": "1.2.2", 49 | "gulp-rev": "7.1.2", 50 | "gulp-rev-napkin": "0.1.0", 51 | "gulp-rev-replace": "0.4.3", 52 | "gulp-sass": "2.3.2", 53 | "gulp-sequence": "0.4.5", 54 | "gulp-sizereport": "1.1.3", 55 | "gulp-sourcemaps": "1.6.0", 56 | "gulp-svgstore": "6.0.0", 57 | "gulp-uglify": "2.0.0", 58 | "gulp-util": "3.0.7", 59 | "gulp-watch": "4.3.9", 60 | "karma": "1.2.0", 61 | "karma-chrome-launcher": "2.0.0", 62 | "karma-cli": "1.0.1", 63 | "karma-firefox-launcher": "1.0.0", 64 | "karma-mocha": "1.1.1", 65 | "karma-nyan-reporter": "0.2.4", 66 | "karma-sinon-chai": "1.2.3", 67 | "karma-webpack": "1.8.0", 68 | "lodash": "4.15.0", 69 | "lolex": "1.5.1", 70 | "mocha": "3.0.2", 71 | "morgan": "1.7.0", 72 | "object-assign": "4.1.0", 73 | "open": "0.0.5", 74 | "pretty-hrtime": "1.0.2", 75 | "require-dir": "0.3.0", 76 | "sinon": "1.17.5", 77 | "sinon-chai": "2.8.0", 78 | "webpack": "1.13.2", 79 | "webpack-dev-middleware": "1.6.1", 80 | "webpack-hot-middleware": "2.12.2" 81 | }, 82 | "license": "MIT" 83 | } 84 | -------------------------------------------------------------------------------- /source/images/middleman-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/index.html.erb: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome to Middleman 3 | --- 4 | 5 |
6 | 9 |

Middleman is Running Gulp Starter

10 |

11 | <%= link_to "Read Documentation Online", "https://middlemanapp.com", target: "_blank" %> 12 |

13 |
14 | -------------------------------------------------------------------------------- /source/javascripts/_test.js: -------------------------------------------------------------------------------- 1 | console.log('Hello World!'); 2 | -------------------------------------------------------------------------------- /source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | // This is where it all goes :) 2 | 3 | require('_test'); 4 | -------------------------------------------------------------------------------- /source/layouts/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= current_page.data.title || "Middleman" %> 10 | 11 | 12 | 13 | <%= stylesheet_link_tag gulp_css_path('site.css') %> 14 | <%= javascript_include_tag gulp_js_path('all.js') %> 15 | 16 | 17 | 18 | <%= yield %> 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/craigmdennis/middleman-gulp-starter/b46de1e817872549a30eef8e823ceca0a9ee49c5/source/static/favicon.ico -------------------------------------------------------------------------------- /source/stylesheets/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 8/9. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | } 34 | 35 | /* 36 | * Prevents modern browsers from displaying `audio` without controls. 37 | * Remove excess height in iOS 5 devices. 38 | */ 39 | 40 | audio:not([controls]) { 41 | display: none; 42 | height: 0; 43 | } 44 | 45 | /* 46 | * Addresses styling for `hidden` attribute not present in IE 8/9. 47 | */ 48 | 49 | [hidden] { 50 | display: none; 51 | } 52 | 53 | /* ========================================================================== 54 | Base 55 | ========================================================================== */ 56 | 57 | /* 58 | * 1. Sets default font family to sans-serif. 59 | * 2. Prevents iOS text size adjust after orientation change, without disabling 60 | * user zoom. 61 | */ 62 | 63 | html { 64 | font-family: sans-serif; /* 1 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | } 68 | 69 | /* 70 | * Removes default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* ========================================================================== 78 | Links 79 | ========================================================================== */ 80 | 81 | /* 82 | * Addresses `outline` inconsistency between Chrome and other browsers. 83 | */ 84 | 85 | a:focus { 86 | outline: thin dotted; 87 | } 88 | 89 | /* 90 | * Improves readability when focused and also mouse hovered in all browsers. 91 | */ 92 | 93 | a:active, 94 | a:hover { 95 | outline: 0; 96 | } 97 | 98 | /* ========================================================================== 99 | Typography 100 | ========================================================================== */ 101 | 102 | /* 103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, 104 | * Safari 5, and Chrome. 105 | */ 106 | 107 | h1 { 108 | font-size: 2em; 109 | } 110 | 111 | /* 112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: 1px dotted; 117 | } 118 | 119 | /* 120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: bold; 126 | } 127 | 128 | /* 129 | * Addresses styling not present in Safari 5 and Chrome. 130 | */ 131 | 132 | dfn { 133 | font-style: italic; 134 | } 135 | 136 | /* 137 | * Addresses styling not present in IE 8/9. 138 | */ 139 | 140 | mark { 141 | background: #ff0; 142 | color: #000; 143 | } 144 | 145 | 146 | /* 147 | * Corrects font family set oddly in Safari 5 and Chrome. 148 | */ 149 | 150 | code, 151 | kbd, 152 | pre, 153 | samp { 154 | font-family: monospace, serif; 155 | font-size: 1em; 156 | } 157 | 158 | /* 159 | * Improves readability of pre-formatted text in all browsers. 160 | */ 161 | 162 | pre { 163 | white-space: pre; 164 | white-space: pre-wrap; 165 | word-wrap: break-word; 166 | } 167 | 168 | /* 169 | * Sets consistent quote types. 170 | */ 171 | 172 | q { 173 | quotes: "\201C" "\201D" "\2018" "\2019"; 174 | } 175 | 176 | /* 177 | * Addresses inconsistent and variable font size in all browsers. 178 | */ 179 | 180 | small { 181 | font-size: 80%; 182 | } 183 | 184 | /* 185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 186 | */ 187 | 188 | sub, 189 | sup { 190 | font-size: 75%; 191 | line-height: 0; 192 | position: relative; 193 | vertical-align: baseline; 194 | } 195 | 196 | sup { 197 | top: -0.5em; 198 | } 199 | 200 | sub { 201 | bottom: -0.25em; 202 | } 203 | 204 | /* ========================================================================== 205 | Embedded content 206 | ========================================================================== */ 207 | 208 | /* 209 | * Removes border when inside `a` element in IE 8/9. 210 | */ 211 | 212 | img { 213 | border: 0; 214 | } 215 | 216 | /* 217 | * Corrects overflow displayed oddly in IE 9. 218 | */ 219 | 220 | svg:not(:root) { 221 | overflow: hidden; 222 | } 223 | 224 | /* ========================================================================== 225 | Figures 226 | ========================================================================== */ 227 | 228 | /* 229 | * Addresses margin not present in IE 8/9 and Safari 5. 230 | */ 231 | 232 | figure { 233 | margin: 0; 234 | } 235 | 236 | /* ========================================================================== 237 | Forms 238 | ========================================================================== */ 239 | 240 | /* 241 | * Define consistent border, margin, and padding. 242 | */ 243 | 244 | fieldset { 245 | border: 1px solid #c0c0c0; 246 | margin: 0 2px; 247 | padding: 0.35em 0.625em 0.75em; 248 | } 249 | 250 | /* 251 | * 1. Corrects color not being inherited in IE 8/9. 252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 253 | */ 254 | 255 | legend { 256 | border: 0; /* 1 */ 257 | padding: 0; /* 2 */ 258 | } 259 | 260 | /* 261 | * 1. Corrects font family not being inherited in all browsers. 262 | * 2. Corrects font size not being inherited in all browsers. 263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome 264 | */ 265 | 266 | button, 267 | input, 268 | select, 269 | textarea { 270 | font-family: inherit; /* 1 */ 271 | font-size: 100%; /* 2 */ 272 | margin: 0; /* 3 */ 273 | } 274 | 275 | /* 276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in 277 | * the UA stylesheet. 278 | */ 279 | 280 | button, 281 | input { 282 | line-height: normal; 283 | } 284 | 285 | /* 286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 287 | * and `video` controls. 288 | * 2. Corrects inability to style clickable `input` types in iOS. 289 | * 3. Improves usability and consistency of cursor style between image-type 290 | * `input` and others. 291 | */ 292 | 293 | button, 294 | html input[type="button"], /* 1 */ 295 | input[type="reset"], 296 | input[type="submit"] { 297 | -webkit-appearance: button; /* 2 */ 298 | cursor: pointer; /* 3 */ 299 | } 300 | 301 | /* 302 | * Re-set default cursor for disabled elements. 303 | */ 304 | 305 | button[disabled], 306 | input[disabled] { 307 | cursor: default; 308 | } 309 | 310 | /* 311 | * 1. Addresses box sizing set to `content-box` in IE 8/9. 312 | * 2. Removes excess padding in IE 8/9. 313 | */ 314 | 315 | input[type="checkbox"], 316 | input[type="radio"] { 317 | box-sizing: border-box; /* 1 */ 318 | padding: 0; /* 2 */ 319 | } 320 | 321 | /* 322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 324 | * (include `-moz` to future-proof). 325 | */ 326 | 327 | input[type="search"] { 328 | -webkit-appearance: textfield; /* 1 */ 329 | -moz-box-sizing: content-box; 330 | -webkit-box-sizing: content-box; /* 2 */ 331 | box-sizing: content-box; 332 | } 333 | 334 | /* 335 | * Removes inner padding and search cancel button in Safari 5 and Chrome 336 | * on OS X. 337 | */ 338 | 339 | input[type="search"]::-webkit-search-cancel-button, 340 | input[type="search"]::-webkit-search-decoration { 341 | -webkit-appearance: none; 342 | } 343 | 344 | /* 345 | * Removes inner padding and border in Firefox 4+. 346 | */ 347 | 348 | button::-moz-focus-inner, 349 | input::-moz-focus-inner { 350 | border: 0; 351 | padding: 0; 352 | } 353 | 354 | /* 355 | * 1. Removes default vertical scrollbar in IE 8/9. 356 | * 2. Improves readability and alignment in all browsers. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; /* 1 */ 361 | vertical-align: top; /* 2 */ 362 | } 363 | 364 | /* ========================================================================== 365 | Tables 366 | ========================================================================== */ 367 | 368 | /* 369 | * Remove most spacing between table cells. 370 | */ 371 | 372 | table { 373 | border-collapse: collapse; 374 | border-spacing: 0; 375 | } -------------------------------------------------------------------------------- /source/stylesheets/site.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | @import "normalize"; 3 | 4 | body { 5 | background: #ECC561; 6 | text-align: center; 7 | font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; 8 | font-size: 1em; 9 | } 10 | 11 | .logo img { 12 | height: 18.75em; 13 | margin-top: 6em; 14 | } 15 | 16 | h1 { 17 | color: rgba(0,0,0,0.5); 18 | font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; 19 | font-size: 1.8em; 20 | font-weight: 400; 21 | margin: 0; 22 | } 23 | 24 | .doc { 25 | margin: 3em 0; 26 | font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; 27 | font-size: 1.1em; 28 | font-weight: 300; 29 | 30 | a { 31 | border: 1px solid white; 32 | border-radius: 3px; 33 | padding: 0.75em 0.7em; 34 | color: white; 35 | text-decoration: none; 36 | transition: color 0.1s linear; 37 | &:hover { 38 | background: rgba(0,0,0,0.2); 39 | color: white; 40 | transition: all 0.15s linear; 41 | } 42 | } 43 | } 44 | 45 | .welcome { 46 | -webkit-animation-name: welcome; 47 | -webkit-animation-duration: .9s; 48 | } 49 | 50 | @-webkit-keyframes welcome { 51 | from { 52 | -webkit-transform: scale(0); 53 | opacity: 0; 54 | } 55 | 50% { 56 | -webkit-transform: scale(0); 57 | opacity: 0; 58 | } 59 | 82.5% { 60 | -webkit-transform: scale(1.03); 61 | -webkit-animation-timing-function: ease-out; 62 | opacity: 1; 63 | } 64 | to { 65 | -webkit-transform: scale(1); 66 | } 67 | } 68 | --------------------------------------------------------------------------------