Skip to content

Commit

Permalink
Importer: General overhauling.. 🐛🪲
Browse files Browse the repository at this point in the history
* Fixes the async crashing by explicitly
  closing async.
* Changes the behavior of renderSync importer
* Updates index accordingly.
* Removes obsolete tests accordingly.
BIG THANKS to @txdv and this addresses sass#586 ! 🎉👍
  • Loading branch information
am11 committed Jan 5, 2015
1 parent a5067a6 commit fc969ff
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 174 deletions.
4 changes: 1 addition & 3 deletions bin/node-sass
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ function getEmitter() {
console.log(data);
});

emitter.on('done', function(){
process.exit(0);
});
emitter.on('done', process.exit);

return emitter;
}
Expand Down
45 changes: 27 additions & 18 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ function getOptions(options) {

var error = options.error;
var success = options.success;
var importer = options.importer;

options.error = function(err, code) {
try {
Expand Down Expand Up @@ -173,23 +172,6 @@ function getOptions(options) {
}
};

if (importer) {
options.importer = function(file, prev, key) {
var done = function(data) {
binding.importedCallback({
index: key,
objectLiteral: data
});
};

var result = importer(file, prev, done);

if (result) {
done(result);
}
};
}

delete options.image_path;
delete options.include_paths;
delete options.includePaths;
Expand Down Expand Up @@ -219,6 +201,25 @@ var binding = require(getBinding());
module.exports.render = function(options) {
options = getOptions(options);

var importer = options.importer;

if (importer) {
options.importer = function(file, prev, key) {
function done(data) {
binding.importedCallback({
index: key,
objectLiteral: data
});
}

var result = importer(file, prev, done);

if (result) {
done(result);
}
};
}

options.data ? binding.render(options) : binding.renderFile(options);
};

Expand All @@ -232,6 +233,14 @@ module.exports.render = function(options) {
module.exports.renderSync = function(options) {
options = getOptions(options);

var importer = options.importer;

if (importer) {
options.importer = function(file, prev) {
return { objectLiteral: importer(file, prev) };
};
}

var status = options.data ? binding.renderSync(options) : binding.renderFileSync(options);
var result = options.result;

Expand Down
135 changes: 70 additions & 65 deletions src/binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

char* CreateString(Local<Value> value) {
if (value->IsNull() || !value->IsString()) {
return const_cast<char*>(""); // return empty string.
return 0;
}

String::Utf8Value string(value);
Expand All @@ -15,6 +15,41 @@ char* CreateString(Local<Value> value) {

std::vector<sass_context_wrapper*> imports_collection;

void prepare_import_results(Local<Value> returned_value, sass_context_wrapper* ctx_w) {
NanScope();

if (returned_value->IsArray()) {
Handle<Array> array = Handle<Array>::Cast(returned_value);

ctx_w->imports = sass_make_import_list(array->Length());

for (size_t i = 0; i < array->Length(); ++i) {
Local<Value> value = array->Get(i);

if (!value->IsObject())
continue;

Local<Object> object = Local<Object>::Cast(value);
char* path = CreateString(object->Get(String::New("file")));
char* contents = CreateString(object->Get(String::New("contents")));

ctx_w->imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
}
else if (returned_value->IsObject()) {
ctx_w->imports = sass_make_import_list(1);
Local<Object> object = Local<Object>::Cast(returned_value);
char* path = CreateString(object->Get(String::New("file")));
char* contents = CreateString(object->Get(String::New("contents")));

ctx_w->imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
else {
ctx_w->imports = sass_make_import_list(1);
ctx_w->imports[0] = sass_make_import_entry(ctx_w->file, 0, 0);
}
}

void dispatched_async_uv_callback(uv_async_t *req) {
NanScope();
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
Expand Down Expand Up @@ -42,39 +77,47 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo

ctx_w->file = file ? strdup(file) : 0;
ctx_w->prev = prev ? strdup(prev) : 0;
ctx_w->async.data = (void*)ctx_w;
uv_async_send(&ctx_w->async);

if (ctx_w->success_callback) {
/* that is async: Render() or RenderFile(),
* the default even loop is unblocked so it
* can run uv_async_send without a push.
*/
ctx_w->async.data = (void*)ctx_w;
uv_async_send(&ctx_w->async);
uv_cond_wait(&ctx_w->importer_condition_variable, &ctx_w->importer_mutex);
}
else {
/* that is sync: RenderSync() or RenderFileSync,
* we need to explicitly uv_run as the event loop
* is blocked; waiting down the chain.
*/
uv_run(ctx_w->async.loop, UV_RUN_DEFAULT);
NanScope();

Handle<Value> argv[] = {
NanNew<String>(strdup(ctx_w->file ? ctx_w->file : 0)),
NanNew<String>(strdup(ctx_w->prev ? ctx_w->prev : 0)),
NanNew<Number>(imports_collection.size() - 1)
};

Local<Object> returned_value = Local<Object>::Cast(NanNew<Value>(ctx_w->importer_callback->Call(3, argv)));

prepare_import_results(returned_value->Get(NanNew("objectLiteral")), ctx_w);
}

return ctx_w->imports;
}

void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile, bool isSync) {
NanScope();

struct Sass_Context* ctx;

NanAssignPersistent(ctx_w->result, options->Get(NanNew("result"))->ToObject());

if (isFile) {
ctx = sass_file_context_get_context((struct Sass_File_Context*) cptr);
ctx_w->fctx = (struct Sass_File_Context*) cptr;
ctx = sass_file_context_get_context(ctx_w->fctx);
}
else {
ctx = sass_data_context_get_context((struct Sass_Data_Context*) cptr);
ctx_w->dctx = (struct Sass_Data_Context*) cptr;
ctx = sass_data_context_get_context(ctx_w->dctx);
}

struct Sass_Options* sass_options = sass_context_get_options(ctx);
Expand All @@ -92,9 +135,10 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx

Local<Function> importer_callback = Local<Function>::Cast(options->Get(NanNew("importer")));

ctx_w->importer_callback = new NanCallback(importer_callback);
ctx_w->importer_callback = NULL;

if (!importer_callback->IsUndefined()) {
ctx_w->importer_callback = new NanCallback(importer_callback);
uv_async_init(uv_default_loop(), &ctx_w->async, (uv_async_cb)dispatched_async_uv_callback);
sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w));
}
Expand All @@ -114,6 +158,8 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
}

void GetStats(sass_context_wrapper* ctx_w, Sass_Context* ctx) {
NanScope();

char** included_files = sass_context_get_included_files(ctx);
Handle<Array> arr = NanNew<Array>();

Expand All @@ -123,13 +169,12 @@ void GetStats(sass_context_wrapper* ctx_w, Sass_Context* ctx) {
}
}

Local<Object> obj = NanNew(ctx_w->result);
obj->Get(NanNew("stats"))->ToObject()->Set(NanNew("includedFiles"), arr);

NanAssignPersistent(ctx_w->result, obj);
NanNew(ctx_w->result)->Get(NanNew("stats"))->ToObject()->Set(NanNew("includedFiles"), arr);
}

void GetSourceMap(sass_context_wrapper* ctx_w, Sass_Context* ctx) {
NanScope();

Handle<Value> source_map;

if (sass_context_get_error_status(ctx)) {
Expand All @@ -143,20 +188,16 @@ void GetSourceMap(sass_context_wrapper* ctx_w, Sass_Context* ctx) {
source_map = NanNew<String>("{}");
}

Local<Object> obj = NanNew(ctx_w->result);
obj->Set(NanNew("sourceMap"), source_map);

NanAssignPersistent(ctx_w->result, obj);
NanNew(ctx_w->result)->Set(NanNew("sourceMap"), source_map);
}

int GetResult(sass_context_wrapper* ctx_w, Sass_Context* ctx) {
NanScope();

int status = sass_context_get_error_status(ctx);

if (status == 0) {
Local<Object> obj = NanNew(ctx_w->result);
obj->Set(NanNew("css"), NanNew<String>(sass_context_get_output_string(ctx)));

NanAssignPersistent(ctx_w->result, obj);
NanNew(ctx_w->result)->Set(NanNew("css"), NanNew<String>(sass_context_get_output_string(ctx)));

GetStats(ctx_w, ctx);
GetSourceMap(ctx_w, ctx);
Expand Down Expand Up @@ -198,6 +239,10 @@ void make_callback(uv_work_t* req) {
node::FatalException(try_catch);
}

if (ctx_w->importer_callback) {
uv_close((uv_handle_t*)&ctx_w->async, NULL);
}

sass_free_context_wrapper(ctx_w);
}

Expand Down Expand Up @@ -228,6 +273,7 @@ NAN_METHOD(RenderSync) {
sass_context_wrapper* ctx_w = sass_make_context_wrapper();

ExtractOptions(options, dctx, ctx_w, false, true);

compile_data(dctx);

int result = GetResult(ctx_w, ctx);
Expand All @@ -238,7 +284,6 @@ NAN_METHOD(RenderSync) {
}

sass_free_context_wrapper(ctx_w);
free(source_string);

if (result != 0) {
NanThrowError(error);
Expand All @@ -260,7 +305,6 @@ NAN_METHOD(RenderFile) {
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)make_callback);

assert(status == 0);
free(input_path);

NanReturnUndefined();
}
Expand All @@ -285,7 +329,6 @@ NAN_METHOD(RenderFileSync) {
}

sass_free_context_wrapper(ctx_w);
free(input_path);

if (result != 0) {
NanThrowError(error);
Expand All @@ -309,51 +352,13 @@ NAN_METHOD(ImportedCallback) {

sass_context_wrapper* ctx_w = imports_collection[index];

if (returned_value->IsArray()) {
Handle<Array> array = Handle<Array>::Cast(returned_value);

ctx_w->imports = sass_make_import_list(array->Length());

for (size_t i = 0; i < array->Length(); ++i) {
Local<Value> value = array->Get(i);

if (!value->IsObject())
continue;

Local<Object> object = Local<Object>::Cast(value);
char* path = CreateString(object->Get(String::New("file")));
char* contents = CreateString(object->Get(String::New("contents")));

ctx_w->imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
}
else if (returned_value->IsObject()) {
ctx_w->imports = sass_make_import_list(1);
Local<Object> object = Local<Object>::Cast(returned_value);
char* path = CreateString(object->Get(String::New("file")));
char* contents = CreateString(object->Get(String::New("contents")));

ctx_w->imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
else {
ctx_w->imports = sass_make_import_list(1);
ctx_w->imports[0] = sass_make_import_entry(ctx_w->file, 0, 0);
}

prepare_import_results(returned_value, ctx_w);
uv_cond_signal(&ctx_w->importer_condition_variable);

if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}

if (!ctx_w->success_callback) {
/*
* that is sync: RenderSync() or RenderFileSync,
* we ran it explictly, so we stop it similarly.
*/
uv_stop(ctx_w->async.loop);
}

NanReturnValue(NanNew<Number>(0));
}

Expand Down
5 changes: 3 additions & 2 deletions src/sass_context_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern "C" {
using namespace std;

void compile_it(uv_work_t* req) {
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
sass_context_wrapper* ctx_w = (sass_context_wrapper*)req->data;

if (ctx_w->dctx) {
compile_data(ctx_w->dctx);
Expand All @@ -23,9 +23,10 @@ extern "C" {
}

sass_context_wrapper* sass_make_context_wrapper() {
auto ctx_w = (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper));
sass_context_wrapper* ctx_w = (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper));
uv_mutex_init(&ctx_w->importer_mutex);
uv_cond_init(&ctx_w->importer_condition_variable);

return ctx_w;
}

Expand Down
Loading

0 comments on commit fc969ff

Please sign in to comment.