Commit 0e168812 by Adeel Mujahid

Merge pull request #577 from am11/simplified_api_2

API: Simplified code usage (#547)
parents 4bea0703 cfef58f3
...@@ -5,7 +5,8 @@ var Emitter = require('events').EventEmitter, ...@@ -5,7 +5,8 @@ var Emitter = require('events').EventEmitter,
meow = require('meow'), meow = require('meow'),
replaceExt = require('replace-ext'), replaceExt = require('replace-ext'),
stdin = require('get-stdin'), stdin = require('get-stdin'),
render = require('../lib/render'); render = require('../lib/render'),
fs = require('fs');
/** /**
* Initialize CLI * Initialize CLI
...@@ -14,6 +15,8 @@ var Emitter = require('events').EventEmitter, ...@@ -14,6 +15,8 @@ var Emitter = require('events').EventEmitter,
var cli = meow({ var cli = meow({
pkg: '../package.json', pkg: '../package.json',
help: [ help: [
require('../lib/').info(),
'',
'Usage', 'Usage',
' node-sass [options] <input.scss> [output.css]', ' node-sass [options] <input.scss> [output.css]',
' cat <input.scss> | node-sass > output.css', ' cat <input.scss> | node-sass > output.css',
...@@ -37,6 +40,7 @@ var cli = meow({ ...@@ -37,6 +40,7 @@ var cli = meow({
' --image-path Path to prepend when using the `image-url()` helper', ' --image-path Path to prepend when using the `image-url()` helper',
' --precision The amount of precision allowed in decimal numbers', ' --precision The amount of precision allowed in decimal numbers',
' --stdout Print the resulting CSS to stdout', ' --stdout Print the resulting CSS to stdout',
' --importer Path to custom importer',
' --help Print usage info' ' --help Print usage info'
].join('\n') ].join('\n')
}, { }, {
...@@ -105,6 +109,10 @@ function getEmitter() { ...@@ -105,6 +109,10 @@ function getEmitter() {
console.log(data); console.log(data);
}); });
emitter.on('done', function(){
process.exit(0);
});
return emitter; return emitter;
} }
...@@ -202,6 +210,15 @@ function run(options, emitter) { ...@@ -202,6 +210,15 @@ function run(options, emitter) {
} }
} }
if (options.importer) {
if (fs.existsSync(options.importer)) {
options.importer = require(options.importer);
} else {
console.error('Could not locate importer.');
process.exit(1);
}
}
if (options.watch) { if (options.watch) {
watch(options, emitter); watch(options, emitter);
} else { } else {
...@@ -240,6 +257,7 @@ if (options.src) { ...@@ -240,6 +257,7 @@ if (options.src) {
} else if (!process.stdin.isTTY) { } else if (!process.stdin.isTTY) {
stdin(function(data) { stdin(function(data) {
options.data = data; options.data = data;
options.stdin = true;
run(options, emitter); run(options, emitter);
}); });
} }
......
...@@ -54,7 +54,7 @@ function getOutFile(options) { ...@@ -54,7 +54,7 @@ function getOutFile(options) {
*/ */
function getStats(options) { function getStats(options) {
var stats = options.stats; var stats = {};
stats.entry = options.file || 'data'; stats.entry = options.file || 'data';
stats.start = Date.now(); stats.start = Date.now();
...@@ -65,17 +65,14 @@ function getStats(options) { ...@@ -65,17 +65,14 @@ function getStats(options) {
/** /**
* End stats * End stats
* *
* @param {Object} options * @param {Object} stats
* @param {Object} sourceMap * @param {Object} sourceMap
* @api private * @api private
*/ */
function endStats(options, sourceMap) { function endStats(stats) {
var stats = options.stats || {};
stats.end = Date.now(); stats.end = Date.now();
stats.duration = stats.end - stats.start; stats.duration = stats.end - stats.start;
stats.sourceMap = sourceMap;
return stats; return stats;
} }
...@@ -139,15 +136,12 @@ function getOptions(options) { ...@@ -139,15 +136,12 @@ function getOptions(options) {
options.paths = (options.include_paths || options.includePaths || []).join(path.delimiter); options.paths = (options.include_paths || options.includePaths || []).join(path.delimiter);
options.precision = parseInt(options.precision) || 5; options.precision = parseInt(options.precision) || 5;
options.sourceMap = getSourceMap(options); options.sourceMap = getSourceMap(options);
options.stats = options.stats || {};
options.style = getStyle(options) || 0; options.style = getStyle(options) || 0;
if (options.imagePath && typeof options.imagePath !== 'string') { if (options.imagePath && typeof options.imagePath !== 'string') {
throw new Error('`imagePath` needs to be a string'); throw new Error('`imagePath` needs to be a string');
} }
getStats(options);
var error = options.error; var error = options.error;
var success = options.success; var success = options.success;
var importer = options.importer; var importer = options.importer;
...@@ -159,29 +153,41 @@ function getOptions(options) { ...@@ -159,29 +153,41 @@ function getOptions(options) {
err = { message: err }; err = { message: err };
} }
err.code = code;
if (error) { if (error) {
error(err, code); error(err);
} }
}; };
options.success = function(css, sourceMap) { options.success = function() {
sourceMap = JSON.parse(sourceMap); options.result.sourceMap = JSON.parse(options.result.sourceMap);
endStats(options, sourceMap); var stats = endStats(options.result.stats);
if (success) { if (success) {
success(css, sourceMap); success({
css: options.result.css,
map: options.result.sourceMap,
stats: stats
});
} }
}; };
if (importer) { if (importer) {
options.importer = function(file, prev, key) { options.importer = function(file, prev, key) {
importer(file, prev, function(data) { var done = function(data) {
binding.importedCallback({ binding.importedCallback({
index: key, index: key,
objectLiteral: data objectLiteral: data
}); });
}); };
var result = importer(file, prev, done);
if (result) {
done(result);
}
}; };
} }
...@@ -191,6 +197,10 @@ function getOptions(options) { ...@@ -191,6 +197,10 @@ function getOptions(options) {
delete options.source_comments; delete options.source_comments;
delete options.sourceComments; delete options.sourceComments;
options.result = {
stats: getStats(options)
};
return options; return options;
} }
...@@ -201,41 +211,6 @@ function getOptions(options) { ...@@ -201,41 +211,6 @@ function getOptions(options) {
var binding = require(getBinding()); var binding = require(getBinding());
/** /**
* Render (deprecated)
*
* @param {String} css
* @param {Function} cb
* @param {Object} options
* @api private
*/
function deprecatedRender(css, cb, options) {
options = getOptions(options);
options.data = css;
options.error = cb;
options.success = function(css) {
cb(null, css);
};
binding.render(options);
}
/**
* Render sync (deprecated)
*
* @param {String} css
* @param {Object} options
* @api private
*/
function deprecatedRenderSync(css, options) {
options = getOptions(options);
options.data = css;
return binding.renderSync(options);
}
/**
* Render * Render
* *
* @param {Object} options * @param {Object} options
...@@ -243,12 +218,9 @@ function deprecatedRenderSync(css, options) { ...@@ -243,12 +218,9 @@ function deprecatedRenderSync(css, options) {
*/ */
module.exports.render = function(options) { module.exports.render = function(options) {
if (typeof arguments[0] === 'string') {
return deprecatedRender.apply(this, arguments);
}
options = getOptions(options); options = getOptions(options);
options.file ? binding.renderFile(options) : binding.render(options);
options.data ? binding.render(options) : binding.renderFile(options);
}; };
/** /**
...@@ -259,79 +231,27 @@ module.exports.render = function(options) { ...@@ -259,79 +231,27 @@ module.exports.render = function(options) {
*/ */
module.exports.renderSync = function(options) { module.exports.renderSync = function(options) {
if (typeof arguments[0] === 'string') {
return deprecatedRenderSync.apply(this, arguments);
}
var output;
options = getOptions(options); options = getOptions(options);
output = options.file ? binding.renderFileSync(options) : binding.renderSync(options);
endStats(options, JSON.parse(options.stats.sourceMap));
return output;
};
/** var result = options.data ? binding.renderSync(options) : binding.renderFileSync(options);
* Render file
*
* `options.sourceMap` can be used to specify that the source map should be saved:
*
* - If falsy the source map will not be saved
* - If `options.sourceMap === true` the source map will be saved to the
* standard location of `options.file + '.map'`
* - Else `options.sourceMap` specifies the path (relative to the `outFile`)
* where the source map should be saved
*
* @param {Object} options
* @api public
*/
module.exports.renderFile = function(options) { if(result) {
options = options || {}; options.result.stats = endStats(options.result.stats);
var outFile = options.outFile; return options.result;
var success = options.success;
if (options.sourceMap === true) {
options.sourceMap = outFile + '.map';
} }
options.success = function(css, sourceMap) {
fs.writeFile(outFile, css, function(err) {
if (err) {
return options.error(err);
}
if (!options.sourceMap) {
return success(outFile);
}
var dir = path.dirname(outFile);
var sourceMapFile = path.resolve(dir, options.sourceMap);
fs.writeFile(sourceMapFile, JSON.stringify(sourceMap), function(err) {
if (err) {
return options.error(err);
}
success(outFile, sourceMapFile);
});
});
};
module.exports.render(options);
}; };
/** /**
* Middleware * API Info
* *
* @api public
*/ */
module.exports.middleware = function() { module.exports.info = function() {
return new Error([ var package = require('../package.json');
'The middleware has been moved to',
'https://github.com/sass/node-sass-middleware' return [
].join(' ')); 'node-sass version: ' + package.version,
'libsass version: ' + package.libsass
].join('\n');
}; };
...@@ -22,7 +22,8 @@ module.exports = function(options, emitter) { ...@@ -22,7 +22,8 @@ module.exports = function(options, emitter) {
sourceComments: options.sourceComments, sourceComments: options.sourceComments,
sourceMapEmbed: options.sourceMapEmbed, sourceMapEmbed: options.sourceMapEmbed,
sourceMapContents: options.sourceMapContents, sourceMapContents: options.sourceMapContents,
sourceMap: options.sourceMap sourceMap: options.sourceMap,
importer: options.importer
}; };
if (options.src) { if (options.src) {
...@@ -31,7 +32,7 @@ module.exports = function(options, emitter) { ...@@ -31,7 +32,7 @@ module.exports = function(options, emitter) {
renderOptions.data = options.data; renderOptions.data = options.data;
} }
renderOptions.success = function(css, sourceMap) { renderOptions.success = function(result) {
var todo = 1; var todo = 1;
var done = function() { var done = function() {
if (--todo <= 0) { if (--todo <= 0) {
...@@ -39,38 +40,38 @@ module.exports = function(options, emitter) { ...@@ -39,38 +40,38 @@ module.exports = function(options, emitter) {
} }
}; };
if (options.stdout || (!options.dest && !process.stdout.isTTY)) { if (options.stdout || (!options.dest && !process.stdout.isTTY) || options.stdin) {
emitter.emit('log', css); emitter.emit('log', result.css);
return done(); return done();
} }
emitter.emit('warn', chalk.green('Rendering Complete, saving .css file...')); emitter.emit('warn', chalk.green('Rendering Complete, saving .css file...'));
fs.writeFile(options.dest, css, function(err) { fs.writeFile(options.dest, result.css, function(err) {
if (err) { if (err) {
return emitter.emit('error', chalk.red(err)); return emitter.emit('error', chalk.red(err));
} }
emitter.emit('warn', chalk.green('Wrote CSS to ' + options.dest)); emitter.emit('warn', chalk.green('Wrote CSS to ' + options.dest));
emitter.emit('write', err, options.dest, css); emitter.emit('write', err, options.dest, result.css);
done(); done();
}); });
if (options.sourceMap) { if (options.sourceMap) {
todo++; todo++;
fs.writeFile(options.sourceMap, sourceMap, function(err) { fs.writeFile(options.sourceMap, result.map, function(err) {
if (err) { if (err) {
return emitter.emit('error', chalk.red('Error' + err)); return emitter.emit('error', chalk.red('Error' + err));
} }
emitter.emit('warn', chalk.green('Wrote Source Map to ' + options.sourceMap)); emitter.emit('warn', chalk.green('Wrote Source Map to ' + options.sourceMap));
emitter.emit('write-source-map', err, options.sourceMap, sourceMap); emitter.emit('write-source-map', err, options.sourceMap, result.sourceMap);
done(); done();
}); });
} }
emitter.emit('render', css); emitter.emit('render', result.css);
}; };
renderOptions.error = function(error) { renderOptions.error = function(error) {
......
{ {
"name": "node-sass", "name": "node-sass",
"version": "1.2.3", "version": "2.0.0-beta",
"libsass": "3.1.0-beta",
"description": "Wrapper around libsass", "description": "Wrapper around libsass",
"license": "MIT", "license": "MIT",
"homepage": "https://github.com/sass/node-sass", "homepage": "https://github.com/sass/node-sass",
...@@ -50,7 +51,7 @@ ...@@ -50,7 +51,7 @@
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"mocha": "^2.0.1", "mocha": "^2.0.1",
"nan": "^1.3.0", "nan": "^1.3.0",
"object-assign": "^1.0.0", "object-assign": "^2.0.0",
"replace-ext": "0.0.1", "replace-ext": "0.0.1",
"request": "^2.48.0", "request": "^2.48.0",
"shelljs": "^0.3.0" "shelljs": "^0.3.0"
......
...@@ -15,7 +15,7 @@ char* CreateString(Local<Value> value) { ...@@ -15,7 +15,7 @@ char* CreateString(Local<Value> value) {
std::vector<sass_context_wrapper*> imports_collection; std::vector<sass_context_wrapper*> imports_collection;
void dispatched_async_uv_callback(uv_async_t *req){ void dispatched_async_uv_callback(uv_async_t *req) {
NanScope(); NanScope();
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data); sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
...@@ -24,8 +24,8 @@ void dispatched_async_uv_callback(uv_async_t *req){ ...@@ -24,8 +24,8 @@ void dispatched_async_uv_callback(uv_async_t *req){
imports_collection.push_back(ctx_w); imports_collection.push_back(ctx_w);
Handle<Value> argv[] = { Handle<Value> argv[] = {
NanNew<String>(strdup(ctx_w->file)), NanNew<String>(strdup(ctx_w->file ? ctx_w->file : 0)),
NanNew<String>(strdup(ctx_w->prev)), NanNew<String>(strdup(ctx_w->prev ? ctx_w->prev : 0)),
NanNew<Number>(imports_collection.size() - 1) NanNew<Number>(imports_collection.size() - 1)
}; };
...@@ -40,8 +40,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo ...@@ -40,8 +40,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
{ {
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(cookie); sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(cookie);
ctx_w->file = strdup(file); ctx_w->file = file ? strdup(file) : 0;
ctx_w->prev = strdup(prev); ctx_w->prev = prev ? strdup(prev) : 0;
ctx_w->async.data = (void*)ctx_w; ctx_w->async.data = (void*)ctx_w;
uv_async_send(&ctx_w->async); uv_async_send(&ctx_w->async);
...@@ -52,7 +52,7 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo ...@@ -52,7 +52,7 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
*/ */
uv_cond_wait(&ctx_w->importer_condition_variable, &ctx_w->importer_mutex); uv_cond_wait(&ctx_w->importer_condition_variable, &ctx_w->importer_mutex);
} }
else{ else {
/* that is sync: RenderSync() or RenderFileSync, /* that is sync: RenderSync() or RenderFileSync,
* we need to explicitly uv_run as the event loop * we need to explicitly uv_run as the event loop
* is blocked; waiting down the chain. * is blocked; waiting down the chain.
...@@ -66,6 +66,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo ...@@ -66,6 +66,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile, bool isSync) { void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile, bool isSync) {
struct Sass_Context* ctx; struct Sass_Context* ctx;
NanAssignPersistent(ctx_w->result, options->Get(NanNew("result"))->ToObject());
if (isFile) { if (isFile) {
ctx = sass_file_context_get_context((struct Sass_File_Context*) cptr); ctx = sass_file_context_get_context((struct Sass_File_Context*) cptr);
ctx_w->fctx = (struct Sass_File_Context*) cptr; ctx_w->fctx = (struct Sass_File_Context*) cptr;
...@@ -78,8 +80,6 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx ...@@ -78,8 +80,6 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
struct Sass_Options* sass_options = sass_context_get_options(ctx); struct Sass_Options* sass_options = sass_context_get_options(ctx);
if (!isSync) { if (!isSync) {
NanAssignPersistent(ctx_w->stats, options->Get(NanNew("stats"))->ToObject());
ctx_w->request.data = ctx_w; ctx_w->request.data = ctx_w;
// async (callback) style // async (callback) style
...@@ -99,6 +99,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx ...@@ -99,6 +99,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w)); sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w));
} }
sass_option_set_input_path(sass_options, CreateString(options->Get(NanNew("file"))));
sass_option_set_output_path(sass_options, CreateString(options->Get(NanNew("outFile")))); sass_option_set_output_path(sass_options, CreateString(options->Get(NanNew("outFile"))));
sass_option_set_image_path(sass_options, CreateString(options->Get(NanNew("imagePath")))); sass_option_set_image_path(sass_options, CreateString(options->Get(NanNew("imagePath"))));
sass_option_set_output_style(sass_options, (Sass_Output_Style)options->Get(NanNew("style"))->Int32Value()); sass_option_set_output_style(sass_options, (Sass_Output_Style)options->Get(NanNew("style"))->Int32Value());
...@@ -112,7 +113,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx ...@@ -112,7 +113,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
sass_option_set_precision(sass_options, options->Get(NanNew("precision"))->Int32Value()); sass_option_set_precision(sass_options, options->Get(NanNew("precision"))->Int32Value());
} }
void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) { void GetStats(Handle<Object> result, Sass_Context* ctx) {
char** included_files = sass_context_get_included_files(ctx); char** included_files = sass_context_get_included_files(ctx);
Handle<Array> arr = NanNew<Array>(); Handle<Array> arr = NanNew<Array>();
...@@ -122,8 +123,10 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) { ...@@ -122,8 +123,10 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
} }
} }
(*stats)->Set(NanNew("includedFiles"), arr); (*result)->Get(NanNew("stats"))->ToObject()->Set(NanNew("includedFiles"), arr);
}
void GetSourceMap(Handle<Object> result, Sass_Context* ctx) {
Handle<Value> source_map; Handle<Value> source_map;
if (sass_context_get_error_status(ctx)) { if (sass_context_get_error_status(ctx)) {
...@@ -137,7 +140,19 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) { ...@@ -137,7 +140,19 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
source_map = NanNew<String>("{}"); source_map = NanNew<String>("{}");
} }
(*stats)->Set(NanNew("sourceMap"), source_map); (*result)->Set(NanNew("sourceMap"), source_map);
}
int GetResult(Handle<Object> result, Sass_Context* ctx) {
int status = sass_context_get_error_status(ctx);
if (status == 0) {
(*result)->Set(NanNew("css"), NanNew<String>(sass_context_get_output_string(ctx)));
GetStats(result, ctx);
GetSourceMap(result, ctx);
}
return status;
} }
void make_callback(uv_work_t* req) { void make_callback(uv_work_t* req) {
...@@ -145,35 +160,27 @@ void make_callback(uv_work_t* req) { ...@@ -145,35 +160,27 @@ void make_callback(uv_work_t* req) {
TryCatch try_catch; TryCatch try_catch;
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data); sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
int error_status;
struct Sass_Context* ctx; struct Sass_Context* ctx;
if (ctx_w->dctx) { if (ctx_w->dctx) {
ctx = sass_data_context_get_context(ctx_w->dctx); ctx = sass_data_context_get_context(ctx_w->dctx);
FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx);
} }
else { else {
ctx = sass_file_context_get_context(ctx_w->fctx); ctx = sass_file_context_get_context(ctx_w->fctx);
FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx);
} }
if (error_status == 0) { int status = GetResult(ctx_w->result, ctx);
if (status == 0) {
// if no error, do callback(null, result) // if no error, do callback(null, result)
const char* val = sass_context_get_output_string(ctx); ctx_w->success_callback->Call(0, 0);
Local<Value> argv[] = {
NanNew<String>(val),
NanNew(ctx_w->stats)->Get(NanNew("sourceMap"))
};
ctx_w->success_callback->Call(2, argv);
} }
else { else {
// if error, do callback(error) // if error, do callback(error)
const char* err = sass_context_get_error_json(ctx); const char* err = sass_context_get_error_json(ctx);
Local<Value> argv[] = { Local<Value> argv[] = {
NanNew<String>(err), NanNew<String>(err),
NanNew<Integer>(error_status) NanNew<Integer>(status)
}; };
ctx_w->error_callback->Call(2, argv); ctx_w->error_callback->Call(2, argv);
} }
...@@ -212,21 +219,22 @@ NAN_METHOD(RenderSync) { ...@@ -212,21 +219,22 @@ NAN_METHOD(RenderSync) {
ExtractOptions(options, dctx, ctx_w, false, true); ExtractOptions(options, dctx, ctx_w, false, true);
compile_data(dctx); compile_data(dctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
if (sass_context_get_error_status(ctx) == 0) { int result = GetResult(ctx_w->result, ctx);
Local<String> output = NanNew<String>(sass_context_get_output_string(ctx)); Local<String> error;
sass_delete_data_context(dctx); if (result != 0) {
NanReturnValue(output); error = NanNew<String>(sass_context_get_error_json(ctx));
} }
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_free_context_wrapper(ctx_w); sass_free_context_wrapper(ctx_w);
NanThrowError(error); free(source_string);
NanReturnUndefined(); if (result != 0) {
NanThrowError(error);
}
NanReturnValue(NanNew<Boolean>(result == 0));
} }
NAN_METHOD(RenderFile) { NAN_METHOD(RenderFile) {
...@@ -258,22 +266,22 @@ NAN_METHOD(RenderFileSync) { ...@@ -258,22 +266,22 @@ NAN_METHOD(RenderFileSync) {
ExtractOptions(options, fctx, ctx_w, true, true); ExtractOptions(options, fctx, ctx_w, true, true);
compile_file(fctx); compile_file(fctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
free(input_path);
if (sass_context_get_error_status(ctx) == 0) { int result = GetResult(ctx_w->result, ctx);
Local<String> output = NanNew<String>(sass_context_get_output_string(ctx)); Local<String> error;
sass_delete_file_context(fctx); if (result != 0) {
NanReturnValue(output); error = NanNew<String>(sass_context_get_error_json(ctx));
} }
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_free_context_wrapper(ctx_w); sass_free_context_wrapper(ctx_w);
NanThrowError(error); free(input_path);
NanReturnUndefined(); if (result != 0) {
NanThrowError(error);
}
NanReturnValue(NanNew<Boolean>(result == 0));
} }
NAN_METHOD(ImportedCallback) { NAN_METHOD(ImportedCallback) {
...@@ -282,9 +290,7 @@ NAN_METHOD(ImportedCallback) { ...@@ -282,9 +290,7 @@ NAN_METHOD(ImportedCallback) {
TryCatch try_catch; TryCatch try_catch;
Local<Object> options = args[0]->ToObject(); Local<Object> options = args[0]->ToObject();
char* source_string = CreateString(options->Get(NanNew("index")));
Local<Value> returned_value = options->Get(NanNew("objectLiteral")); Local<Value> returned_value = options->Get(NanNew("objectLiteral"));
size_t index = options->Get(NanNew("index"))->Int32Value(); size_t index = options->Get(NanNew("index"))->Int32Value();
if (index >= imports_collection.size()) { if (index >= imports_collection.size()) {
......
Subproject commit 7aaa45a979ce65d50b3158ff50bf9409f8955063 Subproject commit cf7c1d14fec91a66ab9c2a3050dcec5fab6198f5
...@@ -37,14 +37,13 @@ extern "C" { ...@@ -37,14 +37,13 @@ extern "C" {
sass_delete_file_context(ctx_w->fctx); sass_delete_file_context(ctx_w->fctx);
} }
NanDisposePersistent(ctx_w->stats); NanDisposePersistent(ctx_w->result);
delete ctx_w->success_callback; delete ctx_w->success_callback;
delete ctx_w->error_callback; delete ctx_w->error_callback;
delete ctx_w->importer_callback; delete ctx_w->importer_callback;
delete ctx_w->file; delete ctx_w->file;
delete ctx_w->prev; delete ctx_w->prev;
delete ctx_w->cookie;
uv_mutex_destroy(&ctx_w->importer_mutex); uv_mutex_destroy(&ctx_w->importer_mutex);
uv_cond_destroy(&ctx_w->importer_condition_variable); uv_cond_destroy(&ctx_w->importer_condition_variable);
......
...@@ -14,7 +14,7 @@ extern "C" { ...@@ -14,7 +14,7 @@ extern "C" {
struct sass_context_wrapper { struct sass_context_wrapper {
Sass_Data_Context* dctx; Sass_Data_Context* dctx;
Sass_File_Context* fctx; Sass_File_Context* fctx;
Persistent<Object> stats; Persistent<Object> result;
uv_work_t request; uv_work_t request;
uv_mutex_t importer_mutex; uv_mutex_t importer_mutex;
uv_cond_t importer_condition_variable; uv_cond_t importer_condition_variable;
......
...@@ -6,79 +6,28 @@ var assert = require('assert'), ...@@ -6,79 +6,28 @@ var assert = require('assert'),
fixture = path.join.bind(null, __dirname, 'fixtures'), fixture = path.join.bind(null, __dirname, 'fixtures'),
resolveFixture = path.resolve.bind(null, __dirname, 'fixtures'); resolveFixture = path.resolve.bind(null, __dirname, 'fixtures');
describe('api (deprecated)', function() { describe('api', function() {
describe('.render(src, fn)', function() { describe('.render(options)', function() {
it('should compile sass to css', function(done) { it('should compile sass to css with file', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
sass.render(src, function(err, css) {
assert(!err);
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
done();
});
});
it('should compile sass to css using indented syntax', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8');
var expected = read(fixture('indent/expected.css'), 'utf8').trim();
sass.render(src, function(err, css) {
assert(!err);
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}, {
indentedSyntax: true
});
});
it('should throw error for bad input', function(done) {
sass.render('#navbar width 80%;', function(err) {
assert(err);
done();
});
});
});
describe('.renderSync(src)', function() {
it('should compile sass to css', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim(); var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var css = sass.renderSync(src).trim();
assert.equal(css, expected.replace(/\r\n/g, '\n'));
done();
});
it('should compile sass to css using indented syntax', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8');
var expected = read(fixture('indent/expected.css'), 'utf8').trim();
var css = sass.renderSync(src, {indentedSyntax: true}).trim();
assert.equal(css, expected.replace(/\r\n/g, '\n')); sass.render({
done(); file: fixture('simple/index.scss'),
}); success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
it('should throw error for bad input', function(done) { done();
assert.throws(function() { }
sass.renderSync('#navbar width 80%;');
}); });
done();
}); });
});
});
describe('api', function() { it('should compile sass to css with data', function(done) {
describe('.render(options)', function() {
it('should compile sass to css', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8'); var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim(); var expected = read(fixture('simple/expected.css'), 'utf8').trim();
sass.render({ sass.render({
data: src, data: src,
success: function(css) { success: function(result) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
} }
}); });
...@@ -91,8 +40,8 @@ describe('api', function() { ...@@ -91,8 +40,8 @@ describe('api', function() {
sass.render({ sass.render({
data: src, data: src,
indentedSyntax: true, indentedSyntax: true,
success: function(css) { success: function(result) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
} }
}); });
...@@ -101,9 +50,9 @@ describe('api', function() { ...@@ -101,9 +50,9 @@ describe('api', function() {
it('should throw error status 1 for bad input', function(done) { it('should throw error status 1 for bad input', function(done) {
sass.render({ sass.render({
data: '#navbar width 80%;', data: '#navbar width 80%;',
error: function(err, status) { error: function(error) {
assert(err); assert(error.message);
assert.equal(status, 1); assert.equal(error.status, 1);
done(); done();
} }
}); });
...@@ -119,8 +68,8 @@ describe('api', function() { ...@@ -119,8 +68,8 @@ describe('api', function() {
fixture('include-path/functions'), fixture('include-path/functions'),
fixture('include-path/lib') fixture('include-path/lib')
], ],
success: function(css) { success: function(result) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
} }
}); });
...@@ -133,8 +82,8 @@ describe('api', function() { ...@@ -133,8 +82,8 @@ describe('api', function() {
sass.render({ sass.render({
data: src, data: src,
imagePath: '/path/to/images', imagePath: '/path/to/images',
success: function(css) { success: function(result) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
} }
}); });
...@@ -160,55 +109,234 @@ describe('api', function() { ...@@ -160,55 +109,234 @@ describe('api', function() {
sass.render({ sass.render({
data: src, data: src,
precision: 10, precision: 10,
success: function(css) { success: function(result) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
} }
}); });
}); });
it('should compile with stats', function(done) { it('should contain all included files in stats when data is passed', function(done) {
var src = fixture('precision/index.scss'); var src = read(fixture('include-files/index.scss'), 'utf8');
var stats = {}; var expected = [
fixture('include-files/bar.scss').replace(/\\/g, '/'),
fixture('include-files/foo.scss').replace(/\\/g, '/')
];
sass.render({ sass.render({
file: src, data: src,
stats: stats, includePaths: [fixture('include-files')],
sourceMap: true, success: function(result) {
success: function() { assert.deepEqual(result.stats.includedFiles, expected);
assert.equal(stats.entry, src);
done(); done();
} }
}); });
}); });
});
it('should contain all included files in stats when data is passed', function(done) { describe('.render(importer)', function() {
var src = fixture('include-files/index.scss'); var src = read(fixture('include-files/index.scss'), 'utf8');
var stats = {};
var expected = [
fixture('include-files/bar.scss').replace(/\\/g, '/'),
fixture('include-files/foo.scss').replace(/\\/g, '/')
];
it('should override imports with "data" as input and fires callback with file and contents', function(done) {
sass.render({ sass.render({
data: read(src, 'utf8'), data: src,
includePaths: [fixture('include-files')], success: function(result) {
stats: stats, assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
success: function() { done();
assert.deepEqual(stats.includedFiles, expected); },
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "file" as input and fires callback with file and contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "data" as input and returns file and contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "file" as input and returns file and contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "data" as input and fires callback with file', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, /* jshint unused:false */ prev, done) {
done({
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
});
}
});
});
it('should override imports with "file" as input and fires callback with file', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, prev, done) {
done({
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
});
}
});
});
it('should override imports with "data" as input and returns file', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, /* jshint unused:false */ prev) {
return {
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
};
}
});
});
it('should override imports with "file" as input and returns file', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, prev) {
return {
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
};
}
});
});
it('should override imports with "data" as input and fires callback with contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "file" as input and fires callback with contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "data" as input and returns contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "file" as input and returns contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done(); done();
},
importer: function() {
return {
contents: 'div {color: yellow;}'
};
} }
}); });
}); });
}); });
describe('.renderSync(options)', function() { describe('.renderSync(options)', function() {
it('should compile sass to css', function(done) { it('should compile sass to css with file', function(done) {
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var result = sass.renderSync({file: fixture('simple/index.scss')});
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
});
it('should compile sass to css with data', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8'); var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim(); var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var css = sass.renderSync({data: src}).trim(); var result = sass.renderSync({data: src});
assert.equal(css, expected.replace(/\r\n/g, '\n')); assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done(); done();
}); });
...@@ -218,7 +346,7 @@ describe('api', function() { ...@@ -218,7 +346,7 @@ describe('api', function() {
var css = sass.renderSync({ var css = sass.renderSync({
data: src, data: src,
indentedSyntax: true indentedSyntax: true
}).trim(); }).css.trim();
assert.equal(css, expected.replace(/\r\n/g, '\n')); assert.equal(css, expected.replace(/\r\n/g, '\n'));
done(); done();
...@@ -233,109 +361,191 @@ describe('api', function() { ...@@ -233,109 +361,191 @@ describe('api', function() {
}); });
}); });
describe('.renderFile(options)', function() { describe('.renderSync(importer)', function() {
it('should compile sass to css', function(done) { var src = read(fixture('include-files/index.scss'), 'utf8');
var src = read(fixture('simple/index.scss'), 'utf8');
var dest = fixture('simple/build.css');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
sass.renderFile({ it('should override imports with "data" as input and fires callback with file and contents', function(done) {
var result = sass.renderSync({
data: src, data: src,
outFile: dest, importer: function(url, prev, done) {
success: function() { done({
assert.equal(read(dest, 'utf8').trim(), expected.replace(/\r\n/g, '\n')); file: '/some/other/path.scss',
fs.unlinkSync(dest); contents: 'div {color: yellow;}'
done(); });
} }
}); });
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
}); });
it('should compile sass to css using indented syntax', function(done) { it('should override imports with "file" as input and fires callback with file and contents', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8'); var result = sass.renderSync({
var dest = fixture('indent/build.css'); file: fixture('include-files/index.scss'),
var expected = read(fixture('indent/expected.css'), 'utf8').trim(); importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
sass.renderFile({ assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "data" as input and returns file and contents', function(done) {
var result = sass.renderSync({
data: src, data: src,
outFile: dest, importer: function(url, prev) {
indentedSyntax: true, return {
success: function() { file: prev + url,
assert.equal(read(dest, 'utf8').trim(), expected.replace(/\r\n/g, '\n')); contents: 'div {color: yellow;}'
fs.unlinkSync(dest); };
done();
} }
}); });
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
}); });
it('should save source map to default name', function(done) { it('should override imports with "file" as input and returns file and contents', function(done) {
var src = fixture('source-map/index.scss'); var result = sass.renderSync({
var dest = fixture('source-map/build.css'); file: fixture('include-files/index.scss'),
var name = 'build.css.map'; importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
sass.renderFile({ assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
file: src, done();
outFile: dest, });
sourceMap: true,
success: function(file, map) { it('should override imports with "data" as input and fires callback with file', function(done) {
assert.equal(path.basename(map), name); var result = sass.renderSync({
assert(read(dest, 'utf8').indexOf('sourceMappingURL=' + name) !== -1); data: src,
fs.unlinkSync(map); importer: function(url, /* jshint unused:false */ prev, done) {
fs.unlinkSync(dest); done({
done(); file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
});
} }
}); });
assert.equal(result.css.trim(), '');
done();
}); });
it('should save source map to specified name', function(done) { it('should override imports with "file" as input and fires callback with file', function(done) {
var src = fixture('source-map/index.scss'); var result = sass.renderSync({
var dest = fixture('source-map/build.css'); file: fixture('include-files/index.scss'),
var name = 'foo.css.map'; importer: function(url, prev, done) {
done({
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
});
}
});
sass.renderFile({ assert.equal(result.css.trim(), '');
file: src, done();
outFile: dest, });
sourceMap: name,
success: function(file, map) { it('should override imports with "data" as input and returns file', function(done) {
assert.equal(path.basename(map), name); var result = sass.renderSync({
assert(read(dest, 'utf8').indexOf('sourceMappingURL=' + name) !== -1); data: src,
fs.unlinkSync(map); importer: function(url, /* jshint unused:false */ prev) {
fs.unlinkSync(dest); return {
done(); file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
};
} }
}); });
assert.equal(result.css.trim(), '');
done();
}); });
it('should save source paths relative to the source map file', function(done) { it('should override imports with "file" as input and returns file', function(done) {
var src = fixture('include-files/index.scss'); var result = sass.renderSync({
var dest = fixture('include-files/build.css'); file: fixture('include-files/index.scss'),
var obj; importer: function(url, prev) {
return {
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
};
}
});
sass.renderFile({ assert.equal(result.css.trim(), '');
file: src, done();
outFile: dest, });
sourceMap: true,
success: function(file, map) { it('should override imports with "data" as input and fires callback with contents', function(done) {
obj = JSON.parse(read(map, 'utf8')); var result = sass.renderSync({
assert.equal(obj.sources[0], 'index.scss'); data: src,
assert.equal(obj.sources[1], 'foo.scss'); importer: function(url, prev, done) {
assert.equal(obj.sources[2], 'bar.scss'); done({
fs.unlinkSync(map); contents: 'div {color: yellow;}'
fs.unlinkSync(dest); });
done(); }
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "file" as input and fires callback with contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "data" as input and returns contents', function(done) {
var result = sass.renderSync({
data: src,
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "file" as input and returns contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function() {
return {
contents: 'div {color: yellow;}'
};
} }
}); });
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
}); });
}); });
describe('.render({stats: {}})', function() { describe('.render({stats: {}})', function() {
var start = Date.now(); var start = Date.now();
var stats = {};
before(function(done) { it('should provide a start timestamp', function(done) {
sass.render({ sass.render({
file: fixture('include-files/index.scss'), file: fixture('include-files/index.scss'),
stats: stats, success: function(result) {
success: function() { assert(typeof result.stats.start === 'number');
assert(result.stats.start >= start);
done(); done();
}, },
error: function(err) { error: function(err) {
...@@ -345,27 +555,48 @@ describe('api', function() { ...@@ -345,27 +555,48 @@ describe('api', function() {
}); });
}); });
it('should provide a start timestamp', function(done) {
assert(typeof stats.start === 'number');
assert(stats.start >= start);
done();
});
it('should provide an end timestamp', function(done) { it('should provide an end timestamp', function(done) {
assert(typeof stats.end === 'number'); sass.render({
assert(stats.end >= stats.start); file: fixture('include-files/index.scss'),
done(); success: function(result) {
assert(typeof result.stats.end === 'number');
assert(result.stats.end >= result.stats.start);
done();
},
error: function(err) {
assert(!err);
done();
}
});
}); });
it('should provide a duration', function(done) { it('should provide a duration', function(done) {
assert(typeof stats.duration === 'number'); sass.render({
assert.equal(stats.end - stats.start, stats.duration); file: fixture('include-files/index.scss'),
done(); success: function(result) {
assert(typeof result.stats.duration === 'number');
assert.equal(result.stats.end - result.stats.start, result.stats.duration);
done();
},
error: function(err) {
assert(!err);
done();
}
});
}); });
it('should contain the given entry file', function(done) { it('should contain the given entry file', function(done) {
assert.equal(stats.entry, fixture('include-files/index.scss')); sass.render({
done(); file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.stats.entry, fixture('include-files/index.scss'));
done();
},
error: function(err) {
assert(!err);
done();
}
});
}); });
it('should contain an array of all included files', function(done) { it('should contain an array of all included files', function(done) {
...@@ -375,8 +606,17 @@ describe('api', function() { ...@@ -375,8 +606,17 @@ describe('api', function() {
fixture('include-files/index.scss').replace(/\\/g, '/') fixture('include-files/index.scss').replace(/\\/g, '/')
]; ];
assert.deepEqual(stats.includedFiles, expected); sass.render({
done(); file: fixture('include-files/index.scss'),
success: function(result) {
assert.deepEqual(result.stats.includedFiles, expected);
done();
},
error: function(err) {
assert(!err);
done();
}
});
}); });
it('should contain array with the entry if there are no import statements', function(done) { it('should contain array with the entry if there are no import statements', function(done) {
...@@ -384,9 +624,8 @@ describe('api', function() { ...@@ -384,9 +624,8 @@ describe('api', function() {
sass.render({ sass.render({
file: fixture('simple/index.scss'), file: fixture('simple/index.scss'),
stats: stats, success: function(result) {
success: function() { assert.deepEqual(result.stats.includedFiles, [expected]);
assert.deepEqual(stats.includedFiles, [expected]);
done(); done();
} }
}); });
...@@ -395,9 +634,8 @@ describe('api', function() { ...@@ -395,9 +634,8 @@ describe('api', function() {
it('should state `data` as entry file', function(done) { it('should state `data` as entry file', function(done) {
sass.render({ sass.render({
data: read(fixture('simple/index.scss'), 'utf8'), data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats, success: function(result) {
success: function() { assert.equal(result.stats.entry, 'data');
assert.equal(stats.entry, 'data');
done(); done();
} }
}); });
...@@ -406,22 +644,8 @@ describe('api', function() { ...@@ -406,22 +644,8 @@ describe('api', function() {
it('should contain an empty array as includedFiles', function(done) { it('should contain an empty array as includedFiles', function(done) {
sass.render({ sass.render({
data: read(fixture('simple/index.scss'), 'utf8'), data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats, success: function(result) {
success: function() { assert.deepEqual(result.stats.includedFiles, []);
assert.deepEqual(stats.includedFiles, []);
done();
}
});
});
it('should report correct source map in stats', function(done) {
sass.render({
file: fixture('simple/index.scss'),
outFile: fixture('simple/build.css'),
stats: stats,
sourceMap: true,
success: function() {
assert.equal(stats.sourceMap.sources[0], 'index.scss');
done(); done();
} }
}); });
...@@ -430,35 +654,30 @@ describe('api', function() { ...@@ -430,35 +654,30 @@ describe('api', function() {
describe('.renderSync({stats: {}})', function() { describe('.renderSync({stats: {}})', function() {
var start = Date.now(); var start = Date.now();
var stats = {}; var result = sass.renderSync({
file: fixture('include-files/index.scss')
before(function() {
sass.renderSync({
file: fixture('include-files/index.scss'),
stats: stats
});
}); });
it('should provide a start timestamp', function(done) { it('should provide a start timestamp', function(done) {
assert(typeof stats.start === 'number'); assert(typeof result.stats.start === 'number');
assert(stats.start >= start); assert(result.stats.start >= start);
done(); done();
}); });
it('should provide an end timestamp', function(done) { it('should provide an end timestamp', function(done) {
assert(typeof stats.end === 'number'); assert(typeof result.stats.end === 'number');
assert(stats.end >= stats.start); assert(result.stats.end >= result.stats.start);
done(); done();
}); });
it('should provide a duration', function(done) { it('should provide a duration', function(done) {
assert(typeof stats.duration === 'number'); assert(typeof result.stats.duration === 'number');
assert.equal(stats.end - stats.start, stats.duration); assert.equal(result.stats.end - result.stats.start, result.stats.duration);
done(); done();
}); });
it('should contain the given entry file', function(done) { it('should contain the given entry file', function(done) {
assert.equal(stats.entry, resolveFixture('include-files/index.scss')); assert.equal(result.stats.entry, resolveFixture('include-files/index.scss'));
done(); done();
}); });
...@@ -469,60 +688,49 @@ describe('api', function() { ...@@ -469,60 +688,49 @@ describe('api', function() {
fixture('include-files/index.scss').replace(/\\/g, '/') fixture('include-files/index.scss').replace(/\\/g, '/')
]; ];
assert.equal(stats.includedFiles[0], expected[0]); assert.equal(result.stats.includedFiles[0], expected[0]);
assert.equal(stats.includedFiles[1], expected[1]); assert.equal(result.stats.includedFiles[1], expected[1]);
assert.equal(stats.includedFiles[2], expected[2]); assert.equal(result.stats.includedFiles[2], expected[2]);
done(); done();
}); });
it('should contain array with the entry if there are no import statements', function(done) { it('should contain array with the entry if there are no import statements', function(done) {
var expected = fixture('simple/index.scss').replace(/\\/g, '/'); var expected = fixture('simple/index.scss').replace(/\\/g, '/');
sass.renderSync({ var result = sass.renderSync({
file: fixture('simple/index.scss'), file: fixture('simple/index.scss')
stats: stats
}); });
assert.deepEqual(stats.includedFiles, [expected]); assert.deepEqual(result.stats.includedFiles, [expected]);
done(); done();
}); });
it('should state `data` as entry file', function(done) { it('should state `data` as entry file', function(done) {
sass.renderSync({ var result = sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8'), data: read(fixture('simple/index.scss'), 'utf8')
stats: stats
}); });
assert.equal(stats.entry, 'data'); assert.equal(result.stats.entry, 'data');
done(); done();
}); });
it('should contain an empty array as includedFiles', function(done) { it('should contain an empty array as includedFiles', function(done) {
sass.renderSync({ var result = sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8'), data: read(fixture('simple/index.scss'), 'utf8')
stats: stats
}); });
assert.deepEqual(stats.includedFiles, []); assert.deepEqual(result.stats.includedFiles, []);
done();
});
it('should report correct source map in stats', function(done) {
sass.renderSync({
file: fixture('simple/index.scss'),
outFile: fixture('simple/build.css'),
stats: stats,
sourceMap: true
});
assert.equal(stats.sourceMap.sources[0], 'index.scss');
done(); done();
}); });
}); });
describe('.middleware()', function() { describe('.info()', function() {
it('should throw error on require', function(done) { it('should return a correct version info', function(done) {
assert.throws(sass.middleware()); assert.equal(sass.info(), [
'node-sass version: ' + require('../package.json').version,
'libsass version: ' + require('../package.json').libsass
].join('\n'));
done(); done();
}); });
}); });
......
...@@ -223,4 +223,106 @@ describe('cli', function() { ...@@ -223,4 +223,106 @@ describe('cli', function() {
}); });
}); });
}); });
describe('importer', function() {
var dest = fixture('include-files/index.css');
var src = fixture('include-files/index.scss');
var expected = read(fixture('include-files/expected-importer.css'), 'utf8').trim().replace(/\r\n/g, '\n');
it('should override imports and fire callback with file and contents', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_and_data_cb.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and fire callback with file', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_cb.js')
]);
bin.on('close', function () {
if (fs.existsSync(dest)) {
assert.equal(read(dest, 'utf8').trim(), '');
fs.unlinkSync(dest);
}
done();
});
});
it('should override imports and fire callback with data', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_data_cb.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and return file and contents', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_and_data.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and return file', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file.js')
]);
bin.on('close', function () {
if (fs.existsSync(dest)) {
assert.equal(read(dest, 'utf8').trim(), '');
fs.unlinkSync(dest);
}
done();
});
});
it('should override imports and return data', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_data.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should return error on for invalid importer file path', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('non/existing/path')
]);
bin.on('close', function (code) {
assert(code !== 0);
done();
});
});
});
}); });
module.exports = function() {
return {
contents: 'div {color: yellow;}'
};
};
module.exports = function(file, prev, done) {
done({
contents: 'div {color: yellow;}'
});
};
var path = require('path');
module.exports = function(file) {console.log('>>>>>>>>>>');console.log(path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss'))));
return {
file: path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss')))
};
};
module.exports = function() {
return {
file: '/some/random/path/file.scss',
contents: 'div {color: yellow;}'
};
};
module.exports = function(file, prev, done) {
done({
file: '/some/random/path/file.scss',
contents: 'div {color: yellow;}'
});
};
var path = require('path');
module.exports = function(file, /* jshint unused:false */ prev, done) {
done({
file: path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss')))
});
};
div {
color: yellow; }
div {
color: yellow; }
Subproject commit 074ae3b24bfc3ff19a98efdec02415ed6fd5759a Subproject commit 00c6154dba85a38bb03b89d7279f439feb055821
...@@ -40,8 +40,8 @@ describe('spec', function () { ...@@ -40,8 +40,8 @@ describe('spec', function () {
sass.render({ sass.render({
file: t.src, file: t.src,
includePaths: t.paths, includePaths: t.paths,
success: function(css) { success: function(result) {
assert.equal(util.normalize(css), expected); assert.equal(util.normalize(result.css), expected);
done(); done();
}, },
error: function(err) { error: function(err) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment