Gruntfile.js 11.4 KB
Newer Older
Chris Rebert's avatar
Chris Rebert committed
1
2
/*!
 * Bootstrap's Gruntfile
3
 * https://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) {
10
  'use strict'
11

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

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

19
20
  var path = require('path')
  var isTravis = require('is-travis')
Mark Otto's avatar
Mark Otto committed
21

22
  var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' })
23
24
25

  Object.keys(configBridge.paths).forEach(function (key) {
    configBridge.paths[key].forEach(function (val, i, arr) {
26
27
28
      arr[i] = path.join('./docs', val)
    })
  })
29

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

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
35
    banner: '/*!\n' +
XhmikosR's avatar
XhmikosR committed
36
37
            ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
            ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
38
            ' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
XhmikosR's avatar
XhmikosR committed
39
            ' */\n',
40
    jqueryCheck: 'if (typeof jQuery === \'undefined\') {\n' +
41
                 '  throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\\\'s JavaScript.\')\n' +
42
43
44
                 '}\n',
    jqueryVersionCheck: '+function ($) {\n' +
                        '  var version = $.fn.jquery.split(\' \')[0].split(\'.\')\n' +
45
46
                        '  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 4)) {\n' +
                        '    throw new Error(\'Bootstrap\\\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0\')\n' +
47
48
                        '  }\n' +
                        '}(jQuery);\n\n',
49
50
51

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

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

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

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

Mark Otto's avatar
Mark Otto committed
123
124
125
126
127
128
129
    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: 'js/tests/index.html'
    },

130
    // CSS build configuration
Mark Otto's avatar
Mark Otto committed
131
    copy: {
Mark Otto's avatar
Mark Otto committed
132
      docs: {
XhmikosR's avatar
XhmikosR committed
133
134
135
136
137
138
        expand: true,
        cwd: 'dist/',
        src: [
          '**/*'
        ],
        dest: 'docs/dist/'
Mark Otto's avatar
Mark Otto committed
139
140
141
      }
    },

142
143
144
145
146
    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
147
        }
148
149
150
      }
    },

151
    jekyll: {
152
      options: {
XhmikosR's avatar
XhmikosR committed
153
        bundleExec: true,
XhmikosR's avatar
XhmikosR committed
154
155
        config: '_config.yml',
        incremental: false
156
157
158
159
160
161
162
      },
      docs: {},
      github: {
        options: {
          raw: 'github: true'
        }
      }
163
164
    },

165
    htmllint: {
166
      options: {
167
        ignore: [
XhmikosR's avatar
XhmikosR committed
168
          'Attribute “autocomplete” is only allowed when the input type is “color”, “date”, “datetime”, “datetime-local”, “email”, “hidden”, “month”, “number”, “password”, “range”, “search”, “tel”, “text”, “time”, “url”, or “week”.',
169
          'Attribute “autocomplete” not allowed on element “button” at this point.',
170
          '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).',
XhmikosR's avatar
XhmikosR committed
171
172
          'Element “div” not allowed as child of element “progress” in this context. (Suppressing further errors from this subtree.)',
          'Element “img” is missing required attribute “src”.',
173
174
          '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.',
XhmikosR's avatar
XhmikosR committed
175
          'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
176
177
178
          '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
179
          'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.'
180
        ]
181
      },
182
      src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html']
183
184
    },

185
186
    watch: {
      src: {
187
        files: '<%= concat.bootstrap.src %>',
fat's avatar
fat committed
188
        tasks: ['babel:dev']
189
      },
190
191
      sass: {
        files: 'scss/**/*.scss',
Mark Otto's avatar
Mark Otto committed
192
        tasks: ['dist-css', 'docs']
Mark Otto's avatar
Mark Otto committed
193
194
195
196
      },
      docs: {
        files: 'docs/assets/scss/**/*.scss',
        tasks: ['dist-css', 'docs']
197
      }
198
199
    },

200
201
202
203
    'saucelabs-qunit': {
      all: {
        options: {
          build: process.env.TRAVIS_JOB_ID,
Chris Rebert's avatar
Chris Rebert committed
204
          concurrency: 10,
205
          maxRetries: 3,
206
          maxPollRetries: 4,
207
          urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
208
          browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
209
210
        }
      }
Chris Rebert's avatar
Chris Rebert committed
211
212
213
    },

    exec: {
214
215
216
217
218
219
      'clean-css': {
        command: 'npm run clean-css'
      },
      'clean-css-docs': {
        command: 'npm run clean-css-docs'
      },
220
221
222
223
224
      postcss: {
        command: 'npm run postcss'
      },
      'postcss-docs': {
        command: 'npm run postcss-docs'
225
      },
226
227
      htmlhint: {
        command: 'npm run htmlhint'
228
229
230
231
232
233
      },
      'scss-lint': {
        command: 'npm run scss-lint'
      },
      'scss-lint-docs': {
        command: 'npm run scss-lint-docs'
234
235
236
237
238
239
      },
      uglify: {
        command: 'npm run uglify'
      },
      'uglify-docs': {
        command: 'npm run uglify-docs'
240
      }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    },

    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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
    },

    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'
          }
        ]
      }
275
    }
XhmikosR's avatar
XhmikosR committed
276

277
  })
278
279


280
  // These plugins provide necessary tasks.
281
282
  require('load-grunt-tasks')(grunt, { scope: 'devDependencies',
    // Exclude Sass compilers. We choose the one to load later on.
283
284
    pattern: ['grunt-*', '!grunt-sass', '!grunt-contrib-sass'] })
  require('time-grunt')(grunt)
285

286
  // Docs HTML validation task
287
  grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint', 'exec:htmlhint'])
288

289
  var runSubset = function (subset) {
290
291
    return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset
  }
292
  var isUndefOrNonZero = function (val) {
293
294
    return val === undefined || val !== '0'
  }
295

296
  // Test task.
297
  var testSubtasks = []
298
  // Skip core tests if running a different subset of the test suite
299
  if (runSubset('core') &&
Mark Otto's avatar
Mark Otto committed
300
    // Skip core tests if this is a Savage build
301
    process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
302
    testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'qunit', 'docs'])
303
304
  }
  // Skip HTML validation if running a different subset of the test suite
305
  if (runSubset('validate-html') &&
Chris Rebert's avatar
Chris Rebert committed
306
307
      isTravis &&
      // Skip HTML5 validator when [skip validator] is in the commit message
308
      isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
309
    testSubtasks.push('validate-html')
310
  }
311
  // Only run Sauce Labs tests if there's a Sauce access key
Chris Rebert's avatar
Chris Rebert committed
312
  if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
313
      // Skip Sauce if running a different subset of the test suite
314
      runSubset('sauce-js-unit')) {
315
    testSubtasks = testSubtasks.concat(['dist', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs'])
316
317
    // Skip Sauce on Travis when [skip sauce] is in the commit message
    if (isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
318
319
      testSubtasks.push('connect')
      testSubtasks.push('saucelabs-qunit')
320
    }
321
  }
322
  grunt.registerTask('test', testSubtasks)
323

324
  // JS distribution task.
325
  grunt.registerTask('dist-js', ['babel:dev', 'concat', 'babel:dist', 'stamp', 'exec:uglify'])
326

327
  grunt.registerTask('test-scss', ['exec:scss-lint']);
Chris Rebert's avatar
Chris Rebert committed
328

329
  // CSS distribution task.
330
331
  // Supported Compilers: sass (Ruby) and libsass.
  (function (sassCompilerName) {
332
333
    require('./grunt/bs-sass-compile/' + sassCompilerName + '.js')(grunt)
  }(process.env.TWBS_SASS || 'libsass'))
334
  // grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs']);
335
  grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs'])
336

337
  grunt.registerTask('dist-css', ['sass-compile', 'exec:postcss', 'exec:clean-css', 'exec:clean-css-docs'])
Mark Otto's avatar
Mark Otto committed
338

339
  // Full distribution task.
340
  grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js'])
341

342
  // Default task.
343
  grunt.registerTask('default', ['clean:dist', 'test'])
344

345
  // Docs task.
346
347
348
349
350
  grunt.registerTask('docs-css', ['exec:clean-css-docs', 'exec:postcss-docs'])
  grunt.registerTask('lint-docs-css', ['exec:scss-lint-docs'])
  grunt.registerTask('docs-js', ['exec:uglify-docs'])
  grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs'])
  grunt.registerTask('docs-github', ['jekyll:github'])
351

352
  grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress'])
353

Mark Otto's avatar
Mark Otto committed
354
  // Publish to GitHub
355
356
  grunt.registerTask('publish', ['buildcontrol:pages'])
}