Commit 2aebbd1c by Jakob Heuser

Render options in callbacks as this.options

This patch and tests introduces a context for all callbacks currently
in node-sass. Right now, they run w/ an ambiguous context, making the
`this` keyword not helpful to importer developers (and eventually
custom function developers). This creates a context object, adds the
render options to said context object, and then uses that context for
importer, success, and error callbacks via `this.options`.

By using a context instead of just `.call(options...)` there is a hook
for later should we find additional environmental information is
valuable to importer / custom function developers such as the node
environment, node-sass version, etc.
parent 4c14f27e
......@@ -104,6 +104,8 @@ Note: If this option is provided to renderSync it will be ignored. In case of `r
#### importer (starting from v2)
`importer` is a `Function` to be called when libsass parser encounters the import directive. If present, libsass will call node-sass and let the user change file, data or both during the compilation. This option is optional, and applies to both render and renderSync functions. Also, it can either return object of form `{file:'..', contents: '..'}` or send it back via `done({})`. Note in renderSync or render, there is no restriction imposed on using `done()` callback or `return` statement (dispite of the asnchrony difference).
The options passed in to `render` and `renderSync` are available as `this.options` within the `Function`.
#### 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 are using the `data` option and have **any** `@import` directives, as otherwise [libsass] may not find your depended-on files.
......
......@@ -127,7 +127,6 @@ function getSourceMap(options) {
* @param {Object} options
* @api private
*/
function getOptions(options) {
options = options || {};
options.comments = options.source_comments || options.sourceComments || false;
......@@ -140,6 +139,9 @@ function getOptions(options) {
options.sourceMap = getSourceMap(options);
options.style = getStyle(options) || 0;
// context object represents node-sass environment
options.context = { options: options };
if (options.imagePath && typeof options.imagePath !== 'string') {
throw new Error('`imagePath` needs to be a string');
}
......@@ -149,7 +151,7 @@ function getOptions(options) {
options.error = function(err) {
if (error) {
error(util._extend(new Error(), JSON.parse(err)));
error.call(options.context, util._extend(new Error(), JSON.parse(err)));
}
};
......@@ -158,7 +160,7 @@ function getOptions(options) {
var stats = endStats(result.stats);
if (success) {
success({
success.call(options.context, {
css: result.css,
map: result.map,
stats: stats
......@@ -207,7 +209,7 @@ module.exports.render = function(options) {
});
}
var result = importer(file, prev, done);
var result = importer.call(options.context, file, prev, done);
if (result) {
done(result);
......@@ -232,7 +234,7 @@ module.exports.renderSync = function(options) {
if (importer) {
options.importer = function(file, prev) {
return { objectLiteral: importer(file, prev) };
return { objectLiteral: importer.call(options.context, file, prev) };
};
}
......
......@@ -299,7 +299,7 @@ describe('api', function() {
}
});
});
it('should override imports with "data" as input and fires callback with contents', function(done) {
sass.render({
data: src,
......@@ -359,6 +359,56 @@ describe('api', function() {
}
});
});
it('should be able to see its options in this.options', function(done) {
var fxt = fixture('include-files/index.scss');
sass.render({
file: fxt,
success: function() {
assert.equal(fxt, this.options.file);
done();
},
importer: function() {
assert.equal(fxt, this.options.file);
return {};
}
});
});
it('should be able to access a persistent options object', function(done) {
sass.render({
data: src,
success: function() {
assert.equal(this.state, 2);
done();
},
importer: function() {
this.state = this.state || 0;
this.state++;
return {
contents: 'div {color: yellow;}'
};
}
});
});
it('should copy all options properties', function(done) {
var options;
options = {
data: src,
success: function() {
assert.strictEqual(this.options.success, options.success);
done();
},
importer: function() {
assert.strictEqual(this.options.importer, options.importer);
return {
contents: 'div {color: yellow;}'
};
}
};
sass.render(options);
});
});
describe('.renderSync(options)', function() {
......@@ -493,7 +543,7 @@ describe('api', function() {
assert.equal(result.css.trim(), '');
done();
});
it('should override imports with "data" as input and returns contents', function(done) {
var result = sass.renderSync({
data: src,
......@@ -521,6 +571,21 @@ describe('api', function() {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should be able to see its options in this.options', function(done) {
var fxt = fixture('include-files/index.scss');
var sync = false;
sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function() {
assert.equal(fxt, this.options.file);
sync = true;
return {};
}
});
assert.equal(sync, true);
done();
});
});
describe('.render({stats: {}})', function() {
......@@ -713,8 +778,8 @@ describe('api', function() {
describe('.info()', function() {
it('should return a correct version info', function(done) {
assert.equal(sass.info(), [
'node-sass version: ' + require('../package.json').version,
'libsass version: ' + require('../package.json').libsass
'node-sass version: ' + require('../package.json').version,
'libsass version: ' + require('../package.json').libsass
].join('\n'));
done();
......
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