Skip to content

Commit

Permalink
Import existing sourcemaps from libraries (#1349)
Browse files Browse the repository at this point in the history
  • Loading branch information
DeMoorJasper authored and devongovett committed Jul 22, 2018
1 parent be2bd6f commit 7869f99
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 0 deletions.
72 changes: 72 additions & 0 deletions packages/core/parcel/src/assets/JSAsset.js
Expand Up @@ -13,13 +13,18 @@ const generate = require('babel-generator').default;
const terser = require('../transforms/terser');
const SourceMap = require('../SourceMap');
const hoist = require('../scope-hoisting/hoist');
const path = require('path');
const fs = require('../utils/fs');
const logger = require('../Logger');

const IMPORT_RE = /\b(?:import\b|export\b|require\s*\()/;
const ENV_RE = /\b(?:process\.env)\b/;
const GLOBAL_RE = /\b(?:process|__dirname|__filename|global|Buffer|define)\b/;
const FS_RE = /\breadFileSync\b/;
const SW_RE = /\bnavigator\s*\.\s*serviceWorker\s*\.\s*register\s*\(/;
const WORKER_RE = /\bnew\s*Worker\s*\(/;
const SOURCEMAP_RE = /\/\/\s*[@#]\s*sourceMappingURL\s*=\s*([^\s]+)/;
const DATA_URL_RE = /^data:[^;]+(?:;charset=[^;]+)?;base64,(.*)/;

class JSAsset extends Asset {
constructor(name, options) {
Expand Down Expand Up @@ -104,7 +109,74 @@ class JSAsset extends Asset {
walk.ancestor(this.ast, collectDependencies, this);
}

async loadSourceMap() {
// Get original sourcemap if there is any
let match = this.contents.match(SOURCEMAP_RE);
if (match) {
this.contents = this.contents.replace(SOURCEMAP_RE, '');

let url = match[1];
let dataURLMatch = url.match(DATA_URL_RE);

try {
let json, filename;
if (dataURLMatch) {
filename = this.name;
json = new Buffer(dataURLMatch[1], 'base64').toString();
} else {
filename = path.join(path.dirname(this.name), url);
json = await fs.readFile(filename, 'utf8');

// Add as a dep so we watch the source map for changes.
this.addDependency(filename, {includedInParent: true});
}

this.sourceMap = JSON.parse(json);

// Attempt to read missing source contents
if (!this.sourceMap.sourcesContent) {
this.sourceMap.sourcesContent = [];
}

let missingSources = this.sourceMap.sources.slice(
this.sourceMap.sourcesContent.length
);
if (missingSources.length) {
let contents = await Promise.all(
missingSources.map(async source => {
try {
let sourceFile = path.join(
path.dirname(filename),
this.sourceMap.sourceRoot || '',
source
);
let result = await fs.readFile(sourceFile, 'utf8');
this.addDependency(sourceFile, {includedInParent: true});
return result;
} catch (err) {
logger.warn(
`Could not load source file "${source}" in source map of "${
this.relativeName
}".`
);
}
})
);

this.sourceMap.sourcesContent = this.sourceMap.sourcesContent.concat(
contents
);
}
} catch (e) {
logger.warn(
`Could not load existing sourcemap of "${this.relativeName}".`
);
}
}
}

async pretransform() {
await this.loadSourceMap();
await babel(this);

// Inline environment variables
Expand Down
1 change: 1 addition & 0 deletions packages/core/parcel/test/graphql.js
Expand Up @@ -31,6 +31,7 @@ describe('graphql', function() {
firstName
lastName
}
`.definitions
);
});
Expand Down
@@ -0,0 +1,5 @@
const sum = require('./sum');

module.exports = function() {
return sum(1, 2);
}
11 changes: 11 additions & 0 deletions packages/core/parcel/test/integration/sourcemap-existing/sum.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/core/parcel/test/integration/sourcemap-existing/sum.map
@@ -0,0 +1,11 @@
{
"version": 3,
"file": "sum.js",
"sourceRoot": "",
"sources": [
"sum.coffee"
],
"names": [],
"mappings": ";AAAA;EAAA,MAAM,CAAC,OAAP,GAAiB,CAAA,SAAA,KAAA;WAAA,SAAC,CAAD,EAAI,CAAJ;aAAU,CAAA,GAAI;IAAd;EAAA,CAAA,CAAA,CAAA,IAAA;AAAjB",
"sourcesContent": ["module.exports = (a, b) => a + b"]
}
@@ -0,0 +1,5 @@
const sum = require('./sum');

module.exports = function() {
return sum(1, 2);
}
@@ -0,0 +1 @@
module.exports = (a, b) => a + b

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,10 @@
{
"version": 3,
"file": "sum.js",
"sourceRoot": "",
"sources": [
"sum.coffee"
],
"names": [],
"mappings": ";AAAA;EAAA,MAAM,CAAC,OAAP,GAAiB,CAAA,SAAA,KAAA;WAAA,SAAC,CAAD,EAAI,CAAJ;aAAU,CAAA,GAAI;IAAd;EAAA,CAAA,CAAA,CAAA,IAAA;AAAjB"
}
@@ -0,0 +1,5 @@
const sum = require('./sum');

module.exports = function() {
return sum(1, 2);
}
9 changes: 9 additions & 0 deletions packages/core/parcel/test/integration/sourcemap-inline/sum.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 106 additions & 0 deletions packages/core/parcel/test/sourcemaps.js
Expand Up @@ -204,4 +204,110 @@ describe('sourcemaps', function() {
let map = (await fs.readFile(path.join(sourcemapReference))).toString();
mapValidator(jsOutput, map);
});

it('should load existing sourcemaps of libraries', async function() {
let b = await bundle(
__dirname + '/integration/sourcemap-existing/index.js'
);

assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', 'sum.js'],
childBundles: [
{
type: 'map'
}
]
});

let jsOutput = await fs.readFile(b.name, 'utf8');

let sourcemapReference = path.join(
__dirname,
'/dist/',
jsOutput.substring(jsOutput.lastIndexOf('//# sourceMappingURL') + 22)
);

assert(
await fs.exists(path.join(sourcemapReference)),
'referenced sourcemap should exist'
);

let map = await fs.readFile(path.join(sourcemapReference), 'utf8');
assert(
map.indexOf('module.exports = (a, b) => a + b') > -1,
'Sourcemap should contain the existing sourcemap'
);
mapValidator(jsOutput, map);
});

it('should load inline sourcemaps of libraries', async function() {
let b = await bundle(__dirname + '/integration/sourcemap-inline/index.js');

assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', 'sum.js'],
childBundles: [
{
type: 'map'
}
]
});

let jsOutput = await fs.readFile(b.name, 'utf8');

let sourcemapReference = path.join(
__dirname,
'/dist/',
jsOutput.substring(jsOutput.lastIndexOf('//# sourceMappingURL') + 22)
);

assert(
await fs.exists(path.join(sourcemapReference)),
'referenced sourcemap should exist'
);

let map = await fs.readFile(path.join(sourcemapReference), 'utf8');
assert(
map.indexOf('module.exports = (a, b) => a + b') > -1,
'Sourcemap should contain the existing sourcemap'
);
mapValidator(jsOutput, map);
});

it('should load referenced contents of sourcemaps', async function() {
let b = await bundle(
__dirname + '/integration/sourcemap-external-contents/index.js'
);

assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', 'sum.js'],
childBundles: [
{
type: 'map'
}
]
});

let jsOutput = await fs.readFile(b.name, 'utf8');

let sourcemapReference = path.join(
__dirname,
'/dist/',
jsOutput.substring(jsOutput.lastIndexOf('//# sourceMappingURL') + 22)
);

assert(
await fs.exists(path.join(sourcemapReference)),
'referenced sourcemap should exist'
);

let map = await fs.readFile(path.join(sourcemapReference), 'utf8');
assert(
map.indexOf('module.exports = (a, b) => a + b') > -1,
'Sourcemap should contain the existing sourcemap'
);
mapValidator(jsOutput, map);
});
});

0 comments on commit 7869f99

Please sign in to comment.