diff --git a/README.md b/README.md index 226a4d8..ae4b7d8 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,15 @@ The syntax type of source string to preprocess. There are 3 main syntax variants `ts`, `tsx`, `peg`, `pegjs`, `jade`, `styl` - `coffee`, aliases: `bash`, `shell`, `sh` +##### options.supportSourceMaps +Type: `Boolean` +Default: `false` + +**Note:** Currently only the @if directive is supported. + +When set to `true`, the preprocessor will replace excluded lines with empty lines, by that maintaining correct line +mapping when sourcemaps are generated. + ### preprocessFile(srcFile, destFile[, context[, callback[, options]]]) Preprocesses a `sourceFile` and saves the result to `destFile`. Simple wrapper around `fs.readFile()` and `fs.writeFile()`. diff --git a/lib/preprocess.js b/lib/preprocess.js index e401d29..77c758a 100644 --- a/lib/preprocess.js +++ b/lib/preprocess.js @@ -82,6 +82,7 @@ function preprocess(src, context, typeOrOptions) { options.fileNotFoundSilentFail = typeOrOptions.fileNotFoundSilentFail || options.fileNotFoundSilentFail; options.srcEol = typeOrOptions.srcEol || options.srcEol; options.type = delim[typeOrOptions.type] || options.type; + options.supportSourceMaps = typeOrOptions.supportSourceMaps || false; } context = copy(context); @@ -160,17 +161,30 @@ function preprocessor(src, context, opts, noRestoreEol) { }); } + function compensateDirectiveRemoval(content){ + return opts.supportSourceMaps ? opts.srcEol + content + opts.srcEol : content; + } + + function getSourceMapCompatiableReplacementBlock(content) { + if (opts.supportSourceMaps) { + var lineCount = content.split(/\r\n|\r|\n/).length; + return Array(lineCount).join(opts.srcEol); + } else { + return ''; + } + } + if (opts.type.if) { rv = replaceRecursive(rv, opts.type.if, function (startMatches, endMatches, include, recurse) { var variant = startMatches[1]; var test = (startMatches[2] || '').trim(); switch(variant) { case 'if': - return testPasses(test,context) ? recurse(include) : ''; + return compensateDirectiveRemoval(testPasses(test,context) ? recurse(include) : getSourceMapCompatiableReplacementBlock(include)); case 'ifdef': - return typeof getDeepPropFromObj(context, test) !== 'undefined' ? recurse(include) : ''; + return compensateDirectiveRemoval(typeof getDeepPropFromObj(context, test) !== 'undefined' ? recurse(include) : getSourceMapCompatiableReplacementBlock(include)); case 'ifndef': - return typeof getDeepPropFromObj(context, test) === 'undefined' ? recurse(include) : ''; + return compensateDirectiveRemoval(typeof getDeepPropFromObj(context, test) === 'undefined' ? recurse(include) : getSourceMapCompatiableReplacementBlock(include)); default: throw new Error('Unknown if variant ' + variant + '.'); } diff --git a/test/sourcemaps.spec.js b/test/sourcemaps.spec.js new file mode 100644 index 0000000..dae01a3 --- /dev/null +++ b/test/sourcemaps.spec.js @@ -0,0 +1,27 @@ +'use strict'; + +var chai = require('chai'), + pp = require('../lib/preprocess'); + +chai.should(); + +describe('when the sourcemaps option is true it should maintain the line count', function () { + + describe('with @if directive', function () { + var input = "a\n" + + "// @if NODE_ENV=='production' \n" + + "b\n" + + "c\n" + + "// @endif \n" + + "d"; + + it('when condition evals to true', function () { + pp.preprocess(input, {NODE_ENV: 'production'}, {type: 'js', supportSourceMaps: true}).should.equal("a\n\nb\nc\n\nd"); + }); + + it('when condition evals to false', function () { + pp.preprocess(input, {NODE_ENV: 'dev'}, {type: 'js', supportSourceMaps: true}).should.equal("a\n\n\n\n\nd"); + }); + }); + +});