• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3var 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//
12try {
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
22var Type = require('../../type');
23
24function 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
45function 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
79function representJavascriptFunction(object /*, style*/) {
80  return object.toString();
81}
82
83function isFunction(object) {
84  return Object.prototype.toString.call(object) === '[object Function]';
85}
86
87module.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});
94