Gruntfile.js 12.2 KB
Newer Older
Chris Rebert's avatar
Chris Rebert committed
1
2
3
/*!
 * Bootstrap's Gruntfile
 * http://getbootstrap.com
4
 * Copyright 2013-2016 The Bootstrap Authors
5
 * Copyright 2013-2016 Twitter, Inc.
Chris Rebert's avatar
Chris Rebert committed
6
7
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */
8

Chris Rebert's avatar
Chris Rebert committed
9
module.exports = function (grunt) {
XhmikosR's avatar
XhmikosR committed
10
  'use strict';
11

12
13
14
  // Force use of Unix newlines
  grunt.util.linefeed = '\n';

Zlatan Vasović's avatar
Zlatan Vasović committed
15
  RegExp.quote = function (string) {
Chris Rebert's avatar
Chris Rebert committed
16
17
    return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  };
18

Chris Rebert's avatar
Chris Rebert committed
19
  var fs = require('fs');
20
  var path = require('path');
Chris Rebert's avatar
Chris Rebert committed
21
  var isTravis = require('is-travis');
Mark Otto's avatar
Mark Otto committed
22

23
24
25
26
27
28
29
  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);
    });
  });
30

31
32
33
34
35
  // Project configuration.
  grunt.initConfig({

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
36
    banner: '/*!\n' +
XhmikosR's avatar
XhmikosR committed
37
38
            ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
            ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
39
            ' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
XhmikosR's avatar
XhmikosR committed
40
            ' */\n',
41
42
43
44
45
    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' +
Chris Rebert's avatar
Chris Rebert committed
46
47
                        '  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' +
48
49
                        '  }\n' +
                        '}(jQuery);\n\n',
50
51
52

    // Task configuration.
    clean: {
53
54
      dist: 'dist',
      docs: 'docs/dist'
55
56
    },

57
    // JS build configuration
fat's avatar
fat committed
58
    babel: {
fat's avatar
fat committed
59
      dev: {
60
61
62
        options: {
          sourceMap: true,
          modules: 'ignore'
63
        },
fat's avatar
fat committed
64
        files: {
fat's avatar
fat committed
65
66
67
68
69
70
71
          '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',
fat's avatar
tab es6    
fat committed
72
          'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
73
          'js/dist/tab.js'       : 'js/src/tab.js',
fat's avatar
fat committed
74
75
          'js/dist/tooltip.js'   : 'js/src/tooltip.js',
          'js/dist/popover.js'   : 'js/src/popover.js'
fat's avatar
fat committed
76
        }
77
      },
78
      dist: {
XhmikosR's avatar
XhmikosR committed
79
        options: {
80
          modules: 'ignore'
XhmikosR's avatar
XhmikosR committed
81
        },
82
83
84
        files: {
          '<%= concat.bootstrap.dest %>' : '<%= concat.bootstrap.dest %>'
        }
85
86
      }
    },
87

88
    stamp: {
89
      options: {
90
91
        banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>\n+function ($) {\n',
        footer: '\n}(jQuery);'
92
93
      },
      bootstrap: {
94
95
96
        files: {
          src: '<%= concat.bootstrap.dest %>'
        }
97
98
      }
    },
99

100
    concat: {
101
      options: {
XhmikosR's avatar
XhmikosR committed
102
103
104
105
        // Custom function to remove all export and import statements
        process: function (src) {
          return src.replace(/^(export|import).*/gm, '');
        },
106
        stripBanners: false
107
      },
108
      bootstrap: {
fat's avatar
fat committed
109
        src: [
110
111
112
113
114
115
116
117
118
119
120
          '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'
fat's avatar
fat committed
121
        ],
122
        dest: 'dist/js/<%= pkg.name %>.js'
fat's avatar
fat committed
123
124
125
126
127
      }
    },

    uglify: {
      options: {
fat's avatar
fat committed
128
129
        compress: {
          warnings: false
fat's avatar
fat committed
130
        },
fat's avatar
fat committed
131
        mangle: true,
XhmikosR's avatar
XhmikosR committed
132
        preserveComments: /^!|@preserve|@license|@cc_on/i
XhmikosR's avatar
XhmikosR committed
133
      },
fat's avatar
fat committed
134
135
      core: {
        src: '<%= concat.bootstrap.dest %>',
136
        dest: 'dist/js/<%= pkg.name %>.min.js'
fat's avatar
fat committed
137
      },
138
      docsJs: {
139
        src: configBridge.paths.docsJs,
140
        dest: 'docs/assets/js/docs.min.js'
141
142
143
      }
    },

Mark Otto's avatar
Mark Otto committed
144
145
146
147
148
149
150
    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: 'js/tests/index.html'
    },

151
    // CSS build configuration
Chris Rebert's avatar
Chris Rebert committed
152
153
    scsslint: {
      options: {
154
        bundleExec: true,
nextgenthemes's avatar
nextgenthemes committed
155
        config: 'scss/.scss-lint.yml',
Mark Otto's avatar
Mark Otto committed
156
157
        reporterOutput: null
      },
Mark Otto's avatar
Mark Otto committed
158
159
160
161
      core: {
        src: ['scss/*.scss', '!scss/_normalize.scss']
      },
      docs: {
vsn4ik's avatar
vsn4ik committed
162
        src: ['docs/assets/scss/*.scss', '!docs/assets/scss/docs.scss']
Mark Otto's avatar
Mark Otto committed
163
      }
Chris Rebert's avatar
Chris Rebert committed
164
165
    },

XhmikosR's avatar
XhmikosR committed
166
    cssmin: {
XhmikosR's avatar
XhmikosR committed
167
      options: {
168
169
        // 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
170
        compatibility: 'ie9',
171
        keepSpecialComments: '*',
172
        sourceMap: true,
Mark Otto's avatar
Mark Otto committed
173
        advanced: false
XhmikosR's avatar
XhmikosR committed
174
      },
Mark Otto's avatar
Mark Otto committed
175
      core: {
176
177
178
179
180
181
182
183
184
        files: [
          {
            expand: true,
            cwd: 'dist/css',
            src: ['*.css', '!*.min.css'],
            dest: 'dist/css',
            ext: '.min.css'
          }
        ]
185
      },
XhmikosR's avatar
XhmikosR committed
186
      docs: {
Mark Otto's avatar
Mark Otto committed
187
        src: 'docs/assets/css/docs.min.css',
188
        dest: 'docs/assets/css/docs.min.css'
XhmikosR's avatar
XhmikosR committed
189
190
191
      }
    },

Mark Otto's avatar
Mark Otto committed
192
    copy: {
Mark Otto's avatar
Mark Otto committed
193
      docs: {
XhmikosR's avatar
XhmikosR committed
194
195
196
197
198
199
        expand: true,
        cwd: 'dist/',
        src: [
          '**/*'
        ],
        dest: 'docs/dist/'
Mark Otto's avatar
Mark Otto committed
200
201
202
      }
    },

203
204
205
206
207
    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
208
        }
209
210
211
      }
    },

212
    jekyll: {
213
      options: {
XhmikosR's avatar
XhmikosR committed
214
        bundleExec: true,
XhmikosR's avatar
XhmikosR committed
215
216
        config: '_config.yml',
        incremental: false
217
218
219
220
221
222
223
      },
      docs: {},
      github: {
        options: {
          raw: 'github: true'
        }
      }
224
225
    },

226
    htmllint: {
227
      options: {
228
229
        ignore: [
          'Element “img” is missing required attribute “src”.',
230
          '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”.',
231
232
          '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.)',
233
          '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).',
234
          'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
235
236
237
238
239
          '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.',
XhmikosR's avatar
XhmikosR committed
240
          'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.'
241
        ]
242
      },
243
      src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html']
244
245
    },

246
247
    watch: {
      src: {
248
        files: '<%= concat.bootstrap.src %>',
fat's avatar
fat committed
249
        tasks: ['babel:dev']
250
      },
251
252
      sass: {
        files: 'scss/**/*.scss',
Mark Otto's avatar
Mark Otto committed
253
        tasks: ['dist-css', 'docs']
Mark Otto's avatar
Mark Otto committed
254
255
256
257
      },
      docs: {
        files: 'docs/assets/scss/**/*.scss',
        tasks: ['dist-css', 'docs']
258
      }
259
260
    },

261
262
263
264
    'saucelabs-qunit': {
      all: {
        options: {
          build: process.env.TRAVIS_JOB_ID,
Chris Rebert's avatar
Chris Rebert committed
265
          concurrency: 10,
266
          maxRetries: 3,
267
          maxPollRetries: 4,
268
          urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
269
          browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
270
271
        }
      }
Chris Rebert's avatar
Chris Rebert committed
272
273
274
    },

    exec: {
275
276
277
278
279
280
      postcss: {
        command: 'npm run postcss'
      },
      'postcss-docs': {
        command: 'npm run postcss-docs'
      }
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    },

    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'
        }
      }
XhmikosR's avatar
XhmikosR committed
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
    },

    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'
          }
        ]
      }
315
    }
XhmikosR's avatar
XhmikosR committed
316

317
  });
318
319


320
  // These plugins provide necessary tasks.
321
322
323
  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'] });
XhmikosR's avatar
XhmikosR committed
324
  require('time-grunt')(grunt);
325

326
  // Docs HTML validation task
XhmikosR's avatar
XhmikosR committed
327
  grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
328

329
330
331
  var runSubset = function (subset) {
    return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
  };
332
333
334
  var isUndefOrNonZero = function (val) {
    return val === undefined || val !== '0';
  };
335

336
  // Test task.
337
338
  var testSubtasks = [];
  // Skip core tests if running a different subset of the test suite
339
  if (runSubset('core') &&
Mark Otto's avatar
Mark Otto committed
340
    // Skip core tests if this is a Savage build
341
    process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
342
    testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'qunit', 'docs']);
343
344
  }
  // Skip HTML validation if running a different subset of the test suite
345
  if (runSubset('validate-html') &&
Chris Rebert's avatar
Chris Rebert committed
346
347
      isTravis &&
      // Skip HTML5 validator when [skip validator] is in the commit message
348
      isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
349
350
    testSubtasks.push('validate-html');
  }
351
  // Only run Sauce Labs tests if there's a Sauce access key
Chris Rebert's avatar
Chris Rebert committed
352
  if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
353
      // Skip Sauce if running a different subset of the test suite
354
355
356
      runSubset('sauce-js-unit') &&
      // Skip Sauce on Travis when [skip sauce] is in the commit message
      isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
357
    testSubtasks.push('babel:dev');
358
359
    testSubtasks.push('connect');
    testSubtasks.push('saucelabs-qunit');
360
361
  }
  grunt.registerTask('test', testSubtasks);
362

363
  // JS distribution task.
364
  grunt.registerTask('dist-js', ['babel:dev', 'concat', 'babel:dist', 'stamp', 'uglify:core']);
365

Mark Otto's avatar
Mark Otto committed
366
  grunt.registerTask('test-scss', ['scsslint:core']);
Chris Rebert's avatar
Chris Rebert committed
367

368
  // CSS distribution task.
369
370
371
372
  // Supported Compilers: sass (Ruby) and libsass.
  (function (sassCompilerName) {
    require('./grunt/bs-sass-compile/' + sassCompilerName + '.js')(grunt);
  })(process.env.TWBS_SASS || 'libsass');
373
374
  // grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs']);
  grunt.registerTask('sass-compile', ['sass:core', 'sass:docs']);
375

376
  grunt.registerTask('dist-css', ['sass-compile', 'exec:postcss', 'cssmin:core', 'cssmin:docs']);
Mark Otto's avatar
Mark Otto committed
377

378
  // Full distribution task.
Mark Otto's avatar
Mark Otto committed
379
  grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js']);
380

381
  // Default task.
Mark Otto's avatar
Mark Otto committed
382
  grunt.registerTask('default', ['clean:dist', 'test']);
383

384
  // Docs task.
385
  grunt.registerTask('docs-css', ['cssmin:docs', 'exec:postcss-docs']);
386
  grunt.registerTask('lint-docs-css', ['scsslint:docs']);
Mark Otto's avatar
Mark Otto committed
387
  grunt.registerTask('docs-js', ['uglify:docsJs']);
388
  grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs']);
389
  grunt.registerTask('docs-github', ['jekyll:github']);
390

391
  grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);
392

Mark Otto's avatar
Mark Otto committed
393
394
  // Publish to GitHub
  grunt.registerTask('publish', ['buildcontrol:pages']);
395
};