Commit 4bea0703 by Adeel Mujahid

Merge pull request #576 from am11/importer

Feature: Custom importer (#530)
parents 7c354bf6 5a6bd67f
...@@ -150,6 +150,7 @@ function getOptions(options) { ...@@ -150,6 +150,7 @@ function getOptions(options) {
var error = options.error; var error = options.error;
var success = options.success; var success = options.success;
var importer = options.importer;
options.error = function(err, code) { options.error = function(err, code) {
try { try {
...@@ -173,6 +174,17 @@ function getOptions(options) { ...@@ -173,6 +174,17 @@ function getOptions(options) {
} }
}; };
if (importer) {
options.importer = function(file, prev, key) {
importer(file, prev, function(data) {
binding.importedCallback({
index: key,
objectLiteral: data
});
});
};
}
delete options.image_path; delete options.image_path;
delete options.include_paths; delete options.include_paths;
delete options.includePaths; delete options.includePaths;
......
#include <nan.h> #include <nan.h>
#include <vector>
#include "sass_context_wrapper.h" #include "sass_context_wrapper.h"
char* CreateString(Local<Value> value) { char* CreateString(Local<Value> value) {
if(value->IsNull() || !value->IsString()) { if (value->IsNull() || !value->IsString()) {
return const_cast<char*>(""); // return empty string. return const_cast<char*>(""); // return empty string.
} }
String::Utf8Value string(value); String::Utf8Value string(value);
char *str = (char *) malloc(string.length() + 1); char *str = (char *)malloc(string.length() + 1);
strcpy(str, *string); strcpy(str, *string);
return str; return str;
} }
void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile) { std::vector<sass_context_wrapper*> imports_collection;
if (ctx_w) {
NanAssignPersistent(ctx_w->stats, options->Get(NanNew("stats"))->ToObject());
// async (callback) style void dispatched_async_uv_callback(uv_async_t *req){
Local<Function> callback = Local<Function>::Cast(options->Get(NanNew("success"))); NanScope();
Local<Function> errorCallback = Local<Function>::Cast(options->Get(NanNew("error"))); sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
if (isFile) {
ctx_w->fctx = (struct Sass_File_Context*) cptr; TryCatch try_catch;
} else {
ctx_w->dctx = (struct Sass_Data_Context*) cptr; imports_collection.push_back(ctx_w);
}
ctx_w->request.data = ctx_w; Handle<Value> argv[] = {
ctx_w->callback = new NanCallback(callback); NanNew<String>(strdup(ctx_w->file)),
ctx_w->errorCallback = new NanCallback(errorCallback); NanNew<String>(strdup(ctx_w->prev)),
NanNew<Number>(imports_collection.size() - 1)
};
NanNew<Value>(ctx_w->importer_callback->Call(3, argv));
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
struct Sass_Import** sass_importer(const char* file, const char* prev, void* cookie)
{
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(cookie);
ctx_w->file = strdup(file);
ctx_w->prev = strdup(prev);
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.
*/
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);
} }
return ctx_w->imports;
}
void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile, bool isSync) {
struct Sass_Context* ctx; struct Sass_Context* ctx;
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);
} else { ctx_w->fctx = (struct Sass_File_Context*) cptr;
}
else {
ctx = sass_data_context_get_context((struct Sass_Data_Context*) cptr); ctx = sass_data_context_get_context((struct Sass_Data_Context*) cptr);
ctx_w->dctx = (struct Sass_Data_Context*) cptr;
} }
struct Sass_Options* sass_options = sass_context_get_options(ctx); struct Sass_Options* sass_options = sass_context_get_options(ctx);
if (!isSync) {
NanAssignPersistent(ctx_w->stats, options->Get(NanNew("stats"))->ToObject());
ctx_w->request.data = ctx_w;
// async (callback) style
Local<Function> success_callback = Local<Function>::Cast(options->Get(NanNew("success")));
Local<Function> error_callback = Local<Function>::Cast(options->Get(NanNew("error")));
ctx_w->success_callback = new NanCallback(success_callback);
ctx_w->error_callback = new NanCallback(error_callback);
}
Local<Function> importer_callback = Local<Function>::Cast(options->Get(NanNew("importer")));
ctx_w->importer_callback = new NanCallback(importer_callback);
if (!importer_callback->IsUndefined()) {
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));
}
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());
...@@ -56,7 +116,7 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) { ...@@ -56,7 +116,7 @@ void FillStatsObj(Handle<Object> stats, 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>();
if(included_files) { if (included_files) {
for (int i = 0; included_files[i] != nullptr; ++i) { for (int i = 0; included_files[i] != nullptr; ++i) {
arr->Set(i, NanNew<String>(included_files[i])); arr->Set(i, NanNew<String>(included_files[i]));
} }
...@@ -72,14 +132,15 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) { ...@@ -72,14 +132,15 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
if (sass_context_get_source_map_string(ctx)) { if (sass_context_get_source_map_string(ctx)) {
source_map = NanNew<String>(sass_context_get_source_map_string(ctx)); source_map = NanNew<String>(sass_context_get_source_map_string(ctx));
} else { }
else {
source_map = NanNew<String>("{}"); source_map = NanNew<String>("{}");
} }
(*stats)->Set(NanNew("sourceMap"), source_map); (*stats)->Set(NanNew("sourceMap"), source_map);
} }
void MakeCallback(uv_work_t* req) { void make_callback(uv_work_t* req) {
NanScope(); NanScope();
TryCatch try_catch; TryCatch try_catch;
...@@ -91,7 +152,8 @@ void MakeCallback(uv_work_t* req) { ...@@ -91,7 +152,8 @@ void MakeCallback(uv_work_t* req) {
ctx = sass_data_context_get_context(ctx_w->dctx); ctx = sass_data_context_get_context(ctx_w->dctx);
FillStatsObj(NanNew(ctx_w->stats), ctx); FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(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); FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx); error_status = sass_context_get_error_status(ctx);
...@@ -104,15 +166,16 @@ void MakeCallback(uv_work_t* req) { ...@@ -104,15 +166,16 @@ void MakeCallback(uv_work_t* req) {
NanNew<String>(val), NanNew<String>(val),
NanNew(ctx_w->stats)->Get(NanNew("sourceMap")) NanNew(ctx_w->stats)->Get(NanNew("sourceMap"))
}; };
ctx_w->callback->Call(2, argv); 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>(error_status)
}; };
ctx_w->errorCallback->Call(2, argv); ctx_w->error_callback->Call(2, argv);
} }
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
node::FatalException(try_catch); node::FatalException(try_catch);
...@@ -129,11 +192,9 @@ NAN_METHOD(Render) { ...@@ -129,11 +192,9 @@ NAN_METHOD(Render) {
struct Sass_Data_Context* dctx = sass_make_data_context(source_string); struct Sass_Data_Context* dctx = sass_make_data_context(source_string);
sass_context_wrapper* ctx_w = sass_make_context_wrapper(); sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ctx_w->dctx = dctx; ExtractOptions(options, dctx, ctx_w, false, false);
ExtractOptions(options, dctx, ctx_w, false); int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)make_callback);
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
assert(status == 0); assert(status == 0);
...@@ -147,8 +208,9 @@ NAN_METHOD(RenderSync) { ...@@ -147,8 +208,9 @@ NAN_METHOD(RenderSync) {
char* source_string = CreateString(options->Get(NanNew("data"))); char* source_string = CreateString(options->Get(NanNew("data")));
struct Sass_Data_Context* dctx = sass_make_data_context(source_string); struct Sass_Data_Context* dctx = sass_make_data_context(source_string);
struct Sass_Context* ctx = sass_data_context_get_context(dctx); struct Sass_Context* ctx = sass_data_context_get_context(dctx);
sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ExtractOptions(options, dctx, NULL, false); ExtractOptions(options, dctx, ctx_w, false, true);
compile_data(dctx); compile_data(dctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx); FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
...@@ -161,7 +223,7 @@ NAN_METHOD(RenderSync) { ...@@ -161,7 +223,7 @@ NAN_METHOD(RenderSync) {
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx)); Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_delete_data_context(dctx); sass_free_context_wrapper(ctx_w);
NanThrowError(error); NanThrowError(error);
NanReturnUndefined(); NanReturnUndefined();
...@@ -175,10 +237,9 @@ NAN_METHOD(RenderFile) { ...@@ -175,10 +237,9 @@ NAN_METHOD(RenderFile) {
struct Sass_File_Context* fctx = sass_make_file_context(input_path); struct Sass_File_Context* fctx = sass_make_file_context(input_path);
sass_context_wrapper* ctx_w = sass_make_context_wrapper(); sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ctx_w->fctx = fctx; ExtractOptions(options, fctx, ctx_w, true, false);
ExtractOptions(options, fctx, ctx_w, true);
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)make_callback);
assert(status == 0); assert(status == 0);
free(input_path); free(input_path);
...@@ -193,8 +254,9 @@ NAN_METHOD(RenderFileSync) { ...@@ -193,8 +254,9 @@ NAN_METHOD(RenderFileSync) {
char* input_path = CreateString(options->Get(NanNew("file"))); char* input_path = CreateString(options->Get(NanNew("file")));
struct Sass_File_Context* fctx = sass_make_file_context(input_path); struct Sass_File_Context* fctx = sass_make_file_context(input_path);
struct Sass_Context* ctx = sass_file_context_get_context(fctx); struct Sass_Context* ctx = sass_file_context_get_context(fctx);
sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ExtractOptions(options, fctx, NULL, true); ExtractOptions(options, fctx, ctx_w, true, true);
compile_file(fctx); compile_file(fctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx); FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
free(input_path); free(input_path);
...@@ -208,17 +270,83 @@ NAN_METHOD(RenderFileSync) { ...@@ -208,17 +270,83 @@ NAN_METHOD(RenderFileSync) {
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx)); Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_delete_file_context(fctx); sass_free_context_wrapper(ctx_w);
NanThrowError(error); NanThrowError(error);
NanReturnUndefined(); NanReturnUndefined();
} }
NAN_METHOD(ImportedCallback) {
NanScope();
TryCatch try_catch;
Local<Object> options = args[0]->ToObject();
char* source_string = CreateString(options->Get(NanNew("index")));
Local<Value> returned_value = options->Get(NanNew("objectLiteral"));
size_t index = options->Get(NanNew("index"))->Int32Value();
if (index >= imports_collection.size()) {
NanReturnUndefined();
}
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);
}
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));
}
void RegisterModule(v8::Handle<v8::Object> target) { void RegisterModule(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "render", Render); NODE_SET_METHOD(target, "render", Render);
NODE_SET_METHOD(target, "renderSync", RenderSync); NODE_SET_METHOD(target, "renderSync", RenderSync);
NODE_SET_METHOD(target, "renderFile", RenderFile); NODE_SET_METHOD(target, "renderFile", RenderFile);
NODE_SET_METHOD(target, "renderFileSync", RenderFileSync); NODE_SET_METHOD(target, "renderFileSync", RenderFileSync);
NODE_SET_METHOD(target, "importedCallback", ImportedCallback);
} }
NODE_MODULE(binding, RegisterModule); NODE_MODULE(binding, RegisterModule);
Subproject commit 5f3558d7ce36bb61202f1585ed8e32a52209e940 Subproject commit 7aaa45a979ce65d50b3158ff50bf9409f8955063
...@@ -8,7 +8,8 @@ extern "C" { ...@@ -8,7 +8,8 @@ extern "C" {
if (ctx_w->dctx) { if (ctx_w->dctx) {
compile_data(ctx_w->dctx); compile_data(ctx_w->dctx);
} else if (ctx_w->fctx) { }
else if (ctx_w->fctx) {
compile_file(ctx_w->fctx); compile_file(ctx_w->fctx);
} }
} }
...@@ -22,19 +23,31 @@ extern "C" { ...@@ -22,19 +23,31 @@ extern "C" {
} }
sass_context_wrapper* sass_make_context_wrapper() { sass_context_wrapper* sass_make_context_wrapper() {
return (sass_context_wrapper*) calloc(1, sizeof(sass_context_wrapper)); auto 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;
} }
void sass_free_context_wrapper(sass_context_wrapper* ctx_w) { void sass_free_context_wrapper(sass_context_wrapper* ctx_w) {
if (ctx_w->dctx) { if (ctx_w->dctx) {
sass_delete_data_context(ctx_w->dctx); sass_delete_data_context(ctx_w->dctx);
} else if (ctx_w->fctx) { }
else if (ctx_w->fctx) {
sass_delete_file_context(ctx_w->fctx); sass_delete_file_context(ctx_w->fctx);
} }
NanDisposePersistent(ctx_w->stats); NanDisposePersistent(ctx_w->stats);
delete ctx_w->callback;
delete ctx_w->errorCallback; delete ctx_w->success_callback;
delete ctx_w->error_callback;
delete ctx_w->importer_callback;
delete ctx_w->file;
delete ctx_w->prev;
delete ctx_w->cookie;
uv_mutex_destroy(&ctx_w->importer_mutex);
uv_cond_destroy(&ctx_w->importer_condition_variable);
free(ctx_w); free(ctx_w);
} }
......
...@@ -5,23 +5,31 @@ ...@@ -5,23 +5,31 @@
extern "C" { extern "C" {
#endif #endif
using namespace v8; using namespace v8;
void compile_data(struct Sass_Data_Context* dctx); void compile_data(struct Sass_Data_Context* dctx);
void compile_file(struct Sass_File_Context* fctx); void compile_file(struct Sass_File_Context* fctx);
void compile_it(uv_work_t* req); void compile_it(uv_work_t* req);
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> stats;
uv_work_t request; uv_work_t request;
NanCallback* callback; uv_mutex_t importer_mutex;
NanCallback* errorCallback; uv_cond_t importer_condition_variable;
}; uv_async_t async;
const char* file;
const char* prev;
void* cookie;
Sass_Import** imports;
NanCallback* success_callback;
NanCallback* error_callback;
NanCallback* importer_callback;
};
struct sass_context_wrapper* sass_make_context_wrapper(void); struct sass_context_wrapper* sass_make_context_wrapper(void);
void sass_free_context_wrapper(struct sass_context_wrapper* ctx_w); void sass_free_context_wrapper(struct sass_context_wrapper* ctx_w);
#ifdef __cplusplus #ifdef __cplusplus
} }
......
Subproject commit b85c617a0e7e5367dd347b0b301ad1c15b6d5087 Subproject commit 074ae3b24bfc3ff19a98efdec02415ed6fd5759a
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