function.js (2819B)
1 'use strict'; 2 3 var esprima; 4 5 // Browserified version does not have esprima 6 // 7 // 1. For node.js just require module as deps 8 // 2. For browser try to require mudule via external AMD system. 9 // If not found - try to fallback to window.esprima. If not 10 // found too - then fail to parse. 11 // 12 try { 13 // workaround to exclude package from browserify list. 14 var _require = require; 15 esprima = _require('esprima'); 16 } catch (_) { 17 /* eslint-disable no-redeclare */ 18 /* global window */ 19 if (typeof window !== 'undefined') esprima = window.esprima; 20 } 21 22 var Type = require('../../type'); 23 24 function resolveJavascriptFunction(data) { 25 if (data === null) return false; 26 27 try { 28 var source = '(' + data + ')', 29 ast = esprima.parse(source, { range: true }); 30 31 if (ast.type !== 'Program' || 32 ast.body.length !== 1 || 33 ast.body[0].type !== 'ExpressionStatement' || 34 (ast.body[0].expression.type !== 'ArrowFunctionExpression' && 35 ast.body[0].expression.type !== 'FunctionExpression')) { 36 return false; 37 } 38 39 return true; 40 } catch (err) { 41 return false; 42 } 43 } 44 45 function constructJavascriptFunction(data) { 46 /*jslint evil:true*/ 47 48 var source = '(' + data + ')', 49 ast = esprima.parse(source, { range: true }), 50 params = [], 51 body; 52 53 if (ast.type !== 'Program' || 54 ast.body.length !== 1 || 55 ast.body[0].type !== 'ExpressionStatement' || 56 (ast.body[0].expression.type !== 'ArrowFunctionExpression' && 57 ast.body[0].expression.type !== 'FunctionExpression')) { 58 throw new Error('Failed to resolve function'); 59 } 60 61 ast.body[0].expression.params.forEach(function (param) { 62 params.push(param.name); 63 }); 64 65 body = ast.body[0].expression.body.range; 66 67 // Esprima's ranges include the first '{' and the last '}' characters on 68 // function expressions. So cut them out. 69 if (ast.body[0].expression.body.type === 'BlockStatement') { 70 /*eslint-disable no-new-func*/ 71 return new Function(params, source.slice(body[0] + 1, body[1] - 1)); 72 } 73 // ES6 arrow functions can omit the BlockStatement. In that case, just return 74 // the body. 75 /*eslint-disable no-new-func*/ 76 return new Function(params, 'return ' + source.slice(body[0], body[1])); 77 } 78 79 function representJavascriptFunction(object /*, style*/) { 80 return object.toString(); 81 } 82 83 function isFunction(object) { 84 return Object.prototype.toString.call(object) === '[object Function]'; 85 } 86 87 module.exports = new Type('tag:yaml.org,2002:js/function', { 88 kind: 'scalar', 89 resolve: resolveJavascriptFunction, 90 construct: constructJavascriptFunction, 91 predicate: isFunction, 92 represent: representJavascriptFunction 93 });