/
precompile.js
112 lines (94 loc) · 2.97 KB
/
precompile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
'use strict';
const uglifyjs = require('uglify-js');
const prefixer = require('./compiler/prefixer');
const tokenizer = require('./compiler/tokenizer');
const parser = require('./compiler/parser');
const compiler = require('./compiler/compiler');
const blocks = require('./compiler/blocks');
const codegen = require('./compiler/codegen');
function wrap(compiled) {
return `
(function (factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
}
})(function () {
${compiled.replace(/\n/g, '\n\t')}
return compiled;
});
`;
}
function compileFallback(source, opts) {
const prefixed = prefixer(source);
const tokens = tokenizer(prefixed);
const parsed = parser(tokens);
const fnAst = compiler(parsed, opts);
const ast = blocks(fnAst);
const code = codegen(ast, { minified: opts.minify });
return wrap(code);
}
function minify(wrapped) {
const result = uglifyjs.minify(wrapped);
if (result.error) {
throw result.error;
}
return result.code;
}
const compile = (() => {
try {
// eslint-disable-next-line global-require, import/no-unresolved
return require('../../rust/benchpress-rs').compile;
} catch (e) {
// eslint-disable-next-line no-console
console.warn('[benchpressjs] Unable to build or find a suitable native module, falling back to JS version');
return compileFallback;
}
})();
/**
* Precompile a benchpress template
* - `precompiled(source, options): Promise<string>`
* - `precompile(source, options, callback) => callback(err, output)`
* - `precompile({ source, ...options }, callback) => callback(err, output)`
*
* @param {string} source - Template source
* @param {Object} options
* @param {boolean} [options.minify = false] - Output minified code
* @param {boolean} [options.unsafe = false] - Disable safety checks, will throw on misshapen data
* @param {boolean} [options.native = true] - Use the native Rust compiler if available
* @param {function} [callback] - (err, output)
* @returns {Promise<string>} - output code
*/
function precompile(source, options, callback) {
if (typeof source === 'object' && typeof options === 'function') {
callback = options;
options = source;
source = options.source;
}
const promise = Promise.try(() => {
const opts = Object.assign({}, precompile.defaults, options);
if (typeof source !== 'string') {
throw Error('source must be a string');
}
// benchpress-rs doesn't support unsafe yet
const compiled = (opts.unsafe || opts.native === false ? compileFallback : compile)(
source,
opts
);
return opts.minify ? minify(compiled) : compiled;
});
if (callback) {
promise.then(
code => process.nextTick(callback, null, code),
err => process.nextTick(callback, err),
);
}
return promise;
}
precompile.defaults = {
minify: false,
unsafe: false,
native: true,
};
module.exports = precompile;