l0bsterssg

node.js static responsive blog post generator
Log | Files | Refs | README

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 });