No Description

sass.js 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /*
  2. * grunt-contrib-sass
  3. * http://gruntjs.com/
  4. *
  5. * Copyright (c) 2013 Sindre Sorhus, contributors
  6. * Licensed under the MIT license.
  7. */
  8. 'use strict';
  9. var path = require('path');
  10. var dargs = require('dargs');
  11. var numCPUs = require('os').cpus().length || 1;
  12. var async = require('async');
  13. var chalk = require('chalk');
  14. var spawn = require('win-spawn');
  15. var which = require('which');
  16. module.exports = function (grunt) {
  17. var bannerCallback = function (filename, banner) {
  18. grunt.verbose.writeln('Writing CSS banner for ' + filename);
  19. grunt.file.write(filename, banner + grunt.util.linefeed + grunt.file.read(filename));
  20. };
  21. var checkBinary = function (cmd, errMess) {
  22. try {
  23. which.sync(cmd);
  24. } catch (err) {
  25. return grunt.warn(
  26. '\n' + errMess + '\n' +
  27. 'More info: https://github.com/gruntjs/grunt-contrib-sass\n'
  28. );
  29. }
  30. };
  31. var checkFiles = function (files, options, cb) {
  32. var failCount = 0;
  33. var filesToCheck = files.filter(function (src) {
  34. return path.basename(src)[0] !== '_' && grunt.file.exists(src);
  35. });
  36. async.eachLimit(filesToCheck, numCPUs, function (src, next) {
  37. var bin;
  38. var args;
  39. if (options.bundleExec) {
  40. bin = 'bundle';
  41. args = ['exec', 'sass', '--check', src];
  42. } else {
  43. bin = 'sass';
  44. args = ['--check', src];
  45. }
  46. grunt.verbose.writeln('Command: ' + bin + ' ' + args.join(' '));
  47. grunt.verbose.writeln('Checking file ' + chalk.cyan(src) + ' syntax.');
  48. spawn(bin, args, { stdio: 'inherit' })
  49. .on('error', grunt.warn)
  50. .on('close', function (code) {
  51. if (code > 0) {
  52. failCount++;
  53. grunt.log.error('Checking file ' + chalk.cyan(src) + ' - ' + chalk.red('failed') + '.');
  54. } else {
  55. grunt.verbose.ok('Checking file ' + chalk.cyan(src) + ' - ' + chalk.green('passed') + '.');
  56. }
  57. next();
  58. });
  59. }, function () {
  60. if (failCount > 0) {
  61. grunt.warn('Sass check failed for ' + failCount + ' files.');
  62. } else {
  63. grunt.log.ok('All ' + chalk.cyan(filesToCheck.length) + ' files passed.');
  64. }
  65. cb();
  66. });
  67. };
  68. grunt.registerMultiTask('sass', 'Compile Sass to CSS', function () {
  69. var cb = this.async();
  70. var options = this.options();
  71. var bundleExec = options.bundleExec;
  72. var banner;
  73. var passedArgs;
  74. if (bundleExec) {
  75. checkBinary('bundle',
  76. 'bundleExec options set but no Bundler executable found in your PATH.'
  77. );
  78. } else {
  79. checkBinary('sass',
  80. 'You need to have Ruby and Sass installed and in your PATH for this task to work.'
  81. );
  82. }
  83. if (options.check) {
  84. checkFiles(this.filesSrc, options, cb);
  85. return;
  86. }
  87. // Unset banner option if set
  88. if (options.banner) {
  89. banner = options.banner;
  90. delete options.banner;
  91. }
  92. passedArgs = dargs(options, ['bundleExec']);
  93. async.eachLimit(this.files, numCPUs, function (file, next) {
  94. var src = file.src[0];
  95. if (typeof src !== 'string') {
  96. src = file.orig.src[0];
  97. }
  98. if (!grunt.file.exists(src)) {
  99. grunt.log.warn('Source file "' + src + '" not found.');
  100. return next();
  101. }
  102. if (path.basename(src)[0] === '_') {
  103. return next();
  104. }
  105. var args = [
  106. src,
  107. file.dest
  108. ].concat(passedArgs);
  109. if (options.update) {
  110. // When the source file hasn't yet been compiled SASS will write an empty file.
  111. // If this is the first time the file has been written we treat it as a if update was not passed
  112. if (!grunt.file.exists(file.dest)) {
  113. // Find where the --update flag is and remove it.
  114. var index = args.indexOf('--update');
  115. args.splice(index, 1);
  116. } else {
  117. // The first two elements in args is our source and destination files,
  118. // we use those values to build a path that SASS recognizes namely: source:destination
  119. var sassPath = args.shift() + ':' + args.shift();
  120. args.push(sassPath);
  121. }
  122. }
  123. var bin = 'sass';
  124. if (bundleExec) {
  125. bin = 'bundle';
  126. args.unshift('exec', 'sass');
  127. }
  128. // If we're compiling scss or css files
  129. if (path.extname(src) === '.css') {
  130. args.push('--scss');
  131. }
  132. // Make sure grunt creates the destination folders if they don't exist
  133. if (!grunt.file.exists(file.dest)) {
  134. grunt.file.write(file.dest, '');
  135. }
  136. grunt.verbose.writeln('Command: ' + bin + ' ' + args.join(' '));
  137. var cp = spawn(bin, args, {stdio: 'inherit'});
  138. cp.on('error', function (err) {
  139. grunt.warn(err);
  140. });
  141. cp.on('close', function (code) {
  142. if (code > 0) {
  143. return grunt.warn('Exited with error code ' + code);
  144. }
  145. // Callback to insert banner
  146. if (banner) {
  147. bannerCallback(file.dest, banner);
  148. }
  149. grunt.verbose.writeln('File ' + chalk.cyan(file.dest) + ' created.');
  150. next();
  151. });
  152. }, cb);
  153. });
  154. };