Gruntfile.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /**
  2. * Gruntfile for compiling theme_bootstrap .less files.
  3. *
  4. * This file configures tasks to be run by Grunt
  5. * http://gruntjs.com/ for the current theme.
  6. *
  7. *
  8. * Requirements:
  9. * -------------
  10. * nodejs, npm, grunt-cli.
  11. *
  12. * Installation:
  13. * -------------
  14. * node and npm: instructions at http://nodejs.org/
  15. *
  16. * grunt-cli: `[sudo] npm install -g grunt-cli`
  17. *
  18. * node dependencies: run `npm install` in the root directory.
  19. *
  20. *
  21. * Usage:
  22. * ------
  23. * Call tasks from the theme root directory. Default behaviour
  24. * (calling only `grunt`) is to run the watch task detailed below.
  25. *
  26. *
  27. * Porcelain tasks:
  28. * ----------------
  29. * The nice user interface intended for everyday use. Provide a
  30. * high level of automation and convenience for specific use-cases.
  31. *
  32. * grunt watch Watch the less directory (and all subdirectories)
  33. * for changes to *.less files then on detection
  34. * run 'grunt compile'
  35. *
  36. * Options:
  37. *
  38. * --dirroot=<path> Optional. Explicitly define the
  39. * path to your Moodle root directory
  40. * when your theme is not in the
  41. * standard location.
  42. * grunt compile Run the .less files through the compiler, create the
  43. * RTL version of the output, then run decache so that
  44. * the results can be seen on the next page load.
  45. *
  46. * Options:
  47. *
  48. * --dirroot=<path> Optional. Explicitly define the
  49. * path to your Moodle root directory
  50. * when your theme is not in the
  51. * standard location.
  52. *
  53. * grunt swatch Task for working with bootswatch files. Expects a
  54. * convention to be followed - bootswatch files are
  55. * contained within a directory providing the name
  56. * by which the swatch is identified. By default the
  57. * directory these should be placed in is less/bootswatch
  58. * however the user may optionally override this.
  59. * e.g. swatch files contained within a directory
  60. * located at less/bootswatch/squib will be associated
  61. * with the swatch name 'squib'.
  62. *
  63. * Switches the current bootswatch files compiled with
  64. * the theme to those of a given bootswatch, recompiles
  65. * less and clears the theme cache.
  66. *
  67. * Options:
  68. *
  69. * --name=<swatchname> Required. Name (as defined by
  70. * the convention) of the swatch
  71. * to activate.
  72. *
  73. * --swatches-dir=<path> Optional. Explicitly define
  74. * the path to the directory
  75. * containing your bootswatches
  76. * (default is less/bootswatch).
  77. *
  78. * --vars-only Optional. Copy the swatch's
  79. * variables.less file only
  80. * and empty custom-bootswatch.less
  81. * Due to issues with grunt's
  82. * handling of boolean options
  83. * if not explicitly set e.g.
  84. * `--vars-only=true` this option
  85. * should be passed last.
  86. *
  87. *
  88. *
  89. * Plumbing tasks & targets:
  90. * -------------------------
  91. * Lower level tasks encapsulating a specific piece of functionality
  92. * but usually only useful when called in combination with another.
  93. *
  94. * grunt less Compile all less files.
  95. *
  96. * grunt less:moodle Compile Moodle less files only.
  97. *
  98. * grunt less:editor Compile editor less files only.
  99. *
  100. * grunt decache Clears the Moodle theme cache.
  101. *
  102. * Options:
  103. *
  104. * --dirroot=<path> Optional. Explicitly define
  105. * the path to your Moodle root
  106. * directory when your theme is
  107. * not in the standard location.
  108. *
  109. * grunt bootswatch Switch the theme less/bootswatch/custom-bootswatch.less
  110. * and less/bootswatch/custom-variables.less files for
  111. * those of a given bootswatch theme using convention
  112. * described in swatch task.
  113. *
  114. * Options:
  115. *
  116. * --name=<swatchname> Required. Name (as defined by
  117. * the convention) of the swatch
  118. * to activate.
  119. *
  120. * --swatches-dir=<path> Optional. Explicitly define
  121. * the path to the directory
  122. * containing your bootswatches
  123. * (default is less/bootswatch).
  124. *
  125. * --vars-only Optional. Copy the swatch's
  126. * variables.less file only
  127. * and empty custom-bootswatch.less
  128. * Due to issues with grunt's
  129. * handling of boolean options
  130. * if not explicitly set e.g.
  131. * `--vars-only=true` this option
  132. * should be passed last.
  133. *
  134. * --none Optional. Reset bootswatch to
  135. * plain Bootstrap (no swatch).
  136. *
  137. * grunt replace Run all text replace tasks.
  138. *
  139. * grunt replace:rtl_images Add _rtl to the filenames of certain images
  140. * that require flipping for use with RTL
  141. * languages.
  142. *
  143. * grunt replace:font_fix Correct the format for the Moodle font
  144. * loader to pick up the Glyphicon font.
  145. *
  146. * grunt replace:svg_colors Change the color of the SVGs in pix_core by
  147. * text replacing #999 with a new hex color.
  148. * Note this requires the SVGs to be #999 to
  149. * start with or the replace will do nothing
  150. * so should usually be preceded by copying
  151. * a fresh set of the original SVGs.
  152. *
  153. * Options:
  154. *
  155. * --svgcolor=<hexcolor> Hex color to use for SVGs
  156. *
  157. * grunt cssflip Create moodle-rtl.css by flipping the direction styles
  158. * in moodle.css.
  159. *
  160. *
  161. * @package theme
  162. * @subpackage bootstrap
  163. * @author Joby Harding www.iamjoby.com
  164. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  165. */
  166. module.exports = function(grunt) {
  167. // Import modules.
  168. var path = require('path');
  169. // Theme Bootstrap constants.
  170. var LESSDIR = 'less',
  171. BOOTSWATCHDIR = path.join(LESSDIR, 'bootswatch'),
  172. BOOTSWATCHFILE = path.join(BOOTSWATCHDIR, 'custom-bootswatch.less'),
  173. BOOTSWATCHVARS = path.join(BOOTSWATCHDIR, 'custom-variables.less'),
  174. THEMEDIR = path.basename(path.resolve('.'));
  175. // PHP strings for exec task.
  176. var moodleroot = path.dirname(path.dirname(__dirname)),
  177. configfile = '',
  178. decachephp = '',
  179. dirrootopt = grunt.option('dirroot') || process.env.MOODLE_DIR || '';
  180. // Allow user to explicitly define Moodle root dir.
  181. if ('' !== dirrootopt) {
  182. moodleroot = path.resolve(dirrootopt);
  183. }
  184. configfile = path.join(moodleroot, 'config.php');
  185. decachephp += 'define(\'CLI_SCRIPT\', true);';
  186. decachephp += 'require(\'' + configfile + '\');';
  187. decachephp += 'theme_reset_all_caches();';
  188. var swatchname = grunt.option('name') || '';
  189. var defaultsvgcolor = {
  190. amelia: '#e8d069',
  191. bootstrap: '#428bca',
  192. classic: '#428bca',
  193. cerulean: '#2fa4e7',
  194. classic: '#428bca',
  195. cosmo: '#007fff',
  196. cupid: '#56caef',
  197. cyborg: '#2a9fd6',
  198. darkly: '#0ce3ac',
  199. flatly: '#18bc9c',
  200. journal: '#eb6864',
  201. lumen: '#158cba',
  202. readable: '#4582ec',
  203. shamrock: '#f8e33c',
  204. simplex: '#d9230f',
  205. slate: '#fff',
  206. spacelab: '#446e9b',
  207. superhero: '#df691a',
  208. united: '#dd4814',
  209. yeti: '#008cba',
  210. };
  211. var svgcolor = grunt.option('svgcolor') || defaultsvgcolor[swatchname] || '#999';
  212. grunt.initConfig({
  213. less: {
  214. // Compile moodle styles.
  215. moodle: {
  216. options: {
  217. compress: false,
  218. sourceMap: true,
  219. sourceMapRootpath: '/theme/' + THEMEDIR,
  220. sourceMapFilename: 'sourcemap-moodle.json'
  221. },
  222. src: 'less/moodle.less',
  223. dest: 'style/moodle.css'
  224. },
  225. // Compile editor styles.
  226. editor: {
  227. options: {
  228. compress: false,
  229. sourceMap: true,
  230. sourceMapRootpath: '/theme/' + THEMEDIR,
  231. sourceMapFilename: 'sourcemap-editor.json'
  232. },
  233. src: 'less/editor.less',
  234. dest: 'style/editor.css'
  235. }
  236. },
  237. exec: {
  238. decache: {
  239. cmd: 'php -r "' + decachephp + '"',
  240. callback: function(error, stdout, stderror) {
  241. // exec will output error messages
  242. // just add one to confirm success.
  243. if (!error) {
  244. grunt.log.writeln("Moodle theme cache reset.");
  245. }
  246. }
  247. }
  248. },
  249. watch: {
  250. // Watch for any changes to less files and compile.
  251. files: ["less/**/*.less"],
  252. tasks: ["compile"],
  253. options: {
  254. spawn: false
  255. }
  256. },
  257. cssflip: {
  258. rtl: {
  259. src: 'style/moodle.css',
  260. dest: 'style/moodle-rtl.css'
  261. }
  262. },
  263. copy: {
  264. svg: {
  265. expand: true,
  266. cwd: 'pix_core_originals/',
  267. src: '**',
  268. dest: 'pix_core/',
  269. }
  270. },
  271. replace: {
  272. rtl_images: {
  273. src: 'style/moodle-rtl.css',
  274. overwrite: true,
  275. replacements: [{
  276. from: '[[pix:theme|fp/path_folder]]',
  277. to: '[[pix:theme|fp/path_folder_rtl]]'
  278. }, {
  279. from: '[[pix:t/collapsed]]',
  280. to: '[[pix:t/collapsed_rtl]]'
  281. }, {
  282. from: '[[pix:t/collapsed_empty]]',
  283. to: '[[pix:t/collapsed_empty_rtl]]'
  284. }, {
  285. from: '[[pix:y/tn]]',
  286. to: '[[pix:y/tn_rtl]]'
  287. }, {
  288. from: '[[pix:y/tp]]',
  289. to: '[[pix:y/tp_rtl]]'
  290. }, {
  291. from: '[[pix:y/ln]]',
  292. to: '[[pix:y/ln_rtl]]'
  293. }, {
  294. from: '[[pix:y/lp]]',
  295. to: '[[pix:y/lp_rtl]]'
  296. }]
  297. },
  298. svg_colors: {
  299. src: 'pix_core/**/*.svg',
  300. overwrite: true,
  301. replacements: [{
  302. from: '#999',
  303. to: svgcolor
  304. }]
  305. },
  306. font_fix: {
  307. src: 'style/moodle.css',
  308. overwrite: true,
  309. replacements: [{
  310. from: 'glyphicons-halflings-regular.eot',
  311. to: 'glyphicons-halflings-regular.eot]]',
  312. }, {
  313. from: 'glyphicons-halflings-regular.svg',
  314. to: 'glyphicons-halflings-regular.svg]]',
  315. }, {
  316. from: 'glyphicons-halflings-regular.ttf',
  317. to: 'glyphicons-halflings-regular.ttf]]',
  318. }, {
  319. from: 'glyphicons-halflings-regular.woff',
  320. to: 'glyphicons-halflings-regular.woff]]',
  321. }]
  322. }
  323. }
  324. });
  325. // Local task functions.
  326. var _bootswatch = function() {
  327. var swatchname = grunt.option('name') || '',
  328. swatchroot = grunt.option('swatches-dir') || '',
  329. varsonly = grunt.option('vars-only'),
  330. noswatch = grunt.option('none');
  331. // Reset bootwatches for default boootstrap.
  332. if (noswatch) {
  333. grunt.file.write(BOOTSWATCHFILE, '');
  334. grunt.file.write(BOOTSWATCHVARS, '');
  335. grunt.log.writeln('Cleared bootswatch.');
  336. return;
  337. }
  338. // Required option.
  339. if ('' === swatchname) {
  340. grunt.fail.fatal('You must provide a swatch name.');
  341. }
  342. var swatchpath = path.join(BOOTSWATCHDIR, swatchname);
  343. // Allow user to explicitly define source swatches dir.
  344. if ('' !== swatchroot) {
  345. swatchpath = path.resolve(swatchroot);
  346. swatchpath = path.join(swatchpath, swatchname);
  347. }
  348. var swatchless = path.join(swatchpath, 'bootswatch.less'),
  349. varsless = path.join(swatchpath, 'variables.less'),
  350. message = '';
  351. // Ensure the swatch directory exists.
  352. if (!grunt.file.isDir(swatchpath)) {
  353. message = "The swatch directory '" + swatchpath + "' ";
  354. message += 'does not exist or is not accessible.';
  355. grunt.fail.fatal(message);
  356. }
  357. // Ensure the bootswatch.less file exists.
  358. if (!varsonly) {
  359. if (!grunt.file.isFile(swatchless)) {
  360. message = "The required file '" + swatchless + "' ";
  361. message += 'does not exist or is not accessible.';
  362. grunt.fail.fatal(message);
  363. }
  364. } else {
  365. grunt.file.write(BOOTSWATCHFILE, '');
  366. }
  367. // Ensure the variables.less file exists.
  368. if (!grunt.file.isFile(varsless)) {
  369. message = "The required file '" + varsless + "' ";
  370. message += 'does not exist or is not accessible.';
  371. grunt.fail.fatal(message);
  372. }
  373. // Copy in new swatch files.
  374. if (!varsonly) {
  375. grunt.file.copy(swatchless, BOOTSWATCHFILE);
  376. }
  377. grunt.file.copy(varsless, BOOTSWATCHVARS);
  378. grunt.log.writeln('Swatch copied.');
  379. };
  380. // Load contrib tasks.
  381. grunt.loadNpmTasks("grunt-contrib-less");
  382. grunt.loadNpmTasks("grunt-contrib-watch");
  383. grunt.loadNpmTasks("grunt-exec");
  384. grunt.loadNpmTasks("grunt-text-replace");
  385. grunt.loadNpmTasks("grunt-css-flip");
  386. grunt.loadNpmTasks('grunt-contrib-copy');
  387. // Register tasks.
  388. grunt.registerTask("default", ["watch"]);
  389. grunt.registerTask("decache", ["exec:decache"]);
  390. grunt.registerTask("bootswatch", _bootswatch);
  391. grunt.registerTask("compile", ["less", "replace:font_fix", "cssflip", "replace:rtl_images", "decache"]);
  392. grunt.registerTask("swatch", ["bootswatch", "svg", "compile"]);
  393. grunt.registerTask("svg", ["copy:svg", "replace:svg_colors"]);
  394. };