Commit 0e168812 by Adeel Mujahid

Merge pull request #577 from am11/simplified_api_2

API: Simplified code usage (#547)
parents 4bea0703 cfef58f3
......@@ -5,7 +5,8 @@ var Emitter = require('events').EventEmitter,
meow = require('meow'),
replaceExt = require('replace-ext'),
stdin = require('get-stdin'),
render = require('../lib/render');
render = require('../lib/render'),
fs = require('fs');
/**
* Initialize CLI
......@@ -14,6 +15,8 @@ var Emitter = require('events').EventEmitter,
var cli = meow({
pkg: '../package.json',
help: [
require('../lib/').info(),
'',
'Usage',
' node-sass [options] <input.scss> [output.css]',
' cat <input.scss> | node-sass > output.css',
......@@ -37,6 +40,7 @@ var cli = meow({
' --image-path Path to prepend when using the `image-url()` helper',
' --precision The amount of precision allowed in decimal numbers',
' --stdout Print the resulting CSS to stdout',
' --importer Path to custom importer',
' --help Print usage info'
].join('\n')
}, {
......@@ -105,6 +109,10 @@ function getEmitter() {
console.log(data);
});
emitter.on('done', function(){
process.exit(0);
});
return emitter;
}
......@@ -202,6 +210,15 @@ function run(options, emitter) {
}
}
if (options.importer) {
if (fs.existsSync(options.importer)) {
options.importer = require(options.importer);
} else {
console.error('Could not locate importer.');
process.exit(1);
}
}
if (options.watch) {
watch(options, emitter);
} else {
......@@ -240,6 +257,7 @@ if (options.src) {
} else if (!process.stdin.isTTY) {
stdin(function(data) {
options.data = data;
options.stdin = true;
run(options, emitter);
});
}
......
......@@ -54,7 +54,7 @@ function getOutFile(options) {
*/
function getStats(options) {
var stats = options.stats;
var stats = {};
stats.entry = options.file || 'data';
stats.start = Date.now();
......@@ -65,17 +65,14 @@ function getStats(options) {
/**
* End stats
*
* @param {Object} options
* @param {Object} stats
* @param {Object} sourceMap
* @api private
*/
function endStats(options, sourceMap) {
var stats = options.stats || {};
function endStats(stats) {
stats.end = Date.now();
stats.duration = stats.end - stats.start;
stats.sourceMap = sourceMap;
return stats;
}
......@@ -139,15 +136,12 @@ function getOptions(options) {
options.paths = (options.include_paths || options.includePaths || []).join(path.delimiter);
options.precision = parseInt(options.precision) || 5;
options.sourceMap = getSourceMap(options);
options.stats = options.stats || {};
options.style = getStyle(options) || 0;
if (options.imagePath && typeof options.imagePath !== 'string') {
throw new Error('`imagePath` needs to be a string');
}
getStats(options);
var error = options.error;
var success = options.success;
var importer = options.importer;
......@@ -159,29 +153,41 @@ function getOptions(options) {
err = { message: err };
}
err.code = code;
if (error) {
error(err, code);
error(err);
}
};
options.success = function(css, sourceMap) {
sourceMap = JSON.parse(sourceMap);
options.success = function() {
options.result.sourceMap = JSON.parse(options.result.sourceMap);
endStats(options, sourceMap);
var stats = endStats(options.result.stats);
if (success) {
success(css, sourceMap);
success({
css: options.result.css,
map: options.result.sourceMap,
stats: stats
});
}
};
if (importer) {
options.importer = function(file, prev, key) {
importer(file, prev, function(data) {
var done = function(data) {
binding.importedCallback({
index: key,
objectLiteral: data
});
});
};
var result = importer(file, prev, done);
if (result) {
done(result);
}
};
}
......@@ -191,6 +197,10 @@ function getOptions(options) {
delete options.source_comments;
delete options.sourceComments;
options.result = {
stats: getStats(options)
};
return options;
}
......@@ -201,41 +211,6 @@ function getOptions(options) {
var binding = require(getBinding());
/**
* Render (deprecated)
*
* @param {String} css
* @param {Function} cb
* @param {Object} options
* @api private
*/
function deprecatedRender(css, cb, options) {
options = getOptions(options);
options.data = css;
options.error = cb;
options.success = function(css) {
cb(null, css);
};
binding.render(options);
}
/**
* Render sync (deprecated)
*
* @param {String} css
* @param {Object} options
* @api private
*/
function deprecatedRenderSync(css, options) {
options = getOptions(options);
options.data = css;
return binding.renderSync(options);
}
/**
* Render
*
* @param {Object} options
......@@ -243,12 +218,9 @@ function deprecatedRenderSync(css, options) {
*/
module.exports.render = function(options) {
if (typeof arguments[0] === 'string') {
return deprecatedRender.apply(this, arguments);
}
options = getOptions(options);
options.file ? binding.renderFile(options) : binding.render(options);
options.data ? binding.render(options) : binding.renderFile(options);
};
/**
......@@ -259,79 +231,27 @@ module.exports.render = function(options) {
*/
module.exports.renderSync = function(options) {
if (typeof arguments[0] === 'string') {
return deprecatedRenderSync.apply(this, arguments);
}
var output;
options = getOptions(options);
output = options.file ? binding.renderFileSync(options) : binding.renderSync(options);
endStats(options, JSON.parse(options.stats.sourceMap));
return output;
};
/**
* Render file
*
* `options.sourceMap` can be used to specify that the source map should be saved:
*
* - If falsy the source map will not be saved
* - If `options.sourceMap === true` the source map will be saved to the
* standard location of `options.file + '.map'`
* - Else `options.sourceMap` specifies the path (relative to the `outFile`)
* where the source map should be saved
*
* @param {Object} options
* @api public
*/
var result = options.data ? binding.renderSync(options) : binding.renderFileSync(options);
module.exports.renderFile = function(options) {
options = options || {};
if(result) {
options.result.stats = endStats(options.result.stats);
var outFile = options.outFile;
var success = options.success;
if (options.sourceMap === true) {
options.sourceMap = outFile + '.map';
return options.result;
}
options.success = function(css, sourceMap) {
fs.writeFile(outFile, css, function(err) {
if (err) {
return options.error(err);
}
if (!options.sourceMap) {
return success(outFile);
}
var dir = path.dirname(outFile);
var sourceMapFile = path.resolve(dir, options.sourceMap);
fs.writeFile(sourceMapFile, JSON.stringify(sourceMap), function(err) {
if (err) {
return options.error(err);
}
success(outFile, sourceMapFile);
});
});
};
module.exports.render(options);
};
/**
* Middleware
* API Info
*
* @api public
*/
module.exports.middleware = function() {
return new Error([
'The middleware has been moved to',
'https://github.com/sass/node-sass-middleware'
].join(' '));
module.exports.info = function() {
var package = require('../package.json');
return [
'node-sass version: ' + package.version,
'libsass version: ' + package.libsass
].join('\n');
};
......@@ -22,7 +22,8 @@ module.exports = function(options, emitter) {
sourceComments: options.sourceComments,
sourceMapEmbed: options.sourceMapEmbed,
sourceMapContents: options.sourceMapContents,
sourceMap: options.sourceMap
sourceMap: options.sourceMap,
importer: options.importer
};
if (options.src) {
......@@ -31,7 +32,7 @@ module.exports = function(options, emitter) {
renderOptions.data = options.data;
}
renderOptions.success = function(css, sourceMap) {
renderOptions.success = function(result) {
var todo = 1;
var done = function() {
if (--todo <= 0) {
......@@ -39,38 +40,38 @@ module.exports = function(options, emitter) {
}
};
if (options.stdout || (!options.dest && !process.stdout.isTTY)) {
emitter.emit('log', css);
if (options.stdout || (!options.dest && !process.stdout.isTTY) || options.stdin) {
emitter.emit('log', result.css);
return done();
}
emitter.emit('warn', chalk.green('Rendering Complete, saving .css file...'));
fs.writeFile(options.dest, css, function(err) {
fs.writeFile(options.dest, result.css, function(err) {
if (err) {
return emitter.emit('error', chalk.red(err));
}
emitter.emit('warn', chalk.green('Wrote CSS to ' + options.dest));
emitter.emit('write', err, options.dest, css);
emitter.emit('write', err, options.dest, result.css);
done();
});
if (options.sourceMap) {
todo++;
fs.writeFile(options.sourceMap, sourceMap, function(err) {
fs.writeFile(options.sourceMap, result.map, function(err) {
if (err) {
return emitter.emit('error', chalk.red('Error' + err));
}
emitter.emit('warn', chalk.green('Wrote Source Map to ' + options.sourceMap));
emitter.emit('write-source-map', err, options.sourceMap, sourceMap);
emitter.emit('write-source-map', err, options.sourceMap, result.sourceMap);
done();
});
}
emitter.emit('render', css);
emitter.emit('render', result.css);
};
renderOptions.error = function(error) {
......
{
"name": "node-sass",
"version": "1.2.3",
"version": "2.0.0-beta",
"libsass": "3.1.0-beta",
"description": "Wrapper around libsass",
"license": "MIT",
"homepage": "https://github.com/sass/node-sass",
......@@ -50,7 +51,7 @@
"mkdirp": "^0.5.0",
"mocha": "^2.0.1",
"nan": "^1.3.0",
"object-assign": "^1.0.0",
"object-assign": "^2.0.0",
"replace-ext": "0.0.1",
"request": "^2.48.0",
"shelljs": "^0.3.0"
......
......@@ -15,7 +15,7 @@ char* CreateString(Local<Value> value) {
std::vector<sass_context_wrapper*> imports_collection;
void dispatched_async_uv_callback(uv_async_t *req){
void dispatched_async_uv_callback(uv_async_t *req) {
NanScope();
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
......@@ -24,8 +24,8 @@ void dispatched_async_uv_callback(uv_async_t *req){
imports_collection.push_back(ctx_w);
Handle<Value> argv[] = {
NanNew<String>(strdup(ctx_w->file)),
NanNew<String>(strdup(ctx_w->prev)),
NanNew<String>(strdup(ctx_w->file ? ctx_w->file : 0)),
NanNew<String>(strdup(ctx_w->prev ? ctx_w->prev : 0)),
NanNew<Number>(imports_collection.size() - 1)
};
......@@ -40,8 +40,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
{
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(cookie);
ctx_w->file = strdup(file);
ctx_w->prev = strdup(prev);
ctx_w->file = file ? strdup(file) : 0;
ctx_w->prev = prev ? strdup(prev) : 0;
ctx_w->async.data = (void*)ctx_w;
uv_async_send(&ctx_w->async);
......@@ -52,7 +52,7 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
*/
uv_cond_wait(&ctx_w->importer_condition_variable, &ctx_w->importer_mutex);
}
else{
else {
/* that is sync: RenderSync() or RenderFileSync,
* we need to explicitly uv_run as the event loop
* is blocked; waiting down the chain.
......@@ -66,6 +66,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx_w, bool isFile, bool isSync) {
struct Sass_Context* ctx;
NanAssignPersistent(ctx_w->result, options->Get(NanNew("result"))->ToObject());
if (isFile) {
ctx = sass_file_context_get_context((struct Sass_File_Context*) cptr);
ctx_w->fctx = (struct Sass_File_Context*) cptr;
......@@ -78,8 +80,6 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* 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
......@@ -99,6 +99,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w));
}
sass_option_set_input_path(sass_options, CreateString(options->Get(NanNew("file"))));
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());
......@@ -112,7 +113,7 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
sass_option_set_precision(sass_options, options->Get(NanNew("precision"))->Int32Value());
}
void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
void GetStats(Handle<Object> result, Sass_Context* ctx) {
char** included_files = sass_context_get_included_files(ctx);
Handle<Array> arr = NanNew<Array>();
......@@ -122,8 +123,10 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
}
}
(*stats)->Set(NanNew("includedFiles"), arr);
(*result)->Get(NanNew("stats"))->ToObject()->Set(NanNew("includedFiles"), arr);
}
void GetSourceMap(Handle<Object> result, Sass_Context* ctx) {
Handle<Value> source_map;
if (sass_context_get_error_status(ctx)) {
......@@ -137,7 +140,19 @@ void FillStatsObj(Handle<Object> stats, Sass_Context* ctx) {
source_map = NanNew<String>("{}");
}
(*stats)->Set(NanNew("sourceMap"), source_map);
(*result)->Set(NanNew("sourceMap"), source_map);
}
int GetResult(Handle<Object> result, Sass_Context* ctx) {
int status = sass_context_get_error_status(ctx);
if (status == 0) {
(*result)->Set(NanNew("css"), NanNew<String>(sass_context_get_output_string(ctx)));
GetStats(result, ctx);
GetSourceMap(result, ctx);
}
return status;
}
void make_callback(uv_work_t* req) {
......@@ -145,35 +160,27 @@ void make_callback(uv_work_t* req) {
TryCatch try_catch;
sass_context_wrapper* ctx_w = static_cast<sass_context_wrapper*>(req->data);
int error_status;
struct Sass_Context* ctx;
if (ctx_w->dctx) {
ctx = sass_data_context_get_context(ctx_w->dctx);
FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx);
}
else {
ctx = sass_file_context_get_context(ctx_w->fctx);
FillStatsObj(NanNew(ctx_w->stats), ctx);
error_status = sass_context_get_error_status(ctx);
}
if (error_status == 0) {
int status = GetResult(ctx_w->result, ctx);
if (status == 0) {
// if no error, do callback(null, result)
const char* val = sass_context_get_output_string(ctx);
Local<Value> argv[] = {
NanNew<String>(val),
NanNew(ctx_w->stats)->Get(NanNew("sourceMap"))
};
ctx_w->success_callback->Call(2, argv);
ctx_w->success_callback->Call(0, 0);
}
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)
NanNew<Integer>(status)
};
ctx_w->error_callback->Call(2, argv);
}
......@@ -212,21 +219,22 @@ NAN_METHOD(RenderSync) {
ExtractOptions(options, dctx, ctx_w, false, true);
compile_data(dctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
if (sass_context_get_error_status(ctx) == 0) {
Local<String> output = NanNew<String>(sass_context_get_output_string(ctx));
int result = GetResult(ctx_w->result, ctx);
Local<String> error;
sass_delete_data_context(dctx);
NanReturnValue(output);
if (result != 0) {
error = NanNew<String>(sass_context_get_error_json(ctx));
}
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_free_context_wrapper(ctx_w);
NanThrowError(error);
free(source_string);
NanReturnUndefined();
if (result != 0) {
NanThrowError(error);
}
NanReturnValue(NanNew<Boolean>(result == 0));
}
NAN_METHOD(RenderFile) {
......@@ -258,22 +266,22 @@ NAN_METHOD(RenderFileSync) {
ExtractOptions(options, fctx, ctx_w, true, true);
compile_file(fctx);
FillStatsObj(options->Get(NanNew("stats"))->ToObject(), ctx);
free(input_path);
if (sass_context_get_error_status(ctx) == 0) {
Local<String> output = NanNew<String>(sass_context_get_output_string(ctx));
int result = GetResult(ctx_w->result, ctx);
Local<String> error;
sass_delete_file_context(fctx);
NanReturnValue(output);
if (result != 0) {
error = NanNew<String>(sass_context_get_error_json(ctx));
}
Local<String> error = NanNew<String>(sass_context_get_error_json(ctx));
sass_free_context_wrapper(ctx_w);
NanThrowError(error);
free(input_path);
NanReturnUndefined();
if (result != 0) {
NanThrowError(error);
}
NanReturnValue(NanNew<Boolean>(result == 0));
}
NAN_METHOD(ImportedCallback) {
......@@ -282,9 +290,7 @@ NAN_METHOD(ImportedCallback) {
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()) {
......
Subproject commit 7aaa45a979ce65d50b3158ff50bf9409f8955063
Subproject commit cf7c1d14fec91a66ab9c2a3050dcec5fab6198f5
......@@ -37,14 +37,13 @@ extern "C" {
sass_delete_file_context(ctx_w->fctx);
}
NanDisposePersistent(ctx_w->stats);
NanDisposePersistent(ctx_w->result);
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);
......
......@@ -14,7 +14,7 @@ extern "C" {
struct sass_context_wrapper {
Sass_Data_Context* dctx;
Sass_File_Context* fctx;
Persistent<Object> stats;
Persistent<Object> result;
uv_work_t request;
uv_mutex_t importer_mutex;
uv_cond_t importer_condition_variable;
......
......@@ -6,79 +6,28 @@ var assert = require('assert'),
fixture = path.join.bind(null, __dirname, 'fixtures'),
resolveFixture = path.resolve.bind(null, __dirname, 'fixtures');
describe('api (deprecated)', function() {
describe('.render(src, fn)', function() {
it('should compile sass to css', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
sass.render(src, function(err, css) {
assert(!err);
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
done();
});
});
it('should compile sass to css using indented syntax', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8');
var expected = read(fixture('indent/expected.css'), 'utf8').trim();
sass.render(src, function(err, css) {
assert(!err);
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}, {
indentedSyntax: true
});
});
it('should throw error for bad input', function(done) {
sass.render('#navbar width 80%;', function(err) {
assert(err);
done();
});
});
});
describe('.renderSync(src)', function() {
it('should compile sass to css', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
describe('api', function() {
describe('.render(options)', function() {
it('should compile sass to css with file', function(done) {
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var css = sass.renderSync(src).trim();
assert.equal(css, expected.replace(/\r\n/g, '\n'));
done();
});
it('should compile sass to css using indented syntax', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8');
var expected = read(fixture('indent/expected.css'), 'utf8').trim();
var css = sass.renderSync(src, {indentedSyntax: true}).trim();
assert.equal(css, expected.replace(/\r\n/g, '\n'));
done();
});
it('should throw error for bad input', function(done) {
assert.throws(function() {
sass.renderSync('#navbar width 80%;');
sass.render({
file: fixture('simple/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
done();
});
});
});
describe('api', function() {
describe('.render(options)', function() {
it('should compile sass to css', function(done) {
it('should compile sass to css with data', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
sass.render({
data: src,
success: function(css) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
......@@ -91,8 +40,8 @@ describe('api', function() {
sass.render({
data: src,
indentedSyntax: true,
success: function(css) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
......@@ -101,9 +50,9 @@ describe('api', function() {
it('should throw error status 1 for bad input', function(done) {
sass.render({
data: '#navbar width 80%;',
error: function(err, status) {
assert(err);
assert.equal(status, 1);
error: function(error) {
assert(error.message);
assert.equal(error.status, 1);
done();
}
});
......@@ -119,8 +68,8 @@ describe('api', function() {
fixture('include-path/functions'),
fixture('include-path/lib')
],
success: function(css) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
......@@ -133,8 +82,8 @@ describe('api', function() {
sass.render({
data: src,
imagePath: '/path/to/images',
success: function(css) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
......@@ -160,55 +109,234 @@ describe('api', function() {
sass.render({
data: src,
precision: 10,
success: function(css) {
assert.equal(css.trim(), expected.replace(/\r\n/g, '\n'));
success: function(result) {
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
}
});
});
it('should compile with stats', function(done) {
var src = fixture('precision/index.scss');
var stats = {};
it('should contain all included files in stats when data is passed', function(done) {
var src = read(fixture('include-files/index.scss'), 'utf8');
var expected = [
fixture('include-files/bar.scss').replace(/\\/g, '/'),
fixture('include-files/foo.scss').replace(/\\/g, '/')
];
sass.render({
file: src,
stats: stats,
sourceMap: true,
success: function() {
assert.equal(stats.entry, src);
data: src,
includePaths: [fixture('include-files')],
success: function(result) {
assert.deepEqual(result.stats.includedFiles, expected);
done();
}
});
});
});
it('should contain all included files in stats when data is passed', function(done) {
var src = fixture('include-files/index.scss');
var stats = {};
var expected = [
fixture('include-files/bar.scss').replace(/\\/g, '/'),
fixture('include-files/foo.scss').replace(/\\/g, '/')
];
describe('.render(importer)', function() {
var src = read(fixture('include-files/index.scss'), 'utf8');
it('should override imports with "data" as input and fires callback with file and contents', function(done) {
sass.render({
data: read(src, 'utf8'),
includePaths: [fixture('include-files')],
stats: stats,
success: function() {
assert.deepEqual(stats.includedFiles, expected);
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "file" as input and fires callback with file and contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "data" as input and returns file and contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "file" as input and returns file and contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "data" as input and fires callback with file', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, /* jshint unused:false */ prev, done) {
done({
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
});
}
});
});
it('should override imports with "file" as input and fires callback with file', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, prev, done) {
done({
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
});
}
});
});
it('should override imports with "data" as input and returns file', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, /* jshint unused:false */ prev) {
return {
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
};
}
});
});
it('should override imports with "file" as input and returns file', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), '');
done();
},
importer: function(url, prev) {
return {
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
};
}
});
});
it('should override imports with "data" as input and fires callback with contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "file" as input and fires callback with contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
});
it('should override imports with "data" as input and returns contents', function(done) {
sass.render({
data: src,
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
});
it('should override imports with "file" as input and returns contents', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
},
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
});
});
describe('.renderSync(options)', function() {
it('should compile sass to css', function(done) {
it('should compile sass to css with file', function(done) {
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var result = sass.renderSync({file: fixture('simple/index.scss')});
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
});
it('should compile sass to css with data', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
var css = sass.renderSync({data: src}).trim();
var result = sass.renderSync({data: src});
assert.equal(css, expected.replace(/\r\n/g, '\n'));
assert.equal(result.css.trim(), expected.replace(/\r\n/g, '\n'));
done();
});
......@@ -218,7 +346,7 @@ describe('api', function() {
var css = sass.renderSync({
data: src,
indentedSyntax: true
}).trim();
}).css.trim();
assert.equal(css, expected.replace(/\r\n/g, '\n'));
done();
......@@ -233,109 +361,191 @@ describe('api', function() {
});
});
describe('.renderFile(options)', function() {
it('should compile sass to css', function(done) {
var src = read(fixture('simple/index.scss'), 'utf8');
var dest = fixture('simple/build.css');
var expected = read(fixture('simple/expected.css'), 'utf8').trim();
describe('.renderSync(importer)', function() {
var src = read(fixture('include-files/index.scss'), 'utf8');
sass.renderFile({
it('should override imports with "data" as input and fires callback with file and contents', function(done) {
var result = sass.renderSync({
data: src,
outFile: dest,
success: function() {
assert.equal(read(dest, 'utf8').trim(), expected.replace(/\r\n/g, '\n'));
fs.unlinkSync(dest);
done();
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should compile sass to css using indented syntax', function(done) {
var src = read(fixture('indent/index.sass'), 'utf8');
var dest = fixture('indent/build.css');
var expected = read(fixture('indent/expected.css'), 'utf8').trim();
it('should override imports with "file" as input and fires callback with file and contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev, done) {
done({
file: '/some/other/path.scss',
contents: 'div {color: yellow;}'
});
}
});
sass.renderFile({
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "data" as input and returns file and contents', function(done) {
var result = sass.renderSync({
data: src,
outFile: dest,
indentedSyntax: true,
success: function() {
assert.equal(read(dest, 'utf8').trim(), expected.replace(/\r\n/g, '\n'));
fs.unlinkSync(dest);
done();
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should save source map to default name', function(done) {
var src = fixture('source-map/index.scss');
var dest = fixture('source-map/build.css');
var name = 'build.css.map';
it('should override imports with "file" as input and returns file and contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev) {
return {
file: prev + url,
contents: 'div {color: yellow;}'
};
}
});
sass.renderFile({
file: src,
outFile: dest,
sourceMap: true,
success: function(file, map) {
assert.equal(path.basename(map), name);
assert(read(dest, 'utf8').indexOf('sourceMappingURL=' + name) !== -1);
fs.unlinkSync(map);
fs.unlinkSync(dest);
done();
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "data" as input and fires callback with file', function(done) {
var result = sass.renderSync({
data: src,
importer: function(url, /* jshint unused:false */ prev, done) {
done({
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
});
}
});
assert.equal(result.css.trim(), '');
done();
});
it('should save source map to specified name', function(done) {
var src = fixture('source-map/index.scss');
var dest = fixture('source-map/build.css');
var name = 'foo.css.map';
it('should override imports with "file" as input and fires callback with file', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev, done) {
done({
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
});
}
});
sass.renderFile({
file: src,
outFile: dest,
sourceMap: name,
success: function(file, map) {
assert.equal(path.basename(map), name);
assert(read(dest, 'utf8').indexOf('sourceMappingURL=' + name) !== -1);
fs.unlinkSync(map);
fs.unlinkSync(dest);
done();
assert.equal(result.css.trim(), '');
done();
});
it('should override imports with "data" as input and returns file', function(done) {
var result = sass.renderSync({
data: src,
importer: function(url, /* jshint unused:false */ prev) {
return {
file: path.resolve(path.dirname(fixture('include-files/index.scss')), url + (path.extname(url) ? '' : '.scss'))
};
}
});
assert.equal(result.css.trim(), '');
done();
});
it('should save source paths relative to the source map file', function(done) {
var src = fixture('include-files/index.scss');
var dest = fixture('include-files/build.css');
var obj;
it('should override imports with "file" as input and returns file', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev) {
return {
file: path.resolve(path.dirname(prev), url + (path.extname(url) ? '' : '.scss'))
};
}
});
sass.renderFile({
file: src,
outFile: dest,
sourceMap: true,
success: function(file, map) {
obj = JSON.parse(read(map, 'utf8'));
assert.equal(obj.sources[0], 'index.scss');
assert.equal(obj.sources[1], 'foo.scss');
assert.equal(obj.sources[2], 'bar.scss');
fs.unlinkSync(map);
fs.unlinkSync(dest);
done();
assert.equal(result.css.trim(), '');
done();
});
it('should override imports with "data" as input and fires callback with contents', function(done) {
var result = sass.renderSync({
data: src,
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "file" as input and fires callback with contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function(url, prev, done) {
done({
contents: 'div {color: yellow;}'
});
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "data" as input and returns contents', function(done) {
var result = sass.renderSync({
data: src,
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
it('should override imports with "file" as input and returns contents', function(done) {
var result = sass.renderSync({
file: fixture('include-files/index.scss'),
importer: function() {
return {
contents: 'div {color: yellow;}'
};
}
});
assert.equal(result.css.trim(), 'div {\n color: yellow; }\n\ndiv {\n color: yellow; }');
done();
});
});
describe('.render({stats: {}})', function() {
var start = Date.now();
var stats = {};
before(function(done) {
it('should provide a start timestamp', function(done) {
sass.render({
file: fixture('include-files/index.scss'),
stats: stats,
success: function() {
success: function(result) {
assert(typeof result.stats.start === 'number');
assert(result.stats.start >= start);
done();
},
error: function(err) {
......@@ -345,27 +555,48 @@ describe('api', function() {
});
});
it('should provide a start timestamp', function(done) {
assert(typeof stats.start === 'number');
assert(stats.start >= start);
done();
});
it('should provide an end timestamp', function(done) {
assert(typeof stats.end === 'number');
assert(stats.end >= stats.start);
done();
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert(typeof result.stats.end === 'number');
assert(result.stats.end >= result.stats.start);
done();
},
error: function(err) {
assert(!err);
done();
}
});
});
it('should provide a duration', function(done) {
assert(typeof stats.duration === 'number');
assert.equal(stats.end - stats.start, stats.duration);
done();
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert(typeof result.stats.duration === 'number');
assert.equal(result.stats.end - result.stats.start, result.stats.duration);
done();
},
error: function(err) {
assert(!err);
done();
}
});
});
it('should contain the given entry file', function(done) {
assert.equal(stats.entry, fixture('include-files/index.scss'));
done();
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.equal(result.stats.entry, fixture('include-files/index.scss'));
done();
},
error: function(err) {
assert(!err);
done();
}
});
});
it('should contain an array of all included files', function(done) {
......@@ -375,8 +606,17 @@ describe('api', function() {
fixture('include-files/index.scss').replace(/\\/g, '/')
];
assert.deepEqual(stats.includedFiles, expected);
done();
sass.render({
file: fixture('include-files/index.scss'),
success: function(result) {
assert.deepEqual(result.stats.includedFiles, expected);
done();
},
error: function(err) {
assert(!err);
done();
}
});
});
it('should contain array with the entry if there are no import statements', function(done) {
......@@ -384,9 +624,8 @@ describe('api', function() {
sass.render({
file: fixture('simple/index.scss'),
stats: stats,
success: function() {
assert.deepEqual(stats.includedFiles, [expected]);
success: function(result) {
assert.deepEqual(result.stats.includedFiles, [expected]);
done();
}
});
......@@ -395,9 +634,8 @@ describe('api', function() {
it('should state `data` as entry file', function(done) {
sass.render({
data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats,
success: function() {
assert.equal(stats.entry, 'data');
success: function(result) {
assert.equal(result.stats.entry, 'data');
done();
}
});
......@@ -406,22 +644,8 @@ describe('api', function() {
it('should contain an empty array as includedFiles', function(done) {
sass.render({
data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats,
success: function() {
assert.deepEqual(stats.includedFiles, []);
done();
}
});
});
it('should report correct source map in stats', function(done) {
sass.render({
file: fixture('simple/index.scss'),
outFile: fixture('simple/build.css'),
stats: stats,
sourceMap: true,
success: function() {
assert.equal(stats.sourceMap.sources[0], 'index.scss');
success: function(result) {
assert.deepEqual(result.stats.includedFiles, []);
done();
}
});
......@@ -430,35 +654,30 @@ describe('api', function() {
describe('.renderSync({stats: {}})', function() {
var start = Date.now();
var stats = {};
before(function() {
sass.renderSync({
file: fixture('include-files/index.scss'),
stats: stats
});
var result = sass.renderSync({
file: fixture('include-files/index.scss')
});
it('should provide a start timestamp', function(done) {
assert(typeof stats.start === 'number');
assert(stats.start >= start);
assert(typeof result.stats.start === 'number');
assert(result.stats.start >= start);
done();
});
it('should provide an end timestamp', function(done) {
assert(typeof stats.end === 'number');
assert(stats.end >= stats.start);
assert(typeof result.stats.end === 'number');
assert(result.stats.end >= result.stats.start);
done();
});
it('should provide a duration', function(done) {
assert(typeof stats.duration === 'number');
assert.equal(stats.end - stats.start, stats.duration);
assert(typeof result.stats.duration === 'number');
assert.equal(result.stats.end - result.stats.start, result.stats.duration);
done();
});
it('should contain the given entry file', function(done) {
assert.equal(stats.entry, resolveFixture('include-files/index.scss'));
assert.equal(result.stats.entry, resolveFixture('include-files/index.scss'));
done();
});
......@@ -469,60 +688,49 @@ describe('api', function() {
fixture('include-files/index.scss').replace(/\\/g, '/')
];
assert.equal(stats.includedFiles[0], expected[0]);
assert.equal(stats.includedFiles[1], expected[1]);
assert.equal(stats.includedFiles[2], expected[2]);
assert.equal(result.stats.includedFiles[0], expected[0]);
assert.equal(result.stats.includedFiles[1], expected[1]);
assert.equal(result.stats.includedFiles[2], expected[2]);
done();
});
it('should contain array with the entry if there are no import statements', function(done) {
var expected = fixture('simple/index.scss').replace(/\\/g, '/');
sass.renderSync({
file: fixture('simple/index.scss'),
stats: stats
var result = sass.renderSync({
file: fixture('simple/index.scss')
});
assert.deepEqual(stats.includedFiles, [expected]);
assert.deepEqual(result.stats.includedFiles, [expected]);
done();
});
it('should state `data` as entry file', function(done) {
sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats
var result = sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8')
});
assert.equal(stats.entry, 'data');
assert.equal(result.stats.entry, 'data');
done();
});
it('should contain an empty array as includedFiles', function(done) {
sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8'),
stats: stats
var result = sass.renderSync({
data: read(fixture('simple/index.scss'), 'utf8')
});
assert.deepEqual(stats.includedFiles, []);
done();
});
it('should report correct source map in stats', function(done) {
sass.renderSync({
file: fixture('simple/index.scss'),
outFile: fixture('simple/build.css'),
stats: stats,
sourceMap: true
});
assert.equal(stats.sourceMap.sources[0], 'index.scss');
assert.deepEqual(result.stats.includedFiles, []);
done();
});
});
describe('.middleware()', function() {
it('should throw error on require', function(done) {
assert.throws(sass.middleware());
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
].join('\n'));
done();
});
});
......
......@@ -223,4 +223,106 @@ describe('cli', function() {
});
});
});
describe('importer', function() {
var dest = fixture('include-files/index.css');
var src = fixture('include-files/index.scss');
var expected = read(fixture('include-files/expected-importer.css'), 'utf8').trim().replace(/\r\n/g, '\n');
it('should override imports and fire callback with file and contents', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_and_data_cb.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and fire callback with file', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_cb.js')
]);
bin.on('close', function () {
if (fs.existsSync(dest)) {
assert.equal(read(dest, 'utf8').trim(), '');
fs.unlinkSync(dest);
}
done();
});
});
it('should override imports and fire callback with data', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_data_cb.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and return file and contents', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file_and_data.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should override imports and return file', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_file.js')
]);
bin.on('close', function () {
if (fs.existsSync(dest)) {
assert.equal(read(dest, 'utf8').trim(), '');
fs.unlinkSync(dest);
}
done();
});
});
it('should override imports and return data', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('extras/my_custom_importer_data.js')
]);
bin.on('close', function () {
assert.equal(read(dest, 'utf8').trim(), expected);
fs.unlinkSync(dest);
done();
});
});
it('should return error on for invalid importer file path', function(done) {
var bin = spawn(cli, [
src, '--output', path.dirname(dest),
'--importer', fixture('non/existing/path')
]);
bin.on('close', function (code) {
assert(code !== 0);
done();
});
});
});
});
module.exports = function() {
return {
contents: 'div {color: yellow;}'
};
};
module.exports = function(file, prev, done) {
done({
contents: 'div {color: yellow;}'
});
};
var path = require('path');
module.exports = function(file) {console.log('>>>>>>>>>>');console.log(path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss'))));
return {
file: path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss')))
};
};
module.exports = function() {
return {
file: '/some/random/path/file.scss',
contents: 'div {color: yellow;}'
};
};
module.exports = function(file, prev, done) {
done({
file: '/some/random/path/file.scss',
contents: 'div {color: yellow;}'
});
};
var path = require('path');
module.exports = function(file, /* jshint unused:false */ prev, done) {
done({
file: path.resolve(path.join(process.cwd(), 'test/fixtures/include-files/', file + (path.extname(file) ? '' : '.scss')))
});
};
div {
color: yellow; }
div {
color: yellow; }
Subproject commit 074ae3b24bfc3ff19a98efdec02415ed6fd5759a
Subproject commit 00c6154dba85a38bb03b89d7279f439feb055821
......@@ -40,8 +40,8 @@ describe('spec', function () {
sass.render({
file: t.src,
includePaths: t.paths,
success: function(css) {
assert.equal(util.normalize(css), expected);
success: function(result) {
assert.equal(util.normalize(result.css), expected);
done();
},
error: function(err) {
......
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