Commit 7caffda6 by Brett Wilkins

Merge branch 'new-api'

parents e5c3c8ae d23939ee
......@@ -16,17 +16,36 @@ Find it on npm: <https://npmjs.org/package/node-sass>
```javascript
var sass = require('node-sass');
sass.render(scss_content, callback [, options]);
sass.render({
file: scss_filename,
success: callback
[, options..]
});
// OR
var css = sass.renderSync(scss_content [, options]);
var css = sass.renderSync({
data: scss_content
[, options..]
});
```
### Options
The options argument is optional, though it's use is recommended. It support two attributes: `includePaths` and `outputStyle`.
The API for using node-sass has changed, so that now there is only one variable - an options hash. Some of these options are optional, and in some circumstances some are mandatory.
#### file
`file` is a `String` of the path to an `scss` file for libsass to render. One of this or `data` options are required, for both render and renderSync.
#### data
`data` is a `String` containing the scss to be rendered by libsass. One of this or `file` options are required, for both render and renderSync. It is recommended that you use the `includePaths` option in conjunction with this, as otherwise libsass may have trouble finding files imported via the `@import` directive.
#### success
`success` is a `Function` to be called upon successful rendering of the scss to css. This option is required but only for the render function. If provided to renderSync it will be ignored.
#### error
`error` is a `Function` to be called upon occurance of an error when rendering the scss to css. This option is optional, and only applies to the render function. If provided to renderSync it will be ignored.
#### includePaths
`includePaths` is an `Array` of path `String`s to look for any `@import`ed files. It is recommended that you use this option if you have **any** `@import` directives, as otherwise libsass may not find your depended-on files.
`includePaths` is an `Array` of path `String`s to look for any `@import`ed files. It is recommended that you use this option if you are using the `data` option and have **any** `@import` directives, as otherwise libsass may not find your depended-on files.
#### outputStyle
`outputStyle` is a `String` to determine how the final CSS should be rendered. Its value should be one of `'nested', 'expanded', 'compact', 'compressed'`.
......@@ -36,13 +55,28 @@ The options argument is optional, though it's use is recommended. It support two
```javascript
var sass = require('node-sass');
sass.render('body{background:blue; a{color:black;}}', function(err, css){
sass.render({
data: 'body{background:blue; a{color:black;}}',
success: function(css){
console.log(css)
}/*, { includePaths: [ 'lib/', 'mod/' ], outputStyle: 'compressed' }*/);
},
error: function(error) {
console.log(error);
},
includePaths: [ 'lib/', 'mod/' ],
outputStyle: 'compressed'
});
// OR
console.log(sass.renderSync('body{background:blue; a{color:black;}}')/*, { includePaths: [ 'lib/', 'mod/' ], outputStyle: 'compressed' }*/);
console.log(sass.renderSync({
data: 'body{background:blue; a{color:black;}}'),
outputStyle: 'compressed'
});
```
### Edge-case behaviours
* In the case that both `file` and `data` options are set, node-sass will only attempt to honour the `file` directive.
## Connect/Express middleware
Recompile `.scss` files automatically for connect and express based http servers
......
......@@ -16,7 +16,7 @@ void WorkOnContext(uv_work_t* req) {
sass_compile(ctx);
}
void MakeCallback(uv_work_t* req) {
void MakeOldCallback(uv_work_t* req) {
HandleScope scope;
TryCatch try_catch;
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
......@@ -46,7 +46,7 @@ void MakeCallback(uv_work_t* req) {
sass_free_context_wrapper(ctx_w);
}
Handle<Value> Render(const Arguments& args) {
Handle<Value> OldRender(const Arguments& args) {
HandleScope scope;
sass_context* ctx = sass_new_context();
sass_context_wrapper* ctx_w = sass_new_context_wrapper();
......@@ -66,6 +66,63 @@ Handle<Value> Render(const Arguments& args) {
ctx_w->callback = Persistent<Function>::New(callback);
ctx_w->request.data = ctx_w;
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeOldCallback);
assert(status == 0);
return scope.Close(Undefined());
}
void MakeCallback(uv_work_t* req) {
HandleScope scope;
TryCatch try_catch;
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
sass_context* ctx = static_cast<sass_context*>(ctx_w->ctx);
if (ctx->error_status == 0) {
// if no error, do callback(null, result)
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(String::New(ctx->output_string))
};
ctx_w->callback->Call(Context::GetCurrent()->Global(), argc, argv);
} else {
// if error, do callback(error)
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(String::New(ctx->error_message))
};
ctx_w->errorCallback->Call(Context::GetCurrent()->Global(), argc, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
sass_free_context_wrapper(ctx_w);
}
Handle<Value> Render(const Arguments& args) {
HandleScope scope;
sass_context* ctx = sass_new_context();
sass_context_wrapper* ctx_w = sass_new_context_wrapper();
char *source;
String::AsciiValue astr(args[0]);
Local<Function> callback = Local<Function>::Cast(args[1]);
Local<Function> errorCallback = Local<Function>::Cast(args[2]);
String::AsciiValue bstr(args[3]);
source = new char[strlen(*astr)+1];
strcpy(source, *astr);
ctx->source_string = source;
ctx->options.include_paths = new char[strlen(*bstr)+1];
strcpy(ctx->options.include_paths, *bstr);
// ctx->options.output_style = SASS_STYLE_NESTED;
ctx->options.output_style = args[4]->Int32Value();
ctx_w->ctx = ctx;
ctx_w->callback = Persistent<Function>::New(callback);
ctx_w->errorCallback = Persistent<Function>::New(errorCallback);
ctx_w->request.data = ctx_w;
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnContext, (uv_after_work_cb)MakeCallback);
assert(status == 0);
......@@ -74,7 +131,6 @@ Handle<Value> Render(const Arguments& args) {
Handle<Value> RenderSync(const Arguments& args) {
HandleScope scope;
TryCatch try_catch;
sass_context* ctx = sass_new_context();
char *source;
String::AsciiValue astr(args[0]);
......@@ -90,15 +146,108 @@ Handle<Value> RenderSync(const Arguments& args) {
sass_compile(ctx);
if (ctx->error_status == 0) {
return scope.Close(Local<Value>::New(String::New(ctx->output_string)));
}
ThrowException(Exception::Error(String::New(ctx->error_message)));
return scope.Close(Undefined());
}
/**
Rendering Files
**/
void WorkOnFileContext(uv_work_t* req) {
sass_file_context_wrapper* ctx_w = static_cast<sass_file_context_wrapper*>(req->data);
sass_file_context* ctx = static_cast<sass_file_context*>(ctx_w->ctx);
sass_compile_file(ctx);
}
void MakeFileCallback(uv_work_t* req) {
HandleScope scope;
TryCatch try_catch;
sass_file_context_wrapper* ctx_w = static_cast<sass_file_context_wrapper*>(req->data);
sass_file_context* ctx = static_cast<sass_file_context*>(ctx_w->ctx);
if (ctx->error_status == 0) {
// if no error, do callback(null, result)
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(String::New(ctx->output_string))
};
ctx_w->callback->Call(Context::GetCurrent()->Global(), argc, argv);
} else {
ThrowException(Exception::Error(String::New("Input does not appear to be valid SCSS.")));
// if error, do callback(error)
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(String::New(ctx->error_message))
};
ctx_w->errorCallback->Call(Context::GetCurrent()->Global(), argc, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
sass_free_file_context_wrapper(ctx_w);
}
Handle<Value> RenderFile(const Arguments& args) {
HandleScope scope;
sass_file_context* ctx = sass_new_file_context();
sass_file_context_wrapper* ctx_w = sass_new_file_context_wrapper();
char *filename;
String::AsciiValue astr(args[0]);
Local<Function> callback = Local<Function>::Cast(args[1]);
Local<Function> errorCallback = Local<Function>::Cast(args[2]);
String::AsciiValue bstr(args[3]);
filename = new char[strlen(*astr)+1];
strcpy(filename, *astr);
ctx->input_path = filename;
ctx->options.include_paths = new char[strlen(*bstr)+1];
strcpy(ctx->options.include_paths, *bstr);
// ctx->options.output_style = SASS_STYLE_NESTED;
ctx->options.output_style = args[4]->Int32Value();
ctx_w->ctx = ctx;
ctx_w->callback = Persistent<Function>::New(callback);
ctx_w->errorCallback = Persistent<Function>::New(errorCallback);
ctx_w->request.data = ctx_w;
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, WorkOnFileContext, (uv_after_work_cb)MakeFileCallback);
assert(status == 0);
return scope.Close(Undefined());
}
Handle<Value> RenderFileSync(const Arguments& args) {
HandleScope scope;
sass_file_context* ctx = sass_new_file_context();
char *filename;
String::AsciiValue astr(args[0]);
String::AsciiValue bstr(args[1]);
filename = new char[strlen(*astr)+1];
strcpy(filename, *astr);
ctx->input_path = filename;
ctx->options.include_paths = new char[strlen(*bstr)+1];
strcpy(ctx->options.include_paths, *bstr);
ctx->options.output_style = args[2]->Int32Value();
sass_compile_file(ctx);
if (ctx->error_status == 0) {
return scope.Close(Local<Value>::New(String::New(ctx->output_string)));
}
ThrowException(Exception::Error(String::New(ctx->error_message)));
return scope.Close(Undefined());
}
void RegisterModule(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "oldRender", OldRender);
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_MODULE(binding, RegisterModule);
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -21,20 +21,61 @@ var SASS_OUTPUT_STYLE = {
compressed: 3
};
exports.render = function(css, callback, options) {
var prepareOptions = function(options) {
var paths, style;
options = typeof options !== 'object' ? {} : options;
paths = options.include_paths || options.includePaths || [];
style = SASS_OUTPUT_STYLE[options.output_style || options.outputStyle] || 0;
return binding.render(css, callback, paths.join(':'), style);
return {
paths: paths,
style: style
};
}
var deprecatedRender = function(css, callback, options) {
options = prepareOptions(options);
return binding.oldRender(css, callback, options.paths.join(':'), options.style);
};
exports.renderSync = function(css, options) {
var paths, style;
options = typeof options !== 'object' ? {} : options;
paths = options.include_paths || options.includePaths || [];
style = SASS_OUTPUT_STYLE[options.output_style || options.outputStyle] || 0;
return binding.renderSync(css, paths.join(':'), style);
var deprecatedRenderSync = function(css, options) {
options = prepareOptions(options);
return binding.renderSync(css, options.paths.join(':'), options.style);
};
exports.render = function(options) {
var newOptions;
if (typeof arguments[0] === 'string') {
return deprecatedRender.apply(this, arguments);
}
newOptions = prepareOptions(options);
options.error = options.error || function(){};
if (options.file !== undefined && options.file !== null) {
return binding.renderFile(options.file, options.success, options.error, newOptions.paths.join(':'), newOptions.style);
}
//Assume data is present if file is not. binding/libsass will tell the user otherwise!
return binding.render(options.data, options.success, options.error, newOptions.paths.join(":"), newOptions.style);
};
exports.renderSync = function(options) {
var newOptions;
if (typeof arguments[0] === 'string') {
return deprecatedRenderSync.apply(this, arguments);
}
newOptions = prepareOptions(options);
if (options.file !== undefined && options.file !== null) {
return binding.renderFileSync(options.file, newOptions.paths.join(':'), newOptions.style);
}
//Assume data is present if file is not. binding/libsass will tell the user otherwise!
return binding.renderSync(options.data, newOptions.paths.join(":"), newOptions.style);
};
exports.middleware = require('./lib/middleware');
......@@ -15,4 +15,16 @@ extern "C" {
free(ctx_w);
}
sass_file_context_wrapper* sass_new_file_context_wrapper()
{
return (sass_file_context_wrapper*) calloc(1, sizeof(sass_file_context_wrapper));
}
void sass_free_file_context_wrapper(sass_file_context_wrapper* ctx_w)
{
if (ctx_w->ctx) sass_free_file_context(ctx_w->ctx);
free(ctx_w);
}
}
......@@ -9,11 +9,22 @@ struct sass_context_wrapper {
sass_context* ctx;
uv_work_t request;
v8::Persistent<v8::Function> callback;
v8::Persistent<v8::Function> errorCallback;
};
struct sass_context_wrapper* sass_new_context_wrapper(void);
void sass_free_context_wrapper(struct sass_context_wrapper* ctx);
struct sass_file_context_wrapper {
sass_file_context* ctx;
uv_work_t request;
v8::Persistent<v8::Function> callback;
v8::Persistent<v8::Function> errorCallback;
};
struct sass_file_context_wrapper* sass_new_file_context_wrapper(void);
void sass_free_file_context_wrapper(struct sass_file_context_wrapper* ctx);
#ifdef __cplusplus
}
#endif
#h1 {
width: 100%;
span {
color: red;
&.active {
color: pink;
}
#navbar {
width: 80%;
height: 23px;
}
#navbar ul {
list-style-type: none;
}
#navbar li {
float: left;
a {
font-weight: bold;
}
}
......@@ -28,8 +28,11 @@ var expectedRender = '#navbar {\n\
#navbar li a {\n\
font-weight: bold; }\n';
var badSampleFilename = 'sample.scss';
var sampleFilename = require('path').resolve(__dirname, 'sample.scss');
describe("compile scss", function() {
describe("DEPRECATED: compile scss", function() {
it("should compile with render", function(done) {
sass.render(scssStr, function(err, css) {
done(err);
......@@ -60,3 +63,80 @@ describe("compile scss", function() {
}));
});
});
describe("compile scss", function() {
it("should compile with render", function(done) {
sass.render({
data: scssStr,
success: function(css) {
done(assert.ok(css));
}
});
});
it("should compile with renderSync", function(done) {
done(assert.ok(sass.renderSync({data: scssStr})));
});
it("should match compiled string with render", function(done) {
sass.render({
data: scssStr,
success: function(css) {
done(assert.equal(css, expectedRender));
},
error: function(error) {
done(error);
}
});
});
it("should match compiled string with renderSync", function(done) {
done(assert.equal(sass.renderSync({data: scssStr}), expectedRender));
});
it("should throw an exception for bad input", function(done) {
done(assert.throws(function() {
sass.renderSync({data: badInput});
}));
});
});
describe("compile file", function() {
it("should compile with render", function(done) {
sass.render({
file: sampleFilename,
success: function (css) {
done(assert.ok(css));
},
error: function (error) {
done(error);
}
});
});
it("should compile with renderSync", function(done) {
done(assert.ok(sass.renderSync({file: sampleFilename})));
});
it("should match compiled string with render", function(done) {
sass.render({
file: sampleFilename,
success: function(css) {
done(assert.equal(css, expectedRender));
},
error: function(error) {
done(error);
}
});
});
it("should match compiled string with renderSync", function(done) {
done(assert.equal(sass.renderSync({file: sampleFilename}), expectedRender));
});
it("should throw an exception for bad input", function(done) {
done(assert.throws(function() {
sass.renderSync({file: badSampleFilename});
}));
});
});
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