Skip to content

Commit

Permalink
New: First implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
phated committed Apr 10, 2017
1 parent a2f20c9 commit ab0dff5
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 2 deletions.
35 changes: 35 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

var through = require('through2');
var normalizePath = require('normalize-path');

var generate = require('./lib/generate');

function identityMap() {

function transform(file, _, cb) {
if (!file.sourceMap || !file.isBuffer()) {
return cb(null, file);
}

var sourcePath = normalizePath(file.relative);
var contents = file.contents.toString();

switch (file.extname) {
case '.js': {
file.sourceMap = generate.js(sourcePath, contents);
break;
}
case '.css': {
file.sourceMap = generate.css(sourcePath, contents);
break;
}
}

cb(null, file);
}

return through.obj(transform);
}

module.exports = identityMap;
65 changes: 65 additions & 0 deletions lib/generate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';

var css = require('css');
var acorn = require('acorn');
var SourceMapGenerator = require('source-map').SourceMapGenerator;

function generateJs(sourcePath, fileContent) {
var generator = new SourceMapGenerator({ file: sourcePath });
var tokenizer = acorn.tokenizer(fileContent, { locations: true });

while (true) {
var token = tokenizer.getToken();

if (token.type.label === 'eof') {
break;
}
var mapping = {
original: token.loc.start,
generated: token.loc.start,
source: sourcePath,
};
if (token.type.label === 'name') {
mapping.name = token.value;
}
generator.addMapping(mapping);
}
generator.setSourceContent(sourcePath, fileContent);

return generator.toJSON();
}

function generateCss(sourcePath, fileContent) {
var generator = new SourceMapGenerator({ file: sourcePath });
var ast = css.parse(fileContent, { silent: true });

function registerTokens(ast) {
if (ast.position) {
generator.addMapping({
original: ast.position.start,
generated: ast.position.start,
source: sourcePath,
});
}

for (var key in ast) {
if (key === 'position' || !ast[key]) {
break;
}
if (Object.prototype.toString.call(ast[key]) === '[object Object]') {
registerTokens(ast[key]);
} else if (Array.isArray(ast[key])) {
ast[key].forEach(registerTokens);
}
}
}
registerTokens(ast);
generator.setSourceContent(sourcePath, fileContent);

return generator.toJSON();
}

module.exports = {
js: generateJs,
css: generateCss,
};
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@
"main": "index.js",
"files": [
"LICENSE",
"index.js"
"index.js",
"lib"
],
"scripts": {
"lint": "eslint . && jscs index.js test/",
"lint": "eslint . && jscs index.js test/index.js lib/",
"pretest": "npm run lint",
"test": "mocha --async-only",
"cover": "istanbul cover _mocha --report lcovonly",
"coveralls": "npm run cover && istanbul-coveralls"
},
"dependencies": {
"acorn": "^5.0.3",
"css": "^2.2.1",
"normalize-path": "^2.1.1",
"source-map": "^0.5.6",
"through2": "^2.0.3"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/helloworld.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
body {
background: #eee;
color: #888;
}
5 changes: 5 additions & 0 deletions test/fixtures/helloworld.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

function helloWorld() {
console.log('Hello world!');
}
166 changes: 166 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
'use strict';

var fs = require('fs');
var path = require('path');

var expect = require('expect');

var miss = require('mississippi');
var File = require('vinyl');

var generate = require('../lib/generate');

var identityMap = require('../');

var pipe = miss.pipe;
var from = miss.from;
var concat = miss.concat;

var jsContent = fs.readFileSync(path.join(__dirname, 'fixtures/helloworld.js'));
var cssContent = fs.readFileSync(path.join(__dirname, 'fixtures/helloworld.css'));

function makeFile() {
var file = new File({
cwd: __dirname,
base: __dirname + '/assets',
path: __dirname + '/assets/helloworld.js',
contents: jsContent,
});

file.sourceMap = {
version: 3,
file: 'helloworld.js',
names: [],
mappings: '',
sources: ['helloworld.js'],
};

return file;
}

describe('identityMap', function() {

it('ignores a file without sourceMap property', function(done) {
var file = makeFile();
delete file.sourceMap;

var spy = expect.spyOn(generate, 'js');

function assert(files) {
expect.restoreSpies();
expect(files.length).toEqual(1);
expect(spy).toNotHaveBeenCalled();
}

pipe([
from.obj([file]),
identityMap(),
concat(assert),
], done);
});

it('only ignores a file without sourceMap property', function(done) {
var file = makeFile();
delete file.sourceMap;
var file2 = makeFile();

var spy = expect.spyOn(generate, 'js');

function assert(files) {
expect.restoreSpies();
expect(files.length).toEqual(2);
expect(spy.calls.length).toEqual(1);
}

pipe([
from.obj([file, file2]),
identityMap(),
concat(assert),
], done);
});

it('ignores a non-Buffer file', function(done) {
var file = makeFile();
file.contents = null;

var spy = expect.spyOn(generate, 'js');

function assert(files) {
expect.restoreSpies();
expect(files.length).toEqual(1);
expect(spy).toNotHaveBeenCalled();
}

pipe([
from.obj([file]),
identityMap(),
concat(assert),
], done);
});

it('only ignores a non-Buffer file', function(done) {
var file = makeFile();
file.contents = null;
var file2 = makeFile();

var spy = expect.spyOn(generate, 'js');

function assert(files) {
expect.restoreSpies();
expect(files.length).toEqual(2);
expect(spy.calls.length).toEqual(1);
}

pipe([
from.obj([file, file2]),
identityMap(),
concat(assert),
], done);
});

it('adds a valid sourcemap for JS', function(done) {
var file = makeFile();

function assert(files) {
expect(files.length).toEqual(1);

var sourcemap = files[0].sourceMap;
expect(sourcemap).toExist();
expect(sourcemap.version).toEqual('3');
expect(sourcemap.sources[0]).toEqual('helloworld.js');
expect(sourcemap.sourcesContent[0]).toEqual(jsContent);
expect(sourcemap.names).toEqual(['helloWorld', 'console','log']);
expect(sourcemap.mappings).toEqual('AAAA,YAAY;;AAEZ,SAASA,UAAU,CAAC,EAAE;CACrBC,OAAO,CAACC,GAAG,CAAC,cAAc,CAAC;AAC5B');
}

pipe([
from.obj([file]),
identityMap(),
concat(assert),
], done);
});

it('adds a valid source map for CSS', function(done) {
var file = makeFile();
file.extname = '.css';
file.contents = cssContent;

function assert(files) {
expect(files.length).toEqual(1);

var sourcemap = files[0].sourceMap;
expect(sourcemap).toExist();
expect(sourcemap.version).toEqual('3');
expect(sourcemap.sources[0]).toBe('helloworld.css');
expect(sourcemap.sourcesContent[0]).toEqual(cssContent);
expect(sourcemap.names).toEqual([]);
expect(sourcemap.mappings).toBe('CAAC;EACC;EACA');
}

pipe([
from.obj([file]),
identityMap(),
concat(assert),
], done);
});
});

0 comments on commit ab0dff5

Please sign in to comment.