UNPKG

5.94 kBJavaScriptView Raw
1'use strict';
2
3/*
4 * 'fast-async' plugin for Babel v6.x. It uses nodent to transform the entire program before passing it off
5 * to the next transformer.
6 */
7module.exports = function (babel) {
8 var logger = console.log.bind(console);
9 var nodent = require('nodent');
10 var compiler = null;
11 var compilerOpts = {};
12 var shouldIncludeRuntime = false;
13 var defaultEnv = {
14 log:logger,
15 dontInstallRequireHook:true,
16 dontMapStackTraces:true,
17 extension:null
18 };
19
20 function getRuntime(symbol, fn, opts, compiler) {
21 var runtime = symbol + '=' + fn.toString().replace(/[\s]+/g, ' ') + ';\n';
22 opts.parser.ranges = false;
23 opts.parser.locations = false;
24 // Use babel rather than nodent (acorn) as babel's AST is not ESTree compliant
25 var ast = babel.transform(runtime).ast.program.body[0];
26 // Remove location information from the runtime as Babel >=6.5.0 does a search by
27 // location and barfs if multiple nodes apparently occupy the same source locations
28 ast = JSON.parse(JSON.stringify(ast, function replacer(key, value) {
29 return (key === 'start' || key === 'end' ? undefined : value);
30 }));
31 return ast;
32 }
33
34 return {
35 // Lifted from https://github.com/babel/babel/blob/master/packages/babel-plugin-syntax-async-functions/src/index.js#L3,
36 // which is not nice, but avoids installation complexity with plugins (which I must try to work out sometime)
37 manipulateOptions: function manipulateOptions(opts, parserOpts) {
38 parserOpts.plugins.push('asyncFunctions');
39 },
40
41 visitor: {
42 Program: {
43 enter: function(path, state){
44 shouldIncludeRuntime = false;
45 },
46 exit: function (path, state) {
47 // Check if there was an async or await keyword before bothering to process the AST
48 if (!shouldIncludeRuntime)
49 return ;
50
51 state.opts = state.opts || {} ;
52 var envOpts = state.opts.env || {};
53 Object.keys(defaultEnv).forEach(function(k){
54 if (!(k in envOpts))
55 envOpts[k] = defaultEnv[k] ;
56 }) ;
57
58 compiler = nodent(envOpts);
59 compilerOpts = compiler.parseCompilerOptions('"use nodent-promises";', compiler.log);
60
61 var defCompilerOpts = state.opts.compiler ;
62 if (state.opts.spec) {
63 defCompilerOpts = {
64 promises:true,
65 wrapAwait:true,
66 noRuntime:true
67 }
68 }
69
70 if (defCompilerOpts && typeof defCompilerOpts==="object") {
71 Object.keys(defCompilerOpts).forEach(function(k){
72 compilerOpts[k] = defCompilerOpts[k] ;
73 }) ;
74 }
75 compilerOpts.babelTree = true;
76
77 var pr = { origCode: state.file.code, filename: '', ast: path.node };
78 compiler.asynchronize(pr, undefined, compilerOpts, compiler.log);
79
80 var runtime ;
81 if (!compilerOpts.noRuntime) {
82 if (compilerOpts.generators) {
83 runtime = getRuntime('Function.prototype.$asyncspawn', Function.prototype.$asyncspawn, compilerOpts, compiler);
84 } else {
85 runtime = getRuntime('Function.prototype.$asyncbind', Function.prototype.$asyncbind, compilerOpts, compiler);
86 }
87
88 if (state.opts.useRuntimeModule) {
89 state.addImport(state.opts.useRuntimeModule === true ? 'nodent-runtime' : state.opts.useRuntimeModule, 'default');
90 }
91 else if (!state.opts.runtimePattern) {
92 path.unshiftContainer('body', runtime);
93 }
94 else if (state.opts.runtimePattern === 'directive') {
95 var hasRuntime = false;
96 for (var index = 0; index < path.node.directives.length; index++) {
97 if (path.node.directives[index].value.value === 'use runtime-nodent') {
98 if (!hasRuntime) {
99 path.unshiftContainer('body', runtime);
100 hasRuntime = true;
101 }
102 path.node.directives.splice(index, 1);
103 }
104 }
105 }
106 else {
107 var pattern = new RegExp(state.opts.runtimePattern);
108 var parserOpts = state.file.parserOpts;
109
110 // The key is called sourceFileName since babel-core 6.16:
111 var sourceFileName = parserOpts.filename || parserOpts.sourceFileName;
112 if (sourceFileName.match(pattern)) {
113 path.unshiftContainer('body', runtime);
114 }
115 }
116 }
117 }
118 },
119
120 AwaitExpression: function Function(path, state) {
121 shouldIncludeRuntime = true;
122 },
123
124 Function: function Function(path, state) {
125 if (path.node.async) {
126 shouldIncludeRuntime = true;
127 }
128 }
129 }
130 };
131};