UNPKG

ali.regenerator

Version:

Source transformer enabling ECMAScript 6 generator functions (yield) in JavaScript-of-today (ES5)

160 lines (125 loc) 3.99 kB
/** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * https://raw.github.com/facebook/regenerator/master/LICENSE file. An * additional grant of patent rights can be found in the PATENTS file in * the same directory. */ var assert = require("assert"); var path = require("path"); var fs = require("fs"); var transform = require("./lib/visit").transform; var utils = require("./lib/util"); var recast = require("recast-harmony"); var types = recast.types; var genFunExp = /\bfunction\s*\*/; var blockBindingExp = /\b(let|const)\s+/; function regenerator(source, options) { options = normalizeOptions(options); var runtime = options.includeRuntime ? fs.readFileSync( regenerator.runtime.dev, "utf-8" ) + "\n" : ""; if (!genFunExp.test(source)) { return runtime + source; // Shortcut: no generators to transform. } var recastOptions = getRecastOptions(options); var ast = recast.parse(source, recastOptions); var path = new types.NodePath(ast); var programPath = path.get("program"); if (shouldVarify(source, options)) { // Transpile let/const into var declarations. varifyAst(programPath.node); } transform(programPath); injectRuntime(runtime, programPath.node); return recast.print(path, recastOptions).code; } function normalizeOptions(options) { options = utils.defaults(options || {}, { includeRuntime: false, supportBlockBinding: true }); if (!options.esprima) { options.esprima = require("esprima-harmony"); } assert.ok( /harmony/.test(options.esprima.version), "Bad esprima version: " + options.esprima.version ); return options; } function getRecastOptions(options) { var recastOptions = { range: true }; function copy(name) { if (name in options) { recastOptions[name] = options[name]; } } copy("esprima"); copy("sourceFileName"); copy("sourceMapName"); copy("inputSourceMap"); copy("sourceRoot"); return recastOptions; } function shouldVarify(source, options) { var supportBlockBinding = !!options.supportBlockBinding; if (supportBlockBinding) { if (!blockBindingExp.test(source)) { supportBlockBinding = false; } } return supportBlockBinding; } function varify(source, options) { var recastOptions = getRecastOptions(normalizeOptions(options)); var ast = recast.parse(source, recastOptions); varifyAst(ast.program); return recast.print(ast, recastOptions).code; } function varifyAst(ast) { types.namedTypes.Program.assert(ast); var defsResult = require("defs")(ast, { ast: true, disallowUnknownReferences: false, disallowDuplicated: false, disallowVars: false, loopClosures: "iife" }); if (defsResult.errors) { throw new Error(defsResult.errors.join("\n")) } return ast; } function injectRuntime(runtime, ast) { types.builtInTypes.string.assert(runtime); types.namedTypes.Program.assert(ast); // Include the runtime by modifying the AST rather than by concatenating // strings. This technique will allow for more accurate source mapping. if (runtime !== "") { var runtimeBody = recast.parse(runtime, { sourceFileName: regenerator.runtime.dev }).program.body; var body = ast.body; body.unshift.apply(body, runtimeBody); } return ast; } function runtime() { require(runtime.dev); } runtime.dev = path.join(__dirname, "runtime", "dev.js"); runtime.min = path.join(__dirname, "runtime", "min.js"); // Convenience for just translating let/const to var declarations. regenerator.varify = varify; // To modify an AST directly, call require("regenerator").transform(ast). regenerator.transform = transform; // To include the runtime in the current node process, call // require("regenerator").runtime(). regenerator.runtime = runtime; // To transform a string of ES6 code, call require("regenerator")(source); module.exports = regenerator;