Gruntfile.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. module.exports = function(grunt) {
  2. var path = require('path');
  3. var SOURCE_DIR = 'wp/';
  4. var BUILD_DIR = 'build/';
  5. // Load tasks.
  6. require('matchdep').filterDev('grunt-*').forEach( grunt.loadNpmTasks );
  7. // Project configuration.
  8. grunt.initConfig({
  9. autoprefixer: {
  10. options: {
  11. browsers: ['Android >= 2.1', 'Chrome >= 21', 'Explorer >= 7', 'Firefox >= 17', 'Opera >= 12.1', 'Safari >= 6.0'],
  12. cascade: false
  13. },
  14. core: {
  15. expand: true,
  16. cwd: SOURCE_DIR,
  17. dest: SOURCE_DIR,
  18. src: [
  19. 'wp-admin/css/*.css',
  20. 'wp-includes/css/*.css'
  21. ]
  22. },
  23. colors: {
  24. expand: true,
  25. cwd: BUILD_DIR,
  26. dest: BUILD_DIR,
  27. src: [
  28. 'wp-admin/css/colors/*/colors.css'
  29. ]
  30. }
  31. },
  32. clean: {
  33. all: [BUILD_DIR],
  34. dynamic: {
  35. dot: true,
  36. expand: true,
  37. cwd: BUILD_DIR,
  38. src: []
  39. },
  40. tinymce: ['<%= concat.tinymce.dest %>'],
  41. qunit: ['tests/qunit/compiled.html']
  42. },
  43. copy: {
  44. files: {
  45. files: [
  46. {
  47. dot: true,
  48. expand: true,
  49. cwd: SOURCE_DIR,
  50. src: [
  51. '**',
  52. '!**/.{svn,git}/**', // Ignore version control directories.
  53. // Ignore unminified versions of external libs we don't ship:
  54. '!wp-includes/js/backbone.js',
  55. '!wp-includes/js/underscore.js',
  56. '!wp-includes/js/jquery/jquery.masonry.js',
  57. '!wp-includes/js/jquery/ui/*.js',
  58. '!wp-includes/js/tinymce/tinymce.js',
  59. '!wp-includes/version.php' // Exclude version.php
  60. ],
  61. dest: BUILD_DIR
  62. },
  63. {
  64. src: 'wp-config-sample.php',
  65. dest: BUILD_DIR
  66. }
  67. ]
  68. },
  69. 'wp-admin-rtl': {
  70. options: {
  71. processContent: function( src ) {
  72. return src.replace( /\.css/g, '-rtl.css' );
  73. }
  74. },
  75. src: SOURCE_DIR + 'wp-admin/css/wp-admin.css',
  76. dest: BUILD_DIR + 'wp-admin/css/wp-admin-rtl.css'
  77. },
  78. version: {
  79. options: {
  80. processContent: function( src ) {
  81. return src.replace( /^\$wp_version = '(.+?)';/m, function( str, version ) {
  82. version = version.replace( /-src$/, '' );
  83. // If the version includes an SVN commit (-12345), it's not a released alpha/beta. Append a date.
  84. version = version.replace( /-[\d]{5}$/, '-' + grunt.template.today( 'yyyymmdd' ) );
  85. /* jshint quotmark: true */
  86. return "$wp_version = '" + version + "';";
  87. });
  88. }
  89. },
  90. files: [
  91. {
  92. src: SOURCE_DIR + 'wp-includes/version.php',
  93. dest: BUILD_DIR + 'wp-includes/version.php'
  94. }
  95. ]
  96. },
  97. dynamic: {
  98. dot: true,
  99. expand: true,
  100. cwd: SOURCE_DIR,
  101. dest: BUILD_DIR,
  102. src: []
  103. },
  104. qunit: {
  105. src: 'tests/qunit/index.html',
  106. dest: 'tests/qunit/compiled.html',
  107. options: {
  108. processContent: function( src ) {
  109. return src.replace( /([^\.])*\.\.\/src/ig , '/../build' );
  110. }
  111. }
  112. }
  113. },
  114. sass: {
  115. colors: {
  116. expand: true,
  117. cwd: SOURCE_DIR,
  118. dest: BUILD_DIR,
  119. ext: '.css',
  120. src: ['wp-admin/css/colors/*/colors.scss'],
  121. options: {
  122. outputStyle: 'expanded'
  123. }
  124. }
  125. },
  126. cssmin: {
  127. core: {
  128. expand: true,
  129. cwd: SOURCE_DIR,
  130. dest: BUILD_DIR,
  131. ext: '.min.css',
  132. src: [
  133. 'wp-admin/css/*.css',
  134. 'wp-includes/css/*.css',
  135. // Exceptions
  136. '!wp-admin/css/farbtastic.css'
  137. ]
  138. },
  139. rtl: {
  140. expand: true,
  141. cwd: BUILD_DIR,
  142. dest: BUILD_DIR,
  143. ext: '.min.css',
  144. src: [
  145. 'wp-admin/css/*-rtl.css',
  146. 'wp-includes/css/*-rtl.css'
  147. ]
  148. },
  149. colors: {
  150. expand: true,
  151. cwd: BUILD_DIR,
  152. dest: BUILD_DIR,
  153. ext: '.min.css',
  154. src: [
  155. 'wp-admin/css/colors/*/*.css'
  156. ]
  157. }
  158. },
  159. cssjanus: {
  160. core: {
  161. options: {
  162. swapLtrRtlInUrl: false
  163. },
  164. expand: true,
  165. cwd: SOURCE_DIR,
  166. dest: BUILD_DIR,
  167. ext: '-rtl.css',
  168. src: [
  169. 'wp-admin/css/*.css',
  170. 'wp-includes/css/*.css'
  171. ]
  172. },
  173. colors: {
  174. options: {
  175. processContent: function( src ) {
  176. return src.replace( /([^/]+)\.css/gi, '$1-rtl.css' );
  177. }
  178. },
  179. expand: true,
  180. cwd: BUILD_DIR,
  181. dest: BUILD_DIR,
  182. ext: '-rtl.css',
  183. src: [
  184. 'wp-admin/css/colors/*/colors.css'
  185. ]
  186. },
  187. dynamic: {
  188. expand: true,
  189. cwd: SOURCE_DIR,
  190. dest: BUILD_DIR,
  191. ext: '-rtl.css',
  192. src: []
  193. }
  194. },
  195. jshint: {
  196. options: grunt.file.readJSON('.jshintrc'),
  197. grunt: {
  198. src: ['Gruntfile.js']
  199. },
  200. tests: {
  201. src: [
  202. 'tests/qunit/**/*.js',
  203. '!tests/qunit/vendor/qunit.js',
  204. '!tests/qunit/editor/**'
  205. ],
  206. options: grunt.file.readJSON('tests/qunit/.jshintrc')
  207. },
  208. themes: {
  209. expand: true,
  210. cwd: SOURCE_DIR + 'wp-content/themes',
  211. src: [
  212. '**/*.js',
  213. '!twenty{eleven,twelve,thirteen}/**',
  214. // Third party scripts
  215. '!twenty{fourteen,fifteen}/js/html5.js'
  216. ]
  217. },
  218. core: {
  219. expand: true,
  220. cwd: SOURCE_DIR,
  221. src: [
  222. 'wp-admin/js/*.js',
  223. 'wp-includes/js/*.js',
  224. // WordPress scripts inside directories
  225. 'wp-includes/js/jquery/jquery.table-hotkeys.js',
  226. 'wp-includes/js/mediaelement/wp-mediaelement.js',
  227. 'wp-includes/js/plupload/handlers.js',
  228. 'wp-includes/js/plupload/wp-plupload.js',
  229. 'wp-includes/js/tinymce/plugins/wordpress/plugin.js',
  230. 'wp-includes/js/tinymce/plugins/wp*/plugin.js',
  231. // Third party scripts
  232. '!wp-admin/js/farbtastic.js',
  233. '!wp-includes/js/backbone*.js',
  234. '!wp-includes/js/swfobject.js',
  235. '!wp-includes/js/underscore*.js',
  236. '!wp-includes/js/colorpicker.js',
  237. '!wp-includes/js/hoverIntent.js',
  238. '!wp-includes/js/json2.js',
  239. '!wp-includes/js/tw-sack.js',
  240. '!**/*.min.js'
  241. ],
  242. // Remove once other JSHint errors are resolved
  243. options: {
  244. curly: false,
  245. eqeqeq: false
  246. },
  247. // Limit JSHint's run to a single specified file
  248. // grunt jshint:core --file=filename.js
  249. filter: function( filepath ) {
  250. var index, file = grunt.option( 'file' );
  251. // Don't filter when no target file is specified
  252. if ( ! file ) {
  253. return true;
  254. }
  255. // Normalize filepath for Windows
  256. filepath = filepath.replace( /\\/g, '/' );
  257. index = filepath.lastIndexOf( '/' + file );
  258. // Match only the filename passed from cli
  259. if ( filepath === file || ( -1 !== index && index === filepath.length - ( file.length + 1 ) ) ) {
  260. return true;
  261. }
  262. return false;
  263. }
  264. },
  265. plugins: {
  266. expand: true,
  267. cwd: SOURCE_DIR + 'wp-content/plugins',
  268. src: [
  269. '**/*.js',
  270. '!**/*.min.js'
  271. ],
  272. // Limit JSHint's run to a single specified plugin directory:
  273. //
  274. // grunt jshint:plugins --dir=foldername
  275. //
  276. filter: function( dirpath ) {
  277. var index, dir = grunt.option( 'dir' );
  278. // Don't filter when no target folder is specified
  279. if ( ! dir ) {
  280. return true;
  281. }
  282. dirpath = dirpath.replace( /\\/g, '/' );
  283. index = dirpath.lastIndexOf( '/' + dir );
  284. // Match only the folder name passed from cli
  285. if ( -1 !== index ) {
  286. return true;
  287. }
  288. return false;
  289. }
  290. }
  291. },
  292. qunit: {
  293. files: [
  294. 'tests/qunit/**/*.html',
  295. '!tests/qunit/editor/**'
  296. ]
  297. },
  298. phpunit: {
  299. 'default': {
  300. cmd: 'phpunit',
  301. args: ['-c', 'phpunit.xml.dist']
  302. },
  303. ajax: {
  304. cmd: 'phpunit',
  305. args: ['-c', 'phpunit.xml.dist', '--group', 'ajax']
  306. },
  307. multisite: {
  308. cmd: 'phpunit',
  309. args: ['-c', 'tests/phpunit/multisite.xml']
  310. }
  311. },
  312. uglify: {
  313. core: {
  314. expand: true,
  315. cwd: SOURCE_DIR,
  316. dest: BUILD_DIR,
  317. ext: '.min.js',
  318. src: [
  319. 'wp-admin/js/*.js',
  320. 'wp-includes/js/*.js',
  321. 'wp-includes/js/plupload/handlers.js',
  322. 'wp-includes/js/plupload/wp-plupload.js',
  323. 'wp-includes/js/tinymce/plugins/wordpress/plugin.js',
  324. 'wp-includes/js/tinymce/plugins/wp*/plugin.js',
  325. // Exceptions
  326. '!wp-admin/js/custom-header.js', // Why? We should minify this.
  327. '!wp-admin/js/farbtastic.js',
  328. '!wp-admin/js/iris.min.js',
  329. '!wp-includes/js/backbone.min.js',
  330. '!wp-includes/js/swfobject.js',
  331. '!wp-includes/js/underscore.min.js',
  332. '!wp-includes/js/zxcvbn.min.js'
  333. ]
  334. }
  335. },
  336. concat: {
  337. tinymce: {
  338. options: {
  339. separator: '\n',
  340. process: function( src, filepath ) {
  341. return '// Source: ' + filepath.replace( BUILD_DIR, '' ) + '\n' + src;
  342. }
  343. },
  344. src: [
  345. BUILD_DIR + 'wp-includes/js/tinymce/tinymce.min.js',
  346. BUILD_DIR + 'wp-includes/js/tinymce/themes/modern/theme.min.js',
  347. BUILD_DIR + 'wp-includes/js/tinymce/plugins/*/plugin.min.js'
  348. ],
  349. dest: BUILD_DIR + 'wp-includes/js/tinymce/wp-tinymce.js'
  350. }
  351. },
  352. compress: {
  353. tinymce: {
  354. options: {
  355. mode: 'gzip',
  356. level: 9
  357. },
  358. src: '<%= concat.tinymce.dest %>',
  359. dest: BUILD_DIR + 'wp-includes/js/tinymce/wp-tinymce.js.gz'
  360. }
  361. },
  362. jsvalidate:{
  363. options: {
  364. globals: {},
  365. esprimaOptions:{},
  366. verbose: false
  367. },
  368. build: {
  369. files: {
  370. src: [
  371. BUILD_DIR + 'wp-{admin,includes}/**/*.js',
  372. BUILD_DIR + 'wp-content/themes/twenty*/**/*.js'
  373. ]
  374. }
  375. }
  376. },
  377. imagemin: {
  378. core: {
  379. expand: true,
  380. cwd: SOURCE_DIR,
  381. src: [
  382. 'wp-{admin,includes}/images/**/*.{png,jpg,gif,jpeg}',
  383. 'wp-includes/js/tinymce/skins/wordpress/images/*.{png,jpg,gif,jpeg}'
  384. ],
  385. dest: SOURCE_DIR
  386. }
  387. },
  388. watch: {
  389. all: {
  390. files: [
  391. SOURCE_DIR + '**',
  392. // Ignore version control directories.
  393. '!' + SOURCE_DIR + '**/.{svn,git}/**'
  394. ],
  395. tasks: ['clean:dynamic', 'copy:dynamic'],
  396. options: {
  397. dot: true,
  398. spawn: false,
  399. interval: 2000
  400. }
  401. },
  402. config: {
  403. files: 'Gruntfile.js'
  404. },
  405. colors: {
  406. files: [SOURCE_DIR + 'wp-admin/css/colors/**'],
  407. tasks: ['sass:colors']
  408. },
  409. rtl: {
  410. files: [
  411. SOURCE_DIR + 'wp-admin/css/*.css',
  412. SOURCE_DIR + 'wp-includes/css/*.css'
  413. ],
  414. tasks: ['cssjanus:dynamic'],
  415. options: {
  416. spawn: false,
  417. interval: 2000
  418. }
  419. },
  420. test: {
  421. files: [
  422. 'tests/qunit/**',
  423. '!tests/qunit/editor/**'
  424. ],
  425. tasks: ['qunit']
  426. }
  427. }
  428. });
  429. // Register tasks.
  430. // Copy task.
  431. grunt.registerTask('copy:all', ['copy:files', 'copy:version']);
  432. // RTL task.
  433. grunt.registerTask('rtl', ['cssjanus:core', 'cssjanus:colors']);
  434. // Color schemes task.
  435. grunt.registerTask('colors', ['sass:colors', 'autoprefixer:colors']);
  436. // Pre-commit task.
  437. grunt.registerTask('precommit', 'Runs front-end dev/test tasks in preparation for a commit.',
  438. ['autoprefixer:core', 'imagemin:core', 'jshint', 'qunit:compiled']);
  439. // Build task.
  440. grunt.registerTask('build', ['clean:all', 'copy:all', 'cssmin:core', 'colors', 'rtl', 'cssmin:rtl', 'cssmin:colors',
  441. 'uglify:core', 'concat:tinymce', 'compress:tinymce', 'clean:tinymce', 'jsvalidate:build']);
  442. // Testing tasks.
  443. grunt.registerMultiTask('phpunit', 'Runs PHPUnit tests, including the ajax and multisite tests.', function() {
  444. grunt.util.spawn({
  445. cmd: this.data.cmd,
  446. args: this.data.args,
  447. opts: {stdio: 'inherit'}
  448. }, this.async());
  449. });
  450. grunt.registerTask('qunit:compiled', 'Runs QUnit tests on compiled as well as uncompiled scripts.',
  451. ['build', 'copy:qunit', 'qunit']);
  452. grunt.registerTask('test', 'Runs all QUnit and PHPUnit tasks.', ['qunit:compiled', 'phpunit']);
  453. // Default task.
  454. grunt.registerTask('default', ['build']);
  455. // Add a listener to the watch task.
  456. //
  457. // On `watch:all`, automatically updates the `copy:dynamic` and `clean:dynamic`
  458. // configurations so that only the changed files are updated.
  459. // On `watch:rtl`, automatically updates the `cssjanus:dynamic` configuration.
  460. grunt.event.on('watch', function( action, filepath, target ) {
  461. if ( target !== 'all' && target !== 'rtl' ) {
  462. return;
  463. }
  464. var relativePath = path.relative( SOURCE_DIR, filepath ),
  465. cleanSrc = ( action === 'deleted' ) ? [relativePath] : [],
  466. copySrc = ( action === 'deleted' ) ? [] : [relativePath];
  467. grunt.config(['clean', 'dynamic', 'src'], cleanSrc);
  468. grunt.config(['copy', 'dynamic', 'src'], copySrc);
  469. grunt.config(['cssjanus', 'dynamic', 'src'], copySrc);
  470. });
  471. };