* That should be much easier with #395 which will get rid of them sooner.
if (!eligibleForCompacting && processableInfo.implementedFor.test(tokens[i][0]))
eligibleForCompacting = true;
- var property = tokens[i][0] + ':' + tokens[i][1];
+ // FIXME: the check should be gone with #396
+ var property = tokens[i][0].indexOf('__ESCAPED_') === 0 ?
+ tokens[i][0] :
+ tokens[i][0] + ':' + tokens[i][1];
tokenized.push({ value: property, metadata: tokens[i][4] });
list.push(property);
}
continue;
}
- var property = t.prop + ':' + t.value + (t.isImportant ? important : '');
+ // FIXME: the check should be gone with #396
+ var property = t.prop === '' && t.value.indexOf('__ESCAPED_') === 0 ?
+ t.value :
+ t.prop + ':' + t.value + (t.isImportant ? important : '');
tokenized.push({ value: property, metadata: t.metadata || {} });
list.push(property);
}
for (var i = 0, l = body.length; i < l; i++) {
var token = body[i];
+
+ // FIXME: the check should be gone with #396
+ if (token.value.indexOf('__ESCAPED_') === 0) {
+ reduced.push(token);
+ properties.push(token.value);
+ continue;
+ }
+
var firstColon = token.value.indexOf(':');
var property = token.value.substring(0, firstColon);
var value = token.value.substring(firstColon + 1);
var next = whatsNext(context);
if (!next) {
var whatsLeft = context.chunk.substring(context.cursor);
- if (whatsLeft.length > 0) {
+ if (whatsLeft.trim().length > 0) {
tokenized.push({ kind: 'text', value: whatsLeft });
context.cursor += whatsLeft.length;
}
if (addSourceMap)
SourceMaps.track(escaped, context);
} else {
- tokenized.push({ kind: 'text', value: escaped });
+ if (escaped.indexOf('__ESCAPED_COMMENT_SPECIAL') === 0)
+ tokenized.push({ kind: 'text', value: escaped });
if (addSourceMap)
SourceMaps.track(escaped, context);
var CommentsProcessor = function CommentsProcessor(context, keepSpecialComments, keepBreaks, saveWaypoints) {
this.comments = new EscapeStore('COMMENT');
+ this.specialComments = new EscapeStore('COMMENT_SPECIAL');
+
this.context = context;
this.keepAll = keepSpecialComments == '*';
this.keepOne = keepSpecialComments == '1' || keepSpecialComments === 1;
tempData.push(data.substring(cursor, nextStart));
var comment = data.substring(nextStart, nextEnd + COMMENT_SUFFIX.length);
+ var isSpecialComment = comment.indexOf(SPECIAL_COMMENT_PREFIX) === 0;
if (saveWaypoints) {
breaksCount = comment.split(lineBreak).length - 1;
indent + comment.length;
}
- if (saveWaypoints || comment.indexOf(SPECIAL_COMMENT_PREFIX) === 0) {
+ if (saveWaypoints || isSpecialComment) {
var metadata = saveWaypoints ? [breaksCount, newIndent] : null;
- var placeholder = this.comments.store(comment, metadata);
+ var placeholder = isSpecialComment ?
+ this.specialComments.store(comment, metadata) :
+ this.comments.store(comment, metadata);
tempData.push(placeholder);
}
data;
};
-CommentsProcessor.prototype.restore = function (data) {
+function restore(context, data, from, isSpecial) {
var tempData = [];
var restored = 0;
var cursor = 0;
var addBreak;
for (; cursor < data.length;) {
- var nextMatch = this.comments.nextMatch(data, cursor);
+ var nextMatch = from.nextMatch(data, cursor);
if (nextMatch.start < 0)
break;
tempData.push(data.substring(cursor, nextMatch.start));
- var comment = this.comments.restore(nextMatch.match);
+ var comment = from.restore(nextMatch.match);
- if (comment.indexOf(SPECIAL_COMMENT_PREFIX) === 0 && (this.keepAll || (this.keepOne && restored === 0))) {
+ if (isSpecial && (context.keepAll || (context.keepOne && restored === 0))) {
restored++;
- addBreak = this.keepBreaks && data[nextMatch.end] != '\n' && data.lastIndexOf('\r\n', nextMatch.end + 1) != nextMatch.end;
+ addBreak = context.keepBreaks && data[nextMatch.end] != '\n' && data.lastIndexOf('\r\n', nextMatch.end + 1) != nextMatch.end;
tempData.push(comment, addBreak ? lineBreak : '');
} else {
- nextMatch.end += this.keepBreaks ? lineBreak.length : 0;
+ nextMatch.end += context.keepBreaks ? lineBreak.length : 0;
}
cursor = nextMatch.end;
return tempData.length > 0 ?
tempData.join('') + data.substring(cursor, data.length) :
data;
+}
+
+CommentsProcessor.prototype.restore = function (data) {
+ data = restore(this, data, this.comments, false);
+ data = restore(this, data, this.specialComments, true);
+ return data;
};
module.exports = CommentsProcessor;
var buffer = [];
var all = [];
var property;
+ var isPropertyEnd;
var isWhitespace;
var wasWhitespace;
var isSpecial;
for (var i = 0, l = string.length; i < l; i++) {
current = string[i];
+ isPropertyEnd = current === ';';
- isEscape = current == '_' && buffer.length < 2 && string.indexOf('__ESCAPED_', i) === 0;
+ isEscape = !isPropertyEnd && current == '_' && string.indexOf('__ESCAPED_COMMENT', i) === i;
if (isEscape) {
- var endOfEscape = string.indexOf('__', i + 1) + 2;
- buffer = all = [string.substring(i, endOfEscape)];
- i = endOfEscape - 1;
+ if (buffer.length > 0) {
+ i--;
+ isPropertyEnd = true;
+ } else {
+ var endOfEscape = string.indexOf('__', i + 1) + 2;
+ var comment = string.substring(i, endOfEscape);
+ i = endOfEscape - 1;
+
+ if (comment.indexOf('__ESCAPED_COMMENT_SPECIAL') === -1) {
+ if (addSourceMap)
+ SourceMaps.track(comment, context, true);
+ continue;
+ }
+ else {
+ buffer = all = [comment];
+ }
+ }
}
- if (current === ';' || isEscape) {
+ if (isPropertyEnd || isEscape) {
if (wasWhitespace && buffer[buffer.length - 1] === ' ')
buffer.pop();
if (buffer.length > 0) {
],
})
)
+ .addBatch(
+ propertyContext('comments', {
+ 'comment': [
+ 'a{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__color:red__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__}',
+ ['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__', 'color:red', '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__']
+ ]
+ })
+ )
.export(module);
'top-level': [
'__ESCAPED_COMMENT_CLEAN_CSS0(0, 5)__a{}',
[
- {
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS0(0, 5)__'
- },
{
kind: 'selector',
value: [{ value: 'a', metadata: { line: 1, column: 5, source: undefined } }],
'top-level with line breaks': [
'__ESCAPED_COMMENT_CLEAN_CSS0(2, 5)__a{}',
[
- {
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS0(2, 5)__'
- },
{
kind: 'selector',
value: [{ value: 'a', metadata: { line: 3, column: 5, source: undefined } }],
}]
],
'in properties': [
- 'div{__ESCAPED_COMMENT_CLEAN_CSS0(2,5)__background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__);color:blue}a{font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__;color:red}',
+ 'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__);color:blue}a{font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__;color:red}',
[
{
kind: 'selector',
value: [{ value: 'div', metadata: { line: 1, column: 0, source: undefined } }],
body: [
- { value: '__ESCAPED_COMMENT_CLEAN_CSS0(2,5)__', metadata: { line: 1, column: 4, source: undefined }},
+ { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__', metadata: { line: 1, column: 4, source: undefined }},
{ value: 'background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__)', metadata: { line: 3, column: 5, source: undefined } },
{ value: 'color:blue', metadata: { line: 3, column: 42, source: undefined } }
]
],
'an escaped content': [
'__ESCAPED_COMMENT_CLEAN_CSS0__',
- [{
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS0__'
- }]
+ []
],
'an escaped content followed by a break': [
'__ESCAPED_COMMENT_CLEAN_CSS0__\n',
- [
- {
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS0__'
- },
- {
- kind: 'text',
- value: '\n'
- }
- ]
+ []
],
'an empty selector': [
'a{}',
'two comments and a selector separated by newline': [
'__ESCAPED_COMMENT_CLEAN_CSS0__\n__ESCAPED_COMMENT_CLEAN_CSS1__\ndiv{}',
[
- {
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS0__'
- },
- {
- kind: 'text',
- value: '__ESCAPED_COMMENT_CLEAN_CSS1__'
- },
{
kind: 'selector',
value: [{ value: 'div' }],
}
]
],
+ 'two properties wrapped between comments': [
+ 'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__color:red__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__}',
+ [{
+ kind: 'selector',
+ value: [{ value: 'div' }],
+ body: [
+ { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' },
+ { value: 'color:red' },
+ { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__' }
+ ]
+ }]
+ ],
'media query': [
'@media (min-width:980px){}',
[{
'',
[]
],
- 'an escaped content': [
+ 'an escaped comment': [
'__ESCAPED_COMMENT_CLEAN_CSS0__',
- [{ kind: 'text', value: '__ESCAPED_COMMENT_CLEAN_CSS0__' }]
+ []
+ ],
+ 'an escaped special comment': [
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__',
+ [{ kind: 'text', value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' }]
],
'an empty selector': [
'a{}',
}
}
]
+ ],
+ 'two properties wrapped between comments': [
+ 'div{__ESCAPED_COMMENT_CLEAN_CSS0(0, 5)__color:red__ESCAPED_COMMENT_CLEAN_CSS1(0, 5)__}',
+ [{
+ kind: 'selector',
+ value: [{ value: 'div' }],
+ body: [
+ { value: 'color:red' }
+ ],
+ metadata: {
+ body: 'color:red',
+ bodiesList: ['color:red'],
+ selector: 'div',
+ selectorsList: ['div']
+ }
+ }]
+ ],
+ 'two properties wrapped between special comments': [
+ 'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__color:red__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__}',
+ [{
+ kind: 'selector',
+ value: [{ value: 'div' }],
+ body: [
+ { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__' },
+ { value: 'color:red' },
+ { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__' }
+ ],
+ metadata: {
+ body: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__,color:red,__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__',
+ bodiesList: ['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__', 'color:red', '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__'],
+ selector: 'div',
+ selectorsList: ['div']
+ }
+ }]
]
}, true)
)
var port = 24682;
vows.describe('source-map')
+ .addBatch({
+ 'vendor prefix with comments': {
+ 'topic': new CleanCSS({ sourceMap: true }).minify('html{font-family:sans-serif;/* 1 */-ms-text-size-adjust:100%;/* 2 */-webkit-text-size-adjust:100%/* 3 */}'),
+ 'gets right output': function (minified) {
+ assert.equal(minified.styles, 'html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}');
+ }
+ }
+ })
.addBatch({
'module #1': {
'topic': new CleanCSS({ sourceMap: true }).minify('/*! a */div[data-id=" abc "] { color:red; }'),
],
'one special comment': [
'/*! some text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__',
'/*! some text */'
],
'two comments': [
],
'two special comments': [
'/*! one text *//*! another text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0____ESCAPED_COMMENT_CLEAN_CSS1__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__',
'/*! one text *//*! another text */'
],
'commented selector': [
],
'one special comment': [
'/*! some text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__',
'/*! some text */'
],
'two special comments': [
'/*! one text *//*! another text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0____ESCAPED_COMMENT_CLEAN_CSS1__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__',
'/*! one text */'
]
}, '1')
],
'one special comment': [
'/*! some text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__',
''
],
'two special comments': [
'/*! one text *//*! another text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0____ESCAPED_COMMENT_CLEAN_CSS1__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__',
''
]
}, '0')
processorContext('zero with breaks', {
'content and special comments': [
'a{}' + lineBreak + '/*! some text */' + lineBreak + 'p{}',
- 'a{}' + lineBreak + '__ESCAPED_COMMENT_CLEAN_CSS0__' + lineBreak + 'p{}',
+ 'a{}' + lineBreak + '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' + lineBreak + 'p{}',
'a{}' + lineBreak + 'p{}'
]
}, '0', true)
processorContext('one with breaks', {
'forces break after comments': [
'a{}/*! some text */p{}',
- 'a{}__ESCAPED_COMMENT_CLEAN_CSS0__p{}',
+ 'a{}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__p{}',
'a{}/*! some text */' + lineBreak + 'p{}'
],
'if not given already comments': [
'a{}/*! some text */' + lineBreak + 'p{}',
- 'a{}__ESCAPED_COMMENT_CLEAN_CSS0__' + lineBreak + 'p{}',
+ 'a{}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' + lineBreak + 'p{}',
'a{}/*! some text */' + lineBreak + 'p{}'
],
'if given an other platform break already': [
'a{}/*! some text */' + otherLineBreak + 'p{}',
- 'a{}__ESCAPED_COMMENT_CLEAN_CSS0__' + otherLineBreak + 'p{}',
+ 'a{}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' + otherLineBreak + 'p{}',
'a{}/*! some text */' + otherLineBreak + 'p{}'
]
}, '1', true)
],
'one special comment': [
'/*! some text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0(0,16)__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0,16)__',
'/*! some text */'
],
'two comments': [
],
'two special comments': [
'/*! one text *//*! another text */',
- '__ESCAPED_COMMENT_CLEAN_CSS0(0,15)____ESCAPED_COMMENT_CLEAN_CSS1(0,35)__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0,15)____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0,35)__',
'/*! one text *//*! another text */'
],
'two special comments with line breaks': [
'/*! one text' + lineBreak + '*//*! another' + lineBreak + ' text' + lineBreak + ' */',
- '__ESCAPED_COMMENT_CLEAN_CSS0(1,2)____ESCAPED_COMMENT_CLEAN_CSS1(2,3)__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(1,2)____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(2,3)__',
'/*! one text' + lineBreak + '*//*! another' + lineBreak + ' text' + lineBreak + ' */'
],
'three special comments with line breaks and content in between': [
'/*! one text' + lineBreak + '*/a{}/*! ' + lineBreak + 'test */p{color:red}/*! another' + lineBreak + ' text' + lineBreak + lineBreak + ' */',
- '__ESCAPED_COMMENT_CLEAN_CSS0(1,2)__a{}__ESCAPED_COMMENT_CLEAN_CSS1(1,7)__p{color:red}__ESCAPED_COMMENT_CLEAN_CSS2(3,4)__',
+ '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(1,2)__a{}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(1,7)__p{color:red}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS2(3,4)__',
'/*! one text' + lineBreak + '*/a{}/*! ' + lineBreak + 'test */p{color:red}/*! another' + lineBreak + ' text' + lineBreak + lineBreak + ' */'
]
}, '*', false, true)