Gruntfile.js 10 KB

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