diff options
Diffstat (limited to 'node_modules/node-sass/lib/index.js')
-rw-r--r-- | node_modules/node-sass/lib/index.js | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/node_modules/node-sass/lib/index.js b/node_modules/node-sass/lib/index.js new file mode 100644 index 0000000..3f20708 --- /dev/null +++ b/node_modules/node-sass/lib/index.js @@ -0,0 +1,477 @@ +/*! + * node-sass: lib/index.js + */ + +var path = require('path'), + clonedeep = require('lodash/cloneDeep'), + assign = require('lodash/assign'), + sass = require('./extensions'); + +/** + * Require binding + */ + +var binding = require('./binding')(sass); + +/** + * Get input file + * + * @param {Object} options + * @api private + */ + +function getInputFile(options) { + return options.file ? path.resolve(options.file) : null; +} + +/** + * Get output file + * + * @param {Object} options + * @api private + */ + +function getOutputFile(options) { + var outFile = options.outFile; + + if (!outFile || typeof outFile !== 'string' || (!options.data && !options.file)) { + return null; + } + + return path.resolve(outFile); +} + +/** + * Get source map + * + * @param {Object} options + * @api private + */ + +function getSourceMap(options) { + var sourceMap = options.sourceMap; + + if (sourceMap && typeof sourceMap !== 'string' && options.outFile) { + sourceMap = options.outFile + '.map'; + } + + return sourceMap && typeof sourceMap === 'string' ? path.resolve(sourceMap) : null; +} + +/** + * Get stats + * + * @param {Object} options + * @api private + */ + +function getStats(options) { + var stats = {}; + + stats.entry = options.file || 'data'; + stats.start = Date.now(); + + return stats; +} + +/** + * End stats + * + * @param {Object} stats + * @param {Object} sourceMap + * @api private + */ + +function endStats(stats) { + stats.end = Date.now(); + stats.duration = stats.end - stats.start; + + return stats; +} + +/** + * Get style + * + * @param {Object} options + * @api private + */ + +function getStyle(options) { + var styles = { + nested: 0, + expanded: 1, + compact: 2, + compressed: 3 + }; + + return styles[options.outputStyle] || 0; +} + +/** + * Get indent width + * + * @param {Object} options + * @api private + */ + +function getIndentWidth(options) { + var width = parseInt(options.indentWidth) || 2; + + return width > 10 ? 2 : width; +} + +/** + * Get indent type + * + * @param {Object} options + * @api private + */ + +function getIndentType(options) { + var types = { + space: 0, + tab: 1 + }; + + return types[options.indentType] || 0; +} + +/** + * Get linefeed + * + * @param {Object} options + * @api private + */ + +function getLinefeed(options) { + var feeds = { + cr: '\r', + crlf: '\r\n', + lf: '\n', + lfcr: '\n\r' + }; + + return feeds[options.linefeed] || '\n'; +} + +/** + * Build an includePaths string + * from the options.includePaths array and the SASS_PATH environment variable + * + * @param {Object} options + * @api private + */ + +function buildIncludePaths(options) { + options.includePaths = options.includePaths || []; + + if (process.env.hasOwnProperty('SASS_PATH')) { + options.includePaths = options.includePaths.concat( + process.env.SASS_PATH.split(path.delimiter) + ); + } + + // Preserve the behaviour people have come to expect. + // This behaviour was removed from Sass in 3.4 and + // LibSass in 3.5. + options.includePaths.unshift(process.cwd()); + + return options.includePaths.join(path.delimiter); +} + +/** + * Get options + * + * @param {Object} options + * @api private + */ + +function getOptions(opts, cb) { + if (typeof opts !== 'object') { + throw new Error('Invalid: options is not an object.'); + } + var options = clonedeep(opts || {}); + + options.sourceComments = options.sourceComments || false; + if (options.hasOwnProperty('file')) { + options.file = getInputFile(options); + } + options.outFile = getOutputFile(options); + options.includePaths = buildIncludePaths(options); + options.precision = parseInt(options.precision) || 5; + options.sourceMap = getSourceMap(options); + options.style = getStyle(options); + options.indentWidth = getIndentWidth(options); + options.indentType = getIndentType(options); + options.linefeed = getLinefeed(options); + + // context object represents node-sass environment + options.context = { options: options, callback: cb }; + + options.result = { + stats: getStats(options) + }; + + return options; +} + +/** + * Executes a callback and transforms any exception raised into a sass error + * + * @param {Function} callback + * @param {Array} arguments + * @api private + */ + +function tryCallback(callback, args) { + try { + return callback.apply(this, args); + } catch (e) { + if (typeof e === 'string') { + return new binding.types.Error(e); + } else if (e instanceof Error) { + return new binding.types.Error(e.message); + } else { + return new binding.types.Error('An unexpected error occurred'); + } + } +} + +/** + * Normalizes the signature of custom functions to make it possible to just supply the + * function name and have the signature default to `fn(...)`. The callback is adjusted + * to transform the input sass list into discrete arguments. + * + * @param {String} signature + * @param {Function} callback + * @return {Object} + * @api private + */ + +function normalizeFunctionSignature(signature, callback) { + if (!/^\*|@warn|@error|@debug|\w+\(.*\)$/.test(signature)) { + if (!/\w+/.test(signature)) { + throw new Error('Invalid function signature format "' + signature + '"'); + } + + return { + signature: signature + '(...)', + callback: function() { + var args = Array.prototype.slice.call(arguments), + list = args.shift(), + i; + + for (i = list.getLength() - 1; i >= 0; i--) { + args.unshift(list.getValue(i)); + } + + return callback.apply(this, args); + } + }; + } + + return { + signature: signature, + callback: callback + }; +} + +/** + * Render + * + * @param {Object} options + * @api public + */ + +module.exports.render = function(opts, cb) { + var options = getOptions(opts, cb); + + // options.error and options.success are for libsass binding + options.error = function(err) { + var payload = assign(new Error(), JSON.parse(err)); + + if (cb) { + options.context.callback.call(options.context, payload, null); + } + }; + + options.success = function() { + var result = options.result; + var stats = endStats(result.stats); + var payload = { + css: result.css, + stats: stats + }; + if (result.map) { + payload.map = result.map; + } + + if (cb) { + options.context.callback.call(options.context, null, payload); + } + }; + + var importer = options.importer; + + if (importer) { + if (Array.isArray(importer)) { + options.importer = []; + importer.forEach(function(subject, index) { + options.importer[index] = function(file, prev, bridge) { + function done(result) { + bridge.success(result === module.exports.NULL ? null : result); + } + + var result = subject.call(options.context, file, prev, done); + + if (result !== undefined) { + done(result); + } + }; + }); + } else { + options.importer = function(file, prev, bridge) { + function done(result) { + bridge.success(result === module.exports.NULL ? null : result); + } + + var result = importer.call(options.context, file, prev, done); + + if (result !== undefined) { + done(result); + } + }; + } + } + + var functions = clonedeep(options.functions); + + if (functions) { + options.functions = {}; + + Object.keys(functions).forEach(function(subject) { + var cb = normalizeFunctionSignature(subject, functions[subject]); + + options.functions[cb.signature] = function() { + var args = Array.prototype.slice.call(arguments), + bridge = args.pop(); + + function done(data) { + bridge.success(data); + } + + var result = tryCallback(cb.callback.bind(options.context), args.concat(done)); + + if (result) { + done(result); + } + }; + }); + } + + if (options.data) { + binding.render(options); + } else if (options.file) { + binding.renderFile(options); + } else { + cb({status: 3, message: 'No input specified: provide a file name or a source string to process' }); + } +}; + +/** + * Render sync + * + * @param {Object} options + * @api public + */ + +module.exports.renderSync = function(opts) { + var options = getOptions(opts); + var importer = options.importer; + + if (importer) { + if (Array.isArray(importer)) { + options.importer = []; + importer.forEach(function(subject, index) { + options.importer[index] = function(file, prev) { + var result = subject.call(options.context, file, prev); + + return result === module.exports.NULL ? null : result; + }; + }); + } else { + options.importer = function(file, prev) { + var result = importer.call(options.context, file, prev); + + return result === module.exports.NULL ? null : result; + }; + } + } + + var functions = clonedeep(options.functions); + + if (options.functions) { + options.functions = {}; + + Object.keys(functions).forEach(function(signature) { + var cb = normalizeFunctionSignature(signature, functions[signature]); + + options.functions[cb.signature] = function() { + return tryCallback(cb.callback.bind(options.context), arguments); + }; + }); + } + + var status; + if (options.data) { + status = binding.renderSync(options); + } else if (options.file) { + status = binding.renderFileSync(options); + } else { + throw new Error('No input specified: provide a file name or a source string to process'); + } + + var result = options.result; + + if (status) { + result.stats = endStats(result.stats); + return result; + } + + throw assign(new Error(), JSON.parse(result.error)); +}; + +/** + * API Info + * + * @api public + */ + +module.exports.info = sass.getVersionInfo(binding); + +/** + * Expose sass types + */ + +module.exports.types = binding.types; +module.exports.TRUE = binding.types.Boolean.TRUE; +module.exports.FALSE = binding.types.Boolean.FALSE; +module.exports.NULL = binding.types.Null.NULL; + +/** + * Polyfill the old API + * + * TODO: remove for 4.0 + */ + +function processSassDeprecationMessage() { + console.log('Deprecation warning: `process.sass` is an undocumented internal that will be removed in future versions of Node Sass.'); +} + +process.sass = process.sass || { + get versionInfo() { processSassDeprecationMessage(); return module.exports.info; }, + get binaryName() { processSassDeprecationMessage(); return sass.getBinaryName(); }, + get binaryUrl() { processSassDeprecationMessage(); return sass.getBinaryUrl(); }, + get binaryPath() { processSassDeprecationMessage(); return sass.getBinaryPath(); }, + get getBinaryPath() { processSassDeprecationMessage(); return sass.getBinaryPath; }, +}; |