==================
* Cleans up url rebase code getting rid of unnecessary state.
+* Cleans up tokenizer code getting rid of unnecessary state.
* Moves URL rebasing & rewriting into lib/urls.
* Fixed issue [#436](https://github.com/jakubpawlowicz/clean-css/issues/436) - refactors URI rewriting.
-var Tokenizer = require('./tokenizer');
+var tokenize = require('./tokenizer');
var SimpleOptimizer = require('./optimizers/simple');
var AdvancedOptimizer = require('./optimizers/advanced');
var addOptimizationMetadata = require('./optimization-metadata');
}
SelectorsOptimizer.prototype.process = function (data, stringify, restoreCallback, sourceMapTracker) {
- var tokens = new Tokenizer(this.context, this.options.sourceMap).toTokens(data);
+ var tokens = tokenize(data, this.context);
addOptimizationMetadata(tokens);
var flatBlock = /(^@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport|counter\-style)|\\@.+?)/;
-function Tokenizer(minifyContext, sourceMaps) {
- this.minifyContext = minifyContext;
- this.sourceMaps = sourceMaps;
-}
-
-Tokenizer.prototype.toTokens = function (data) {
- data = data.replace(/\r\n/g, '\n');
-
- var chunker = new Chunker(data, '}', 128);
+function tokenize(data, outerContext) {
+ var chunker = new Chunker(normalize(data), '}', 128);
if (chunker.isEmpty())
return [];
var context = {
+ chunk: chunker.next(),
+ chunker: chunker,
+ column: 0,
cursor: 0,
+ line: 1,
mode: 'top',
- chunker: chunker,
- chunk: chunker.next(),
- outer: this.minifyContext,
- track: this.sourceMaps ?
+ resolvePath: outerContext.options.explicitTarget ?
+ relativePathResolver(outerContext.options.root, outerContext.options.target) :
+ null,
+ source: undefined,
+ sourceMap: outerContext.options.sourceMap,
+ sourceMapInlineSources: outerContext.options.sourceMapInlineSources,
+ sourceMapTracker: outerContext.inputSourceMapTracker,
+ sourceReader: outerContext.sourceReader,
+ sourceTracker: outerContext.sourceTracker,
+ state: [],
+ track: outerContext.options.sourceMap ?
function (data, snapshotMetadata, fallbacks) { return [[track(data, context, snapshotMetadata, fallbacks)]]; } :
function () { return []; },
- sourceMaps: this.sourceMaps,
- state: [],
- line: 1,
- column: 0,
- source: undefined
+ warnings: outerContext.warnings
};
- if (this.minifyContext.options.explicitTarget)
- context.resolvePath = relativePathResolver(context);
+ return intoTokens(context);
+}
- return tokenize(context);
-};
+function normalize(data) {
+ return data.replace(/\r\n/g, '\n');
+}
-function relativePathResolver(context) {
- var rebaseTo = path.relative(context.outer.options.root, context.outer.options.target);
+function relativePathResolver(root, target) {
+ var rebaseTo = path.relative(root, target);
return function (relativeTo, sourcePath) {
return relativeTo != sourcePath ?
return [closest, 'special'];
}
-function tokenize(context) {
+function intoTokens(context) {
var chunk = context.chunk;
var tokenized = [];
var newToken;
var whatsLeft = context.chunk.substring(context.cursor);
if (whatsLeft.trim().length > 0) {
if (context.mode == 'body') {
- context.outer.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');
+ context.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');
} else {
tokenized.push(['text', [whatsLeft]]);
}
var isSingle = firstSemicolonAt > -1 && (firstOpenBraceAt == -1 || firstSemicolonAt < firstOpenBraceAt);
var isBroken = firstOpenBraceAt == -1 && firstSemicolonAt == -1;
if (isBroken) {
- context.outer.warnings.push('Broken declaration: \'' + chunk.substring(context.cursor) + '\'.');
+ context.warnings.push('Broken declaration: \'' + chunk.substring(context.cursor) + '\'.');
context.cursor = chunk.length;
} else if (isSingle) {
nextEnd = chunk.indexOf(';', nextSpecial + 1);
newToken.push([trimmedValue].concat(context.track(value, true)));
context.track('{');
- newToken.push(tokenize(context));
+ newToken.push(intoTokens(context));
if (typeof newToken[2] == 'string')
newToken[2] = Extract.properties(newToken[2], [[trimmedValue]], context);
} else if (what == 'escape') {
nextEnd = chunk.indexOf('__', nextSpecial + 1);
var escaped = chunk.substring(context.cursor, nextEnd + 2);
- var isStartSourceMarker = !!context.outer.sourceTracker.nextStart(escaped);
- var isEndSourceMarker = !!context.outer.sourceTracker.nextEnd(escaped);
+ var isStartSourceMarker = !!context.sourceTracker.nextStart(escaped);
+ var isEndSourceMarker = !!context.sourceTracker.nextEnd(escaped);
if (isStartSourceMarker) {
context.track(escaped);
line: context.line,
column: context.column
});
- context.source = context.outer.sourceTracker.nextStart(escaped).filename;
+ context.source = context.sourceTracker.nextStart(escaped).filename;
context.line = 1;
context.column = 0;
} else if (isEndSourceMarker) {
context.cursor = nextSpecial + 1;
context.mode = 'body';
- var body = Extract.properties(tokenize(context), selectors, context);
+ var body = Extract.properties(intoTokens(context), selectors, context);
context.track('{');
context.mode = oldMode;
'Unexpected \'}\' in \'' + chunk.substring(at - 20, at + 20) + '\'. Ignoring.' :
'Unexpected content: \'' + chunk.substring(at, nextSpecial + 1) + '\'. Ignoring.';
- context.outer.warnings.push(warning);
+ context.warnings.push(warning);
context.cursor = nextSpecial + 1;
continue;
}
return tokenized;
}
-module.exports = Tokenizer;
+module.exports = tokenize;
string = string.replace(/(__ESCAPED_COMMENT_(SPECIAL_)?CLEAN_CSS[^_]+?__)/g, ';$1;');
if (string.indexOf(')') > -1)
- string = string.replace(/\)([^\s_;:,\)])/g, context.sourceMaps ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ $1' : ') $1');
+ string = string.replace(/\)([^\s_;:,\)])/g, context.sourceMap ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ $1' : ') $1');
if (string.indexOf('ESCAPED_URL_CLEAN_CSS') > -1)
- string = string.replace(/(ESCAPED_URL_CLEAN_CSS[^_]+?__)/g, context.sourceMaps ? '$1 __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ ' : '$1 ');
+ string = string.replace(/(ESCAPED_URL_CLEAN_CSS[^_]+?__)/g, context.sourceMap ? '$1 __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ ' : '$1 ');
var candidates = string.split(';');
var values = splitter.split(candidate.substring(firstColonAt + 1), true);
if (values.length == 1 && values[0] === '') {
- context.outer.warnings.push('Empty property \'' + name + '\' inside \'' + selectors.filter(selectorName).join(',') + '\' selector. Ignoring.');
+ context.warnings.push('Empty property \'' + name + '\' inside \'' + selectors.filter(selectorName).join(',') + '\' selector. Ignoring.');
continue;
}
source: context.source
};
var sourceContent = null;
- var sourceMetadata = context.outer.inputSourceMapTracker.isTracking(metadata.source) ?
- context.outer.inputSourceMapTracker.originalPositionFor(metadata, data, fallbacks || 0) :
+ var sourceMetadata = context.sourceMapTracker.isTracking(metadata.source) ?
+ context.sourceMapTracker.originalPositionFor(metadata, data, fallbacks || 0) :
{};
metadata.line = sourceMetadata.line || metadata.line;
sourceMetadata.source :
sourceFor(sourceMetadata, metadata, context);
- if (context.outer.options.sourceMapInlineSources) {
- var sourceMapSourcesContent = context.outer.inputSourceMapTracker.sourcesContentFor(context.source);
+ if (context.sourceMapInlineSources) {
+ var sourceMapSourcesContent = context.sourceMapTracker.sourcesContentFor(context.source);
sourceContent = sourceMapSourcesContent && sourceMapSourcesContent[metadata.source] ?
sourceMapSourcesContent :
- context.outer.sourceReader.sourceAt(context.source);
+ context.sourceReader.sourceAt(context.source);
}
return sourceContent ?
var optimize = require('../../lib/properties/optimizer');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var Compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
function _optimize(source) {
- var tokens = new Tokenizer({
+ var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
warnings: []
- }).toTokens(source);
+ });
addOptimizationMetadata(tokens);
var optimize = require('../../lib/properties/optimizer');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var Compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var validator = new Validator(compatibility);
function _optimize(source, mergeAdjacent, aggressiveMerging) {
- var tokens = new Tokenizer({
+ var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
warnings: []
- }).toTokens(source);
+ });
addOptimizationMetadata(tokens);
optimize(tokens[0][1], tokens[0][2], mergeAdjacent, true, { compatibility: compatibility, aggressiveMerging: aggressiveMerging }, validator);
var optimize = require('../../lib/properties/optimizer');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var Compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
function _optimize(source, compatibility, aggressiveMerging) {
- var tokens = new Tokenizer({
+ var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
warnings: []
- }).toTokens(source);
+ });
compatibility = new Compatibility(compatibility).toOptions();
var validator = new Validator(compatibility);
var optimize = require('../../lib/properties/optimizer');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var SourceReader = require('../../lib/utils/source-reader');
var InputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
errors: {},
sourceTracker: new SourceTracker()
});
- var tokens = new Tokenizer({
- options: {},
+ var tokens = tokenize(source, {
+ options: { sourceMap: true },
inputSourceMapTracker: inputSourceMapTracker,
sourceReader: new SourceReader(),
sourceTracker: new SourceTracker(),
warnings: []
- }, true).toTokens(source);
+ });
var compatibility = new Compatibility().toOptions();
var validator = new Validator(compatibility);
var optimize = require('../../lib/properties/optimizer');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var Compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
function _optimize(source) {
- var tokens = new Tokenizer({
+ var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
warnings: []
- }).toTokens(source);
+ });
var compatibility = new Compatibility(compatibility).toOptions();
var validator = new Validator(compatibility);
var vows = require('vows');
var assert = require('assert');
-var SelectorTokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var extractor = require('../../lib/selectors/extractor');
function buildToken(source) {
- return new SelectorTokenizer({ options: {} }).toTokens(source)[0];
+ return tokenize(source, { options: {} })[0];
}
vows.describe(extractor)
var vows = require('vows');
var assert = require('assert');
-var Tokenizer = require('../../../lib/selectors/tokenizer');
+var tokenize = require('../../../lib/selectors/tokenizer');
var SimpleOptimizer = require('../../../lib/selectors/optimizers/simple');
var Compatibility = require('../../../lib/utils/compatibility');
var addOptimizationMetadata = require('../../../lib/selectors/optimization-metadata');
function optimized(selectors) {
return function (source) {
- var tokens = new Tokenizer({ options: {} }).toTokens(source);
+ var tokens = tokenize(source, { options: {} });
new SimpleOptimizer(options).optimize(tokens);
assert.deepEqual(tokens[0] ? tokens[0][1] : null, selectors);
function optimized(selectors) {
return function (source) {
- var tokens = new Tokenizer({ options: {} }).toTokens(source);
+ var tokens = tokenize(source, { options: {} });
addOptimizationMetadata(tokens);
new SimpleOptimizer(options).optimize(tokens);
var vows = require('vows');
var assert = require('assert');
-var SelectorTokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var extractProperties = require('../../lib/selectors/extractor');
var canReorder = require('../../lib/selectors/reorderable').canReorder;
var canReorderSingle = require('../../lib/selectors/reorderable').canReorderSingle;
function propertiesIn(source) {
- return extractProperties(new SelectorTokenizer({ options: {} }, false).toTokens(source)[0]);
+ return extractProperties(tokenize(source, { options: {} })[0]);
}
vows.describe(canReorder)
var vows = require('vows');
var assert = require('assert');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
var SourceReader = require('../../lib/utils/source-reader');
var InputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
function toTokens(source) {
return function () {
- return new Tokenizer({
+ return tokenize(source, {
sourceTracker: sourceTracker,
sourceReader: sourceReader,
inputSourceMapTracker: inputSourceMapTracker,
- options: {}
- }, true).toTokens(source);
+ options: { sourceMap: true }
+ });
};
}
var tracker = new SourceTracker();
var reader = new SourceReader();
var inputTracker = new InputSourceMapTracker({ options: { inliner: {} }, errors: {}, sourceTracker: tracker });
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
var data = tracker.store('one.css', 'a{}');
- return tokenizer.toTokens(data);
+ return tokenize(data, {
+ sourceTracker: tracker,
+ sourceReader: reader,
+ inputSourceMapTracker: inputTracker,
+ options: { sourceMap: true }
+ });
},
[
[
var tracker = new SourceTracker();
var reader = new SourceReader();
var inputTracker = new InputSourceMapTracker({ options: { inliner: {} }, errors: {}, sourceTracker: tracker });
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
var data1 = tracker.store('one.css', 'a{}');
var data2 = tracker.store('two.css', '\na{color:red}');
- return tokenizer.toTokens(data1 + data2);
+ return tokenize(data1 + data2, {
+ sourceTracker: tracker,
+ sourceReader: reader,
+ inputSourceMapTracker: inputTracker,
+ options: { sourceMap: true }
+ });
},
[
[
var inputTracker = new InputSourceMapTracker({ options: { inliner: {}, sourceMap: inputMap, options: {} }, errors: {}, sourceTracker: tracker });
inputTracker.track('', function () {});
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
- return tokenizer.toTokens('div > a {\n color: red;\n}');
+ return tokenize('div > a {\n color: red;\n}', {
+ sourceTracker: tracker,
+ sourceReader: reader,
+ inputSourceMapTracker: inputTracker,
+ options: { sourceMap: true }
+ });
},
[
[
var inputTracker = new InputSourceMapTracker({ options: { inliner: {}, sourceMap: inputMap, options: {} }, errors: {}, sourceTracker: tracker });
inputTracker.track('', function () {});
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
- return tokenizer.toTokens('div > a {\n color: red red;\n}');
+ return tokenize('div > a {\n color: red red;\n}', {
+ sourceTracker: tracker,
+ sourceReader: reader,
+ inputSourceMapTracker: inputTracker,
+ options: { sourceMap: true }
+ });
},
[
[
var vows = require('vows');
var assert = require('assert');
-var Tokenizer = require('../../lib/selectors/tokenizer');
+var tokenize = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
function tokenizerContext(name, specs) {
function tokenized(target) {
return function (source) {
- var tokenized = new Tokenizer({
+ var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
warnings: []
- }).toTokens(source);
+ });
- assert.deepEqual(tokenized, target);
+ assert.deepEqual(tokens, target);
};
}
return ctx;
}
-vows.describe(Tokenizer)
+vows.describe(tokenize)
.addBatch(
tokenizerContext('basic', {
'no content': [