| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 | 
							- #!/usr/bin/env node
 
- var Emitter = require('events').EventEmitter,
 
-   forEach = require('async-foreach').forEach,
 
-   Gaze = require('gaze'),
 
-   meow = require('meow'),
 
-   util = require('util'),
 
-   path = require('path'),
 
-   glob = require('glob'),
 
-   sass = require('../lib'),
 
-   render = require('../lib/render'),
 
-   watcher = require('../lib/watcher'),
 
-   stdout = require('stdout-stream'),
 
-   stdin = require('get-stdin'),
 
-   fs = require('fs');
 
- /**
 
-  * Initialize CLI
 
-  */
 
- var cli = meow({
 
-   pkg: '../package.json',
 
-   version: sass.info,
 
-   help: [
 
-     'Usage:',
 
-     '  node-sass [options] <input.scss>',
 
-     '  cat <input.scss> | node-sass [options] > output.css',
 
-     '',
 
-     'Example: Compile foobar.scss to foobar.css',
 
-     '  node-sass --output-style compressed foobar.scss > foobar.css',
 
-     '  cat foobar.scss | node-sass --output-style compressed > foobar.css',
 
-     '',
 
-     'Example: Watch the sass directory for changes, compile with sourcemaps to the css directory',
 
-     '  node-sass --watch --recursive --output css',
 
-     '    --source-map true --source-map-contents sass',
 
-     '',
 
-     'Options',
 
-     '  -w, --watch                Watch a directory or file',
 
-     '  -r, --recursive            Recursively watch directories or files',
 
-     '  -o, --output               Output directory',
 
-     '  -x, --omit-source-map-url  Omit source map URL comment from output',
 
-     '  -i, --indented-syntax      Treat data from stdin as sass code (versus scss)',
 
-     '  -q, --quiet                Suppress log output except on error',
 
-     '  -v, --version              Prints version info',
 
-     '  --output-style             CSS output style (nested | expanded | compact | compressed)',
 
-     '  --indent-type              Indent type for output CSS (space | tab)',
 
-     '  --indent-width             Indent width; number of spaces or tabs (maximum value: 10)',
 
-     '  --linefeed                 Linefeed style (cr | crlf | lf | lfcr)',
 
-     '  --source-comments          Include debug info in output',
 
-     '  --source-map               Emit source map (boolean, or path to output .map file)',
 
-     '  --source-map-contents      Embed include contents in map',
 
-     '  --source-map-embed         Embed sourceMappingUrl as data URI',
 
-     '  --source-map-root          Base path, will be emitted in source-map as is',
 
-     '  --include-path             Path to look for imported files',
 
-     '  --follow                   Follow symlinked directories',
 
-     '  --precision                The amount of precision allowed in decimal numbers',
 
-     '  --error-bell               Output a bell character on errors',
 
-     '  --importer                 Path to .js file containing custom importer',
 
-     '  --functions                Path to .js file containing custom functions',
 
-     '  --help                     Print usage info'
 
-   ].join('\n')
 
- }, {
 
-   boolean: [
 
-     'error-bell',
 
-     'follow',
 
-     'indented-syntax',
 
-     'omit-source-map-url',
 
-     'quiet',
 
-     'recursive',
 
-     'source-map-embed',
 
-     'source-map-contents',
 
-     'source-comments',
 
-     'watch'
 
-   ],
 
-   string: [
 
-     'functions',
 
-     'importer',
 
-     'include-path',
 
-     'indent-type',
 
-     'linefeed',
 
-     'output',
 
-     'output-style',
 
-     'precision',
 
-     'source-map-root'
 
-   ],
 
-   alias: {
 
-     c: 'source-comments',
 
-     i: 'indented-syntax',
 
-     q: 'quiet',
 
-     o: 'output',
 
-     r: 'recursive',
 
-     x: 'omit-source-map-url',
 
-     v: 'version',
 
-     w: 'watch'
 
-   },
 
-   default: {
 
-     'include-path': process.cwd(),
 
-     'indent-type': 'space',
 
-     'indent-width': 2,
 
-     linefeed: 'lf',
 
-     'output-style': 'nested',
 
-     precision: 5,
 
-     quiet: false,
 
-     recursive: true
 
-   }
 
- });
 
- /**
 
-  * Is a Directory
 
-  *
 
-  * @param {String} filePath
 
-  * @returns {Boolean}
 
-  * @api private
 
-  */
 
- function isDirectory(filePath) {
 
-   var isDir = false;
 
-   try {
 
-     var absolutePath = path.resolve(filePath);
 
-     isDir = fs.statSync(absolutePath).isDirectory();
 
-   } catch (e) {
 
-     isDir = e.code === 'ENOENT';
 
-   }
 
-   return isDir;
 
- }
 
- /**
 
-  * Get correct glob pattern
 
-  *
 
-  * @param {Object} options
 
-  * @returns {String}
 
-  * @api private
 
-  */
 
- function globPattern(options) {
 
-   return options.recursive ? '**/*.{sass,scss}' : '*.{sass,scss}';
 
- }
 
- /**
 
-  * Create emitter
 
-  *
 
-  * @api private
 
-  */
 
- function getEmitter() {
 
-   var emitter = new Emitter();
 
-   emitter.on('error', function(err) {
 
-     if (options.errorBell) {
 
-       err += '\x07';
 
-     }
 
-     console.error(err);
 
-     if (!options.watch) {
 
-       process.exit(1);
 
-     }
 
-   });
 
-   emitter.on('warn', function(data) {
 
-     if (!options.quiet) {
 
-       console.warn(data);
 
-     }
 
-   });
 
-   emitter.on('info', function(data) {
 
-     if (!options.quiet) {
 
-       console.info(data);
 
-     }
 
-   });
 
-   emitter.on('log', stdout.write.bind(stdout));
 
-   return emitter;
 
- }
 
- /**
 
-  * Construct options
 
-  *
 
-  * @param {Array} arguments
 
-  * @param {Object} options
 
-  * @api private
 
-  */
 
- function getOptions(args, options) {
 
-   var cssDir, sassDir, file, mapDir;
 
-   options.src = args[0];
 
-   if (args[1]) {
 
-     options.dest = path.resolve(args[1]);
 
-   } else if (options.output) {
 
-     options.dest = path.join(
 
-       path.resolve(options.output),
 
-       [path.basename(options.src, path.extname(options.src)), '.css'].join(''));  // replace ext.
 
-   }
 
-   if (options.directory) {
 
-     sassDir = path.resolve(options.directory);
 
-     file = path.relative(sassDir, args[0]);
 
-     cssDir = path.resolve(options.output);
 
-     options.dest = path.join(cssDir, file).replace(path.extname(file), '.css');
 
-   }
 
-   if (options.sourceMap) {
 
-     if(!options.sourceMapOriginal) {
 
-       options.sourceMapOriginal = options.sourceMap;
 
-     }
 
-     // check if sourceMap path ends with .map to avoid isDirectory false-positive
 
-     var sourceMapIsDirectory = options.sourceMapOriginal.indexOf('.map', options.sourceMapOriginal.length - 4) === -1 && isDirectory(options.sourceMapOriginal);
 
-     if (options.sourceMapOriginal === 'true') {
 
-       options.sourceMap = options.dest + '.map';
 
-     } else if (!sourceMapIsDirectory) {
 
-       options.sourceMap = path.resolve(options.sourceMapOriginal);
 
-     } else if (sourceMapIsDirectory) {
 
-       if (!options.directory) {
 
-         options.sourceMap = path.resolve(options.sourceMapOriginal, path.basename(options.dest) + '.map');
 
-       } else {
 
-         sassDir = path.resolve(options.directory);
 
-         file = path.relative(sassDir, args[0]);
 
-         mapDir = path.resolve(options.sourceMapOriginal);
 
-         options.sourceMap = path.join(mapDir, file).replace(path.extname(file), '.css.map');
 
-       }
 
-     }
 
-   }
 
-   return options;
 
- }
 
- /**
 
-  * Watch
 
-  *
 
-  * @param {Object} options
 
-  * @param {Object} emitter
 
-  * @api private
 
-  */
 
- function watch(options, emitter) {
 
-   var handler = function(files) {
 
-     files.added.forEach(function(file) {
 
-       var watch = gaze.watched();
 
-       Object.keys(watch).forEach(function (dir) {
 
-         if (watch[dir].indexOf(file) !== -1) {
 
-           gaze.add(file);
 
-         }
 
-       });
 
-     });
 
-     files.changed.forEach(function(file) {
 
-       if (path.basename(file)[0] !== '_') {
 
-         renderFile(file, options, emitter);
 
-       }
 
-     });
 
-     files.removed.forEach(function(file) {
 
-       gaze.remove(file);
 
-     });
 
-   };
 
-   var gaze = new Gaze();
 
-   gaze.add(watcher.reset(options));
 
-   gaze.on('error', emitter.emit.bind(emitter, 'error'));
 
-   gaze.on('changed', function(file) {
 
-     handler(watcher.changed(file));
 
-   });
 
-   gaze.on('added', function(file) {
 
-     handler(watcher.added(file));
 
-   });
 
-   gaze.on('deleted', function(file) {
 
-     handler(watcher.removed(file));
 
-   });
 
- }
 
- /**
 
-  * Run
 
-  *
 
-  * @param {Object} options
 
-  * @param {Object} emitter
 
-  * @api private
 
-  */
 
- function run(options, emitter) {
 
-   if (!Array.isArray(options.includePath)) {
 
-     options.includePath = [options.includePath];
 
-   }
 
-   if (options.directory) {
 
-     if (!options.output) {
 
-       emitter.emit('error', 'An output directory must be specified when compiling a directory');
 
-     }
 
-     if (!isDirectory(options.output)) {
 
-       emitter.emit('error', 'An output directory must be specified when compiling a directory');
 
-     }
 
-   }
 
-   if (options.sourceMapOriginal && options.directory && !isDirectory(options.sourceMapOriginal) && options.sourceMapOriginal !== 'true') {
 
-     emitter.emit('error', 'The --source-map option must be either a boolean or directory when compiling a directory');
 
-   }
 
-   if (options.importer) {
 
-     if ((path.resolve(options.importer) === path.normalize(options.importer).replace(/(.+)([\/|\\])$/, '$1'))) {
 
-       options.importer = require(options.importer);
 
-     } else {
 
-       options.importer = require(path.resolve(options.importer));
 
-     }
 
-   }
 
-   if (options.functions) {
 
-     if ((path.resolve(options.functions) === path.normalize(options.functions).replace(/(.+)([\/|\\])$/, '$1'))) {
 
-       options.functions = require(options.functions);
 
-     } else {
 
-       options.functions = require(path.resolve(options.functions));
 
-     }
 
-   }
 
-   if (options.watch) {
 
-     watch(options, emitter);
 
-   } else if (options.directory) {
 
-     renderDir(options, emitter);
 
-   } else {
 
-     render(options, emitter);
 
-   }
 
- }
 
- /**
 
-  * Render a file
 
-  *
 
-  * @param {String} file
 
-  * @param {Object} options
 
-  * @param {Object} emitter
 
-  * @api private
 
-  */
 
- function renderFile(file, options, emitter) {
 
-   options = getOptions([path.resolve(file)], options);
 
-   if (options.watch && !options.quiet) {
 
-     emitter.emit('info', util.format('=> changed: %s', file));
 
-   }
 
-   render(options, emitter);
 
- }
 
- /**
 
-  * Render all sass files in a directory
 
-  *
 
-  * @param {Object} options
 
-  * @param {Object} emitter
 
-  * @api private
 
-  */
 
- function renderDir(options, emitter) {
 
-   var globPath = path.resolve(options.directory, globPattern(options));
 
-   glob(globPath, { ignore: '**/_*', follow: options.follow }, function(err, files) {
 
-     if (err) {
 
-       return emitter.emit('error', util.format('You do not have permission to access this path: %s.', err.path));
 
-     } else if (!files.length) {
 
-       return emitter.emit('error', 'No input file was found.');
 
-     }
 
-     forEach(files, function(subject) {
 
-       emitter.once('done', this.async());
 
-       renderFile(subject, options, emitter);
 
-     }, function(successful, arr) {
 
-       var outputDir = path.join(process.cwd(), options.output);
 
-       if (!options.quiet) {
 
-         emitter.emit('info', util.format('Wrote %s CSS files to %s', arr.length, outputDir));
 
-       }
 
-       process.exit();
 
-     });
 
-   });
 
- }
 
- /**
 
-  * Arguments and options
 
-  */
 
- var options = getOptions(cli.input, cli.flags);
 
- var emitter = getEmitter();
 
- /**
 
-  * Show usage if no arguments are supplied
 
-  */
 
- if (!options.src && process.stdin.isTTY) {
 
-   emitter.emit('error', [
 
-     'Provide a Sass file to render',
 
-     '',
 
-     'Example: Compile foobar.scss to foobar.css',
 
-     '  node-sass --output-style compressed foobar.scss > foobar.css',
 
-     '  cat foobar.scss | node-sass --output-style compressed > foobar.css',
 
-     '',
 
-     'Example: Watch the sass directory for changes, compile with sourcemaps to the css directory',
 
-     '  node-sass --watch --recursive --output css',
 
-     '    --source-map true --source-map-contents sass',
 
-   ].join('\n'));
 
- }
 
- /**
 
-  * Apply arguments
 
-  */
 
- if (options.src) {
 
-   if (isDirectory(options.src)) {
 
-     options.directory = options.src;
 
-   }
 
-   run(options, emitter);
 
- } else if (!process.stdin.isTTY) {
 
-   stdin(function(data) {
 
-     options.data = data;
 
-     options.stdin = true;
 
-     run(options, emitter);
 
-   });
 
- }
 
 
  |