fonteditor/node/amd2module.js
2015-01-28 14:16:58 +08:00

188 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file amd 模块转换为commonjs模块
* @author mengke01(kekee000@gmail.com)
*/
var esprima = require('esprima');
var estraverse = require( 'estraverse' );
var escodegen = require('escodegen');
var SYNTAX = estraverse.Syntax;
// 顶级模块,用来生成相对位置
var REG_TOP_MODULE = /^(:?common|math|graphics|ttf)/;
var REG_REQUIRE = /require\(\s*(['"])([^'"]+)\1\s*\)/g;
/**
* 获取ast树
* @param {string} code 代码片段
* @return {Object} ast树
*/
function getAst(code) {
var ast = null;
try {
ast = esprima.parse(code, {
range: true
});
} catch (ex) {
throw 'can\'t parse amd code';
}
return ast;
}
/**
* 获取define的factory
*
* @return {astNode}
*/
function getDefineFactory(defineExpr) {
var args = defineExpr['arguments'];
var factoryAst;
// 解析参数
for (var i = 0, l = args.length; i < l; i++) {
var argument = args[i];
if (argument.type === SYNTAX.FunctionExpression || argument.type === SYNTAX.ObjectExpression) {
factoryAst = argument;
break;
}
}
return factoryAst;
}
/**
* 解析define块提取factory和return
*
* @param {Object} code code
* @return {Array}
*/
function getDefineBlock(code) {
var ast = getAst(code);
var defineList = [];
// require('fs').writeFileSync('ast.json', JSON.stringify(ast));
estraverse.traverse(ast, {
enter: function (node, parent) {
if ( node.type == SYNTAX.ExpressionStatement
&& node.expression.type == SYNTAX.CallExpression
&& node.expression.callee.name == 'define'
) {
var defineBlock = {};
defineBlock.defineRange = node.range;
var factory = getDefineFactory(node.expression);
// define('xxx', {})
if (factory.type === SYNTAX.ObjectExpression) {
defineBlock.type = 'object';
defineBlock.factoryRange = factory.range;
}
// define(function() {})
else if (factory.type === SYNTAX.FunctionExpression){
defineBlock.type = 'function';
var body = factory.body.body;
defineBlock.factoryRange = factory.body.range;
var returnList = defineBlock.returnRange = [];
// 替换return
for (var i = body.length - 1; i >=0; i--) {
if (body[i].type == SYNTAX.ReturnStatement) {
returnList.push(body[i].range);
}
}
}
defineList.push(defineBlock);
this.skip();
}
}
});
return defineList;
}
/**
* 替换define的return为 module.exports
*
* @param {Object} code code
* @return {Object}
*/
function replaceDefine(code) {
var defineList = getDefineBlock(code);
var segments = [];
var index = 0;
defineList.forEach(function (block) {
if (block.type === 'function') {
segments.push(code.slice(index, block.defineRange[0]));
index = block.factoryRange[0] + 1;
block.returnRange.forEach(function (range) {
segments.push(code.slice(index, range[0]));
segments.push('module.exports = ');
segments.push(code.slice(range[0] + 6, range[1]));
index = range[1];
});
index = block.defineRange[1];
}
else if (block.type === 'object'){
segments.push(code.slice(index, block.defineRange[0]));
segments.push('module.exports = ');
segments.push(code.slice(block.factoryRange[0], block.factoryRange[1]) + ';');
index = block.defineRange[1];
}
});
segments.push(code.slice(index));
code = segments.join('');
return code;
}
/**
* 替换require的绝对路径为相对路径
*
* @param {Object} code code
* @param {Object} codeDepth 当前模块位置
* @return {Object} code
*/
function replaceRequire(code, codeDepth) {
return code.replace(REG_REQUIRE, function ($0, $1, moduleId) {
if (REG_TOP_MODULE.test(moduleId)) {
moduleId = codeDepth + moduleId;
}
return 'require(\'' + moduleId + '\')';
});
}
/**
* 生成commonjs代码
* @param {Object} ast ast树
* @return {string} 生成后的代码
*/
function genCommonJS(ast) {
return escodegen.generate(ast);
}
module.exports = function (code, codeDepth) {
if (codeDepth && codeDepth[codeDepth.length - 1] !== '/') {
codeDepth += '/';
}
code = String(code);
code = replaceDefine(code);
code = replaceRequire(code, codeDepth);
return code;
};