Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

API: Provides support for array of importers #832

Merged
merged 2 commits into from
Apr 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,21 @@ var result = sass.renderSync({

## Options
### file
Type: `String | null`
Type: `String`
Default: `null`
**Special**: `file` or `data` must be specified

Path to a file for [libsass] to render.

### data
Type: `String | null`
Type: `String`
Default: `null`
**Special**: `file` or `data` must be specified

A string to pass to [libsass] to render. It is recommended that you use `includePaths` in conjunction with this so that [libsass] can find files when using the `@import` directive.

### importer (>= v2.0.0)
Type: `Function` signature `function(url, prev, done)`
Type: `Function | Function[]` signature `function(url, prev, done)`
Default: `undefined`

Function Parameters and Information:
Expand All @@ -89,6 +89,8 @@ When returning or calling `done()` with `{ contents: "String" }`, the string val

Starting from v3.0.0, `this` refers to a contextual scope for the immediate run of `sass.render` or `sass.renderSync`

Starting from v3.0.0, importer can be an array of functions, which will be called by libsass in the order of their occurance in array. This helps user specify special importer for particular kind of path (filesystem, http). If the importer does not handle particular path, it should return `sass.NULL`. See [functions section](#functions) for more details on Sass types.

### functions (>= v3.0.0)
`functions` is an `Object` that holds a collection of custom functions that may be invoked by the sass files being compiled. They may take zero or more input parameters and must return a value either synchronously (`return ...;`) or asynchronously (`done();`). Those parameters will be instances of one of the constructors contained in the `require('node-sass').types` hash. The return value must be of one of these types as well. See the list of available types below:

Expand Down
56 changes: 42 additions & 14 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,26 +282,42 @@ module.exports.render = function(options, cb) {
var importer = options.importer;

if (importer) {
options.importer = function(file, prev, bridge) {
function done(data) {
bridge.success(data);
}
if (Array.isArray(importer)) {
importer.forEach(function(subject, index) {
options.importer[index] = function(file, prev, bridge) {
function done(data) {
bridge.success(data);
}

var result = subject.call(options.context, file, prev, done);

if (result) {
done(result === module.exports.NULL ? null : result);
}
};
});
} else {
options.importer = function(file, prev, bridge) {
function done(data) {
bridge.success(data);
}

var result = importer.call(options.context, file, prev, done);
var result = importer.call(options.context, file, prev, done);

if (result) {
done(result);
}
};
if (result) {
done(result === module.exports.NULL ? null : result);
}
};
}
}

var functions = options.functions;

if (functions) {
options.functions = {};

Object.keys(functions).forEach(function(signature) {
var cb = normalizeFunctionSignature(signature, functions[signature]);
Object.keys(functions).forEach(function(subject) {
var cb = normalizeFunctionSignature(subject, functions[subject]);

options.functions[cb.signature] = function() {
var args = Array.prototype.slice.call(arguments),
Expand Down Expand Up @@ -336,9 +352,21 @@ module.exports.renderSync = function(options) {
var importer = options.importer;

if (importer) {
options.importer = function(file, prev) {
return importer.call(options.context, file, prev);
};
if (Array.isArray(importer)) {
importer.forEach(function(subject, index) {
options.importer[index] = function(file, prev) {
var result = subject.call(options.context, file, prev);

return result === module.exports.NULL ? null : result;
};
});
} else {
options.importer = function(file, prev) {
var result = importer.call(options.context, file, prev);

return result === module.exports.NULL ? null : result;
};
}
}

var functions = options.functions;
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "node-sass",
"version": "3.0.0-beta.4",
"libsass": "3.2.0-beta.2",
"libsass": "3.2.0-beta.5",
"description": "Wrapper around libsass",
"license": "MIT",
"homepage": "https://github.com/sass/node-sass",
Expand Down Expand Up @@ -53,12 +53,12 @@
"nan": "^1.7.0",
"npmconf": "^2.1.1",
"pangyp": "^2.1.0",
"request": "^2.53.0",
"request": "^2.55.0",
"sass-graph": "^1.2.0"
},
"devDependencies": {
"coveralls": "^2.11.2",
"cross-spawn": "^0.2.6",
"cross-spawn": "^0.2.8",
"jscoverage": "^0.5.9",
"jshint": "^2.6.3",
"mocha": "^2.2.1",
Expand Down
69 changes: 50 additions & 19 deletions src/binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
#include "create_string.h"
#include "sass_types/factory.h"

struct Sass_Import** sass_importer(const char* file, const char* prev, void* cookie)
Sass_Import_List sass_importer(const char* cur_path, Sass_Importer_Entry cb, struct Sass_Compiler* comp)
{
void* cookie = sass_importer_get_cookie(cb);
struct Sass_Import* previous = sass_compiler_get_last_import(comp);
const char* prev_path = sass_import_get_path(previous);
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(cookie);
CustomImporterBridge& bridge = *(ctx_w->importer_bridge);
CustomImporterBridge& bridge = *(static_cast<CustomImporterBridge*>(cookie));

std::vector<void*> argv;
argv.push_back((void*)file);
argv.push_back((void*)prev);
argv.push_back((void*)cur_path);
argv.push_back((void*)prev_path);

return bridge(argv);
}

union Sass_Value* sass_custom_function(const union Sass_Value* s_args, void* cookie)
union Sass_Value* sass_custom_function(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Options* opts)
{
void* cookie = sass_function_get_cookie(cb);
CustomFunctionBridge& bridge = *(static_cast<CustomFunctionBridge*>(cookie));

std::vector<void*> argv;
Expand Down Expand Up @@ -65,13 +69,6 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
ctx_w->error_callback = new NanCallback(error_callback);
}

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

if (importer_callback->IsFunction()) {
ctx_w->importer_bridge = new CustomImporterBridge(new NanCallback(importer_callback), ctx_w->is_sync);
sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w));
}

if (!is_file) {
ctx_w->file = create_string(options->Get(NanNew("file")));
sass_option_set_input_path(sass_options, ctx_w->file);
Expand Down Expand Up @@ -106,25 +103,58 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
sass_option_set_indent(sass_options, ctx_w->indent);
sass_option_set_linefeed(sass_options, ctx_w->linefeed);

Local<Object> custom_functions = Local<Object>::Cast(options->Get(NanNew("functions")));
Local<Value> importer_callback = options->Get(NanNew("importer"));

if (importer_callback->IsFunction()) {
Local<Function> importer = Local<Function>::Cast(importer_callback);
auto bridge = std::make_shared<CustomImporterBridge>(new NanCallback(importer), ctx_w->is_sync);
ctx_w->importer_bridges.push_back(bridge);

Sass_Importer_List c_importers = sass_make_importer_list(1);
c_importers[0] = sass_make_importer(sass_importer, 0, bridge.get());

sass_option_set_c_importers(sass_options, c_importers);
}
else if (importer_callback->IsArray()) {
Handle<Array> importers = Handle<Array>::Cast(importer_callback);
Sass_Importer_List c_importers = sass_make_importer_list(importers->Length());

for (size_t i = 0; i < importers->Length(); ++i) {
Local<Function> callback = Local<Function>::Cast(importers->Get(static_cast<uint32_t>(i)));

if (!callback->IsFunction()) {
NanThrowError(NanNew("options.importer must be set to a function or array of functions"));
}

auto bridge = std::make_shared<CustomImporterBridge>(new NanCallback(callback), ctx_w->is_sync);
ctx_w->importer_bridges.push_back(bridge);

c_importers[i] = sass_make_importer(sass_importer, importers->Length() - i - 1, bridge.get());
}

sass_option_set_c_importers(sass_options, c_importers);
}

Local<Value> custom_functions = options->Get(NanNew("functions"));

if (custom_functions->IsObject()) {
Local<Array> signatures = custom_functions->GetOwnPropertyNames();
Local<Object> functions = Local<Object>::Cast(custom_functions);
Local<Array> signatures = functions->GetOwnPropertyNames();
unsigned num_signatures = signatures->Length();
Sass_C_Function_List fn_list = sass_make_function_list(num_signatures);
Sass_Function_List fn_list = sass_make_function_list(num_signatures);

for (unsigned i = 0; i < num_signatures; i++) {
Local<String> signature = Local<String>::Cast(signatures->Get(NanNew(i)));
Local<Function> callback = Local<Function>::Cast(custom_functions->Get(signature));
Local<Function> callback = Local<Function>::Cast(functions->Get(signature));

if (!signature->IsString() || !callback->IsFunction()) {
NanThrowError(NanNew("options.functions must be a (signature -> function) hash"));
}

CustomFunctionBridge* bridge = new CustomFunctionBridge(new NanCallback(callback), ctx_w->is_sync);
auto bridge = std::make_shared<CustomFunctionBridge>(new NanCallback(callback), ctx_w->is_sync);
ctx_w->function_bridges.push_back(bridge);

Sass_C_Function_Callback fn = sass_make_function(create_string(signature), sass_custom_function, bridge);
Sass_Function_Entry fn = sass_make_function(create_string(signature), sass_custom_function, bridge.get());
sass_function_set_list_entry(fn_list, i, fn);
}

Expand Down Expand Up @@ -274,7 +304,8 @@ NAN_METHOD(render_file_sync) {

int result = GetResult(ctx_w, ctx, true);

sass_wrapper_dispose(ctx_w, input_path);
free(input_path);
sass_free_context_wrapper(ctx_w);

NanReturnValue(NanNew<Boolean>(result == 0));
}
Expand Down
7 changes: 3 additions & 4 deletions src/custom_function_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
Sass_Value* CustomFunctionBridge::post_process_return_value(Handle<Value> val) const {
try {
return SassTypes::Factory::unwrap(val)->get_sass_value();
} catch (const std::invalid_argument& e) {
}
catch (const std::invalid_argument& e) {
return sass_make_error(e.what());
}
}
Expand All @@ -14,9 +15,7 @@ std::vector<Handle<Value>> CustomFunctionBridge::pre_process_args(std::vector<vo
std::vector<Handle<Value>> argv = std::vector<Handle<Value>>();

for (void* value : in) {
argv.push_back(
SassTypes::Factory::create(static_cast<Sass_Value*>(value))->get_js_object()
);
argv.push_back(SassTypes::Factory::create(static_cast<Sass_Value*>(value))->get_js_object());
}

return argv;
Expand Down
12 changes: 5 additions & 7 deletions src/custom_importer_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "create_string.h"

SassImportList CustomImporterBridge::post_process_return_value(Handle<Value> val) const {
SassImportList imports;
SassImportList imports = 0;
NanScope();

Local<Value> returned_value = NanNew(val);
Expand Down Expand Up @@ -32,8 +32,9 @@ SassImportList CustomImporterBridge::post_process_return_value(Handle<Value> val
else {
char* path = create_string(object->Get(NanNew<String>("file")));
char* contents = create_string(object->Get(NanNew<String>("contents")));
char* srcmap = create_string(object->Get(NanNew<String>("map")));

imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
imports[i] = sass_make_import_entry(path, contents, srcmap);
}
}
}
Expand All @@ -51,12 +52,9 @@ SassImportList CustomImporterBridge::post_process_return_value(Handle<Value> val
Local<Object> object = Local<Object>::Cast(returned_value);
char* path = create_string(object->Get(NanNew<String>("file")));
char* contents = create_string(object->Get(NanNew<String>("contents")));
char* srcmap = create_string(object->Get(NanNew<String>("map")));

imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
else {
imports = sass_make_import_list(1);
imports[0] = sass_make_import_entry((char const*) this->argv[0], 0, 0);
imports[0] = sass_make_import_entry(path, contents, srcmap);
}

return imports;
Expand Down
2 changes: 1 addition & 1 deletion src/custom_importer_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

using namespace v8;

typedef Sass_Import** SassImportList;
typedef Sass_Import_List SassImportList;

class CustomImporterBridge : public CallbackBridge<SassImportList> {
public:
Expand Down
2 changes: 1 addition & 1 deletion src/libsass
Submodule libsass updated 66 files
+2 −0 .gitignore
+3 −1 .travis.yml
+3 −0 Makefile
+16 −28 Makefile.am
+102 −51 ast.hpp
+2 −0 ast_factory.hpp
+1 −0 ast_fwd_decl.hpp
+15 −13 bind.cpp
+17 −5 configure.ac
+22 −2 constants.cpp
+21 −2 constants.hpp
+78 −56 context.cpp
+23 −9 context.hpp
+2 −28 contextualize.cpp
+6 −10 contextualize.hpp
+93 −0 contextualize_eval.cpp
+44 −0 contextualize_eval.hpp
+36 −9 contrib/plugin.cpp
+3 −1 cssize.cpp
+49 −16 debugger.hpp
+1 −1 emitter.cpp
+1 −1 emitter.hpp
+113 −25 environment.hpp
+3 −3 error_handling.cpp
+0 −1 error_handling.hpp
+105 −61 eval.cpp
+9 −1 eval.hpp
+97 −60 expand.cpp
+5 −2 expand.hpp
+7 −5 extend.cpp
+170 −123 file.cpp
+44 −7 file.hpp
+22 −12 functions.cpp
+2 −2 functions.hpp
+23 −14 inspect.cpp
+1 −0 inspect.hpp
+133 −0 lexer.cpp
+224 −0 lexer.hpp
+83 −0 listize.cpp
+41 −0 listize.hpp
+2 −0 operation.hpp
+5 −6 output.cpp
+414 −378 parser.cpp
+90 −108 parser.hpp
+15 −2 plugins.cpp
+6 −4 plugins.hpp
+52 −17 position.cpp
+18 −17 position.hpp
+203 −241 prelexer.cpp
+73 −333 prelexer.hpp
+21 −11 sass.cpp
+6 −6 sass.h
+167 −81 sass_context.cpp
+26 −6 sass_context.h
+49 −40 sass_functions.cpp
+55 −43 sass_functions.h
+9 −8 sass_interface.cpp
+3 −3 sass_interface.h
+8 −0 sass_version.h
+8 −0 sass_version.h.in
+2 −1 script/ci-report-coverage
+2 −2 source_map.cpp
+37 −11 util.cpp
+2 −1 util.hpp
+12 −0 win/libsass.filters
+10 −0 win/libsass.vcxproj
5 changes: 4 additions & 1 deletion src/libsass.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'libsass/constants.cpp',
'libsass/context.cpp',
'libsass/contextualize.cpp',
'libsass/contextualize_eval.cpp',
'libsass/cssize.cpp',
'libsass/emitter.cpp',
'libsass/error_handling.cpp',
Expand All @@ -21,6 +22,8 @@
'libsass/functions.cpp',
'libsass/inspect.cpp',
'libsass/json.cpp',
'libsass/lexer.cpp',
'libsass/listize.cpp',
'libsass/node.cpp',
'libsass/output.cpp',
'libsass/parser.cpp',
Expand Down Expand Up @@ -72,7 +75,7 @@
'VCCLCompilerTool': {
'AdditionalOptions': [
'/GR',
'/EHsc'
'/EHs'
]
}
}
Expand Down
21 changes: 3 additions & 18 deletions src/sass_context_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern "C" {
return (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper));
}

void sass_wrapper_dispose(struct sass_context_wrapper* ctx_w, char* string = 0) {
void sass_free_context_wrapper(sass_context_wrapper* ctx_w) {
if (ctx_w->dctx) {
sass_delete_data_context(ctx_w->dctx);
}
Expand All @@ -46,23 +46,8 @@ extern "C" {
free(ctx_w->source_map_root);
free(ctx_w->indent);

if (string) {
free(string);
}

if (!ctx_w->function_bridges.empty()) {
for (CustomFunctionBridge* bridge : ctx_w->function_bridges) {
delete bridge;
}
}

if (ctx_w->importer_bridge) {
delete ctx_w->importer_bridge;
}
}

void sass_free_context_wrapper(sass_context_wrapper* ctx_w) {
sass_wrapper_dispose(ctx_w);
ctx_w->importer_bridges.resize(0);
ctx_w->function_bridges.resize(0);

free(ctx_w);
}
Expand Down
Loading