UNPKG

6.01 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 compilerOpts.parser.noNodentExtensions = true ;
77
78 var pr = { origCode: state.file.code, filename: '', ast: path.node };
79 compiler.asynchronize(pr, undefined, compilerOpts, compiler.log);
80
81 var runtime ;
82 if (!compilerOpts.noRuntime) {
83 if (compilerOpts.generators) {
84 runtime = getRuntime('Function.prototype.$asyncspawn', Function.prototype.$asyncspawn, compilerOpts, compiler);
85 } else {
86 runtime = getRuntime('Function.prototype.$asyncbind', Function.prototype.$asyncbind, compilerOpts, compiler);
87 }
88
89 if (state.opts.useRuntimeModule) {
90 state.addImport(state.opts.useRuntimeModule === true ? 'nodent-runtime' : state.opts.useRuntimeModule, 'default');
91 }
92 else if (!state.opts.runtimePattern) {
93 path.unshiftContainer('body', runtime);
94 }
95 else if (state.opts.runtimePattern === 'directive') {
96 var hasRuntime = false;
97 for (var index = 0; index < path.node.directives.length; index++) {
98 if (path.node.directives[index].value.value === 'use runtime-nodent') {
99 if (!hasRuntime) {
100 path.unshiftContainer('body', runtime);
101 hasRuntime = true;
102 }
103 path.node.directives.splice(index, 1);
104 }
105 }
106 }
107 else {
108 var pattern = new RegExp(state.opts.runtimePattern);
109 var parserOpts = state.file.parserOpts;
110
111 // The key is called sourceFileName since babel-core 6.16:
112 var sourceFileName = parserOpts.filename || parserOpts.sourceFileName;
113 if (sourceFileName.match(pattern)) {
114 path.unshiftContainer('body', runtime);
115 }
116 }
117 }
118 }
119 },
120
121 AwaitExpression: function Function(path, state) {
122 shouldIncludeRuntime = true;
123 },
124
125 Function: function Function(path, state) {
126 if (path.node.async) {
127 shouldIncludeRuntime = true;
128 }
129 }
130 }
131 };
132};