1 | import fs from 'fs'; |
||
2 | import { all, promisify, reject, resolve } from 'bluebird'; |
||
3 | import { |
||
4 | T, |
||
5 | cond, |
||
6 | curry, |
||
7 | endsWith, |
||
8 | fromPairs, |
||
9 | map, |
||
10 | merge, |
||
11 | path, |
||
12 | startsWith, |
||
13 | uniq |
||
14 | } from 'ramda'; |
||
15 | import { transform } from 'babel-core'; |
||
16 | import { compileES6 } from './compiler'; |
||
17 | |||
18 | const readFile = promisify(fs.readFile); |
||
19 | |||
20 | /** |
||
21 | * Ensures a piece of source has no import or require declarations |
||
22 | * |
||
23 | * @param {String} source |
||
24 | * @return {Promise} |
||
25 | */ |
||
26 | export const ensureNoImports = curry((filename, source) => { |
||
27 | const { modules } = inspect(source); |
||
28 | return modules.length |
||
29 | ? reject(new Error(`Cannot import modules on autocomplete files (${filename})`)) |
||
30 | : resolve(); |
||
31 | }); |
||
32 | |||
33 | /** |
||
34 | * Traverses the AST, getting the imported modules and compile to pairs |
||
35 | * |
||
36 | * @author Marcelo Haskel Camargo |
||
37 | * @param {String} source |
||
38 | * @return {Promise<String[][]>} |
||
39 | */ |
||
40 | export function compileModulesFromSource(source) { |
||
41 | const { modules } = inspect(source); |
||
42 | return all(modules.filter(startsWith('./')).map(compileModule)); |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Compiles a module to a pair with (filename :: string, source :: string) |
||
47 | * |
||
48 | * @author Marcelo Haskell Camargo |
||
49 | * @param {String} module |
||
50 | * @return {Promise} |
||
51 | */ |
||
52 | export function compileModule(module) { |
||
53 | const extensionIs = extension => ~endsWith(extension, module); |
||
54 | |||
55 | return readFile(module, 'utf-8') |
||
56 | .then(cond([ |
||
57 | [extensionIs('.js'), compileES6], |
||
58 | [extensionIs('.json'), JSON.parse & JSON.stringify], |
||
59 | [T, ~reject(new Error(`Unknown module loader for file ${module}`))]])) |
||
60 | .then(source => [module, source]); |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | * Evaluates a list of pairs of modules. modules :: [(String, String)] |
||
65 | * |
||
66 | * @author Marcelo Haskell Camargo |
||
67 | * @param {NodeVM} vm - Virtual machine instance to run |
||
0 ignored issues
–
show
Documentation
introduced
by
![]() |
|||
68 | * @param {String[][]} modules pairs, with (name :: string, source :: string) |
||
0 ignored issues
–
show
|
|||
69 | */ |
||
70 | export const evaluateModules = (vm, modules) => fromPairs(map(([module, source]) => { |
||
71 | // JSON doesn't need to run on VM. We can directly parse it |
||
72 | const convertToBytecode = cond([ |
||
73 | [endsWith('.json'), ~JSON.parse(source)], |
||
74 | [endsWith('.js'), vm.run(source, _)], |
||
0 ignored issues
–
show
The variable
_ seems to be never declared. If this is a global, consider adding a /** global: _ */ comment.
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed. To learn more about declaring variables in Javascript, see the MDN. ![]() |
|||
75 | [T, module => { |
||
76 | throw new Error(`Unknown file type for ${module}`); |
||
77 | }] |
||
78 | ]); |
||
79 | |||
80 | return [module, convertToBytecode(module)]; |
||
81 | }, modules)); |
||
82 | |||
83 | /** |
||
84 | * Inspects a JS source and returns processed information, such as ES5 code, |
||
85 | * the ast, source map and the used modules |
||
86 | * |
||
87 | * @author Marcelo Haskell Camargo |
||
88 | * @param {String} source - ES6 source |
||
89 | * @return {String[]} |
||
90 | */ |
||
91 | export function inspect(source) { |
||
92 | const modules = []; |
||
93 | const result = transform(source, { |
||
94 | comments: false, |
||
95 | compact: true, |
||
96 | presets: ['es2015', 'react'], |
||
97 | plugins: [ |
||
98 | ['transform-react-jsx', { pragma: '__render__' }], |
||
99 | [~({ |
||
100 | visitor: { |
||
101 | ImportDeclaration({ node }) { |
||
102 | modules.push(node.source.value); |
||
103 | }, |
||
104 | CallExpression({ node }) { |
||
105 | // Find and extract require(module) |
||
106 | const callee = path(['callee', 'name'], node); |
||
107 | |||
108 | if (callee === 'require') { |
||
109 | const [moduleNode] = node.arguments; |
||
110 | const isLiteralModule = moduleNode && moduleNode.type === 'StringLiteral'; |
||
111 | |||
112 | if (isLiteralModule) { |
||
113 | modules.push(moduleNode.value); |
||
114 | } |
||
115 | } |
||
116 | } |
||
117 | } |
||
118 | })] |
||
119 | ] |
||
120 | }); |
||
121 | |||
122 | return merge(result, { modules: uniq(modules) }); |
||
123 | } |
||
124 |