Subtheme of barrio
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
5.7 KiB

2 years ago
'use strict';
var path = require('path');
var fs = require('graceful-fs');
var nal = require('now-and-later');
var File = require('vinyl');
var convert = require('convert-source-map');
var removeBOM = require('remove-bom-buffer');
var appendBuffer = require('append-buffer');
var normalizePath = require('normalize-path');
var urlRegex = /^(https?|webpack(-[^:]+)?):\/\//;
function isRemoteSource(source) {
return source.match(urlRegex);
}
function parse(data) {
try {
return JSON.parse(removeBOM(data));
} catch (err) {
// TODO: should this log a debug?
}
}
function loadSourceMap(file, state, callback) {
// Try to read inline source map
state.map = convert.fromSource(state.content);
if (state.map) {
state.map = state.map.toObject();
// Sources in map are relative to the source file
state.path = file.dirname;
state.content = convert.removeComments(state.content);
// Remove source map comment from source
file.contents = new Buffer(state.content, 'utf8');
return callback();
}
// Look for source map comment referencing a source map file
var mapComment = convert.mapFileCommentRegex.exec(state.content);
var mapFile;
if (mapComment) {
mapFile = path.resolve(file.dirname, mapComment[1] || mapComment[2]);
state.content = convert.removeMapFileComments(state.content);
// Remove source map comment from source
file.contents = new Buffer(state.content, 'utf8');
} else {
// If no comment try map file with same name as source file
mapFile = file.path + '.map';
}
// Sources in external map are relative to map file
state.path = path.dirname(mapFile);
fs.readFile(mapFile, onRead);
function onRead(err, data) {
if (err) {
return callback();
}
state.map = parse(data);
callback();
}
}
// Fix source paths and sourceContent for imported source map
function fixImportedSourceMap(file, state, callback) {
if (!state.map) {
return callback();
}
state.map.sourcesContent = state.map.sourcesContent || [];
nal.map(state.map.sources, normalizeSourcesAndContent, callback);
function assignSourcesContent(sourceContent, idx) {
state.map.sourcesContent[idx] = sourceContent;
}
function normalizeSourcesAndContent(sourcePath, idx, cb) {
var sourceRoot = state.map.sourceRoot || '';
var sourceContent = state.map.sourcesContent[idx] || null;
if (isRemoteSource(sourcePath)) {
assignSourcesContent(sourceContent, idx);
return cb();
}
if (state.map.sourcesContent[idx]) {
return cb();
}
if (sourceRoot && isRemoteSource(sourceRoot)) {
assignSourcesContent(sourceContent, idx);
return cb();
}
var basePath = path.resolve(file.base, sourceRoot);
var absPath = path.resolve(state.path, sourceRoot, sourcePath);
var relPath = path.relative(basePath, absPath);
var unixRelPath = normalizePath(relPath);
state.map.sources[idx] = unixRelPath;
if (absPath !== file.path) {
// Load content from file async
return fs.readFile(absPath, onRead);
}
// If current file: use content
assignSourcesContent(state.content, idx);
cb();
function onRead(err, data) {
if (err) {
assignSourcesContent(null, idx);
return cb();
}
assignSourcesContent(removeBOM(data).toString('utf8'), idx);
cb();
}
}
}
function mapsLoaded(file, state, callback) {
if (!state.map) {
state.map = {
version: 3,
names: [],
mappings: '',
sources: [normalizePath(file.relative)],
sourcesContent: [state.content],
};
}
state.map.file = normalizePath(file.relative);
file.sourceMap = state.map;
callback();
}
function addSourceMaps(file, state, callback) {
var tasks = [
loadSourceMap,
fixImportedSourceMap,
mapsLoaded,
];
function apply(fn, key, cb) {
fn(file, state, cb);
}
nal.mapSeries(tasks, apply, done);
function done() {
callback(null, file);
}
}
/* Write Helpers */
function createSourceMapFile(opts) {
return new File({
cwd: opts.cwd,
base: opts.base,
path: opts.path,
contents: new Buffer(JSON.stringify(opts.content)),
stat: {
isFile: function() {
return true;
},
isDirectory: function() {
return false;
},
isBlockDevice: function() {
return false;
},
isCharacterDevice: function() {
return false;
},
isSymbolicLink: function() {
return false;
},
isFIFO: function() {
return false;
},
isSocket: function() {
return false;
},
},
});
}
var needsMultiline = ['.css'];
function getCommentOptions(extname) {
var opts = {
multiline: (needsMultiline.indexOf(extname) !== -1),
};
return opts;
}
function writeSourceMaps(file, destPath, callback) {
var sourceMapFile;
var commentOpts = getCommentOptions(file.extname);
var comment;
if (destPath == null) {
// Encode source map into comment
comment = convert.fromObject(file.sourceMap).toComment(commentOpts);
} else {
var mapFile = path.join(destPath, file.relative) + '.map';
var sourceMapPath = path.join(file.base, mapFile);
// Create new sourcemap File
sourceMapFile = createSourceMapFile({
cwd: file.cwd,
base: file.base,
path: sourceMapPath,
content: file.sourceMap,
});
var sourcemapLocation = path.relative(file.dirname, sourceMapPath);
sourcemapLocation = normalizePath(sourcemapLocation);
comment = convert.generateMapFileComment(sourcemapLocation, commentOpts);
}
// Append source map comment
file.contents = appendBuffer(file.contents, comment);
callback(null, file, sourceMapFile);
}
module.exports = {
addSourceMaps: addSourceMaps,
writeSourceMaps: writeSourceMaps,
};