var override = require('./utils/override');
var DEFAULT_TIMEOUT = 5000;
+var inputSourceMapTracker = require('./utils/input-source-map-tracker');
var readSources = require('./utils/read-sources');
var basicOptimize = require('./optimizer/basic');
var advancedOptimize = require('./optimizer/advanced');
timeSpent: 0
},
errors: [],
- warnings: [],
- options: this.options,
+ inputSourceMapTracker: inputSourceMapTracker(),
localOnly: !callback,
- validator: new Validator(this.options.compatibility)
+ options: this.options,
+ source: null,
+ sourcesContent: {},
+ validator: new Validator(this.options.compatibility),
+ warnings: []
};
+ if (typeof this.options.sourceMap == 'string') {
+ context.inputSourceMapTracker.track(undefined, this.options.sourceMap);
+ }
+
return runner(context.localOnly)(function () {
return readSources(input, context, function (tokens) {
var stringify = context.options.sourceMap ?
return false;
}
-function componentSourceMaps(components) {
- var sourceMapping = [];
-
- for (var name in components) {
- var component = components[name];
- var originalValue = component.all[component.position];
- var mapping = originalValue[0][originalValue[0].length - 1];
-
- if (Array.isArray(mapping))
- Array.prototype.push.apply(sourceMapping, mapping);
+function joinMetadata(components, at) {
+ var metadata = [];
+ var component;
+ var originalValue;
+ var componentMetadata;
+ var name;
+
+ for (name in components) {
+ component = components[name];
+ originalValue = component.all[component.position];
+ componentMetadata = originalValue[at][originalValue[at].length - 1];
+
+ Array.prototype.push.apply(metadata, componentMetadata);
}
- return sourceMapping;
+ return metadata;
}
function replaceWithShorthand(properties, candidateComponents, name, validator) {
var descriptor = compactable[name];
+ var nameMetadata;
+ var valueMetadata;
var newValuePlaceholder = [
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
candidateComponents[componentName].unused = true;
}
- // var sourceMapping = componentSourceMaps(candidateComponents);
- // if (sourceMapping.length > 0)
- // newValuePlaceholder[0].push(sourceMapping);
- // }
+ nameMetadata = joinMetadata(candidateComponents, 1);
+ newValuePlaceholder[1].push(nameMetadata);
+
+ valueMetadata = joinMetadata(candidateComponents, 2);
+ newValuePlaceholder[2].push(valueMetadata);
newProperty.position = all.length;
newProperty.all = all;
+++ /dev/null
-var escapePrefix = '__ESCAPED_';
-
-function trackPrefix(value, context, interestingContent) {
- if (!interestingContent && value.indexOf('\n') == -1) {
- if (value.indexOf(escapePrefix) === 0) {
- return value;
- } else {
- context.column += value.length;
- return;
- }
- }
-
- var withoutContent = 0;
- var split = value.split('\n');
- var total = split.length;
- var shift = 0;
-
- while (true) {
- if (withoutContent == total - 1)
- break;
-
- var part = split[withoutContent];
- if (/\S/.test(part))
- break;
-
- shift += part.length + 1;
- withoutContent++;
- }
-
- context.line += withoutContent;
- context.column = withoutContent > 0 ? 0 : context.column;
- context.column += /^(\s)*/.exec(split[withoutContent])[0].length;
-
- return value.substring(shift).trimLeft();
-}
-
-function sourceFor(originalMetadata, contextMetadata, context) {
- var source = originalMetadata.source || contextMetadata.source;
-
- if (source && context.resolvePath)
- return context.resolvePath(contextMetadata.source, source);
-
- return source;
-}
-
-function snapshot(data, context, fallbacks) {
- var metadata = {
- line: context.line,
- column: context.column,
- source: context.source
- };
- var sourceContent = null;
- var sourceMetadata = context.sourceMapTracker.isTracking(metadata.source) ?
- context.sourceMapTracker.originalPositionFor(metadata, data, fallbacks || 0) :
- {};
-
- metadata.line = sourceMetadata.line || metadata.line;
- metadata.column = sourceMetadata.column || metadata.column;
- metadata.source = sourceMetadata.sourceResolved ?
- sourceMetadata.source :
- sourceFor(sourceMetadata, metadata, context);
-
- if (context.sourceMapInlineSources) {
- var sourceMapSourcesContent = context.sourceMapTracker.sourcesContentFor(context.source);
- sourceContent = sourceMapSourcesContent && sourceMapSourcesContent[metadata.source] ?
- sourceMapSourcesContent :
- context.sourceReader.sourceAt(context.source);
- }
-
- return sourceContent ?
- [metadata.line, metadata.column, metadata.source, sourceContent] :
- [metadata.line, metadata.column, metadata.source];
-}
-
-function trackSuffix(data, context) {
- var parts = data.split('\n');
-
- for (var i = 0, l = parts.length; i < l; i++) {
- var part = parts[i];
- var cursor = 0;
-
- if (i > 0) {
- context.line++;
- context.column = 0;
- }
-
- while (true) {
- var next = part.indexOf(escapePrefix, cursor);
-
- if (next == -1) {
- context.column += part.substring(cursor).length;
- break;
- }
-
- context.column += next - cursor;
- cursor += next - cursor;
-
- var escaped = part.substring(next, part.indexOf('__', next + 1) + 2);
- var encodedValues = escaped.substring(escaped.indexOf('(') + 1, escaped.indexOf(')')).split(',');
- context.line += ~~encodedValues[0];
- context.column = (~~encodedValues[0] === 0 ? context.column : 0) + ~~encodedValues[1];
- cursor += escaped.length;
- }
- }
-}
-
-function track(data, context, snapshotMetadata, fallbacks) {
- var untracked = trackPrefix(data, context, snapshotMetadata);
- var metadata = snapshotMetadata ?
- snapshot(untracked, context, fallbacks) :
- [];
-
- if (untracked)
- trackSuffix(untracked, context);
-
- return metadata;
-}
-
-module.exports = track;
break;
case Token.AT_RULE_BLOCK:
rules(token[1], context);
- store(joinCharacter, context);
store(Marker.OPEN_BRACE, context);
body(token[2], context);
store(Marker.CLOSE_BRACE, context);
rules(token[1], context);
store(Marker.OPEN_BRACE, context);
all(token[2], context);
+ store(joinCharacter, context);
store(Marker.CLOSE_BRACE, context);
break;
case Token.COMMENT:
var SourceMapGenerator = require('source-map').SourceMapGenerator;
var all = require('./helpers').all;
+var isRemoteResource = require('../utils/is-remote-resource');
+
var isWindows = process.platform == 'win32';
-var unknownSource = '$stdin';
+var UNKNOWN_SOURCE = '$stdin';
-function store(element, context) {
+function store(element, stringifyContext) {
var fromString = typeof element == 'string';
var value = fromString ? element : element[1];
+ var mappings = fromString ? null : element[2];
- if (value.indexOf('_') > -1)
- value = context.restore(value, prefixContentFrom(context.output));
-
- track(value, fromString ? null : element, context);
- context.output.push(value);
+ track(value, mappings, stringifyContext);
+ stringifyContext.output.push(value);
}
-function prefixContentFrom(values) {
- var content = [];
-
- for (var i = values.length - 1; i >= 0; i--) {
- var value = values[i];
- content.unshift(value);
+function track(value, mappings, stringifyContext) {
+ var parts = value.split('\n');
- if (value == '{' || value == ';')
- break;
+ if (mappings) {
+ trackAllMappings(mappings, stringifyContext);
}
- return content.join('');
-}
-
-function track(value, element, context) {
- if (element)
- trackAllMappings(element, context);
-
- var parts = value.split('\n');
- context.line += parts.length - 1;
- context.column = parts.length > 1 ? 0 : (context.column + parts.pop().length);
+ stringifyContext.line += parts.length - 1;
+ stringifyContext.column = parts.length > 1 ? 0 : (stringifyContext.column + parts.pop().length);
}
-function trackAllMappings(element, context) {
- var mapping = element[element.length - 1];
-
- if (!Array.isArray(mapping))
- return;
-
- for (var i = 0, l = mapping.length; i < l; i++) {
- trackMapping(mapping[i], context);
+function trackAllMappings(mappings, stringifyContext) {
+ for (var i = 0, l = mappings.length; i < l; i++) {
+ trackMapping(mappings[i], stringifyContext);
}
}
-function trackMapping(mapping, context) {
- var source = mapping[2] || unknownSource;
+function trackMapping(mapping, stringifyContext) {
+ var line = mapping[0];
+ var column = mapping[1];
+ var originalSource = mapping[2];
+ var source = originalSource;
+ var storedSource = source || UNKNOWN_SOURCE;
- if (isWindows)
+ if (isWindows && source && !isRemoteResource(source)) {
source = source.replace(/\\/g, '/');
+ }
- context.outputMap.addMapping({
+ stringifyContext.outputMap.addMapping({
generated: {
- line: context.line,
- column: context.column
+ line: stringifyContext.line,
+ column: stringifyContext.column
},
- source: source,
+ source: storedSource,
original: {
- line: mapping[0],
- column: mapping[1]
+ line: line,
+ column: column
}
});
- if (mapping[3])
- context.outputMap.setSourceContent(source, mapping[3][mapping[2]]);
+ if (stringifyContext.inlineSources && (originalSource in stringifyContext.sourcesContent)) {
+ stringifyContext.outputMap.setSourceContent(storedSource, stringifyContext.sourcesContent[originalSource]);
+ }
}
function stringify(tokens, context) {
var stringifyContext = {
column: 0,
- inputMapTracker: context.inputMapTracker,
keepBreaks: context.options.keepBreaks,
+ inlineSources: context.options.sourceMapInlineSources,
line: 1,
output: [],
outputMap: new SourceMapGenerator(),
- sourceMapInlineSources: context.options.sourceMapInlineSources,
+ sourcesContent: context.sourcesContent,
spaceAfterClosingBrace: context.options.compatibility.properties.spaceAfterClosingBrace,
store: store
};
var levels = [];
var buffer = [];
var buffers = [];
+ var serializedBuffer;
var roundBracketLevel = 0;
var isQuoted;
var isSpace;
isCommentEnd = !wasCommentStart && level == Level.COMMENT && character == Marker.FORWARD_SLASH && source[position.index - 1] == Marker.STAR;
metadata = buffer.length === 0 ?
- metadataFrom(position, 0, externalContext) :
+ [position.line, position.column, position.source] :
metadata;
if (isEscaped) {
buffers.push(buffer.slice(0, buffer.length - 2));
buffer = buffer.slice(buffer.length - 2);
- metadata = metadataFrom(position, 1, externalContext);
+ metadata = [position.line, position.column - 1, position.source];
levels.push(level);
level = Level.COMMENT;
buffer.push(character);
} else if (isCommentEnd) {
// comment end, e.g. /* comment */<--
- lastToken = [Token.COMMENT, buffer.join('').trim() + character, [metadata]];
+ serializedBuffer = buffer.join('').trim() + character;
+ lastToken = [Token.COMMENT, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]];
newTokens.push(lastToken);
level = levels.pop();
roundBracketLevel--;
} else if (character == Marker.SEMICOLON && level == Level.BLOCK) {
// semicolon ending rule at block level, e.g. @import '...';<--
- allTokens.push([Token.AT_RULE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ allTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
buffer = [];
} else if (character == Marker.COMMA && level == Level.BLOCK && ruleToken) {
// comma separator at block level, e.g. a,div,<--
- ruleToken[1].push([tokenScopeFrom(ruleToken[0]), buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]]);
buffer = [];
} else if (character == Marker.COMMA && level == Level.BLOCK && tokenTypeFrom(buffer) == Token.AT_RULE) {
} else if (character == Marker.COMMA && level == Level.BLOCK) {
// comma separator at block level, e.g. a,<--
ruleToken = [tokenTypeFrom(buffer), [], []];
- ruleToken[1].push([tokenScopeFrom(ruleToken[0]), buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, 0)]]);
buffer = [];
} else if (character == Marker.OPEN_BRACE && level == Level.BLOCK && ruleToken && ruleToken[0] == Token.BLOCK) {
// open brace opening at-rule at block level, e.g. @media{<--
- ruleToken[1].push([Token.BLOCK_SCOPE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ ruleToken[1].push([Token.BLOCK_SCOPE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
allTokens.push(ruleToken);
levels.push(level);
ruleToken = null;
} else if (character == Marker.OPEN_BRACE && level == Level.BLOCK && tokenTypeFrom(buffer) == Token.BLOCK) {
// open brace opening at-rule at block level, e.g. @media{<--
+ serializedBuffer = buffer.join('').trim();
ruleToken = ruleToken || [Token.BLOCK, [], []];
- ruleToken[1].push([Token.BLOCK_SCOPE, buffer.join('').trim(), [metadata]]);
+ ruleToken[1].push([Token.BLOCK_SCOPE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
allTokens.push(ruleToken);
levels.push(level);
ruleToken = null;
} else if (character == Marker.OPEN_BRACE && level == Level.BLOCK) {
// open brace opening rule at block level, e.g. div{<--
+ serializedBuffer = buffer.join('').trim();
ruleToken = ruleToken || [tokenTypeFrom(buffer), [], []];
- ruleToken[1].push([tokenScopeFrom(ruleToken[0]), buffer.join('').trim(), [metadata]]);
+ ruleToken[1].push([tokenScopeFrom(ruleToken[0]), serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]]);
newTokens = ruleToken[2];
allTokens.push(ruleToken);
seekingValue = false;
} else if (character == Marker.COLON && level == Level.RULE && !seekingValue) {
// colon at rule level, e.g. a{color:<--
- propertyToken = [Token.PROPERTY, [Token.PROPERTY_NAME, buffer.join('').trim(), [metadata]]];
+ serializedBuffer = buffer.join('').trim();
+ propertyToken = [Token.PROPERTY, [Token.PROPERTY_NAME, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]];
newTokens.push(propertyToken);
seekingValue = true;
buffer = [];
} else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && ruleTokens.length > 0 && buffer.length > 0 && buffer[0] == Marker.AT) {
// semicolon at rule level for at-rule, e.g. a{--color:{@apply(--other-color);<--
- ruleToken[1].push([Token.AT_RULE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ ruleToken[1].push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
buffer = [];
} else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && buffer.length > 0) {
// semicolon at rule level, e.g. a{color:red;<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
seekingValue = false;
buffer = [];
seekingValue = false;
} else if (character == Marker.SEMICOLON && level == Level.RULE && buffer.length > 0 && buffer[0] == Marker.AT) {
// semicolon for at-rule at rule level, e.g. a{@apply(--variable);<--
- newTokens.push([Token.AT_RULE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('');
+ newTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
seekingValue = false;
buffer = [];
// noop
} else if (character == Marker.CLOSE_BRACE && level == Level.RULE && propertyToken && seekingValue && buffer.length > 0 && ruleTokens.length > 0) {
// close brace at rule level, e.g. a{--color:{color:red}<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join(''), [metadata]]);
+ serializedBuffer = buffer.join('');
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
propertyToken = null;
ruleToken = ruleTokens.pop();
newTokens = ruleToken[2];
buffer = [];
} else if (character == Marker.CLOSE_BRACE && level == Level.RULE && propertyToken && buffer.length > 0 && buffer[0] == Marker.AT && ruleTokens.length > 0) {
// close brace at rule level for at-rule, e.g. a{--color:{@apply(--other-color)}<--
- ruleToken[1].push([Token.AT_RULE, buffer.join(''), [metadata]]);
+ serializedBuffer = buffer.join('');
+ ruleToken[1].push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
propertyToken = null;
ruleToken = ruleTokens.pop();
newTokens = ruleToken[2];
seekingValue = false;
} else if (character == Marker.CLOSE_BRACE && level == Level.RULE && propertyToken && buffer.length > 0) {
// close brace at rule level, e.g. a{color:red}<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join(''), [metadata]]);
+ serializedBuffer = buffer.join('');
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
propertyToken = null;
ruleToken = ruleTokens.pop();
newTokens = allTokens;
} else if (character == Marker.CLOSE_BRACE && level == Level.RULE && buffer.length > 0 && buffer[0] == Marker.AT) {
// close brace after at-rule at rule level, e.g. a{@apply(--variable)}<--
ruleToken = null;
- newTokens.push([Token.AT_RULE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ newTokens.push([Token.AT_RULE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
newTokens = allTokens;
level = levels.pop();
} else if (character == Marker.CLOSE_ROUND_BRACKET && level == Level.RULE && seekingValue && roundBracketLevel == 1) {
// round close bracket, e.g. a{color:hsla(0,0%,0%)<--
buffer.push(character);
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
roundBracketLevel--;
buffer = [];
roundBracketLevel--;
} else if (character == Marker.FORWARD_SLASH && source[position.index + 1] != Marker.STAR && level == Level.RULE && seekingValue && buffer.length > 0) {
// forward slash within a property, e.g. a{background:url(image.png) 0 0/<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
- propertyToken.push([Token.PROPERTY_VALUE, character, [metadataFrom(position, 0, externalContext)]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
+ propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]);
buffer = [];
} else if (character == Marker.FORWARD_SLASH && source[position.index + 1] != Marker.STAR && level == Level.RULE && seekingValue) {
// forward slash within a property after space, e.g. a{background:url(image.png) 0 0 /<--
- propertyToken.push([Token.PROPERTY_VALUE, character, [metadataFrom(position, 0, externalContext)]]);
+ propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]);
buffer = [];
} else if (character == Marker.COMMA && level == Level.RULE && seekingValue && buffer.length > 0) {
// comma within a property, e.g. a{background:url(image.png),<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
- propertyToken.push([Token.PROPERTY_VALUE, character, [metadataFrom(position, 0, externalContext)]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
+ propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]);
buffer = [];
} else if (character == Marker.COMMA && level == Level.RULE && seekingValue) {
// comma within a property after space, e.g. a{background:url(image.png) ,<--
- propertyToken.push([Token.PROPERTY_VALUE, character, [metadataFrom(position, 0, externalContext)]]);
+ propertyToken.push([Token.PROPERTY_VALUE, character, [[position.line, position.column, position.source]]]);
buffer = [];
} else if ((isSpace || (isNewLineNix && !isNewLineWin)) && level == Level.RULE && seekingValue && propertyToken && buffer.length > 0) {
// space or *nix newline within property, e.g. a{margin:0 <--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
buffer = [];
} else if (isNewLineWin && level == Level.RULE && seekingValue && propertyToken && buffer.length > 1) {
// win newline within property, e.g. a{margin:0\r\n<--
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').trim(), [metadata]]);
+ serializedBuffer = buffer.join('').trim();
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
buffer = [];
} else if (isNewLineWin && level == Level.RULE && seekingValue) {
}
if (seekingValue && buffer.length > 0) {
- propertyToken.push([Token.PROPERTY_VALUE, buffer.join('').replace(TAIL_BROKEN_VALUE_PATTERN, ''), [metadata]]);
+ serializedBuffer = buffer.join('').replace(TAIL_BROKEN_VALUE_PATTERN, '');
+ propertyToken.push([Token.PROPERTY_VALUE, serializedBuffer, [originalMetadata(metadata, serializedBuffer, externalContext)]]);
}
return allTokens;
}
-function metadataFrom(position, columnDelta, externalContext) {
- var metadata = [position.line, position.column - columnDelta, position.source];
- metadata = externalContext.inputSourceMap ?
- externalContext.inputSourceMapTracker.originalPositionFor(metadata) :
- metadata;
+function originalMetadata(metadata, value, externalContext, selectorFallbacks) {
+ var source = metadata[2];
- return metadata;
+ return externalContext.inputSourceMapTracker.isTracking(source) ?
+ externalContext.inputSourceMapTracker.originalPositionFor(metadata, value.length, selectorFallbacks) :
+ metadata;
}
function tokenTypeFrom(buffer) {
return ROUND_BRACKETS_PATTERN.test(url);
}
-function rewriteUrl(originalUrl, rebaseConfig) {
+function rewriteUrl(originalUrl, rebaseConfig, pathOnly) {
var strippedUrl = originalUrl
.replace(URL_PREFIX_PATTERN, '')
.replace(URL_SUFFIX_PATTERN, '')
strippedUrl[0] :
quoteFor(unquotedUrl);
- return URL_PREFIX + quote + rebase(unquotedUrl, rebaseConfig) + quote + URL_SUFFIX;
+ return pathOnly ?
+ rebase(unquotedUrl, rebaseConfig) :
+ URL_PREFIX + quote + rebase(unquotedUrl, rebaseConfig) + quote + URL_SUFFIX;
}
module.exports = rewriteUrl;
--- /dev/null
+var fs = require('fs');
+var path = require('path');
+
+var isAllowedResource = require('./is-allowed-resource');
+var isDataUriResource = require('./is-data-uri-resource');
+var isRemoteResource = require('./is-remote-resource');
+var loadRemoteResource = require('./load-remote-resource');
+var matchDataUri = require('./match-data-uri');
+var rebaseLocalMap = require('./rebase-local-map');
+var rebaseRemoteMap = require('./rebase-remote-map');
+
+var Token = require('../tokenizer/token');
+
+var MAP_MARKER_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/;
+
+function applySourceMaps(tokens, context, callback) {
+ var applyContext = {
+ callback: callback,
+ index: 0,
+ inliner: context.options.inliner,
+ inputSourceMapTracker: context.inputSourceMapTracker,
+ localOnly: context.localOnly,
+ processedTokens: [],
+ processImportFrom: context.options.processImportFrom,
+ rebaseTo: context.options.rebaseTo,
+ sourceTokens: tokens,
+ warnings: context.warnings
+ };
+
+ return tokens.length > 0 ?
+ doApplySourceMaps(applyContext) :
+ callback(tokens);
+}
+
+function doApplySourceMaps(applyContext) {
+ var singleSourceTokens = [];
+ var lastSource = findTokenSource(applyContext.sourceTokens[0]);
+ var source;
+ var token;
+ var l;
+
+ for (l = applyContext.sourceTokens.length; applyContext.index < l; applyContext.index++) {
+ token = applyContext.sourceTokens[applyContext.index];
+ source = findTokenSource(token);
+
+ if (source != lastSource) {
+ singleSourceTokens = [];
+ lastSource = source;
+ }
+
+ singleSourceTokens.push(token);
+ applyContext.processedTokens.push(token);
+
+ if (token[0] == Token.COMMENT && MAP_MARKER_PATTERN.test(token[1])) {
+ return fetchAndApplySourceMap(token[1], source, singleSourceTokens, applyContext);
+ }
+ }
+
+ return applyContext.callback(applyContext.processedTokens);
+}
+
+function findTokenSource(token) {
+ var scope;
+ var metadata;
+
+ if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) {
+ metadata = token[2][0];
+ } else {
+ scope = token[1][0];
+ metadata = scope[2][0];
+ }
+
+ return metadata[2];
+}
+
+function fetchAndApplySourceMap(sourceMapComment, source, singleSourceTokens, applyContext) {
+ return extractInputSourceMapFrom(sourceMapComment, applyContext, function (inputSourceMap) {
+ if (inputSourceMap) {
+ applyContext.inputSourceMapTracker.track(source, inputSourceMap);
+ applySourceMapRecursively(singleSourceTokens, applyContext.inputSourceMapTracker);
+ }
+
+ applyContext.index++;
+ return doApplySourceMaps(applyContext);
+ });
+}
+
+function extractInputSourceMapFrom(sourceMapComment, applyContext, whenSourceMapReady) {
+ var uri = MAP_MARKER_PATTERN.exec(sourceMapComment)[1];
+ var rebasedToCurrentPath;
+ var sourceMap;
+ var rebasedMap;
+
+ if (isDataUriResource(uri)) {
+ sourceMap = extractInputSourceMapFromDataUri(uri);
+ return whenSourceMapReady(sourceMap);
+ } else if (isRemoteResource(uri)) {
+ return loadInputSourceMapFromRemoteUri(uri, applyContext, function (sourceMap) {
+ var parsedMap;
+
+ if (sourceMap) {
+ parsedMap = JSON.parse(sourceMap);
+ rebasedMap = rebaseRemoteMap(parsedMap, uri);
+ whenSourceMapReady(rebasedMap);
+ } else {
+ whenSourceMapReady(null);
+ }
+ });
+ } else {
+ // at this point `uri` is already rebased, see read-sources.js#rebaseSourceMapComment
+ // it is rebased to be consistent with rebasing other URIs
+ // however here we need to resolve it back to read it from disk
+ rebasedToCurrentPath = path.resolve(applyContext.rebaseTo, uri);
+ sourceMap = loadInputSourceMapFromLocalUri(rebasedToCurrentPath, applyContext);
+
+ if (sourceMap) {
+ rebasedMap = rebaseLocalMap(sourceMap, rebasedToCurrentPath, applyContext.rebaseTo);
+ return whenSourceMapReady(rebasedMap);
+ } else {
+ return whenSourceMapReady(null);
+ }
+ }
+}
+
+function extractInputSourceMapFromDataUri(uri) {
+ var dataUriMatch = matchDataUri(uri);
+ var charset = dataUriMatch[2] ? dataUriMatch[2].split(/[=;]/)[2] : 'us-ascii';
+ var encoding = dataUriMatch[3] ? dataUriMatch[3].split(';')[1] : 'utf8';
+ var data = encoding == 'utf8' ? global.unescape(dataUriMatch[4]) : dataUriMatch[4];
+
+ var buffer = new Buffer(data, encoding);
+ buffer.charset = charset;
+
+ return JSON.parse(buffer.toString());
+}
+
+function loadInputSourceMapFromRemoteUri(uri, applyContext, whenLoaded) {
+ var isAllowed = isAllowedResource(uri, true, applyContext.processImportFrom);
+
+ if (applyContext.localOnly) {
+ applyContext.warnings.push('Cannot fetch remote resource from "' + uri + '" as no callback given.');
+ return whenLoaded(null);
+ } else if (!isAllowed) {
+ applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.');
+ return whenLoaded(null);
+ }
+
+ loadRemoteResource(uri, applyContext.inliner, function (error, body) {
+ if (error) {
+ applyContext.warnings.push('Missing source map at "' + uri + '" - ' + error);
+ return whenLoaded(null);
+ }
+
+ whenLoaded(body);
+ });
+}
+
+function loadInputSourceMapFromLocalUri(uri, applyContext) {
+ var isAllowed = isAllowedResource(uri, true, applyContext.processImportFrom);
+ var sourceMap;
+
+ if (!fs.existsSync(uri) || !fs.statSync(uri).isFile()) {
+ applyContext.warnings.push('Ignoring local source map at "' + uri + '" as resource is missing.');
+ return null;
+ } else if (!isAllowed) {
+ applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.');
+ return null;
+ }
+
+ sourceMap = fs.readFileSync(uri, 'utf-8');
+ return JSON.parse(sourceMap);
+}
+
+function applySourceMapRecursively(tokens, inputSourceMapTracker) {
+ var token;
+ var i, l;
+
+ for (i = 0, l = tokens.length; i < l; i++) {
+ token = tokens[i];
+
+ switch (token[0]) {
+ case Token.AT_RULE:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.AT_RULE_BLOCK:
+ applySourceMapRecursively(token[1], inputSourceMapTracker);
+ applySourceMapRecursively(token[2], inputSourceMapTracker);
+ break;
+ case Token.AT_RULE_BLOCK_SCOPE:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.BLOCK:
+ applySourceMapRecursively(token[1], inputSourceMapTracker);
+ applySourceMapRecursively(token[2], inputSourceMapTracker);
+ break;
+ case Token.BLOCK_SCOPE:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.COMMENT:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.PROPERTY:
+ applySourceMapRecursively(token, inputSourceMapTracker);
+ break;
+ case Token.PROPERTY_BLOCK:
+ applySourceMapRecursively(token[1], inputSourceMapTracker);
+ break;
+ case Token.PROPERTY_NAME:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.PROPERTY_VALUE:
+ applySourceMapTo(token, inputSourceMapTracker);
+ break;
+ case Token.RULE:
+ applySourceMapRecursively(token[1], inputSourceMapTracker);
+ applySourceMapRecursively(token[2], inputSourceMapTracker);
+ break;
+ case Token.RULE_SCOPE:
+ applySourceMapTo(token, inputSourceMapTracker);
+ }
+ }
+
+ return tokens;
+}
+
+function applySourceMapTo(token, inputSourceMapTracker) {
+ var value = token[1];
+ var metadata = token[2];
+ var newMetadata = [];
+ var i, l;
+
+ for (i = 0, l = metadata.length; i < l; i++) {
+ newMetadata.push(inputSourceMapTracker.originalPositionFor(metadata[i], value.length));
+ }
+
+ token[2] = newMetadata;
+}
+
+module.exports = applySourceMaps;
--- /dev/null
+var split = require('../utils/split');
+
+var BRACE_PREFIX = /^\(/;
+var BRACE_SUFFIX = /\)$/;
+var IMPORT_PREFIX_PATTERN = /^@import/i;
+var QUOTE_PREFIX_PATTERN = /['"]\s*/;
+var QUOTE_SUFFIX_PATTERN = /\s*['"]/;
+var URL_PREFIX_PATTERN = /^url\(\s*/i;
+var URL_SUFFIX_PATTERN = /\s*\)/i;
+
+function extractImportUrlAndMedia(atRuleValue) {
+ var uri;
+ var mediaQuery;
+ var stripped;
+ var parts;
+
+ stripped = atRuleValue
+ .replace(IMPORT_PREFIX_PATTERN, '')
+ .trim()
+ .replace(URL_PREFIX_PATTERN, '(')
+ .replace(URL_SUFFIX_PATTERN, ')')
+ .replace(QUOTE_PREFIX_PATTERN, '')
+ .replace(QUOTE_SUFFIX_PATTERN, '');
+
+ parts = split(stripped, ' ');
+
+ uri = parts[0]
+ .replace(BRACE_PREFIX, '')
+ .replace(BRACE_SUFFIX, '');
+ mediaQuery = parts.slice(1).join(' ');
+
+ return [uri, mediaQuery];
+}
+
+module.exports = extractImportUrlAndMedia;
--- /dev/null
+var NO_PROTOCOL_RESOURCE_PATTERN = /^\/\//;
+
+function hasProtocol(uri) {
+ return !NO_PROTOCOL_RESOURCE_PATTERN.test(uri);
+}
+
+module.exports = hasProtocol;
+++ /dev/null
-var SourceMapConsumer = require('source-map').SourceMapConsumer;
-
-function inputSourceMapTracker() {
- var maps = {};
-
- return {
- originalPositionFor: originalPositionFor.bind(null, maps),
- track: track.bind(null, maps)
- };
-}
-
-function originalPositionFor(maps, metadata) {
- var line = metadata[0];
- var column = metadata[1];
- var source = metadata[2];
-
- return source in maps ?
- toMetadata(maps[source].originalPositionFor({ line: line, column: column })) :
- metadata;
-}
-
-function toMetadata(asHash) {
- return [asHash.line, asHash.column, asHash.source];
-}
-
-function track(maps, source, data) {
- maps[source] = new SourceMapConsumer(data);
-}
-
-module.exports = inputSourceMapTracker;
var SourceMapConsumer = require('source-map').SourceMapConsumer;
-var fs = require('fs');
-var path = require('path');
-var http = require('http');
-var https = require('https');
-var url = require('url');
-
-var override = require('../utils/object.js').override;
-
-var MAP_MARKER = /\/\*# sourceMappingURL=(\S+) \*\//;
-var REMOTE_RESOURCE = /^(https?:)?\/\//;
-var DATA_URI = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/;
-
-var unescape = global.unescape;
-
-function InputSourceMapStore(outerContext) {
- this.options = outerContext.options;
- this.errors = outerContext.errors;
- this.warnings = outerContext.warnings;
- this.sourceTracker = outerContext.sourceTracker;
- this.timeout = this.options.inliner.timeout;
- this.requestOptions = this.options.inliner.request;
- this.localOnly = outerContext.localOnly;
- this.relativeTo = outerContext.options.target || process.cwd();
-
- this.maps = {};
- this.sourcesContent = {};
-}
-
-function fromString(self, _, whenDone) {
- self.trackLoaded(undefined, undefined, self.options.sourceMap);
- return whenDone();
-}
-
-function fromSource(self, data, whenDone, context) {
- var nextAt = 0;
-
- function proceedToNext() {
- context.cursor += nextAt + 1;
- fromSource(self, data, whenDone, context);
- }
-
- while (context.cursor < data.length) {
- var fragment = data.substring(context.cursor);
-
- var markerStartMatch = self.sourceTracker.nextStart(fragment) || { index: -1 };
- var markerEndMatch = self.sourceTracker.nextEnd(fragment) || { index: -1 };
- var mapMatch = MAP_MARKER.exec(fragment) || { index: -1 };
- var sourceMapFile = mapMatch[1];
-
- nextAt = data.length;
- if (markerStartMatch.index > -1)
- nextAt = markerStartMatch.index;
- if (markerEndMatch.index > -1 && markerEndMatch.index < nextAt)
- nextAt = markerEndMatch.index;
- if (mapMatch.index > -1 && mapMatch.index < nextAt)
- nextAt = mapMatch.index;
-
- if (nextAt == data.length)
- break;
-
- if (nextAt == markerStartMatch.index) {
- context.files.push(markerStartMatch.filename);
- } else if (nextAt == markerEndMatch.index) {
- context.files.pop();
- } else if (nextAt == mapMatch.index) {
- var isRemote = /^https?:\/\//.test(sourceMapFile) || /^\/\//.test(sourceMapFile);
- var isDataUri = DATA_URI.test(sourceMapFile);
-
- if (isRemote) {
- return fetchMapFile(self, sourceMapFile, context, proceedToNext);
- } else {
- var sourceFile = context.files[context.files.length - 1];
- var sourceMapPath, sourceMapData;
- var sourceDir = sourceFile ? path.dirname(sourceFile) : self.options.relativeTo;
-
- if (isDataUri) {
- // source map's path is the same as the source file it comes from
- sourceMapPath = path.resolve(self.options.root, sourceFile || '');
- sourceMapData = fromDataUri(sourceMapFile);
- } else {
- sourceMapPath = path.resolve(self.options.root, path.join(sourceDir || '', sourceMapFile));
- sourceMapData = fs.readFileSync(sourceMapPath, 'utf-8');
- }
- self.trackLoaded(sourceFile || undefined, sourceMapPath, sourceMapData);
- }
- }
-
- context.cursor += nextAt + 1;
- }
-
- return whenDone();
-}
-
-function fromDataUri(uriString) {
- var match = DATA_URI.exec(uriString);
- var charset = match[2] ? match[2].split(/[=;]/)[2] : 'us-ascii';
- var encoding = match[3] ? match[3].split(';')[1] : 'utf8';
- var data = encoding == 'utf8' ? unescape(match[4]) : match[4];
-
- var buffer = new Buffer(data, encoding);
- buffer.charset = charset;
-
- return buffer.toString();
+function inputSourceMapTracker() {
+ var maps = {};
+
+ return {
+ all: all.bind(null, maps),
+ isTracking: isTracking.bind(null, maps),
+ originalPositionFor: originalPositionFor.bind(null, maps),
+ track: track.bind(null, maps)
+ };
}
-function fetchMapFile(self, sourceUrl, context, done) {
- fetch(self, sourceUrl, function (data) {
- self.trackLoaded(context.files[context.files.length - 1] || undefined, sourceUrl, data);
- done();
- }, function (message) {
- context.errors.push('Broken source map at "' + sourceUrl + '" - ' + message);
- return done();
- });
+function all(maps) {
+ return maps;
}
-function fetch(self, path, onSuccess, onFailure) {
- var protocol = path.indexOf('https') === 0 ? https : http;
- var requestOptions = override(url.parse(path), self.requestOptions);
- var errorHandled = false;
-
- protocol
- .get(requestOptions, function (res) {
- if (res.statusCode < 200 || res.statusCode > 299)
- return onFailure(res.statusCode);
-
- var chunks = [];
- res.on('data', function (chunk) {
- chunks.push(chunk.toString());
- });
- res.on('end', function () {
- onSuccess(chunks.join(''));
- });
- })
- .on('error', function (res) {
- if (errorHandled)
- return;
-
- onFailure(res.message);
- errorHandled = true;
- })
- .on('timeout', function () {
- if (errorHandled)
- return;
-
- onFailure('timeout');
- errorHandled = true;
- })
- .setTimeout(self.timeout);
+function isTracking(maps, source) {
+ return source in maps;
}
-function originalPositionIn(trackedSource, line, column, token, allowNFallbacks) {
- var originalPosition;
- var maxRange = token.length;
+function originalPositionFor(maps, metadata, range, selectorFallbacks) {
+ var line = metadata[0];
+ var column = metadata[1];
+ var source = metadata[2];
var position = {
line: line,
- column: column + maxRange
+ column: column + range
};
+ var originalPosition;
- while (maxRange-- > 0) {
+ while (!originalPosition && position.column > column) {
position.column--;
- originalPosition = trackedSource.data.originalPositionFor(position);
-
- if (originalPosition)
- break;
+ originalPosition = maps[source].originalPositionFor(position);
}
- if (originalPosition.line === null && line > 1 && allowNFallbacks > 0)
- return originalPositionIn(trackedSource, line - 1, column, token, allowNFallbacks - 1);
-
- if (trackedSource.path && originalPosition.source) {
- originalPosition.source = REMOTE_RESOURCE.test(trackedSource.path) ?
- url.resolve(trackedSource.path, originalPosition.source) :
- path.join(trackedSource.path, originalPosition.source);
-
- originalPosition.sourceResolved = true;
+ if (originalPosition.line === null && line > 1 && selectorFallbacks > 0) {
+ return originalPositionFor(maps, [line - 1, column, source], range, selectorFallbacks - 1);
}
- return originalPosition;
+ return originalPosition.line !== null ?
+ toMetadata(originalPosition) :
+ metadata;
}
-function trackContentSources(self, sourceFile) {
- var consumer = self.maps[sourceFile].data;
- var isRemote = REMOTE_RESOURCE.test(sourceFile);
- var sourcesMapping = {};
-
- consumer.sources.forEach(function (file, index) {
- var uniquePath = isRemote ?
- url.resolve(path.dirname(sourceFile), file) :
- path.relative(self.relativeTo, path.resolve(path.dirname(sourceFile || '.'), file));
-
- sourcesMapping[uniquePath] = consumer.sourcesContent && consumer.sourcesContent[index];
- });
- self.sourcesContent[sourceFile] = sourcesMapping;
+function toMetadata(asHash) {
+ return [asHash.line, asHash.column, asHash.source];
}
-function _resolveSources(self, remaining, whenDone) {
- function processNext() {
- return _resolveSources(self, remaining, whenDone);
- }
-
- if (remaining.length === 0)
- return whenDone();
-
- var current = remaining.shift();
- var sourceFile = current[0];
- var originalFile = current[1];
- var isRemote = REMOTE_RESOURCE.test(sourceFile);
-
- if (isRemote && self.localOnly) {
- self.warnings.push('No callback given to `#minify` method, cannot fetch a remote file from "' + originalFile + '"');
- return processNext();
- }
-
- if (isRemote) {
- fetch(self, originalFile, function (data) {
- self.sourcesContent[sourceFile][originalFile] = data;
- processNext();
- }, function (message) {
- self.warnings.push('Broken original source file at "' + originalFile + '" - ' + message);
- processNext();
- });
- } else {
- var fullPath = path.join(self.options.root, originalFile);
- if (fs.existsSync(fullPath))
- self.sourcesContent[sourceFile][originalFile] = fs.readFileSync(fullPath, 'utf-8');
- else
- self.warnings.push('Missing original source file at "' + fullPath + '".');
- return processNext();
- }
+function track(maps, source, data) {
+ maps[source] = new SourceMapConsumer(data);
}
-InputSourceMapStore.prototype.track = function (data, whenDone) {
- return typeof this.options.sourceMap == 'string' ?
- fromString(this, data, whenDone) :
- fromSource(this, data, whenDone, { files: [], cursor: 0, errors: this.errors });
-};
-
-InputSourceMapStore.prototype.trackLoaded = function (sourcePath, mapPath, mapData) {
- var relativeTo = this.options.explicitTarget ? this.options.target : this.options.root;
- var isRemote = REMOTE_RESOURCE.test(sourcePath);
-
- if (mapPath) {
- mapPath = isRemote ?
- path.dirname(mapPath) :
- path.dirname(path.relative(relativeTo, mapPath));
- }
-
- this.maps[sourcePath] = {
- path: mapPath,
- data: new SourceMapConsumer(mapData)
- };
-
- trackContentSources(this, sourcePath);
-};
-
-InputSourceMapStore.prototype.isTracking = function (source) {
- return !!this.maps[source];
-};
-
-InputSourceMapStore.prototype.originalPositionFor = function (sourceInfo, token, allowNFallbacks) {
- return originalPositionIn(this.maps[sourceInfo.source], sourceInfo.line, sourceInfo.column, token, allowNFallbacks);
-};
-
-InputSourceMapStore.prototype.sourcesContentFor = function (contextSource) {
- return this.sourcesContent[contextSource];
-};
-
-InputSourceMapStore.prototype.resolveSources = function (whenDone) {
- var toResolve = [];
-
- for (var sourceFile in this.sourcesContent) {
- var contents = this.sourcesContent[sourceFile];
- for (var originalFile in contents) {
- if (!contents[originalFile])
- toResolve.push([sourceFile, originalFile]);
- }
- }
-
- return _resolveSources(this, toResolve, whenDone);
-};
-
-module.exports = InputSourceMapStore;
+module.exports = inputSourceMapTracker;
--- /dev/null
+var isRemoteResource = require('./is-remote-resource');
+
+function isAbsoluteResource(uri) {
+ return !isRemoteResource(uri) && uri[0] == '/';
+}
+
+module.exports = isAbsoluteResource;
--- /dev/null
+var url = require('url');
+
+var hasProtocol = require('./has-protocol');
+
+var HTTP_PROTOCOL = 'http:';
+
+function isAllowedResource(uri, isRemote, rules) {
+ var match;
+ var allowed = true;
+ var rule;
+ var i;
+
+ if (rules.length === 0) {
+ return false;
+ }
+
+ if (isRemote && !hasProtocol(uri)) {
+ uri = HTTP_PROTOCOL + uri;
+ }
+
+ match = isRemote ?
+ url.parse(uri).host :
+ uri;
+
+ for (i = 0; i < rules.length; i++) {
+ rule = rules[i];
+
+ if (rule == 'all') {
+ allowed = true;
+ } else if (isRemote && rule == 'local') {
+ allowed = false;
+ } else if (isRemote && rule == 'remote') {
+ allowed = true;
+ } else if (!isRemote && rule == 'remote') {
+ allowed = false;
+ } else if (!isRemote && rule == 'local') {
+ allowed = true;
+ } else if (rule[0] == '!' && rule.substring(1) === match) {
+ allowed = false;
+ }
+ }
+
+ return allowed;
+}
+
+module.exports = isAllowedResource;
--- /dev/null
+var DATA_URI_PATTERN = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/;
+
+function isDataUriResource(uri) {
+ return DATA_URI_PATTERN.test(uri);
+}
+
+module.exports = isDataUriResource;
--- /dev/null
+var HTTP_RESOURCE_PATTERN = /^http:\/\//;
+
+function isHttpResource(uri) {
+ return HTTP_RESOURCE_PATTERN.test(uri);
+}
+
+module.exports = isHttpResource;
--- /dev/null
+var HTTPS_RESOURCE_PATTERN = /^https:\/\//;
+
+function isHttpsResource(uri) {
+ return HTTPS_RESOURCE_PATTERN.test(uri);
+}
+
+module.exports = isHttpsResource;
--- /dev/null
+var IMPORT_PREFIX_PATTERN = /^@import/i;
+
+function isImport(value) {
+ return IMPORT_PREFIX_PATTERN.test(value);
+}
+
+module.exports = isImport;
--- /dev/null
+var REMOTE_RESOURCE_PATTERN = /^(\w+:\/\/|\/\/)/;
+
+function isRemoteResource(uri) {
+ return REMOTE_RESOURCE_PATTERN.test(uri);
+}
+
+module.exports = isRemoteResource;
--- /dev/null
+var fs = require('fs');
+var path = require('path');
+
+var isAllowedResource = require('./is-allowed-resource');
+var isRemoteResource = require('./is-remote-resource');
+var loadRemoteResource = require('./load-remote-resource');
+
+function loadOriginalSources(context, callback) {
+ var loadContext = {
+ callback: callback,
+ index: 0,
+ inliner: context.options.inliner,
+ localOnly: context.localOnly,
+ processImportFrom: context.options.processImportFrom,
+ rebaseTo: context.options.rebaseTo,
+ sourcesContent: context.sourcesContent,
+ uriToSource: uriToSourceMapping(context.inputSourceMapTracker.all()),
+ warnings: context.warnings
+ };
+
+ return doLoadOriginalSources(loadContext);
+}
+
+function uriToSourceMapping(allSourceMapConsumers) {
+ var mapping = {};
+ var consumer;
+ var uri;
+ var source;
+ var i, l;
+
+ for (source in allSourceMapConsumers) {
+ consumer = allSourceMapConsumers[source];
+
+ for (i = 0, l = consumer.sources.length; i < l; i++) {
+ uri = consumer.sources[i];
+ source = consumer.sourceContentFor(uri, true);
+
+ mapping[uri] = source;
+ }
+ }
+
+ return mapping;
+}
+
+function doLoadOriginalSources(loadContext) {
+ var uris = Object.keys(loadContext.uriToSource);
+ var uri;
+ var source;
+ var total;
+
+ for (total = uris.length; loadContext.index < total; loadContext.index++) {
+ uri = uris[loadContext.index];
+ source = loadContext.uriToSource[uri];
+
+ if (source) {
+ loadContext.sourcesContent[uri] = source;
+ } else {
+ return loadOriginalSource(uri, loadContext);
+ }
+ }
+
+ return loadContext.callback();
+}
+
+function loadOriginalSource(uri, loadContext) {
+ var content;
+
+ if (isRemoteResource(uri)) {
+ return loadOriginalSourceFromRemoteUri(uri, loadContext, function (content) {
+ loadContext.index++;
+ loadContext.sourcesContent[uri] = content;
+ return doLoadOriginalSources(loadContext);
+ });
+ } else {
+ content = loadOriginalSourceFromLocalUri(uri, loadContext);
+ loadContext.index++;
+ loadContext.sourcesContent[uri] = content;
+ return doLoadOriginalSources(loadContext);
+ }
+}
+
+function loadOriginalSourceFromRemoteUri(uri, loadContext, whenLoaded) {
+ var isAllowed = isAllowedResource(uri, true, loadContext.processImportFrom);
+
+ if (loadContext.localOnly) {
+ loadContext.warnings.push('Cannot fetch remote resource from "' + uri + '" as no callback given.');
+ return whenLoaded(null);
+ } else if (!isAllowed) {
+ loadContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.');
+ return whenLoaded(null);
+ }
+
+ loadRemoteResource(uri, loadContext.inliner, function (error, content) {
+ if (error) {
+ loadContext.warnings.push('Missing original source at "' + uri + '" - ' + error);
+ }
+
+ whenLoaded(content);
+ });
+}
+
+function loadOriginalSourceFromLocalUri(uri, loadContext) {
+ var isAllowed = isAllowedResource(uri, true, loadContext.processImportFrom);
+ var resolvedUri = path.resolve(loadContext.rebaseTo, uri);
+
+ if (!fs.existsSync(resolvedUri) || !fs.statSync(resolvedUri).isFile()) {
+ loadContext.warnings.push('Ignoring local source map at "' + resolvedUri + '" as resource is missing.');
+ return null;
+ } else if (!isAllowed) {
+ loadContext.warnings.push('Cannot fetch "' + resolvedUri + '" as resource is not allowed.');
+ return null;
+ }
+
+ return fs.readFileSync(resolvedUri, 'utf8');
+}
+
+module.exports = loadOriginalSources;
--- /dev/null
+var http = require('http');
+var https = require('https');
+var url = require('url');
+
+var hasProtocol = require('./has-protocol');
+var isHttpResource = require('./is-http-resource');
+var isHttpsResource = require('./is-https-resource');
+var override = require('./override');
+
+var HTTP_PROTOCOL = 'http:';
+
+function loadRemoteResource(uri, inlinerOptions, callback) {
+ var proxyProtocol = inlinerOptions.request.protocol || inlinerOptions.request.hostname;
+ var errorHandled = false;
+ var requestOptions;
+ var fetch;
+
+ if (!hasProtocol(uri)) {
+ uri = 'http:' + uri;
+ }
+
+ requestOptions = override(
+ url.parse(uri),
+ inlinerOptions.request || {}
+ );
+
+ if (inlinerOptions.request.hostname !== undefined) {
+ // overwrite as we always expect a http proxy currently
+ requestOptions.protocol = inlinerOptions.request.protocol || HTTP_PROTOCOL;
+ requestOptions.path = requestOptions.href;
+ }
+
+ fetch = (proxyProtocol && !isHttpsResource(proxyProtocol)) || isHttpResource(uri) ?
+ http.get :
+ https.get;
+
+ fetch(requestOptions, function (res) {
+ var chunks = [];
+ var movedUri;
+
+ if (res.statusCode < 200 || res.statusCode > 399) {
+ return callback(res.statusCode, null);
+ } else if (res.statusCode > 299) {
+ movedUri = url.resolve(uri, res.headers.location);
+ return loadRemoteResource(movedUri, inlinerOptions, callback);
+ }
+
+ res.on('data', function (chunk) {
+ chunks.push(chunk.toString());
+ });
+ res.on('end', function () {
+ var body = chunks.join('');
+ callback(null, body);
+ });
+ })
+ .on('error', function (res) {
+ if (errorHandled) {
+ return;
+ }
+
+ errorHandled = true;
+ callback(res.message, null);
+ })
+ .on('timeout', function () {
+ if (errorHandled) {
+ return;
+ }
+
+ errorHandled = true;
+ callback('timeout', null);
+ })
+ .setTimeout(inlinerOptions.timeout);
+}
+
+module.exports = loadRemoteResource;
--- /dev/null
+var DATA_URI_PATTERN = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/;
+
+function matchDataUri(uri) {
+ return DATA_URI_PATTERN.exec(uri);
+}
+
+module.exports = matchDataUri;
var fs = require('fs');
-var http = require('http');
-var https = require('https');
var path = require('path');
-var url = require('url');
+
+var applySourceMaps = require('./apply-source-maps');
+var extractImportUrlAndMedia = require('./extract-import-url-and-media');
+var isAbsoluteResource = require('./is-absolute-resource');
+var isAllowedResource = require('./is-allowed-resource');
+var isImport = require('./is-import');
+var isRemoteResource = require('./is-remote-resource');
+var loadOriginalSources = require('./load-original-sources');
+var loadRemoteResource = require('./load-remote-resource');
+var rebase = require('./rebase');
+var rebaseLocalMap = require('./rebase-local-map');
+var rebaseRemoteMap = require('./rebase-remote-map');
+var restoreImport = require('./restore-import');
var tokenize = require('../tokenizer/tokenize');
var Token = require('../tokenizer/token');
-var rewriteUrl = require('../urls/rewrite');
-
-var override = require('../utils/override');
-var split = require('../utils/split');
-
-var IMPORT_PREFIX_PATTERN = /^@import/i;
-var BRACE_PREFIX = /^\(/;
-var BRACE_SUFFIX = /\)$/;
-var QUOTE_PREFIX_PATTERN = /['"]\s*/;
-var QUOTE_SUFFIX_PATTERN = /\s*['"]/;
-var URL_PREFIX_PATTERN = /^url\(\s*/i;
-var URL_SUFFIX_PATTERN = /\s*\)/i;
-
-var HTTP_PROTOCOL = 'http:';
-var HTTP_RESOURCE_PATTERN = /^http:\/\//;
-var HTTPS_RESOURCE_PATTERN = /^https:\/\//;
-var NO_PROTOCOL_RESOURCE_PATTERN = /^\/\//;
-var REMOTE_RESOURCE_PATTERN = /^(https?:)?\/\//;
+var UNKNOWN_SOURCE = 'unknown-source';
function readSources(input, context, callback) {
+ return doReadSources(input, context, function (tokens) {
+ return applySourceMaps(tokens, context, function () {
+ return context.options.sourceMapInlineSources ?
+ loadOriginalSources(context, function () { return callback(tokens); }) :
+ callback(tokens);
+ });
+ });
+}
+
+function doReadSources(input, context, callback) {
if (typeof input == 'string') {
return fromString(input, context, {}, callback);
} else if (Buffer.isBuffer(input)) {
function fromString(input, context, parentInlinerContext, callback) {
var inputAsHash = {};
- inputAsHash[false] = {
- styles: input
+ inputAsHash[UNKNOWN_SOURCE] = {
+ styles: input,
+ sourceMap: (typeof context.options.sourceMap === 'string') ? context.options.sourceMap : null
};
return fromHash(inputAsHash, context, parentInlinerContext, callback);
}
function fromArray(input, context, parentInlinerContext, callback) {
+ var currentPath = path.resolve('');
var inputAsHash = input.reduce(function (accumulator, uri) {
- var absolutePath = uri[0] == '/' ?
+ var isRemoteUri = isRemoteResource(uri);
+ var absolutePath = uri[0] == '/' || isRemoteUri ?
uri :
path.resolve(uri);
+ var relativeToCurrentPath;
- if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
- context.errors.push('Ignoring "' + uri + '" as resource is missing.');
- } else {
+ if (isRemoteUri) {
accumulator[uri] = {
+ styles: restoreImport(uri, '') + ';'
+ };
+ } else if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
+ context.errors.push('Ignoring "' + absolutePath + '" as resource is missing.');
+ } else {
+ relativeToCurrentPath = path.relative(currentPath, absolutePath);
+ accumulator[relativeToCurrentPath] = {
styles: fs.readFileSync(absolutePath, 'utf-8')
};
}
function fromHash(input, context, parentInlinerContext, callback) {
var tokens = [];
+ var newTokens = [];
var sourcePath;
var source;
var rebaseFrom;
var rebaseTo;
+ var parsedMap;
+ var rebasedMap;
var rebaseConfig;
for (sourcePath in input) {
source = input[sourcePath];
- if (isRemote(sourcePath)) {
+ if (sourcePath !== UNKNOWN_SOURCE && isRemoteResource(sourcePath)) {
rebaseFrom = sourcePath;
rebaseTo = sourcePath;
- } else if (isAbsolute(sourcePath)) {
+ } else if (sourcePath !== UNKNOWN_SOURCE && isAbsoluteResource(sourcePath)) {
rebaseFrom = path.dirname(sourcePath);
rebaseTo = context.options.rebaseTo;
} else {
- rebaseFrom = sourcePath ?
+ rebaseFrom = sourcePath !== UNKNOWN_SOURCE ?
path.dirname(path.resolve(sourcePath)) :
- path.resolve(sourcePath);
+ path.resolve('');
rebaseTo = context.options.rebaseTo;
}
toBase: rebaseTo
};
- tokens = tokens.concat(
- rebase(
- tokenize(source.styles, context),
- context.options.rebase,
- context.validator,
- rebaseConfig
- )
- );
-
- context.stats.originalSize += source.styles.length;
- }
-
- return context.options.processImport ?
- inlineImports(tokens, context, parentInlinerContext, callback) :
- callback(tokens);
-}
-
-function isAbsolute(uri) {
- return uri && uri[0] == '/';
-}
-
-function rebase(tokens, rebaseAll, validator, rebaseConfig) {
- return rebaseAll ?
- rebaseEverything(tokens, validator, rebaseConfig) :
- rebaseAtRules(tokens, validator, rebaseConfig);
-}
-
-function rebaseEverything(tokens, validator, rebaseConfig) {
- var token;
- var i, l;
-
- for (i = 0, l = tokens.length; i < l; i++) {
- token = tokens[i];
-
- switch (token[0]) {
- case Token.AT_RULE:
- rebaseAtRule(token, validator, rebaseConfig);
- break;
- case Token.AT_RULE_BLOCK:
- //
- break;
- case Token.BLOCK:
- rebaseEverything(token[2], validator, rebaseConfig);
- break;
- case Token.PROPERTY:
- //
- break;
- case Token.RULE:
- rebaseProperties(token[2], validator, rebaseConfig);
- break;
+ if (source.sourceMap) {
+ parsedMap = JSON.parse(source.sourceMap);
+ rebasedMap = isRemoteResource(sourcePath) ?
+ rebaseRemoteMap(parsedMap, sourcePath) :
+ rebaseLocalMap(parsedMap, sourcePath, context.options.rebaseTo);
+ context.inputSourceMapTracker.track(sourcePath, rebasedMap);
}
- }
-
- return tokens;
-}
-
-function rebaseAtRules(tokens, validator, rebaseConfig) {
- var token;
- var i, l;
- for (i = 0, l = tokens.length; i < l; i++) {
- token = tokens[i];
+ context.source = sourcePath !== UNKNOWN_SOURCE ? sourcePath : undefined;
+ context.sourcesContent[context.source] = source.styles;
- switch (token[0]) {
- case Token.AT_RULE:
- rebaseAtRule(token, validator, rebaseConfig);
- break;
- }
- }
+ newTokens = tokenize(source.styles, context);
+ newTokens = rebase(newTokens, context.options.rebase, context.validator, rebaseConfig);
- return tokens;
-}
+ tokens = tokens.concat(newTokens);
-function rebaseAtRule(token, validator, rebaseConfig) {
- if (!IMPORT_PREFIX_PATTERN.test(token[1])) {
- return;
+ context.stats.originalSize += source.styles.length;
}
- var uriAndMediaQuery = extractUrlAndMedia(token[1]);
- var newUrl = rewriteUrl(uriAndMediaQuery[0], rebaseConfig);
- var mediaQuery = uriAndMediaQuery[1];
-
- token[1] = restoreImport(newUrl, mediaQuery);
-}
-
-function restoreImport(uri, mediaQuery) {
- return ('@import ' + uri + ' ' + mediaQuery).trim();
-}
-
-function rebaseProperties(properties, validator, rebaseConfig) {
- var property;
- var value;
- var i, l;
- var j, m;
-
- for (i = 0, l = properties.length; i < l; i++) {
- property = properties[i];
-
- for (j = 2 /* 0 is Token.PROPERTY, 1 is name */, m = property.length; j < m; j++) {
- value = property[j][1];
-
- if (validator.isValidUrl(value)) {
- property[j][1] = rewriteUrl(value, rebaseConfig);
- }
- }
- }
+ return context.options.processImport ?
+ inlineImports(tokens, context, parentInlinerContext, callback) :
+ callback(tokens);
}
function inlineImports(tokens, externalContext, parentInlinerContext, callback) {
var inlinerContext = {
afterContent: false,
callback: callback,
+ errors: externalContext.errors,
externalContext: externalContext,
imported: parentInlinerContext.imported || [],
+ inlinerOptions: externalContext.options.inliner,
isRemote: parentInlinerContext.isRemote || false,
+ localOnly: externalContext.localOnly,
outputTokens: [],
- sourceTokens: tokens
+ processImportFrom: externalContext.options.processImportFrom,
+ rebaseTo: externalContext.options.rebaseTo,
+ sourceTokens: tokens,
+ warnings: externalContext.warnings
};
return doInlineImports(inlinerContext);
for (i = 0, l = inlinerContext.sourceTokens.length; i < l; i++) {
token = inlinerContext.sourceTokens[i];
- if (token[0] == Token.AT_RULE && IMPORT_PREFIX_PATTERN.test(token[1])) {
+ if (token[0] == Token.AT_RULE && isImport(token[1])) {
inlinerContext.sourceTokens.splice(0, i);
return inlineStylesheet(token, inlinerContext);
} else if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) {
}
function inlineStylesheet(token, inlinerContext) {
- var uriAndMediaQuery = extractUrlAndMedia(token[1]);
+ var uriAndMediaQuery = extractImportUrlAndMedia(token[1]);
var uri = uriAndMediaQuery[0];
var mediaQuery = uriAndMediaQuery[1];
+ var metadata = token[2];
- return isRemote(uri) ?
- inlineRemoteStylesheet(uri, mediaQuery, inlinerContext) :
- inlineLocalStylesheet(uri, mediaQuery, inlinerContext);
-}
-
-function extractUrlAndMedia(atRuleValue) {
- var uri;
- var mediaQuery;
- var stripped;
- var parts;
-
- stripped = atRuleValue
- .replace(IMPORT_PREFIX_PATTERN, '')
- .trim()
- .replace(URL_PREFIX_PATTERN, '(')
- .replace(URL_SUFFIX_PATTERN, ')')
- .replace(QUOTE_PREFIX_PATTERN, '')
- .replace(QUOTE_SUFFIX_PATTERN, '');
-
- parts = split(stripped, ' ');
-
- uri = parts[0]
- .replace(BRACE_PREFIX, '')
- .replace(BRACE_SUFFIX, '');
- mediaQuery = parts.slice(1).join(' ');
-
- return [uri, mediaQuery];
-}
-
-function isRemote(uri) {
- return uri && REMOTE_RESOURCE_PATTERN.test(uri);
-}
-
-function allowedResource(uri, isRemote, rules) {
- var match;
- var allowed = true;
- var rule;
- var i;
-
- if (rules.length === 0) {
- return false;
- }
-
- if (isRemote && NO_PROTOCOL_RESOURCE_PATTERN.test(uri)) {
- uri = 'http:' + uri;
- }
-
- match = isRemote ?
- url.parse(uri).host :
- uri;
-
- for (i = 0; i < rules.length; i++) {
- rule = rules[i];
-
- if (rule == 'all') {
- allowed = true;
- } else if (isRemote && rule == 'local') {
- allowed = false;
- } else if (isRemote && rule == 'remote') {
- allowed = true;
- } else if (!isRemote && rule == 'remote') {
- allowed = false;
- } else if (!isRemote && rule == 'local') {
- allowed = true;
- } else if (rule[0] == '!' && rule.substring(1) === match) {
- allowed = false;
- }
- }
-
- return allowed;
+ return isRemoteResource(uri) ?
+ inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) :
+ inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext);
}
-function inlineRemoteStylesheet(uri, mediaQuery, inlinerContext) {
- var inliner = inlinerContext.externalContext.options.inliner;
- var errorHandled = false;
- var fetch;
- var isAllowed = allowedResource(uri, true, inlinerContext.externalContext.options.processImportFrom);
- var onError;
- var options;
+function inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) {
+ var isAllowed = isAllowedResource(uri, true, inlinerContext.processImportFrom);
var originalUri = uri;
- var proxyProtocol = inliner.request.protocol || inliner.request.hostname;
if (inlinerContext.imported.indexOf(uri) > -1) {
- inlinerContext.externalContext.warnings.push('Ignoring remote @import of "' + uri + '" as it has already been imported.');
+ inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as it has already been imported.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
- } else if (inlinerContext.externalContext.localOnly && inlinerContext.afterContent) {
- inlinerContext.externalContext.warnings.push('Ignoring remote @import of "' + uri + '" as no callback given and after other content.');
+ } else if (inlinerContext.localOnly && inlinerContext.afterContent) {
+ inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as no callback given and after other content.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
- } else if (inlinerContext.externalContext.localOnly) {
- inlinerContext.externalContext.warnings.push('Skipping remote @import of "' + uri + '" as no callback given.');
+ } else if (inlinerContext.localOnly) {
+ inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no callback given.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} else if (!isAllowed && inlinerContext.afterContent) {
- inlinerContext.externalContext.warnings.push('Ignoring remote @import of "' + uri + '" as resource not allowed and after other content.');
+ inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as resource is not allowed and after other content.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} else if (!isAllowed) {
- inlinerContext.externalContext.warnings.push('Skipping remote @import of "' + uri + '" as resource not allowed.');
+ inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as resource is not allowed.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
}
- if (NO_PROTOCOL_RESOURCE_PATTERN.test(uri)) {
- uri = 'http:' + uri;
- }
-
- fetch = (proxyProtocol && !HTTPS_RESOURCE_PATTERN.test(proxyProtocol)) || HTTP_RESOURCE_PATTERN.test(uri) ?
- http.get :
- https.get;
-
- options = override(url.parse(uri), inliner.request);
- if (inliner.request.hostname !== undefined) {
- // overwrite as we always expect a http proxy currently
- options.protocol = inliner.request.protocol || HTTP_PROTOCOL;
- options.path = options.href;
- }
-
- onError = function(message) {
- if (errorHandled)
- return;
-
- errorHandled = true;
- inlinerContext.externalContext.errors.push('Broken @import declaration of "' + uri + '" - ' + message);
-
- process.nextTick(function () {
- inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
- inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
- doInlineImports(inlinerContext);
- });
- };
-
inlinerContext.imported.push(uri);
- fetch(options, function (res) {
- var chunks = [];
- var movedUri;
+ loadRemoteResource(uri, inlinerContext.inlinerOptions, function (error, importedStyles) {
+ var sourceHash = {};
- if (res.statusCode < 200 || res.statusCode > 399) {
- return onError('error ' + res.statusCode);
- } else if (res.statusCode > 299) {
- movedUri = url.resolve(uri, res.headers.location);
- return inlineRemoteStylesheet(movedUri, mediaQuery, inlinerContext);
- }
+ if (error) {
+ inlinerContext.errors.push('Broken @import declaration of "' + uri + '" - ' + error);
- res.on('data', function (chunk) {
- chunks.push(chunk.toString());
- });
- res.on('end', function () {
- var importedStyles;
- var sourceHash = {};
+ return process.nextTick(function () {
+ inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
+ inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
+ doInlineImports(inlinerContext);
+ });
+ }
- importedStyles = chunks.join('');
- sourceHash[originalUri] = {
- styles: importedStyles
- };
+ sourceHash[originalUri] = {
+ styles: importedStyles
+ };
- inlinerContext.isRemote = true;
- fromHash(sourceHash, inlinerContext.externalContext, inlinerContext, function (importedTokens) {
- importedTokens = wrapInMedia(importedTokens, mediaQuery);
+ inlinerContext.isRemote = true;
+ fromHash(sourceHash, inlinerContext.externalContext, inlinerContext, function (importedTokens) {
+ importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
- inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
- inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
+ inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
+ inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
- doInlineImports(inlinerContext);
- });
+ doInlineImports(inlinerContext);
});
-
- })
- .on('error', function (res) {
- onError(res.message);
- })
- .on('timeout', function () {
- onError('timeout');
- })
- .setTimeout(inliner.timeout);
+ });
}
-function inlineLocalStylesheet(uri, mediaQuery, inlinerContext) {
- var resolvedPath = uri[0] == '/' ?
- path.resolve(path.resolve(''), uri.substring(1)) :
- path.resolve(inlinerContext.externalContext.options.rebaseTo, uri);
+function inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext) {
+ var currentPath = path.resolve('');
+ var relativeTo = uri[0] == '/' ?
+ currentPath :
+ path.resolve(inlinerContext.rebaseTo || '');
+ var absolutePath = uri[0] == '/' ?
+ path.resolve(relativeTo, uri.substring(1)) :
+ path.resolve(relativeTo, uri);
+ var relativeToCurrentPath = path.relative(currentPath, absolutePath);
var importedStyles;
var importedTokens;
- var isAllowed = allowedResource(uri, false, inlinerContext.externalContext.options.processImportFrom);
+ var isAllowed = isAllowedResource(uri, false, inlinerContext.processImportFrom);
var sourceHash = {};
- if (inlinerContext.imported.indexOf(resolvedPath) > -1) {
- inlinerContext.externalContext.warnings.push('Ignoring local @import of "' + uri + '" as it has already beeb imported.');
- } else if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isFile()) {
- inlinerContext.externalContext.errors.push('Ignoring local @import of "' + uri + '" as resource is missing.');
+ if (inlinerContext.imported.indexOf(absolutePath) > -1) {
+ inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as it has already been imported.');
+ } else if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
+ inlinerContext.errors.push('Ignoring local @import of "' + uri + '" as resource is missing.');
} else if (!isAllowed && inlinerContext.afterContent) {
- inlinerContext.externalContext.warnings.push('Ignoring local @import of "' + uri + '" as resource not allowed and after other content.');
+ inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as resource is not allowed and after other content.');
} else if (inlinerContext.afterContent) {
- inlinerContext.externalContext.warnings.push('Ignoring local @import of "' + uri + '" as after other content.');
+ inlinerContext.warnings.push('Ignoring local @import of "' + uri + '" as after other content.');
} else if (!isAllowed) {
- inlinerContext.externalContext.warnings.push('Skipping local @import of "' + uri + '" as resource not allowed.');
+ inlinerContext.warnings.push('Skipping local @import of "' + uri + '" as resource is not allowed.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
} else {
- importedStyles = fs.readFileSync(resolvedPath, 'utf-8');
- inlinerContext.imported.push(resolvedPath);
+ importedStyles = fs.readFileSync(absolutePath, 'utf-8');
+ inlinerContext.imported.push(absolutePath);
- sourceHash[resolvedPath] = {
+ sourceHash[relativeToCurrentPath] = {
styles: importedStyles
};
importedTokens = fromHash(sourceHash, inlinerContext.externalContext, inlinerContext, function (tokens) { return tokens; });
- importedTokens = wrapInMedia(importedTokens, mediaQuery);
+ importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
}
return doInlineImports(inlinerContext);
}
-function wrapInMedia(tokens, mediaQuery) {
+function wrapInMedia(tokens, mediaQuery, metadata) {
if (mediaQuery) {
- return [[Token.BLOCK, [[Token.BLOCK_SCOPE, '@media ' + mediaQuery]], tokens]];
+ return [[Token.BLOCK, [[Token.BLOCK_SCOPE, '@media ' + mediaQuery, metadata]], tokens]];
} else {
return tokens;
}
--- /dev/null
+var path = require('path');
+
+function rebaseLocalMap(sourceMap, sourceUri, rebaseTo) {
+ var currentPath = path.resolve('');
+ var absolutePath = path.resolve(currentPath, sourceUri);
+ var sourceDirectory = path.dirname(absolutePath);
+
+ sourceMap.sources = sourceMap.sources.map(function(source) {
+ return path.relative(rebaseTo, path.resolve(sourceDirectory, source));
+ });
+
+ return sourceMap;
+}
+
+module.exports = rebaseLocalMap;
--- /dev/null
+var path = require('path');
+var url = require('url');
+
+function rebaseRemoteMap(sourceMap, sourceUri) {
+ var sourceDirectory = path.dirname(sourceUri);
+
+ sourceMap.sources = sourceMap.sources.map(function(source) {
+ return url.resolve(sourceDirectory, source);
+ });
+
+ return sourceMap;
+}
+
+module.exports = rebaseRemoteMap;
--- /dev/null
+var extractImportUrlAndMedia = require('./extract-import-url-and-media');
+var isImport = require('./is-import');
+var restoreImport = require('./restore-import');
+
+var rewriteUrl = require('../urls/rewrite');
+var Token = require('../tokenizer/token');
+
+var SOURCE_MAP_COMMENT_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/;
+
+function rebase(tokens, rebaseAll, validator, rebaseConfig) {
+ return rebaseAll ?
+ rebaseEverything(tokens, validator, rebaseConfig) :
+ rebaseAtRules(tokens, validator, rebaseConfig);
+}
+
+function rebaseEverything(tokens, validator, rebaseConfig) {
+ var token;
+ var i, l;
+
+ for (i = 0, l = tokens.length; i < l; i++) {
+ token = tokens[i];
+
+ switch (token[0]) {
+ case Token.AT_RULE:
+ rebaseAtRule(token, validator, rebaseConfig);
+ break;
+ case Token.AT_RULE_BLOCK:
+ rebaseProperties(token[2], validator, rebaseConfig);
+ break;
+ case Token.BLOCK:
+ rebaseEverything(token[2], validator, rebaseConfig);
+ break;
+ case Token.COMMENT:
+ rebaseSourceMapComment(token, rebaseConfig);
+ break;
+ case Token.RULE:
+ rebaseProperties(token[2], validator, rebaseConfig);
+ break;
+ }
+ }
+
+ return tokens;
+}
+
+function rebaseAtRules(tokens, validator, rebaseConfig) {
+ var token;
+ var i, l;
+
+ for (i = 0, l = tokens.length; i < l; i++) {
+ token = tokens[i];
+
+ switch (token[0]) {
+ case Token.AT_RULE:
+ rebaseAtRule(token, validator, rebaseConfig);
+ break;
+ }
+ }
+
+ return tokens;
+}
+
+function rebaseAtRule(token, validator, rebaseConfig) {
+ if (!isImport(token[1])) {
+ return;
+ }
+
+ var uriAndMediaQuery = extractImportUrlAndMedia(token[1]);
+ var newUrl = rewriteUrl(uriAndMediaQuery[0], rebaseConfig);
+ var mediaQuery = uriAndMediaQuery[1];
+
+ token[1] = restoreImport(newUrl, mediaQuery);
+}
+
+function rebaseSourceMapComment(token, rebaseConfig) {
+ var matches = SOURCE_MAP_COMMENT_PATTERN.exec(token[1]);
+
+ if (matches && matches[1].indexOf('data:') === -1) {
+ token[1] = token[1].replace(matches[1], rewriteUrl(matches[1], rebaseConfig, true));
+ }
+}
+
+function rebaseProperties(properties, validator, rebaseConfig) {
+ var property;
+ var value;
+ var i, l;
+ var j, m;
+
+ for (i = 0, l = properties.length; i < l; i++) {
+ property = properties[i];
+
+ for (j = 2 /* 0 is Token.PROPERTY, 1 is name */, m = property.length; j < m; j++) {
+ value = property[j][1];
+
+ if (validator.isValidUrl(value)) {
+ property[j][1] = rewriteUrl(value, rebaseConfig);
+ }
+ }
+ }
+}
+
+module.exports = rebase;
--- /dev/null
+function restoreImport(uri, mediaQuery) {
+ return ('@import ' + uri + ' ' + mediaQuery).trim();
+}
+
+module.exports = restoreImport;
assert.equal(minified.styles, '@import url(test/fixtures/partials/one.css);@import url(test/fixtures/partials/extra/three.css);@import url(test/fixtures/partials/extra/four.css);.two{color:#fff}');
}
},
- 'off and rebase off': {
+ 'off - many files': {
'topic': function () {
- return new CleanCSS({ processImport: false, rebase: false }).minify(['./test/fixtures/partials/two.css']);
+ return new CleanCSS({ processImport: false }).minify(['./test/fixtures/partials/remote.css', './test/fixtures/partials-absolute/base.css']);
},
'should give right output': function (minified) {
- assert.equal(minified.styles, '@import url(test/fixtures/partials/one.css);@import url(test/fixtures/partials/extra/three.css);@import url(test/fixtures/partials/extra/four.css);.two{color:#fff}');
+ assert.equal(minified.styles, '@import url(http://jakubpawlowicz.com/styles.css);@import url(test/fixtures/partials-absolute/extra/sub.css);.base{margin:0}');
}
},
+ 'off - many files with content': {
+ 'topic': function () {
+ return new CleanCSS({ processImport: false }).minify(['./test/fixtures/partials/two.css', './test/fixtures/partials-absolute/base.css']);
+ },
+ 'should give right output': function (minified) {
+ assert.equal(minified.styles, '@import url(test/fixtures/partials/one.css);@import url(test/fixtures/partials/extra/three.css);@import url(test/fixtures/partials/extra/four.css);.two{color:#fff}.base{margin:0}');
+ }
+ }
}
},
'accepts a list of source files as hash': {
var vows = require('vows');
var assert = require('assert');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var extractProperties = require('../../lib/optimizer/extract-properties');
+function _tokenize(source) {
+ return tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker()
+ });
+}
+
vows.describe(extractProperties)
.addBatch({
'no properties': {
'topic': function () {
- return extractProperties(tokenize('a{}', {})[0]);
+ return extractProperties(_tokenize('a{}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, []);
},
'no valid properties': {
'topic': function () {
- return extractProperties(tokenize('a{:red}', {})[0]);
+ return extractProperties(_tokenize('a{:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, []);
},
'one property': {
'topic': function () {
- return extractProperties(tokenize('a{color:red}', {})[0]);
+ return extractProperties(_tokenize('a{color:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'one important property': {
'topic': function () {
- return extractProperties(tokenize('a{color:red!important}', {})[0]);
+ return extractProperties(_tokenize('a{color:red!important}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'one property - simple selector': {
'topic': function () {
- return extractProperties(tokenize('#one span{color:red}', {})[0]);
+ return extractProperties(_tokenize('#one span{color:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'one property - variable': {
'topic': function () {
- return extractProperties(tokenize('#one span{--color:red}', {})[0]);
+ return extractProperties(_tokenize('#one span{--color:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, []);
},
'one property - block variable': {
'topic': function () {
- return extractProperties(tokenize('#one span{--color:{color:red;display:block};}', {})[0]);
+ return extractProperties(_tokenize('#one span{--color:{color:red;display:block};}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, []);
},
'one property - complex selector': {
'topic': function () {
- return extractProperties(tokenize('.one{color:red}', {})[0]);
+ return extractProperties(_tokenize('.one{color:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'two properties': {
'topic': function () {
- return extractProperties(tokenize('a{color:red;display:block}', {})[0]);
+ return extractProperties(_tokenize('a{color:red;display:block}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'from @media': {
'topic': function () {
- return extractProperties(tokenize('@media{a{color:red;display:block}p{color:red}}', {})[0]);
+ return extractProperties(_tokenize('@media{a{color:red;display:block}p{color:red}}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
'name root special cases': {
'vendor prefix': {
'topic': function () {
- return extractProperties(tokenize('a{-moz-transform:none}', {})[0]);
+ return extractProperties(_tokenize('a{-moz-transform:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'list-style': {
'topic': function () {
- return extractProperties(tokenize('a{list-style:none}', {})[0]);
+ return extractProperties(_tokenize('a{list-style:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-radius': {
'topic': function () {
- return extractProperties(tokenize('a{border-top-left-radius:none}', {})[0]);
+ return extractProperties(_tokenize('a{border-top-left-radius:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'vendor prefixed border-radius': {
'topic': function () {
- return extractProperties(tokenize('a{-webkit-border-top-left-radius:none}', {})[0]);
+ return extractProperties(_tokenize('a{-webkit-border-top-left-radius:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-image-width': {
'topic': function () {
- return extractProperties(tokenize('a{border-image-width:2px}', {})[0]);
+ return extractProperties(_tokenize('a{border-image-width:2px}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-color': {
'topic': function () {
- return extractProperties(tokenize('a{border-color:red}', {})[0]);
+ return extractProperties(_tokenize('a{border-color:red}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-top-style': {
'topic': function () {
- return extractProperties(tokenize('a{border-top-style:none}', {})[0]);
+ return extractProperties(_tokenize('a{border-top-style:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-top': {
'topic': function () {
- return extractProperties(tokenize('a{border-top:none}', {})[0]);
+ return extractProperties(_tokenize('a{border-top:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'border-collapse': {
'topic': function () {
- return extractProperties(tokenize('a{border-collapse:collapse}', {})[0]);
+ return extractProperties(_tokenize('a{border-collapse:collapse}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
},
'text-shadow': {
'topic': function () {
- return extractProperties(tokenize('a{text-shadow:none}', {})[0]);
+ return extractProperties(_tokenize('a{text-shadow:none}')[0]);
},
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
var assert = require('assert');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var extractProperties = require('../../lib/optimizer/extract-properties');
var canReorder = require('../../lib/optimizer/reorderable').canReorder;
var canReorderSingle = require('../../lib/optimizer/reorderable').canReorderSingle;
function propertiesIn(source) {
- return extractProperties(tokenize(source, { options: {} })[0]);
+ return extractProperties(
+ tokenize(
+ source,
+ {
+ inputSourceMapTracker: inputSourceMapTracker(),
+ options: {}
+ }
+ )[0]
+ );
}
vows.describe(canReorder)
var optimize = require('../../lib/properties/optimizer');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
function _optimize(source) {
var tokens = tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
warnings: []
});
var optimize = require('../../lib/properties/optimizer');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var validator = new Validator(compat);
var tokens = tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
warnings: []
});
var optimize = require('../../lib/properties/optimizer');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
function _optimize(source, compat, aggressiveMerging) {
var tokens = tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
warnings: []
});
var optimize = require('../../lib/properties/optimizer');
var tokenize = require('../../lib/tokenizer/tokenize');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
function _optimize(source) {
var tokens = tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
warnings: []
});
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'background'],
+ ['property-name', 'background', [
+ [1, 2, undefined],
+ [1, 24, undefined],
+ [1, 56, undefined],
+ [1, 81, undefined],
+ [1, 105, undefined],
+ [1, 134, undefined],
+ [1, 155, undefined],
+ [1, 185, undefined]
+ ]],
['property-value', 'url(image.png)', [[1, 41, undefined]]],
['property-value', '#111', [[1, 19, undefined]]]
]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'background'],
+ ['property-name', 'background', [
+ [1, 2, undefined],
+ [1, 24, undefined],
+ [1, 56, undefined],
+ [1, 84, undefined],
+ [1, 108, undefined],
+ [1, 137, undefined],
+ [1, 158, undefined],
+ [1, 188, undefined]
+ ]],
['property-value', 'url(image.png)', [[1, 41, undefined]]],
['property-value', 'no-repeat', [[1, 74, undefined]]],
['property-value', '#111', [[1, 19, undefined]]]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'background'],
+ ['property-name', 'background', [
+ [1, 2, undefined],
+ [1, 34, undefined],
+ [1, 76, undefined],
+ [1, 111, undefined],
+ [1, 145, undefined],
+ [1, 184, undefined],
+ [1, 215, undefined],
+ [1, 255, undefined]
+ ]],
['property-value', 'url(image.png)', [[1, 51, undefined]]],
['property-value', '#111!important', [[1, 19, undefined]]]
]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-width'],
+ ['property-name', 'border-width', [
+ [1, 2, undefined],
+ [1, 23, undefined],
+ [1, 47, undefined],
+ [1, 69, undefined]
+ ]],
['property-value', '7px', [[1, 19, undefined]]],
['property-value', '4px', [[1, 88, undefined]]]
]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-color'],
+ ['property-name', 'border-color', [
+ [1, 2, undefined],
+ [1, 27, undefined],
+ [1, 55, undefined],
+ [1, 81, undefined]
+ ]],
['property-value', '#9fce00', [[1, 19, undefined]]]
]
]);
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-color'],
+ ['property-name', 'border-color', [
+ [1, 2, undefined],
+ [1, 26, undefined],
+ [1, 51, undefined],
+ [1, 73, undefined]
+ ]],
['property-value', '#001', [[1, 68, undefined]]],
['property-value', '#002', [[1, 21, undefined]]],
['property-value', '#003', [[1, 46, undefined]]],
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-radius'],
+ ['property-name', 'border-radius', [
+ [1, 2, undefined],
+ [1, 29, undefined],
+ [1, 60, undefined],
+ [1, 90, undefined]
+ ]],
['property-value', '7px', [[1, 25, undefined]]],
['property-value', '3px', [[1, 114, undefined]]],
['property-value', '6px', [[1, 56, undefined]]],
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-radius'],
+ ['property-name', 'border-radius', [
+ [1, 2, undefined],
+ [1, 33, undefined],
+ [1, 65, undefined],
+ [1, 100, undefined]
+ ]],
['property-value', '7px', [[1, 25, undefined]]],
['property-value', '/'],
['property-value', '3px', [[1, 29, undefined]]]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'border-radius'],
+ ['property-name', 'border-radius', [
+ [1, 2, undefined],
+ [1, 33, undefined],
+ [1, 65, undefined],
+ [1, 100, undefined]
+ ]],
['property-value', '7px', [[1, 25, undefined]]],
['property-value', '6px', [[1, 57, undefined]]],
['property-value', '5px', [[1, 92, undefined]]],
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'list-style'],
+ ['property-name', 'list-style', [
+ [1, 2, undefined],
+ [1, 25, undefined],
+ [1, 53, undefined]
+ ]],
['property-value', 'circle', [[1, 18, undefined]]],
['property-value', 'url(image.png)', [[1, 70, undefined]]]
]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'list-style'],
+ ['property-name', 'list-style', [
+ [1, 2, undefined],
+ [1, 34, undefined],
+ [1, 57, undefined]
+ ]],
['property-value', 'circle', [[1, 50, undefined]]],
['property-value', 'inside', [[1, 77, undefined]]],
['property-value', 'url(image.png)', [[1, 19, undefined]]]
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'margin'],
+ ['property-name', 'margin', [
+ [1, 2, undefined],
+ [1, 18, undefined],
+ [1, 35, undefined],
+ [1, 53, undefined]
+ ]],
['property-value', '10px', [[1, 13, undefined]]],
['property-value', '5px', [[1, 31, undefined]]],
['property-value', '3px', [[1, 49, undefined]]],
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'padding'],
+ ['property-name', 'padding', [
+ [1, 2, undefined],
+ [1, 19, undefined],
+ [1, 36, undefined],
+ [1, 55, undefined]
+ ]],
['property-value', '10px', [[1, 14, undefined]]],
['property-value', '2px', [[1, 69, undefined]]],
['property-value', '3px', [[1, 51, undefined]]],
assert.deepEqual(properties, [
[
'property',
- ['property-name', 'padding'],
+ ['property-name', 'padding', [
+ [1, 2, undefined],
+ [1, 34, undefined],
+ [1, 67, undefined],
+ [1, 104, undefined]
+ ]],
['property-value', '10px', [[1, 14, undefined]]],
['property-value', '2px', [[1, 118, undefined]]],
['property-value', '3px', [[1, 82, undefined]]],
],
[
'property',
- ['property-name', 'margin'],
+ ['property-name', 'margin', [
+ [1, 19, undefined],
+ [1, 51, undefined],
+ [1, 86, undefined],
+ [1, 122, undefined]
+ ]],
['property-value', '3px', [[1, 30, undefined]]]
]
]);
],
[
'property',
- ['property-name', 'padding'],
+ ['property-name', 'padding', [
+ [1, 2, undefined],
+ [1, 19, undefined],
+ [1, 36, undefined],
+ [1, 65, undefined]
+ ]],
['property-value', '10px', [[1, 14, undefined]]],
['property-value', '2px', [[1, 79, undefined]]],
['property-value', '3px', [[1, 51, undefined]]],
},
'should not raise errors': function (errors, minified) {
assert.lengthOf(errors, 1);
- assert.equal(errors[0], 'Broken @import declaration of "http://127.0.0.1/missing.css" - error 404');
+ assert.equal(errors[0], 'Broken @import declaration of "http://127.0.0.1/missing.css" - 404');
},
'should process @import': function (errors, minified) {
assert.equal(minified.styles, '@import url(http://127.0.0.1/missing.css);p{font-size:13px}a{color:red}');
},
'should raise warnings': function (error, minified) {
assert.lengthOf(minified.warnings, 1);
- assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/skipped.css" as resource not allowed.');
+ assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/skipped.css" as resource is not allowed.');
},
'should keep imports': function (error, minified) {
assert.equal(minified.styles, '@import url(http://127.0.0.1/skipped.css);.one{color:red}');
},
'should raise warnings': function (error, minified) {
assert.lengthOf(minified.warnings, 2);
- assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/remote.css" as resource not allowed.');
- assert.equal(minified.warnings[1], 'Skipping remote @import of "http://assets.127.0.0.1/remote.css" as resource not allowed.');
+ assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/remote.css" as resource is not allowed.');
+ assert.equal(minified.warnings[1], 'Skipping remote @import of "http://assets.127.0.0.1/remote.css" as resource is not allowed.');
},
'should keeps imports': function (error, minified) {
assert.equal(minified.styles, '@import url(http://127.0.0.1/remote.css);@import url(http://assets.127.0.0.1/remote.css);.one{color:red}');
},
'should raise a warning': function (error, minified) {
assert.lengthOf(minified.warnings, 3);
- assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/remote.css" as resource not allowed.');
- assert.equal(minified.warnings[1], 'Skipping remote @import of "http://assets.127.0.0.1/remote.css" as resource not allowed.');
- assert.equal(minified.warnings[2], 'Skipping local @import of "test/fixtures/partials/one.css" as resource not allowed.');
+ assert.equal(minified.warnings[0], 'Skipping remote @import of "http://127.0.0.1/remote.css" as resource is not allowed.');
+ assert.equal(minified.warnings[1], 'Skipping remote @import of "http://assets.127.0.0.1/remote.css" as resource is not allowed.');
+ assert.equal(minified.warnings[2], 'Skipping local @import of "test/fixtures/partials/one.css" as resource is not allowed.');
},
'should process first imports': function (error, minified) {
assert.equal(minified.styles, '@import url(http://127.0.0.1/remote.css);@import url(http://assets.127.0.0.1/remote.css);@import url(test/fixtures/partials/one.css);');
},
'should raise a warning': function (error, minified) {
assert.lengthOf(minified.warnings, 1);
- assert.equal(minified.warnings[0], 'Skipping remote @import of "//127.0.0.1/remote.css" as resource not allowed.');
+ assert.equal(minified.warnings[0], 'Skipping remote @import of "//127.0.0.1/remote.css" as resource is not allowed.');
},
'should process first imports': function (error, minified) {
assert.equal(minified.styles, '@import url(//127.0.0.1/remote.css);.one{color:red}');
/* jshint unused: false */
-var vows = require('vows');
var assert = require('assert');
-var CleanCSS = require('../index');
-
var fs = require('fs');
+var http = require('http');
var path = require('path');
-var inputMapPath = path.join('test', 'fixtures', 'source-maps', 'styles.css.map');
-var inputMap = fs.readFileSync(inputMapPath, 'utf-8');
-var nock = require('nock');
-var http = require('http');
var enableDestroy = require('server-destroy');
-
+var nock = require('nock');
+var vows = require('vows');
var port = 24682;
+var CleanCSS = require('../index');
+
+var inputMapPath = path.join('test', 'fixtures', 'source-maps', 'styles.css.map');
+var inputMap = fs.readFileSync(inputMapPath, 'utf-8');
var lineBreak = require('os').EOL;
var escape = global.escape;
return new CleanCSS({ sourceMap: true }).minify('/*! a */div[data-id=" abc "] { color:red; }');
},
'has 3 mappings': function (minified) {
- assert.lengthOf(minified.sourceMap._mappings._array, 3);
+ assert.lengthOf(minified.sourceMap._mappings._array, 4);
+ },
+ 'has comment mapping': function (minified) {
+ var mapping = {
+ generatedLine: 1,
+ generatedColumn: 0,
+ originalLine: 1,
+ originalColumn: 0,
+ source: '$stdin',
+ name: null
+ };
+ assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
},
'has selector mapping': function (minified) {
var mapping = {
source: '$stdin',
name: null
};
- assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
+ assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
},
'has name mapping': function (minified) {
var mapping = {
source: '$stdin',
name: null
};
- assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
+ assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
},
'has value mapping': function (minified) {
var mapping = {
source: '$stdin',
name: null
};
- assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
+ assert.deepEqual(minified.sourceMap._mappings._array[3], mapping);
}
},
'module #2': {
generatedColumn: 0,
originalLine: 1,
originalColumn: 4,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 6,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 12,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
}
},
- 'input map from source with root': {
+ 'input map from source with rebaseTo': {
'topic': function () {
- return new CleanCSS({ sourceMap: true, root: './test/fixtures' }).minify('div > a {\n color: red;\n}/*# sourceMappingURL=source-maps/styles.css.map */');
+ return new CleanCSS({ sourceMap: true, rebaseTo: './test/fixtures' }).minify('div > a {\n color: red;\n}/*# sourceMappingURL=' + inputMapPath + ' */');
},
'has 3 mappings': function (minified) {
assert.lengthOf(minified.sourceMap._mappings._array, 3);
generatedColumn: 0,
originalLine: 1,
originalColumn: 4,
- source: 'source-maps/styles.less',
+ source: path.join('source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 6,
originalLine: 2,
originalColumn: 2,
- source: 'source-maps/styles.less',
+ source: path.join('source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 12,
originalLine: 2,
originalColumn: 2,
- source: 'source-maps/styles.less',
- name: null
- };
- assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
- }
- },
- 'input map from source with target': {
- 'topic': function () {
- return new CleanCSS({ sourceMap: true, target: './test' }).minify('div > a {\n color: red;\n}/*# sourceMappingURL=' + inputMapPath + ' */');
- },
- 'has 3 mappings': function (minified) {
- assert.lengthOf(minified.sourceMap._mappings._array, 3);
- },
- 'has `div > a` mapping': function (minified) {
- var mapping = {
- generatedLine: 1,
- generatedColumn: 0,
- originalLine: 1,
- originalColumn: 4,
- source: 'fixtures/source-maps/styles.less',
- name: null
- };
- assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
- },
- 'has `color` mapping': function (minified) {
- var mapping = {
- generatedLine: 1,
- generatedColumn: 6,
- originalLine: 2,
- originalColumn: 2,
- source: 'fixtures/source-maps/styles.less',
- name: null
- };
- assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
- },
- 'has `red` mapping': function (minified) {
- var mapping = {
- generatedLine: 1,
- generatedColumn: 12,
- originalLine: 2,
- originalColumn: 2,
- source: 'fixtures/source-maps/styles.less',
+ source: path.join('source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
'input map as inlined data URI with explicit charset us-ascii, not base64': inlineDataUriContext('data:application/json;charset=us-ascii,' + escape(inputMap)),
'complex input map': {
'topic': function () {
- return new CleanCSS({ sourceMap: true, root: path.dirname(inputMapPath) }).minify('@import url(import.css);');
+ return new CleanCSS({ sourceMap: true }).minify('@import url(' + path.dirname(inputMapPath) + '/import.css);');
},
'has 6 mappings': function (minified) {
assert.lengthOf(minified.sourceMap._mappings._array, 6);
generatedColumn: 0,
originalLine: 1,
originalColumn: 0,
- source: 'some.less',
+ source: path.join(path.dirname(inputMapPath), 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 4,
originalLine: 2,
originalColumn: 2,
- source: 'some.less',
+ source: path.join(path.dirname(inputMapPath), 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 10,
originalLine: 2,
originalColumn: 2,
- source: 'some.less',
+ source: path.join(path.dirname(inputMapPath), 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
generatedColumn: 14,
originalLine: 1,
originalColumn: 4,
- source: 'styles.less',
+ source: path.join(path.dirname(inputMapPath), 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[3], mapping);
generatedColumn: 20,
originalLine: 2,
originalColumn: 2,
- source: 'styles.less',
+ source: path.join(path.dirname(inputMapPath), 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[4], mapping);
generatedColumn: 26,
originalLine: 2,
originalColumn: 2,
- source: 'styles.less',
+ source: path.join(path.dirname(inputMapPath), 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[5], mapping);
}
},
- 'complex input map referenced by path': {
- 'topic': function () {
- return new CleanCSS({ sourceMap: true }).minify('@import url(test/fixtures/source-maps/import.css);');
- },
- 'has 6 mappings': function (minified) {
- assert.lengthOf(minified.sourceMap._mappings._array, 6);
- }
- },
'complex but partial input map referenced by path': {
'topic': function () {
return new CleanCSS({ sourceMap: true }).minify('@import url(test/fixtures/source-maps/no-map-import.css);');
},
'has 3 mappings to .less file': function (minified) {
var fromLess = minified.sourceMap._mappings._array.filter(function (mapping) {
- return mapping.source == 'test/fixtures/source-maps/styles.less';
+ return mapping.source == path.join('test', 'fixtures', 'source-maps', 'styles.less');
});
assert.lengthOf(fromLess, 3);
},
'has 3 mappings to .css file': function (minified) {
var fromCSS = minified.sourceMap._mappings._array.filter(function (mapping) {
- return mapping.source == 'test/fixtures/source-maps/no-map.css';
+ return mapping.source == path.join('test', 'fixtures', 'source-maps', 'no-map.css');
});
assert.lengthOf(fromCSS, 3);
}
},
- 'complex input map with an existing file as target': {
+ 'complex input map with an existing file as rebaseTo': {
'topic': function () {
- return new CleanCSS({ sourceMap: true, target: path.join('test', 'fixtures', 'source-maps', 'styles.css') }).minify('@import url(test/fixtures/source-maps/styles.css);');
+ return new CleanCSS({ sourceMap: true, rebaseTo: path.join('test', 'fixtures', 'source-maps') }).minify('@import url(test/fixtures/source-maps/styles.css);');
},
'has 3 mappings': function (minified) {
assert.lengthOf(minified.sourceMap._mappings._array, 3);
generatedColumn: 0,
originalLine: 2,
originalColumn: 8,
- source: 'test/fixtures/source-maps/nested/once.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/once.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 14,
originalLine: 3,
originalColumn: 4,
- source: 'test/fixtures/source-maps/nested/once.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/once.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 20,
originalLine: 3,
originalColumn: 4,
- source: 'test/fixtures/source-maps/nested/once.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/once.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
generatedColumn: 0,
originalLine: 3,
originalColumn: 4,
- source: 'test/fixtures/source-maps/nested/twice.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/twice.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 11,
originalLine: 4,
originalColumn: 6,
- source: 'test/fixtures/source-maps/nested/twice.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/twice.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 17,
originalLine: 4,
originalColumn: 6,
- source: 'test/fixtures/source-maps/nested/twice.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/twice.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
'has mapping': function (errors, minified) {
assert.isDefined(minified.sourceMap);
},
- 'raises an error': function (errors, _) {
- assert.lengthOf(errors, 1);
- assert.equal(errors[0], 'Broken source map at "http://127.0.0.1/remote.css.map" - 404');
+ 'raises no errors': function (errors, _) {
+ assert.isNull(errors);
+ },
+ 'raises a warning': function (_, minified) {
+ assert.lengthOf(minified.warnings, 1);
+ assert.equal(minified.warnings[0], 'Missing source map at "http://127.0.0.1/remote.css.map" - 404');
},
teardown: function () {
assert.isTrue(this.reqMocks.isDone());
'has mapping': function (errors, minified) {
assert.isDefined(minified.sourceMap);
},
- 'raises an error': function (errors, _) {
- assert.lengthOf(errors, 1);
- assert.include(errors[0], 'Broken source map at "http://127.0.0.1:' + port + '/remote.css.map"');
+ 'raises no errors': function (errors, _) {
+ assert.isNull(errors);
+ },
+ 'raises a warning': function (_, minified) {
+ assert.lengthOf(minified.warnings, 1);
+ assert.equal(minified.warnings[0], 'Missing source map at "http://127.0.0.1:24682/remote.css.map" - timeout');
},
teardown: function () {
this.server.destroy();
topic: function () {
this.reqMocks = nock('https://127.0.0.1')
.get('/remote.css')
- .reply(200, '/*# sourceMappingURL=https://127.0.0.1/remote.css.map */')
+ .reply(200, 'div>a{color:blue}/*# sourceMappingURL=https://127.0.0.1/remote.css.map */')
.get('/remote.css.map')
.reply(200, inputMap);
'has mapping': function (errors, minified) {
assert.isDefined(minified.sourceMap);
},
+ 'maps to external source file': function (errors, minified) {
+ assert.equal(minified.sourceMap._mappings._array[0].source, 'https://127.0.0.1/styles.less');
+ },
teardown: function () {
assert.isTrue(this.reqMocks.isDone());
nock.cleanAll();
topic: function () {
this.reqMocks = nock('http://127.0.0.1')
.get('/remote.css')
- .reply(200, '/*# sourceMappingURL=remote.css.map */')
+ .reply(200, 'div>a{color:blue}/*# sourceMappingURL=remote.css.map */')
.get('/remote.css.map')
.reply(200, inputMap);
'has mapping': function (errors, minified) {
assert.isDefined(minified.sourceMap);
},
+ 'maps to external source file': function (errors, minified) {
+ assert.equal(minified.sourceMap._mappings._array[0].source, 'http://127.0.0.1/styles.less');
+ },
teardown: function () {
assert.isTrue(this.reqMocks.isDone());
nock.cleanAll();
topic: function () {
this.reqMocks = nock('http://127.0.0.1')
.post('/remote.css')
- .reply(200, '/*# sourceMappingURL=remote.css.map */')
+ .reply(200, 'div>a{color:blue}/*# sourceMappingURL=remote.css.map */')
.post('/remote.css.map')
.reply(200, inputMap);
'has mapping': function (errors, minified) {
assert.isDefined(minified.sourceMap);
},
+ 'maps to external source file': function (errors, minified) {
+ assert.equal(minified.sourceMap._mappings._array[0].source, 'http://127.0.0.1/styles.less');
+ },
teardown: function () {
assert.isTrue(this.reqMocks.isDone());
nock.cleanAll();
return new CleanCSS({ sourceMap: true, keepSpecialComments: 1 }).minify('div { color: #f00 !important; /*!1*/} /*!2*/ a{/*!3*/}');
},
'has right output': function (errors, minified) {
- assert.equal(minified.styles, 'div{color:red!important/*!1*/}a{}');
+ assert.equal(minified.styles, 'div{color:red!important/*!1*/}');
}
}
})
generatedColumn: 0,
originalLine: 1,
originalColumn: 0,
- source: 'test/fixtures/source-maps/some.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
generatedColumn: 4,
originalLine: 2,
originalColumn: 8,
- source: 'test/fixtures/source-maps/nested/once.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'nested/once.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
generatedColumn: 18,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/some.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
generatedColumn: 24,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/some.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'some.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[3], mapping);
generatedColumn: 28,
originalLine: 1,
originalColumn: 4,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[4], mapping);
generatedColumn: 34,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[5], mapping);
generatedColumn: 40,
originalLine: 2,
originalColumn: 2,
- source: 'test/fixtures/source-maps/styles.less',
+ source: path.join('test', 'fixtures', 'source-maps', 'styles.less'),
name: null
};
assert.deepEqual(minified.sourceMap._mappings._array[6], mapping);
'relative to path': {
'complex but partial input map referenced by path': {
'topic': function () {
- return new CleanCSS({ sourceMap: true, target: './test' }).minify({
+ return new CleanCSS({ sourceMap: true, rebaseTo: './test' }).minify({
'test/fixtures/source-maps/some.css': {
styles: 'div {\n color: red;\n}',
sourceMap: '{"version":3,"sources":["some.less"],"names":[],"mappings":"AAAA;EACE,UAAA","file":"some.css"}'
});
assert.deepEqual(sources, [
- 'fixtures/source-maps/some.less',
- 'fixtures/source-maps/nested/once.less',
- 'fixtures/source-maps/styles.less'
+ path.join('fixtures', 'source-maps', 'some.less'),
+ path.join('fixtures', 'source-maps', 'nested', 'once.less'),
+ path.join('fixtures', 'source-maps', 'styles.less')
]);
}
}
'from array - off': {
'topic': function () {
return new CleanCSS({ sourceMap: true }).minify([
- 'test/fixtures/partials/one.css',
- 'test/fixtures/partials/three.css'
+ path.join('test', 'fixtures', 'partials', 'one.css'),
+ path.join('test', 'fixtures', 'partials', 'three.css')
]);
},
'has 6 mappings': function (minified) {
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'test/fixtures/partials/one.css',
- 'test/fixtures/partials/three.css'
+ path.join('test', 'fixtures', 'partials', 'one.css'),
+ path.join('test', 'fixtures', 'partials', 'three.css')
]);
},
'has embedded sources content': function (minified) {
'from array - on': {
'topic': function () {
return new CleanCSS({ sourceMap: true, sourceMapInlineSources: true }).minify([
- 'test/fixtures/partials/one.css',
- 'test/fixtures/partials/three.css'
+ path.join('test', 'fixtures', 'partials', 'one.css'),
+ path.join('test', 'fixtures', 'partials', 'three.css')
]);
},
'has 6 mappings': function (minified) {
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'test/fixtures/partials/one.css',
- 'test/fixtures/partials/three.css'
+ path.join('test', 'fixtures', 'partials', 'one.css'),
+ path.join('test', 'fixtures', 'partials', 'three.css')
]);
},
'has embedded sources content': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
- '.one { color:#f00; }' + lineBreak,
- '.three {background-image: url(test/fixtures/partials/extra/down.gif);}' + lineBreak
+ '.one { color:#f00; }\n',
+ '.three {background-image: url(extra/down.gif);}\n'
]);
}
},
},
'has embedded sources content': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
- 'div{background:url(http://127.0.0.1/image.png)}',
+ 'div{background:url(image.png)}',
]);
},
'teardown': function () {
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'test/fixtures/source-maps/some.less',
- 'test/fixtures/source-maps/nested/once.less',
- 'test/fixtures/source-maps/styles.less'
+ path.join('test', 'fixtures', 'source-maps', 'some.less'),
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
+ path.join('test', 'fixtures', 'source-maps', 'styles.less')
]);
},
'has embedded sources content': function (minified) {
]);
}
},
- 'multiple relative to a target path': {
+ 'multiple relative to rebaseTo path': {
'topic': function () {
- return new CleanCSS({ sourceMap: true, sourceMapInlineSources: true, target: path.join(process.cwd(), 'test') }).minify({
+ return new CleanCSS({ sourceMap: true, sourceMapInlineSources: true, rebaseTo: './test' }).minify({
'test/fixtures/source-maps/some.css': {
styles: 'div {\n color: red;\n}',
sourceMap: '{"version":3,"sources":["some.less"],"names":[],"mappings":"AAAA;EACE,UAAA","file":"some.css","sourcesContent":["div {\\n color: red;\\n}\\n"]}'
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'fixtures/source-maps/some.less',
- 'fixtures/source-maps/nested/once.less',
- 'fixtures/source-maps/styles.less'
+ path.join('fixtures', 'source-maps', 'some.less'),
+ path.join('fixtures', 'source-maps', 'nested', 'once.less'),
+ path.join('fixtures', 'source-maps', 'styles.less')
]);
},
'has embedded sources content': function (minified) {
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'test/fixtures/source-maps/some.less',
- 'test/fixtures/source-maps/nested/once.less',
- 'test/fixtures/source-maps/styles.less'
+ path.join('test', 'fixtures', 'source-maps', 'some.less'),
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
+ path.join('test', 'fixtures', 'source-maps', 'styles.less')
]);
},
'has embedded sources content': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
'div {\n color: red;\n}\n',
'section {\n > div a {\n color:red;\n }\n}\n',
- 'div > a {' + lineBreak + ' color: blue;' + lineBreak + '}' + lineBreak
+ 'div > a {\n color: blue;\n}\n'
]);
}
},
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
- 'test/fixtures/source-maps/some.less',
- 'test/fixtures/source-maps/nested/once.less',
- 'test/fixtures/source-maps/styles.less'
+ path.join('test', 'fixtures', 'source-maps', 'some.less'),
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
+ path.join('test', 'fixtures', 'source-maps', 'styles.less')
]);
},
'has embedded sources content': function (minified) {
}
}, this.callback);
},
- 'has 7 mappings': function (minified) {
+ 'has 7 mappings': function (errors, minified) {
assert.lengthOf(minified.sourceMap._mappings._array, 7);
},
- 'has embedded sources': function (minified) {
+ 'has embedded sources': function (errors, minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
'http://127.0.0.1/some.less',
- 'test/fixtures/source-maps/nested/once.less',
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
'http://127.0.0.1/styles.less'
]);
},
- 'has embedded sources content': function (minified) {
+ 'has embedded sources content': function (errors, minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
'div {\n color: red;\n}\n',
- 'section {' + lineBreak + ' > div a {' + lineBreak + ' color:red;' + lineBreak + ' }' + lineBreak + '}' + lineBreak,
+ 'section {\n > div a {\n color:red;\n }\n}\n',
'div > a {\n color: blue;\n}\n'
]);
},
assert.lengthOf(minified.sourceMap._mappings._array, 7);
},
'should warn about some.less': function (minified) {
- assert.deepEqual(minified.warnings, ['Broken original source file at "http://127.0.0.1/some.less" - 404']);
+ assert.deepEqual(minified.warnings, ['Missing original source at "http://127.0.0.1/some.less" - 404']);
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
'http://127.0.0.1/some.less',
- 'test/fixtures/source-maps/nested/once.less',
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
'http://127.0.0.1/styles.less'
]);
},
'has embedded sources content': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
null,
- 'section {' + lineBreak + ' > div a {' + lineBreak + ' color:red;' + lineBreak + ' }' + lineBreak + '}' + lineBreak,
+ 'section {\n > div a {\n color:red;\n }\n}\n',
'div > a {\n color: blue;\n}\n'
]);
},
},
'should warn about some.less and styles.less': function (minified) {
assert.deepEqual(minified.warnings, [
- 'No callback given to `#minify` method, cannot fetch a remote file from "http://127.0.0.1/some.less"',
- 'No callback given to `#minify` method, cannot fetch a remote file from "http://127.0.0.1/styles.less"'
+ 'Cannot fetch remote resource from "http://127.0.0.1/some.less" as no callback given.',
+ 'Cannot fetch remote resource from "http://127.0.0.1/styles.less" as no callback given.'
]);
},
'has embedded sources': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sources, [
'http://127.0.0.1/some.less',
- 'test/fixtures/source-maps/nested/once.less',
+ path.join('test', 'fixtures', 'source-maps', 'nested', 'once.less'),
'http://127.0.0.1/styles.less'
]);
},
'has embedded sources content': function (minified) {
assert.deepEqual(JSON.parse(minified.sourceMap.toString()).sourcesContent, [
null,
- 'section {' + lineBreak + ' > div a {' + lineBreak + ' color:red;' + lineBreak + ' }' + lineBreak + '}' + lineBreak,
+ 'section {\n > div a {\n color:red;\n }\n}\n',
null
]);
}
var vows = require('vows');
var assert = require('assert');
var tokenize = require('../../lib/tokenizer/tokenize');
-var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker-2');
+var inputSourceMapTracker = require('../../lib/utils/input-source-map-tracker');
var fs = require('fs');
var path = require('path');
function toTokens(source) {
return function () {
return tokenize(source, {
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
warnings: []
});
var warnings = [];
tokenize('a{display:block', {
- source: 'one.css',
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
+ source: 'one.css',
warnings: warnings
});
'sources - rule with properties': {
'topic': function () {
return tokenize('a{color:red}', {
- source: 'one.css',
+ inputSourceMapTracker: inputSourceMapTracker(),
options: {},
+ source: 'one.css',
warnings: []
});
},
.addBatch({
'input source maps - simple': {
'topic': function () {
- var sourceMapTracker = inputSourceMapTracker({
- errors: {}
- });
+ var sourceMapTracker = inputSourceMapTracker();
sourceMapTracker.track('styles.css', inputMap);
return tokenize('div > a {\n color: red;\n}', {
source: 'styles.css',
- inputSourceMap: true,
inputSourceMapTracker: sourceMapTracker,
options: {},
warnings: []
'rule-scope',
'div > a',
[
- [1, 0, 'styles.less']
+ [1, 4, 'styles.less']
]
]
],
},
'with fallback for properties': {
'topic': function () {
- var sourceMapTracker = inputSourceMapTracker({
- errors: {}
- });
+ var sourceMapTracker = inputSourceMapTracker();
sourceMapTracker.track('styles.css', inputMap);
return tokenize('div > a {\n color: red red;\n}', {
source: 'styles.css',
- inputSourceMap: true,
inputSourceMapTracker: sourceMapTracker,
options: {},
warnings: []
'rule-scope',
'div > a',
[
- [1, 0, 'styles.less']
+ [1, 4, 'styles.less']
]
]
],