Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source maps, fixes #2 #40

Merged
merged 2 commits into from Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 37 additions & 1 deletion index.js
Expand Up @@ -12,6 +12,9 @@ var inspect = require('object-inspect');
var evaluate = require('static-eval');
var copy = require('shallow-copy');
var has = require('has');
var MagicString = require('magic-string');
var convertSourceMap = require('convert-source-map');
var mergeSourceMap = require('merge-source-map');

module.exports = function parse (modules, opts) {
if (!opts) opts = {};
Expand All @@ -22,12 +25,20 @@ module.exports = function parse (modules, opts) {
var skipOffset = opts.skipOffset || 0;
var parserOpts = opts.parserOpts || { ecmaVersion: 8 };
var updates = [];
var sourcemapper;
var inputMap;

var output = through();
var body;
return duplexer(concat(function (buf) {
try {
body = buf.toString('utf8').replace(/^#!/, '//#!');
if (opts.sourceMap) {
inputMap = convertSourceMap.fromSource(body);
if (inputMap) inputMap = inputMap.toObject();
body = convertSourceMap.removeComments(body);
sourcemapper = new MagicString(body);
}
falafel(body, parserOpts, walk);
}
catch (err) { return error(err) }
Expand All @@ -46,11 +57,31 @@ module.exports = function parse (modules, opts) {
pos = s.start + s.offset;

s.stream.pipe(output, { end: false });
s.stream.on('end', next);
if (opts.sourceMap) {
s.stream.pipe(concat({ encoding: 'string' }, function (chunk) {
// We have to give magic-string the replacement string,
// so it can calculate the amount of lines and columns.
sourcemapper.overwrite(s.start, s.start + s.offset, chunk);
})).on('finish', next);
} else {
s.stream.on('end', next);
}
})();

function done () {
output.push(src.slice(pos));
if (opts.sourceMap) {
var map = sourcemapper.generateMap({
source: opts.inputFilename || 'input.js',
includeContent: true
});
if (inputMap) {
var merged = mergeSourceMap(inputMap, map);
output.push('\n' + convertSourceMap.fromObject(merged).toComment() + '\n');
} else {
output.push('\n//# sourceMappingURL=' + map.toUrl() + '\n');
}
}
output.push(null);
}
}
Expand All @@ -61,6 +92,11 @@ module.exports = function parse (modules, opts) {
}

function walk (node) {
if (opts.sourceMap) {
sourcemapper.addSourcemapLocation(node.start);
sourcemapper.addSourcemapLocation(node.end);
}

var isreq = isRequire(node);
var isreqm = false, isreqv = false, reqid;
if (isreq) {
Expand Down
7 changes: 6 additions & 1 deletion package.json
Expand Up @@ -5,10 +5,13 @@
"main": "index.js",
"dependencies": {
"concat-stream": "~1.6.0",
"convert-source-map": "^1.5.1",
"duplexer2": "~0.1.4",
"escodegen": "~1.9.0",
"falafel": "^2.1.0",
"has": "^1.0.1",
"magic-string": "^0.22.4",
"merge-source-map": "1.0.4",
"object-inspect": "~1.4.0",
"quote-stream": "~1.0.2",
"readable-stream": "~2.3.3",
Expand All @@ -18,7 +21,9 @@
},
"devDependencies": {
"resolve": "^1.5.0",
"tape": "^4.8.0"
"source-map": "^0.6.1",
"tape": "^4.8.0",
"uglify-js": "3.3.12"
},
"scripts": {
"test": "tape test/*.js"
Expand Down
4 changes: 4 additions & 0 deletions readme.markdown
Expand Up @@ -100,6 +100,10 @@ process.stdin.pipe(sm).pipe(process.stdout);
Use `opts.parserOpts` to set additional options for the
[acorn](https://github.com/acornjs/acorn) parser.

Set `opts.sourceMap` to `true` to generate a source map and add it as an inline
comment. You can add `opts.inputFilename` to configure the original file name
that will be listed in the source map.

# install

With [npm](https://npmjs.org) do:
Expand Down
88 changes: 88 additions & 0 deletions test/sourcemap.js
@@ -0,0 +1,88 @@
var test = require('tape');
var fs = require('fs');
var concat = require('concat-stream');
var PassThrough = require('stream').PassThrough;
var convertSourceMap = require('convert-source-map');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var uglify = require('uglify-js');
var staticModule = require('../');

test('source maps', function (t) {
t.plan(6);

var transform = staticModule({
sheetify: function (filename) {
var stream = PassThrough();
stream.write('`.css{\n');
stream.write(' color: red;\n');
stream.end('}`');
return stream;
}
}, { sourceMap: true, inputFilename: 'main.js' });

fs.createReadStream(__dirname + '/sourcemap/main.js').pipe(transform).pipe(concat({ encoding: 'string' }, function (res) {
var consumer = new SourceMapConsumer(convertSourceMap.fromSource(res).toObject());

var mapped = consumer.originalPositionFor({
line: 8,
column: 0
});
t.equal(mapped.line, 8);
t.equal(mapped.column, 0);

mapped = consumer.originalPositionFor({
line: 10,
column: 2
});
t.equal(mapped.line, 8);
t.equal(mapped.column, 19);

mapped = consumer.originalPositionFor({
line: 12,
column: 0
});
t.equal(mapped.line, 10);
t.equal(mapped.column, 0);
}));
});

test('input source map', function (t) {
t.plan(4);

var content = fs.readFileSync(__dirname + '/sourcemap/main.js', 'utf8');
var minified = uglify.minify({ 'main.js': content }, {
output: { beautify: true },
sourceMap: {
url: 'inline',
includeSources: true
}
});

var transform = staticModule({
sheetify: function (filename) {
var stream = PassThrough();
stream.end('`.css{\n color: orange;\n}`');
return stream;
}
}, { sourceMap: true, inputFilename: 'main.js' });

transform.pipe(concat({ encoding: 'string' }, function (res) {
var consumer = new SourceMapConsumer(convertSourceMap.fromSource(res).toObject());

var mapped = consumer.originalPositionFor({
line: 7,
column: 0
});
t.equal(mapped.line, 8);
t.equal(mapped.column, 0);

mapped = consumer.originalPositionFor({
line: 9,
column: 4
});
t.equal(mapped.line, 10);
t.equal(mapped.column, 0);
}));

transform.end(minified.code);
});
10 changes: 10 additions & 0 deletions test/sourcemap/main.js
@@ -0,0 +1,10 @@
var css = require('sheetify');

function doSomeStuff () {
// doing some stuff before inlining a static module call
return yadda(yadda)
}

css('filename.css');

exports.blah = whatever.xyz