UNPKG

broccoli-ember-preparse

Version:

Broccoli plugin that applies some compile time performance optimizations to an ember build.

119 lines (103 loc) 3.85 kB
var babylon = require('babylon'); var traverse = require('babel-traverse').default; var ModuleGraph = require('./module_graph'); var fs = require('fs'); var path = require('path'); var generateLoader = require('./generate-loader'); module.exports = function processEmber(string, relativePath, eagerLoadList, moduleFilter) { var ast = babylon.parse(string); var blockStatementPath; var first = -1; var count = 0; var graph = new ModuleGraph(moduleFilter); var visitor = { CallExpression: function(path) { var callExpression = path.node; if (callExpression.callee.name !== 'enifed' || callExpression.arguments.length !== 3 || callExpression.arguments[0].type !== 'StringLiteral' || callExpression.arguments[1].type !== 'ArrayExpression' || callExpression.arguments[2].type !== 'FunctionExpression') { return; } var statementPath = path.parentPath; if (blockStatementPath === undefined) { blockStatementPath = statementPath.parentPath; } else { if (blockStatementPath !== statementPath.parentPath) { throw new Error('defines expected to be in the same block'); } } if (first === -1) { first = statementPath.key; } else { if ((first+count) !== statementPath.key) { throw new Error('define statements expected to be contiguous '+(first+count)+' '+statementPath.key); } } count++; graph.add(callExpression); } }; traverse(ast, visitor); graph.buildEdges(); graph.createEdge('ember-metal/core', 'ember-metal/debug'); if (relativePath.indexOf('ember.debug.js') > -1) { // Workaround for the fact that ember-metal imports ember-debug via require // https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/index.js#L360 graph.createEdge('ember-metal', 'ember-debug'); } eagerLoadList.forEach(graph.makeEager, graph); var lines = string.split(/\n/); function getLines(loc) { var start = loc.start.line-1; var end = loc.end.line; var src = lines.slice(start, end); if (loc.start.line === loc.end.line) { src[0] = src[0].slice(loc.start.column, loc.end.column); } else { src[0] = src[0].slice(loc.start.column); src[src.length-1] = src[src.length-1].slice(0, loc.end.column); } return src; } var buffer = [string.match(/\/\*![\s\S]+?\*\//)[0]]; buffer.push('if (typeof Ember === \'undefined\') { Em = Ember = {}; }'); buffer.push('Em.__global = this'); buffer.push('Em._eager = ' + JSON.stringify(graph.eagerSet.names) + ';'); buffer.push(generateLoader()); function pushLines(lines) { for (var i = 0; i < lines.length; i++) { var line = lines[i]; line = line.replace(/mainContext/g, 'Em.__global'); line = line.replace(/\s(require|requireModule)\(/g, 'Em.__loader.require.default('); buffer.push(line); } } graph.set.names.forEach(function (name) { var module = graph.node(name); if (!graph.eagerSet.has(name)) { var src = getLines(module.functionExpression.loc); src[0] = 'Em.__loader.define(' + JSON.stringify(name) + ', ' + JSON.stringify(module.deps) + ', ' + src[0]; src[src.length - 1] += ');'; pushLines(src); } }); graph.eagerSet.names.forEach(function (name) { var module = graph.node(name); if (!module) { return; } var src = getLines(module.functionExpression.loc); src[0] = '('+ src[0]; src[src.length - 1] += ')('+ module.deps.map(function (dep) { if (dep === 'require') { return 'Em.__loader.require'; } var index = graph.eagerSet.map[dep === 'exports' ? name : dep]; return 'Em._e['+index+']'; }).join(',')+');'; pushLines(src); }); return buffer.join('\n'); };