Skip to content

Commit 59bf990

Browse files
Fixes #964 - allows for configurable line breaks.
Use `{ format: { breakWith: 'lf' } }` option to configure what line break looks like, allows `'crlf'` or `'lf'`, defaults to current system one so former on Windows and latter on Unix.
1 parent 48808f7 commit 59bf990

8 files changed

Lines changed: 68 additions & 15 deletions

File tree

History.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Fixed issue [#861](https://github.com/jakubpawlowicz/clean-css/issues/861) - new `transition` property optimizer.
66
* Fixed issue [#895](https://github.com/jakubpawlowicz/clean-css/issues/895) - ignoring specific styles.
77
* Fixed issue [#947](https://github.com/jakubpawlowicz/clean-css/issues/947) - selector based filtering.
8+
* Fixed issue [#964](https://github.com/jakubpawlowicz/clean-css/issues/964) - adds configurable line breaks.
89
* Fixed issue [#986](https://github.com/jakubpawlowicz/clean-css/issues/986) - level 2 optimizations and CSS 4 colors.
910
* Fixed issue [#1000](https://github.com/jakubpawlowicz/clean-css/issues/1000) - carriage return handling in tokenizer.
1011
* Fixed issue [#1038](https://github.com/jakubpawlowicz/clean-css/issues/1038) - `font-variation-settings` quoting.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ clean-css 4.2 will introduce the following changes / features:
122122
* new `transition` property optimizer;
123123
* preserves any CSS content between `/* clean-css ignore:start */` and `/* clean-css ignore:end */` comments;
124124
* allows filtering based on selector in `transform` callback, see [example](#how-to-apply-arbitrary-transformations-to-css-properties);
125+
* adds configurable line breaks via `format: { breakWith: 'lf' }` option;
125126

126127
## Constructor options
127128

@@ -264,6 +265,7 @@ new CleanCSS({
264265
beforeBlockEnds: false, // controls if a line break comes before a block ends; defaults to `false`
265266
betweenSelectors: false // controls if a line break comes between selectors; defaults to `false`
266267
},
268+
breakWith: '\n', // controls the new line character, can be `'\r\n'` or `'\n'` (aliased as `'windows'` and `'unix'` or `'crlf'` and `'lf'`); defaults to system one, so former on Windows and latter on Unix
267269
indentBy: 0, // controls number of characters to indent with; defaults to `0`
268270
indentWith: 'space', // controls a character to indent with, can be `'space'` or `'tab'`; defaults to `'space'`
269271
spaces: { // controls where to insert spaces

lib/options/format.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
var systemLineBreak = require('os').EOL;
2+
13
var override = require('../utils/override');
24

35
var Breaks = {
@@ -12,6 +14,12 @@ var Breaks = {
1214
BetweenSelectors: 'betweenSelectors'
1315
};
1416

17+
var BreakWith = {
18+
CarriageReturnLineFeed: '\r\n',
19+
LineFeed: '\n',
20+
System: systemLineBreak
21+
};
22+
1523
var IndentWith = {
1624
Space: ' ',
1725
Tab: '\t'
@@ -25,6 +33,7 @@ var Spaces = {
2533

2634
var DEFAULTS = {
2735
breaks: breaks(false),
36+
breakWith: BreakWith.System,
2837
indentBy: 0,
2938
indentWith: IndentWith.Space,
3039
spaces: spaces(false),
@@ -76,6 +85,10 @@ function formatFrom(source) {
7685
return false;
7786
}
7887

88+
if (typeof source == 'object' && 'breakWith' in source) {
89+
source = override(source, { breakWith: mapBreakWith(source.breakWith) });
90+
}
91+
7992
if (typeof source == 'object' && 'indentBy' in source) {
8093
source = override(source, { indentBy: parseInt(source.indentBy) });
8194
}
@@ -168,6 +181,21 @@ function normalizeValue(value) {
168181
}
169182
}
170183

184+
function mapBreakWith(value) {
185+
switch (value) {
186+
case 'windows':
187+
case 'crlf':
188+
case BreakWith.CarriageReturnLineFeed:
189+
return BreakWith.CarriageReturnLineFeed;
190+
case 'unix':
191+
case 'lf':
192+
case BreakWith.LineFeed:
193+
return BreakWith.LineFeed;
194+
default:
195+
return systemLineBreak;
196+
}
197+
}
198+
171199
function mapIndentWith(value) {
172200
switch (value) {
173201
case 'space':

lib/writer/helpers.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
var lineBreak = require('os').EOL;
21
var emptyCharacter = '';
32

43
var Breaks = require('../options/format').Breaks;
@@ -153,7 +152,7 @@ function openBrace(context, where, needsPrefixSpace) {
153152
context.indentWith = context.format.indentWith.repeat(context.indentBy);
154153
return (needsPrefixSpace && allowsSpace(context, Spaces.BeforeBlockBegins) ? Marker.SPACE : emptyCharacter) +
155154
Marker.OPEN_CURLY_BRACKET +
156-
(allowsBreak(context, where) ? lineBreak : emptyCharacter) +
155+
(allowsBreak(context, where) ? context.format.breakWith : emptyCharacter) +
157156
context.indentWith;
158157
} else {
159158
return Marker.OPEN_CURLY_BRACKET;
@@ -164,10 +163,10 @@ function closeBrace(context, where, beforeBlockEnd, isLast) {
164163
if (context.format) {
165164
context.indentBy -= context.format.indentBy;
166165
context.indentWith = context.format.indentWith.repeat(context.indentBy);
167-
return (allowsBreak(context, Breaks.AfterProperty) || beforeBlockEnd && allowsBreak(context, Breaks.BeforeBlockEnds) ? lineBreak : emptyCharacter) +
166+
return (allowsBreak(context, Breaks.AfterProperty) || beforeBlockEnd && allowsBreak(context, Breaks.BeforeBlockEnds) ? context.format.breakWith : emptyCharacter) +
168167
context.indentWith +
169168
Marker.CLOSE_CURLY_BRACKET +
170-
(isLast ? emptyCharacter : (allowsBreak(context, where) ? lineBreak : emptyCharacter) + context.indentWith);
169+
(isLast ? emptyCharacter : (allowsBreak(context, where) ? context.format.breakWith : emptyCharacter) + context.indentWith);
171170
} else {
172171
return Marker.CLOSE_CURLY_BRACKET;
173172
}
@@ -181,13 +180,13 @@ function colon(context) {
181180

182181
function semicolon(context, where, isLast) {
183182
return context.format ?
184-
Marker.SEMICOLON + (isLast || !allowsBreak(context, where) ? emptyCharacter : lineBreak + context.indentWith) :
183+
Marker.SEMICOLON + (isLast || !allowsBreak(context, where) ? emptyCharacter : context.format.breakWith + context.indentWith) :
185184
Marker.SEMICOLON;
186185
}
187186

188187
function comma(context) {
189188
return context.format ?
190-
Marker.COMMA + (allowsBreak(context, Breaks.BetweenSelectors) ? lineBreak : emptyCharacter) + context.indentWith :
189+
Marker.COMMA + (allowsBreak(context, Breaks.BetweenSelectors) ? context.format.breakWith : emptyCharacter) + context.indentWith :
191190
Marker.COMMA;
192191
}
193192

@@ -220,7 +219,7 @@ function all(context, tokens) {
220219
break;
221220
case Token.COMMENT:
222221
store(context, token);
223-
store(context, allowsBreak(context, Breaks.AfterComment) ? lineBreak : emptyCharacter);
222+
store(context, allowsBreak(context, Breaks.AfterComment) ? context.format.breakWith : emptyCharacter);
224223
break;
225224
case Token.RAW:
226225
store(context, token);

lib/writer/simple.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
var all = require('./helpers').all;
22

3-
var lineBreak = require('os').EOL;
4-
53
function store(serializeContext, token) {
64
var value = typeof token == 'string' ?
75
token :
@@ -15,8 +13,8 @@ function store(serializeContext, token) {
1513

1614
function wrap(serializeContext, value) {
1715
if (serializeContext.column + value.length > serializeContext.format.wrapAt) {
18-
track(serializeContext, lineBreak);
19-
serializeContext.output.push(lineBreak);
16+
track(serializeContext, serializeContext.format.breakWith);
17+
serializeContext.output.push(serializeContext.format.breakWith);
2018
}
2119
}
2220

lib/writer/source-maps.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
var SourceMapGenerator = require('source-map').SourceMapGenerator;
22
var all = require('./helpers').all;
33

4-
var lineBreak = require('os').EOL;
54
var isRemoteResource = require('../utils/is-remote-resource');
65

76
var isWindows = process.platform == 'win32';
@@ -23,8 +22,8 @@ function store(serializeContext, element) {
2322

2423
function wrap(serializeContext, value) {
2524
if (serializeContext.column + value.length > serializeContext.format.wrapAt) {
26-
track(serializeContext, lineBreak, false);
27-
serializeContext.output.push(lineBreak);
25+
track(serializeContext, serializeContext.format.breakWith, false);
26+
serializeContext.output.push(serializeContext.format.breakWith);
2827
}
2928
}
3029

test/integration-test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,22 @@ vows.describe('integration tests')
308308
]
309309
}, { format: 'keep-breaks', level: { 1: { specialComments: 0 } } })
310310
)
311+
.addBatch(
312+
optimizerContext('CRLF line breaks', {
313+
'uses specified one': [
314+
'.block{color:red;display:block}',
315+
'.block{color:red;\r\ndisplay:block\r\n}'
316+
]
317+
}, { format: { breaks: { afterProperty: true }, breakWith: 'crlf' } })
318+
)
319+
.addBatch(
320+
optimizerContext('LF line breaks', {
321+
'uses specified one': [
322+
'.block{color:red;display:block}',
323+
'.block{color:red;\ndisplay:block\n}'
324+
]
325+
}, { format: { breaks: { afterProperty: true }, breakWith: 'lf' } })
326+
)
311327
.addBatch(
312328
optimizerContext('selectors', {
313329
'not expand + in selectors mixed with calc methods': [

test/options/format-test.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var assert = require('assert');
2+
var systemLineBreak = require('os').EOL;
23

34
var vows = require('vows');
45

@@ -39,6 +40,7 @@ vows.describe(formatFrom)
3940
beforeBlockEnds: false,
4041
betweenSelectors: false
4142
},
43+
breakWith: systemLineBreak,
4244
indentBy: 0,
4345
indentWith: ' ',
4446
spaces: {
@@ -53,7 +55,7 @@ vows.describe(formatFrom)
5355
},
5456
'hash': {
5557
'topic': function () {
56-
return formatFrom({ breaks: { afterProperty: true }, indentBy: 1 });
58+
return formatFrom({ breaks: { afterProperty: true }, breakWith: '\r\n', indentBy: 1 });
5759
},
5860
'is merged with default': function (formatOptions) {
5961
assert.deepEqual(formatOptions, {
@@ -68,6 +70,7 @@ vows.describe(formatFrom)
6870
beforeBlockEnds: false,
6971
betweenSelectors: false
7072
},
73+
breakWith: '\r\n',
7174
indentBy: 1,
7275
indentWith: ' ',
7376
spaces: {
@@ -97,6 +100,7 @@ vows.describe(formatFrom)
97100
beforeBlockEnds: false,
98101
betweenSelectors: false
99102
},
103+
breakWith: systemLineBreak,
100104
indentBy: 2,
101105
indentWith: ' ',
102106
spaces: {
@@ -126,6 +130,7 @@ vows.describe(formatFrom)
126130
beforeBlockEnds: false,
127131
betweenSelectors: false
128132
},
133+
breakWith: systemLineBreak,
129134
indentBy: 0,
130135
indentWith: '\t',
131136
spaces: {
@@ -155,6 +160,7 @@ vows.describe(formatFrom)
155160
beforeBlockEnds: false,
156161
betweenSelectors: false
157162
},
163+
breakWith: systemLineBreak,
158164
indentBy: 0,
159165
indentWith: '\t',
160166
spaces: {
@@ -184,6 +190,7 @@ vows.describe(formatFrom)
184190
beforeBlockEnds: false,
185191
betweenSelectors: false
186192
},
193+
breakWith: systemLineBreak,
187194
indentBy: 3,
188195
indentWith: ' ',
189196
spaces: {
@@ -213,6 +220,7 @@ vows.describe(formatFrom)
213220
beforeBlockEnds: false,
214221
betweenSelectors: false
215222
},
223+
breakWith: systemLineBreak,
216224
indentBy: 0,
217225
indentWith: '\t',
218226
spaces: {
@@ -242,6 +250,7 @@ vows.describe(formatFrom)
242250
beforeBlockEnds: true,
243251
betweenSelectors: true
244252
},
253+
breakWith: systemLineBreak,
245254
indentBy: 2,
246255
indentWith: ' ',
247256
spaces: {
@@ -271,6 +280,7 @@ vows.describe(formatFrom)
271280
beforeBlockEnds: true,
272281
betweenSelectors: false
273282
},
283+
breakWith: systemLineBreak,
274284
indentBy: 0,
275285
indentWith: ' ',
276286
spaces: {

0 commit comments

Comments
 (0)