Skip to content

Commit

Permalink
flatten sourcemaps lazily when serving (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed May 8, 2015
1 parent c98c180 commit 455b40b
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 78 deletions.
13 changes: 3 additions & 10 deletions src/builtins/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@ import config from '../config';
import extractLocationInfo from '../utils/extractLocationInfo';
import { isRegExp } from '../utils/is';
import { ABORTED } from '../utils/signals';

let SOURCEMAPPING_URL = 'sourceMa';
SOURCEMAPPING_URL += 'ppingURL';

const SOURCEMAP_COMMENT = new RegExp( `\n*(?:` +
`\\/\\/[@#]\\s*${SOURCEMAPPING_URL}=([^'"]+)|` + // js
`\\/\\*#?\\s*${SOURCEMAPPING_URL}=([^'"]+)\\s\\+\\/)` + // css
`\\s*$`, 'g' );
import { getSourcemapComment, SOURCEMAPPING_URL, SOURCEMAP_COMMENT } from '../utils/sourcemap';

export default function map ( inputdir, outputdir, options ) {
let changed = {};
Expand Down Expand Up @@ -125,7 +118,7 @@ function processResult ( result, original, src, dest, codepath ) {
// if a sourcemap was returned, use it
if ( result.map ) {
return {
code: result.code.replace( SOURCEMAP_COMMENT, '' ) + sourceMappingURLComment( codepath ),
code: result.code.replace( SOURCEMAP_COMMENT, '' ) + getSourcemapComment( encodeURI( codepath + '.map' ), extname( codepath ) ),
map: processSourcemap( result.map, src, dest, original )
};
}
Expand Down Expand Up @@ -158,7 +151,7 @@ function processInlineSourceMap ( code, src, dest, original, codepath ) {
let json = atob( match[1] );

map = processSourcemap( json, src, dest, original );
code = code.replace( SOURCEMAP_COMMENT, '' ) + sourceMappingURLComment( codepath );
code = code.replace( SOURCEMAP_COMMENT, '' ) + getSourcemapComment( encodeURI( codepath + '.map' ), extname( codepath ) );
}

return { code, map };
Expand Down
39 changes: 4 additions & 35 deletions src/nodes/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,45 +82,14 @@ export default class Node extends EventEmitter2 {
node.on( 'error', handleError );

function build () {
const buildStart = Date.now();

buildScheduled = false;

watchTask.emit( 'build:start' );

node.ready()
.then( outputdir => {
watchTask.emit( 'built', {
dir: outputdir,
duration: Date.now() - buildStart
});
watchTask.emit( 'build:end', outputdir );
})
/*.then( inputdir => {
const sourcemapProcessStart = Date.now();
watchTask.emit( 'info', {
code: 'SOURCEMAP_PROCESS_START',
progressIndicator: true
});
// create new directory for sourcemaps...
const outputdir = join( session.config.gobbledir, '.final', '' + uid++ );
return copydir( inputdir ).to( outputdir )
.then( () => flattenSourcemaps( inputdir, outputdir, dest, watchTask ) )
.then( () => {
watchTask.emit( 'info', {
code: 'SOURCEMAP_PROCESS_COMPLETE',
duration: Date.now() - sourcemapProcessStart
});
watchTask.emit( 'info', {
code: 'BUILD_COMPLETE',
duration: Date.now() - buildStart,
watch: true
});
watchTask.emit( 'built', outputdir );
});
})*/
.catch( handleError );
}

Expand All @@ -137,7 +106,7 @@ export default class Node extends EventEmitter2 {
watchTask.close = () => node.stop();

this.start();
build();
process.nextTick( build );

return watchTask;
}
Expand Down
2 changes: 0 additions & 2 deletions src/nodes/Transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ export default class Transformer extends Node {
transformation.aborted = true;
};

this.sourcemaps = {};

outputdir = resolve( session.config.gobbledir, this.id, '' + this.counter++ );

this._ready = this.input.ready().then( inputdir => {
Expand Down
10 changes: 8 additions & 2 deletions src/nodes/serve/handleRequest.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { join } from 'path';
import { extname, join } from 'path';
import { parse } from 'url';
import { stat, Promise } from 'sander';
import serveFile from './serveFile';
import serveDir from './serveDir';
import serveSourcemap from './serveSourcemap';
import serveError from './serveError';

export default function handleRequest ( srcDir, error, request, response ) {
export default function handleRequest ( srcDir, error, sourcemapPromises, request, response ) {
const parsedUrl = parse( request.url );
const pathname = parsedUrl.pathname;

Expand All @@ -29,6 +30,11 @@ export default function handleRequest ( srcDir, error, request, response ) {

filepath = join( srcDir, pathname );

if ( extname( filepath ) === '.map' ) {
return serveSourcemap( filepath, sourcemapPromises, request, response )
.catch( err => serveError( err, request, response ) );
}

return stat( filepath ).then( stats => {
if ( stats.isDirectory() ) {
// might need to redirect from `foo` to `foo/`
Expand Down
16 changes: 13 additions & 3 deletions src/nodes/serve/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function serve ( node, options = {} ) {
let buildStarted = Date.now();
let watchTask;
let srcDir;
let sourcemapPromises;
let server;
let serverReady;
let lrServer;
Expand All @@ -35,14 +36,23 @@ export default function serve ( node, options = {} ) {
task.emit( 'error', err );
});

watchTask.on( 'built', d => {
let buildStart;
watchTask.on( 'build:start', () => buildStart = Date.now() );

watchTask.on( 'build:end', dir => {
error = null;
srcDir = d;
sourcemapPromises = {};
srcDir = dir;

built = true;

task.emit( 'built' );

task.emit( 'info', {
code: 'BUILD_COMPLETE',
duration: Date.now() - buildStart
});

if ( !firedReadyEvent && serverReady ) {
task.emit( 'ready' );
firedReadyEvent = true;
Expand Down Expand Up @@ -114,7 +124,7 @@ export default function serve ( node, options = {} ) {
});

server.on( 'request', ( request, response ) => {
handleRequest( srcDir, error, request, response )
handleRequest( srcDir, error, sourcemapPromises, request, response )
.catch( err => task.emit( 'error', err ) );
});

Expand Down
29 changes: 24 additions & 5 deletions src/nodes/serve/serveFile.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import { basename, extname } from 'path';
import { lookup } from 'mime';
import { readFile } from 'sander';
import { readFile, stat, createReadStream } from 'sander';
import { getSourcemapComment, SOURCEMAP_COMMENT } from '../../utils/sourcemap';

export default function serveFile ( filepath, request, response ) {
return readFile( filepath ).then( data => {
const ext = extname( filepath );

// this might be turn out to be a really bad idea. But let's try it and see
if ( ext === '.js' || ext === '.css' ) {
return readFile( filepath ).then( data => {
// this takes the auto-generated absolute sourcemap path, and turns
// it into what you'd get with `gobble build` or `gobble watch`
const sourcemapComment = getSourcemapComment( basename( filepath ) + '.map', ext );
data = data.toString().replace( SOURCEMAP_COMMENT, sourcemapComment );

response.statusCode = 200;
response.setHeader( 'Content-Type', lookup( filepath ) );

response.write( data );
response.end();
});
}

return stat( filepath ).then( stats => {
response.statusCode = 200;
response.setHeader( 'Content-Type', lookup( filepath ) );
response.setHeader( 'Content-Length', data.length );
response.setHeader( 'Content-Length', stats.size );

response.write( data );
response.end();
createReadStream( filepath ).pipe( response );
});
}
24 changes: 24 additions & 0 deletions src/nodes/serve/serveSourcemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { load } from 'sorcery';

export default function serveSourcemap ( filepath, sourcemapPromises, request, response ) {
const owner = filepath.slice( 0, -4 );

if ( !sourcemapPromises[ filepath ] ) {
sourcemapPromises[ filepath ] = load( owner )
.then( chain => {
if ( !chain ) {
throw new Error( 'Could not resolve sourcemap for ' + owner );
}

return chain.apply().toString();
});
}

return sourcemapPromises[ filepath ].then( map => {
response.statusCode = 200;
response.setHeader( 'Content-Type', 'application/json' );

response.write( map );
response.end();
});
}
29 changes: 27 additions & 2 deletions src/nodes/watch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { copydir, rimraf, Promise } from 'sander';
import cleanup from '../../utils/cleanup';
import session from '../../session';
import GobbleError from '../../utils/GobbleError';
import flattenSourcemaps from '../../utils/flattenSourcemaps';

export default function watch ( node, options ) {
if ( !options || !options.dest ) {
Expand All @@ -23,11 +24,35 @@ export default function watch ( node, options ) {
watchTask.on( 'info', details => task.emit( 'info', details ) );
watchTask.on( 'error', err => task.emit( 'error', err ) );

watchTask.on( 'built', { dir, duration } => {
let buildStart;
watchTask.on( 'build:start', () => buildStart = Date.now() );

watchTask.on( 'build:end', dir => {
const dest = options.dest;

rimraf( dest )
.then( () => copydir( outputdir ).to( dest ) )
.then( () => copydir( dir ).to( dest ) )
.then( () => {
const sourcemapProcessStart = Date.now();

task.emit( 'info', {
code: 'SOURCEMAP_PROCESS_START',
progressIndicator: true
});

return flattenSourcemaps( dir, dest, dest, task ).then( () => {
task.emit( 'info', {
code: 'SOURCEMAP_PROCESS_COMPLETE',
duration: Date.now() - sourcemapProcessStart
});

task.emit( 'info', {
code: 'BUILD_COMPLETE',
duration: Date.now() - buildStart,
watch: true
});
});
})
.then( () => task.emit( 'built', dest ) )
.catch( err => task.emit( 'error', err ) );
});
Expand Down
17 changes: 17 additions & 0 deletions src/utils/sourcemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
let SOURCEMAPPING_URL = 'sourceMa';
SOURCEMAPPING_URL += 'ppingURL';

const SOURCEMAP_COMMENT = new RegExp( `\n*(?:` +
`\\/\\/[@#]\\s*${SOURCEMAPPING_URL}=([^'"]+)|` + // js
`\\/\\*#?\\s*${SOURCEMAPPING_URL}=([^'"]+)\\s\\+\\/)` + // css
`\\s*$`, 'g' );

function getSourcemapComment ( url, ext ) {
if ( ext === '.css' ) {
return `\n/*# ${SOURCEMAPPING_URL}=${url} */\n`;
}

return `\n//# ${SOURCEMAPPING_URL}=${url}\n`;
}

export { getSourcemapComment, SOURCEMAP_COMMENT, SOURCEMAPPING_URL };
Loading

0 comments on commit 455b40b

Please sign in to comment.