Background information:
I am currently exploring using Yeoman to generate a Jekyll site using generator-jekyllrb. I am using Grunt to help pull everything together. The documentation for generator-jekyllrb contains more information about my base setup.
The problem
Currently, I am trying to use Bootstrap, which I've installed using Bower. My main.scss
file looks like the following:
@import 'bootstrap-sass-official/assets/stylesheets/bootstrap';
@import 'custom/reset';
When I run grunt build
, everything works correctly -- Grunt compiles both the bootstrap files and my own custom reset/sass file.
However, when I run grunt serve
, Grunt initially seems to completely ignore the bootstrap files. It successfully compiles my custom styles, but ignores bootstrap altogether.
What I've tried
I did manage to find a workaround: if I make any change to main.scss
(even something as minor as adding a space), Grunt will correctly compile both bootstrap and my styles. However, this is very annoying, and I would prefer to just have everything working correctly from the start.
I've tried deleting the .jekyll
, .sass-cache
, .tmp
, and dist
folders in case there's some cache that's in a broken state, but doing so had no effect.
At this point, I suspect that the problem is with my Gruntfile.js
file. The tasks Grunt performs when serving vs building are fairly different, so I'm wondering if the issue is caused by something I have for one task but not the other.
Unfortunately, I've never used either Yeoman or Grunt before, so I don't seem to be able to identify exactly what the problem is or how to fix it.
My code
Here's my gruntfile:
// Generated on 2014-07-08 using generator-jekyllrb 1.2.1
'use strict';
// Directory reference:
// css: _assets/css
// compass: _assets/scss
// javascript: _assets/javascript
// images: _assets/media
// fonts: _assets/fonts
module.exports = function (grunt) {
// Show elapsed time after tasks run
require('time-grunt')(grunt);
// Load all Grunt tasks
require('load-grunt-tasks')(grunt);
grunt.initConfig({
// Configurable paths
yeoman: {
app: 'app',
dist: 'dist'
},
watch: {
compass: {
files: [
'<%= yeoman.app %>/_assets/scss/**/*.{scss,sass}',
'<%= yeoman.app %>/_bower_components/bootstrap-sass-official/assets/stylesheets/*.{scss,sass}'],
tasks: ['compass:server', 'autoprefixer:server']
},
autoprefixer: {
files: ['<%= yeoman.app %>/_assets/css/**/*.css'],
tasks: ['copy:stageCss', 'autoprefixer:server']
},
jekyll: {
files: [
'<%= yeoman.app %>/**/*.{html,yml,md,mkd,markdown}',
'!<%= yeoman.app %>/_bower_components/**/*'
],
tasks: ['jekyll:server']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'.jekyll/**/*.html',
'.tmp/_assets/css/**/*.css',
'{.tmp,<%= yeoman.app %>}/<%= js %>/**/*.js',
'<%= yeoman.app %>/_assets/media/**/*.{gif,jpg,jpeg,png,svg,webp}'
]
}
},
connect: {
options: {
port: 9000,
livereload: 35729,
// change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
livereload: {
options: {
open: true,
base: [
'.tmp',
'.jekyll',
'<%= yeoman.app %>'
]
}
},
dist: {
options: {
open: true,
base: [
'<%= yeoman.dist %>'
]
}
},
test: {
options: {
base: [
'.tmp',
'.jekyll',
'test',
'<%= yeoman.app %>'
]
}
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'<%= yeoman.dist %>/*',
// Running Jekyll also cleans the target directory. Exclude any
// non-standard `keep_files` here (e.g., the generated files
// directory from Jekyll Picture Tag).
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: [
'.tmp',
'.jekyll'
]
},
compass: {
options: {
// If you're using global Sass gems, require them here.
// require: ['singularity', 'jacket'],
bundleExec: true,
sassDir: '<%= yeoman.app %>/_assets/scss',
cssDir: '.tmp/_assets/css',
imagesDir: '<%= yeoman.app %>/_assets/media',
javascriptsDir: '<%= yeoman.app %>/_assets/javascript',
importPath: '<%= yeoman.app %>/_bower_components',
relativeAssets: false,
httpImagesPath: '/_assets/media',
httpGeneratedImagesPath: '/_assets/media/generated',
outputStyle: 'expanded',
raw: 'extensions_dir = "<%= yeoman.app %>/_bower_components"\n'
},
dist: {
options: {
generatedImagesDir: '<%= yeoman.dist %>/_assets/media/generated'
}
},
server: {
options: {
debugInfo: true,
generatedImagesDir: '.tmp/_assets/media/generated'
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 versions']
},
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.dist %>/_assets/css',
src: '**/*.css',
dest: '<%= yeoman.dist %>/_assets/css'
}]
},
server: {
files: [{
expand: true,
cwd: '.tmp/_assets/css',
src: '**/*.css',
dest: '.tmp/_assets/css'
}]
}
},
jekyll: {
options: {
bundleExec: true,
config: '_config.yml,_config.build.yml',
src: '<%= yeoman.app %>'
},
dist: {
options: {
dest: '<%= yeoman.dist %>',
}
},
server: {
options: {
config: '_config.yml',
dest: '.jekyll'
}
},
check: {
options: {
doctor: true
}
}
},
useminPrepare: {
options: {
dest: '<%= yeoman.dist %>'
},
html: '<%= yeoman.dist %>/index.html'
},
usemin: {
options: {
assetsDirs: '<%= yeoman.dist %>',
},
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/_assets/css/**/*.css']
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: '**/*.html',
dest: '<%= yeoman.dist %>'
}]
}
},
// Usemin adds files to concat
concat: {},
// Usemin adds files to uglify
uglify: {},
// Usemin adds files to cssmin
cssmin: {
dist: {
options: {
check: 'gzip'
}
}
},
imagemin: {
dist: {
options: {
progressive: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: '**/*.{jpg,jpeg,png}',
dest: '<%= yeoman.dist %>'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: '**/*.svg',
dest: '<%= yeoman.dist %>'
}]
}
},
copy: {
dist: {
files: [
{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
src: [
// Jekyll processes and moves HTML and text files.
// Usemin moves CSS and javascript inside of Usemin blocks.
// Copy moves asset files and directories.
'_assets/media/**/*',
'_assets/fonts/**/*',
// Like Jekyll, exclude files & folders prefixed with an underscore.
'!**/_*{,/**}',
// Explicitly add any files your site needs for distribution here.
//'_bower_components/jquery/jquery.js',
//'favicon.ico',
//'apple-touch*.png'
],
dest: '<%= yeoman.dist %>'
},
{
expand: true,
flatten: true,
dot: true,
cwd: '<%= yeoman.app %>',
src: [
'_bower_components/bootstrap-sass-official/assets/fonts/**/*'
],
dest: '<%= yeoman.dist %>/_assets/css/bootstrap'
}
]
},
// Copy CSS into .tmp directory for Autoprefixer processing
stageCss: {
files: [
{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>/_assets/css',
src: '**/*.css',
dest: '.tmp/_assets/css'
},
{
expand: true,
flatten: true,
dot: true,
cwd: '<%= yeoman.app %>',
src: [
'_bower_components/bootstrap-sass-official/assets/fonts/**/*'
],
dest: '.tmp/_assets/css/bootstrap'
}
]
}
},
filerev: {
options: {
length: 4
},
dist: {
files: [{
src: [
'<%= yeoman.dist %>/_assets/javascript/**/*.js',
'<%= yeoman.dist %>/_assets/css/**/*.css',
'<%= yeoman.dist %>/_assets/media/**/*.{gif,jpg,jpeg,png,svg,webp}',
'<%= yeoman.dist %>/_assets/fonts/**/*.{eot*,otf,svg,ttf,woff}'
]
}]
}
},
buildcontrol: {
dist: {
options: {
remote: '../',
branch: 'gh-pages',
commit: true,
push: true
}
}
},
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/_assets/javascript/**/*.js',
'test/spec/**/*.js'
]
},
csslint: {
options: {
csslintrc: '.csslintrc'
},
check: {
src: [
'<%= yeoman.app %>/_assets/css/**/*.css',
'<%= yeoman.app %>/_assets/scss/**/*.scss'
]
}
},
concurrent: {
server: [
'compass:server',
'copy:stageCss',
'jekyll:server'
],
dist: [
'compass:dist',
'copy:dist'
]
}
});
// Define Tasks
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'concurrent:server',
'autoprefixer:server',
'connect:livereload',
'watch'
]);
});
grunt.registerTask('server', function () {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve']);
});
// No real tests yet. Add your own.
grunt.registerTask('test', [
// 'clean:server',
// 'concurrent:test',
// 'connect:test'
]);
grunt.registerTask('check', [
'clean:server',
'jekyll:check',
'compass:server',
'jshint:all',
'csslint:check'
]);
grunt.registerTask('build', [
'clean',
// Jekyll cleans files from the target directory, so must run first
'jekyll:dist',
'concurrent:dist',
'useminPrepare',
'concat',
'autoprefixer:dist',
'cssmin',
'uglify',
'imagemin',
'svgmin',
'filerev',
'usemin',
'htmlmin'
]);
grunt.registerTask('deploy', [
'check',
'test',
'build',
'buildcontrol'
]);
grunt.registerTask('default', [
'check',
'test',
'build'
]);
};
If you want to play with the entire codebase, you can clone the github repo:
git clone https://github.com/Michael0x2a/uwfig.git
cd uwfig
bundle install
bower install
npm install
You can then reproduce the behavior by running:
grunt serve
Grunt should be serving the default Jekyll page on localhost:9000
. I've added two buttons to the page, both using styles from Bootstrap. If Bootstrap were working the left button would be colored blue, while the right button would have a star glyphicon inside. Currently, the bootstrap styles are not being applied and the buttons are plain.
I'm currently using Windows 8.1, Ruby version 1.9.3p545, Node.js version 0.10.26, grunt version 0.4.5, and grunt-cli version 0.1.13.