alloy
Version:
Appcelerator Titanium MVC Framework
220 lines (200 loc) • 6.93 kB
JavaScript
/*
Sets options and wraps some functionality around the source mapping functions
provided by the UglifyJS library
*/
var SM = require('source-map'),
fs = require('fs'),
path = require('path'),
wrench = require('wrench'),
U = require('../../utils'),
CONST = require('../../common/constants'),
uglifyjs = require('uglify-js'),
logger = require('../../logger'),
_ = require('../../lib/alloy/underscore')._;
var lineSplitter = /(?:\r\n|\r|\n)/,
mods = [
'builtins',
'optimizer',
'compress'
],
modLocation = './ast/';
exports.OPTIONS_OUTPUT = {
indent_start : 0, // start indentation on every line (only when `beautify`)
indent_level : 4, // indentation level (only when `beautify`)
quote_keys : false, // quote all keys in object literals?
space_colon : true, // add a space after colon signs?
ascii_only : false, // output ASCII-safe? (encodes Unicode characters as ASCII)
inline_script : false, // escape "</script"?
width : 80, // informative maximum line width (for beautified output)
max_line_len : 32000, // maximum line length (for non-beautified output)
beautify : true, // beautify output?
bracketize : false, // use brackets every time?
comments : false, // output comments?
semicolons : true // use semicolons to separate statements?
};
function mapLine(mapper, theMap, genMap, line) {
mapper.addMapping({
original: {
line: theMap.count++,
column: 0
},
generated: {
line: genMap.count++,
column: 0
},
source: theMap.filename
});
genMap.code += line + '\n';
}
function getTextFromGenerator(content, template) {
if (typeof content !== 'undefined' && content !== null) {
return content;
} else {
if (template && fs.existsSync(template)) {
return fs.readFileSync(template,'utf8');
} else {
return '';
}
}
}
exports.generateCodeAndSourceMap = function(generator, compileConfig) {
var target = generator.target;
var data = generator.data;
var markers = _.map(data, function(v,k) { return k; });
var mapper = new SM.SourceMapGenerator({ file: target.filename });
var genMap = {
file: target.filename,
count: 1,
code: ''
};
// initialize the rest of the generator properties
target.count = 1;
target.lines = getTextFromGenerator(target.templateContent,target.template).split(lineSplitter);
_.each(markers, function(m) {
var marker = data[m];
marker.count = 1;
marker.lines = getTextFromGenerator(marker.fileContent,marker.filepath).split(lineSplitter);
});
// generate the source map and composite code
_.each(target.lines, function(line) {
var trimmed = U.trim(line);
if (_.contains(markers, trimmed)) {
_.each(data[trimmed].lines, function(line) {
mapLine(mapper, data[trimmed], genMap, line);
});
} else {
mapLine(mapper, target, genMap, line);
}
});
// parse composite code into an AST
var ast;
try {
ast = uglifyjs.parse(genMap.code, { filename: genMap.file });
} catch (e) {
logger.trace(genMap.code);
throw e;
}
// process all AST operations
_.each(mods, function(mod) {
logger.trace('- Processing "' + mod + '" module...');
ast.figure_out_scope();
ast = require(modLocation+mod).process(ast, compileConfig) || ast;
});
// create uglify-js source map and stream it out
var stream, sourceMap;
if (compileConfig.sourcemap) {
sourceMap = uglifyjs.SourceMap({
file: target.filename,
orig: mapper.toString()
});
stream = uglifyjs.OutputStream(_.extend(_.clone(exports.OPTIONS_OUTPUT), {
source_map: sourceMap
}));
} else {
stream = uglifyjs.OutputStream(exports.OPTIONS_OUTPUT);
}
ast.print(stream);
// write the generated controller code
var outfile = target.filepath;
var relativeOutfile = path.relative(compileConfig.dir.project,outfile);
wrench.mkdirSyncRecursive(path.dirname(outfile), 0755);
fs.writeFileSync(outfile, stream.toString());
logger.info(' created: "' + relativeOutfile + '"');
// write source map for the generated file
if (compileConfig.sourcemap !== false) {
var mapDir = path.join(compileConfig.dir.project,CONST.DIR.MAP);
outfile = path.join(mapDir,relativeOutfile) + '.' + CONST.FILE_EXT.MAP;
relativeOutfile = path.relative(compileConfig.dir.project,outfile);
wrench.mkdirSyncRecursive(path.dirname(outfile), 0755);
fs.writeFileSync(outfile, sourceMap.toString());
logger.debug(' map: "' + relativeOutfile + '"');
}
};
exports.generateSourceMap = function(generator, compileConfig) {
if(!fs.existsSync(generator.target.filename) || fs.statSync(generator.target.filename).isDirectory()) {
return;
}
var target = generator.target;
var data = generator.data;
var markers = _.map(data, function(v,k) { return k; });
var mapper = new SM.SourceMapGenerator({ file: target.filename });
var genMap = {
file: target.filename,
count: 1,
code: ''
};
// initialize the rest of the generator properties
target.count = 1;
target.lines = getTextFromGenerator(target.templateContent,target.template).split(lineSplitter);
_.each(markers, function(m) {
var marker = data[m];
marker.count = 1;
marker.lines = getTextFromGenerator(marker.fileContent,marker.filename).split(lineSplitter);
});
// generate the source map and composite code
_.each(target.lines, function(line) {
var trimmed = U.trim(line);
if (_.contains(markers, trimmed)) {
_.each(data[trimmed].lines, function(line) {
mapLine(mapper, data[trimmed], genMap, line);
});
} else {
mapLine(mapper, target, genMap, line);
}
});
// parse composite code into an AST
var ast;
try {
ast = uglifyjs.parse(genMap.code, { filename: genMap.file });
} catch (e) {
logger.trace(genMap.code);
throw e;
}
// process all AST operations
_.each(mods, function(mod) {
logger.trace('- Processing "' + mod + '" module...');
ast.figure_out_scope();
ast = require(modLocation+mod).process(ast, compileConfig) || ast;
});
// create uglify-js source map and stream it out
var origFileName = path.relative(compileConfig.dir.project,generator.origFile.filename),
compiledFileName = path.join('Resources',path.basename(generator.origFile.filename));
var sourceMap = uglifyjs.SourceMap({
file: compiledFileName,
orig: mapper.toString()
});
var stream = uglifyjs.OutputStream(_.extend(_.clone(exports.OPTIONS_OUTPUT), {
source_map: sourceMap
}));
ast.print(stream);
// write source map for the generated file
var relativeOutfile = path.relative(compileConfig.dir.project,target.filepath);
var mapDir = path.join(compileConfig.dir.project,CONST.DIR.MAP);
var outfile = path.join(mapDir,relativeOutfile,path.basename(target.filename)) + '.' + CONST.FILE_EXT.MAP;
wrench.mkdirSyncRecursive(path.dirname(outfile), 0755);
var tmp = JSON.parse(sourceMap.toString())
tmp.sources[0] = compiledFileName;
tmp.sources[1] = origFileName;
fs.writeFileSync(outfile, JSON.stringify(tmp));
logger.debug(' map: "' + outfile + '"');
};