var lineBreak = require('os').EOL;
-function SourceMapStringifier(options, restoreCallback, inputMapTracker) {
+function Rebuilder(options, restoreCallback, inputMapTracker) {
+ this.column = 0;
+ this.line = 1;
+ this.output = [];
this.keepBreaks = options.keepBreaks;
- this.restoreCallback = restoreCallback;
- this.outputMap = new SourceMapGenerator();
+ this.relativeTo = options.relativeTo;
+ this.restore = restoreCallback;
this.inputMapTracker = inputMapTracker;
+ this.outputMap = new SourceMapGenerator();
if (options.root) {
- this.resolvePath = rootPathResolver(options);
+ this.rebaseTo = path.resolve(options.root);
+ this.resolvePath = this.rootPathResolver;
} else if (options.target) {
- this.resolvePath = targetRelativePathResolver(options);
+ this.rebaseTo = path.dirname(path.resolve(process.cwd(), options.target));
+ this.resolvePath = this.relativePathResolver;
}
}
-function rootPathResolver(options) {
- var rootPath = path.resolve(options.root);
- return function (sourcePath) {
- return sourcePath.replace(rootPath, '');
- };
-}
+Rebuilder.prototype.rootPathResolver = function (sourcePath) {
+ return sourcePath.replace(this.rebaseTo, '');
+};
-function targetRelativePathResolver(options) {
- var relativeTo = path.dirname(path.resolve(process.cwd(), options.target));
- return function (sourcePath, sourceRelativeTo) {
- if (sourceRelativeTo)
- sourcePath = path.resolve(path.dirname(sourceRelativeTo), sourcePath);
+Rebuilder.prototype.relativePathResolver = function (sourcePath, sourceRelativeTo) {
+ if (sourceRelativeTo)
+ sourcePath = path.resolve(path.dirname(sourceRelativeTo), sourcePath);
- return path.normalize(sourcePath) === path.resolve(sourcePath) ?
- path.relative(relativeTo, sourcePath) :
- path.relative(relativeTo, path.join(options.relativeTo, sourcePath));
- };
-}
+ return path.normalize(sourcePath) === path.resolve(sourcePath) ?
+ path.relative(this.rebaseTo, sourcePath) :
+ path.relative(this.rebaseTo, path.join(this.relativeTo, sourcePath));
+};
-function valueRebuilder(list, store, separator) {
+Rebuilder.prototype.rebuildValue = function (list, separator) {
for (var i = 0, l = list.length; i < l; i++) {
- store(list[i]);
- store(i < l - 1 ? separator : '');
+ this.store(list[i]);
+ this.store(i < l - 1 ? separator : '');
}
-}
+};
+
+Rebuilder.prototype.store = function (token) {
+ var value = typeof token == 'string' ?
+ token :
+ token.value.indexOf('_') > -1 ? this.restore(token.value) : token.value;
+
+ this.track(value, token.metadata);
+ this.output.push(value);
+};
-function rebuild(tokens, store, keepBreaks, isFlatBlock) {
- var joinCharacter = isFlatBlock ? ';' : (keepBreaks ? lineBreak : '');
+Rebuilder.prototype.rebuildList = function (tokens, isFlatBlock) {
+ var joinCharacter = isFlatBlock ? ';' : (this.keepBreaks ? lineBreak : '');
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token.kind === 'text' || token.kind == 'at-rule') {
- store(token);
+ this.store(token);
continue;
}
if (token.kind == 'block') {
if (token.body.length > 0) {
- valueRebuilder([{ value: token.value, metadata: token.metadata }], store, '');
- store('{');
+ this.rebuildValue([{ value: token.value, metadata: token.metadata }], '');
+ this.store('{');
if (token.isFlatBlock)
- valueRebuilder(token.body, store, ';');
+ this.rebuildValue(token.body, ';');
else
- rebuild(token.body, store, keepBreaks, false);
- store('}');
+ this.rebuildList(token.body, false);
+ this.store('}');
}
} else {
- valueRebuilder(token.value, store, ',');
- store('{');
- valueRebuilder(token.body, store, ';');
- store('}');
+ this.rebuildValue(token.value, ',');
+ this.store('{');
+ this.rebuildValue(token.body, ';');
+ this.store('}');
}
- store(joinCharacter);
+ this.store(joinCharacter);
}
-}
+};
+
+Rebuilder.prototype.track = function (value, metadata) {
+ if (metadata)
+ this.trackMetadata(metadata);
-function track(context, value, metadata) {
- if (metadata) {
- var original = context.inputMapTracker.isTracking() ?
- context.inputMapTracker.originalPositionFor(metadata) :
- {};
- var source = original.source || metadata.source;
-
- if (source) {
- if (metadata.source && (/^https?:\/\//.test(metadata.source) || /^\/\//.test(metadata.source)) && source != metadata.source)
- source = url.resolve(metadata.source, source);
- else if (context.resolvePath)
- source = context.resolvePath(source, metadata.source);
+ var parts = value.split('\n');
+ this.line += parts.length - 1;
+ this.column = parts.length > 1 ? 0 : (this.column + parts.pop().length);
+};
+
+Rebuilder.prototype.trackMetadata = function (metadata) {
+ var original = this.inputMapTracker.isTracking() ?
+ this.inputMapTracker.originalPositionFor(metadata) :
+ {};
+
+ this.outputMap.addMapping({
+ generated: {
+ line: this.line,
+ column: this.column,
+ },
+ source: this.stylingSourceFor(original, metadata) || '__stdin__.css',
+ original: {
+ line: original.line || metadata.line,
+ column: original.column || metadata.column
}
+ });
+};
- context.outputMap.addMapping({
- generated: {
- line: context.line,
- column: context.column,
- },
- source: source || '__stdin__.css',
- original: {
- line: original.line || metadata.line,
- column: original.column || metadata.column
- }
- });
- }
+Rebuilder.prototype.stylingSourceFor = function (original, metadata) {
+ var source = original.source || metadata.source;
- var parts = value.split('\n');
- context.line += parts.length - 1;
- context.column = parts.length > 1 ? 0 : (context.column + parts.pop().length);
-}
+ if (source && metadata.source && (/^https?:\/\//.test(metadata.source) || /^\/\//.test(metadata.source)) && source != metadata.source)
+ return url.resolve(metadata.source, source);
+ else if (source && this.resolvePath)
+ return this.resolvePath(source, metadata.source);
+ else
+ return source;
+};
-SourceMapStringifier.prototype.toString = function (tokens) {
- var self = this;
- var output = [];
- var context = {
- column: 0,
- line: 1,
- inputMapTracker: this.inputMapTracker,
- outputMap: this.outputMap,
- resolvePath: this.resolvePath
- };
- function store(token) {
- if (typeof token == 'string') {
- track(context, token);
- output.push(token);
- } else {
- var val = token.value.indexOf('_') > -1 ?
- self.restoreCallback(token.value) :
- token.value;
- track(context, val, token.metadata);
- output.push(val);
- }
- }
+function SourceMapStringifier(options, restoreCallback, inputMapTracker) {
+ this.rebuilder = new Rebuilder(options, restoreCallback, inputMapTracker);
+}
- rebuild(tokens, store, this.keepBreaks, false);
+SourceMapStringifier.prototype.toString = function (tokens) {
+ this.rebuilder.rebuildList(tokens);
return {
- sourceMap: this.outputMap,
- styles: output.join('').trim()
+ sourceMap: this.rebuilder.outputMap,
+ styles: this.rebuilder.output.join('').trim()
};
};