'use strict';
|
|
var esprima;
|
|
// Browserified version does not have esprima
|
//
|
// 1. For node.js just require module as deps
|
// 2. For browser try to require mudule via external AMD system.
|
// If not found - try to fallback to window.esprima. If not
|
// found too - then fail to parse.
|
//
|
try {
|
// workaround to exclude package from browserify list.
|
var _require = require;
|
esprima = _require('esprima');
|
} catch (_) {
|
/* eslint-disable no-redeclare */
|
/* global window */
|
if (typeof window !== 'undefined') esprima = window.esprima;
|
}
|
|
var Type = require('../../type');
|
|
function resolveJavascriptFunction(data) {
|
if (data === null) return false;
|
|
try {
|
var source = '(' + data + ')',
|
ast = esprima.parse(source, { range: true });
|
|
if (ast.type !== 'Program' ||
|
ast.body.length !== 1 ||
|
ast.body[0].type !== 'ExpressionStatement' ||
|
(ast.body[0].expression.type !== 'ArrowFunctionExpression' &&
|
ast.body[0].expression.type !== 'FunctionExpression')) {
|
return false;
|
}
|
|
return true;
|
} catch (err) {
|
return false;
|
}
|
}
|
|
function constructJavascriptFunction(data) {
|
/*jslint evil:true*/
|
|
var source = '(' + data + ')',
|
ast = esprima.parse(source, { range: true }),
|
params = [],
|
body;
|
|
if (ast.type !== 'Program' ||
|
ast.body.length !== 1 ||
|
ast.body[0].type !== 'ExpressionStatement' ||
|
(ast.body[0].expression.type !== 'ArrowFunctionExpression' &&
|
ast.body[0].expression.type !== 'FunctionExpression')) {
|
throw new Error('Failed to resolve function');
|
}
|
|
ast.body[0].expression.params.forEach(function (param) {
|
params.push(param.name);
|
});
|
|
body = ast.body[0].expression.body.range;
|
|
// Esprima's ranges include the first '{' and the last '}' characters on
|
// function expressions. So cut them out.
|
if (ast.body[0].expression.body.type === 'BlockStatement') {
|
/*eslint-disable no-new-func*/
|
return new Function(params, source.slice(body[0] + 1, body[1] - 1));
|
}
|
// ES6 arrow functions can omit the BlockStatement. In that case, just return
|
// the body.
|
/*eslint-disable no-new-func*/
|
return new Function(params, 'return ' + source.slice(body[0], body[1]));
|
}
|
|
function representJavascriptFunction(object /*, style*/) {
|
return object.toString();
|
}
|
|
function isFunction(object) {
|
return Object.prototype.toString.call(object) === '[object Function]';
|
}
|
|
module.exports = new Type('tag:yaml.org,2002:js/function', {
|
kind: 'scalar',
|
resolve: resolveJavascriptFunction,
|
construct: constructJavascriptFunction,
|
predicate: isFunction,
|
represent: representJavascriptFunction
|
});
|