Commit a7c9d473 by Andrew Nesbitt

Merge pull request #290 from jhnns/feature/provide-included-files

Feature: Provide included files
parents 50e38666 fd570002
...@@ -78,6 +78,22 @@ The `map` option will create the source map file in your CSS destination. ...@@ -78,6 +78,22 @@ The `map` option will create the source map file in your CSS destination.
If your `sourceComments` option is set to `map`, `sourceMap` allows setting a new path context for the referenced Sass files. If your `sourceComments` option is set to `map`, `sourceMap` allows setting a new path context for the referenced Sass files.
The source map describes a path from your CSS file location, into the the folder where the Sass files are located. In most occasions this will work out-of-the-box but, in some cases, you may need to set a different output. The source map describes a path from your CSS file location, into the the folder where the Sass files are located. In most occasions this will work out-of-the-box but, in some cases, you may need to set a different output.
#### stats
`stats` is an empty `Object` that will be filled with stats from the compilation:
```javascript
{
entry: "path/to/entry.scss", // or just "data" if the source was not a file
start: 10000000, // Date.now() before the compilation
end: 10000001, // Date.now() after the compilation
duration: 1, // end - start
includedFiles: [ ... ], // absolute paths to all related scss files
sourceMap: "..." // the source map string or null
}
```
`includedFiles` isn't sorted in any meaningful way, it's just a list of all imported scss files including the entry.
### renderFile() ### renderFile()
Same as `render()` but writes the CSS and sourceMap (if requested) to the filesystem. Same as `render()` but writes the CSS and sourceMap (if requested) to the filesystem.
...@@ -101,22 +117,27 @@ where the source map should be saved ...@@ -101,22 +117,27 @@ where the source map should be saved
```javascript ```javascript
var sass = require('node-sass'); var sass = require('node-sass');
var stats = {};
sass.render({ sass.render({
data: 'body{background:blue; a{color:black;}}', data: 'body{background:blue; a{color:black;}}',
success: function(css){ success: function(css) {
console.log(css) console.log(css);
console.log(stats);
}, },
error: function(error) { error: function(error) {
console.log(error); console.log(error);
}, },
includePaths: [ 'lib/', 'mod/' ], includePaths: [ 'lib/', 'mod/' ],
outputStyle: 'compressed' outputStyle: 'compressed',
stats: stats
}); });
// OR // OR
console.log(sass.renderSync({ console.log(sass.renderSync({
data: 'body{background:blue; a{color:black;}}', data: 'body{background:blue; a{color:black;}}',
outputStyle: 'compressed' outputStyle: 'compressed',
stats: stats
})); }));
console.log(stats);
``` ```
### Edge-case behaviours ### Edge-case behaviours
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
using namespace v8; using namespace v8;
using namespace std; using namespace std;
void WorkOnContext(uv_work_t* req) { void WorkOnContext(uv_work_t* req) {
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data); sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
if (ctx_w->ctx) { if (ctx_w->ctx) {
...@@ -20,119 +19,120 @@ void WorkOnContext(uv_work_t* req) { ...@@ -20,119 +19,120 @@ void WorkOnContext(uv_work_t* req) {
} }
} }
void extractOptions(_NAN_METHOD_ARGS, void* cptr, sass_context_wrapper* ctx_w, bool isFile) { char* CreateString(Local<Value> value) {
char *source; size_t count;
char* pathOrData; return NanCString(value, &count);
char* imagePath; }
int output_style;
int source_comments;
String::AsciiValue astr(args[0]);
String::AsciiValue bstr(args[1]); void ExtractOptions(Local<Value> optionsValue, void* cptr, sass_context_wrapper* ctx_w, bool isFile) {
imagePath = new char[strlen(*bstr)+1]; int source_comments;
strcpy(imagePath, *bstr); Local<Object> options = Local<Object>::Cast(optionsValue);
if (ctx_w) { if (ctx_w) {
NanInitPersistent(Object, stats, Local<Object>::Cast(options->Get(NanSymbol("stats"))));
// async (callback) style // async (callback) style
Local<Function> callback = Local<Function>::Cast(args[2]); Local<Function> callback = Local<Function>::Cast(options->Get(NanSymbol("success")));
Local<Function> errorCallback = Local<Function>::Cast(args[3]); Local<Function> errorCallback = Local<Function>::Cast(options->Get(NanSymbol("error")));
if (isFile) { if (isFile) {
ctx_w->fctx = (sass_file_context*) cptr; ctx_w->fctx = (sass_file_context*) cptr;
} else { } else {
ctx_w->ctx = (sass_context*) cptr; ctx_w->ctx = (sass_context*) cptr;
} }
ctx_w->stats = stats;
ctx_w->request.data = ctx_w; ctx_w->request.data = ctx_w;
ctx_w->callback = new NanCallback(callback); ctx_w->callback = new NanCallback(callback);
ctx_w->errorCallback = new NanCallback(errorCallback); ctx_w->errorCallback = new NanCallback(errorCallback);
output_style = args[5]->Int32Value();
source_comments = args[6]->Int32Value();
String::AsciiValue cstr(args[4]);
pathOrData = new char[strlen(*cstr)+1];
strcpy(pathOrData, *cstr);
} else {
// synchronous style
output_style = args[3]->Int32Value();
source_comments = args[4]->Int32Value();
String::AsciiValue cstr(args[2]);
pathOrData = new char[strlen(*cstr)+1];
strcpy(pathOrData, *cstr);
} }
if (isFile) { if (isFile) {
sass_file_context *ctx = (sass_file_context*)cptr; sass_file_context* ctx = (sass_file_context*) cptr;
char *filename = new char[strlen(*astr)+1]; ctx->input_path = CreateString(options->Get(NanSymbol("file")));
strcpy(filename, *astr); ctx->options.image_path = CreateString(options->Get(NanSymbol("imagePath")));
ctx->input_path = filename; ctx->options.output_style = options->Get(NanSymbol("style"))->Int32Value();
ctx->options.image_path = imagePath; ctx->options.source_comments = source_comments = options->Get(NanSymbol("comments"))->Int32Value();
ctx->options.output_style = output_style; ctx->options.include_paths = CreateString(options->Get(NanSymbol("paths")));
ctx->options.source_comments = source_comments;
ctx->options.include_paths = pathOrData;
if (source_comments == SASS_SOURCE_COMMENTS_MAP) { if (source_comments == SASS_SOURCE_COMMENTS_MAP) {
String::AsciiValue dstr(args[7]); ctx->source_map_file = CreateString(options->Get(NanSymbol("sourceMap")));
ctx->source_map_file = new char[strlen(*dstr)+1];
strcpy((char*) ctx->source_map_file, *dstr);
} }
} else { } else {
sass_context *ctx = (sass_context*)cptr; sass_context* ctx = (sass_context*) cptr;
source = new char[strlen(*astr)+1]; ctx->source_string = CreateString(options->Get(NanSymbol("data")));
strcpy(source, *astr); ctx->options.image_path = CreateString(options->Get(NanSymbol("imagePath")));
ctx->source_string = source; ctx->options.output_style = options->Get(NanSymbol("style"))->Int32Value();
ctx->options.image_path = imagePath; ctx->options.source_comments = source_comments = options->Get(NanSymbol("comments"))->Int32Value();
ctx->options.output_style = output_style; ctx->options.include_paths = CreateString(options->Get(NanSymbol("paths")));
ctx->options.source_comments = source_comments; }
ctx->options.include_paths = pathOrData; }
template<typename Ctx>
void FillStatsObj(Handle<Object> stats, Ctx ctx) {
int i;
Handle<Array> arr;
arr = Array::New(ctx->num_included_files);
for (i = 0; i < ctx->num_included_files; i++) {
arr->Set(i, String::New(ctx->included_files[i]));
}
(*stats)->Set(NanSymbol("includedFiles"), arr);
}
void FillStatsObj(Handle<Object> stats, sass_file_context* ctx) {
Handle<Value> source_map;
FillStatsObj<sass_file_context*>(stats, ctx);
if (ctx->options.source_comments == SASS_SOURCE_COMMENTS_MAP) {
source_map = String::New(ctx->source_map_string);
} else {
source_map = Null();
} }
(*stats)->Set(NanSymbol("sourceMap"), source_map);
} }
void MakeCallback(uv_work_t* req) { void MakeCallback(uv_work_t* req) {
NanScope(); NanScope();
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);
Handle<Value> val, err; Handle<Value> val, err;
const unsigned argc = 2;
int error_status = ctx_w->ctx ? ctx_w->ctx->error_status : ctx_w->fctx->error_status; int error_status = ctx_w->ctx ? ctx_w->ctx->error_status : ctx_w->fctx->error_status;
if (ctx_w->ctx) {
FillStatsObj(ctx_w->stats, ctx_w->ctx);
} else {
FillStatsObj(ctx_w->stats, ctx_w->fctx);
}
if (error_status == 0) { if (error_status == 0) {
// if no error, do callback(null, result) // if no error, do callback(null, result)
Handle<Value> source_map;
if (ctx_w->fctx && ctx_w->fctx->options.source_comments == SASS_SOURCE_COMMENTS_MAP) {
source_map = String::New(ctx_w->fctx->source_map_string);
} else {
source_map = Null();
}
val = ctx_w->ctx ? NanNewLocal(String::New(ctx_w->ctx->output_string)) : NanNewLocal(String::New(ctx_w->fctx->output_string)); val = ctx_w->ctx ? NanNewLocal(String::New(ctx_w->ctx->output_string)) : NanNewLocal(String::New(ctx_w->fctx->output_string));
Local<Value> argv[argc] = { Local<Value> argv[] = {
NanNewLocal(val), NanNewLocal(val),
NanNewLocal(source_map), NanNewLocal(ctx_w->stats->Get(NanSymbol("sourceMap")))
}; };
ctx_w->callback->Call(argc, argv); ctx_w->callback->Call(2, argv);
} else { } else {
// if error, do callback(error) // if error, do callback(error)
err = ctx_w->ctx ? NanNewLocal(String::New(ctx_w->ctx->error_message)) : NanNewLocal(String::New(ctx_w->fctx->error_message)); err = ctx_w->ctx ? NanNewLocal(String::New(ctx_w->ctx->error_message)) : NanNewLocal(String::New(ctx_w->fctx->error_message));
Local<Value> argv[argc] = { Local<Value> argv[] = {
NanNewLocal(err), NanNewLocal(err),
NanNewLocal(Integer::New(error_status)) NanNewLocal(Integer::New(error_status))
}; };
ctx_w->errorCallback->Call(argc, argv); ctx_w->errorCallback->Call(2, argv);
} }
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
node::FatalException(try_catch); node::FatalException(try_catch);
} }
if (ctx_w->ctx) {
delete ctx_w->ctx->source_string;
} else {
delete ctx_w->fctx->input_path;
}
sass_free_context_wrapper(ctx_w); sass_free_context_wrapper(ctx_w);
} }
NAN_METHOD(Render) { NAN_METHOD(Render) {
NanScope(); NanScope();
sass_context* ctx = sass_new_context(); sass_context* ctx = sass_new_context();
sass_context_wrapper* ctx_w = sass_new_context_wrapper(); sass_context_wrapper* ctx_w = sass_new_context_wrapper();
ctx_w->ctx = ctx; ctx_w->ctx = ctx;
extractOptions(args, ctx, ctx_w, false); ExtractOptions(args[0], ctx, ctx_w, false);
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeCallback); int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeCallback);
assert(status == 0); assert(status == 0);
...@@ -142,25 +142,22 @@ NAN_METHOD(Render) { ...@@ -142,25 +142,22 @@ NAN_METHOD(Render) {
NAN_METHOD(RenderSync) { NAN_METHOD(RenderSync) {
NanScope(); NanScope();
Handle<Object> options = args[0]->ToObject();
sass_context* ctx = sass_new_context(); sass_context* ctx = sass_new_context();
extractOptions(args, ctx, NULL, false); ExtractOptions(args[0], ctx, NULL, false);
sass_compile(ctx); sass_compile(ctx);
delete ctx->source_string; FillStatsObj(options->Get(NanSymbol("stats"))->ToObject(), ctx);
ctx->source_string = NULL;
delete ctx->options.include_paths;
ctx->options.include_paths = NULL;
if (ctx->error_status == 0) { if (ctx->error_status == 0) {
Local<Value> output = NanNewLocal(String::New(ctx->output_string)); Local<Value> output = NanNewLocal(String::New(ctx->output_string));
sass_free_context(ctx); free_context(ctx);
NanReturnValue(output); NanReturnValue(output);
} }
Local<String> error = String::New(ctx->error_message); Local<String> error = String::New(ctx->error_message);
free_context(ctx);
sass_free_context(ctx);
NanThrowError(error); NanThrowError(error);
NanReturnUndefined(); NanReturnUndefined();
} }
...@@ -170,7 +167,7 @@ NAN_METHOD(RenderFile) { ...@@ -170,7 +167,7 @@ NAN_METHOD(RenderFile) {
sass_file_context* fctx = sass_new_file_context(); sass_file_context* fctx = sass_new_file_context();
sass_context_wrapper* ctx_w = sass_new_context_wrapper(); sass_context_wrapper* ctx_w = sass_new_context_wrapper();
ctx_w->fctx = fctx; ctx_w->fctx = fctx;
extractOptions(args, fctx, ctx_w, true); ExtractOptions(args[0], fctx, ctx_w, true);
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeCallback); int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeCallback);
assert(status == 0); assert(status == 0);
...@@ -181,24 +178,21 @@ NAN_METHOD(RenderFile) { ...@@ -181,24 +178,21 @@ NAN_METHOD(RenderFile) {
NAN_METHOD(RenderFileSync) { NAN_METHOD(RenderFileSync) {
NanScope(); NanScope();
sass_file_context* ctx = sass_new_file_context(); sass_file_context* ctx = sass_new_file_context();
extractOptions(args, ctx, NULL, true); ExtractOptions(args[0], ctx, NULL, true);
Handle<Object> options = args[0]->ToObject();
sass_compile_file(ctx); sass_compile_file(ctx);
delete ctx->input_path; FillStatsObj(options->Get(NanSymbol("stats"))->ToObject(), ctx);
ctx->input_path = NULL;
delete ctx->options.include_paths;
ctx->options.include_paths = NULL;
if (ctx->error_status == 0) { if (ctx->error_status == 0) {
Local<Value> output = NanNewLocal(String::New(ctx->output_string)); Local<Value> output = NanNewLocal(String::New(ctx->output_string));
sass_free_file_context(ctx); free_file_context(ctx);
NanReturnValue(output); NanReturnValue(output);
} }
Local<String> error = String::New(ctx->error_message);
sass_free_file_context(ctx);
Local<String> error = String::New(ctx->error_message);
free_file_context(ctx);
NanThrowError(error); NanThrowError(error);
NanReturnUndefined(); NanReturnUndefined();
} }
......
...@@ -3,14 +3,14 @@ var fs = require('fs'); ...@@ -3,14 +3,14 @@ var fs = require('fs');
function requireBinding() { function requireBinding() {
var v8 = 'v8-' + /[0-9]+\.[0-9]+/.exec(process.versions.v8)[0]; var v8 = 'v8-' + /[0-9]+\.[0-9]+/.exec(process.versions.v8)[0];
var candidates = [ var candidates = [
[__dirname, 'build', 'Release', 'obj.target', 'binding.node'], [__dirname, 'build', 'Release', 'obj.target', 'binding.node'],
[__dirname, 'bin', process.platform + '-' + process.arch + '-' + v8, 'binding.node'], [__dirname, 'bin', process.platform + '-' + process.arch + '-' + v8, 'binding.node']
]; ];
var candidate;
for (var i = 0, l = candidates.length; i < l; i++) { for (var i = 0, l = candidates.length; i < l; i++) {
var candidate = path.join.apply(path.join, candidates[i]); candidate = path.join.apply(path.join, candidates[i]);
if (fs.existsSync(candidate)) { if (fs.existsSync(candidate)) {
return require(candidate); return require(candidate);
...@@ -23,88 +23,109 @@ function requireBinding() { ...@@ -23,88 +23,109 @@ function requireBinding() {
var binding = requireBinding(); var binding = requireBinding();
var SASS_OUTPUT_STYLE = { var SASS_OUTPUT_STYLE = {
nested: 0, nested: 0,
expanded: 1, expanded: 1,
compact: 2, compact: 2,
compressed: 3 compressed: 3
}; };
var SASS_SOURCE_COMMENTS = { var SASS_SOURCE_COMMENTS = {
none: 0, none: 0,
// This is called default in libsass, but is a reserved keyword here normal: 1,
normal: 1, 'default': 1,
map: 2 map: 2
}; };
var prepareOptions = function (options) {
var success;
var error;
var stats;
var sourceComments;
var prepareOptions = function(options) { options = options || {};
var paths, imagePath, style, comments; success = options.success;
options = typeof options !== 'object' ? {} : options; error = options.error;
var sourceComments = options.source_comments || options.sourceComments; stats = options.stats || {};
sourceComments = options.source_comments || options.sourceComments;
if (options.sourceMap && !sourceComments) { if (options.sourceMap && !sourceComments) {
sourceComments = 'map'; sourceComments = 'map';
} }
paths = options.include_paths || options.includePaths || []; prepareStats(options, stats);
imagePath = options.image_path || options.imagePath || '';
style = SASS_OUTPUT_STYLE[options.output_style || options.outputStyle] || 0;
comments = SASS_SOURCE_COMMENTS[sourceComments] || 0;
return { return {
paths: paths, file: options.file || null,
imagePath: imagePath, outFile: options.outFile || null,
style: style, data: options.data || null,
comments: comments paths: (options.include_paths || options.includePaths || []).join(path.delimiter),
imagePath: options.image_path || options.imagePath || '',
style: SASS_OUTPUT_STYLE[options.output_style || options.outputStyle] || 0,
comments: SASS_SOURCE_COMMENTS[sourceComments] || 0,
stats: stats,
sourceMap: options.sourceMap,
success: function onSuccess(css, sourceMap) {
finishStats(stats, sourceMap);
success && success(css, sourceMap);
},
error: function onError(err, errStatus) {
error && error(err, errStatus);
}
}; };
}; };
var prepareStats = function (options, stats) {
stats.entry = options.file || 'data';
stats.start = Date.now();
return stats;
};
var finishStats = function (stats, sourceMap) {
stats.end = Date.now();
stats.duration = stats.end - stats.start;
stats.sourceMap = sourceMap;
return stats;
};
var deprecatedRender = function(css, callback, options) { var deprecatedRender = function(css, callback, options) {
options = prepareOptions(options); options = prepareOptions(options);
var errCallback = function(err) { // providing the deprecated single callback signature
callback(err); options.error = callback;
}; options.success = function(css) {
var oldCallback = function(css) {
callback(null, css); callback(null, css);
}; };
return binding.render(css, options.imagePath, oldCallback, errCallback, options.paths.join(path.delimiter), options.style, options.comments); options.data = css;
binding.render(options);
}; };
var deprecatedRenderSync = function(css, options) { var deprecatedRenderSync = function(css, options) {
options = prepareOptions(options); options = prepareOptions(options);
return binding.renderSync(css, options.imagePath, options.paths.join(path.delimiter), options.style, options.comments); options.data = css;
return binding.renderSync(options);
}; };
exports.render = function(options) { exports.render = function(options) {
var newOptions;
if (typeof arguments[0] === 'string') { if (typeof arguments[0] === 'string') {
return deprecatedRender.apply(this, arguments); return deprecatedRender.apply(this, arguments);
} }
newOptions = prepareOptions(options); options = prepareOptions(options);
options.error = options.error || function(){}; options.file? binding.renderFile(options) : binding.render(options);
if (options.file !== undefined && options.file !== null) {
return binding.renderFile(options.file, newOptions.imagePath, options.success, options.error, newOptions.paths.join(path.delimiter), newOptions.style, newOptions.comments, options.sourceMap);
}
//Assume data is present if file is not. binding/libsass will tell the user otherwise!
return binding.render(options.data, newOptions.imagePath, options.success, options.error, newOptions.paths.join(path.delimiter), newOptions.style);
}; };
exports.renderSync = function(options) { exports.renderSync = function(options) {
var newOptions; var output;
if (typeof arguments[0] === 'string') { if (typeof arguments[0] === 'string') {
return deprecatedRenderSync.apply(this, arguments); return deprecatedRenderSync.apply(this, arguments);
} }
newOptions = prepareOptions(options); options = prepareOptions(options);
output = options.file? binding.renderFileSync(options) : binding.renderSync(options);
if (options.file !== undefined && options.file !== null) { finishStats(options.stats);
return binding.renderFileSync(options.file, newOptions.imagePath, newOptions.paths.join(path.delimiter), newOptions.style, newOptions.comments);
}
//Assume data is present if file is not. binding/libsass will tell the user otherwise! return output;
return binding.renderSync(options.data, newOptions.imagePath, newOptions.paths.join(path.delimiter), newOptions.style);
}; };
/** /**
...@@ -120,26 +141,26 @@ exports.renderSync = function(options) { ...@@ -120,26 +141,26 @@ exports.renderSync = function(options) {
where the source map should be saved where the source map should be saved
*/ */
exports.renderFile = function(options) { exports.renderFile = function(options) {
var newOptions = {}; var success;
for (var i in options) {
if (options.hasOwnProperty(i)) { options = prepareOptions(options);
newOptions[i] = options[i]; success = options.success;
}
}
if (options.sourceMap === true) { if (options.sourceMap === true) {
newOptions.sourceMap = path.basename(options.outFile) + '.map'; options.sourceMap = path.basename(options.outFile) + '.map';
} }
newOptions.success = function(css, sourceMap) { options.success = function(css, sourceMap) {
fs.writeFile(options.outFile, css, function(err) { fs.writeFile(options.outFile, css, function(err) {
var dir, sourceMapFile;
if (err) { if (err) {
return error(err); return options.error(err);
} }
if (options.sourceMap) { if (options.sourceMap) {
var dir = path.dirname(options.outFile); dir = path.dirname(options.outFile);
var sourceMapFile = path.resolve(dir, newOptions.sourceMap); sourceMapFile = path.resolve(dir, options.sourceMap);
fs.writeFile(sourceMapFile, sourceMap, function(err) { fs.writeFile(sourceMapFile, sourceMap, function(err) {
if (err) { if (err) {
return error(err); return options.error(err);
} }
success(options.outFile, sourceMapFile); success(options.outFile, sourceMapFile);
}); });
...@@ -149,17 +170,7 @@ exports.renderFile = function(options) { ...@@ -149,17 +170,7 @@ exports.renderFile = function(options) {
} }
}); });
}; };
function error(err) { exports.render(options);
if (options.error) {
options.error(err);
}
}
function success(css, sourceMap) {
if (options.success) {
options.success(css, sourceMap);
}
}
exports.render(newOptions);
}; };
exports.middleware = require('./lib/middleware'); exports.middleware = require('./lib/middleware');
#include "sass_context_wrapper.h" #include "sass_context_wrapper.h"
#include <nan.h>
#include <cstdlib> #include <cstdlib>
extern "C" { extern "C" {
using namespace std; using namespace std;
sass_context_wrapper* sass_new_context_wrapper() void free_context(sass_context* ctx) {
{ delete[] ctx->source_string;
delete[] ctx->options.include_paths;
delete[] ctx->options.image_path;
sass_free_context(ctx);
}
void free_file_context(sass_file_context* fctx) {
delete[] fctx->input_path;
delete[] fctx->output_path;
delete[] fctx->options.include_paths;
delete[] fctx->options.image_path;
sass_free_file_context(fctx);
}
sass_context_wrapper* sass_new_context_wrapper() {
return (sass_context_wrapper*) calloc(1, sizeof(sass_context_wrapper)); return (sass_context_wrapper*) calloc(1, sizeof(sass_context_wrapper));
} }
void sass_free_context_wrapper(sass_context_wrapper* ctx_w) void sass_free_context_wrapper(sass_context_wrapper* ctx_w) {
{ if (ctx_w->ctx) {
if (ctx_w->ctx) sass_free_context(ctx_w->ctx); free_context(ctx_w->ctx);
if (ctx_w->fctx) sass_free_file_context(ctx_w->fctx); } else if (ctx_w->fctx) {
free_file_context(ctx_w->fctx);
}
NanDisposePersistent(ctx_w->stats);
delete ctx_w->callback; delete ctx_w->callback;
delete ctx_w->errorCallback; delete ctx_w->errorCallback;
......
...@@ -5,9 +5,15 @@ ...@@ -5,9 +5,15 @@
extern "C" { extern "C" {
#endif #endif
using namespace v8;
void free_context(sass_context* ctx);
void free_file_context(sass_file_context* fctx);
struct sass_context_wrapper { struct sass_context_wrapper {
sass_context* ctx; sass_context* ctx;
sass_file_context* fctx; sass_file_context* fctx;
Persistent<Object> stats;
uv_work_t request; uv_work_t request;
NanCallback* callback; NanCallback* callback;
NanCallback* errorCallback; NanCallback* errorCallback;
......
...@@ -39,7 +39,8 @@ describe('cli', function() { ...@@ -39,7 +39,8 @@ describe('cli', function() {
exec('node ' + cliPath + ' ' + sampleFilename, { exec('node ' + cliPath + ' ' + sampleFilename, {
cwd: __dirname cwd: __dirname
}, function() { }, function(err) {
assert.equal(err, null);
fs.exists(resultPath, function(exists) { fs.exists(resultPath, function(exists) {
assert(exists); assert(exists);
...@@ -53,7 +54,8 @@ describe('cli', function() { ...@@ -53,7 +54,8 @@ describe('cli', function() {
exec('node ' + cliPath + ' ' + sampleFilename + ' ../out.css', { exec('node ' + cliPath + ' ' + sampleFilename + ' ../out.css', {
cwd: __dirname cwd: __dirname
}, function() { }, function(err) {
assert.equal(err, null);
fs.exists(resultPath, function(exists) { fs.exists(resultPath, function(exists) {
assert(exists); assert(exists);
......
@import "sample.scss";
@import "image_path.scss";
exports.input = '#navbar {\
width: 80%;\
height: 23px; }\
#navbar ul {\
list-style-type: none; }\
#navbar li {\
float: left;\
a {\
font-weight: bold; }}\
@mixin keyAnimation($name, $attr, $value) {\
@-webkit-keyframes #{$name} {\
0% { #{$attr}: $value; }\
}\
}';
// Note that the bad
exports.badInput = '#navbar \n\
width: 80%';
exports.expectedRender = '#navbar {\n\
width: 80%;\n\
height: 23px; }\n\
\n\
#navbar ul {\n\
list-style-type: none; }\n\
\n\
#navbar li {\n\
float: left; }\n\
#navbar li a {\n\
font-weight: bold; }\n';
\ No newline at end of file
'use strict';
var path = require('path');
var assert = require('assert');
var sass = process.env.NODESASS_COVERAGE ? require('../sass-coverage') : require('../sass');
var includedFilesFile = path.resolve(__dirname, 'included_files.scss');
var sampleFile = path.resolve(__dirname, 'sample.scss');
var imagePathFile = path.resolve(__dirname, 'image_path.scss');
var sample = require('./sample.js');
describe('stats', function() {
var start = Date.now();
var stats;
function checkTimingStats() {
it('should provide a start timestamp', function() {
assert.ok(typeof stats.start === 'number');
assert.ok(stats.start >= start);
});
it('should provide an end timestamp', function() {
assert.ok(typeof stats.end === 'number');
assert.ok(stats.end >= stats.start);
});
it('should provide a duration', function() {
assert.ok(typeof stats.duration === 'number');
assert.equal(stats.end - stats.start, stats.duration);
});
}
describe('using renderSync()', function() {
describe('and file-context', function() {
before(function() {
sass.renderSync({
file: includedFilesFile,
stats: stats = {}
});
});
checkTimingStats();
it('should contain the given entry file', function() {
assert.equal(stats.entry, includedFilesFile);
});
it('should contain an array of all included files', function() {
// the included_files aren't sorted by libsass in any way
assert.deepEqual(
stats.includedFiles.sort(),
[includedFilesFile, sampleFile, imagePathFile].sort()
);
});
it('should contain an array with the entry-file if the there are no import statements', function () {
sass.renderSync({
file: sampleFile,
stats: stats = {}
});
assert.deepEqual(stats.includedFiles, [sampleFile]);
});
});
describe('and data-context', function() {
before(function() {
sass.renderSync({
data: sample.input,
stats: stats = {}
});
});
checkTimingStats();
it('should state "data" as entry file', function() {
assert.equal(stats.entry, 'data');
});
it('should contain an empty array as includedFiles in the data-context', function() {
assert.deepEqual(stats.includedFiles, []);
});
});
});
describe('using render()', function () {
describe('and file-context', function() {
before(function(done) {
sass.render({
file: includedFilesFile,
stats: stats = {},
success: function() {
done();
},
error: done
});
});
checkTimingStats();
it('should contain the given entry file', function() {
assert.equal(stats.entry, includedFilesFile);
});
it('should contain an array of all included files', function() {
// the included_files aren't sorted by libsass in any way
assert.deepEqual(
stats.includedFiles.sort(),
[includedFilesFile, sampleFile, imagePathFile].sort()
);
});
it('should contain an array with the entry-file if the there are no import statements', function(done) {
sass.render({
file: sampleFile,
stats: stats = {},
success: function() {
assert.deepEqual(stats.includedFiles, [sampleFile]);
done();
},
error: done
});
});
});
describe('and data-context', function() {
before(function(done) {
sass.render({
data: sample.input,
stats: stats = {},
success: function() {
done();
},
error: done
});
});
checkTimingStats();
it('should state "data" as entry file', function() {
assert.equal(stats.entry, 'data');
});
it('should contain an empty array as includedFiles in the data-context', function() {
assert.deepEqual(stats.includedFiles, []);
});
});
});
});
\ No newline at end of file
...@@ -5,53 +5,25 @@ var fs = require('fs'); ...@@ -5,53 +5,25 @@ var fs = require('fs');
var sinon = require('sinon'); var sinon = require('sinon');
var badSampleFilename = 'sample.scss'; var badSampleFilename = 'sample.scss';
var sampleFilename = path.resolve(__dirname, 'sample.scss'); var sampleFilename = path.resolve(__dirname, 'sample.scss');
var sample = require('./sample.js');
var scssStr = '#navbar {\
width: 80%;\
height: 23px; }\
#navbar ul {\
list-style-type: none; }\
#navbar li {\
float: left;\
a {\
font-weight: bold; }}\
@mixin keyAnimation($name, $attr, $value) {\
@-webkit-keyframes #{$name} {\
0% { #{$attr}: $value; }\
}\
}';
// Note that the bad
var badInput = '#navbar \n\
width: 80%';
var expectedRender = '#navbar {\n\
width: 80%;\n\
height: 23px; }\n\
\n\
#navbar ul {\n\
list-style-type: none; }\n\
\n\
#navbar li {\n\
float: left; }\n\
#navbar li a {\n\
font-weight: bold; }\n';
describe('DEPRECATED: compile scss', function() { describe('DEPRECATED: compile scss', function() {
it('should compile with render', function(done) { it('should compile with render', function(done) {
sass.render(scssStr, function(err) { sass.render(sample.input, function(err) {
done(err); done(err);
}); });
}); });
it('should compile with renderSync', function(done) { it('should compile with renderSync', function(done) {
done(assert.ok(sass.renderSync(scssStr))); done(assert.ok(sass.renderSync(sample.input)));
}); });
it('should match compiled string with render', function(done) { it('should match compiled string with render', function(done) {
sass.render(scssStr, function(err, css) { sass.render(sample.input, function(err, css) {
if (!err) { if (!err) {
done(assert.equal(css, expectedRender)); done(assert.equal(css, sample.expectedRender));
} else { } else {
done(err); done(err);
} }
...@@ -59,12 +31,12 @@ describe('DEPRECATED: compile scss', function() { ...@@ -59,12 +31,12 @@ describe('DEPRECATED: compile scss', function() {
}); });
it('should match compiled string with renderSync', function(done) { it('should match compiled string with renderSync', function(done) {
done(assert.equal(sass.renderSync(scssStr), expectedRender)); done(assert.equal(sass.renderSync(sample.input), sample.expectedRender));
}); });
it('should throw an exception for bad input', function(done) { it('should throw an exception for bad input', function(done) {
done(assert.throws(function() { done(assert.throws(function() {
sass.renderSync(badInput); sass.renderSync(sample.badInput);
})); }));
}); });
}); });
...@@ -72,7 +44,7 @@ describe('DEPRECATED: compile scss', function() { ...@@ -72,7 +44,7 @@ describe('DEPRECATED: compile scss', function() {
describe('compile scss', function() { describe('compile scss', function() {
it('should compile with render', function(done) { it('should compile with render', function(done) {
sass.render({ sass.render({
data: scssStr, data: sample.input,
success: function(css) { success: function(css) {
done(assert.ok(css)); done(assert.ok(css));
} }
...@@ -80,14 +52,14 @@ describe('compile scss', function() { ...@@ -80,14 +52,14 @@ describe('compile scss', function() {
}); });
it('should compile with renderSync', function(done) { it('should compile with renderSync', function(done) {
done(assert.ok(sass.renderSync({data: scssStr}))); done(assert.ok(sass.renderSync({data: sample.input})));
}); });
it('should match compiled string with render', function(done) { it('should match compiled string with render', function(done) {
sass.render({ sass.render({
data: scssStr, data: sample.input,
success: function(css) { success: function(css) {
done(assert.equal(css, expectedRender)); done(assert.equal(css, sample.expectedRender));
}, },
error: function(error) { error: function(error) {
done(error); done(error);
...@@ -109,12 +81,12 @@ describe('compile scss', function() { ...@@ -109,12 +81,12 @@ describe('compile scss', function() {
}); });
it('should match compiled string with renderSync', function(done) { it('should match compiled string with renderSync', function(done) {
done(assert.equal(sass.renderSync({data: scssStr}), expectedRender)); done(assert.equal(sass.renderSync({data: sample.input}), sample.expectedRender));
}); });
it('should throw an exception for bad input', function(done) { it('should throw an exception for bad input', function(done) {
done(assert.throws(function() { done(assert.throws(function() {
sass.renderSync({data: badInput}); sass.renderSync({data: sample.badInput});
})); }));
}); });
}); });
...@@ -140,7 +112,7 @@ describe('compile file with image path', function(){ ...@@ -140,7 +112,7 @@ describe('compile file with image path', function(){
file: path.resolve(__dirname, 'image_path.scss'), file: path.resolve(__dirname, 'image_path.scss'),
imagePath: '/path/to/images', imagePath: '/path/to/images',
success: function (css) { success: function (css) {
done(assert.equal(css, 'body {\n background-image: url(\"/path/to/images/image.png\"); }\n')); done(assert.equal(css, 'body {\n background-image: url("/path/to/images/image.png"); }\n'));
}, },
error: function (error) { error: function (error) {
done(error); done(error);
...@@ -154,7 +126,7 @@ describe('compile file', function() { ...@@ -154,7 +126,7 @@ describe('compile file', function() {
sass.render({ sass.render({
file: sampleFilename, file: sampleFilename,
success: function (css) { success: function (css) {
done(assert.equal(css, expectedRender)); done(assert.equal(css, sample.expectedRender));
}, },
error: function (error) { error: function (error) {
done(error); done(error);
...@@ -170,7 +142,7 @@ describe('compile file', function() { ...@@ -170,7 +142,7 @@ describe('compile file', function() {
sass.render({ sass.render({
file: sampleFilename, file: sampleFilename,
success: function(css) { success: function(css) {
done(assert.equal(css, expectedRender)); done(assert.equal(css, sample.expectedRender));
}, },
error: function(error) { error: function(error) {
done(error); done(error);
...@@ -179,7 +151,7 @@ describe('compile file', function() { ...@@ -179,7 +151,7 @@ describe('compile file', function() {
}); });
it('should match compiled string with renderSync', function(done) { it('should match compiled string with renderSync', function(done) {
done(assert.equal(sass.renderSync({file: sampleFilename}), expectedRender)); done(assert.equal(sass.renderSync({file: sampleFilename}), sample.expectedRender));
}); });
it('should throw an exception for bad input', function(done) { it('should throw an exception for bad input', function(done) {
...@@ -209,7 +181,7 @@ describe('render to file', function() { ...@@ -209,7 +181,7 @@ describe('render to file', function() {
outFile: outFile, outFile: outFile,
success: function () { success: function () {
var contents = filesWritten[outFile]; var contents = filesWritten[outFile];
done(assert.equal(contents, expectedRender)); done(assert.equal(contents, sample.expectedRender));
}, },
error: function (error) { error: function (error) {
done(error); done(error);
...@@ -272,4 +244,4 @@ describe('render to file', function() { ...@@ -272,4 +244,4 @@ describe('render to file', function() {
}); });
}); });
}); });
\ No newline at end of file
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