01522e4076dd0b90e976237a87ed2c765c7c5cf464755f9c882272d7137451361aabe3f2895034fa9c89d1496608d95e1f4ed11b892552361514e76da6d6ff 2.8 KB

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