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) {
var error = options.error;
var success = options.success;
var importer = options.importer;
options.error = function(err, code) {
try {
......@@ -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.include_paths;
delete options.includePaths;
......
#include <nan.h>
#include <vector>
#include "sass_context_wrapper.h"
char* CreateString(Local<Value> value) {
if(value->IsNull() || !value->IsString()) {
if (value->IsNull() || !value->IsString()) {
return const_cast<char*>(""); // return empty string.
}
String::Utf8Value string(value);
char *str = (char *) malloc(string.length() + 1);
char *str = (char *)malloc(string.length() + 1);
strcpy(str, *string);
return str;
}
void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile) {
if (ctx_w) {
NanAssignPersistent(ctx_w->stats, options->Get(NanNew("stats"))->ToObject());
std::vector<sass_context_wrapper*> imports_collection;
// async (callback) style
Local<Function> callback = Local<Function>::Cast(options->Get(NanNew("success")));
Local<Function> errorCallback = Local<Function>::Cast(options->Get(NanNew("error")));
if (isFile) {
ctx_w->fctx = (struct Sass_File_Context*) cptr;
} else {
ctx_w->dctx = (struct Sass_Data_Context*) cptr;
void dispatched_async_uv_callback(uv_async_t *req){
NanScope();
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
TryCatch try_catch;
imports_collection.push_back(ctx_w);
Handle<Value> argv[] = {
NanNew<String>(strdup(ctx_w->file)),
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);
}
ctx_w->request.data = ctx_w;
ctx_w->callback = new NanCallback(callback);
ctx_w->errorCallback = new NanCallback(errorCallback);
}
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;
if (isFile) {
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_w->dctx = (struct Sass_Data_Context*) cptr;
}
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_image_path(sass_options, CreateString(options->Get(NanNew("imagePath"))));
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) {
char** included_files = sass_context_get_included_files(ctx);
Handle<Array> arr = NanNew<Array>();
if(included_files) {
if (included_files) {
for (int i = 0; included_files[i] != nullptr; ++i) {
arr->Set(i, NanNew<String>(included_files[i]));
}
......@@ -72,14 +132,15 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
if (sass_context_get_source_map_string(ctx)) {
source_map = NanNew<String>(sass_context_get_source_map_string(ctx));
} else {
}
else {
source_map = NanNew<String>("{}");
}
(*stats)->Set(NanNew("sourceMap"), source_map);
}
void MakeCallback(uv_work_t* req) {
void make_callback(uv_work_t* req) {
NanScope();
TryCatch try_catch;
......@@ -91,7 +152,8 @@ void MakeCallback(uv_work_t* req) {
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);
FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx);
......@@ -104,15 +166,16 @@ void MakeCallback(uv_work_t* req) {
NanNew<String>(val),
NanNew(ctx_w->stats)->Get(NanNew("sourceMap"))
};
ctx_w->callback->Call(2, argv);
} else {
ctx_w->success_callback->Call(2, argv);
}
else {
// if error, do callback(error)
const char* err = sass_context_get_error_json(ctx);
Local<Value> argv[] = {
NanNew<String>(err),
NanNew<Integer>(error_status)
};
ctx_w->errorCallback->Call(2, argv);
ctx_w->error_callback->Call(2, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
......@@ -129,11 +192,9 @@ NAN_METHOD(Render) {
struct Sass_Data_Context* dctx = sass_make_data_context(source_string);
sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ctx_w->dctx = dctx;
ExtractOptions(options, dctx, ctx_w, false);
ExtractOptions(options, dctx, ctx_w, false, false);
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);
......@@ -147,8 +208,9 @@ NAN_METHOD(RenderSync) {
char* source_string = CreateString(options->Get(NanNew("data")));
struct Sass_Data_Context* dctx = sass_make_data_context(source_string);
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);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
......@@ -161,7 +223,7 @@ NAN_METHOD(RenderSync) {
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_delete_data_context(dctx);
sass_free_context_wrapper(ctx_w);
NanThrowError(error);
NanReturnUndefined();
......@@ -175,10 +237,9 @@ NAN_METHOD(RenderFile) {
struct Sass_File_Context* fctx = sass_make_file_context(input_path);
sass_context_wrapper* ctx_w = sass_make_context_wrapper();
ctx_w->fctx = fctx;
ExtractOptions(options, fctx, ctx_w, true);
ExtractOptions(options, fctx, ctx_w, true, false);
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);
free(input_path);
......@@ -193,8 +254,9 @@ NAN_METHOD(RenderFileSync) {
char* input_path = CreateString(options->Get(NanNew("file")));
struct Sass_File_Context* fctx = sass_make_file_context(input_path);
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);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
free(input_path);
......@@ -208,17 +270,83 @@ NAN_METHOD(RenderFileSync) {
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_delete_file_context(fctx);
sass_free_context_wrapper(ctx_w);
NanThrowError(error);
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) {
NODE_SET_METHOD(target, "render", Render);
NODE_SET_METHOD(target, "renderSync", RenderSync);
NODE_SET_METHOD(target, "renderFile", RenderFile);
NODE_SET_METHOD(target, "renderFileSync", RenderFileSync);
NODE_SET_METHOD(target, "importedCallback", ImportedCallback);
}
NODE_MODULE(binding, RegisterModule);
Subproject commit 5f3558d7ce36bb61202f1585ed8e32a52209e940
Subproject commit 7aaa45a979ce65d50b3158ff50bf9409f8955063
......@@ -8,7 +8,8 @@ extern "C" {
if (ctx_w->dctx) {
compile_data(ctx_w->dctx);
} else if (ctx_w->fctx) {
}
else if (ctx_w->fctx) {
compile_file(ctx_w->fctx);
}
}
......@@ -22,19 +23,31 @@ extern "C" {
}
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) {
if (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);
}
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);
}
......
......@@ -5,23 +5,31 @@
extern "C" {
#endif
using namespace v8;
using namespace v8;
void compile_data(struct Sass_Data_Context* dctx);
void compile_file(struct Sass_File_Context* fctx);
void compile_it(uv_work_t* req);
void compile_data(struct Sass_Data_Context* dctx);
void compile_file(struct Sass_File_Context* fctx);
void compile_it(uv_work_t* req);
struct sass_context_wrapper {
struct sass_context_wrapper {
Sass_Data_Context* dctx;
Sass_File_Context* fctx;
Persistent<Object> stats;
uv_work_t request;
NanCallback* callback;
NanCallback* errorCallback;
};
uv_mutex_t importer_mutex;
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);
void sass_free_context_wrapper(struct sass_context_wrapper* ctx_w);
struct sass_context_wrapper* sass_make_context_wrapper(void);
void sass_free_context_wrapper(struct sass_context_wrapper* ctx_w);
#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