From f9663e4f49f978d3c790d37706698a22d351f748 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Wed, 8 Apr 2015 08:26:08 +0100 Subject: [PATCH] Unifies all stringifying code. Instead of having 3 of them (simple, source maps, and advanced optimizations helpers), we have one now with a slightly different storing logic for source maps. --- lib/clean.js | 11 +- lib/selectors/extractor.js | 8 +- lib/selectors/optimizer.js | 4 +- lib/selectors/optimizers/advanced.js | 38 +++--- lib/selectors/source-map-stringifier.js | 157 ------------------------ lib/selectors/stringifier.js | 103 ---------------- lib/stringifier/helpers.js | 136 ++++++++++++++++++++ lib/stringifier/one-time.js | 36 ++++++ lib/stringifier/simple.js | 21 ++++ lib/stringifier/source-maps.js | 74 +++++++++++ lib/utils/stringify-tokens.js | 39 ------ test/fixtures/font-awesome-min.css | 20 +-- test/fixtures/issue-117-snippet-min.css | 3 +- test/fixtures/issue-241-min.css | 3 +- test/selectors/optimizer-test.js | 8 +- 15 files changed, 323 insertions(+), 338 deletions(-) delete mode 100644 lib/selectors/source-map-stringifier.js delete mode 100644 lib/selectors/stringifier.js create mode 100644 lib/stringifier/helpers.js create mode 100644 lib/stringifier/one-time.js create mode 100644 lib/stringifier/simple.js create mode 100644 lib/stringifier/source-maps.js delete mode 100644 lib/utils/stringify-tokens.js diff --git a/lib/clean.js b/lib/clean.js index 73a6bc36..2fa5563d 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -8,8 +8,9 @@ var ImportInliner = require('./imports/inliner'); var UrlRebase = require('./images/url-rebase'); var SelectorsOptimizer = require('./selectors/optimizer'); -var Stringifier = require('./selectors/stringifier'); -var SourceMapStringifier = require('./selectors/source-map-stringifier'); + +var simpleStringify = require('./stringifier/simple'); +var sourceMapStringify = require('./stringifier/source-maps'); var CommentsProcessor = require('./text/comments-processor'); var ExpressionsProcessor = require('./text/expressions-processor'); @@ -162,7 +163,7 @@ function minify(context, data) { var urlRebase = new UrlRebase(context); var selectorsOptimizer = new SelectorsOptimizer(options, context); - var stringifierClass = options.sourceMap ? SourceMapStringifier : Stringifier; + var stringify = options.sourceMap ? sourceMapStringify : simpleStringify; var run = function (processor, action) { data = typeof processor == 'function' ? @@ -179,15 +180,13 @@ function minify(context, data) { run(freeTextProcessor, 'escape'); run(function() { - var stringifier = new stringifierClass(options, function (data) { + return selectorsOptimizer.process(data, stringify, function (data) { data = freeTextProcessor.restore(data); data = urlsProcessor.restore(data); data = options.rebase ? urlRebase.process(data) : data; data = expressionsProcessor.restore(data); return commentsProcessor.restore(data); }, sourceMapTracker); - - return selectorsOptimizer.process(data, stringifier); }); return data; diff --git a/lib/selectors/extractor.js b/lib/selectors/extractor.js index 877fc688..08ae0ca8 100644 --- a/lib/selectors/extractor.js +++ b/lib/selectors/extractor.js @@ -2,14 +2,14 @@ // IMPORTANT: Mind Token class and this code is not related! // Properties will be tokenized in one step, see #429 -var stringifySelector = require('../utils/stringify-tokens').selector; -var stringifyValue = require('../utils/stringify-tokens').value; +var stringifySelectors = require('../stringifier/one-time').selectors; +var stringifyValue = require('../stringifier/one-time').value; function extract(token) { var properties = []; if (token[0] == 'selector') { - var inSimpleSelector = !/[\.\+#>~\s]/.test(stringifySelector(token[1])); + var inSimpleSelector = !/[\.\+#>~\s]/.test(stringifySelectors(token[1])); for (var i = 0, l = token[2].length; i < l; i++) { var property = token[2][i]; @@ -20,7 +20,7 @@ function extract(token) { if (name.length === 0) continue; - var value = stringifyValue(token[2][i]); + var value = stringifyValue(token[2], i); properties.push([ name, diff --git a/lib/selectors/optimizer.js b/lib/selectors/optimizer.js index a7fa21f3..3cfc6f8e 100644 --- a/lib/selectors/optimizer.js +++ b/lib/selectors/optimizer.js @@ -8,7 +8,7 @@ function SelectorsOptimizer(options, context) { this.context = context || {}; } -SelectorsOptimizer.prototype.process = function (data, stringifier) { +SelectorsOptimizer.prototype.process = function (data, stringify, restoreCallback, sourceMapTracker) { var tokens = new Tokenizer(this.context, this.options.sourceMap).toTokens(data); addOptimizationMetadata(tokens); @@ -17,7 +17,7 @@ SelectorsOptimizer.prototype.process = function (data, stringifier) { if (this.options.advanced) new AdvancedOptimizer(this.options, this.context).optimize(tokens); - return stringifier.toString(tokens); + return stringify(tokens, this.options, restoreCallback, sourceMapTracker); }; module.exports = SelectorsOptimizer; diff --git a/lib/selectors/optimizers/advanced.js b/lib/selectors/optimizers/advanced.js index 4502de84..d4b04521 100644 --- a/lib/selectors/optimizers/advanced.js +++ b/lib/selectors/optimizers/advanced.js @@ -4,8 +4,8 @@ var CleanUp = require('./clean-up'); var extractProperties = require('../extractor'); var canReorder = require('../reorderable').canReorder; var canReorderSingle = require('../reorderable').canReorderSingle; -var stringifyBody = require('../../utils/stringify-tokens').body; -var stringifySelector = require('../../utils/stringify-tokens').selector; +var stringifyBody = require('../../stringifier/one-time').body; +var stringifySelectors = require('../../stringifier/one-time').selectors; function AdvancedOptimizer(options) { this.options = options; @@ -34,7 +34,7 @@ AdvancedOptimizer.prototype.removeDuplicates = function (tokens) { if (token[0] != 'selector') continue; - id = stringifySelector(token[1]); + id = stringifySelectors(token[1]); if (matched[id] && matched[id].length == 1) moreThanOnce.push(id); @@ -72,13 +72,13 @@ AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) { continue; } - if (lastToken[0] == 'selector' && stringifySelector(token[1]) == stringifySelector(lastToken[1])) { + if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) { var joinAt = [lastToken[2].length]; Array.prototype.push.apply(lastToken[2], token[2]); optimizeProperties(token[1], lastToken[2], joinAt, this.options); token[2] = []; } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) && - !this.isSpecial(stringifySelector(token[1])) && !this.isSpecial(stringifySelector(lastToken[1]))) { + !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(lastToken[1]))) { lastToken[1] = CleanUp.selectors(lastToken[1].concat(token[1]), false, adjacentSpace); token[2] = []; } else { @@ -99,7 +99,7 @@ AdvancedOptimizer.prototype.reduceNonAdjacent = function (tokens) { if (token[2].length === 0) continue; - var selectorAsString = stringifySelector(token[1]); + var selectorAsString = stringifySelectors(token[1]); var isComplexAndNotSpecial = token[1].length > 1 && !this.isSpecial(selectorAsString); var selectors = isComplexAndNotSpecial ? [selectorAsString].concat(token[1]) : @@ -251,7 +251,7 @@ AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) { if (tokens[i][2].length === 0) continue; - var selector = stringifySelector(tokens[i][1]); + var selector = stringifySelectors(tokens[i][1]); allSelectors[selector] = [i].concat(allSelectors[selector] || []); if (allSelectors[selector].length == 2) @@ -317,11 +317,11 @@ AdvancedOptimizer.prototype.mergeNonAdjacentByBody = function (tokens) { if (token[0] != 'selector') continue; - if (token[2].length > 0 && unsafeSelector(stringifySelector(token[1]))) + if (token[2].length > 0 && unsafeSelector(stringifySelectors(token[1]))) candidates = {}; var oldToken = candidates[stringifyBody(token[2])]; - if (oldToken && !this.isSpecial(stringifySelector(token[1])) && !this.isSpecial(stringifySelector(oldToken[1]))) { + if (oldToken && !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(oldToken[1]))) { token[1] = CleanUp.selectors(oldToken[1].concat(token[1]), false, adjacentSpace); oldToken[2] = []; @@ -383,7 +383,7 @@ AdvancedOptimizer.prototype.restructure = function (tokens) { function cacheId(cachedTokens) { var id = []; for (var i = 0, l = cachedTokens.length; i < l; i++) { - id.push(stringifySelector(cachedTokens[i][1])); + id.push(stringifySelectors(cachedTokens[i][1])); } return id.join(ID_JOIN_CHARACTER); } @@ -393,7 +393,7 @@ AdvancedOptimizer.prototype.restructure = function (tokens) { var mergeableTokens = []; for (var i = sourceTokens.length - 1; i >= 0; i--) { - if (self.isSpecial(stringifySelector(sourceTokens[i][1]))) + if (self.isSpecial(stringifySelectors(sourceTokens[i][1]))) continue; mergeableTokens.unshift(sourceTokens[i]); @@ -457,7 +457,7 @@ AdvancedOptimizer.prototype.restructure = function (tokens) { function sizeDifference(tokensVariant, propertySize, propertiesCount) { var allSelectorsSize = 0; for (var i = tokensVariant.length - 1; i >= 0; i--) { - allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelector(tokensVariant[i][1]).length : -1; + allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelectors(tokensVariant[i][1]).length : -1; } return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1; } @@ -664,13 +664,21 @@ AdvancedOptimizer.prototype.mergeMediaQueries = function (tokens) { AdvancedOptimizer.prototype.removeEmpty = function (tokens) { for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i]; + var isEmpty = false; - if (token[0] == 'selector' && (token[1].length === 0 || token[2].length === 0)) { + switch (token[0]) { + case 'selector': + isEmpty = token[1].length === 0 || token[2].length === 0; + break; + case 'block': + this.removeEmpty(token[2]); + isEmpty = token[2].length === 0; + } + + if (isEmpty) { tokens.splice(i, 1); i--; l--; - } else if (token[1] == 'block') { - this.removeEmpty(token[2]); } } }; diff --git a/lib/selectors/source-map-stringifier.js b/lib/selectors/source-map-stringifier.js deleted file mode 100644 index 58519193..00000000 --- a/lib/selectors/source-map-stringifier.js +++ /dev/null @@ -1,157 +0,0 @@ -var SourceMapGenerator = require('source-map').SourceMapGenerator; - -var lineBreak = require('os').EOL; -var unknownSource = '$stdin'; - -function hasMoreProperties(elements, index) { - for (var i = index, l = elements.length; i < l; i++) { - if (typeof elements[i] != 'string') - return true; - } - - return false; -} - -function Rebuilder(options, restoreCallback, inputMapTracker) { - this.column = 0; - this.line = 1; - this.output = []; - this.keepBreaks = options.keepBreaks; - this.sourceMapInlineSources = options.sourceMapInlineSources; - this.restore = restoreCallback; - this.inputMapTracker = inputMapTracker; - this.outputMap = new SourceMapGenerator(); -} - -Rebuilder.prototype.rebuildSelectors = function (elements) { - for (var i = 0, l = elements.length; i < l; i++) { - var element = elements[i]; - this.store(element); - - if (i < l - 1) - this.store(','); - } -}; - -Rebuilder.prototype.rebuildBody = function (elements) { - for (var i = 0, l = elements.length; i < l; i++) { - var element = elements[i]; - - if (typeof element == 'string') { - this.store(element); - continue; - } - - for (var j = 0, m = element.length; j < m; j++) { - this.store(element[j]); - - if (j == m - 1 && element[0][1]) - this.store('!important'); - - if (j === 0) { - this.store(':'); - } else if (j < m - 1) { - this.store(' '); - } else if (i < l - 1 && hasMoreProperties(elements, i + 1)) { - this.store(';'); - } - } - } -}; - -Rebuilder.prototype.store = function (element) { - // handles defaults and values like `,` or `/` which do not have mapping - if (Array.isArray(element) && element.length == 1) - element = element[0]; - - var fromString = typeof element == 'string'; - var value = fromString ? element : element[0]; - - if (value.indexOf('_') > -1) - value = this.restore(value); - - this.track(value, fromString ? null : element); - this.output.push(value); -}; - -Rebuilder.prototype.rebuildList = function (tokens, isFlatBlock) { - var joinCharacter = isFlatBlock ? ';' : (this.keepBreaks ? lineBreak : ''); - - for (var i = 0, l = tokens.length; i < l; i++) { - var token = tokens[i]; - - switch (token[0]) { - case 'at-rule': - case 'text': - this.store(token[1][0]); - break; - case 'block': - this.rebuildSelectors([token[1]]); - this.store('{'); - this.rebuildList(token[2], false); - this.store('}'); - this.store(joinCharacter); - break; - case 'flat-block': - this.rebuildSelectors([token[1]]); - this.store('{'); - this.rebuildBody(token[2]); - this.store('}'); - this.store(joinCharacter); - break; - default: - this.rebuildSelectors(token[1]); - this.store('{'); - this.rebuildBody(token[2]); - this.store('}'); - this.store(joinCharacter); - } - } -}; - -Rebuilder.prototype.track = function (value, element) { - if (element) - this.trackMetadata(element); - - var parts = value.split('\n'); - this.line += parts.length - 1; - this.column = parts.length > 1 ? 0 : (this.column + parts.pop().length); -}; - -Rebuilder.prototype.trackMetadata = function (element) { - var sourceAt = element.length - 1; - if (typeof element[sourceAt] == 'object') - sourceAt--; - - var source = element[sourceAt] || unknownSource; - - this.outputMap.addMapping({ - generated: { - line: this.line, - column: this.column - }, - source: source, - original: { - line: element[sourceAt - 2], - column: element[sourceAt - 1] - } - }); - - if (element[sourceAt + 1]) - this.outputMap.setSourceContent(source, element[sourceAt + 1][element[sourceAt]]); -}; - -function SourceMapStringifier(options, restoreCallback, inputMapTracker) { - this.rebuilder = new Rebuilder(options, restoreCallback, inputMapTracker); -} - -SourceMapStringifier.prototype.toString = function (tokens) { - this.rebuilder.rebuildList(tokens); - - return { - sourceMap: this.rebuilder.outputMap, - styles: this.rebuilder.output.join('').trim() - }; -}; - -module.exports = SourceMapStringifier; diff --git a/lib/selectors/stringifier.js b/lib/selectors/stringifier.js deleted file mode 100644 index ce57f93a..00000000 --- a/lib/selectors/stringifier.js +++ /dev/null @@ -1,103 +0,0 @@ -var lineBreak = require('os').EOL; - -function Stringifier(options, restoreCallback) { - this.keepBreaks = options.keepBreaks; - this.restoreCallback = restoreCallback; -} - -function selectorRebuilder(elements) { - var merged = ''; - - for (var i = 0, l = elements.length; i < l; i++) { - merged += elements[i] + (i < l - 1 ? ',' : ''); - } - - return merged; -} - -function bodyRebuilder(elements) { - var merged = ''; - var element, important, lastSemicolonAt, value, valueLastChar, shouldSkipSpaceAfter; - - for (var i = 0, l = elements.length; i < l; i++) { - element = elements[i]; - - if (typeof element == 'string' && element.indexOf('__ESCAPED_') === 0) { - merged += element; - - if (i === l - 1) { - lastSemicolonAt = merged.lastIndexOf(';'); - merged = merged.substring(0, lastSemicolonAt) + merged.substring(lastSemicolonAt + 1); - } - } else { - important = element[0][1]; - merged += element[0][0] + ':'; - - for (var j = 1, m = element.length; j < m; j++) { - value = element[j][0]; - valueLastChar = value[value.length - 1]; - - if (value == ',' || value == '/') { - if (merged[merged.length - 1] == ')') - merged += value; - else - merged = merged.substring(0, merged.length - 1) + value; - } else { - shouldSkipSpaceAfter = j == m - 1 || valueLastChar == ')' && value.indexOf('progid') == -1; - merged += value + (shouldSkipSpaceAfter ? '' : ' '); - } - } - - if (important) - merged += '!important'; - - merged += (i < l - 1 ? ';' : ''); - } - } - - return merged; -} - -function rebuild(tokens, keepBreaks, isFlatBlock) { - var joinCharacter = isFlatBlock ? ';' : (keepBreaks ? lineBreak : ''); - var parts = []; - var body; - var selector; - - for (var i = 0, l = tokens.length; i < l; i++) { - var token = tokens[i]; - - switch (token[0]) { - case 'at-rule': - case 'text': - parts.push(token[1][0]); - break; - case 'block': - body = rebuild(token[2], keepBreaks, false); - if (body.length > 0) - parts.push(token[1][0] + '{' + body + '}'); - break; - case 'flat-block': - body = bodyRebuilder(token[2]); - if (body.length > 0) - parts.push(token[1][0] + '{' + body + '}'); - break; - default: - selector = selectorRebuilder(token[1]); - body = bodyRebuilder(token[2]); - parts.push(selector + '{' + body + '}'); - } - } - - return parts.join(joinCharacter); -} - -Stringifier.prototype.toString = function (tokens) { - var rebuilt = rebuild(tokens, this.keepBreaks, false); - - return { - styles: this.restoreCallback(rebuilt).trim() - }; -}; - -module.exports = Stringifier; diff --git a/lib/stringifier/helpers.js b/lib/stringifier/helpers.js new file mode 100644 index 00000000..d25fedef --- /dev/null +++ b/lib/stringifier/helpers.js @@ -0,0 +1,136 @@ +var lineBreak = require('os').EOL; + +function hasMoreProperties(tokens, index) { + for (var i = index, l = tokens.length; i < l; i++) { + if (typeof tokens[i] != 'string') + return true; + } + + return false; +} + +function afterClosingBrace(token, valueIndex) { + return token[valueIndex][0][token[valueIndex][0].length - 1] == ')'; +} + +function afterComma(token, valueIndex) { + return token[valueIndex][0] == ','; +} + +function afterSlash(token, valueIndex) { + return token[valueIndex][0] == '/'; +} + +function beforeComma(token, valueIndex) { + return token[valueIndex + 1] && token[valueIndex + 1][0] == ','; +} + +function beforeSlash(token, valueIndex) { + return token[valueIndex + 1] && token[valueIndex + 1][0] == '/'; +} + +function inFilter(token) { + return token[0][0] == 'filter' || token[0][0] == '-ms-filter'; +} + +function inSpecialContext(token, valueIndex) { + return afterClosingBrace(token, valueIndex) || + beforeSlash(token, valueIndex) || + afterSlash(token, valueIndex) || + beforeComma(token, valueIndex) || + afterComma(token, valueIndex); +} + +function selectors(tokens, context) { + var store = context.store; + + for (var i = 0, l = tokens.length; i < l; i++) { + store(tokens[i], context); + + if (i < l - 1) + store(',', context); + } +} + +function body(tokens, context) { + for (var i = 0, l = tokens.length; i < l; i++) { + property(tokens, i, i == l - 1, context); + } +} + +function property(tokens, position, isLast, context) { + var store = context.store; + var token = tokens[position]; + + if (typeof token == 'string') { + store(token, context); + } else { + store(token[0], context); + store(':', context); + value(tokens, position, isLast, context); + } +} + +function value(tokens, position, isLast, context) { + var store = context.store; + var token = tokens[position]; + var isImportant = token[0][1]; + + for (var j = 1, m = token.length; j < m; j++) { + store(token[j], context); + + if (j == m - 1 && isImportant) + store('!important', context); + + if (j < m - 1 && (inFilter(token) || !inSpecialContext(token, j))) { + store(' ', context); + } else if (j == m - 1 && !isLast && hasMoreProperties(tokens, position + 1)) { + store(';', context); + } + } +} + +function all(tokens, context) { + var joinCharacter = context.keepBreaks ? lineBreak : ''; + var store = context.store; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + switch (token[0]) { + case 'at-rule': + case 'text': + store(token[1][0], context); + store(joinCharacter, context); + break; + case 'block': + selectors([token[1]], context); + store('{', context); + all(token[2], context); + store('}', context); + store(joinCharacter, context); + break; + case 'flat-block': + selectors([token[1]], context); + store('{', context); + body(token[2], context); + store('}', context); + store(joinCharacter, context); + break; + default: + selectors(token[1], context); + store('{', context); + body(token[2], context); + store('}', context); + store(joinCharacter, context); + } + } +} + +module.exports = { + all: all, + body: body, + property: property, + selectors: selectors, + value: value +}; diff --git a/lib/stringifier/one-time.js b/lib/stringifier/one-time.js new file mode 100644 index 00000000..2e8d8daa --- /dev/null +++ b/lib/stringifier/one-time.js @@ -0,0 +1,36 @@ +var helpers = require('./helpers'); + +function store(token, context) { + context.output.push(typeof token == 'string' ? token : token[0]); +} + +function context() { + return { + output: [], + store: store + }; +} + +function body(tokens) { + var fakeContext = context(); + helpers.body(tokens, fakeContext); + return fakeContext.output.join(''); +} + +function selectors(tokens) { + var fakeContext = context(); + helpers.selectors(tokens, fakeContext); + return fakeContext.output.join(''); +} + +function value(tokens, position) { + var fakeContext = context(); + helpers.value(tokens, position, true, fakeContext); + return fakeContext.output.join(''); +} + +module.exports = { + body: body, + selectors: selectors, + value: value +}; diff --git a/lib/stringifier/simple.js b/lib/stringifier/simple.js new file mode 100644 index 00000000..80a32dbc --- /dev/null +++ b/lib/stringifier/simple.js @@ -0,0 +1,21 @@ +var all = require('./helpers').all; + +function store(token, context) { + context.output.push(typeof token == 'string' ? token : token[0]); +} + +function stringify(tokens, options, restoreCallback) { + var context = { + keepBreaks: options.keepBreaks, + output: [], + store: store + }; + + all(tokens, context, false); + + return { + styles: restoreCallback(context.output.join('')).trim() + }; +} + +module.exports = stringify; diff --git a/lib/stringifier/source-maps.js b/lib/stringifier/source-maps.js new file mode 100644 index 00000000..dc26fd79 --- /dev/null +++ b/lib/stringifier/source-maps.js @@ -0,0 +1,74 @@ +var SourceMapGenerator = require('source-map').SourceMapGenerator; +var all = require('./helpers').all; + +var unknownSource = '$stdin'; + +function store(element, context) { + // handles defaults and values like `,` or `/` which do not have mapping + if (Array.isArray(element) && element.length == 1) + element = element[0]; + + var fromString = typeof element == 'string'; + var value = fromString ? element : element[0]; + + if (value.indexOf('_') > -1) + value = context.restore(value); + + track(value, fromString ? null : element, context); + context.output.push(value); +} + +function track(value, element, context) { + if (element) + trackMetadata(element, context); + + var parts = value.split('\n'); + context.line += parts.length - 1; + context.column = parts.length > 1 ? 0 : (context.column + parts.pop().length); +} + +function trackMetadata(element, context) { + var sourceAt = element.length - 1; + if (typeof element[sourceAt] == 'object') + sourceAt--; + + var source = element[sourceAt] || unknownSource; + + context.outputMap.addMapping({ + generated: { + line: context.line, + column: context.column + }, + source: source, + original: { + line: element[sourceAt - 2], + column: element[sourceAt - 1] + } + }); + + if (element[sourceAt + 1]) + context.outputMap.setSourceContent(source, element[sourceAt + 1][element[sourceAt]]); +} + +function stringify(tokens, options, restoreCallback, inputMapTracker) { + var context = { + column: 0, + inputMapTracker: inputMapTracker, + keepBreaks: options.keepBreaks, + line: 1, + output: [], + outputMap: new SourceMapGenerator(), + restore: restoreCallback, + sourceMapInlineSources: options.sourceMapInlineSources, + store: store + }; + + all(tokens, context, false); + + return { + sourceMap: context.outputMap, + styles: context.output.join('').trim() + }; +} + +module.exports = stringify; diff --git a/lib/utils/stringify-tokens.js b/lib/utils/stringify-tokens.js deleted file mode 100644 index b77a0b0c..00000000 --- a/lib/utils/stringify-tokens.js +++ /dev/null @@ -1,39 +0,0 @@ -function stringifyValue(property) { - var result = ''; - for (var i = 1, l = property.length; i < l; i++) { - result += property[i][0] + (i < l - 1 ? ' ' : ''); - } - - return result; -} - -function stringifyBody(properties) { - var result = ''; - for (var i = 0, l = properties.length; i < l; i++) { - var property = properties[i]; - - result += property[0][0] + ':'; - for (var j = 1, m = property.length; j < m; j++) { - result += property[j][0] + (j < m - 1 ? ' ' : ''); - } - - result += (i < l - 1 ? ';' : ''); - } - - return result; -} - -function stringifySelector(list) { - var result = ''; - for (var i = 0, l = list.length; i < l; i++) { - result += list[i][0] + (i < l - 1 ? ',' : ''); - } - - return result; -} - -module.exports = { - value: stringifyValue, - body: stringifyBody, - selector: stringifySelector -}; diff --git a/test/fixtures/font-awesome-min.css b/test/fixtures/font-awesome-min.css index 32ad134c..104c8a46 100644 --- a/test/fixtures/font-awesome-min.css +++ b/test/fixtures/font-awesome-min.css @@ -54,19 +54,25 @@ ul.icons li [class*=" icon-"],ul.icons li [class^=icon-]{width:.75em} .btn.btn-large [class*=" icon-"].pull-right.icon-2x,.btn.btn-large [class^=icon-].pull-right.icon-2x{margin-left:.2em} .icon-spin{-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear} @-moz-keyframes spin{0%{-moz-transform:rotate(0)} -100%{-moz-transform:rotate(359deg)}} +100%{-moz-transform:rotate(359deg)} +} @-webkit-keyframes spin{0%{-webkit-transform:rotate(0)} -100%{-webkit-transform:rotate(359deg)}} +100%{-webkit-transform:rotate(359deg)} +} @-o-keyframes spin{0%{-o-transform:rotate(0)} -100%{-o-transform:rotate(359deg)}} +100%{-o-transform:rotate(359deg)} +} @-ms-keyframes spin{0%{-ms-transform:rotate(0)} -100%{-ms-transform:rotate(359deg)}} +100%{-ms-transform:rotate(359deg)} +} @keyframes spin{0%{transform:rotate(0)} -100%{transform:rotate(359deg)}} +100%{transform:rotate(359deg)} +} @-moz-document url-prefix(){.icon-spin{height:.9em} .btn .icon-spin{height:auto} .icon-spin.icon-large{height:1.25em} -.btn .icon-spin.icon-large{height:.75em}} +.btn .icon-spin.icon-large{height:.75em} +} .icon-glass:before{content:"\f000"} .icon-music:before{content:"\f001"} .icon-search:before{content:"\f002"} @@ -315,4 +321,4 @@ ul.icons li [class*=" icon-"],ul.icons li [class^=icon-]{width:.75em} .icon-reply:before{content:"\f112"} .icon-github-alt:before{content:"\f113"} .icon-folder-close-alt:before{content:"\f114"} -.icon-folder-open-alt:before{content:"\f115"} \ No newline at end of file +.icon-folder-open-alt:before{content:"\f115"} diff --git a/test/fixtures/issue-117-snippet-min.css b/test/fixtures/issue-117-snippet-min.css index bb804499..98320d1b 100644 --- a/test/fixtures/issue-117-snippet-min.css +++ b/test/fixtures/issue-117-snippet-min.css @@ -1,3 +1,4 @@ @media only print{a,a:visited{text-decoration:underline} a[href]:after{content:" (" attr(href)")"} -abbr[title]:after{content:" (" attr(title)")"}} \ No newline at end of file +abbr[title]:after{content:" (" attr(title)")"} +} diff --git a/test/fixtures/issue-241-min.css b/test/fixtures/issue-241-min.css index 2ca76423..1fd155f1 100644 --- a/test/fixtures/issue-241-min.css +++ b/test/fixtures/issue-241-min.css @@ -1,2 +1,3 @@ .c4:not(.c1,.c2){width:1px} -@media (min-width:1px){.c3{width:1px}} +@media (min-width:1px){.c3{width:1px} +} diff --git a/test/selectors/optimizer-test.js b/test/selectors/optimizer-test.js index fd8e59a2..06f22f2f 100644 --- a/test/selectors/optimizer-test.js +++ b/test/selectors/optimizer-test.js @@ -1,12 +1,14 @@ var vows = require('vows'); var assert = require('assert'); var SelectorsOptimizer = require('../../lib/selectors/optimizer'); -var Stringifier = require('../../lib/selectors/stringifier'); +var stringify = require('../../lib/stringifier/simple'); var Compatibility = require('../../lib/utils/compatibility'); var SourceTracker = require('../../lib/utils/source-tracker'); function optimizerContext(group, specs, options) { - var stringifier = new Stringifier(false, function (data) { return data; }); + function restoreCallback(data) { + return data; + } var context = {}; options = options || {}; @@ -20,7 +22,7 @@ function optimizerContext(group, specs, options) { function optimized(target) { return function (source) { - assert.equal(new SelectorsOptimizer(options, outerContext).process(source, stringifier).styles, target); + assert.equal(new SelectorsOptimizer(options, outerContext).process(source, stringify, restoreCallback).styles, target); }; } -- 2.34.1