function build_kendo(options) { var kcomp = get_kendo_config().components; var selection = []; options.components.forEach(function require(comp) { if (typeof comp == "string") { for (var i = 0; i < kcomp.length; ++i) { if (kcomp[i].id == comp) { comp = kcomp[i]; break; } } } if (comp.depends) { comp.depends.forEach(require); } push_uniq(selection, comp.source); }); var toplevel = new U2.AST_Toplevel({ body: [] }); selection.forEach(function(filename){ if (options.log) { options.log(filename); } filename = PATH.join(options.kendo_dir, filename); var code = FS.readFileSync(filename, { encoding: "utf8" }); toplevel = U2.parse(code, { filename: filename, toplevel: toplevel }); }); toplevel = toplevel.transform(new U2.TreeTransformer(function before(node, descend){ if (node === toplevel) return undefined; if (!(this.parent() instanceof U2.AST_Toplevel)) return node; // no point to descend. // discard RequireJS boilerplate if (node instanceof U2.AST_SimpleStatement && node.body instanceof U2.AST_Call && node.body.expression instanceof U2.AST_Conditional && node.body.expression.consequent instanceof U2.AST_SymbolRef && node.body.expression.consequent.name == "define") // WHOA, I should really implement some pattern matching in UglifyJS { // so if that's the case, we want to replace the whole // simple statement with the *body* of the function // that gets passed to `define`. var f = node.body.args[1]; // args[0] is the dependency list return U2.MAP.splice(f.body); } return node; })); return toplevel.print_to_string(); }
function as_toplevel(input) { if (input instanceof uglify.AST_BlockStatement) input = input.body; else if (input instanceof uglify.AST_Statement) input = [ input ]; else throw new Error("Unsupported input syntax"); var toplevel = new uglify.AST_Toplevel({ body: input }); toplevel.figure_out_scope(); return toplevel; }
function extract_widget_info(ast) { ast = new U2.AST_Toplevel(ast); ast.figure_out_scope(); var widgets = []; var scope = null; // Quick-n-dirty heuristic that should cover the use cases in Kendo. function dumb_eval(node) { if (node instanceof U2.AST_Constant) { return node.getValue(); } if (node instanceof U2.AST_SymbolRef) { var init = node.definition().init; if (init) { return dumb_eval(init); } return node.name; } if (node instanceof U2.AST_Dot) { return dumb_eval(node.expression) + "." + node.property; } if (node instanceof U2.AST_Call && is_widget(node)) { return "kendo.ui.Widget.extend"; } return null; // dunno how to handle } // determine if node points to [window.]kendo.ui.SOMETHING function is_widget(node) { if (node instanceof U2.AST_Call && node.expression instanceof U2.AST_Dot && node.expression.property == "extend") { var x = dumb_eval(node.expression); if (!x) return false; return /^(window\.)?(kendo|kendo\.dataviz|kendo\.mobile)\.ui\..+?\.extend/.test(x); } } var tw = new U2.TreeWalker(function(node, descend){ if (node instanceof U2.AST_Scope) { var save_scope = scope; scope = node; descend(); scope = save_scope; return true; } if (is_widget(node)) { var def = node.args[0]; var options = def.properties.filter(function(prop){ return prop.key == "options"; })[0]; var events = def.properties.filter(function(prop){ return prop.key == "events"; })[0]; if (events && events.value instanceof U2.AST_Array) { events = events.value.elements.map(function(el){ return dumb_eval(el); }); } if (options) { var name = options.value.properties.filter(function(prop){ return prop.key == "name"; })[0]; if (name && name.value) { name = name.value.value; widgets.push({ name : name, options : options.value.properties.map(function(prop){ return prop.key }), events : events, inherits : node.expression.expression.print_to_string({ beautify: true }), file : node.start.file, line : node.start.line, col : node.start.col }); } } } }); ast.walk(tw); return widgets; }