From b54b50c9c902be626ae3a010dbf5c3574ecd2dfa Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Sat, 29 Nov 2014 23:47:12 +0000 Subject: [PATCH] Refactors source map stringifier in OO style. --- lib/selectors/source-map-stringifier.js | 181 ++++++++++++------------ 1 file changed, 89 insertions(+), 92 deletions(-) diff --git a/lib/selectors/source-map-stringifier.js b/lib/selectors/source-map-stringifier.js index 0e197f9e..e8b9ba7b 100644 --- a/lib/selectors/source-map-stringifier.js +++ b/lib/selectors/source-map-stringifier.js @@ -5,53 +5,62 @@ var SourceMapGenerator = require('source-map').SourceMapGenerator; 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; } @@ -61,86 +70,74 @@ function rebuild(tokens, store, keepBreaks, isFlatBlock) { 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() }; }; -- 2.34.1