Guest User

Gruntfile.js - Aug 25, 2014

a guest
Aug 25th, 2014
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.11 KB | None | 0 0
  1. /*!
  2. * Bootstrap's Gruntfile
  3. * http://getbootstrap.com
  4. * Copyright 2013-2014 Twitter, Inc.
  5. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  6. */
  7.  
  8. module.exports = function (grunt) {
  9. 'use strict';
  10.  
  11. // Force use of Unix newlines
  12. grunt.util.linefeed = '\n';
  13.  
  14. RegExp.quote = function (string) {
  15. return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  16. };
  17.  
  18. var fs = require('fs');
  19. var path = require('path');
  20. var npmShrinkwrap = require('npm-shrinkwrap');
  21. var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
  22. var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
  23. var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
  24.  
  25. // Project configuration.
  26. grunt.initConfig({
  27.  
  28. // Metadata.
  29. pkg: grunt.file.readJSON('package.json'),
  30. banner: '/*!\n' +
  31. ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
  32. ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
  33. ' * Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' +
  34. ' */\n',
  35. // NOTE: This jqueryCheck code is duplicated in customizer.js; if making changes here, be sure to update the other copy too.
  36. jqueryCheck: 'if (typeof jQuery === \'undefined\') { throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery\') }\n\n',
  37.  
  38. // Task configuration.
  39. clean: {
  40. dist: ['dist', 'docs/dist']
  41. },
  42.  
  43. jshint: {
  44. options: {
  45. jshintrc: 'js/.jshintrc'
  46. },
  47. grunt: {
  48. options: {
  49. jshintrc: 'grunt/.jshintrc'
  50. },
  51. src: ['Gruntfile.js', 'grunt/*.js']
  52. },
  53. src: {
  54. src: 'js/*.js'
  55. },
  56. test: {
  57. options: {
  58. jshintrc: 'js/tests/unit/.jshintrc'
  59. },
  60. src: 'js/tests/unit/*.js'
  61. },
  62. assets: {
  63. src: ['docs/assets/js/_src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
  64. }
  65. },
  66.  
  67. jscs: {
  68. options: {
  69. config: 'js/.jscsrc'
  70. },
  71. grunt: {
  72. src: '<%= jshint.grunt.src %>'
  73. },
  74. src: {
  75. src: '<%= jshint.src.src %>'
  76. },
  77. test: {
  78. src: '<%= jshint.test.src %>'
  79. },
  80. assets: {
  81. options: {
  82. requireCamelCaseOrUpperCaseIdentifiers: null
  83. },
  84. src: '<%= jshint.assets.src %>'
  85. }
  86. },
  87.  
  88. concat: {
  89. options: {
  90. banner: '<%= banner %>\n<%= jqueryCheck %>',
  91. stripBanners: false
  92. },
  93. bootstrap: {
  94. src: [
  95. 'js/transition.js',
  96. 'js/alert.js',
  97. 'js/button.js',
  98. 'js/carousel.js',
  99. 'js/collapse.js',
  100. 'js/dropdown.js',
  101. 'js/modal.js',
  102. 'js/tooltip.js',
  103. 'js/popover.js',
  104. 'js/scrollspy.js',
  105. 'js/tab.js',
  106. 'js/affix.js'
  107. ],
  108. dest: 'dist/js/<%= pkg.name %>.js'
  109. }
  110. },
  111.  
  112. uglify: {
  113. options: {
  114. preserveComments: 'some'
  115. },
  116. bootstrap: {
  117. src: '<%= concat.bootstrap.dest %>',
  118. dest: 'dist/js/<%= pkg.name %>.min.js'
  119. },
  120. customize: {
  121. src: [
  122. 'docs/assets/js/_vendor/less.min.js',
  123. 'docs/assets/js/_vendor/jszip.min.js',
  124. 'docs/assets/js/_vendor/uglify.min.js',
  125. 'docs/assets/js/_vendor/blob.js',
  126. 'docs/assets/js/_vendor/filesaver.js',
  127. 'docs/assets/js/raw-files.min.js',
  128. 'docs/assets/js/_src/customizer.js'
  129. ],
  130. dest: 'docs/assets/js/customize.min.js'
  131. },
  132. docsJs: {
  133. src: [
  134. 'docs/assets/js/_vendor/holder.js',
  135. 'docs/assets/js/_vendor/ZeroClipboard.min.js',
  136. 'docs/assets/js/_src/application.js'
  137. ],
  138. dest: 'docs/assets/js/docs.min.js'
  139. }
  140. },
  141.  
  142. qunit: {
  143. options: {
  144. inject: 'js/tests/unit/phantom.js'
  145. },
  146. files: 'js/tests/index.html'
  147. },
  148.  
  149. less: {
  150. compileCore: {
  151. options: {
  152. strictMath: true,
  153. sourceMap: true,
  154. outputSourceFiles: true,
  155. sourceMapURL: '<%= pkg.name %>.css.map',
  156. sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
  157. },
  158. files: {
  159. 'dist/css/<%= pkg.name %>.css': 'less/bootstrap.less'
  160. }
  161. },
  162. compileTheme: {
  163. options: {
  164. strictMath: true,
  165. sourceMap: true,
  166. outputSourceFiles: true,
  167. sourceMapURL: '<%= pkg.name %>-theme.css.map',
  168. sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
  169. },
  170. files: {
  171. 'dist/css/<%= pkg.name %>-theme.css': 'less/theme.less'
  172. }
  173. }
  174. },
  175.  
  176. autoprefixer: {
  177. options: {
  178. browsers: [
  179. 'Android 2.3',
  180. 'Android >= 4',
  181. 'Chrome >= 20',
  182. 'Firefox >= 24', // Firefox 24 is the latest ESR
  183. 'Explorer >= 8',
  184. 'iOS >= 6',
  185. 'Opera >= 12',
  186. 'Safari >= 6'
  187. ]
  188. },
  189. core: {
  190. options: {
  191. map: true
  192. },
  193. src: 'dist/css/<%= pkg.name %>.css'
  194. },
  195. theme: {
  196. options: {
  197. map: true
  198. },
  199. src: 'dist/css/<%= pkg.name %>-theme.css'
  200. },
  201. docs: {
  202. src: 'docs/assets/css/_src/docs.css'
  203. },
  204. examples: {
  205. expand: true,
  206. cwd: 'docs/examples/',
  207. src: ['**/*.css'],
  208. dest: 'docs/examples/'
  209. }
  210. },
  211.  
  212. csslint: {
  213. options: {
  214. csslintrc: 'less/.csslintrc'
  215. },
  216. src: [
  217. 'dist/css/bootstrap.css',
  218. 'dist/css/bootstrap-theme.css'
  219. ],
  220. examples: [
  221. 'docs/examples/**/*.css'
  222. ],
  223. docs: {
  224. options: {
  225. ids: false,
  226. 'overqualified-elements': false
  227. },
  228. src: 'docs/assets/css/_src/docs.css'
  229. }
  230. },
  231.  
  232. cssmin: {
  233. options: {
  234. compatibility: 'ie8',
  235. keepSpecialComments: '*',
  236. noAdvanced: true
  237. },
  238. core: {
  239. files: {
  240. 'dist/css/<%= pkg.name %>.min.css': 'dist/css/<%= pkg.name %>.css',
  241. 'dist/css/<%= pkg.name %>-theme.min.css': 'dist/css/<%= pkg.name %>-theme.css'
  242. }
  243. },
  244. docs: {
  245. src: [
  246. 'docs/assets/css/_src/docs.css',
  247. 'docs/assets/css/_src/pygments-manni.css'
  248. ],
  249. dest: 'docs/assets/css/docs.min.css'
  250. }
  251. },
  252.  
  253. usebanner: {
  254. options: {
  255. position: 'top',
  256. banner: '<%= banner %>'
  257. },
  258. files: {
  259. src: 'dist/css/*.css'
  260. }
  261. },
  262.  
  263. csscomb: {
  264. options: {
  265. config: 'less/.csscomb.json'
  266. },
  267. dist: {
  268. expand: true,
  269. cwd: 'dist/css/',
  270. src: ['*.css', '!*.min.css'],
  271. dest: 'dist/css/'
  272. },
  273. examples: {
  274. expand: true,
  275. cwd: 'docs/examples/',
  276. src: '**/*.css',
  277. dest: 'docs/examples/'
  278. },
  279. docs: {
  280. files: {
  281. 'docs/assets/css/_src/docs.css': 'docs/assets/css/_src/docs.css'
  282. }
  283. }
  284. },
  285.  
  286. copy: {
  287. fonts: {
  288. expand: true,
  289. src: 'fonts/*',
  290. dest: 'dist/'
  291. },
  292. docs: {
  293. expand: true,
  294. cwd: './dist',
  295. src: [
  296. '{css,js}/*.min.*',
  297. 'css/*.map',
  298. 'fonts/*'
  299. ],
  300. dest: 'docs/dist'
  301. }
  302. },
  303.  
  304. connect: {
  305. server: {
  306. options: {
  307. port: 3000,
  308. base: '.'
  309. }
  310. }
  311. },
  312.  
  313. jekyll: {
  314. docs: {}
  315. },
  316.  
  317. jade: {
  318. compile: {
  319. options: {
  320. pretty: true,
  321. data: function () {
  322. var filePath = path.join(__dirname, 'less/variables.less');
  323. var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
  324. var parser = new BsLessdocParser(fileContent);
  325. return { sections: parser.parseFile() };
  326. }
  327. },
  328. files: {
  329. 'docs/_includes/customizer-variables.html': 'docs/_jade/customizer-variables.jade',
  330. 'docs/_includes/nav/customize.html': 'docs/_jade/customizer-nav.jade'
  331. }
  332. }
  333. },
  334.  
  335. validation: {
  336. options: {
  337. charset: 'utf-8',
  338. doctype: 'HTML5',
  339. failHard: true,
  340. reset: true,
  341. relaxerror: [
  342. 'Bad value X-UA-Compatible for attribute http-equiv on element meta.',
  343. 'Element img is missing required attribute src.'
  344. ]
  345. },
  346. files: {
  347. src: '_gh_pages/**/*.html'
  348. }
  349. },
  350.  
  351. watch: {
  352. src: {
  353. files: '<%= jshint.src.src %>',
  354. tasks: ['jshint:src', 'qunit']
  355. },
  356. test: {
  357. files: '<%= jshint.test.src %>',
  358. tasks: ['jshint:test', 'qunit']
  359. },
  360. less: {
  361. files: 'less/*.less',
  362. tasks: 'less'
  363. }
  364. },
  365.  
  366. sed: {
  367. versionNumber: {
  368. pattern: (function () {
  369. var old = grunt.option('oldver');
  370. return old ? RegExp.quote(old) : old;
  371. })(),
  372. replacement: grunt.option('newver'),
  373. recursive: true
  374. }
  375. },
  376.  
  377. 'saucelabs-qunit': {
  378. all: {
  379. options: {
  380. build: process.env.TRAVIS_JOB_ID,
  381. concurrency: 10,
  382. maxRetries: 3,
  383. urls: ['http://127.0.0.1:3000/js/tests/index.html'],
  384. browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
  385. }
  386. }
  387. },
  388.  
  389. exec: {
  390. npmUpdate: {
  391. command: 'npm update'
  392. }
  393. }
  394. });
  395.  
  396.  
  397. // These plugins provide necessary tasks.
  398. require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
  399. require('time-grunt')(grunt);
  400.  
  401. // Docs HTML validation task
  402. grunt.registerTask('validate-html', ['jekyll', 'validation']);
  403.  
  404. var runSubset = function (subset) {
  405. return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
  406. };
  407. var isUndefOrNonZero = function (val) {
  408. return val === undefined || val !== '0';
  409. };
  410.  
  411. // Test task.
  412. var testSubtasks = [];
  413. // Skip core tests if running a different subset of the test suite
  414. if (runSubset('core')) {
  415. testSubtasks = testSubtasks.concat(['dist-css', 'csslint', 'jshint', 'jscs', 'qunit', 'build-customizer-html']);
  416. }
  417. // Skip HTML validation if running a different subset of the test suite
  418. if (runSubset('validate-html') &&
  419. // Skip HTML5 validator on Travis when [skip validator] is in the commit message
  420. isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
  421. testSubtasks.push('validate-html');
  422. }
  423. // Only run Sauce Labs tests if there's a Sauce access key
  424. if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
  425. // Skip Sauce if running a different subset of the test suite
  426. runSubset('sauce-js-unit') &&
  427. // Skip Sauce on Travis when [skip sauce] is in the commit message
  428. isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
  429. testSubtasks.push('connect');
  430. testSubtasks.push('saucelabs-qunit');
  431. }
  432. grunt.registerTask('test', testSubtasks);
  433.  
  434. // JS distribution task.
  435. grunt.registerTask('dist-js', ['concat', 'uglify']);
  436.  
  437. // CSS distribution task.
  438. grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
  439. grunt.registerTask('dist-css', ['less-compile', 'autoprefixer', 'usebanner', 'csscomb', 'cssmin']);
  440.  
  441. // Docs distribution task.
  442. grunt.registerTask('dist-docs', 'copy:docs');
  443.  
  444. // Full distribution task.
  445. grunt.registerTask('dist', ['clean', 'dist-css', 'copy:fonts', 'dist-js', 'dist-docs']);
  446.  
  447. // Default task.
  448. grunt.registerTask('default', ['test', 'dist', 'build-glyphicons-data', 'build-customizer']);
  449.  
  450. // Version numbering task.
  451. // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
  452. // This can be overzealous, so its changes should always be manually reviewed!
  453. grunt.registerTask('change-version-number', 'sed');
  454.  
  455. grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });
  456.  
  457. // task for building customizer
  458. grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
  459. grunt.registerTask('build-customizer-html', 'jade');
  460. grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
  461. var banner = grunt.template.process('<%= banner %>');
  462. generateRawFiles(grunt, banner);
  463. });
  464.  
  465. // Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
  466. // This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
  467. grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
  468. grunt.registerTask('_update-shrinkwrap', function () {
  469. var done = this.async();
  470. npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
  471. if (err) {
  472. grunt.fail.warn(err)
  473. }
  474. var dest = 'test-infra/npm-shrinkwrap.json';
  475. fs.renameSync('npm-shrinkwrap.json', dest);
  476. grunt.log.writeln('File ' + dest.cyan + ' updated.');
  477. done();
  478. });
  479. });
  480. };
Advertisement
Add Comment
Please, Sign In to add comment