0
0
mirror of https://github.com/twbs/bootstrap.git synced 2024-12-12 00:08:59 +01:00
Bootstrap/Gruntfile.js

512 lines
16 KiB
JavaScript
Raw Normal View History

2014-01-21 01:00:52 +01:00
/*!
* Bootstrap's Gruntfile
* http://getbootstrap.com
* Copyright 2013-2016 Twitter, Inc.
2014-01-21 01:00:52 +01:00
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
2013-12-07 01:51:59 +01:00
module.exports = function (grunt) {
2013-09-18 18:50:02 +02:00
'use strict';
// Force use of Unix newlines
grunt.util.linefeed = '\n';
2013-12-08 11:24:47 +01:00
RegExp.quote = function (string) {
2014-01-17 20:51:53 +01:00
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
2014-01-17 20:51:53 +01:00
var fs = require('fs');
2014-01-19 15:04:29 +01:00
var path = require('path');
var glob = require('glob');
var isTravis = require('is-travis');
var npmShrinkwrap = require('npm-shrinkwrap');
var mq4HoverShim = require('mq4-hover-shim');
var autoprefixerSettings = require('./grunt/autoprefixer-settings.js');
var autoprefixer = require('autoprefixer')(autoprefixerSettings);
2014-12-10 22:51:43 +01:00
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
Object.keys(configBridge.paths).forEach(function (key) {
configBridge.paths[key].forEach(function (val, i, arr) {
arr[i] = path.join('./docs/assets', val);
});
});
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
2014-01-28 12:16:13 +01:00
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
2014-01-28 12:16:13 +01:00
' */\n',
jqueryCheck: 'if (typeof jQuery === \'undefined\') {\n' +
' throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery\')\n' +
'}\n',
jqueryVersionCheck: '+function ($) {\n' +
' var version = $.fn.jquery.split(\' \')[0].split(\'.\')\n' +
' if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 3)) {\n' +
' throw new Error(\'Bootstrap\\\'s JavaScript requires at least jQuery v1.9.1 but less than v3.0.0\')\n' +
' }\n' +
'}(jQuery);\n\n',
// Task configuration.
clean: {
dist: 'dist',
docs: 'docs/dist'
},
// JS build configuration
lineremover: {
es6Import: {
files: {
'<%= concat.bootstrap.dest %>': '<%= concat.bootstrap.dest %>'
},
options: {
exclusionPattern: /^(import|export)/g
}
}
},
2015-05-07 21:48:22 +02:00
babel: {
2015-05-13 21:48:34 +02:00
dev: {
options: {
sourceMap: true,
modules: 'ignore'
},
2015-05-07 21:48:22 +02:00
files: {
2015-05-11 21:05:35 +02:00
'js/dist/util.js' : 'js/src/util.js',
'js/dist/alert.js' : 'js/src/alert.js',
'js/dist/button.js' : 'js/src/button.js',
'js/dist/carousel.js' : 'js/src/carousel.js',
'js/dist/collapse.js' : 'js/src/collapse.js',
'js/dist/dropdown.js' : 'js/src/dropdown.js',
'js/dist/modal.js' : 'js/src/modal.js',
2015-05-11 21:29:06 +02:00
'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
'js/dist/tab.js' : 'js/src/tab.js',
2015-05-12 23:28:11 +02:00
'js/dist/tooltip.js' : 'js/src/tooltip.js',
'js/dist/popover.js' : 'js/src/popover.js'
2015-05-07 21:48:22 +02:00
}
},
dist: {
options: {
modules: 'ignore'
},
files: {
'<%= concat.bootstrap.dest %>' : '<%= concat.bootstrap.dest %>'
}
},
2015-05-13 19:13:34 +02:00
umd: {
options: {
modules: 'umd'
},
files: {
'dist/js/umd/util.js' : 'js/src/util.js',
'dist/js/umd/alert.js' : 'js/src/alert.js',
'dist/js/umd/button.js' : 'js/src/button.js',
'dist/js/umd/carousel.js' : 'js/src/carousel.js',
'dist/js/umd/collapse.js' : 'js/src/collapse.js',
'dist/js/umd/dropdown.js' : 'js/src/dropdown.js',
'dist/js/umd/modal.js' : 'js/src/modal.js',
'dist/js/umd/scrollspy.js' : 'js/src/scrollspy.js',
'dist/js/umd/tab.js' : 'js/src/tab.js',
'dist/js/umd/tooltip.js' : 'js/src/tooltip.js',
'dist/js/umd/popover.js' : 'js/src/popover.js'
}
}
},
2013-08-08 08:06:29 +02:00
2015-08-19 04:22:46 +02:00
eslint: {
options: {
configFile: 'js/.eslintrc'
},
target: 'js/src/*.js'
},
2013-12-07 01:51:38 +01:00
jscs: {
options: {
2014-02-28 16:29:36 +01:00
config: 'js/.jscsrc'
2013-12-07 01:51:38 +01:00
},
2014-01-20 22:06:13 +01:00
grunt: {
2015-05-07 21:48:22 +02:00
src: ['Gruntfile.js', 'grunt/*.js']
2013-12-07 01:51:38 +01:00
},
core: {
2015-05-07 21:48:22 +02:00
src: 'js/src/*.js'
2013-12-07 01:51:38 +01:00
},
test: {
2015-05-07 21:48:22 +02:00
src: 'js/tests/unit/*.js'
},
assets: {
options: {
requireCamelCaseOrUpperCaseIdentifiers: null
},
2015-05-07 21:48:22 +02:00
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
2013-12-07 01:51:38 +01:00
}
},
stamp: {
options: {
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>\n+function ($) {\n',
footer: '\n}(jQuery);'
},
bootstrap: {
files: {
src: '<%= concat.bootstrap.dest %>'
}
}
},
2013-08-08 08:06:29 +02:00
concat: {
2014-06-15 16:41:24 +02:00
options: {
stripBanners: false
2014-06-15 16:41:24 +02:00
},
bootstrap: {
2015-01-03 22:58:44 +01:00
src: [
'js/src/util.js',
'js/src/alert.js',
'js/src/button.js',
'js/src/carousel.js',
'js/src/collapse.js',
'js/src/dropdown.js',
'js/src/modal.js',
'js/src/scrollspy.js',
'js/src/tab.js',
'js/src/tooltip.js',
'js/src/popover.js'
2015-01-03 22:58:44 +01:00
],
dest: 'dist/js/<%= pkg.name %>.js'
2015-01-03 22:58:44 +01:00
}
},
uglify: {
options: {
2015-05-06 22:34:14 +02:00
compress: {
warnings: false
2015-01-03 22:58:44 +01:00
},
2015-05-06 22:34:14 +02:00
mangle: true,
preserveComments: /^!|@preserve|@license|@cc_on/i
2013-09-19 16:41:14 +02:00
},
2015-05-06 22:34:14 +02:00
core: {
src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
2015-05-06 22:34:14 +02:00
},
docsJs: {
src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js'
}
},
2014-03-10 00:09:36 +01:00
qunit: {
options: {
inject: 'js/tests/unit/phantom.js'
},
files: 'js/tests/index.html'
},
// CSS build configuration
2014-12-09 04:02:25 +01:00
scsslint: {
options: {
bundleExec: true,
2015-09-30 00:48:56 +02:00
config: 'scss/.scss-lint.yml',
2015-06-19 09:14:52 +02:00
reporterOutput: null
},
2016-01-04 03:09:09 +01:00
core: {
src: ['scss/*.scss', '!scss/_normalize.scss']
},
docs: {
src: ['docs/assets/scss/*.scss', '!scss/_normalize.scss', '!docs/assets/scss/docs.scss']
2016-01-04 03:09:09 +01:00
}
2014-12-09 04:02:25 +01:00
},
2015-01-01 10:41:56 +01:00
postcss: {
2014-02-22 11:17:58 +01:00
core: {
options: {
map: true,
processors: [
mq4HoverShim.postprocessorFor({ hoverSelectorPrefix: '.bs-true-hover ' }),
autoprefixer
]
2014-02-22 11:17:58 +01:00
},
src: 'dist/css/*.css'
2014-02-22 11:17:58 +01:00
},
docs: {
options: {
processors: [
autoprefixer
]
},
2014-12-01 05:29:47 +01:00
src: 'docs/assets/css/docs.min.css'
2014-02-22 11:17:58 +01:00
},
examples: {
options: {
processors: [
autoprefixer
]
},
2014-02-22 11:17:58 +01:00
expand: true,
cwd: 'docs/examples/',
src: ['**/*.css'],
dest: 'docs/examples/'
}
},
2013-12-22 08:25:18 +01:00
cssmin: {
2014-03-11 18:48:14 +01:00
options: {
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
compatibility: 'ie9',
keepSpecialComments: '*',
sourceMap: true,
2015-12-08 06:51:04 +01:00
advanced: false
2014-03-11 18:48:14 +01:00
},
2014-12-01 05:29:47 +01:00
core: {
files: [
{
expand: true,
cwd: 'dist/css',
src: ['*.css', '!*.min.css'],
dest: 'dist/css',
ext: '.min.css'
}
]
},
2014-03-11 18:48:14 +01:00
docs: {
2014-12-01 05:29:47 +01:00
src: 'docs/assets/css/docs.min.css',
dest: 'docs/assets/css/docs.min.css'
2013-12-22 08:25:18 +01:00
}
},
2013-08-18 09:36:51 +02:00
copy: {
2013-12-31 20:38:32 +01:00
docs: {
2015-02-20 10:22:06 +01:00
expand: true,
cwd: 'dist/',
src: [
'**/*'
],
dest: 'docs/dist/'
2013-08-18 09:36:51 +02:00
}
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
jekyll: {
options: {
2015-08-25 07:43:47 +02:00
bundleExec: true,
2015-10-28 06:20:47 +01:00
config: '_config.yml',
incremental: false
},
docs: {},
github: {
options: {
raw: 'github: true'
}
}
},
htmllint: {
options: {
ignore: [
'Element “img” is missing required attribute “src”.',
'Attribute “autocomplete” is only allowed when the input type is “color”, “date”, “datetime”, “datetime-local”, “email”, “month”, “number”, “password”, “range”, “search”, “tel”, “text”, “time”, “url”, or “week”.',
'Attribute “autocomplete” not allowed on element “button” at this point.',
'Element “div” not allowed as child of element “progress” in this context. (Suppressing further errors from this subtree.)',
'Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).',
'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “color” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “date” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “datetime-local” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “month” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “time” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
'Attribute “integrity” not allowed on element “script” at this point.' // Until https://github.com/jzaefferer/grunt-html/issues/86 gets fixed
]
},
src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html']
},
watch: {
src: {
2015-05-07 21:48:22 +02:00
files: '<%= jscs.core.src %>',
2015-05-13 21:48:34 +02:00
tasks: ['babel:dev']
},
sass: {
files: 'scss/**/*.scss',
2015-08-13 05:14:14 +02:00
tasks: ['dist-css', 'docs']
},
docs: {
files: 'docs/assets/scss/**/*.scss',
tasks: ['dist-css', 'docs']
}
},
'saucelabs-qunit': {
all: {
options: {
build: process.env.TRAVIS_JOB_ID,
concurrency: 10,
maxRetries: 3,
maxPollRetries: 4,
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
}
}
2014-01-20 22:06:13 +01:00
},
exec: {
npmUpdate: {
command: 'npm update'
2014-05-13 07:01:29 +02:00
}
2015-08-19 07:47:26 +02:00
},
buildcontrol: {
options: {
dir: '_gh_pages',
commit: true,
push: true,
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
},
pages: {
options: {
remote: 'git@github.com:twbs/derpstrap.git',
branch: 'gh-pages'
}
}
},
compress: {
main: {
options: {
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
mode: 'zip',
level: 9,
pretty: true
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: 'bootstrap-<%= pkg.version %>-dist'
}
]
}
}
});
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt, { scope: 'devDependencies',
// Exclude Sass compilers. We choose the one to load later on.
pattern: ['grunt-*', '!grunt-sass', '!grunt-contrib-sass'] });
2014-03-07 07:46:15 +01:00
require('time-grunt')(grunt);
// Docs HTML validation task
2015-08-25 07:43:47 +02:00
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
2014-05-19 00:52:42 +02:00
var runSubset = function (subset) {
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
};
var isUndefOrNonZero = function (val) {
return val === undefined || val !== '0';
};
2014-05-19 00:52:42 +02:00
// Test task.
var testSubtasks = [];
// Skip core tests if running a different subset of the test suite
2014-11-20 00:43:23 +01:00
if (runSubset('core') &&
2014-12-01 05:29:47 +01:00
// Skip core tests if this is a Savage build
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'test-js', 'docs']);
}
// Skip HTML validation if running a different subset of the test suite
if (runSubset('validate-html') &&
isTravis &&
// Skip HTML5 validator when [skip validator] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
testSubtasks.push('validate-html');
}
// Only run Sauce Labs tests if there's a Sauce access key
2014-01-17 20:51:53 +01:00
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
// Skip Sauce if running a different subset of the test suite
runSubset('sauce-js-unit') &&
// Skip Sauce on Travis when [skip sauce] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
testSubtasks.push('babel:dev');
testSubtasks.push('connect');
testSubtasks.push('saucelabs-qunit');
2013-08-06 09:39:35 +02:00
}
grunt.registerTask('test', testSubtasks);
2015-08-19 04:22:46 +02:00
grunt.registerTask('test-js', ['eslint', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
grunt.registerTask('dist-js', ['babel:dev', 'concat', 'lineremover', 'babel:dist', 'stamp', 'uglify:core', 'commonjs']);
2016-01-04 03:09:09 +01:00
grunt.registerTask('test-scss', ['scsslint:core']);
2014-12-09 04:02:25 +01:00
// CSS distribution task.
// Supported Compilers: sass (Ruby) and libsass.
(function (sassCompilerName) {
require('./grunt/bs-sass-compile/' + sassCompilerName + '.js')(grunt);
})(process.env.TWBS_SASS || 'libsass');
// grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs']);
grunt.registerTask('sass-compile', ['sass:core', 'sass:docs']);
grunt.registerTask('dist-css', ['sass-compile', 'postcss:core', 'cssmin:core', 'cssmin:docs']);
2013-08-18 09:36:51 +02:00
// Full distribution task.
2014-12-01 05:29:47 +01:00
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js']);
// Default task.
2014-12-01 05:29:47 +01:00
grunt.registerTask('default', ['clean:dist', 'test']);
2013-08-08 08:06:29 +02:00
2015-05-13 19:13:34 +02:00
grunt.registerTask('commonjs', ['babel:umd', 'npm-js']);
grunt.registerTask('npm-js', 'Generate npm-js entrypoint module in dist dir.', function () {
var srcFiles = Object.keys(grunt.config.get('babel.umd.files')).map(function (filename) {
return './' + path.join('umd', path.basename(filename))
})
2014-08-28 03:39:41 +02:00
var destFilepath = 'dist/js/npm.js';
generateCommonJSModule(grunt, srcFiles, destFilepath);
});
// Docs task.
grunt.registerTask('docs-css', ['postcss:docs', 'postcss:examples', 'cssmin:docs']);
grunt.registerTask('lint-docs-css', ['scsslint:docs']);
2014-12-10 22:51:43 +01:00
grunt.registerTask('docs-js', ['uglify:docsJs']);
2015-05-07 21:48:22 +02:00
grunt.registerTask('lint-docs-js', ['jscs:assets']);
grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs']);
grunt.registerTask('docs-github', ['jekyll:github']);
grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);
2015-08-19 07:49:26 +02:00
// Publish to GitHub
grunt.registerTask('publish', ['buildcontrol:pages']);
// Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
// This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
grunt.registerTask('_update-shrinkwrap', function () {
var done = this.async();
npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
if (err) {
grunt.fail.warn(err);
}
var dest = 'grunt/npm-shrinkwrap.json';
fs.renameSync('npm-shrinkwrap.json', dest);
grunt.log.writeln('File ' + dest.cyan + ' updated.');
done();
});
});
};