| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- 'use strict';
- var through = require('through2');
- var fs = require('fs');
- var path = require('path');
- var File = require('vinyl');
- var convert = require('convert-source-map');
- var PLUGIN_NAME = 'gulp-sourcemap';
- var urlRegex = /^https?:\/\//;
- /**
- * Initialize source mapping chain
- */
- module.exports.init = function init(options) {
- function sourceMapInit(file, encoding, callback) {
- /*jshint validthis:true */
- if (file.isNull()) {
- this.push(file);
- return callback();
- }
- if (file.isStream()) {
- return callback(new Error(PLUGIN_NAME + '-init: Streaming not supported'));
- }
- var fileContent = file.contents.toString();
- var sourceMap;
- if (options && options.loadMaps) {
- var sourcePath = ''; //root path for the sources in the map
- // Try to read inline source map
- sourceMap = convert.fromSource(fileContent);
- if (sourceMap) {
- sourceMap = sourceMap.toObject();
- // sources in map are relative to the source file
- sourcePath = path.dirname(file.path);
- fileContent = convert.removeComments(fileContent);
- } else {
- // look for source map comment referencing a source map file
- var mapComment = convert.mapFileCommentRegex.exec(fileContent);
- var mapFile;
- if (mapComment) {
- mapFile = path.resolve(path.dirname(file.path), mapComment[1] || mapComment[2]);
- fileContent = convert.removeMapFileComments(fileContent);
- // if no comment try map file with same name as source file
- } else {
- mapFile = file.path + '.map';
- }
- // sources in external map are relative to map file
- sourcePath = path.dirname(mapFile);
- try {
- sourceMap = JSON.parse(fs.readFileSync(mapFile).toString());
- } catch(e) {}
- }
- // fix source paths and sourceContent for imported source map
- if (sourceMap) {
- sourceMap.sourcesContent = sourceMap.sourcesContent || [];
- sourceMap.sources.forEach(function(source, i) {
- if (source.match(urlRegex)) {
- sourceMap.sourcesContent[i] = sourceMap.sourcesContent[i] || null;
- return;
- }
- var absPath = path.resolve(sourcePath, source);
- sourceMap.sources[i] = unixStylePath(path.relative(file.base, absPath));
- if (!sourceMap.sourcesContent[i]) {
- var sourceContent = null;
- if (sourceMap.sourceRoot) {
- if (sourceMap.sourceRoot.match(urlRegex)) {
- sourceMap.sourcesContent[i] = null;
- return;
- }
- absPath = path.resolve(sourcePath, sourceMap.sourceRoot, source);
- }
- // if current file: use content
- if (absPath === file.path) {
- sourceContent = fileContent;
- // else load content from file
- } else {
- try {
- if (options.debug)
- console.log(PLUGIN_NAME + '-init: No source content for "' + source + '". Loading from file.');
- sourceContent = fs.readFileSync(absPath).toString();
- } catch (e) {
- if (options.debug)
- console.warn(PLUGIN_NAME + '-init: source file not found: ' + absPath);
- }
- }
- sourceMap.sourcesContent[i] = sourceContent;
- }
- });
- // remove source map comment from source
- file.contents = new Buffer(fileContent, 'utf8');
- }
- }
- if (!sourceMap) {
- // Make an empty source map
- sourceMap = {
- version : 3,
- file: unixStylePath(file.relative),
- names: [],
- mappings: '',
- sources: [unixStylePath(file.relative)],
- sourcesContent: [fileContent]
- };
- }
- sourceMap.file = file.relative;
- file.sourceMap = sourceMap;
- this.push(file);
- callback();
- }
- return through.obj(sourceMapInit);
- };
- /**
- * Write the source map
- *
- * @param options options to change the way the source map is written
- *
- */
- module.exports.write = function write(destPath, options) {
- if (options === undefined && Object.prototype.toString.call(destPath) === '[object Object]') {
- options = destPath;
- destPath = undefined;
- }
- options = options || {};
- // set defaults for options if unset
- if (options.includeContent === undefined)
- options.includeContent = true;
- if (options.addComment === undefined)
- options.addComment = true;
- function sourceMapWrite(file, encoding, callback) {
- /*jshint validthis:true */
- if (file.isNull() || !file.sourceMap) {
- this.push(file);
- return callback();
- }
- if (file.isStream()) {
- return callback(new Error(PLUGIN_NAME + '-write: Streaming not supported'));
- }
- var sourceMap = file.sourceMap;
- // fix paths if Windows style paths
- sourceMap.file = unixStylePath(file.relative);
- sourceMap.sources = sourceMap.sources.map(function(filePath) {
- return unixStylePath(filePath);
- });
- if (options.sourceRoot) {
- if (typeof options.sourceRoot === 'function') {
- sourceMap.sourceRoot = options.sourceRoot(file);
- } else {
- sourceMap.sourceRoot = options.sourceRoot;
- }
- }
- if (options.includeContent) {
- sourceMap.sourceRoot = sourceMap.sourceRoot || '/source/';
- sourceMap.sourcesContent = sourceMap.sourcesContent || [];
- // load missing source content
- for (var i = 0; i < file.sourceMap.sources.length; i++) {
- if (!sourceMap.sourcesContent[i]) {
- var sourcePath = path.resolve(file.base, sourceMap.sources[i]);
- try {
- if (options.debug)
- console.log(PLUGIN_NAME + '-write: No source content for "' + sourceMap.sources[i] + '". Loading from file.');
- sourceMap.sourcesContent[i] = fs.readFileSync(sourcePath).toString();
- } catch (e) {
- if (options.debug)
- console.warn(PLUGIN_NAME + '-write: source file not found: ' + sourcePath);
- }
- }
- }
- } else {
- delete sourceMap.sourcesContent;
- }
- var extension = file.relative.split('.').pop();
- var commentFormatter;
- switch (extension) {
- case 'css':
- commentFormatter = function(url) { return "\n/*# sourceMappingURL=" + url + " */"; };
- break;
- case 'js':
- commentFormatter = function(url) { return "\n//# sourceMappingURL=" + url; };
- break;
- default:
- commentFormatter = function(url) { return ""; };
- }
- var comment;
- if (!destPath) {
- // encode source map into comment
- var base64Map = new Buffer(JSON.stringify(sourceMap)).toString('base64');
- comment = commentFormatter('data:application/json;base64,' + base64Map);
- } else {
- // add new source map file to stream
- var sourceMapFile = new File({
- cwd: file.cwd,
- base: file.base,
- path: path.join(file.base, destPath, file.relative) + '.map',
- contents: new Buffer(JSON.stringify(sourceMap))
- });
- this.push(sourceMapFile);
- comment = commentFormatter(unixStylePath(path.join(path.relative(path.dirname(file.path), file.base), destPath, file.relative) + '.map'));
- if (options.sourceMappingURLPrefix) {
- if (typeof options.sourceMappingURLPrefix === 'function') {
- options.sourceMappingURLPrefix = options.sourceMappingURLPrefix(file);
- }
- comment = comment.replace(/sourceMappingURL=\.*/, 'sourceMappingURL=' + options.sourceMappingURLPrefix);
- }
- }
- // append source map comment
- if (options.addComment)
- file.contents = Buffer.concat([file.contents, new Buffer(comment)]);
- this.push(file);
- callback();
- }
- return through.obj(sourceMapWrite);
- };
- function unixStylePath(filePath) {
- return filePath.split(path.sep).join('/');
- }
|