1 | # broccoli-babel-transpiler
|
2 |
|
3 | [![Build Status](https://travis-ci.org/babel/broccoli-babel-transpiler.svg?branch=master)](https://travis-ci.org/babel/broccoli-babel-transpiler)
|
4 | [![Build status](https://ci.appveyor.com/api/projects/status/a0nbd84m1x4y5fp5?svg=true)](https://ci.appveyor.com/project/embercli/broccoli-babel-transpiler)
|
5 |
|
6 |
|
7 | A [Broccoli](https://github.com/broccolijs/broccoli) plugin which transpiles ES6 to readable ES5 (and much more) by using [Babel](https://github.com/babel/babel).
|
8 |
|
9 | ## How to install?
|
10 |
|
11 | ```sh
|
12 | $ npm install broccoli-babel-transpiler --save-dev
|
13 | ```
|
14 |
|
15 | ## How to use?
|
16 |
|
17 | In your `Brocfile.js`:
|
18 |
|
19 | ```js
|
20 | const esTranspiler = require('broccoli-babel-transpiler');
|
21 | const scriptTree = esTranspiler(inputTree, babelOptions);
|
22 | ```
|
23 |
|
24 | Note that, since Babel 6 (and v6 of this plugin), you need to be specific as to
|
25 | what your transpilation target is. Running `esTranspiler` with empty options
|
26 | will not transpile anything. You will need:
|
27 |
|
28 | * Explicit options, such as `presets`. See available [options](https://babeljs.io/docs/usage/options) at Babel's GitHub repo.
|
29 | * Babel plugins that implement the transforms you require.
|
30 |
|
31 | For a quick running example, install this plugin:
|
32 |
|
33 | ```sh
|
34 | $ npm install babel-preset-env
|
35 | ```
|
36 |
|
37 | And then run the transform like this:
|
38 |
|
39 | ```js
|
40 | let scriptTree = babel(inputTree, {
|
41 | presets: [
|
42 | ['env', {
|
43 | 'targets': {
|
44 | 'browsers': ['last 2 versions']
|
45 | }
|
46 | }]
|
47 | ]
|
48 | });
|
49 | ```
|
50 |
|
51 | ### Examples
|
52 |
|
53 | You'll find three example projects using this plugin in the repository
|
54 | [broccoli-babel-examples](https://github.com/givanse/broccoli-babel-examples).
|
55 | Each one of them builds on top of the previous example so you can progess from
|
56 | bare minimum to ambitious development.
|
57 |
|
58 | * [es6-fruits](https://github.com/givanse/broccoli-babel-examples/tree/master/es6-fruits) - Execute a single ES6 script.
|
59 | * [es6-website](https://github.com/givanse/broccoli-babel-examples/tree/master/es6-website) - Build a simple website.
|
60 | * [es6-modules](https://github.com/givanse/broccoli-babel-examples/tree/master/es6-modules) - Handle modules and unit tests.
|
61 |
|
62 | ## About source map
|
63 |
|
64 | Currently this plugin only supports inline source map. If you need
|
65 | separate source map feature, you're welcome to submit a pull request.
|
66 |
|
67 | ## Advanced usage
|
68 |
|
69 | `filterExtensions` is an option to limit (or expand) the set of file extensions
|
70 | that will be transformed.
|
71 |
|
72 | The default `filterExtension` is `js`
|
73 |
|
74 | ```js
|
75 | const esTranspiler = require('broccoli-babel-transpiler');
|
76 | let scriptTree = esTranspiler(inputTree, {
|
77 | filterExtensions:['js', 'es6'] // babelize both .js and .es6 files
|
78 | });
|
79 | ```
|
80 |
|
81 | `targetExtension` is an option to specify the extension of the output files
|
82 |
|
83 | The default `targetExtension` is `js`
|
84 |
|
85 | ```js
|
86 | const esTranspiler = require('broccoli-babel-transpiler');
|
87 | let scriptTree = esTranspiler(inputTree, {
|
88 | targetExtension: 'module.js' // create output files with module.js extension
|
89 | });
|
90 | ```
|
91 |
|
92 | ## Polyfill
|
93 |
|
94 | In order to use some of the ES6 features you must include the Babel
|
95 | [polyfill](http://babeljs.io/docs/usage/polyfill/#usage-in-browser).
|
96 |
|
97 | You don't always need this, review which features need the polyfill here: [ES6
|
98 | Features](https://babeljs.io/docs/learn-es6).
|
99 |
|
100 | ```js
|
101 | const esTranspiler = require('broccoli-babel-transpiler');
|
102 | let scriptTree = esTranspiler(inputTree, { browserPolyfill: true });
|
103 | ```
|
104 |
|
105 | ## Plugins
|
106 |
|
107 | Use of custom plugins works similarly to `babel` itself. You would pass a
|
108 | `plugins` array in `options`:
|
109 |
|
110 | ```js
|
111 | const esTranspiler = require('broccoli-babel-transpiler');
|
112 | const applyFeatureFlags = require('babel-plugin-feature-flags');
|
113 |
|
114 | let featureFlagPlugin = applyFeatureFlags({
|
115 | import: { module: 'ember-metal/features' },
|
116 | features: {
|
117 | 'ember-metal-blah': true
|
118 | }
|
119 | });
|
120 |
|
121 | let scriptTree = esTranspiler(inputTree, {
|
122 | plugins: [
|
123 | featureFlagPlugin
|
124 | ]
|
125 | });
|
126 | ```
|
127 |
|
128 | ### Caching
|
129 |
|
130 | broccoli-babel-transpiler uses a persistent cache to enable rebuilds to be
|
131 | significantly faster (by avoiding transpilation for files that have not
|
132 | changed). However, since a plugin can do many things to affect the transpiled
|
133 | output it must also influence the cache key to ensure transpiled files are
|
134 | rebuilt
|
135 | if the plugin changes (or the plugins configuration).
|
136 |
|
137 | In order to aid plugin developers in this process, broccoli-babel-transpiler
|
138 | will invoke two methods on a plugin so that it can augment the cache key:
|
139 |
|
140 | * `cacheKey` - This method is used to describe any runtime information that may
|
141 | want to invalidate the cached result of each file transpilation. This is
|
142 | generally only needed when the configuration provided to the plugin is used
|
143 | to modify the AST output by a plugin like `babel-plugin-filter-imports`
|
144 | (module
|
145 | exports to strip from a build), `babel-plugin-feature-flags` (configured
|
146 | features and current status to strip or embed in a final build), or
|
147 | `babel-plugin-htmlbars-inline-precompile` (uses `ember-template-compiler.js`
|
148 | to compile inlined templates).
|
149 | * `baseDir` - This method is expected to return the plugins base dir. The
|
150 | provided `baseDir` is used to ensure the cache is invalidated if any of the
|
151 | plugin's files change (including its deps). Each plugin should implement
|
152 | `baseDir` as: `Plugin.prototype.baseDir = function() { return \_\_dirname;
|
153 | };`.
|
154 |
|
155 | ## Parallel Transpilation
|
156 |
|
157 | broccoli-babel-transpiler can run multiple babel transpiles in parallel using a
|
158 | pool of workers, to take advantage of multi-core systems. Because these workers
|
159 | are separate processes, the plugins and callback functions that are normally
|
160 | passed as options to babel must be specified in a serializable form.
|
161 | To enable this parallelization there is an API to tell the worker how to
|
162 | construct the plugin or callback in its process.
|
163 |
|
164 | To ensure a build remains parallel safe, one can set the
|
165 | `throwUnlessParallelizable` option to true (defaults to false). This will cause
|
166 | an error to be thrown, if parallelization is not possible due to an
|
167 | incompatible babel plugin.
|
168 |
|
169 | ```js
|
170 | new Babel(input, { throwUnlessParallelizable: true | false });
|
171 | ```
|
172 |
|
173 | Alternatively, an environment variable can be set:
|
174 |
|
175 | ```sh
|
176 | THROW_UNLESS_PARALLELIZABLE=1 node build.js
|
177 | ```
|
178 |
|
179 | Plugins are specified as an object with a `_parallelBabel` property:
|
180 |
|
181 | ```js
|
182 | let plugin = {
|
183 | _parallelBabel: {
|
184 | requireFile: '/full/path/to/the/file',
|
185 | useMethod: 'methodName',
|
186 | buildUsing: 'buildFunction',
|
187 | params: { ok: 'this object will be passed to buildFunction()' }
|
188 | }
|
189 | };
|
190 | ```
|
191 |
|
192 | Callbacks can be specified like plugins, or as functions with a
|
193 | `_parallelBabel` property:
|
194 |
|
195 | ```js
|
196 | function callback() { /* do something */ };
|
197 |
|
198 | callback._parallelBabel = {
|
199 | requireFile: '/full/path/to/the/file',
|
200 | useMethod: 'methodName',
|
201 | buildUsing: 'buildFunction',
|
202 | params: { ok: 'this object will be passed to buildFunction()' }
|
203 | };
|
204 | ```
|
205 |
|
206 | ### requireFile (required)
|
207 |
|
208 | This property specifies the file to require in the worker process to create the
|
209 | plugin or callback. This must be given as an absolute path.
|
210 |
|
211 | ```js
|
212 | const esTranspiler = require('broccoli-babel-transpiler');
|
213 |
|
214 | let somePlugin = {
|
215 | _parallelBabel: {
|
216 | requireFile: '/full/path/to/the/file'
|
217 | }
|
218 | });
|
219 |
|
220 | let scriptTree = esTranspiler(inputTree, {
|
221 | plugins: [
|
222 | 'transform-strict-mode', // plugins that are given as strings will automatically be parallelized
|
223 | somePlugin
|
224 | ]
|
225 | });
|
226 | ```
|
227 |
|
228 | ### useMethod (optional)
|
229 |
|
230 | This property specifies the method to use from the file that is required.
|
231 |
|
232 | If you have a plugin defined like this:
|
233 |
|
234 | ```js
|
235 | // some_plugin.js
|
236 |
|
237 | module.exports = {
|
238 | pluginFunction(babel) {
|
239 | // do plugin things
|
240 | }
|
241 | };
|
242 | ```
|
243 |
|
244 | You can tell broccoli-babel-transpiler to use that function in the worker
|
245 | processes like so:
|
246 |
|
247 | ```js
|
248 | const esTranspiler = require('broccoli-babel-transpiler');
|
249 |
|
250 | let somePlugin = {
|
251 | _parallelBabel: {
|
252 | requireFile: '/path/to/some_plugin',
|
253 | useMethod: 'pluginFunction'
|
254 | }
|
255 | });
|
256 |
|
257 | let scriptTree = esTranspiler(inputTree, {
|
258 | plugins: [ somePlugin ]
|
259 | });
|
260 | ```
|
261 |
|
262 | ### buildUsing and params (optional)
|
263 |
|
264 | These properties specify a function to run to build the plugin (or callback),
|
265 | and any parameters to pass to that function.
|
266 |
|
267 | If the plugin needs to be built dynamically, you can do that like so:
|
268 |
|
269 | ```js
|
270 | // some_plugin.js
|
271 |
|
272 | module.exports = {
|
273 | buildPlugin(params) {
|
274 | return doSomethingWith(params.text);
|
275 | }
|
276 | };
|
277 | ```
|
278 |
|
279 | This will tell the worker process to require the plugin and call the
|
280 | `buildPlugin` function with the `params` object as an argument:
|
281 |
|
282 | ```js
|
283 | const esTranspiler = require('broccoli-babel-transpiler');
|
284 |
|
285 | let somePlugin = {
|
286 | _parallelBabel: {
|
287 | requireFile: '/path/to/some_plugin',
|
288 | buildUsing: 'buildPlugin',
|
289 | params: { text: 'some text' }
|
290 | }
|
291 | });
|
292 |
|
293 | let scriptTree = esTranspiler(inputTree, {
|
294 | plugins: [ somePlugin ]
|
295 | });
|
296 | ```
|
297 |
|
298 | Note: If both `useMethod` and `buildUsing` are specified, `useMethod` takes
|
299 | precedence.
|
300 |
|
301 | ### Number of jobs
|
302 |
|
303 | The number of parallel jobs defaults to the number of detected CPUs - 1.
|
304 |
|
305 | This can be changed with the `JOBS` environment variable:
|
306 |
|
307 | ```sh
|
308 | JOBS=4 ember build
|
309 | ```
|
310 |
|
311 | To disable parallelization:
|
312 |
|
313 | ```sh
|
314 | JOBS=1 ember build
|
315 | ```
|