Commit 1e075931 by Adeel Mujahid

Merge pull request #792 from am11/master

Feature: Configurable linefeed and indent
parents 0558325e 279d14bb
......@@ -89,7 +89,7 @@ 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`
### functions
### 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:
#### types.Number(value [, unit = ""])
......@@ -163,6 +163,25 @@ Default: `false`
`true` values enable [Sass Indented Syntax](http://sass-lang.com/documentation/file.INDENTED_SYNTAX.html) for parsing the data string or file.
### indentType (>= v3.0.0)
Type: `String`
Default: `space`
Used to determine whether to use space or tab character for indentation.
### indentWidth (>= v3.0.0)
Type: `Number`
Default: `2`
Maximum: `10`
Used to determine the number of spaces or tabs to be used for indentation.
### linefeed (>= v3.0.0)
Type: `String`
Default: `lf`
Used to determine whether to use `cr`, `crlf`, `lf` or `lfcr` sequence for line break.
### omitSourceMapUrl
Type: `Boolean`
Default: `false`
......@@ -404,7 +423,8 @@ The interface for command-line usage is fairly simplistic at this stage, as seen
Output will be saved with the same name as input SASS file into the current working directory if it's omitted.
### Usage
`node-sass [options] <input.scss> [<output.css>]`
`node-sass [options] <input.scss> [output.css]`
`cat <input.scss> | node-sass > output.css`
**Options:**
......@@ -415,7 +435,10 @@ Output will be saved with the same name as input SASS file into the current work
-x, --omit-source-map-url Omit source map URL comment from output
-i, --indented-syntax Treat data from stdin as sass code (versus scss)
-v, --version Prints version info
--output-style CSS output style (nested|expanded|compact|compressed)
--output-style CSS output style (nested | expanded | compact | compressed)
--indent-type Indent type for output CSS (space | tab)
--indent-width Indent width; number of spaces or tabs (maximum value: 10)
--linefeed Linefeed style (cr | crlf | lf | lfcr)
--source-comments Include debug info in output
--source-map Emit source map
--source-map-embed Embed sourceMappingUrl as data URI
......
......@@ -31,7 +31,10 @@ var cli = meow({
' -x, --omit-source-map-url Omit source map URL comment from output',
' -i, --indented-syntax Treat data from stdin as sass code (versus scss)',
' -v, --version Prints version info',
' --output-style CSS output style (nested|expanded|compact|compressed)',
' --output-style CSS output style (nested | expanded | compact | compressed)',
' --indent-type Indent type for output CSS (space | tab)',
' --indent-width Indent width; number of spaces or tabs (maximum value: 10)',
' --linefeed Linefeed style (cr | crlf | lf | lfcr)',
' --source-comments Include debug info in output',
' --source-map Emit source map',
' --source-map-embed Embed sourceMappingUrl as data URI',
......@@ -52,6 +55,8 @@ var cli = meow({
],
string: [
'include-path',
'indent-type',
'linefeed',
'output',
'output-style',
'precision',
......@@ -68,6 +73,9 @@ var cli = meow({
},
default: {
'include-path': process.cwd(),
'indent-type': 'space',
'indent-width': 2,
linefeed: 'lf',
'output-style': 'nested',
precision: 5
}
......
......@@ -97,7 +97,6 @@ function endStats(stats) {
*/
function getStyle(options) {
var style = options.outputStyle;
var styles = {
nested: 0,
expanded: 1,
......@@ -105,7 +104,54 @@ function getStyle(options) {
compressed: 3
};
return styles[style];
return styles[options.outputStyle] || 0;
}
/**
* Get indent width
*
* @param {Object} options
* @api private
*/
function getIndentWidth(options) {
var width = parseInt(options.indentWidth) || 2;
return width > 10 ? 2 : width;
}
/**
* Get indent type
*
* @param {Object} options
* @api private
*/
function getIndentType(options) {
var types = {
space: 0,
tab: 1
};
return types[options.indentType] || 0;
}
/**
* Get linefeed
*
* @param {Object} options
* @api private
*/
function getLinefeed(options) {
var feeds = {
cr: '\r',
crlf: '\r\n',
lf: '\n',
lfcr: '\n\r'
};
return feeds[options.linefeed] || '\n';
}
/**
......@@ -124,7 +170,10 @@ function getOptions(options, cb) {
options.includePaths = (options.includePaths || []).join(path.delimiter);
options.precision = parseInt(options.precision) || 5;
options.sourceMap = getSourceMap(options);
options.style = getStyle(options) || 0;
options.style = getStyle(options);
options.indentWidth = getIndentWidth(options);
options.indentType = getIndentType(options);
options.linefeed = getLinefeed(options);
// context object represents node-sass environment
options.context = { options: options, callback: cb };
......@@ -192,7 +241,7 @@ function normalizeFunctionSignature(signature, callback) {
}
return {
signature: signature,
signature: signature,
callback: callback
};
}
......@@ -332,4 +381,4 @@ module.exports.info = process.sass.versionInfo;
module.exports.types = binding.types;
module.exports.TRUE = binding.types.Boolean.TRUE;
module.exports.FALSE = binding.types.Boolean.FALSE;
module.exports.NULL = binding.types.Null.NULL;
\ No newline at end of file
module.exports.NULL = binding.types.Null.NULL;
......@@ -28,8 +28,11 @@ module.exports = function(options, emitter) {
sourceMapEmbed: options.sourceMapEmbed,
sourceMapContents: options.sourceMapContents,
sourceMap: options.sourceMap,
importer: options.importer
};
importer: options.importer,
indentWidth: options.indentWidth,
indentType: options.indentType,
linefeed: options.linefeed
};
if (options.data) {
renderOptions.data = options.data;
......
......@@ -11,8 +11,8 @@ struct Sass_Import** sass_importer(const char* file, const char* prev, void* coo
CustomImporterBridge& bridge = *(ctx_w->importer_bridge);
std::vector<void*> argv;
argv.push_back((void*) file);
argv.push_back((void*) prev);
argv.push_back((void*)file);
argv.push_back((void*)prev);
return bridge(argv);
}
......@@ -23,12 +23,13 @@ union Sass_Value* sass_custom_function(const union Sass_Value* s_args, void* coo
std::vector<void*> argv;
for (unsigned l = sass_list_get_length(s_args), i = 0; i < l; i++) {
argv.push_back((void*) sass_list_get_value(s_args, i));
argv.push_back((void*)sass_list_get_value(s_args, i));
}
try {
return bridge(argv);
} catch (const std::exception& e) {
}
catch (const std::exception& e) {
return sass_make_error(e.what());
}
}
......@@ -72,19 +73,36 @@ void ExtractOptions(Local<Object> options, void* cptr, sass_context_wrapper* ctx
}
if (!is_file) {
sass_option_set_input_path(sass_options, create_string(options->Get(NanNew("file"))));
ctx_w->file = create_string(options->Get(NanNew("file")));
sass_option_set_input_path(sass_options, ctx_w->file);
}
sass_option_set_output_path(sass_options, create_string(options->Get(NanNew("outFile"))));
int indent_len = options->Get(NanNew("indentWidth"))->Int32Value();
ctx_w->indent = (char*)malloc(indent_len + 1);
strcpy(ctx_w->indent, std::string(
indent_len,
options->Get(NanNew("indentType"))->Int32Value() == 1 ? '\t' : ' '
).c_str());
ctx_w->linefeed = create_string(options->Get(NanNew("linefeed")));
ctx_w->include_path = create_string(options->Get(NanNew("includePaths")));
ctx_w->out_file = create_string(options->Get(NanNew("outFile")));
ctx_w->source_map = create_string(options->Get(NanNew("sourceMap")));
sass_option_set_output_path(sass_options, ctx_w->out_file);
sass_option_set_output_style(sass_options, (Sass_Output_Style)options->Get(NanNew("style"))->Int32Value());
sass_option_set_is_indented_syntax_src(sass_options, options->Get(NanNew("indentedSyntax"))->BooleanValue());
sass_option_set_source_comments(sass_options, options->Get(NanNew("sourceComments"))->BooleanValue());
sass_option_set_omit_source_map_url(sass_options, options->Get(NanNew("omitSourceMapUrl"))->BooleanValue());
sass_option_set_source_map_embed(sass_options, options->Get(NanNew("sourceMapEmbed"))->BooleanValue());
sass_option_set_source_map_contents(sass_options, options->Get(NanNew("sourceMapContents"))->BooleanValue());
sass_option_set_source_map_file(sass_options, create_string(options->Get(NanNew("sourceMap"))));
sass_option_set_include_path(sass_options, create_string(options->Get(NanNew("includePaths"))));
sass_option_set_source_map_file(sass_options, ctx_w->source_map);
sass_option_set_include_path(sass_options, ctx_w->include_path);
sass_option_set_precision(sass_options, options->Get(NanNew("precision"))->Int32Value());
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")));
......
......@@ -39,7 +39,13 @@ extern "C" {
NanDisposePersistent(ctx_w->result);
if(string) {
free(ctx_w->include_path);
free(ctx_w->linefeed);
free(ctx_w->out_file);
free(ctx_w->source_map);
free(ctx_w->indent);
if (string) {
free(string);
}
......
......@@ -23,6 +23,12 @@ extern "C" {
// binding related
bool is_sync;
void* cookie;
char* file;
char* include_path;
char* out_file;
char* source_map;
char* linefeed;
char* indent;
// libsass related
Sass_Data_Context* dctx;
......
......@@ -103,7 +103,7 @@ describe('api', function() {
});
});
it('should render with --precision option', function(done) {
it('should render with precision option', function(done) {
var src = read(fixture('precision/index.scss'), 'utf8');
var expected = read(fixture('precision/expected.css'), 'utf8').trim();
......@@ -131,6 +131,27 @@ describe('api', function() {
done();
});
});
it('should render with indentWidth and indentType options', function(done) {
sass.render({
data: 'div { color: transparent; }',
indentWidth: 7,
indentType: 'tab'
}, function(error, result) {
assert.equal(result.css.toString().trim(), 'div {\n\t\t\t\t\t\t\tcolor: transparent; }');
done();
});
});
it('should render with linefeed option', function(done) {
sass.render({
data: 'div { color: transparent; }',
linefeed: 'lfcr'
}, function(error, result) {
assert.equal(result.css.toString().trim(), 'div {\n\r color: transparent; }');
done();
});
});
});
describe('.render(importer)', function() {
......@@ -441,7 +462,7 @@ describe('api', function() {
functions: {
'foo($a)': function(str) {
var unquoted = str.getValue().replace(/['"]/g, '');
str.setValue('"' + unquoted + unquoted + '"');
str.setValue('"' + unquoted + unquoted + '"');
return str;
}
}
......@@ -472,8 +493,8 @@ describe('api', function() {
}
}, function(error, result) {
assert.equal(
result.css.toString().trim(),
'div {\n color: rgba(255, 255, 0, 0.5);' +
result.css.toString().trim(),
'div {\n color: rgba(255, 255, 0, 0.5);' +
'\n background-color: rgba(255, 0, 255, 0.2);' +
'\n border-color: red; }'
);
......@@ -483,7 +504,7 @@ describe('api', function() {
it('should properly convert boolean when calling custom functions', function(done) {
sass.render({
data: 'div { color: if(foo(true, false), #fff, #000);' +
data: 'div { color: if(foo(true, false), #fff, #000);' +
'\n background-color: if(foo(true, true), #fff, #000); }',
functions: {
'foo($a, $b)': function(a, b) {
......@@ -535,7 +556,7 @@ describe('api', function() {
}
}, function(error, result) {
assert.equal(
result.css.toString().trim(),
result.css.toString().trim(),
'.foo {\n color: #fff; }\n\n.bar {\n color: #fff; }\n\n.baz {\n color: #fff; }'
);
done();
......@@ -576,7 +597,7 @@ describe('api', function() {
it('should properly convert null when calling custom functions', function(done) {
sass.render({
data: 'div { color: if(foo("bar"), #fff, #000); } ' +
data: 'div { color: if(foo("bar"), #fff, #000); } ' +
'span { color: if(foo(null), #fff, #000); }' +
'table { color: if(bar() == null, #fff, #000); }',
functions: {
......@@ -589,7 +610,7 @@ describe('api', function() {
}
}, function(error, result) {
assert.equal(
result.css.toString().trim(),
result.css.toString().trim(),
'div {\n color: #000; }\n\nspan {\n color: #fff; }\n\ntable {\n color: #fff; }'
);
done();
......@@ -868,7 +889,7 @@ describe('api', function() {
}
});
}, /This is a test error/);
done();
});
......@@ -883,7 +904,7 @@ describe('api', function() {
}
});
}, /unexpected error/);
done();
});
......@@ -911,7 +932,7 @@ describe('api', function() {
}
});
}, /Supplied value should be a string/);
done();
});
});
......
......@@ -2,6 +2,7 @@ var assert = require('assert'),
fs = require('fs'),
path = require('path'),
read = require('fs').readFileSync,
stream = require('stream'),
spawn = require('cross-spawn'),
cli = path.join(__dirname, '..', 'bin', 'node-sass'),
fixture = path.join.bind(null, __dirname, 'fixtures');
......@@ -63,6 +64,40 @@ describe('cli', function() {
src.pipe(bin.stdin);
});
it('should render with indentWidth and indentType options', function(done) {
var src = new stream.Readable();
var bin = spawn(cli, ['--indent-width', 7, '--indent-type', 'tab']);
src._read = function() { };
src.push('div { color: transparent; }');
src.push(null);
bin.stdout.setEncoding('utf8');
bin.stdout.once('data', function(data) {
assert.equal(data.trim(), 'div {\n\t\t\t\t\t\t\tcolor: transparent; }');
done();
});
src.pipe(bin.stdin);
});
it('should render with linefeed option', function(done) {
var src = new stream.Readable();
var bin = spawn(cli, ['--linefeed', 'lfcr']);
src._read = function() { };
src.push('div { color: transparent; }');
src.push(null);
bin.stdout.setEncoding('utf8');
bin.stdout.once('data', function(data) {
assert.equal(data.trim(), 'div {\n\r color: transparent; }');
done();
});
src.pipe(bin.stdin);
});
});
describe('node-sass in.scss', function() {
......
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